From 2365a6ecd5c5b7772baa189e5c4dd1e3370ff8f8 Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Mon, 2 Sep 2024 17:32:21 +0200 Subject: [PATCH 01/13] Add renaming column tests --- .../src/api/routes/tests/viewV2.spec.ts | 82 +++++++++++++++++++ 1 file changed, 82 insertions(+) diff --git a/packages/server/src/api/routes/tests/viewV2.spec.ts b/packages/server/src/api/routes/tests/viewV2.spec.ts index 356f01dee02..5ff8fed0bdc 100644 --- a/packages/server/src/api/routes/tests/viewV2.spec.ts +++ b/packages/server/src/api/routes/tests/viewV2.spec.ts @@ -18,6 +18,7 @@ import { ViewV2, SearchResponse, BasicOperator, + RelationshipType, } from "@budibase/types" import { generator, mocks } from "@budibase/backend-core/tests" import { DatabaseName, getDatasource } from "../../../integrations/tests/utils" @@ -1176,6 +1177,87 @@ describe.each([ } ) }) + + it("updating a column will update link columns configuration", async () => { + let 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 }, + }, + }, + }) + ) + + const table = await config.api.table.save( + saveTableRequest({ + schema: { + aux: { + name: "aux", + relationshipType: RelationshipType.ONE_TO_MANY, + type: FieldType.LINK, + tableId: auxTable._id!, + fieldName: "fk_aux", + constraints: { presence: true }, + }, + }, + }) + ) + + const view = await config.api.viewV2.create({ + name: "view a", + tableId: table._id!, + schema: { + aux: { + visible: true, + columns: { + name: { visible: true, readonly: true }, + age: { visible: true, readonly: true }, + }, + }, + }, + }) + + // Refetch autTable + auxTable = await config.api.table.get(auxTable._id!) + await config.api.table.save({ + ...auxTable, + schema: { + ...auxTable.schema, + // @ts-ignore deleting age to force the rename + age: undefined, + dob: { + name: "dob", + type: FieldType.NUMBER, + constraints: { presence: true }, + }, + }, + _rename: { old: "age", updated: "dob" }, + }) + + const updatedView = await config.api.viewV2.get(view.id) + expect(updatedView).toEqual( + expect.objectContaining({ + schema: expect.objectContaining({ + aux: expect.objectContaining({ + columns: { + id: { visible: false, readonly: false }, + name: { visible: true, readonly: true }, + dob: { visible: true, readonly: true }, + }, + }), + }), + }) + ) + }) }) }) }) From ae0e3cdb5950025b5ef28b433a10b622bb0fbffc Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Mon, 2 Sep 2024 18:36:44 +0200 Subject: [PATCH 02/13] Fix test for external --- packages/server/src/api/routes/tests/viewV2.spec.ts | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/packages/server/src/api/routes/tests/viewV2.spec.ts b/packages/server/src/api/routes/tests/viewV2.spec.ts index 5ff8fed0bdc..699d388a6d7 100644 --- a/packages/server/src/api/routes/tests/viewV2.spec.ts +++ b/packages/server/src/api/routes/tests/viewV2.spec.ts @@ -1199,18 +1199,25 @@ describe.each([ const table = await config.api.table.save( saveTableRequest({ + schema: {}, + }) + ) + await config.api.table.save({ + ...table, schema: { + ...table.schema, aux: { name: "aux", relationshipType: RelationshipType.ONE_TO_MANY, type: FieldType.LINK, tableId: auxTable._id!, fieldName: "fk_aux", - constraints: { presence: true }, + constraints: { type: "array" }, }, }, }) - ) + // Refetch auxTable + auxTable = await config.api.table.get(auxTable._id!) const view = await config.api.viewV2.create({ name: "view a", @@ -1226,8 +1233,6 @@ describe.each([ }, }) - // Refetch autTable - auxTable = await config.api.table.get(auxTable._id!) await config.api.table.save({ ...auxTable, schema: { From 74eb0357a4b7a926b5be996b742c1eff3c33dbb8 Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Tue, 3 Sep 2024 10:15:26 +0200 Subject: [PATCH 03/13] Rename link relationship fields --- .../server/src/api/controllers/table/index.ts | 3 ++ .../src/api/routes/tests/viewV2.spec.ts | 18 +++---- packages/server/src/sdk/app/views/index.ts | 48 +++++++++++++++++++ 3 files changed, 60 insertions(+), 9 deletions(-) diff --git a/packages/server/src/api/controllers/table/index.ts b/packages/server/src/api/controllers/table/index.ts index bb97a89f6d7..0e160770928 100644 --- a/packages/server/src/api/controllers/table/index.ts +++ b/packages/server/src/api/controllers/table/index.ts @@ -116,6 +116,9 @@ export async function save(ctx: UserCtx) { } else { await events.table.updated(savedTable) } + if (renaming) { + await sdk.views.renameLinkedViews(savedTable, renaming) + } if (isImport) { await events.table.imported(savedTable) } diff --git a/packages/server/src/api/routes/tests/viewV2.spec.ts b/packages/server/src/api/routes/tests/viewV2.spec.ts index 699d388a6d7..9d14a528156 100644 --- a/packages/server/src/api/routes/tests/viewV2.spec.ts +++ b/packages/server/src/api/routes/tests/viewV2.spec.ts @@ -1204,18 +1204,18 @@ describe.each([ ) await config.api.table.save({ ...table, - schema: { + schema: { ...table.schema, - aux: { - name: "aux", - relationshipType: RelationshipType.ONE_TO_MANY, - type: FieldType.LINK, - tableId: auxTable._id!, - fieldName: "fk_aux", + aux: { + name: "aux", + relationshipType: RelationshipType.ONE_TO_MANY, + type: FieldType.LINK, + tableId: auxTable._id!, + fieldName: "fk_aux", constraints: { type: "array" }, - }, }, - }) + }, + }) // Refetch auxTable auxTable = await config.api.table.get(auxTable._id!) diff --git a/packages/server/src/sdk/app/views/index.ts b/packages/server/src/sdk/app/views/index.ts index 9cd11712ed4..120b56183c4 100644 --- a/packages/server/src/sdk/app/views/index.ts +++ b/packages/server/src/sdk/app/views/index.ts @@ -1,6 +1,7 @@ import { FieldType, RelationSchemaField, + RelationshipFieldMetadata, RenameColumn, Table, TableSchema, @@ -252,3 +253,50 @@ export function syncSchema( return view } + +export async function renameLinkedViews(table: Table, renaming: RenameColumn) { + const relatedLinks: Record = {} + + for (const field of Object.values(table.schema)) { + if (field.type !== FieldType.LINK) { + continue + } + + relatedLinks[field.tableId] ??= [] + relatedLinks[field.tableId].push(field) + } + + const relatedTables = await sdk.tables.getTables(Object.keys(relatedLinks)) + for (const relatedTable of relatedTables) { + let toSave = false + const viewsV2 = Object.values(relatedTable.views || {}).filter( + sdk.views.isV2 + ) + if (!viewsV2) { + continue + } + + for (const view of viewsV2) { + for (const relField of Object.keys(view.schema || {}).filter(f => { + const tableField = relatedTable.schema[f] + if (tableField.type !== FieldType.LINK) { + return false + } + + return tableField.tableId === table._id + })) { + const columns = view.schema && view.schema[relField]?.columns + + if (columns && columns[renaming.old]) { + columns[renaming.updated] = columns[renaming.old] + delete columns[renaming.old] + toSave = true + } + } + } + + if (toSave) { + await sdk.tables.saveTable(relatedTable) + } + } +} From a9669c1c1d40af79a23e2d6cfc7919e4486b8497 Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Tue, 3 Sep 2024 10:40:29 +0200 Subject: [PATCH 04/13] Extra tests --- .../src/api/routes/tests/viewV2.spec.ts | 208 +++++++++++++++++- 1 file changed, 199 insertions(+), 9 deletions(-) diff --git a/packages/server/src/api/routes/tests/viewV2.spec.ts b/packages/server/src/api/routes/tests/viewV2.spec.ts index 9d14a528156..2949f9bd0fd 100644 --- a/packages/server/src/api/routes/tests/viewV2.spec.ts +++ b/packages/server/src/api/routes/tests/viewV2.spec.ts @@ -1183,17 +1183,87 @@ describe.each([ saveTableRequest({ primaryDisplay: "name", schema: { - name: { - name: "name", - type: FieldType.STRING, - constraints: { presence: true }, - }, - age: { - name: "age", - type: FieldType.NUMBER, - constraints: { presence: true }, + name: { name: "name", type: FieldType.STRING }, + age: { name: "age", type: FieldType.NUMBER }, + }, + }) + ) + + const table = await config.api.table.save( + saveTableRequest({ + schema: {}, + }) + ) + await config.api.table.save({ + ...table, + schema: { + ...table.schema, + aux: { + name: "aux", + relationshipType: RelationshipType.ONE_TO_MANY, + type: FieldType.LINK, + tableId: auxTable._id!, + fieldName: "fk_aux", + constraints: { type: "array" }, + }, + }, + }) + // Refetch auxTable + auxTable = await config.api.table.get(auxTable._id!) + + const view = await config.api.viewV2.create({ + name: "view a", + tableId: table._id!, + schema: { + aux: { + visible: true, + columns: { + name: { visible: true, readonly: true }, + age: { visible: true, readonly: true }, }, }, + }, + }) + + await config.api.table.save({ + ...auxTable, + schema: { + ...auxTable.schema, + // @ts-ignore deleting age to force the rename + age: undefined, + dob: { + name: "dob", + type: FieldType.NUMBER, + constraints: { presence: true }, + }, + }, + _rename: { old: "age", updated: "dob" }, + }) + + const updatedView = await config.api.viewV2.get(view.id) + expect(updatedView).toEqual( + expect.objectContaining({ + schema: expect.objectContaining({ + aux: expect.objectContaining({ + columns: { + id: { visible: false, readonly: false }, + name: { visible: true, readonly: true }, + dob: { visible: true, readonly: true }, + }, + }), + }), + }) + ) + }) + + it("handles multiple fields using the same table", async () => { + let auxTable = await config.api.table.save( + saveTableRequest({ + primaryDisplay: "name", + schema: { + name: { name: "name", type: FieldType.STRING }, + age: { name: "age", type: FieldType.NUMBER }, + }, }) ) @@ -1214,6 +1284,14 @@ describe.each([ fieldName: "fk_aux", constraints: { type: "array" }, }, + aux2: { + name: "aux2", + relationshipType: RelationshipType.ONE_TO_MANY, + type: FieldType.LINK, + tableId: auxTable._id!, + fieldName: "fk_aux2", + constraints: { type: "array" }, + }, }, }) // Refetch auxTable @@ -1230,6 +1308,13 @@ describe.each([ age: { visible: true, readonly: true }, }, }, + aux2: { + visible: true, + columns: { + name: { visible: true, readonly: true }, + age: { visible: true, readonly: true }, + }, + }, }, }) @@ -1259,6 +1344,111 @@ describe.each([ dob: { visible: true, readonly: true }, }, }), + aux2: expect.objectContaining({ + columns: { + id: { visible: false, readonly: false }, + name: { visible: true, readonly: true }, + dob: { visible: true, readonly: true }, + }, + }), + }), + }) + ) + }) + + it("does not rename columns with the same name but from other tables", async () => { + let auxTable = await config.api.table.save( + saveTableRequest({ + primaryDisplay: "name", + schema: { name: { name: "name", type: FieldType.STRING } }, + }) + ) + let aux2Table = await config.api.table.save( + saveTableRequest({ + primaryDisplay: "name", + schema: { name: { name: "name", type: FieldType.STRING } }, + }) + ) + + const table = await config.api.table.save( + saveTableRequest({ + schema: {}, + }) + ) + await config.api.table.save({ + ...table, + schema: { + ...table.schema, + aux: { + name: "aux", + relationshipType: RelationshipType.ONE_TO_MANY, + type: FieldType.LINK, + tableId: auxTable._id!, + fieldName: "fk_aux", + constraints: { type: "array" }, + }, + aux2: { + name: "aux2", + relationshipType: RelationshipType.ONE_TO_MANY, + type: FieldType.LINK, + tableId: aux2Table._id!, + fieldName: "fk_aux2", + constraints: { type: "array" }, + }, + }, + }) + // Refetch auxTable + auxTable = await config.api.table.get(auxTable._id!) + + const view = await config.api.viewV2.create({ + name: "view a", + tableId: table._id!, + schema: { + aux: { + visible: true, + columns: { + name: { visible: true, readonly: true }, + }, + }, + aux2: { + visible: true, + columns: { + name: { visible: true, readonly: true }, + }, + }, + }, + }) + + await config.api.table.save({ + ...auxTable, + schema: { + ...auxTable.schema, + // @ts-ignore deleting age to force the rename + name: undefined, + fullName: { + name: "fullName", + type: FieldType.STRING, + }, + }, + _rename: { old: "name", updated: "fullName" }, + }) + + const updatedView = await config.api.viewV2.get(view.id) + expect(updatedView).toEqual( + expect.objectContaining({ + schema: expect.objectContaining({ + aux: expect.objectContaining({ + columns: { + id: { visible: false, readonly: false }, + fullName: { visible: true, readonly: true }, + }, + }), + aux2: expect.objectContaining({ + columns: { + id: { visible: false, readonly: false }, + name: { visible: true, readonly: true }, + }, + }), }), }) ) From 6d24eba6790780942aa0ad738529f5890480352e Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Tue, 3 Sep 2024 10:42:27 +0200 Subject: [PATCH 05/13] Move describes --- packages/server/src/api/routes/tests/viewV2.spec.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/server/src/api/routes/tests/viewV2.spec.ts b/packages/server/src/api/routes/tests/viewV2.spec.ts index 2949f9bd0fd..87af53a9da3 100644 --- a/packages/server/src/api/routes/tests/viewV2.spec.ts +++ b/packages/server/src/api/routes/tests/viewV2.spec.ts @@ -1177,7 +1177,9 @@ describe.each([ } ) }) + }) + describe("foreign relationship columns", () => { it("updating a column will update link columns configuration", async () => { let auxTable = await config.api.table.save( saveTableRequest({ From e61b61974073a3b902fd7c6785a382472495ab32 Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Tue, 3 Sep 2024 10:43:44 +0200 Subject: [PATCH 06/13] Clean code --- packages/server/src/sdk/app/views/index.ts | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/packages/server/src/sdk/app/views/index.ts b/packages/server/src/sdk/app/views/index.ts index 120b56183c4..b90c308ead8 100644 --- a/packages/server/src/sdk/app/views/index.ts +++ b/packages/server/src/sdk/app/views/index.ts @@ -1,7 +1,6 @@ import { FieldType, RelationSchemaField, - RelationshipFieldMetadata, RenameColumn, Table, TableSchema, @@ -255,18 +254,20 @@ export function syncSchema( } export async function renameLinkedViews(table: Table, renaming: RenameColumn) { - const relatedLinks: Record = {} + const relatedTableIds = new Set() for (const field of Object.values(table.schema)) { if (field.type !== FieldType.LINK) { continue } - relatedLinks[field.tableId] ??= [] - relatedLinks[field.tableId].push(field) + relatedTableIds.add(field.tableId) + break } - const relatedTables = await sdk.tables.getTables(Object.keys(relatedLinks)) + const relatedTables = await sdk.tables.getTables( + Array.from(relatedTableIds.values()) + ) for (const relatedTable of relatedTables) { let toSave = false const viewsV2 = Object.values(relatedTable.views || {}).filter( From e364bf5d6526cce85185e33bfea4d597d1bc65cd Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Tue, 3 Sep 2024 11:00:15 +0200 Subject: [PATCH 07/13] Don't use `sdk.tables.getTables` (as it fails randomly) --- packages/server/src/sdk/app/views/index.ts | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/packages/server/src/sdk/app/views/index.ts b/packages/server/src/sdk/app/views/index.ts index b90c308ead8..07a09cb0257 100644 --- a/packages/server/src/sdk/app/views/index.ts +++ b/packages/server/src/sdk/app/views/index.ts @@ -254,21 +254,18 @@ export function syncSchema( } export async function renameLinkedViews(table: Table, renaming: RenameColumn) { - const relatedTableIds = new Set() + const relatedTables: Record = {} for (const field of Object.values(table.schema)) { if (field.type !== FieldType.LINK) { continue } - relatedTableIds.add(field.tableId) + relatedTables[field.tableId] ??= await sdk.tables.getTable(field.tableId) break } - const relatedTables = await sdk.tables.getTables( - Array.from(relatedTableIds.values()) - ) - for (const relatedTable of relatedTables) { + for (const relatedTable of Object.values(relatedTables)) { let toSave = false const viewsV2 = Object.values(relatedTable.views || {}).filter( sdk.views.isV2 From 2e4ba9cb3c98e1f7003a990557baaa2c7bba0089 Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Tue, 3 Sep 2024 11:35:31 +0200 Subject: [PATCH 08/13] Remove break --- packages/server/src/sdk/app/views/index.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/server/src/sdk/app/views/index.ts b/packages/server/src/sdk/app/views/index.ts index 07a09cb0257..0feb9705867 100644 --- a/packages/server/src/sdk/app/views/index.ts +++ b/packages/server/src/sdk/app/views/index.ts @@ -262,7 +262,6 @@ export async function renameLinkedViews(table: Table, renaming: RenameColumn) { } relatedTables[field.tableId] ??= await sdk.tables.getTable(field.tableId) - break } for (const relatedTable of Object.values(relatedTables)) { From 662c6be43527873abf5f12c8342e23f1d2438d28 Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Tue, 3 Sep 2024 11:39:01 +0200 Subject: [PATCH 09/13] Use optional chaining Co-authored-by: Sam Rose --- packages/server/src/sdk/app/views/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/server/src/sdk/app/views/index.ts b/packages/server/src/sdk/app/views/index.ts index 0feb9705867..099cb75dc9a 100644 --- a/packages/server/src/sdk/app/views/index.ts +++ b/packages/server/src/sdk/app/views/index.ts @@ -282,7 +282,7 @@ export async function renameLinkedViews(table: Table, renaming: RenameColumn) { return tableField.tableId === table._id })) { - const columns = view.schema && view.schema[relField]?.columns + const columns = view.schema?.[relField]?.columns if (columns && columns[renaming.old]) { columns[renaming.updated] = columns[renaming.old] From 3a3151b59dceca3c126014f33dde629d6ca399d6 Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Tue, 3 Sep 2024 14:23:08 +0200 Subject: [PATCH 10/13] Add extra test --- .../src/api/routes/tests/viewV2.spec.ts | 95 +++++++++++++++++++ 1 file changed, 95 insertions(+) diff --git a/packages/server/src/api/routes/tests/viewV2.spec.ts b/packages/server/src/api/routes/tests/viewV2.spec.ts index 87af53a9da3..bf246c47567 100644 --- a/packages/server/src/api/routes/tests/viewV2.spec.ts +++ b/packages/server/src/api/routes/tests/viewV2.spec.ts @@ -1455,6 +1455,101 @@ describe.each([ }) ) }) + + it("updates all views references", async () => { + let auxTable = await config.api.table.save( + saveTableRequest({ + primaryDisplay: "name", + schema: { + name: { name: "name", type: FieldType.STRING }, + age: { name: "age", type: FieldType.NUMBER }, + }, + }) + ) + + const createTableWithRelationship = async () => { + const table = await config.api.table.save( + saveTableRequest({ + schema: {}, + }) + ) + await config.api.table.save({ + ...table, + schema: { + ...table.schema, + aux: { + name: "aux", + relationshipType: RelationshipType.ONE_TO_MANY, + type: FieldType.LINK, + tableId: auxTable._id!, + fieldName: `fk_${table.name}`, + constraints: { type: "array" }, + }, + }, + }) + + return table + } + + const table1 = await createTableWithRelationship() + const table2 = await createTableWithRelationship() + + // Refetch auxTable + auxTable = await config.api.table.get(auxTable._id!) + + const createView = async (tableId: string) => { + const view = await config.api.viewV2.create({ + name: generator.guid(), + tableId, + schema: { + aux: { + visible: true, + columns: { + name: { visible: true, readonly: true }, + age: { visible: true, readonly: true }, + }, + }, + }, + }) + return view + } + + const view1 = await createView(table1._id!) + const view2 = await createView(table1._id!) + const view3 = await createView(table2._id!) + + await config.api.table.save({ + ...auxTable, + schema: { + ...auxTable.schema, + // @ts-ignore deleting age to force the rename + age: undefined, + dob: { + name: "dob", + type: FieldType.NUMBER, + constraints: { presence: true }, + }, + }, + _rename: { old: "age", updated: "dob" }, + }) + + for (const view of [view1, view2, view3]) { + const updatedView = await config.api.viewV2.get(view.id) + expect(updatedView).toEqual( + expect.objectContaining({ + schema: expect.objectContaining({ + aux: expect.objectContaining({ + columns: { + id: { visible: false, readonly: false }, + name: { visible: true, readonly: true }, + dob: { visible: true, readonly: true }, + }, + }), + }), + }) + ) + } + }) }) }) }) From 4efb3d6ed65ba68d96daf39b91222b7469cf5453 Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Tue, 3 Sep 2024 14:44:55 +0200 Subject: [PATCH 11/13] Unify tests --- .../src/api/routes/tests/viewV2.spec.ts | 353 ++++++------------ 1 file changed, 120 insertions(+), 233 deletions(-) diff --git a/packages/server/src/api/routes/tests/viewV2.spec.ts b/packages/server/src/api/routes/tests/viewV2.spec.ts index bf246c47567..de21a2bfe22 100644 --- a/packages/server/src/api/routes/tests/viewV2.spec.ts +++ b/packages/server/src/api/routes/tests/viewV2.spec.ts @@ -19,6 +19,9 @@ import { SearchResponse, BasicOperator, RelationshipType, + TableSchema, + ViewFieldMetadata, + RenameColumn, } from "@budibase/types" import { generator, mocks } from "@budibase/backend-core/tests" import { DatabaseName, getDatasource } from "../../../integrations/tests/utils" @@ -1180,8 +1183,8 @@ describe.each([ }) describe("foreign relationship columns", () => { - it("updating a column will update link columns configuration", async () => { - let auxTable = await config.api.table.save( + const createAuxTable = () => + config.api.table.save( saveTableRequest({ primaryDisplay: "name", schema: { @@ -1191,6 +1194,13 @@ describe.each([ }) ) + const createMainTable = async ( + links: { + name: string + tableId: string + fk: string + }[] + ) => { const table = await config.api.table.save( saveTableRequest({ schema: {}, @@ -1200,48 +1210,68 @@ describe.each([ ...table, schema: { ...table.schema, - aux: { - name: "aux", - relationshipType: RelationshipType.ONE_TO_MANY, - type: FieldType.LINK, - tableId: auxTable._id!, - fieldName: "fk_aux", - constraints: { type: "array" }, - }, + ...links.reduce((acc, c) => { + acc[c.name] = { + name: c.name, + relationshipType: RelationshipType.ONE_TO_MANY, + type: FieldType.LINK, + tableId: c.tableId, + fieldName: c.fk, + constraints: { type: "array" }, + } + return acc + }, {}), }, }) - // Refetch auxTable - auxTable = await config.api.table.get(auxTable._id!) + return table + } - const view = await config.api.viewV2.create({ - name: "view a", - tableId: table._id!, - schema: { - aux: { - visible: true, - columns: { - name: { visible: true, readonly: true }, - age: { visible: true, readonly: true }, - }, - }, - }, + const createView = async ( + tableId: string, + schema: Record + ) => + await config.api.viewV2.create({ + name: generator.guid(), + tableId, + schema, }) + const renameColumn = async (table: Table, renaming: RenameColumn) => { + const newSchema = { ...table.schema } + ;(newSchema[renaming.updated] = { + ...table.schema[renaming.old], + name: renaming.updated, + }), + delete newSchema[renaming.old] + await config.api.table.save({ - ...auxTable, - schema: { - ...auxTable.schema, - // @ts-ignore deleting age to force the rename - age: undefined, - dob: { - name: "dob", - type: FieldType.NUMBER, - constraints: { presence: true }, + ...table, + schema: newSchema, + _rename: renaming, + }) + } + + it("updating a column will update link columns configuration", async () => { + let auxTable = await createAuxTable() + + const table = await createMainTable([ + { name: "aux", tableId: auxTable._id!, fk: "fk_aux" }, + ]) + // Refetch auxTable + auxTable = await config.api.table.get(auxTable._id!) + + const view = await createView(table._id!, { + aux: { + visible: true, + columns: { + name: { visible: true, readonly: true }, + age: { visible: true, readonly: true }, }, }, - _rename: { old: "age", updated: "dob" }, }) + await renameColumn(auxTable, { old: "age", updated: "dob" }) + const updatedView = await config.api.viewV2.get(view.id) expect(updatedView).toEqual( expect.objectContaining({ @@ -1259,82 +1289,34 @@ describe.each([ }) it("handles multiple fields using the same table", async () => { - let auxTable = await config.api.table.save( - saveTableRequest({ - primaryDisplay: "name", - schema: { - name: { name: "name", type: FieldType.STRING }, - age: { name: "age", type: FieldType.NUMBER }, - }, - }) - ) + let auxTable = await createAuxTable() - const table = await config.api.table.save( - saveTableRequest({ - schema: {}, - }) - ) - await config.api.table.save({ - ...table, - schema: { - ...table.schema, - aux: { - name: "aux", - relationshipType: RelationshipType.ONE_TO_MANY, - type: FieldType.LINK, - tableId: auxTable._id!, - fieldName: "fk_aux", - constraints: { type: "array" }, - }, - aux2: { - name: "aux2", - relationshipType: RelationshipType.ONE_TO_MANY, - type: FieldType.LINK, - tableId: auxTable._id!, - fieldName: "fk_aux2", - constraints: { type: "array" }, - }, - }, - }) + const table = await createMainTable([ + { name: "aux", tableId: auxTable._id!, fk: "fk_aux" }, + { name: "aux2", tableId: auxTable._id!, fk: "fk_aux2" }, + ]) // Refetch auxTable auxTable = await config.api.table.get(auxTable._id!) - const view = await config.api.viewV2.create({ - name: "view a", - tableId: table._id!, - schema: { - aux: { - visible: true, - columns: { - name: { visible: true, readonly: true }, - age: { visible: true, readonly: true }, - }, - }, - aux2: { - visible: true, - columns: { - name: { visible: true, readonly: true }, - age: { visible: true, readonly: true }, - }, + const view = await createView(table._id!, { + aux: { + visible: true, + columns: { + name: { visible: true, readonly: true }, + age: { visible: true, readonly: true }, }, }, - }) - - await config.api.table.save({ - ...auxTable, - schema: { - ...auxTable.schema, - // @ts-ignore deleting age to force the rename - age: undefined, - dob: { - name: "dob", - type: FieldType.NUMBER, - constraints: { presence: true }, + aux2: { + visible: true, + columns: { + name: { visible: true, readonly: true }, + age: { visible: true, readonly: true }, }, }, - _rename: { old: "age", updated: "dob" }, }) + await renameColumn(auxTable, { old: "age", updated: "dob" }) + const updatedView = await config.api.viewV2.get(view.id) expect(updatedView).toEqual( expect.objectContaining({ @@ -1359,82 +1341,34 @@ describe.each([ }) it("does not rename columns with the same name but from other tables", async () => { - let auxTable = await config.api.table.save( - saveTableRequest({ - primaryDisplay: "name", - schema: { name: { name: "name", type: FieldType.STRING } }, - }) - ) - let aux2Table = await config.api.table.save( - saveTableRequest({ - primaryDisplay: "name", - schema: { name: { name: "name", type: FieldType.STRING } }, - }) - ) + let auxTable = await createAuxTable() + let aux2Table = await createAuxTable() + + const table = await createMainTable([ + { name: "aux", tableId: auxTable._id!, fk: "fk_aux" }, + { name: "aux2", tableId: aux2Table._id!, fk: "fk_aux2" }, + ]) - const table = await config.api.table.save( - saveTableRequest({ - schema: {}, - }) - ) - await config.api.table.save({ - ...table, - schema: { - ...table.schema, - aux: { - name: "aux", - relationshipType: RelationshipType.ONE_TO_MANY, - type: FieldType.LINK, - tableId: auxTable._id!, - fieldName: "fk_aux", - constraints: { type: "array" }, - }, - aux2: { - name: "aux2", - relationshipType: RelationshipType.ONE_TO_MANY, - type: FieldType.LINK, - tableId: aux2Table._id!, - fieldName: "fk_aux2", - constraints: { type: "array" }, - }, - }, - }) // Refetch auxTable auxTable = await config.api.table.get(auxTable._id!) - const view = await config.api.viewV2.create({ - name: "view a", - tableId: table._id!, - schema: { - aux: { - visible: true, - columns: { - name: { visible: true, readonly: true }, - }, - }, - aux2: { - visible: true, - columns: { - name: { visible: true, readonly: true }, - }, + const view = await createView(table._id!, { + aux: { + visible: true, + columns: { + name: { visible: true, readonly: true }, }, }, - }) - - await config.api.table.save({ - ...auxTable, - schema: { - ...auxTable.schema, - // @ts-ignore deleting age to force the rename - name: undefined, - fullName: { - name: "fullName", - type: FieldType.STRING, + aux2: { + visible: true, + columns: { + name: { visible: true, readonly: true }, }, }, - _rename: { old: "name", updated: "fullName" }, }) + await renameColumn(auxTable, { old: "name", updated: "fullName" }) + const updatedView = await config.api.viewV2.get(view.id) expect(updatedView).toEqual( expect.objectContaining({ @@ -1443,12 +1377,14 @@ describe.each([ columns: { id: { visible: false, readonly: false }, fullName: { visible: true, readonly: true }, + age: { visible: false, readonly: false }, }, }), aux2: expect.objectContaining({ columns: { id: { visible: false, readonly: false }, name: { visible: true, readonly: true }, + age: { visible: false, readonly: false }, }, }), }), @@ -1457,81 +1393,32 @@ describe.each([ }) it("updates all views references", async () => { - let auxTable = await config.api.table.save( - saveTableRequest({ - primaryDisplay: "name", - schema: { - name: { name: "name", type: FieldType.STRING }, - age: { name: "age", type: FieldType.NUMBER }, - }, - }) - ) - - const createTableWithRelationship = async () => { - const table = await config.api.table.save( - saveTableRequest({ - schema: {}, - }) - ) - await config.api.table.save({ - ...table, - schema: { - ...table.schema, - aux: { - name: "aux", - relationshipType: RelationshipType.ONE_TO_MANY, - type: FieldType.LINK, - tableId: auxTable._id!, - fieldName: `fk_${table.name}`, - constraints: { type: "array" }, - }, - }, - }) - - return table - } + let auxTable = await createAuxTable() - const table1 = await createTableWithRelationship() - const table2 = await createTableWithRelationship() + const table1 = await createMainTable([ + { name: "aux", tableId: auxTable._id!, fk: "fk_aux_table1" }, + ]) + const table2 = await createMainTable([ + { name: "aux", tableId: auxTable._id!, fk: "fk_aux_table2" }, + ]) // Refetch auxTable auxTable = await config.api.table.get(auxTable._id!) - const createView = async (tableId: string) => { - const view = await config.api.viewV2.create({ - name: generator.guid(), - tableId, - schema: { - aux: { - visible: true, - columns: { - name: { visible: true, readonly: true }, - age: { visible: true, readonly: true }, - }, - }, + const viewSchema = { + aux: { + visible: true, + columns: { + name: { visible: true, readonly: true }, + age: { visible: true, readonly: true }, }, - }) - return view + }, } + const view1 = await createView(table1._id!, viewSchema) + const view2 = await createView(table1._id!, viewSchema) + const view3 = await createView(table2._id!, viewSchema) - const view1 = await createView(table1._id!) - const view2 = await createView(table1._id!) - const view3 = await createView(table2._id!) - - await config.api.table.save({ - ...auxTable, - schema: { - ...auxTable.schema, - // @ts-ignore deleting age to force the rename - age: undefined, - dob: { - name: "dob", - type: FieldType.NUMBER, - constraints: { presence: true }, - }, - }, - _rename: { old: "age", updated: "dob" }, - }) + await renameColumn(auxTable, { old: "age", updated: "dob" }) for (const view of [view1, view2, view3]) { const updatedView = await config.api.viewV2.get(view.id) From 2667238d934cab84e531cf05710afdc0549abc09 Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Tue, 3 Sep 2024 14:47:49 +0200 Subject: [PATCH 12/13] Add undefined check --- packages/server/src/sdk/app/views/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/server/src/sdk/app/views/index.ts b/packages/server/src/sdk/app/views/index.ts index 099cb75dc9a..7aa010775e2 100644 --- a/packages/server/src/sdk/app/views/index.ts +++ b/packages/server/src/sdk/app/views/index.ts @@ -276,7 +276,7 @@ export async function renameLinkedViews(table: Table, renaming: RenameColumn) { for (const view of viewsV2) { for (const relField of Object.keys(view.schema || {}).filter(f => { const tableField = relatedTable.schema[f] - if (tableField.type !== FieldType.LINK) { + if (!tableField || tableField.type !== FieldType.LINK) { return false } From 52283e07aa2836a3c1e0390ee127a235485dcde5 Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Tue, 3 Sep 2024 16:15:33 +0200 Subject: [PATCH 13/13] Tidying --- packages/server/src/api/routes/tests/viewV2.spec.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/server/src/api/routes/tests/viewV2.spec.ts b/packages/server/src/api/routes/tests/viewV2.spec.ts index de21a2bfe22..6d2d13e5808 100644 --- a/packages/server/src/api/routes/tests/viewV2.spec.ts +++ b/packages/server/src/api/routes/tests/viewV2.spec.ts @@ -1238,11 +1238,11 @@ describe.each([ const renameColumn = async (table: Table, renaming: RenameColumn) => { const newSchema = { ...table.schema } - ;(newSchema[renaming.updated] = { + newSchema[renaming.updated] = { ...table.schema[renaming.old], name: renaming.updated, - }), - delete newSchema[renaming.old] + } + delete newSchema[renaming.old] await config.api.table.save({ ...table,