Fixes issue #2417 in the backend, looks for fields which could contain a key value and if they do it will resolve them down to the ID field value.

This commit is contained in:
mike12345567 2021-10-18 17:54:34 +01:00
parent 0b2bdf4d47
commit dd75d236c0
2 changed files with 64 additions and 4 deletions

View File

@ -15,8 +15,9 @@ import {
import { import {
breakRowIdField, breakRowIdField,
generateRowIdField, generateRowIdField,
isRowId,
convertRowId,
} from "../../../integrations/utils" } from "../../../integrations/utils"
import { RelationshipTypes } from "../../../constants"
interface ManyRelationship { interface ManyRelationship {
tableId?: string tableId?: string
@ -36,7 +37,7 @@ interface RunConfig {
module External { module External {
const { makeExternalQuery } = require("./utils") const { makeExternalQuery } = require("./utils")
const { DataSourceOperation, FieldTypes } = require("../../../constants") const { DataSourceOperation, FieldTypes, RelationshipTypes } = require("../../../constants")
const { breakExternalTableId, isSQL } = require("../../../integrations/utils") const { breakExternalTableId, isSQL } = require("../../../integrations/utils")
const { processObjectSync } = require("@budibase/string-templates") const { processObjectSync } = require("@budibase/string-templates")
const { cloneDeep } = require("lodash/fp") const { cloneDeep } = require("lodash/fp")
@ -83,6 +84,48 @@ module External {
} }
} }
/**
* This function checks the incoming parameters to make sure all the inputs are
* valid based on on the table schema. The main thing this is looking for is when a
* user has made use of the _id field of a row for a foreign key or a search parameter.
* In these cases the key will be sent up as [1], rather than 1. In these cases we will
* simplify it down to the requirements. This function is quite complex as we try to be
* relatively restrictive over what types of columns we will perform this action for.
*/
function cleanupConfig(config: RunConfig, table: Table): RunConfig {
const primaryOptions = [
FieldTypes.STRING,
FieldTypes.LONGFORM,
FieldTypes.OPTIONS,
FieldTypes.NUMBER,
]
// filter out fields which cannot be keys
const fieldNames = Object.entries(table.schema)
.filter(schema => primaryOptions.find(val => val === schema[1].type))
.map(([fieldName]) => fieldName)
const iterateObject = (obj: { [key: string]: any }) => {
for (let [field, value] of Object.entries(obj)) {
if (fieldNames.find(name => name === field) && isRowId(value)) {
obj[field] = convertRowId(value)
}
}
}
// check the row and filters to make sure they aren't a key of some sort
if (config.filters) {
for (let filter of Object.values(config.filters)) {
if (typeof filter !== "object" || Object.keys(filter).length === 0) {
continue
}
iterateObject(filter)
}
}
if (config.row) {
iterateObject(config.row)
}
return config
}
function generateIdForRow(row: Row | undefined, table: Table): string { function generateIdForRow(row: Row | undefined, table: Table): string {
const primary = table.primary const primary = table.primary
if (!row || !primary) { if (!row || !primary) {
@ -509,7 +552,7 @@ module External {
return fields return fields
} }
async run({ id, row, filters, sort, paginate }: RunConfig) { async run(config: RunConfig) {
const { appId, operation, tableId } = this const { appId, operation, tableId } = this
let { datasourceId, tableName } = breakExternalTableId(tableId) let { datasourceId, tableName } = breakExternalTableId(tableId)
if (!this.datasource) { if (!this.datasource) {
@ -525,9 +568,11 @@ module External {
if (!table) { if (!table) {
throw `Unable to process query, table "${tableName}" not defined.` throw `Unable to process query, table "${tableName}" not defined.`
} }
// clean up row on ingress using schema // look for specific components of config which may not be considered acceptable
let { id, row, filters, sort, paginate } = cleanupConfig(config, table)
filters = buildFilters(id, filters || {}, table) filters = buildFilters(id, filters || {}, table)
const relationships = this.buildRelationships(table) const relationships = this.buildRelationships(table)
// clean up row on ingress using schema
const processed = this.inputProcessing(row, table) const processed = this.inputProcessing(row, table)
row = processed.row row = processed.row
if ( if (

View File

@ -5,6 +5,7 @@ const { DocumentTypes, SEPARATOR } = require("../db/utils")
const { FieldTypes } = require("../constants") const { FieldTypes } = require("../constants")
const DOUBLE_SEPARATOR = `${SEPARATOR}${SEPARATOR}` const DOUBLE_SEPARATOR = `${SEPARATOR}${SEPARATOR}`
const ROW_ID_REGEX = /^\[.*]$/g
export function isExternalTable(tableId: string) { export function isExternalTable(tableId: string) {
return tableId.includes(DocumentTypes.DATASOURCE) return tableId.includes(DocumentTypes.DATASOURCE)
@ -32,6 +33,20 @@ export function generateRowIdField(keyProps: any[] = []) {
return encodeURIComponent(JSON.stringify(keyProps).replace(/"/g, "'")) return encodeURIComponent(JSON.stringify(keyProps).replace(/"/g, "'"))
} }
export function isRowId(field: any) {
return Array.isArray(field) || (typeof field === "string" && field.match(ROW_ID_REGEX) != null)
}
export function convertRowId(field: any) {
if (Array.isArray(field)) {
return field[0]
}
if (typeof field === "string" && field.match(ROW_ID_REGEX) != null) {
return field.substring(1, field.length - 1)
}
return field
}
// should always return an array // should always return an array
export function breakRowIdField(_id: string | { _id: string }): any[] { export function breakRowIdField(_id: string | { _id: string }): any[] {
if (!_id) { if (!_id) {