diff --git a/packages/backend-core/src/cache/docWritethrough.ts b/packages/backend-core/src/cache/docWritethrough.ts index 51018b2317..1b129bb26a 100644 --- a/packages/backend-core/src/cache/docWritethrough.ts +++ b/packages/backend-core/src/cache/docWritethrough.ts @@ -10,6 +10,7 @@ interface ProcessDocMessage { } const PERSIST_MAX_ATTEMPTS = 100 +let processor: DocWritethroughProcessor | undefined export const docWritethroughProcessorQueue = createQueue( JobQueue.DOC_WRITETHROUGH_QUEUE, @@ -61,8 +62,6 @@ class DocWritethroughProcessor { } } -export const processor = new DocWritethroughProcessor().init() - export class DocWritethrough { private db: Database private _docId: string @@ -84,3 +83,15 @@ export class DocWritethrough { }) } } + +export function init(): DocWritethroughProcessor { + processor = new DocWritethroughProcessor().init() + return processor +} + +export function getProcessor(): DocWritethroughProcessor { + if (!processor) { + return init() + } + return processor +} diff --git a/packages/backend-core/src/cache/tests/docWritethrough.spec.ts b/packages/backend-core/src/cache/tests/docWritethrough.spec.ts index d90c83afd3..5fe09b95ff 100644 --- a/packages/backend-core/src/cache/tests/docWritethrough.spec.ts +++ b/packages/backend-core/src/cache/tests/docWritethrough.spec.ts @@ -7,6 +7,7 @@ import { getDB } from "../../db" import { DocWritethrough, docWritethroughProcessorQueue, + init, } from "../docWritethrough" import InMemoryQueue from "../../queue/inMemoryQueue" @@ -19,6 +20,10 @@ async function waitForQueueCompletion() { } describe("docWritethrough", () => { + beforeAll(() => { + init() + }) + const config = new DBTestConfiguration() const db = getDB(structures.db.id()) diff --git a/packages/server/src/api/controllers/public/utils.ts b/packages/server/src/api/controllers/public/utils.ts index 1d67b49e0d..ec1d7cfa47 100644 --- a/packages/server/src/api/controllers/public/utils.ts +++ b/packages/server/src/api/controllers/public/utils.ts @@ -1,11 +1,12 @@ import { context } from "@budibase/backend-core" import { isExternalTableID } from "../../../integrations/utils" import { APP_PREFIX, DocumentType } from "../../../db/utils" +import { Row } from "@budibase/types" export async function addRev( body: { _id?: string; _rev?: string }, tableId?: string -) { +): Promise { if (!body._id || (tableId && isExternalTableID(tableId))) { return body } diff --git a/packages/server/src/api/controllers/row/external.ts b/packages/server/src/api/controllers/row/external.ts index dc3b556c54..8ce9ff0f9d 100644 --- a/packages/server/src/api/controllers/row/external.ts +++ b/packages/server/src/api/controllers/row/external.ts @@ -128,7 +128,10 @@ export async function bulkDestroy(ctx: UserCtx) { ) } const responses = await Promise.all(promises) - return { response: { ok: true }, rows: responses.map(resp => resp.row) } + const finalRows = responses + .map(resp => resp.row) + .filter(row => row && row._id) + return { response: { ok: true }, rows: finalRows } } export async function fetchEnrichedRow(ctx: UserCtx) { diff --git a/packages/server/src/api/controllers/row/index.ts b/packages/server/src/api/controllers/row/index.ts index 54c294c42b..3466a4491d 100644 --- a/packages/server/src/api/controllers/row/index.ts +++ b/packages/server/src/api/controllers/row/index.ts @@ -131,7 +131,10 @@ async function processDeleteRowsRequest(ctx: UserCtx) { : fixRow(processedRow, ctx.params) }) - return await Promise.all(processedRows) + const responses = await Promise.allSettled(processedRows) + return responses + .filter(resp => resp.status === "fulfilled") + .map(resp => (resp as PromiseFulfilledResult).value) } async function deleteRows(ctx: UserCtx) { diff --git a/packages/server/src/api/routes/tests/row.spec.ts b/packages/server/src/api/routes/tests/row.spec.ts index de411f5397..a032f4324c 100644 --- a/packages/server/src/api/routes/tests/row.spec.ts +++ b/packages/server/src/api/routes/tests/row.spec.ts @@ -636,6 +636,17 @@ describe.each([ expect(res[0]._id).toEqual(createdRow._id) await assertRowUsage(rowUsage - 1) }) + + it("should be able to bulk delete rows, including a row that doesn't exist", async () => { + const createdRow = await config.createRow() + + const res = await config.api.row.bulkDelete(table._id!, { + rows: [createdRow, { _id: "2" }], + }) + + expect(res[0]._id).toEqual(createdRow._id) + expect(res.length).toEqual(1) + }) }) describe("validate", () => { diff --git a/packages/server/src/startup.ts b/packages/server/src/startup.ts index abe931f503..2cedda1099 100644 --- a/packages/server/src/startup.ts +++ b/packages/server/src/startup.ts @@ -7,6 +7,7 @@ import { logging, tenancy, users, + cache, } from "@budibase/backend-core" import fs from "fs" import { watch } from "./watch" @@ -74,6 +75,7 @@ export async function startup(app?: Koa, server?: Server) { eventEmitter.emitPort(env.PORT) fileSystem.init() await redis.init() + cache.docWritethrough.init() eventInit() if (app && server) { initialiseWebsockets(app, server) diff --git a/packages/worker/src/index.ts b/packages/worker/src/index.ts index 3da9a42065..4e770c6ecb 100644 --- a/packages/worker/src/index.ts +++ b/packages/worker/src/index.ts @@ -17,6 +17,7 @@ import { env as coreEnv, timers, redis, + cache, } from "@budibase/backend-core" db.init() @@ -90,6 +91,7 @@ export default server.listen(parseInt(env.PORT || "4002"), async () => { console.log(`Worker running on ${JSON.stringify(server.address())}`) await initPro() await redis.clients.init() + cache.docWritethrough.init() // configure events to use the pro audit log write // can't integrate directly into backend-core due to cyclic issues await events.processors.init(proSdk.auditLogs.write)