From b3cd06944f4c941efc2da45c9cd84845fd076454 Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Thu, 22 Aug 2024 17:23:19 +0200 Subject: [PATCH 01/14] Enrich based on schema --- packages/server/src/db/linkedRows/index.ts | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/packages/server/src/db/linkedRows/index.ts b/packages/server/src/db/linkedRows/index.ts index 2da7e212b9..04755cd601 100644 --- a/packages/server/src/db/linkedRows/index.ts +++ b/packages/server/src/db/linkedRows/index.ts @@ -253,20 +253,33 @@ export async function squashLinksToPrimaryDisplay( // will populate this as we find them const linkedTables = [table] const isArray = Array.isArray(enriched) - let enrichedArray = !isArray ? [enriched] : enriched - for (let row of enrichedArray) { + const enrichedArray = !isArray ? [enriched] : enriched + for (const row of enrichedArray) { // this only fetches the table if its not already in array const rowTable = await getLinkedTable(row.tableId!, linkedTables) - for (let [column, schema] of Object.entries(rowTable?.schema || {})) { + const safeSchema = + (rowTable?.schema && + (await sdk.tables.enrichRelationshipSchema(rowTable.schema))) || + {} + for (let [column, schema] of Object.entries(safeSchema)) { if (schema.type !== FieldType.LINK || !Array.isArray(row[column])) { continue } const newLinks = [] - for (let link of row[column]) { + for (const link of row[column]) { const linkTblId = link.tableId || getRelatedTableForField(table, column) const linkedTable = await getLinkedTable(linkTblId!, linkedTables) const obj: any = { _id: link._id } obj.primaryDisplay = getPrimaryDisplayValue(link, linkedTable) + + if (schema.schema) { + for (const relField of Object.entries(schema.schema) + .filter(([_, field]) => field.visible !== false) + .map(([fieldKey]) => fieldKey)) { + obj[relField] = link[relField] + } + } + newLinks.push(obj) } row[column] = newLinks From c397dadd76b174368d6648f4b17eb7df21c08727 Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Thu, 22 Aug 2024 17:28:10 +0200 Subject: [PATCH 02/14] Add "flag" --- packages/server/src/db/linkedRows/index.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/server/src/db/linkedRows/index.ts b/packages/server/src/db/linkedRows/index.ts index 04755cd601..2394d00d0e 100644 --- a/packages/server/src/db/linkedRows/index.ts +++ b/packages/server/src/db/linkedRows/index.ts @@ -272,7 +272,8 @@ export async function squashLinksToPrimaryDisplay( const obj: any = { _id: link._id } obj.primaryDisplay = getPrimaryDisplayValue(link, linkedTable) - if (schema.schema) { + const allowRelationshipSchemas = true // TODO + if (schema.schema && allowRelationshipSchemas) { for (const relField of Object.entries(schema.schema) .filter(([_, field]) => field.visible !== false) .map(([fieldKey]) => fieldKey)) { From daa152183cf0565022a6229d65b09020715535aa Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Fri, 23 Aug 2024 10:42:43 +0200 Subject: [PATCH 03/14] Use isvisible for populating schemas instead of readonly --- packages/server/src/sdk/app/tables/getters.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/server/src/sdk/app/tables/getters.ts b/packages/server/src/sdk/app/tables/getters.ts index f3e2e97795..b85add114e 100644 --- a/packages/server/src/sdk/app/tables/getters.ts +++ b/packages/server/src/sdk/app/tables/getters.ts @@ -171,9 +171,10 @@ export async function enrichRelationshipSchema( continue } + const isVisible = !!fieldSchema[relTableFieldName]?.visible const isReadonly = !!fieldSchema[relTableFieldName]?.readonly resultSchema[relTableFieldName] = { - visible: isReadonly, + visible: isVisible, readonly: isReadonly, } } From c77ec0d17afed03eb45a691bf33c4daada10931a Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Fri, 23 Aug 2024 10:42:57 +0200 Subject: [PATCH 04/14] Exclude formulas --- packages/server/src/sdk/app/tables/getters.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/server/src/sdk/app/tables/getters.ts b/packages/server/src/sdk/app/tables/getters.ts index b85add114e..4ce54adc30 100644 --- a/packages/server/src/sdk/app/tables/getters.ts +++ b/packages/server/src/sdk/app/tables/getters.ts @@ -163,7 +163,7 @@ export async function enrichRelationshipSchema( for (const relTableFieldName of Object.keys(relTable.schema)) { const relTableField = relTable.schema[relTableFieldName] - if (relTableField.type === FieldType.LINK) { + if ([FieldType.LINK, FieldType.FORMULA].includes(relTableField.type)) { continue } From a3bbbb32be448ec0c44e7c4d8bd7a7e1a914e0da Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Fri, 23 Aug 2024 10:47:26 +0200 Subject: [PATCH 05/14] Add test --- .../server/src/api/routes/tests/row.spec.ts | 182 ++++++++++++++++++ 1 file changed, 182 insertions(+) diff --git a/packages/server/src/api/routes/tests/row.spec.ts b/packages/server/src/api/routes/tests/row.spec.ts index 95e714fbd6..2c4626a661 100644 --- a/packages/server/src/api/routes/tests/row.spec.ts +++ b/packages/server/src/api/routes/tests/row.spec.ts @@ -2407,6 +2407,188 @@ describe.each([ }) }) + describe("relationships", () => { + let tableId: string + + let auxData: Row[] = [] + + beforeAll(async () => { + const aux2Table = await config.api.table.save(defaultTable()) + const aux2Data = await config.api.row.save(aux2Table._id!, {}) + + const auxTable = await config.api.table.save( + defaultTable({ + schema: { + name: { + name: "name", + type: FieldType.STRING, + constraints: { presence: true }, + }, + age: { + name: "age", + type: FieldType.NUMBER, + constraints: { presence: true }, + }, + address: { + name: "address", + type: FieldType.STRING, + constraints: { presence: true }, + visible: false, + }, + link: { + name: "link", + type: FieldType.LINK, + tableId: aux2Table._id!, + relationshipType: RelationshipType.MANY_TO_MANY, + fieldName: "fk_aux", + constraints: { presence: true }, + }, + formula: { + name: "formula", + type: FieldType.FORMULA, + formula: "{{ any }}", + constraints: { presence: true }, + }, + }, + }) + ) + const auxTableId = auxTable._id! + + for (const name of generator.unique(() => generator.name(), 10)) { + auxData.push( + await config.api.row.save(auxTableId, { + name, + age: generator.age(), + address: generator.address(), + link: [aux2Data], + }) + ) + } + + const table = await config.api.table.save( + defaultTable({ + schema: { + title: { + name: "title", + type: FieldType.STRING, + constraints: { presence: true }, + }, + relWithNoSchema: { + name: "relWithNoSchema", + relationshipType: RelationshipType.ONE_TO_MANY, + type: FieldType.LINK, + tableId: auxTableId, + fieldName: "fk_relWithNoSchema", + constraints: { presence: true }, + }, + relWithEmptySchema: { + name: "relWithEmptySchema", + relationshipType: RelationshipType.ONE_TO_MANY, + type: FieldType.LINK, + tableId: auxTableId, + fieldName: "fk_relWithEmptySchema", + constraints: { presence: true }, + schema: {}, + }, + relWithFullSchema: { + name: "relWithFullSchema", + relationshipType: RelationshipType.ONE_TO_MANY, + type: FieldType.LINK, + tableId: auxTableId, + fieldName: "fk_relWithFullSchema", + constraints: { presence: true }, + schema: Object.keys(auxTable.schema).reduce( + (acc, c) => ({ ...acc, [c]: { visible: true } }), + {} + ), + }, + relWithHalfSchema: { + name: "relWithHalfSchema", + relationshipType: RelationshipType.ONE_TO_MANY, + type: FieldType.LINK, + tableId: auxTableId, + fieldName: "fk_relWithHalfSchema", + constraints: { presence: true }, + schema: { + name: { visible: true }, + age: { visible: false, readonly: true }, + }, + }, + relWithIllegalSchema: { + name: "relWithIllegalSchema", + relationshipType: RelationshipType.ONE_TO_MANY, + type: FieldType.LINK, + tableId: auxTableId, + fieldName: "fk_relWithIllegalSchema", + constraints: { presence: true }, + schema: { + name: { visible: true }, + address: { visible: true }, + unexisting: { visible: true }, + }, + }, + }, + }) + ) + tableId = table._id! + }) + + it("can retrieve rows with populated relationships", async () => { + const otherRows = _.sampleSize(auxData, 5) + + const row = await config.api.row.save(tableId, { + title: generator.word(), + relWithNoSchema: [otherRows[0]], + relWithEmptySchema: [otherRows[1]], + relWithFullSchema: [otherRows[2]], + relWithHalfSchema: [otherRows[3]], + relWithIllegalSchema: [otherRows[4]], + }) + + const retrieved = await config.api.row.get(tableId, row._id!) + expect(retrieved).toEqual( + expect.objectContaining({ + title: row.title, + relWithNoSchema: [ + { + _id: otherRows[0]._id, + primaryDisplay: otherRows[0].name, + }, + ], + relWithEmptySchema: [ + { + _id: otherRows[1]._id, + primaryDisplay: otherRows[1].name, + }, + ], + relWithFullSchema: [ + { + _id: otherRows[2]._id, + primaryDisplay: otherRows[2].name, + name: otherRows[2].name, + age: otherRows[2].age, + id: otherRows[2].id, + }, + ], + relWithHalfSchema: [ + { + _id: otherRows[3]._id, + primaryDisplay: otherRows[3].name, + name: otherRows[3].name, + }, + ], + relWithIllegalSchema: [ + { + _id: otherRows[4]._id, + primaryDisplay: otherRows[4].name, + name: otherRows[4].name, + }, + ], + }) + ) + }) + }) + describe("Formula fields", () => { let table: Table let otherTable: Table From 329fefc7dd4ccb779af590484848a39ebf6b78dc Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Fri, 23 Aug 2024 10:55:46 +0200 Subject: [PATCH 06/14] Prepare reusing --- .../server/src/api/routes/tests/row.spec.ts | 109 +++++++++--------- 1 file changed, 57 insertions(+), 52 deletions(-) diff --git a/packages/server/src/api/routes/tests/row.spec.ts b/packages/server/src/api/routes/tests/row.spec.ts index 2c4626a661..648b05c209 100644 --- a/packages/server/src/api/routes/tests/row.spec.ts +++ b/packages/server/src/api/routes/tests/row.spec.ts @@ -2533,60 +2533,65 @@ describe.each([ tableId = table._id! }) - it("can retrieve rows with populated relationships", async () => { - const otherRows = _.sampleSize(auxData, 5) + it.each([ + ["get row", (rowId: string) => config.api.row.get(tableId, rowId)], + ])( + "can retrieve rows with populated relationships (via %s)", + async (__, retrieveDelegate) => { + const otherRows = _.sampleSize(auxData, 5) - const row = await config.api.row.save(tableId, { - title: generator.word(), - relWithNoSchema: [otherRows[0]], - relWithEmptySchema: [otherRows[1]], - relWithFullSchema: [otherRows[2]], - relWithHalfSchema: [otherRows[3]], - relWithIllegalSchema: [otherRows[4]], - }) - - const retrieved = await config.api.row.get(tableId, row._id!) - expect(retrieved).toEqual( - expect.objectContaining({ - title: row.title, - relWithNoSchema: [ - { - _id: otherRows[0]._id, - primaryDisplay: otherRows[0].name, - }, - ], - relWithEmptySchema: [ - { - _id: otherRows[1]._id, - primaryDisplay: otherRows[1].name, - }, - ], - relWithFullSchema: [ - { - _id: otherRows[2]._id, - primaryDisplay: otherRows[2].name, - name: otherRows[2].name, - age: otherRows[2].age, - id: otherRows[2].id, - }, - ], - relWithHalfSchema: [ - { - _id: otherRows[3]._id, - primaryDisplay: otherRows[3].name, - name: otherRows[3].name, - }, - ], - relWithIllegalSchema: [ - { - _id: otherRows[4]._id, - primaryDisplay: otherRows[4].name, - name: otherRows[4].name, - }, - ], + const row = await config.api.row.save(tableId, { + title: generator.word(), + relWithNoSchema: [otherRows[0]], + relWithEmptySchema: [otherRows[1]], + relWithFullSchema: [otherRows[2]], + relWithHalfSchema: [otherRows[3]], + relWithIllegalSchema: [otherRows[4]], }) - ) - }) + + const retrieved = await retrieveDelegate(row._id!) + expect(retrieved).toEqual( + expect.objectContaining({ + title: row.title, + relWithNoSchema: [ + { + _id: otherRows[0]._id, + primaryDisplay: otherRows[0].name, + }, + ], + relWithEmptySchema: [ + { + _id: otherRows[1]._id, + primaryDisplay: otherRows[1].name, + }, + ], + relWithFullSchema: [ + { + _id: otherRows[2]._id, + primaryDisplay: otherRows[2].name, + name: otherRows[2].name, + age: otherRows[2].age, + id: otherRows[2].id, + }, + ], + relWithHalfSchema: [ + { + _id: otherRows[3]._id, + primaryDisplay: otherRows[3].name, + name: otherRows[3].name, + }, + ], + relWithIllegalSchema: [ + { + _id: otherRows[4]._id, + primaryDisplay: otherRows[4].name, + name: otherRows[4].name, + }, + ], + }) + ) + } + ) }) describe("Formula fields", () => { From a28114a01cf9cace5c15e42562adffac5c9e4521 Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Fri, 23 Aug 2024 11:08:56 +0200 Subject: [PATCH 07/14] Extra tests --- .../server/src/api/routes/tests/row.spec.ts | 30 +++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/packages/server/src/api/routes/tests/row.spec.ts b/packages/server/src/api/routes/tests/row.spec.ts index 648b05c209..0484fcd81e 100644 --- a/packages/server/src/api/routes/tests/row.spec.ts +++ b/packages/server/src/api/routes/tests/row.spec.ts @@ -2535,6 +2535,36 @@ describe.each([ it.each([ ["get row", (rowId: string) => config.api.row.get(tableId, rowId)], + [ + "fetch", + async (rowId: string) => { + const rows = await config.api.row.fetch(tableId) + return rows.find(r => r._id === rowId) + }, + ], + [ + "search", + async (rowId: string) => { + const { rows } = await config.api.row.search(tableId) + return rows.find(r => r._id === rowId) + }, + ], + [ + "from view", + async (rowId: string) => { + const table = await config.api.table.get(tableId) + const view = await config.api.viewV2.create({ + name: generator.guid(), + tableId, + schema: Object.keys(table.schema).reduce( + (acc, c) => ({ ...acc, [c]: { visible: true } }), + {} + ), + }) + const { rows } = await config.api.viewV2.search(view.id) + return rows.find(r => r._id === rowId) + }, + ], ])( "can retrieve rows with populated relationships (via %s)", async (__, retrieveDelegate) => { From 02fdbae7ac84071a484f47b5a511f548c4814fed Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Fri, 23 Aug 2024 11:12:31 +0200 Subject: [PATCH 08/14] More tests --- .../server/src/api/routes/tests/row.spec.ts | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/packages/server/src/api/routes/tests/row.spec.ts b/packages/server/src/api/routes/tests/row.spec.ts index 0484fcd81e..2781f58823 100644 --- a/packages/server/src/api/routes/tests/row.spec.ts +++ b/packages/server/src/api/routes/tests/row.spec.ts @@ -2534,24 +2534,24 @@ describe.each([ }) it.each([ - ["get row", (rowId: string) => config.api.row.get(tableId, rowId)], + ["get row", (row: Row) => config.api.row.get(tableId, row._id!)], [ "fetch", - async (rowId: string) => { + async (row: Row) => { const rows = await config.api.row.fetch(tableId) - return rows.find(r => r._id === rowId) + return rows.find(r => r._id === row._id) }, ], [ "search", - async (rowId: string) => { + async (row: Row) => { const { rows } = await config.api.row.search(tableId) - return rows.find(r => r._id === rowId) + return rows.find(r => r._id === row._id) }, ], [ "from view", - async (rowId: string) => { + async (row: Row) => { const table = await config.api.table.get(tableId) const view = await config.api.viewV2.create({ name: generator.guid(), @@ -2562,9 +2562,10 @@ describe.each([ ), }) const { rows } = await config.api.viewV2.search(view.id) - return rows.find(r => r._id === rowId) + return rows.find(r => r._id === row._id!) }, ], + ["from original saved row", (row: Row) => row], ])( "can retrieve rows with populated relationships (via %s)", async (__, retrieveDelegate) => { @@ -2579,7 +2580,7 @@ describe.each([ relWithIllegalSchema: [otherRows[4]], }) - const retrieved = await retrieveDelegate(row._id!) + const retrieved = await retrieveDelegate(row) expect(retrieved).toEqual( expect.objectContaining({ title: row.title, From ccf734486fe0198668caf6139e220b15932b13f9 Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Fri, 23 Aug 2024 12:28:02 +0200 Subject: [PATCH 09/14] Test sqs --- .../server/src/api/routes/tests/row.spec.ts | 30 +++++++++++++++---- .../src/utilities/rowProcessor/index.ts | 11 ++++++- 2 files changed, 35 insertions(+), 6 deletions(-) diff --git a/packages/server/src/api/routes/tests/row.spec.ts b/packages/server/src/api/routes/tests/row.spec.ts index 2781f58823..a91ce60949 100644 --- a/packages/server/src/api/routes/tests/row.spec.ts +++ b/packages/server/src/api/routes/tests/row.spec.ts @@ -9,7 +9,13 @@ import { import tk from "timekeeper" import emitter from "../../../../src/events" import { outputProcessing } from "../../../utilities/rowProcessor" -import { context, InternalTable, tenancy } from "@budibase/backend-core" +import { + context, + InternalTable, + tenancy, + withEnv as withCoreEnv, + setEnv as setCoreEnv, +} from "@budibase/backend-core" import { quotas } from "@budibase/pro" import { AttachmentFieldMetadata, @@ -69,6 +75,7 @@ async function waitForEvent( describe.each([ ["internal", undefined], + ["sqs", undefined], [DatabaseName.POSTGRES, getDatasource(DatabaseName.POSTGRES)], [DatabaseName.MYSQL, getDatasource(DatabaseName.MYSQL)], [DatabaseName.SQL_SERVER, getDatasource(DatabaseName.SQL_SERVER)], @@ -76,6 +83,7 @@ describe.each([ [DatabaseName.ORACLE, getDatasource(DatabaseName.ORACLE)], ])("/rows (%s)", (providerType, dsProvider) => { const isInternal = dsProvider === undefined + const isSqs = providerType === "sqs" const isMSSQL = providerType === DatabaseName.SQL_SERVER const isOracle = providerType === DatabaseName.ORACLE const config = setup.getConfig() @@ -83,9 +91,17 @@ describe.each([ let table: Table let datasource: Datasource | undefined let client: Knex | undefined + let envCleanup: (() => void) | undefined beforeAll(async () => { - await config.init() + await withCoreEnv({ SQS_SEARCH_ENABLE: "true" }, () => config.init()) + if (isSqs) { + envCleanup = setCoreEnv({ + SQS_SEARCH_ENABLE: "true", + SQS_SEARCH_ENABLE_TENANTS: [config.getTenantId()], + }) + } + if (dsProvider) { const rawDatasource = await dsProvider datasource = await config.createDatasource({ @@ -97,6 +113,9 @@ describe.each([ afterAll(async () => { setup.afterAll() + if (envCleanup) { + envCleanup() + } }) function saveTableRequest( @@ -2413,11 +2432,12 @@ describe.each([ let auxData: Row[] = [] beforeAll(async () => { - const aux2Table = await config.api.table.save(defaultTable()) + const aux2Table = await config.api.table.save(saveTableRequest()) const aux2Data = await config.api.row.save(aux2Table._id!, {}) const auxTable = await config.api.table.save( - defaultTable({ + saveTableRequest({ + primaryDisplay: "name", schema: { name: { name: "name", @@ -2466,7 +2486,7 @@ describe.each([ } const table = await config.api.table.save( - defaultTable({ + saveTableRequest({ schema: { title: { name: "title", diff --git a/packages/server/src/utilities/rowProcessor/index.ts b/packages/server/src/utilities/rowProcessor/index.ts index 4b2fd83882..acbff40602 100644 --- a/packages/server/src/utilities/rowProcessor/index.ts +++ b/packages/server/src/utilities/rowProcessor/index.ts @@ -3,6 +3,7 @@ import { fixAutoColumnSubType, processFormulas } from "./utils" import { cache, context, + db, HTTPError, objectStore, utils, @@ -349,11 +350,19 @@ export async function outputProcessing( } // remove null properties to match internal API const isExternal = isExternalTableID(table._id!) - if (isExternal) { + if (isExternal || db.isSqsEnabledForTenant()) { for (const row of enriched) { for (const key of Object.keys(row)) { if (row[key] === null) { delete row[key] + } else if (row[key] && table.schema[key]?.type === FieldType.LINK) { + for (const link of row[key] || []) { + for (const linkKey of Object.keys(link)) { + if (link[linkKey] === null) { + delete link[linkKey] + } + } + } } } } From 85aa6361b2c4af9cd3f6912f093f94fe3836d146 Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Fri, 23 Aug 2024 12:35:43 +0200 Subject: [PATCH 10/14] Fix broken sqs tests --- packages/server/src/api/routes/tests/row.spec.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/server/src/api/routes/tests/row.spec.ts b/packages/server/src/api/routes/tests/row.spec.ts index a91ce60949..2922dc3939 100644 --- a/packages/server/src/api/routes/tests/row.spec.ts +++ b/packages/server/src/api/routes/tests/row.spec.ts @@ -83,6 +83,7 @@ describe.each([ [DatabaseName.ORACLE, getDatasource(DatabaseName.ORACLE)], ])("/rows (%s)", (providerType, dsProvider) => { const isInternal = dsProvider === undefined + const isLucene = providerType === "lucene" const isSqs = providerType === "sqs" const isMSSQL = providerType === DatabaseName.SQL_SERVER const isOracle = providerType === DatabaseName.ORACLE @@ -365,7 +366,7 @@ describe.each([ expect(ids).toEqual(expect.arrayContaining(sequence)) }) - isInternal && + isLucene && it("row values are coerced", async () => { const str: FieldSchema = { type: FieldType.STRING, From 3186179f0e17e3f6ae3db1eb44cafb1c49f4f2e8 Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Fri, 23 Aug 2024 12:48:34 +0200 Subject: [PATCH 11/14] Fix test --- .../utilities/rowProcessor/tests/outputProcessing.spec.ts | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/packages/server/src/utilities/rowProcessor/tests/outputProcessing.spec.ts b/packages/server/src/utilities/rowProcessor/tests/outputProcessing.spec.ts index fd48f46016..0d0049a36e 100644 --- a/packages/server/src/utilities/rowProcessor/tests/outputProcessing.spec.ts +++ b/packages/server/src/utilities/rowProcessor/tests/outputProcessing.spec.ts @@ -10,6 +10,14 @@ import { outputProcessing } from ".." import { generator, structures } from "@budibase/backend-core/tests" import * as bbReferenceProcessor from "../bbReferenceProcessor" +jest.mock("@budibase/backend-core", () => ({ + ...jest.requireActual("@budibase/backend-core"), + db: { + ...jest.requireActual("@budibase/backend-core").db, + isSqsEnabledForTenant: () => true, + }, +})) + jest.mock("../bbReferenceProcessor", (): typeof bbReferenceProcessor => ({ processInputBBReference: jest.fn(), processInputBBReferences: jest.fn(), From 266ae96a804f288e9570f10e7900383363752f19 Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Fri, 23 Aug 2024 13:54:55 +0200 Subject: [PATCH 12/14] Don't run for mssql and oracle --- .../server/src/api/routes/tests/row.spec.ts | 178 +++++++++--------- 1 file changed, 90 insertions(+), 88 deletions(-) diff --git a/packages/server/src/api/routes/tests/row.spec.ts b/packages/server/src/api/routes/tests/row.spec.ts index 2922dc3939..c179711fcf 100644 --- a/packages/server/src/api/routes/tests/row.spec.ts +++ b/packages/server/src/api/routes/tests/row.spec.ts @@ -2554,96 +2554,98 @@ describe.each([ tableId = table._id! }) - it.each([ - ["get row", (row: Row) => config.api.row.get(tableId, row._id!)], - [ - "fetch", - async (row: Row) => { - const rows = await config.api.row.fetch(tableId) - return rows.find(r => r._id === row._id) - }, - ], - [ - "search", - async (row: Row) => { - const { rows } = await config.api.row.search(tableId) - return rows.find(r => r._id === row._id) - }, - ], - [ - "from view", - async (row: Row) => { - const table = await config.api.table.get(tableId) - const view = await config.api.viewV2.create({ - name: generator.guid(), - tableId, - schema: Object.keys(table.schema).reduce( - (acc, c) => ({ ...acc, [c]: { visible: true } }), - {} - ), - }) - const { rows } = await config.api.viewV2.search(view.id) - return rows.find(r => r._id === row._id!) - }, - ], - ["from original saved row", (row: Row) => row], - ])( - "can retrieve rows with populated relationships (via %s)", - async (__, retrieveDelegate) => { - const otherRows = _.sampleSize(auxData, 5) + !isMSSQL && + !isOracle && + it.each([ + ["get row", (row: Row) => config.api.row.get(tableId, row._id!)], + [ + "fetch", + async (row: Row) => { + const rows = await config.api.row.fetch(tableId) + return rows.find(r => r._id === row._id) + }, + ], + [ + "search", + async (row: Row) => { + const { rows } = await config.api.row.search(tableId) + return rows.find(r => r._id === row._id) + }, + ], + [ + "from view", + async (row: Row) => { + const table = await config.api.table.get(tableId) + const view = await config.api.viewV2.create({ + name: generator.guid(), + tableId, + schema: Object.keys(table.schema).reduce( + (acc, c) => ({ ...acc, [c]: { visible: true } }), + {} + ), + }) + const { rows } = await config.api.viewV2.search(view.id) + return rows.find(r => r._id === row._id!) + }, + ], + ["from original saved row", (row: Row) => row], + ])( + "can retrieve rows with populated relationships (via %s)", + async (__, retrieveDelegate) => { + const otherRows = _.sampleSize(auxData, 5) - const row = await config.api.row.save(tableId, { - title: generator.word(), - relWithNoSchema: [otherRows[0]], - relWithEmptySchema: [otherRows[1]], - relWithFullSchema: [otherRows[2]], - relWithHalfSchema: [otherRows[3]], - relWithIllegalSchema: [otherRows[4]], - }) - - const retrieved = await retrieveDelegate(row) - expect(retrieved).toEqual( - expect.objectContaining({ - title: row.title, - relWithNoSchema: [ - { - _id: otherRows[0]._id, - primaryDisplay: otherRows[0].name, - }, - ], - relWithEmptySchema: [ - { - _id: otherRows[1]._id, - primaryDisplay: otherRows[1].name, - }, - ], - relWithFullSchema: [ - { - _id: otherRows[2]._id, - primaryDisplay: otherRows[2].name, - name: otherRows[2].name, - age: otherRows[2].age, - id: otherRows[2].id, - }, - ], - relWithHalfSchema: [ - { - _id: otherRows[3]._id, - primaryDisplay: otherRows[3].name, - name: otherRows[3].name, - }, - ], - relWithIllegalSchema: [ - { - _id: otherRows[4]._id, - primaryDisplay: otherRows[4].name, - name: otherRows[4].name, - }, - ], + const row = await config.api.row.save(tableId, { + title: generator.word(), + relWithNoSchema: [otherRows[0]], + relWithEmptySchema: [otherRows[1]], + relWithFullSchema: [otherRows[2]], + relWithHalfSchema: [otherRows[3]], + relWithIllegalSchema: [otherRows[4]], }) - ) - } - ) + + const retrieved = await retrieveDelegate(row) + expect(retrieved).toEqual( + expect.objectContaining({ + title: row.title, + relWithNoSchema: [ + { + _id: otherRows[0]._id, + primaryDisplay: otherRows[0].name, + }, + ], + relWithEmptySchema: [ + { + _id: otherRows[1]._id, + primaryDisplay: otherRows[1].name, + }, + ], + relWithFullSchema: [ + { + _id: otherRows[2]._id, + primaryDisplay: otherRows[2].name, + name: otherRows[2].name, + age: otherRows[2].age, + id: otherRows[2].id, + }, + ], + relWithHalfSchema: [ + { + _id: otherRows[3]._id, + primaryDisplay: otherRows[3].name, + name: otherRows[3].name, + }, + ], + relWithIllegalSchema: [ + { + _id: otherRows[4]._id, + primaryDisplay: otherRows[4].name, + name: otherRows[4].name, + }, + ], + }) + ) + } + ) }) describe("Formula fields", () => { From 99bd057160fe9b3e915f1e550b9401a9e69f1378 Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Fri, 23 Aug 2024 13:55:47 +0200 Subject: [PATCH 13/14] Add comment --- packages/server/src/api/routes/tests/row.spec.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/server/src/api/routes/tests/row.spec.ts b/packages/server/src/api/routes/tests/row.spec.ts index c179711fcf..c7604eb111 100644 --- a/packages/server/src/api/routes/tests/row.spec.ts +++ b/packages/server/src/api/routes/tests/row.spec.ts @@ -2554,6 +2554,8 @@ describe.each([ tableId = table._id! }) + // Upserting isn't yet supported in MSSQL or Oracle, see: + // https://github.com/knex/knex/pull/6050 !isMSSQL && !isOracle && it.each([ From 8929b4a336ffef45fdbac61d13ac7b4f0e0c4a10 Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Fri, 23 Aug 2024 14:15:23 +0200 Subject: [PATCH 14/14] Fix jest issues --- .../server/src/api/routes/tests/row.spec.ts | 244 +++++++++--------- 1 file changed, 122 insertions(+), 122 deletions(-) diff --git a/packages/server/src/api/routes/tests/row.spec.ts b/packages/server/src/api/routes/tests/row.spec.ts index c7604eb111..ca9ef134cb 100644 --- a/packages/server/src/api/routes/tests/row.spec.ts +++ b/packages/server/src/api/routes/tests/row.spec.ts @@ -2427,137 +2427,137 @@ describe.each([ }) }) - describe("relationships", () => { - let tableId: string + // Upserting isn't yet supported in MSSQL or Oracle, see: + // https://github.com/knex/knex/pull/6050 + !isMSSQL && + !isOracle && + describe("relationships", () => { + let tableId: string - let auxData: Row[] = [] + let auxData: Row[] = [] - beforeAll(async () => { - const aux2Table = await config.api.table.save(saveTableRequest()) - const aux2Data = await config.api.row.save(aux2Table._id!, {}) + beforeAll(async () => { + const aux2Table = await config.api.table.save(saveTableRequest()) + const aux2Data = await config.api.row.save(aux2Table._id!, {}) - const auxTable = await config.api.table.save( - saveTableRequest({ - primaryDisplay: "name", - schema: { - name: { - name: "name", - type: FieldType.STRING, - constraints: { presence: true }, + const auxTable = await config.api.table.save( + saveTableRequest({ + primaryDisplay: "name", + schema: { + name: { + name: "name", + type: FieldType.STRING, + constraints: { presence: true }, + }, + age: { + name: "age", + type: FieldType.NUMBER, + constraints: { presence: true }, + }, + address: { + name: "address", + type: FieldType.STRING, + constraints: { presence: true }, + visible: false, + }, + link: { + name: "link", + type: FieldType.LINK, + tableId: aux2Table._id!, + relationshipType: RelationshipType.MANY_TO_MANY, + fieldName: "fk_aux", + constraints: { presence: true }, + }, + formula: { + name: "formula", + type: FieldType.FORMULA, + formula: "{{ any }}", + constraints: { presence: true }, + }, }, - age: { - name: "age", - type: FieldType.NUMBER, - constraints: { presence: true }, - }, - address: { - name: "address", - type: FieldType.STRING, - constraints: { presence: true }, - visible: false, - }, - link: { - name: "link", - type: FieldType.LINK, - tableId: aux2Table._id!, - relationshipType: RelationshipType.MANY_TO_MANY, - fieldName: "fk_aux", - constraints: { presence: true }, - }, - formula: { - name: "formula", - type: FieldType.FORMULA, - formula: "{{ any }}", - constraints: { presence: true }, - }, - }, - }) - ) - const auxTableId = auxTable._id! - - for (const name of generator.unique(() => generator.name(), 10)) { - auxData.push( - await config.api.row.save(auxTableId, { - name, - age: generator.age(), - address: generator.address(), - link: [aux2Data], }) ) - } + const auxTableId = auxTable._id! - const table = await config.api.table.save( - saveTableRequest({ - schema: { - title: { - name: "title", - type: FieldType.STRING, - constraints: { presence: true }, - }, - relWithNoSchema: { - name: "relWithNoSchema", - relationshipType: RelationshipType.ONE_TO_MANY, - type: FieldType.LINK, - tableId: auxTableId, - fieldName: "fk_relWithNoSchema", - constraints: { presence: true }, - }, - relWithEmptySchema: { - name: "relWithEmptySchema", - relationshipType: RelationshipType.ONE_TO_MANY, - type: FieldType.LINK, - tableId: auxTableId, - fieldName: "fk_relWithEmptySchema", - constraints: { presence: true }, - schema: {}, - }, - relWithFullSchema: { - name: "relWithFullSchema", - relationshipType: RelationshipType.ONE_TO_MANY, - type: FieldType.LINK, - tableId: auxTableId, - fieldName: "fk_relWithFullSchema", - constraints: { presence: true }, - schema: Object.keys(auxTable.schema).reduce( - (acc, c) => ({ ...acc, [c]: { visible: true } }), - {} - ), - }, - relWithHalfSchema: { - name: "relWithHalfSchema", - relationshipType: RelationshipType.ONE_TO_MANY, - type: FieldType.LINK, - tableId: auxTableId, - fieldName: "fk_relWithHalfSchema", - constraints: { presence: true }, - schema: { - name: { visible: true }, - age: { visible: false, readonly: true }, + for (const name of generator.unique(() => generator.name(), 10)) { + auxData.push( + await config.api.row.save(auxTableId, { + name, + age: generator.age(), + address: generator.address(), + link: [aux2Data], + }) + ) + } + + const table = await config.api.table.save( + saveTableRequest({ + schema: { + title: { + name: "title", + type: FieldType.STRING, + constraints: { presence: true }, + }, + relWithNoSchema: { + name: "relWithNoSchema", + relationshipType: RelationshipType.ONE_TO_MANY, + type: FieldType.LINK, + tableId: auxTableId, + fieldName: "fk_relWithNoSchema", + constraints: { presence: true }, + }, + relWithEmptySchema: { + name: "relWithEmptySchema", + relationshipType: RelationshipType.ONE_TO_MANY, + type: FieldType.LINK, + tableId: auxTableId, + fieldName: "fk_relWithEmptySchema", + constraints: { presence: true }, + schema: {}, + }, + relWithFullSchema: { + name: "relWithFullSchema", + relationshipType: RelationshipType.ONE_TO_MANY, + type: FieldType.LINK, + tableId: auxTableId, + fieldName: "fk_relWithFullSchema", + constraints: { presence: true }, + schema: Object.keys(auxTable.schema).reduce( + (acc, c) => ({ ...acc, [c]: { visible: true } }), + {} + ), + }, + relWithHalfSchema: { + name: "relWithHalfSchema", + relationshipType: RelationshipType.ONE_TO_MANY, + type: FieldType.LINK, + tableId: auxTableId, + fieldName: "fk_relWithHalfSchema", + constraints: { presence: true }, + schema: { + name: { visible: true }, + age: { visible: false, readonly: true }, + }, + }, + relWithIllegalSchema: { + name: "relWithIllegalSchema", + relationshipType: RelationshipType.ONE_TO_MANY, + type: FieldType.LINK, + tableId: auxTableId, + fieldName: "fk_relWithIllegalSchema", + constraints: { presence: true }, + schema: { + name: { visible: true }, + address: { visible: true }, + unexisting: { visible: true }, + }, }, }, - relWithIllegalSchema: { - name: "relWithIllegalSchema", - relationshipType: RelationshipType.ONE_TO_MANY, - type: FieldType.LINK, - tableId: auxTableId, - fieldName: "fk_relWithIllegalSchema", - constraints: { presence: true }, - schema: { - name: { visible: true }, - address: { visible: true }, - unexisting: { visible: true }, - }, - }, - }, - }) - ) - tableId = table._id! - }) + }) + ) + tableId = table._id! + }) - // Upserting isn't yet supported in MSSQL or Oracle, see: - // https://github.com/knex/knex/pull/6050 - !isMSSQL && - !isOracle && it.each([ ["get row", (row: Row) => config.api.row.get(tableId, row._id!)], [ @@ -2648,7 +2648,7 @@ describe.each([ ) } ) - }) + }) describe("Formula fields", () => { let table: Table