diff --git a/packages/server/src/api/controllers/table/index.ts b/packages/server/src/api/controllers/table/index.ts index a02ecc665e..ee6947cec8 100644 --- a/packages/server/src/api/controllers/table/index.ts +++ b/packages/server/src/api/controllers/table/index.ts @@ -34,7 +34,11 @@ import sdk from "../../../sdk" import { jsonFromCsvString } from "../../../utilities/csv" import { builderSocket } from "../../../websockets" import { cloneDeep, isEqual } from "lodash" -import { helpers } from "@budibase/shared-core" +import { + helpers, + PROTECTED_EXTERNAL_COLUMNS, + PROTECTED_INTERNAL_COLUMNS, +} from "@budibase/shared-core" function pickApi({ tableId, table }: { tableId?: string; table?: Table }) { if (table && isExternalTable(table)) { @@ -167,7 +171,7 @@ export async function validateNewTableImport( if (isRows(rows) && isSchema(schema)) { ctx.status = 200 - ctx.body = validateSchema(rows, schema) + ctx.body = validateSchema(rows, schema, PROTECTED_INTERNAL_COLUMNS) } else { ctx.status = 422 } @@ -180,6 +184,7 @@ export async function validateExistingTableImport( let schema = null + let protectedColumnNames if (tableId) { const table = await sdk.tables.getTable(tableId) schema = table.schema @@ -189,6 +194,9 @@ export async function validateExistingTableImport( name: "_id", type: FieldType.STRING, } + protectedColumnNames = PROTECTED_INTERNAL_COLUMNS.filter(x => x !== "_id") + } else { + protectedColumnNames = PROTECTED_EXTERNAL_COLUMNS } } else { ctx.status = 422 @@ -197,7 +205,7 @@ export async function validateExistingTableImport( if (tableId && isRows(rows) && isSchema(schema)) { ctx.status = 200 - ctx.body = validateSchema(rows, schema) + ctx.body = validateSchema(rows, schema, protectedColumnNames) } else { ctx.status = 422 } diff --git a/packages/server/src/utilities/schema.ts b/packages/server/src/utilities/schema.ts index 4bd4e8f583..c7a4427dd7 100644 --- a/packages/server/src/utilities/schema.ts +++ b/packages/server/src/utilities/schema.ts @@ -41,7 +41,11 @@ export function isRows(rows: any): rows is Rows { return Array.isArray(rows) && rows.every(row => typeof row === "object") } -export function validate(rows: Rows, schema: TableSchema): ValidationResults { +export function validate( + rows: Rows, + schema: TableSchema, + protectedColumnNames: readonly string[] +): ValidationResults { const results: ValidationResults = { schemaValidation: {}, allValid: false, @@ -49,6 +53,8 @@ export function validate(rows: Rows, schema: TableSchema): ValidationResults { errors: {}, } + protectedColumnNames = protectedColumnNames.map(x => x.toLowerCase()) + rows.forEach(row => { Object.entries(row).forEach(([columnName, columnData]) => { const { @@ -63,6 +69,12 @@ export function validate(rows: Rows, schema: TableSchema): ValidationResults { return } + if (protectedColumnNames.includes(columnName.toLowerCase())) { + results.schemaValidation[columnName] = false + results.errors[columnName] = `${columnName} is a protected name` + return + } + // If the columnType is not a string, then it's not present in the schema, and should be added to the invalid columns array if (typeof columnType !== "string") { results.invalidColumns.push(columnName)