diff --git a/packages/server/src/appMigrations/migrations/20240604153647_initial_sqs.ts b/packages/server/src/appMigrations/migrations/20240604153647_initial_sqs.ts index 05174b8cf1..6ad3c09c3c 100644 --- a/packages/server/src/appMigrations/migrations/20240604153647_initial_sqs.ts +++ b/packages/server/src/appMigrations/migrations/20240604153647_initial_sqs.ts @@ -17,15 +17,17 @@ const migration = async () => { // it already has the junction table ID - no need to migrate if (!linkDoc.tableId) { - linkDoc.tableId = new LinkDocumentImpl( + const newLink = new LinkDocumentImpl( linkDoc.doc1.tableId, linkDoc.doc1.fieldName, linkDoc.doc1.rowId, linkDoc.doc2.tableId, linkDoc.doc2.fieldName, linkDoc.doc2.rowId - ).tableId - docsToUpdate.push(linkDoc) + ) + newLink._id = linkDoc._id! + newLink._rev = linkDoc._rev + docsToUpdate.push(newLink) } } diff --git a/packages/server/src/appMigrations/migrations/tests/20240604153647_initial_sqs.spec.ts b/packages/server/src/appMigrations/migrations/tests/20240604153647_initial_sqs.spec.ts new file mode 100644 index 0000000000..64420d239f --- /dev/null +++ b/packages/server/src/appMigrations/migrations/tests/20240604153647_initial_sqs.spec.ts @@ -0,0 +1,116 @@ +import * as setup from "../../../api/routes/tests/utilities" +import { basicTable } from "../../../tests/utilities/structures" +import { + db as dbCore, + SQLITE_DESIGN_DOC_ID, + context, +} from "@budibase/backend-core" +import { + LinkDocument, + DocumentType, + SQLiteDefinition, + SQLiteType, +} from "@budibase/types" +import { + generateJunctionTableID, + generateLinkID, + generateRowID, +} from "../../../db/utils" +import migration from "../20240604153647_initial_sqs" + +const config = setup.getConfig() +let tableId: string + +function oldLinkDocInfo() { + const tableId1 = `${DocumentType.TABLE}_a`, + tableId2 = `${DocumentType.TABLE}_b` + return { + tableId1, + tableId2, + rowId1: generateRowID(tableId1, "b"), + rowId2: generateRowID(tableId2, "a"), + col1: "columnB", + col2: "columnA", + } +} + +function oldLinkDocID() { + const { tableId1, tableId2, rowId1, rowId2, col1, col2 } = oldLinkDocInfo() + return generateLinkID(tableId1, tableId2, rowId1, rowId2, col1, col2) +} + +function oldLinkDocument(): Omit { + const { tableId1, tableId2, rowId1, rowId2, col1, col2 } = oldLinkDocInfo() + return { + type: "link", + _id: oldLinkDocID(), + doc1: { + tableId: tableId1, + fieldName: col1, + rowId: rowId1, + }, + doc2: { + tableId: tableId2, + fieldName: col2, + rowId: rowId2, + }, + } +} + +async function sqsDisabled(cb: () => Promise) { + await config.withEnv({ SQS_SEARCH_ENABLE: "" }, cb) +} + +async function sqsEnabled(cb: () => Promise) { + await config.withEnv({ SQS_SEARCH_ENABLE: "1" }, cb) +} + +beforeAll(async () => { + await sqsDisabled(async () => { + await config.init() + const table = await config.api.table.save(basicTable()) + tableId = table._id! + const db = dbCore.getDB(config.appId!) + // old link document + await db.put(oldLinkDocument()) + }) +}) + +describe("SQS migration", () => { + it("test migration runs as expected against an older DB", async () => { + const db = dbCore.getDB(config.appId!) + // confirm nothing exists initially + await sqsDisabled(async () => { + let error: any | undefined + try { + await db.get(SQLITE_DESIGN_DOC_ID) + } catch (err: any) { + error = err + } + expect(error).toBeDefined() + expect(error.status).toBe(404) + }) + await sqsEnabled(async () => { + await context.doInAppContext(config.appId!, async () => { + await migration() + }) + const designDoc = await db.get(SQLITE_DESIGN_DOC_ID) + expect(designDoc.sql.tables).toBeDefined() + const mainTableDef = designDoc.sql.tables[tableId] + expect(mainTableDef).toBeDefined() + expect(mainTableDef.fields.name).toEqual(SQLiteType.TEXT) + expect(mainTableDef.fields.description).toEqual(SQLiteType.TEXT) + + const { tableId1, tableId2, rowId1, rowId2 } = oldLinkDocInfo() + const linkDoc = await db.get(oldLinkDocID()) + expect(linkDoc.tableId).toEqual( + generateJunctionTableID(tableId1, tableId2) + ) + // should have swapped the documents + expect(linkDoc.doc1.tableId).toEqual(tableId2) + expect(linkDoc.doc1.rowId).toEqual(rowId2) + expect(linkDoc.doc2.tableId).toEqual(tableId1) + expect(linkDoc.doc2.rowId).toEqual(rowId1) + }) + }) +}) diff --git a/packages/server/src/environment.ts b/packages/server/src/environment.ts index 2c91f1cb48..b44d7547a2 100644 --- a/packages/server/src/environment.ts +++ b/packages/server/src/environment.ts @@ -96,6 +96,7 @@ const environment = { DISABLE_THREADING: process.env.DISABLE_THREADING, DISABLE_AUTOMATION_LOGS: process.env.DISABLE_AUTOMATION_LOGS, DISABLE_RATE_LIMITING: process.env.DISABLE_RATE_LIMITING, + DISABLE_APP_MIGRATIONS: process.env.SKIP_APP_MIGRATIONS || false, MULTI_TENANCY: process.env.MULTI_TENANCY, ENABLE_ANALYTICS: process.env.ENABLE_ANALYTICS, SELF_HOSTED: process.env.SELF_HOSTED, diff --git a/packages/server/src/middleware/appMigrations.ts b/packages/server/src/middleware/appMigrations.ts index fdaeb3a03d..6ad356427b 100644 --- a/packages/server/src/middleware/appMigrations.ts +++ b/packages/server/src/middleware/appMigrations.ts @@ -1,13 +1,13 @@ import { UserCtx } from "@budibase/types" import { checkMissingMigrations } from "../appMigrations" -import { env } from "@budibase/backend-core" +import env from "../environment" export default async (ctx: UserCtx, next: any) => { const { appId } = ctx // migrations can be disabled via environment variable if you // need to completely disable migrations, e.g. for testing - if (env.SKIP_APP_MIGRATIONS) { + if (env.DISABLE_APP_MIGRATIONS) { return next() }