Merge branch 'master' into view-calculation-filtering

This commit is contained in:
Sam Rose 2024-10-10 09:02:35 +01:00 committed by GitHub
commit 93ebe39e43
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 65 additions and 17 deletions

View File

@ -325,7 +325,7 @@ class InternalBuilder {
return input return input
} }
private parseBody(body: any) { private parseBody(body: Record<string, any>) {
for (let [key, value] of Object.entries(body)) { for (let [key, value] of Object.entries(body)) {
const { column } = this.splitter.run(key) const { column } = this.splitter.run(key)
const schema = this.table.schema[column] const schema = this.table.schema[column]
@ -1259,6 +1259,10 @@ class InternalBuilder {
create(opts: QueryOptions): Knex.QueryBuilder { create(opts: QueryOptions): Knex.QueryBuilder {
const { body } = this.query const { body } = this.query
if (!body) {
throw new Error("Cannot create without row body")
}
let query = this.qualifiedKnex({ alias: false }) let query = this.qualifiedKnex({ alias: false })
const parsedBody = this.parseBody(body) const parsedBody = this.parseBody(body)
@ -1417,6 +1421,9 @@ class InternalBuilder {
update(opts: QueryOptions): Knex.QueryBuilder { update(opts: QueryOptions): Knex.QueryBuilder {
const { body, filters } = this.query const { body, filters } = this.query
if (!body) {
throw new Error("Cannot update without row body")
}
let query = this.qualifiedKnex() let query = this.qualifiedKnex()
const parsedBody = this.parseBody(body) const parsedBody = this.parseBody(body)
query = this.addFilters(query, filters) query = this.addFilters(query, filters)

View File

@ -4,6 +4,7 @@ import {
AutoFieldSubType, AutoFieldSubType,
AutoReason, AutoReason,
Datasource, Datasource,
DatasourcePlusQueryResponse,
FieldSchema, FieldSchema,
FieldType, FieldType,
FilterType, FilterType,
@ -269,18 +270,13 @@ export class ExternalRequest<T extends Operation> {
} }
} }
private async removeManyToManyRelationships( private async removeManyToManyRelationships(rowId: string, table: Table) {
rowId: string,
table: Table,
colName: string
) {
const tableId = table._id! const tableId = table._id!
const filters = this.prepareFilters(rowId, {}, table) const filters = this.prepareFilters(rowId, {}, table)
// safety check, if there are no filters on deletion bad things happen // safety check, if there are no filters on deletion bad things happen
if (Object.keys(filters).length !== 0) { if (Object.keys(filters).length !== 0) {
return getDatasourceAndQuery({ return getDatasourceAndQuery({
endpoint: getEndpoint(tableId, Operation.DELETE), endpoint: getEndpoint(tableId, Operation.DELETE),
body: { [colName]: null },
filters, filters,
meta: { meta: {
table, table,
@ -291,13 +287,18 @@ export class ExternalRequest<T extends Operation> {
} }
} }
private async removeOneToManyRelationships(rowId: string, table: Table) { private async removeOneToManyRelationships(
rowId: string,
table: Table,
colName: string
) {
const tableId = table._id! const tableId = table._id!
const filters = this.prepareFilters(rowId, {}, table) const filters = this.prepareFilters(rowId, {}, table)
// safety check, if there are no filters on deletion bad things happen // safety check, if there are no filters on deletion bad things happen
if (Object.keys(filters).length !== 0) { if (Object.keys(filters).length !== 0) {
return getDatasourceAndQuery({ return getDatasourceAndQuery({
endpoint: getEndpoint(tableId, Operation.UPDATE), endpoint: getEndpoint(tableId, Operation.UPDATE),
body: { [colName]: null },
filters, filters,
meta: { meta: {
table, table,
@ -557,8 +558,9 @@ export class ExternalRequest<T extends Operation> {
return matchesPrimaryLink return matchesPrimaryLink
} }
const matchesSecondayLink = row[linkSecondary] === body?.[linkSecondary] const matchesSecondaryLink =
return matchesPrimaryLink && matchesSecondayLink row[linkSecondary] === body?.[linkSecondary]
return matchesPrimaryLink && matchesSecondaryLink
} }
const existingRelationship = rows.find((row: { [key: string]: any }) => const existingRelationship = rows.find((row: { [key: string]: any }) =>
@ -595,8 +597,8 @@ export class ExternalRequest<T extends Operation> {
for (let row of rows) { for (let row of rows) {
const rowId = generateIdForRow(row, table) const rowId = generateIdForRow(row, table)
const promise: Promise<any> = isMany const promise: Promise<any> = isMany
? this.removeManyToManyRelationships(rowId, table, colName) ? this.removeManyToManyRelationships(rowId, table)
: this.removeOneToManyRelationships(rowId, table) : this.removeOneToManyRelationships(rowId, table, colName)
if (promise) { if (promise) {
promises.push(promise) promises.push(promise)
} }
@ -619,12 +621,12 @@ export class ExternalRequest<T extends Operation> {
rows.map(row => { rows.map(row => {
const rowId = generateIdForRow(row, table) const rowId = generateIdForRow(row, table)
return isMany return isMany
? this.removeManyToManyRelationships( ? this.removeManyToManyRelationships(rowId, table)
: this.removeOneToManyRelationships(
rowId, rowId,
table, table,
relationshipColumn.fieldName relationshipColumn.fieldName
) )
: this.removeOneToManyRelationships(rowId, table)
}) })
) )
} }
@ -669,6 +671,7 @@ export class ExternalRequest<T extends Operation> {
config.includeSqlRelationships === IncludeRelationship.INCLUDE config.includeSqlRelationships === IncludeRelationship.INCLUDE
// clean up row on ingress using schema // clean up row on ingress using schema
const unprocessedRow = config.row
const processed = this.inputProcessing(row, table) const processed = this.inputProcessing(row, table)
row = processed.row row = processed.row
let manyRelationships = processed.manyRelationships let manyRelationships = processed.manyRelationships
@ -743,9 +746,20 @@ export class ExternalRequest<T extends Operation> {
// aliasing can be disabled fully if desired // aliasing can be disabled fully if desired
const aliasing = new sdk.rows.AliasTables(Object.keys(this.tables)) const aliasing = new sdk.rows.AliasTables(Object.keys(this.tables))
let response = env.SQL_ALIASING_DISABLE let response: DatasourcePlusQueryResponse
? await getDatasourceAndQuery(json) // there's a chance after input processing nothing needs updated, so pass over the call
: await aliasing.queryWithAliasing(json, makeExternalQuery) // we might still need to perform other operations like updating the foreign keys on other rows
if (
this.operation === Operation.UPDATE &&
Object.keys(row || {}).length === 0 &&
unprocessedRow
) {
response = [unprocessedRow]
} else {
response = env.SQL_ALIASING_DISABLE
? await getDatasourceAndQuery(json)
: await aliasing.queryWithAliasing(json, makeExternalQuery)
}
// if it's a counting operation there will be no more processing, just return the number // if it's a counting operation there will be no more processing, just return the number
if (this.operation === Operation.COUNT) { if (this.operation === Operation.COUNT) {

View File

@ -1114,6 +1114,33 @@ describe.each([
expect(getResp.user2[0]._id).toEqual(user2._id) expect(getResp.user2[0]._id).toEqual(user2._id)
}) })
it("should be able to remove a relationship from many side", async () => {
const row = await config.api.row.save(otherTable._id!, {
name: "test",
description: "test",
})
const row2 = await config.api.row.save(otherTable._id!, {
name: "test",
description: "test",
})
const { _id } = await config.api.row.save(table._id!, {
relationship: [{ _id: row._id }, { _id: row2._id }],
})
const relatedRow = await config.api.row.get(table._id!, _id!, {
status: 200,
})
expect(relatedRow.relationship.length).toEqual(2)
await config.api.row.save(table._id!, {
...relatedRow,
relationship: [{ _id: row._id }],
})
const afterRelatedRow = await config.api.row.get(table._id!, _id!, {
status: 200,
})
expect(afterRelatedRow.relationship.length).toEqual(1)
expect(afterRelatedRow.relationship[0]._id).toEqual(row._id)
})
it("should be able to update relationships when both columns are same name", async () => { it("should be able to update relationships when both columns are same name", async () => {
let row = await config.api.row.save(table._id!, { let row = await config.api.row.save(table._id!, {
name: "test", name: "test",