From a6f6942288f674a902de77220d030e8cb4d81f6a Mon Sep 17 00:00:00 2001 From: mike12345567 Date: Thu, 3 Oct 2024 16:10:07 +0100 Subject: [PATCH 1/6] Fixing an issue with corrupt relationship records referencing rows which don't exist, this is a temporary measure as these relationships should be cleaned up correctly but for now ignore any which reference rows which no longer exist. --- .../backend-core/src/db/couch/DatabaseImpl.ts | 12 +++++++---- .../src/db/linkedRows/LinkController.ts | 20 ++++++++++--------- packages/types/src/sdk/db.ts | 2 +- 3 files changed, 20 insertions(+), 14 deletions(-) diff --git a/packages/backend-core/src/db/couch/DatabaseImpl.ts b/packages/backend-core/src/db/couch/DatabaseImpl.ts index 274c1b9e93..472e0f44ad 100644 --- a/packages/backend-core/src/db/couch/DatabaseImpl.ts +++ b/packages/backend-core/src/db/couch/DatabaseImpl.ts @@ -213,17 +213,21 @@ export class DatabaseImpl implements Database { async getMultiple( ids: string[], - opts?: { allowMissing?: boolean } + opts?: { allowMissing?: boolean; excludeDocs?: boolean } ): Promise { // get unique ids = [...new Set(ids)] + const includeDocs = !opts?.excludeDocs const response = await this.allDocs({ keys: ids, - include_docs: true, + include_docs: includeDocs, }) const rowUnavailable = (row: RowResponse) => { // row is deleted - key lookup can return this - if (row.doc == null || ("deleted" in row.value && row.value.deleted)) { + if ( + (includeDocs && row.doc == null) || + (row.value && "deleted" in row.value && row.value.deleted) + ) { return true } return row.error === "not_found" @@ -237,7 +241,7 @@ export class DatabaseImpl implements Database { const missingIds = missing.map(row => row.key).join(", ") throw new Error(`Unable to get documents: ${missingIds}`) } - return rows.map(row => row.doc!) + return rows.map(row => (includeDocs ? row.doc! : row.value)) } async remove(idOrDoc: string | Document, rev?: string) { diff --git a/packages/server/src/db/linkedRows/LinkController.ts b/packages/server/src/db/linkedRows/LinkController.ts index 85a160713b..3c0601c9ee 100644 --- a/packages/server/src/db/linkedRows/LinkController.ts +++ b/packages/server/src/db/linkedRows/LinkController.ts @@ -211,19 +211,21 @@ class LinkController { linkedSchema?.type === FieldType.LINK && linkedSchema?.relationshipType === RelationshipType.ONE_TO_MANY ) { - let links = ( - await getLinkDocuments({ - tableId: field.tableId, - rowId: linkId, - }) - ).filter( - link => - link.id !== row._id && link.fieldName === linkedSchema.name + let links = await getLinkDocuments({ + tableId: field.tableId, + rowId: linkId, + fieldName: linkedSchema.name, + }) + + // check all the related rows exist + const foundRecords = await this._db.getMultiple( + links.map(l => l.id), + { allowMissing: true, excludeDocs: true } ) // The 1 side of 1:N is already related to something else // You must remove the existing relationship - if (links.length > 0) { + if (foundRecords.length > 0) { throw new Error( `1:N Relationship Error: Record already linked to another.` ) diff --git a/packages/types/src/sdk/db.ts b/packages/types/src/sdk/db.ts index a081f4f1a2..49baaa5bb1 100644 --- a/packages/types/src/sdk/db.ts +++ b/packages/types/src/sdk/db.ts @@ -133,7 +133,7 @@ export interface Database { exists(docId: string): Promise getMultiple( ids: string[], - opts?: { allowMissing?: boolean } + opts?: { allowMissing?: boolean; excludeDocs?: boolean } ): Promise remove(idOrDoc: Document): Promise remove(idOrDoc: string, rev?: string): Promise From 86846eff3f79192385f420b200b1a58b59a0042f Mon Sep 17 00:00:00 2001 From: mike12345567 Date: Thu, 3 Oct 2024 16:22:32 +0100 Subject: [PATCH 2/6] Small fix. --- packages/server/src/db/linkedRows/LinkController.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/packages/server/src/db/linkedRows/LinkController.ts b/packages/server/src/db/linkedRows/LinkController.ts index 3c0601c9ee..944542e73f 100644 --- a/packages/server/src/db/linkedRows/LinkController.ts +++ b/packages/server/src/db/linkedRows/LinkController.ts @@ -214,8 +214,10 @@ class LinkController { let links = await getLinkDocuments({ tableId: field.tableId, rowId: linkId, - fieldName: linkedSchema.name, - }) + }).filter( + link => + link.id !== row._id && link.fieldName === linkedSchema.name + ) // check all the related rows exist const foundRecords = await this._db.getMultiple( From 6fb844753b4ffcc7b3af857c723a0f193b2bc1f5 Mon Sep 17 00:00:00 2001 From: mike12345567 Date: Thu, 3 Oct 2024 16:24:54 +0100 Subject: [PATCH 3/6] Another small fix. --- packages/server/src/db/linkedRows/LinkController.ts | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/packages/server/src/db/linkedRows/LinkController.ts b/packages/server/src/db/linkedRows/LinkController.ts index 944542e73f..1cd4240d4b 100644 --- a/packages/server/src/db/linkedRows/LinkController.ts +++ b/packages/server/src/db/linkedRows/LinkController.ts @@ -211,10 +211,12 @@ class LinkController { linkedSchema?.type === FieldType.LINK && linkedSchema?.relationshipType === RelationshipType.ONE_TO_MANY ) { - let links = await getLinkDocuments({ - tableId: field.tableId, - rowId: linkId, - }).filter( + let links = ( + await getLinkDocuments({ + tableId: field.tableId, + rowId: linkId, + }) + ).filter( link => link.id !== row._id && link.fieldName === linkedSchema.name ) From abd7b3b84e5e1fda05e4987e34cc66072d33e347 Mon Sep 17 00:00:00 2001 From: Budibase Staging Release Bot <> Date: Wed, 9 Oct 2024 09:12:59 +0000 Subject: [PATCH 4/6] Bump version to 2.32.13 --- lerna.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lerna.json b/lerna.json index 55721b6b2d..faed02052e 100644 --- a/lerna.json +++ b/lerna.json @@ -1,6 +1,6 @@ { "$schema": "node_modules/lerna/schemas/lerna-schema.json", - "version": "2.32.12", + "version": "2.32.13", "npmClient": "yarn", "packages": [ "packages/*", From f05bf25e21e0246b9baa726d2ae71e7b5a09fa15 Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Wed, 9 Oct 2024 13:31:11 +0200 Subject: [PATCH 5/6] Add failing test --- packages/server/src/api/routes/tests/row.spec.ts | 12 +++++++++++- packages/server/src/tests/utilities/api/row.ts | 2 +- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/packages/server/src/api/routes/tests/row.spec.ts b/packages/server/src/api/routes/tests/row.spec.ts index 207420bf9f..0995bd3824 100644 --- a/packages/server/src/api/routes/tests/row.spec.ts +++ b/packages/server/src/api/routes/tests/row.spec.ts @@ -1846,7 +1846,7 @@ describe.each([ }) describe("exportRows", () => { - beforeAll(async () => { + beforeEach(async () => { table = await config.api.table.save(defaultTable()) }) @@ -1883,6 +1883,16 @@ describe.each([ }) }) + it("should allow exporting without filtering", async () => { + const existing = await config.api.row.save(table._id!, {}) + const res = await config.api.row.exportRows(table._id!) + const results = JSON.parse(res) + expect(results.length).toEqual(1) + const row = results[0] + + expect(row._id).toEqual(existing._id) + }) + it("should allow exporting only certain columns", async () => { const existing = await config.api.row.save(table._id!, {}) const res = await config.api.row.exportRows(table._id!, { diff --git a/packages/server/src/tests/utilities/api/row.ts b/packages/server/src/tests/utilities/api/row.ts index 6bec59fdf7..52317e142a 100644 --- a/packages/server/src/tests/utilities/api/row.ts +++ b/packages/server/src/tests/utilities/api/row.ts @@ -105,7 +105,7 @@ export class RowAPI extends TestAPI { exportRows = async ( tableId: string, - body: ExportRowsRequest, + body?: ExportRowsRequest, format: RowExportFormat = RowExportFormat.JSON, expectations?: Expectations ) => { From 865b7a97e0f6c7f96d39a84f22255dd8e565c6bf Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Wed, 9 Oct 2024 13:31:34 +0200 Subject: [PATCH 6/6] Fix --- packages/server/src/sdk/app/rows/search/internal/internal.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/server/src/sdk/app/rows/search/internal/internal.ts b/packages/server/src/sdk/app/rows/search/internal/internal.ts index 6617fc376c..0ca10e82d4 100644 --- a/packages/server/src/sdk/app/rows/search/internal/internal.ts +++ b/packages/server/src/sdk/app/rows/search/internal/internal.ts @@ -62,10 +62,10 @@ export async function exportRows( ).rows.map(row => row.doc!) result = await outputProcessing(table, response) - } else if (query) { + } else { let searchResponse = await sdk.rows.search({ tableId, - query, + query: query || {}, sort, sortOrder, })