diff --git a/packages/server/src/api/controllers/table/index.ts b/packages/server/src/api/controllers/table/index.ts index 36dee4811e..132bae3d81 100644 --- a/packages/server/src/api/controllers/table/index.ts +++ b/packages/server/src/api/controllers/table/index.ts @@ -11,7 +11,6 @@ import { BulkImportRequest, BulkImportResponse, FetchTablesResponse, - InternalTable, MigrateRequest, MigrateResponse, SaveTableRequest, @@ -19,8 +18,6 @@ import { Table, TableResponse, UserCtx, - isBBReferenceField, - isRelationshipField, } from "@budibase/types" import sdk from "../../../sdk" import { jsonFromCsvString } from "../../../utilities/csv" @@ -164,53 +161,8 @@ export async function validateExistingTableImport(ctx: UserCtx) { } } -function error(ctx: UserCtx, message: string, status = 400) { - ctx.status = status - ctx.body = { message } -} - export async function migrate(ctx: UserCtx) { const { tableId, oldColumn, newColumn } = ctx.request.body - - // For now we're only supporting migrations of user relationships to user - // columns in internal tables. In future we may want to support other - // migrations but for now return an error if we aren't migrating a user - // relationship. - if (isExternalTable(tableId)) { - return error(ctx, "External tables cannot be migrated") - } - const table = await sdk.tables.getTable(tableId) - - if (!(oldColumn.name in table.schema)) { - return error( - ctx, - `Column "${oldColumn.name}" does not exist on table "${table.name}"` - ) - } - - if (newColumn.name in table.schema) { - return error( - ctx, - `Column "${newColumn.name}" already exists on table "${table.name}"` - ) - } - - if (!isBBReferenceField(newColumn)) { - return error(ctx, `Column "${newColumn.name}" is not a user column`) - } - - if (newColumn.subtype !== "user" && newColumn.subtype !== "users") { - return error(ctx, `Column "${newColumn.name}" is not a user column`) - } - - if (!isRelationshipField(oldColumn)) { - return error(ctx, `Column "${oldColumn.name}" is not a user relationship`) - } - - if (oldColumn.tableId !== InternalTable.USER_METADATA) { - return error(ctx, `Column "${oldColumn.name}" is not a user relationship`) - } - - let rows = await sdk.rows.fetch(tableId) + await sdk.tables.migrate(table, oldColumn, newColumn) } diff --git a/packages/server/src/sdk/app/tables/index.ts b/packages/server/src/sdk/app/tables/index.ts index 64fcde4bff..e4884b2ec4 100644 --- a/packages/server/src/sdk/app/tables/index.ts +++ b/packages/server/src/sdk/app/tables/index.ts @@ -7,6 +7,7 @@ import { } from "../../../integrations/utils" import { Database, + FieldSchema, Table, TableResponse, TableViewsResponse, @@ -14,6 +15,7 @@ import { import datasources from "../datasources" import { populateExternalTableSchemas } from "./validation" import sdk from "../../../sdk" +import { migrate } from "./migration" async function getAllInternalTables(db?: Database): Promise { if (!db) { @@ -84,6 +86,14 @@ async function saveTable(table: Table) { } } +async function addColumn(table: Table, newColumn: FieldSchema) { + if (newColumn.name in table.schema) { + throw `Column "${newColumn.name}" already exists on table "${table.name}"` + } + table.schema[newColumn.name] = newColumn + await saveTable(table) +} + export default { getAllInternalTables, getAllExternalTables, @@ -92,4 +102,6 @@ export default { populateExternalTableSchemas, enrichViewSchemas, saveTable, + addColumn, + migrate, } diff --git a/packages/server/src/sdk/app/tables/migration.ts b/packages/server/src/sdk/app/tables/migration.ts new file mode 100644 index 0000000000..f727b5ea9d --- /dev/null +++ b/packages/server/src/sdk/app/tables/migration.ts @@ -0,0 +1,84 @@ +import { BadRequestError } from "@budibase/backend-core" +import { + BBReferenceFieldMetadata, + FieldSchema, + InternalTable, + RelationshipFieldMetadata, + Table, + isBBReferenceField, + isRelationshipField, +} from "@budibase/types" +import { isExternalTable } from "src/integrations/utils" +import sdk from "../../../sdk" + +export async function migrate( + table: Table, + oldColumn: FieldSchema, + newColumn: FieldSchema +) { + let migrator = getColumnMigrator(table, oldColumn, newColumn) + + await sdk.tables.addColumn(table, newColumn) + + migrator.doMigration() +} + +interface ColumnMigrator { + doMigration(): Promise +} + +function getColumnMigrator( + table: Table, + oldColumn: FieldSchema, + newColumn: FieldSchema +): ColumnMigrator { + // For now we're only supporting migrations of user relationships to user + // columns in internal tables. In future we may want to support other + // migrations but for now return an error if we aren't migrating a user + // relationship. + if (isExternalTable(table._id!)) { + throw new BadRequestError("External tables cannot be migrated") + } + + if (!(oldColumn.name in table.schema)) { + throw new BadRequestError(`Column "${oldColumn.name}" does not exist`) + } + + if (newColumn.name in table.schema) { + throw new BadRequestError(`Column "${newColumn.name}" already exists`) + } + + if (!isBBReferenceField(newColumn)) { + throw new BadRequestError(`Column "${newColumn.name}" is not a user column`) + } + + if (newColumn.subtype !== "user" && newColumn.subtype !== "users") { + throw new BadRequestError(`Column "${newColumn.name}" is not a user column`) + } + + if (!isRelationshipField(oldColumn)) { + throw new BadRequestError( + `Column "${oldColumn.name}" is not a user relationship` + ) + } + + if (oldColumn.tableId !== InternalTable.USER_METADATA) { + throw new BadRequestError( + `Column "${oldColumn.name}" is not a user relationship` + ) + } + + return new UserColumnMigrator(table, oldColumn, newColumn) +} + +class UserColumnMigrator implements ColumnMigrator { + constructor( + private table: Table, + private oldColumn: RelationshipFieldMetadata, + private newColumn: BBReferenceFieldMetadata + ) {} + + async doMigration() { + let rows = await sdk.rows.fetch(this.table._id!) + } +}