diff --git a/packages/backend-core/src/db/couch/DatabaseImpl.ts b/packages/backend-core/src/db/couch/DatabaseImpl.ts index b3fce7aa29..86fc06c24c 100644 --- a/packages/backend-core/src/db/couch/DatabaseImpl.ts +++ b/packages/backend-core/src/db/couch/DatabaseImpl.ts @@ -264,11 +264,16 @@ export class DatabaseImpl implements Database { if (body) { args.body = body } - const response = await directCouchUrlCall(args) - if (response.status > 300) { - throw new Error(await response.text()) - } - return (await response.json()) as T + return this.performCall(() => { + return async () => { + const response = await directCouchUrlCall(args) + const json = await response.json() + if (response.status > 300) { + throw json + } + return json as T + } + }) } async sql( diff --git a/packages/backend-core/src/db/instrumentation.ts b/packages/backend-core/src/db/instrumentation.ts index 715985766b..ef1c912e0b 100644 --- a/packages/backend-core/src/db/instrumentation.ts +++ b/packages/backend-core/src/db/instrumentation.ts @@ -62,7 +62,13 @@ export class DDInstrumentedDatabase implements Database { ): Promise { return tracer.trace("db.remove", span => { span?.addTags({ db_name: this.name, doc_id: id }) - return this.db.remove(id, rev) + if (typeof id === "object") { + return this.db.remove(id) + } else if (rev) { + return this.db.remove(id, rev) + } else { + throw new Error("No revision supplied for removal") + } }) } diff --git a/packages/backend-core/src/users/db.ts b/packages/backend-core/src/users/db.ts index f77c6385ba..37547573bd 100644 --- a/packages/backend-core/src/users/db.ts +++ b/packages/backend-core/src/users/db.ts @@ -492,7 +492,7 @@ export class UserDB { await platform.users.removeUser(dbUser) - await db.remove(userId, dbUser._rev) + await db.remove(userId, dbUser._rev!) const creatorsToDelete = (await isCreator(dbUser)) ? 1 : 0 await UserDB.quotas.removeUsers(1, creatorsToDelete) diff --git a/packages/server/src/api/controllers/application.ts b/packages/server/src/api/controllers/application.ts index e73058239b..6d28be9d8d 100644 --- a/packages/server/src/api/controllers/application.ts +++ b/packages/server/src/api/controllers/application.ts @@ -60,6 +60,7 @@ import sdk from "../../sdk" import { builderSocket } from "../../websockets" import { sdk as sharedCoreSDK } from "@budibase/shared-core" import * as appMigrations from "../../appMigrations" +import { cleanupApp } from "../../sdk/app/tables/internal/sqs" // utility function, need to do away with this async function getLayouts() { @@ -589,6 +590,9 @@ async function destroyApp(ctx: UserCtx) { } async function preDestroyApp(ctx: UserCtx) { + if (env.SQS_SEARCH_ENABLE) { + await sdk.tables.sqs.cleanupApp() + } const { rows } = await getUniqueRows([ctx.params.appId]) ctx.rowCount = rows.length } diff --git a/packages/server/src/api/controllers/table/utils.ts b/packages/server/src/api/controllers/table/utils.ts index dfbee2e4fc..a42cfc43c3 100644 --- a/packages/server/src/api/controllers/table/utils.ts +++ b/packages/server/src/api/controllers/table/utils.ts @@ -325,7 +325,7 @@ class TableSaveFunctions { user: this.user, }) if (env.SQS_SEARCH_ENABLE) { - await sdk.tables.sqs.addTableToSqlite(table) + await sdk.tables.sqs.addTable(table) } return table } @@ -519,7 +519,7 @@ export async function internalTableCleanup(table: Table, rows?: Row[]) { await AttachmentCleanup.tableDelete(table, rows) } if (env.SQS_SEARCH_ENABLE) { - await sdk.tables.sqs.removeTableFromSqlite(table) + await sdk.tables.sqs.removeTable(table) } } diff --git a/packages/server/src/sdk/app/rows/search/sqs.ts b/packages/server/src/sdk/app/rows/search/sqs.ts index dabccc4b55..d1230614a7 100644 --- a/packages/server/src/sdk/app/rows/search/sqs.ts +++ b/packages/server/src/sdk/app/rows/search/sqs.ts @@ -19,7 +19,7 @@ import { sqlOutputProcessing, } from "../../../../api/controllers/row/utils" import sdk from "../../../index" -import { context } from "@budibase/backend-core" +import { context, SQLITE_DESIGN_DOC_ID } from "@budibase/backend-core" import { CONSTANT_INTERNAL_ROW_COLS, SQS_DATASOURCE_INTERNAL, @@ -195,6 +195,10 @@ export async function search( } } catch (err: any) { const msg = typeof err === "string" ? err : err.message + if (err.status === 404 && err.message?.includes(SQLITE_DESIGN_DOC_ID)) { + await sdk.tables.sqs.syncDefinition() + return search(options, table) + } throw new Error(`Unable to search by SQL - ${msg}`, { cause: err }) } } diff --git a/packages/server/src/sdk/app/tables/internal/sqs.ts b/packages/server/src/sdk/app/tables/internal/sqs.ts index d10a7d447b..3d868b446a 100644 --- a/packages/server/src/sdk/app/tables/internal/sqs.ts +++ b/packages/server/src/sdk/app/tables/internal/sqs.ts @@ -15,7 +15,9 @@ import { generateJunctionTableID, } from "../../../../db/utils" -const BASIC_SQLITE_DOC: SQLiteDefinition = { +type PreSaveSQLiteDefinition = Omit + +const BASIC_SQLITE_DOC: PreSaveSQLiteDefinition = { _id: SQLITE_DESIGN_DOC_ID, language: "sqlite", sql: { @@ -102,7 +104,7 @@ function mapTable(table: Table): SQLiteTables { } // nothing exists, need to iterate though existing tables -async function buildBaseDefinition(): Promise { +async function buildBaseDefinition(): Promise { const tables = await tablesSdk.getAllInternalTables() const definition = cloneDeep(BASIC_SQLITE_DOC) for (let table of tables) { @@ -114,9 +116,15 @@ async function buildBaseDefinition(): Promise { return definition } -export async function addTableToSqlite(table: Table) { +export async function syncDefinition(): Promise { const db = context.getAppDB() - let definition: SQLiteDefinition + const definition = await buildBaseDefinition() + await db.put(definition) +} + +export async function addTable(table: Table) { + const db = context.getAppDB() + let definition: PreSaveSQLiteDefinition | SQLiteDefinition try { definition = await db.get(SQLITE_DESIGN_DOC_ID) } catch (err) { @@ -129,7 +137,7 @@ export async function addTableToSqlite(table: Table) { await db.put(definition) } -export async function removeTableFromSqlite(table: Table) { +export async function removeTable(table: Table) { const db = context.getAppDB() try { const definition = await db.get(SQLITE_DESIGN_DOC_ID) @@ -147,3 +155,18 @@ export async function removeTableFromSqlite(table: Table) { } } } + +export async function cleanupApp() { + const db = context.getAppDB() + if (!(await db.exists())) { + throw new Error("Cleanup must be preformed before app deletion.") + } + try { + const definition = await db.get(SQLITE_DESIGN_DOC_ID) + // delete the design document + await db.remove(SQLITE_DESIGN_DOC_ID, definition._rev) + await db.sqlCleanup() + } catch (err: any) { + throw new Error(`Unable to cleanup SQS files - ${err.message}`) + } +} diff --git a/packages/types/src/documents/app/sqlite.ts b/packages/types/src/documents/app/sqlite.ts index e23a68b336..c380a2d6b4 100644 --- a/packages/types/src/documents/app/sqlite.ts +++ b/packages/types/src/documents/app/sqlite.ts @@ -20,6 +20,7 @@ export type SQLiteTables = Record< export interface SQLiteDefinition { _id: string + _rev: string language: string sql: { tables: SQLiteTables diff --git a/packages/types/src/sdk/db.ts b/packages/types/src/sdk/db.ts index cd4d656a28..150a9a8c21 100644 --- a/packages/types/src/sdk/db.ts +++ b/packages/types/src/sdk/db.ts @@ -135,10 +135,8 @@ export interface Database { ids: string[], opts?: { allowMissing?: boolean } ): Promise - remove( - id: string | Document, - rev?: string - ): Promise + remove(idOrDoc: Document): Promise + remove(idOrDoc: string, rev: string): Promise put( document: AnyDocument, opts?: DatabasePutOpts