diff --git a/packages/server/src/api/controllers/row/ExternalRequest.ts b/packages/server/src/api/controllers/row/ExternalRequest.ts index 74d48cb93e..45940abff8 100644 --- a/packages/server/src/api/controllers/row/ExternalRequest.ts +++ b/packages/server/src/api/controllers/row/ExternalRequest.ts @@ -181,7 +181,15 @@ function getEndpoint(tableId: string | undefined, operation: string) { } } -function basicProcessing(row: Row, table: Table, isLinked: boolean): Row { +function basicProcessing({ + row, + table, + isLinked, +}: { + row: Row + table: Table + isLinked: boolean +}): Row { const thisRow: Row = {} // filter the row down to what is actually the row (not joined) for (let field of Object.values(table.schema)) { @@ -389,7 +397,7 @@ export class ExternalRequest { continue } - let linked = basicProcessing(row, linkedTable, true) + let linked = basicProcessing({ row, table: linkedTable, isLinked: true }) if (!linked._id) { continue } @@ -437,7 +445,10 @@ export class ExternalRequest { ) continue } - const thisRow = fixArrayTypes(basicProcessing(row, table, false), table) + const thisRow = fixArrayTypes( + basicProcessing({ row, table, isLinked: false }), + table + ) if (thisRow._id == null) { throw "Unable to generate row ID for SQL rows" } @@ -583,20 +594,36 @@ export class ExternalRequest { return } - const linkSecondary = - linkTable?.primary && - linkTable?.primary?.length > 1 && - linkTable?.primary[1] + // @ts-ignore + const linkSecondary = linkTable?.primary[1] const rows = related[key]?.rows || [] - const found = rows.find( - (row: { [key: string]: any }) => + + function relationshipMatchPredicate({ + row, + linkPrimary, + linkSecondary, + }: { + row: { [key: string]: any } + linkPrimary: string + linkSecondary?: string + }) { + const matchesPrimaryLink = row[linkPrimary] === relationship.id || - (row[linkPrimary] === body?.[linkPrimary] && - (!linkSecondary || row[linkSecondary] === body?.[linkSecondary])) + row[linkPrimary] === body?.[linkPrimary] + if (!matchesPrimaryLink || !linkSecondary) { + return matchesPrimaryLink + } + + const matchesSecondayLink = row[linkSecondary] === body?.[linkSecondary] + return matchesPrimaryLink && matchesSecondayLink + } + + const existingRelationship = rows.find((row: { [key: string]: any }) => + relationshipMatchPredicate({ row, linkPrimary, linkSecondary }) ) const operation = isUpdate ? Operation.UPDATE : Operation.CREATE - if (!found) { + if (!existingRelationship) { promises.push( getDatasourceAndQuery({ endpoint: getEndpoint(tableId, operation), @@ -607,7 +634,7 @@ export class ExternalRequest { ) } else { // remove the relationship from cache so it isn't adjusted again - rows.splice(rows.indexOf(found), 1) + rows.splice(rows.indexOf(existingRelationship), 1) } } // finally cleanup anything that needs to be removed diff --git a/packages/server/src/integration-test/postgres.spec.ts b/packages/server/src/integration-test/postgres.spec.ts index df0e4bac15..e6f2116f43 100644 --- a/packages/server/src/integration-test/postgres.spec.ts +++ b/packages/server/src/integration-test/postgres.spec.ts @@ -27,9 +27,9 @@ describe("row api - postgres", () => { let makeRequest: MakeRequestResponse, postgresDatasource: Datasource, primaryPostgresTable: Table, - o2mInfo: ForeignTableInfo, - m2oInfo: ForeignTableInfo, - m2mInfo: ForeignTableInfo + oneToManyRelationshipInfo: ForeignTableInfo, + manyToOneRelationshipInfo: ForeignTableInfo, + manyToManyRelationshipInfo: ForeignTableInfo let host: string let port: number @@ -96,17 +96,17 @@ describe("row api - postgres", () => { }) } - o2mInfo = { + oneToManyRelationshipInfo = { table: await createAuxTable("o2m"), fieldName: "oneToManyRelation", relationshipType: RelationshipTypes.ONE_TO_MANY, } - m2oInfo = { + manyToOneRelationshipInfo = { table: await createAuxTable("m2o"), fieldName: "manyToOneRelation", relationshipType: RelationshipTypes.MANY_TO_ONE, } - m2mInfo = { + manyToManyRelationshipInfo = { table: await createAuxTable("m2m"), fieldName: "manyToManyRelation", relationshipType: RelationshipTypes.MANY_TO_MANY, @@ -146,10 +146,10 @@ describe("row api - postgres", () => { type: "array", presence: false, }, - fieldName: o2mInfo.fieldName, + fieldName: oneToManyRelationshipInfo.fieldName, name: "oneToManyRelation", relationshipType: RelationshipTypes.ONE_TO_MANY, - tableId: o2mInfo.table._id, + tableId: oneToManyRelationshipInfo.table._id, main: true, }, manyToOneRelation: { @@ -158,10 +158,10 @@ describe("row api - postgres", () => { type: "array", presence: false, }, - fieldName: m2oInfo.fieldName, + fieldName: manyToOneRelationshipInfo.fieldName, name: "manyToOneRelation", relationshipType: RelationshipTypes.MANY_TO_ONE, - tableId: m2oInfo.table._id, + tableId: manyToOneRelationshipInfo.table._id, main: true, }, manyToManyRelation: { @@ -170,10 +170,10 @@ describe("row api - postgres", () => { type: "array", presence: false, }, - fieldName: m2mInfo.fieldName, + fieldName: manyToManyRelationshipInfo.fieldName, name: "manyToManyRelation", relationshipType: RelationshipTypes.MANY_TO_MANY, - tableId: m2mInfo.table._id, + tableId: manyToManyRelationshipInfo.table._id, main: true, }, }, @@ -239,10 +239,10 @@ describe("row api - postgres", () => { } if (opts?.createForeignRows?.createOne2Many) { - const foreignKey = `fk_${o2mInfo.table.name}_${o2mInfo.fieldName}` + const foreignKey = `fk_${oneToManyRelationshipInfo.table.name}_${oneToManyRelationshipInfo.fieldName}` const foreignRow = await config.createRow({ - tableId: o2mInfo.table._id, + tableId: oneToManyRelationshipInfo.table._id, title: generator.name(), }) @@ -252,21 +252,22 @@ describe("row api - postgres", () => { } foreignRows.push({ row: foreignRow, - relationshipType: o2mInfo.relationshipType, + relationshipType: oneToManyRelationshipInfo.relationshipType, }) } for (let i = 0; i < (opts?.createForeignRows?.createMany2One || 0); i++) { const foreignRow = await config.createRow({ - tableId: m2oInfo.table._id, + tableId: manyToOneRelationshipInfo.table._id, title: generator.name(), }) rowData = { ...rowData, - [m2oInfo.fieldName]: rowData[m2oInfo.fieldName] || [], + [manyToOneRelationshipInfo.fieldName]: + rowData[manyToOneRelationshipInfo.fieldName] || [], } - rowData[m2oInfo.fieldName].push(foreignRow._id) + rowData[manyToOneRelationshipInfo.fieldName].push(foreignRow._id) foreignRows.push({ row: foreignRow, relationshipType: RelationshipTypes.MANY_TO_ONE, @@ -275,15 +276,16 @@ describe("row api - postgres", () => { for (let i = 0; i < (opts?.createForeignRows?.createMany2Many || 0); i++) { const foreignRow = await config.createRow({ - tableId: m2mInfo.table._id, + tableId: manyToManyRelationshipInfo.table._id, title: generator.name(), }) rowData = { ...rowData, - [m2mInfo.fieldName]: rowData[m2mInfo.fieldName] || [], + [manyToManyRelationshipInfo.fieldName]: + rowData[manyToManyRelationshipInfo.fieldName] || [], } - rowData[m2mInfo.fieldName].push(foreignRow._id) + rowData[manyToManyRelationshipInfo.fieldName].push(foreignRow._id) foreignRows.push({ row: foreignRow, relationshipType: RelationshipTypes.MANY_TO_MANY, @@ -577,11 +579,11 @@ describe("row api - postgres", () => { tableId: row.tableId, _id: expect.any(String), _rev: expect.any(String), - [`fk_${o2mInfo.table.name}_${o2mInfo.fieldName}`]: + [`fk_${oneToManyRelationshipInfo.table.name}_${oneToManyRelationshipInfo.fieldName}`]: one2ManyForeignRows[0].row.id, }) - expect(res.body[o2mInfo.fieldName]).toBeUndefined() + expect(res.body[oneToManyRelationshipInfo.fieldName]).toBeUndefined() }) }) @@ -608,11 +610,11 @@ describe("row api - postgres", () => { tableId: row.tableId, _id: expect.any(String), _rev: expect.any(String), - [`fk_${o2mInfo.table.name}_${o2mInfo.fieldName}`]: + [`fk_${oneToManyRelationshipInfo.table.name}_${oneToManyRelationshipInfo.fieldName}`]: foreignRows[0].row.id, }) - expect(res.body[o2mInfo.fieldName]).toBeUndefined() + expect(res.body[oneToManyRelationshipInfo.fieldName]).toBeUndefined() }) }) @@ -641,7 +643,7 @@ describe("row api - postgres", () => { _rev: expect.any(String), }) - expect(res.body[o2mInfo.fieldName]).toBeUndefined() + expect(res.body[oneToManyRelationshipInfo.fieldName]).toBeUndefined() }) }) @@ -670,7 +672,7 @@ describe("row api - postgres", () => { _rev: expect.any(String), }) - expect(res.body[o2mInfo.fieldName]).toBeUndefined() + expect(res.body[oneToManyRelationshipInfo.fieldName]).toBeUndefined() }) }) }) @@ -921,30 +923,33 @@ describe("row api - postgres", () => { ) expect(res.body).toEqual({ ...rowData, - [`fk_${o2mInfo.table.name}_${o2mInfo.fieldName}`]: + [`fk_${oneToManyRelationshipInfo.table.name}_${oneToManyRelationshipInfo.fieldName}`]: foreignRowsByType[RelationshipTypes.ONE_TO_MANY][0].row.id, - [o2mInfo.fieldName]: [ + [oneToManyRelationshipInfo.fieldName]: [ { ...foreignRowsByType[RelationshipTypes.ONE_TO_MANY][0].row, _id: expect.any(String), _rev: expect.any(String), }, ], - [m2oInfo.fieldName]: [ + [manyToOneRelationshipInfo.fieldName]: [ { ...foreignRowsByType[RelationshipTypes.MANY_TO_ONE][0].row, - [`fk_${m2oInfo.table.name}_${m2oInfo.fieldName}`]: row.id, + [`fk_${manyToOneRelationshipInfo.table.name}_${manyToOneRelationshipInfo.fieldName}`]: + row.id, }, { ...foreignRowsByType[RelationshipTypes.MANY_TO_ONE][1].row, - [`fk_${m2oInfo.table.name}_${m2oInfo.fieldName}`]: row.id, + [`fk_${manyToOneRelationshipInfo.table.name}_${manyToOneRelationshipInfo.fieldName}`]: + row.id, }, { ...foreignRowsByType[RelationshipTypes.MANY_TO_ONE][2].row, - [`fk_${m2oInfo.table.name}_${m2oInfo.fieldName}`]: row.id, + [`fk_${manyToOneRelationshipInfo.table.name}_${manyToOneRelationshipInfo.fieldName}`]: + row.id, }, ], - [m2mInfo.fieldName]: [ + [manyToManyRelationshipInfo.fieldName]: [ { ...foreignRowsByType[RelationshipTypes.MANY_TO_MANY][0].row, },