Disallowing prohibited columns consistently, no matter the case, and backend validation for this as well.
This commit is contained in:
parent
96be6e85e3
commit
def3b0260e
|
@ -1,14 +1,5 @@
|
||||||
export const CONSTANT_INTERNAL_ROW_COLS = [
|
export {
|
||||||
"_id",
|
CONSTANT_INTERNAL_ROW_COLS,
|
||||||
"_rev",
|
CONSTANT_EXTERNAL_ROW_COLS,
|
||||||
"type",
|
isInternalColumnName,
|
||||||
"createdAt",
|
} from "@budibase/shared-core"
|
||||||
"updatedAt",
|
|
||||||
"tableId",
|
|
||||||
] as const
|
|
||||||
|
|
||||||
export const CONSTANT_EXTERNAL_ROW_COLS = ["_id", "_rev", "tableId"] as const
|
|
||||||
|
|
||||||
export function isInternalColumnName(name: string): boolean {
|
|
||||||
return (CONSTANT_INTERNAL_ROW_COLS as readonly string[]).includes(name)
|
|
||||||
}
|
|
||||||
|
|
|
@ -17,6 +17,8 @@
|
||||||
SWITCHABLE_TYPES,
|
SWITCHABLE_TYPES,
|
||||||
ValidColumnNameRegex,
|
ValidColumnNameRegex,
|
||||||
helpers,
|
helpers,
|
||||||
|
CONSTANT_INTERNAL_ROW_COLS,
|
||||||
|
CONSTANT_EXTERNAL_ROW_COLS,
|
||||||
} from "@budibase/shared-core"
|
} from "@budibase/shared-core"
|
||||||
import { createEventDispatcher, getContext, onMount } from "svelte"
|
import { createEventDispatcher, getContext, onMount } from "svelte"
|
||||||
import { cloneDeep } from "lodash/fp"
|
import { cloneDeep } from "lodash/fp"
|
||||||
|
@ -487,20 +489,27 @@
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
const newError = {}
|
const newError = {}
|
||||||
|
const prohibited = externalTable
|
||||||
|
? CONSTANT_EXTERNAL_ROW_COLS
|
||||||
|
: CONSTANT_INTERNAL_ROW_COLS
|
||||||
if (!externalTable && fieldInfo.name?.startsWith("_")) {
|
if (!externalTable && fieldInfo.name?.startsWith("_")) {
|
||||||
newError.name = `Column name cannot start with an underscore.`
|
newError.name = `Column name cannot start with an underscore.`
|
||||||
} else if (fieldInfo.name && !fieldInfo.name.match(ValidColumnNameRegex)) {
|
} else if (fieldInfo.name && !fieldInfo.name.match(ValidColumnNameRegex)) {
|
||||||
newError.name = `Illegal character; must be alpha-numeric.`
|
newError.name = `Illegal character; must be alpha-numeric.`
|
||||||
} else if (PROHIBITED_COLUMN_NAMES.some(name => fieldInfo.name === name)) {
|
} else if (
|
||||||
newError.name = `${PROHIBITED_COLUMN_NAMES.join(
|
prohibited.some(
|
||||||
|
name => fieldInfo?.name?.toLowerCase() === name.toLowerCase()
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
newError.name = `${prohibited.join(
|
||||||
", "
|
", "
|
||||||
)} are not allowed as column names`
|
)} are not allowed as column names - case insensitive.`
|
||||||
} else if (inUse($tables.selected, fieldInfo.name, originalName)) {
|
} else if (inUse($tables.selected, fieldInfo.name, originalName)) {
|
||||||
newError.name = `Column name already in use.`
|
newError.name = `Column name already in use.`
|
||||||
}
|
}
|
||||||
|
|
||||||
if (fieldInfo.type === FieldType.AUTO && !fieldInfo.subtype) {
|
if (fieldInfo.type === FieldType.AUTO && !fieldInfo.subtype) {
|
||||||
newError.subtype = `Auto Column requires a type`
|
newError.subtype = `Auto Column requires a type.`
|
||||||
}
|
}
|
||||||
|
|
||||||
if (fieldInfo.fieldName && fieldInfo.tableId) {
|
if (fieldInfo.fieldName && fieldInfo.tableId) {
|
||||||
|
|
|
@ -16,7 +16,8 @@ import { EventType, updateLinks } from "../../../../db/linkedRows"
|
||||||
import { cloneDeep } from "lodash/fp"
|
import { cloneDeep } from "lodash/fp"
|
||||||
import isEqual from "lodash/isEqual"
|
import isEqual from "lodash/isEqual"
|
||||||
import { runStaticFormulaChecks } from "../../../../api/controllers/table/bulkFormula"
|
import { runStaticFormulaChecks } from "../../../../api/controllers/table/bulkFormula"
|
||||||
import { context } from "@budibase/backend-core"
|
import { context, db as dbCore } from "@budibase/backend-core"
|
||||||
|
import { findDuplicateInternalColumns } from "@budibase/shared-core"
|
||||||
import { getTable } from "../getters"
|
import { getTable } from "../getters"
|
||||||
import { checkAutoColumns } from "./utils"
|
import { checkAutoColumns } from "./utils"
|
||||||
import * as viewsSdk from "../../views"
|
import * as viewsSdk from "../../views"
|
||||||
|
@ -44,6 +45,15 @@ export async function save(
|
||||||
if (hasTypeChanged(table, oldTable)) {
|
if (hasTypeChanged(table, oldTable)) {
|
||||||
throw new Error("A column type has changed.")
|
throw new Error("A column type has changed.")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// check for case sensitivity - we don't want to allow duplicated columns
|
||||||
|
const duplicateColumn = findDuplicateInternalColumns(table)
|
||||||
|
if (duplicateColumn) {
|
||||||
|
throw new Error(
|
||||||
|
`Column "${duplicateColumn}" is duplicated - make sure there are no duplicate columns names, this is case insensitive.`
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
// check that subtypes have been maintained
|
// check that subtypes have been maintained
|
||||||
table = checkAutoColumns(table, oldTable)
|
table = checkAutoColumns(table, oldTable)
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
export * from "./api"
|
export * from "./api"
|
||||||
export * from "./fields"
|
export * from "./fields"
|
||||||
|
export * from "./rows"
|
||||||
|
|
||||||
export const OperatorOptions = {
|
export const OperatorOptions = {
|
||||||
Equals: {
|
Equals: {
|
||||||
|
|
|
@ -0,0 +1,14 @@
|
||||||
|
export const CONSTANT_INTERNAL_ROW_COLS = [
|
||||||
|
"_id",
|
||||||
|
"_rev",
|
||||||
|
"type",
|
||||||
|
"createdAt",
|
||||||
|
"updatedAt",
|
||||||
|
"tableId",
|
||||||
|
] as const
|
||||||
|
|
||||||
|
export const CONSTANT_EXTERNAL_ROW_COLS = ["_id", "_rev", "tableId"] as const
|
||||||
|
|
||||||
|
export function isInternalColumnName(name: string): boolean {
|
||||||
|
return (CONSTANT_INTERNAL_ROW_COLS as readonly string[]).includes(name)
|
||||||
|
}
|
|
@ -1,4 +1,5 @@
|
||||||
import { FieldType } from "@budibase/types"
|
import { FieldType, Table } from "@budibase/types"
|
||||||
|
import { CONSTANT_INTERNAL_ROW_COLS } from "./constants"
|
||||||
|
|
||||||
const allowDisplayColumnByType: Record<FieldType, boolean> = {
|
const allowDisplayColumnByType: Record<FieldType, boolean> = {
|
||||||
[FieldType.STRING]: true,
|
[FieldType.STRING]: true,
|
||||||
|
@ -51,3 +52,22 @@ export function canBeDisplayColumn(type: FieldType): boolean {
|
||||||
export function canBeSortColumn(type: FieldType): boolean {
|
export function canBeSortColumn(type: FieldType): boolean {
|
||||||
return !!allowSortColumnByType[type]
|
return !!allowSortColumnByType[type]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function findDuplicateInternalColumns(table: Table): string | undefined {
|
||||||
|
// get the column names
|
||||||
|
const columnNames = Object.keys(table.schema)
|
||||||
|
.concat(CONSTANT_INTERNAL_ROW_COLS)
|
||||||
|
.map(colName => colName.toLowerCase())
|
||||||
|
// there are duplicates
|
||||||
|
const set = new Set(columnNames)
|
||||||
|
let foundDuplicate: string | undefined
|
||||||
|
if (set.size !== columnNames.length) {
|
||||||
|
for (let key of set.keys()) {
|
||||||
|
const count = columnNames.filter(name => name === key).length
|
||||||
|
if (count > 1) {
|
||||||
|
foundDuplicate = key
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return foundDuplicate
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue