Catch a few more edge cases with column names, add tests for them.

This commit is contained in:
Sam Rose 2023-10-27 15:59:31 +01:00
parent d04f2198b6
commit 45543cbc03
No known key found for this signature in database
4 changed files with 123 additions and 17 deletions

View File

@ -8,3 +8,7 @@ export const CONSTANT_INTERNAL_ROW_COLS = [
] as const ] as const
export const CONSTANT_EXTERNAL_ROW_COLS = ["_id", "_rev", "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)
}

View File

@ -17,6 +17,9 @@
$: error = checkNewColumnName(newColumnName) $: error = checkNewColumnName(newColumnName)
const checkNewColumnName = newColumnName => { const checkNewColumnName = newColumnName => {
if (newColumnName === "") {
return "Column name can't be empty."
}
if (newColumnName in $definition.schema) { if (newColumnName in $definition.schema) {
return "New column name can't be the same as an existing column name." return "New column name can't be the same as an existing column name."
} }

View File

@ -10,11 +10,13 @@ import {
SaveTableRequest, SaveTableRequest,
Table, Table,
TableSourceType, TableSourceType,
User,
ViewCalculation, ViewCalculation,
} from "@budibase/types" } from "@budibase/types"
import { checkBuilderEndpoint } from "./utilities/TestFunctions" import { checkBuilderEndpoint } from "./utilities/TestFunctions"
import * as setup from "./utilities" import * as setup from "./utilities"
import sdk from "../../../sdk" import sdk from "../../../sdk"
import uuid from "uuid"
const { basicTable } = setup.structures const { basicTable } = setup.structures
@ -426,13 +428,16 @@ describe("/tables", () => {
}) })
describe("migrate", () => { describe("migrate", () => {
it("should successfully migrate a one-to-many user relationship to a user column", async () => { let users: User[]
const users = await Promise.all([ beforeAll(async () => {
config.createUser({ email: "1@example.com" }), users = await Promise.all([
config.createUser({ email: "2@example.com" }), config.createUser({ email: `${uuid.v4()}@example.com` }),
config.createUser({ email: "3@example.com" }), config.createUser({ email: `${uuid.v4()}@example.com` }),
config.createUser({ email: `${uuid.v4()}@example.com` }),
]) ])
})
it("should successfully migrate a one-to-many user relationship to a user column", async () => {
const table = await config.api.table.create({ const table = await config.api.table.create({
name: "table", name: "table",
type: "table", type: "table",
@ -488,12 +493,6 @@ describe("/tables", () => {
}) })
it("should successfully migrate a many-to-many user relationship to a users column", async () => { it("should successfully migrate a many-to-many user relationship to a users column", async () => {
const users = await Promise.all([
config.createUser({ email: "1@example.com" }),
config.createUser({ email: "2@example.com" }),
config.createUser({ email: "3@example.com" }),
])
const table = await config.api.table.create({ const table = await config.api.table.create({
name: "table", name: "table",
type: "table", type: "table",
@ -551,12 +550,6 @@ describe("/tables", () => {
}) })
it("should successfully migrate a many-to-one user relationship to a users column", async () => { it("should successfully migrate a many-to-one user relationship to a users column", async () => {
const users = await Promise.all([
config.createUser({ email: "1@example.com" }),
config.createUser({ email: "2@example.com" }),
config.createUser({ email: "3@example.com" }),
])
const table = await config.api.table.create({ const table = await config.api.table.create({
name: "table", name: "table",
type: "table", type: "table",
@ -612,5 +605,102 @@ describe("/tables", () => {
users[2]._id, users[2]._id,
]) ])
}) })
describe("unhappy paths", () => {
let table: Table
beforeAll(async () => {
table = await config.api.table.create({
name: "table",
type: "table",
sourceId: INTERNAL_TABLE_SOURCE_ID,
sourceType: TableSourceType.INTERNAL,
schema: {
"user relationship": {
type: FieldType.LINK,
fieldName: "test",
name: "user relationship",
constraints: {
type: "array",
presence: false,
},
relationshipType: RelationshipType.MANY_TO_ONE,
tableId: InternalTable.USER_METADATA,
},
num: {
type: FieldType.NUMBER,
name: "num",
constraints: {
type: "number",
presence: false,
},
},
},
})
})
it("should fail if the new column name is blank", async () => {
await config.api.table.migrate(
table._id!,
{
oldColumn: table.schema["user relationship"],
newColumn: {
name: "",
type: FieldType.BB_REFERENCE,
subtype: FieldSubtype.USERS,
},
},
{ expectStatus: 400 }
)
})
it("should fail if the new column name is a reserved name", async () => {
await config.api.table.migrate(
table._id!,
{
oldColumn: table.schema["user relationship"],
newColumn: {
name: "_id",
type: FieldType.BB_REFERENCE,
subtype: FieldSubtype.USERS,
},
},
{ expectStatus: 400 }
)
})
it("should fail if the new column name is the same as an existing column", async () => {
await config.api.table.migrate(
table._id!,
{
oldColumn: table.schema["user relationship"],
newColumn: {
name: "num",
type: FieldType.BB_REFERENCE,
subtype: FieldSubtype.USERS,
},
},
{ expectStatus: 400 }
)
})
it("should fail if the old column name isn't a column in the table", async () => {
await config.api.table.migrate(
table._id!,
{
oldColumn: {
name: "not a column",
type: FieldType.BB_REFERENCE,
subtype: FieldSubtype.USERS,
},
newColumn: {
name: "new column",
type: FieldType.BB_REFERENCE,
subtype: FieldSubtype.USERS,
},
},
{ expectStatus: 400 }
)
})
})
}) })
}) })

View File

@ -16,6 +16,7 @@ import sdk from "../../../sdk"
import { isExternalTableID } from "../../../integrations/utils" import { isExternalTableID } from "../../../integrations/utils"
import { EventType, updateLinks } from "../../../db/linkedRows" import { EventType, updateLinks } from "../../../db/linkedRows"
import { cloneDeep } from "lodash" import { cloneDeep } from "lodash"
import { isInternalColumnName } from "@budibase/backend-core/src/db"
export interface MigrationResult { export interface MigrationResult {
tablesUpdated: Table[] tablesUpdated: Table[]
@ -30,6 +31,14 @@ export async function migrate(
throw new BadRequestError(`Column "${newColumn.name}" already exists`) throw new BadRequestError(`Column "${newColumn.name}" already exists`)
} }
if (newColumn.name === "") {
throw new BadRequestError(`Column name cannot be empty`)
}
if (isInternalColumnName(newColumn.name)) {
throw new BadRequestError(`Column name cannot be a reserved column name`)
}
table.schema[newColumn.name] = newColumn table.schema[newColumn.name] = newColumn
table = await sdk.tables.saveTable(table) table = await sdk.tables.saveTable(table)