diff --git a/packages/server/src/api/controllers/table/index.ts b/packages/server/src/api/controllers/table/index.ts index 132bae3d81..75532dbfdf 100644 --- a/packages/server/src/api/controllers/table/index.ts +++ b/packages/server/src/api/controllers/table/index.ts @@ -162,7 +162,11 @@ export async function validateExistingTableImport(ctx: UserCtx) { } export async function migrate(ctx: UserCtx) { - const { tableId, oldColumn, newColumn } = ctx.request.body + const { oldColumn, newColumn } = ctx.request.body + let tableId = ctx.params.tableId as string const table = await sdk.tables.getTable(tableId) await sdk.tables.migrate(table, oldColumn, newColumn) + + ctx.status = 200 + ctx.body = { message: `Column ${oldColumn.name} migrated.` } } diff --git a/packages/server/src/api/routes/tests/table.spec.ts b/packages/server/src/api/routes/tests/table.spec.ts index ded54729b9..eaf3757ea8 100644 --- a/packages/server/src/api/routes/tests/table.spec.ts +++ b/packages/server/src/api/routes/tests/table.spec.ts @@ -6,6 +6,8 @@ import { Table, ViewCalculation, AutoFieldSubTypes, + InternalTable, + FieldSubtype, } from "@budibase/types" import { checkBuilderEndpoint } from "./utilities/TestFunctions" import * as setup from "./utilities" @@ -417,4 +419,46 @@ describe("/tables", () => { }) }) }) + + describe("migrate", () => { + it("should successfully migrate a user relationship to a user column", async () => { + const users = await config.api.row.fetch(InternalTable.USER_METADATA) + const table = await config.api.table.create({ + name: "table", + type: "table", + schema: { + "user relationship": { + type: FieldType.LINK, + fieldName: "test", + name: "user relationship", + constraints: { + type: "array", + presence: false, + }, + relationshipType: RelationshipType.ONE_TO_MANY, + tableId: InternalTable.USER_METADATA, + }, + }, + }) + + await config.api.row.save(table._id!, { + "user relationship": users, + }) + + await config.api.table.migrate(table._id!, { + oldColumn: table.schema["user relationship"], + newColumn: { + name: "user column", + type: FieldType.BB_REFERENCE, + subtype: FieldSubtype.USER, + }, + }) + + const migratedTable = await config.api.table.get(table._id!) + expect(migratedTable.schema["user column"]).toBeDefined() + + const rows = await config.api.row.fetch(table._id!) + expect(rows[0]["user column"]).toBeDefined() + }) + }) }) diff --git a/packages/server/src/sdk/app/links/index.ts b/packages/server/src/sdk/app/links/index.ts new file mode 100644 index 0000000000..6655a76656 --- /dev/null +++ b/packages/server/src/sdk/app/links/index.ts @@ -0,0 +1,5 @@ +import * as links from "./links" + +export default { + ...links, +} diff --git a/packages/server/src/sdk/app/links/links.ts b/packages/server/src/sdk/app/links/links.ts new file mode 100644 index 0000000000..7f3dcbd4ac --- /dev/null +++ b/packages/server/src/sdk/app/links/links.ts @@ -0,0 +1,32 @@ +import { context } from "@budibase/backend-core" +import { isTableId } from "@budibase/backend-core/src/docIds" +import { LinkDocument, LinkDocumentValue } from "@budibase/types" +import { ViewName, getQueryIndex } from "../../../../src/db/utils" + +export async function fetch(tableId: string): Promise { + if (!isTableId(tableId)) { + throw new Error(`Invalid tableId: ${tableId}`) + } + + const db = context.getAppDB() + const params: any = { startkey: [tableId], endkey: [tableId, {}] } + const linkRows = (await db.query(getQueryIndex(ViewName.LINK), params)).rows + return linkRows.map(row => row.value as LinkDocumentValue) +} + +export async function fetchWithDocument( + tableId: string +): Promise { + if (!isTableId(tableId)) { + throw new Error(`Invalid tableId: ${tableId}`) + } + + const db = context.getAppDB() + const params: any = { + startkey: [tableId], + endkey: [tableId, {}], + include_docs: true, + } + const linkRows = (await db.query(getQueryIndex(ViewName.LINK), params)).rows + return linkRows.map(row => row.doc as LinkDocument) +} diff --git a/packages/server/src/sdk/app/rows/search.ts b/packages/server/src/sdk/app/rows/search.ts index ced35db9be..f75bd07437 100644 --- a/packages/server/src/sdk/app/rows/search.ts +++ b/packages/server/src/sdk/app/rows/search.ts @@ -1,4 +1,4 @@ -import { SearchFilters, SearchParams, Row } from "@budibase/types" +import { SearchFilters, SearchParams } from "@budibase/types" import { isExternalTable } from "../../../integrations/utils" import * as internal from "./search/internal" import * as external from "./search/external" @@ -45,7 +45,7 @@ export async function exportRows( return pickApi(options.tableId).exportRows(options) } -export async function fetch(tableId: string): Promise { +export async function fetch(tableId: string) { return pickApi(tableId).fetch(tableId) } @@ -53,6 +53,6 @@ export async function fetchView( tableId: string, viewName: string, params: ViewParams -): Promise { +) { return pickApi(tableId).fetchView(viewName, params) } diff --git a/packages/server/src/sdk/app/tables/migration.ts b/packages/server/src/sdk/app/tables/migration.ts index f727b5ea9d..ac7f3a3765 100644 --- a/packages/server/src/sdk/app/tables/migration.ts +++ b/packages/server/src/sdk/app/tables/migration.ts @@ -8,8 +8,8 @@ import { isBBReferenceField, isRelationshipField, } from "@budibase/types" -import { isExternalTable } from "src/integrations/utils" import sdk from "../../../sdk" +import { isExternalTable } from "../../../../src/integrations/utils" export async function migrate( table: Table, @@ -80,5 +80,6 @@ class UserColumnMigrator implements ColumnMigrator { async doMigration() { let rows = await sdk.rows.fetch(this.table._id!) + let links = await sdk.links.fetchWithDocument(this.table._id!) } } diff --git a/packages/server/src/sdk/index.ts b/packages/server/src/sdk/index.ts index 24eb1ebf3c..c3057e3d4f 100644 --- a/packages/server/src/sdk/index.ts +++ b/packages/server/src/sdk/index.ts @@ -5,6 +5,7 @@ import { default as applications } from "./app/applications" import { default as datasources } from "./app/datasources" import { default as queries } from "./app/queries" import { default as rows } from "./app/rows" +import { default as links } from "./app/links" import { default as users } from "./users" import { default as plugins } from "./plugins" import * as views from "./app/views" @@ -22,6 +23,7 @@ const sdk = { plugins, views, permissions, + links, } // default export for TS diff --git a/packages/server/src/tests/utilities/api/table.ts b/packages/server/src/tests/utilities/api/table.ts index 04432a788a..501841f6e7 100644 --- a/packages/server/src/tests/utilities/api/table.ts +++ b/packages/server/src/tests/utilities/api/table.ts @@ -1,4 +1,10 @@ -import { SaveTableRequest, SaveTableResponse, Table } from "@budibase/types" +import { + MigrateRequest, + MigrateResponse, + SaveTableRequest, + SaveTableResponse, + Table, +} from "@budibase/types" import TestConfiguration from "../TestConfiguration" import { TestAPI } from "./base" @@ -42,4 +48,18 @@ export class TableAPI extends TestAPI { .expect(expectStatus) return res.body } + + migrate = async ( + tableId: string, + data: MigrateRequest, + { expectStatus } = { expectStatus: 200 } + ): Promise => { + const res = await this.request + .post(`/api/tables/${tableId}/migrate`) + .send(data) + .set(this.config.defaultHeaders()) + .expect("Content-Type", /json/) + .expect(expectStatus) + return res.body + } } diff --git a/packages/types/src/api/web/app/table.ts b/packages/types/src/api/web/app/table.ts index 61da12d041..f4d6720516 100644 --- a/packages/types/src/api/web/app/table.ts +++ b/packages/types/src/api/web/app/table.ts @@ -36,7 +36,6 @@ export interface BulkImportResponse { } export interface MigrateRequest { - tableId: string oldColumn: FieldSchema newColumn: FieldSchema }