From 60e2ccad8c87620906f963c151a8f3c3bbb032d7 Mon Sep 17 00:00:00 2001 From: Pranav C Date: Thu, 15 Jun 2023 00:06:37 +0530 Subject: [PATCH 01/13] feat: add nested list api Signed-off-by: Pranav C --- .../src/controllers/data-table.controller.ts | 21 ++++++ .../nocodb/src/services/data-table.service.ts | 67 ++++++++++++++++++- 2 files changed, 87 insertions(+), 1 deletion(-) diff --git a/packages/nocodb/src/controllers/data-table.controller.ts b/packages/nocodb/src/controllers/data-table.controller.ts index 808650b20b..7f46a68f3c 100644 --- a/packages/nocodb/src/controllers/data-table.controller.ts +++ b/packages/nocodb/src/controllers/data-table.controller.ts @@ -126,4 +126,25 @@ export class DataTableController { viewId, }); } + + + @Get(['/api/v1/tables/:modelId/links/:columnId/row/:rowId']) + @Acl('nestedDataList') + async nestedDataList( + @Request() req, + @Param('modelId') modelId: string, + @Query('viewId') viewId: string, + @Param('columnId') columnId: string, + @Param('rowId') rowId: string, + @Body() refRowIds: string | string[] | number | number[], + ) { + return await this.dataTableService.nestedDataList({ + modelId, + rowId: rowId, + query: req.query, + viewId, + refRowIds, + columnId, + }); + } } diff --git a/packages/nocodb/src/services/data-table.service.ts b/packages/nocodb/src/services/data-table.service.ts index b630300c93..0042b0ae10 100644 --- a/packages/nocodb/src/services/data-table.service.ts +++ b/packages/nocodb/src/services/data-table.service.ts @@ -1,6 +1,9 @@ import { Injectable } from '@nestjs/common'; +import { RelationTypes, UITypes } from 'nocodb-sdk' import { NcError } from '../helpers/catchError'; -import { Base, Model, View } from '../models'; +import { PagedResponseImpl } from '../helpers/PagedResponse' +import { Base, Column, LinkToAnotherRecordColumn, Model, View } from '../models' +import { getColumnByIdOrName } from '../modules/datas/helpers' import NcConnectionMgrv2 from '../utils/common/NcConnectionMgrv2'; import { DatasService } from './datas.service'; @@ -231,4 +234,66 @@ export class DataTableService { keys.add(pk); } } + + async nestedDataList(param: { + viewId: string; + modelId: string; + query: any; + rowId: string | string[] | number | number[]; + columnId: string; + }) { + const { model, view } = await this.getModelAndView(param); + const base = await Base.get(model.base_id); + + const baseModel = await Model.getBaseModelSQL({ + id: model.id, + viewId: view?.id, + dbDriver: await NcConnectionMgrv2.get(base), + }); + + const column = await Column.get({colId: param.columnId}) + + if(!column) NcError.badRequest('Column not found' + + if(column.fk_model_id !== model.id) + NcError.badRequest('Column not belong to model') + + if (column.uidt !== UITypes.LinkToAnotherRecord) + NcError.badRequest('Column is not LTAR'); + + const colOptions = await column.getColOptions( + ) + + let data:any[] ; + let count:number; + if(colOptions.type === RelationTypes.MANY_TO_MANY) { + data = await baseModel.mmList( + { + colId: column.id, + parentId: param.rowId, + }, + param.query as any, + ); + count = await baseModel.mmListCount({ + colId: column.id, + parentId: param.rowId, + }); + }else { + data = await baseModel.hmList( + { + colId: column.id, + id: param.rowId, + }, + param.query as any, + ); + count = await baseModel.hmListCount({ + colId: column.id, + id: param.rowId, + }); + } + return new PagedResponseImpl(data, { + count, + ...param.query, + }); + } } From 3c4850060434b6016db4d833bb950e981474a8d3 Mon Sep 17 00:00:00 2001 From: Pranav C Date: Thu, 15 Jun 2023 01:22:14 +0530 Subject: [PATCH 02/13] feat: add nested link/unlik (WIP) Signed-off-by: Pranav C --- .../src/controllers/data-table.controller.ts | 42 +++- packages/nocodb/src/db/BaseModelSqlv2.ts | 191 ++++++++++++++++++ .../nocodb/src/services/data-table.service.ts | 73 ++++++- 3 files changed, 294 insertions(+), 12 deletions(-) diff --git a/packages/nocodb/src/controllers/data-table.controller.ts b/packages/nocodb/src/controllers/data-table.controller.ts index 7f46a68f3c..eaac1c6e6a 100644 --- a/packages/nocodb/src/controllers/data-table.controller.ts +++ b/packages/nocodb/src/controllers/data-table.controller.ts @@ -136,15 +136,55 @@ export class DataTableController { @Query('viewId') viewId: string, @Param('columnId') columnId: string, @Param('rowId') rowId: string, - @Body() refRowIds: string | string[] | number | number[], ) { return await this.dataTableService.nestedDataList({ modelId, rowId: rowId, query: req.query, viewId, + columnId, + }); + } + + + @Get(['/api/v1/tables/:modelId/links/:columnId/row/:rowId']) + @Post('nestedDataLink') + async nestedLink( + @Request() req, + @Param('modelId') modelId: string, + @Query('viewId') viewId: string, + @Param('columnId') columnId: string, + @Param('rowId') rowId: string, + @Body() refRowIds: string | string[] | number | number[], + ) { + return await this.dataTableService.nestedLink({ + modelId, + rowId: rowId, + query: req.query, + viewId, + columnId, refRowIds, + }); + } + + + @Delete(['/api/v1/tables/:modelId/links/:columnId/row/:rowId']) + @Acl('nestedDataUnlink') + async nestedUnlink( + @Request() req, + @Param('modelId') modelId: string, + @Query('viewId') viewId: string, + @Param('columnId') columnId: string, + @Param('rowId') rowId: string, + @Body() refRowIds: string | string[] | number | number[], + ) { + return await this.dataTableService.nestedUnlink({ + modelId, + rowId: rowId, + query: req.query, + viewId, columnId, + refRowIds, }); } } diff --git a/packages/nocodb/src/db/BaseModelSqlv2.ts b/packages/nocodb/src/db/BaseModelSqlv2.ts index d67613ca4c..4f6157b6eb 100644 --- a/packages/nocodb/src/db/BaseModelSqlv2.ts +++ b/packages/nocodb/src/db/BaseModelSqlv2.ts @@ -3483,6 +3483,197 @@ class BaseModelSqlv2 { } return data; } + + async addLinks({ + cookie, + childIds, + colId, + rowId, + }: { + cookie: any; + childIds: string | string[] | number | number[]; + colId: string; + rowId: string; + }) { + const columns = await this.model.getColumns(); + const column = columns.find((c) => c.id === colId); + + if (!column || column.uidt !== UITypes.LinkToAnotherRecord) + NcError.notFound('Column not found'); + + const colOptions = await column.getColOptions(); + + const childColumn = await colOptions.getChildColumn(); + const parentColumn = await colOptions.getParentColumn(); + const parentTable = await parentColumn.getModel(); + const childTable = await childColumn.getModel(); + await childTable.getColumns(); + await parentTable.getColumns(); + + const childTn = this.getTnPath(childTable); + const parentTn = this.getTnPath(parentTable); + + switch (colOptions.type) { + case RelationTypes.MANY_TO_MANY: + { + const vChildCol = await colOptions.getMMChildColumn(); + const vParentCol = await colOptions.getMMParentColumn(); + const vTable = await colOptions.getMMModel(); + + const vTn = this.getTnPath(vTable); + + if (this.isSnowflake) { + const parentPK = this.dbDriver(parentTn) + .select(parentColumn.column_name) + .where(_wherePk(parentTable.primaryKeys, childId)) + .first(); + + const childPK = this.dbDriver(childTn) + .select(childColumn.column_name) + .where(_wherePk(childTable.primaryKeys, rowId)) + .first(); + + await this.dbDriver.raw( + `INSERT INTO ?? (??, ??) SELECT (${parentPK.toQuery()}), (${childPK.toQuery()})`, + [vTn, vParentCol.column_name, vChildCol.column_name], + ); + } else { + await this.dbDriver(vTn).insert({ + [vParentCol.column_name]: this.dbDriver(parentTn) + .select(parentColumn.column_name) + .where(_wherePk(parentTable.primaryKeys, childId)) + .first(), + [vChildCol.column_name]: this.dbDriver(childTn) + .select(childColumn.column_name) + .where(_wherePk(childTable.primaryKeys, rowId)) + .first(), + }); + } + } + break; + case RelationTypes.HAS_MANY: + { + await this.dbDriver(childTn) + .update({ + [childColumn.column_name]: this.dbDriver.from( + this.dbDriver(parentTn) + .select(parentColumn.column_name) + .where(_wherePk(parentTable.primaryKeys, rowId)) + .first() + .as('___cn_alias'), + ), + }) + .where(_wherePk(childTable.primaryKeys, childId)); + } + break; + case RelationTypes.BELONGS_TO: + { + await this.dbDriver(childTn) + .update({ + [childColumn.column_name]: this.dbDriver.from( + this.dbDriver(parentTn) + .select(parentColumn.column_name) + .where(_wherePk(parentTable.primaryKeys, childId)) + .first() + .as('___cn_alias'), + ), + }) + .where(_wherePk(childTable.primaryKeys, rowId)); + } + break; + } + + const response = await this.readByPk(rowId); + await this.afterInsert(response, this.dbDriver, cookie); + await this.afterAddChild(rowId, childId, cookie); + } + + async removeLinks({ + cookie, + childIds, + colId, + rowId, + }: { + cookie: any; + childIds: string | string[] | number | number[]; + colId: string; + rowId: string; + }) { + const columns = await this.model.getColumns(); + const column = columns.find((c) => c.id === colId); + + if (!column || column.uidt !== UITypes.LinkToAnotherRecord) + NcError.notFound('Column not found'); + + const colOptions = await column.getColOptions(); + + const childColumn = await colOptions.getChildColumn(); + const parentColumn = await colOptions.getParentColumn(); + const parentTable = await parentColumn.getModel(); + const childTable = await childColumn.getModel(); + await childTable.getColumns(); + await parentTable.getColumns(); + + const childTn = this.getTnPath(childTable); + const parentTn = this.getTnPath(parentTable); + + const prevData = await this.readByPk(rowId); + + switch (colOptions.type) { + case RelationTypes.MANY_TO_MANY: + { + const vChildCol = await colOptions.getMMChildColumn(); + const vParentCol = await colOptions.getMMParentColumn(); + const vTable = await colOptions.getMMModel(); + + const vTn = this.getTnPath(vTable); + + await this.dbDriver(vTn) + .where({ + [vParentCol.column_name]: this.dbDriver(parentTn) + .select(parentColumn.column_name) + .where(_wherePk(parentTable.primaryKeys, childId)) + .first(), + [vChildCol.column_name]: this.dbDriver(childTn) + .select(childColumn.column_name) + .where(_wherePk(childTable.primaryKeys, rowId)) + .first(), + }) + .delete(); + } + break; + case RelationTypes.HAS_MANY: + { + await this.dbDriver(childTn) + // .where({ + // [childColumn.cn]: this.dbDriver(parentTable.tn) + // .select(parentColumn.cn) + // .where(parentTable.primaryKey.cn, rowId) + // .first() + // }) + .where(_wherePk(childTable.primaryKeys, childId)) + .update({ [childColumn.column_name]: null }); + } + break; + case RelationTypes.BELONGS_TO: + { + await this.dbDriver(childTn) + // .where({ + // [childColumn.cn]: this.dbDriver(parentTable.tn) + // .select(parentColumn.cn) + // .where(parentTable.primaryKey.cn, childId) + // .first() + // }) + .where(_wherePk(childTable.primaryKeys, rowId)) + .update({ [childColumn.column_name]: null }); + } + break; + } + + const newData = await this.readByPk(rowId); + await this.afterUpdate(prevData, newData, this.dbDriver, cookie); + await this.afterRemoveChild(rowId, childId, cookie); + } } function extractSortsObject( diff --git a/packages/nocodb/src/services/data-table.service.ts b/packages/nocodb/src/services/data-table.service.ts index 0042b0ae10..ddfb72efde 100644 --- a/packages/nocodb/src/services/data-table.service.ts +++ b/packages/nocodb/src/services/data-table.service.ts @@ -3,7 +3,7 @@ import { RelationTypes, UITypes } from 'nocodb-sdk' import { NcError } from '../helpers/catchError'; import { PagedResponseImpl } from '../helpers/PagedResponse' import { Base, Column, LinkToAnotherRecordColumn, Model, View } from '../models' -import { getColumnByIdOrName } from '../modules/datas/helpers' +import { getColumnByIdOrName, getViewAndModelByAliasOrId, PathParams } from '../modules/datas/helpers' import NcConnectionMgrv2 from '../utils/common/NcConnectionMgrv2'; import { DatasService } from './datas.service'; @@ -250,16 +250,7 @@ export class DataTableService { viewId: view?.id, dbDriver: await NcConnectionMgrv2.get(base), }); - - const column = await Column.get({colId: param.columnId}) - - if(!column) NcError.badRequest('Column not found' - - if(column.fk_model_id !== model.id) - NcError.badRequest('Column not belong to model') - - if (column.uidt !== UITypes.LinkToAnotherRecord) - NcError.badRequest('Column is not LTAR'); + const column = await this.getColumn(param) const colOptions = await column.getColOptions( ) @@ -296,4 +287,64 @@ export class DataTableService { ...param.query, }); } + + private async getColumn(param: {modelId: string; columnId: string }) { + const column = await Column.get({ colId: param.columnId }) + + if (!column) NcError.badRequest('Column not found' + + if (column.fk_model_id !== param.modelId) + NcError.badRequest('Column not belong to model') + + if (column.uidt !== UITypes.LinkToAnotherRecord) + NcError.badRequest('Column is not LTAR') + return column + } + + async nestedLink(param: { cookie:any;viewId: string; modelId: string; columnId: string; query: any; refRowIds: string | string[] | number | number[]; rowId: string }) { + const { model, view } = await this.getModelAndView(param); + if (!model) NcError.notFound('Table not found'); + + const base = await Base.get(model.base_id); + + const baseModel = await Model.getBaseModelSQL({ + id: model.id, + viewId: view?.id, + dbDriver: await NcConnectionMgrv2.get(base), + }); + + const column = await this.getColumn(param); + + await baseModel.addLinks({ + colId: column.id, + childIds: param.refRowIds, + rowId: param.rowId, + cookie: param.cookie, + }); + + return true; } + + async nestedUnlink(param: { cookie:any;viewId: string; modelId: string; columnId: string; query: any; refRowIds: string | string[] | number | number[]; rowId: string }) { + const { model, view } = await this.getModelAndView(param); + if (!model) NcError.notFound('Table not found'); + + const base = await Base.get(model.base_id); + + const baseModel = await Model.getBaseModelSQL({ + id: model.id, + viewId: view?.id, + dbDriver: await NcConnectionMgrv2.get(base), + }); + + const column = await this.getColumn(param); + + await baseModel.removeLinks({ + colId: column.id, + childIds: param.refRowIds, + rowId: param.rowId, + cookie: param.cookie, + }); + + return true; + } } From 956f20a8f96126d5cfea1d3f9735a2c5be57e964 Mon Sep 17 00:00:00 2001 From: Pranav C Date: Thu, 15 Jun 2023 01:27:46 +0530 Subject: [PATCH 03/13] fix: update where condition in remove/add links method Signed-off-by: Pranav C --- packages/nocodb/src/db/BaseModelSqlv2.ts | 36 ++++++++++++++---------- 1 file changed, 21 insertions(+), 15 deletions(-) diff --git a/packages/nocodb/src/db/BaseModelSqlv2.ts b/packages/nocodb/src/db/BaseModelSqlv2.ts index 4f6157b6eb..c324f796a1 100644 --- a/packages/nocodb/src/db/BaseModelSqlv2.ts +++ b/packages/nocodb/src/db/BaseModelSqlv2.ts @@ -3491,7 +3491,7 @@ class BaseModelSqlv2 { rowId, }: { cookie: any; - childIds: string | string[] | number | number[]; + childIds: (string|number)[]; colId: string; rowId: string; }) { @@ -3525,7 +3525,8 @@ class BaseModelSqlv2 { if (this.isSnowflake) { const parentPK = this.dbDriver(parentTn) .select(parentColumn.column_name) - .where(_wherePk(parentTable.primaryKeys, childId)) + // .where(_wherePk(parentTable.primaryKeys, childId)) + .whereIn(parentTable.primaryKey.column_name, childIds) .first(); const childPK = this.dbDriver(childTn) @@ -3541,7 +3542,8 @@ class BaseModelSqlv2 { await this.dbDriver(vTn).insert({ [vParentCol.column_name]: this.dbDriver(parentTn) .select(parentColumn.column_name) - .where(_wherePk(parentTable.primaryKeys, childId)) + // .where(_wherePk(parentTable.primaryKeys, childId)) + .where(parentTable.primaryKey.column_name, childIds) .first(), [vChildCol.column_name]: this.dbDriver(childTn) .select(childColumn.column_name) @@ -3563,7 +3565,8 @@ class BaseModelSqlv2 { .as('___cn_alias'), ), }) - .where(_wherePk(childTable.primaryKeys, childId)); + // .where(_wherePk(childTable.primaryKeys, childId)); + .whereIn(childTable.primaryKey.column_name, childIds); } break; case RelationTypes.BELONGS_TO: @@ -3573,7 +3576,8 @@ class BaseModelSqlv2 { [childColumn.column_name]: this.dbDriver.from( this.dbDriver(parentTn) .select(parentColumn.column_name) - .where(_wherePk(parentTable.primaryKeys, childId)) + // .where(_wherePk(parentTable.primaryKeys, childId)) + .whereIn(parentTable.primaryKey.column_name, childIds) .first() .as('___cn_alias'), ), @@ -3583,9 +3587,9 @@ class BaseModelSqlv2 { break; } - const response = await this.readByPk(rowId); - await this.afterInsert(response, this.dbDriver, cookie); - await this.afterAddChild(rowId, childId, cookie); + // const response = await this.readByPk(rowId); + // await this.afterInsert(response, this.dbDriver, cookie); + // await this.afterAddChild(rowId, childId, cookie); } async removeLinks({ @@ -3595,7 +3599,7 @@ class BaseModelSqlv2 { rowId, }: { cookie: any; - childIds: string | string[] | number | number[]; + childIds: (string|number)[]; colId: string; rowId: string; }) { @@ -3632,7 +3636,7 @@ class BaseModelSqlv2 { .where({ [vParentCol.column_name]: this.dbDriver(parentTn) .select(parentColumn.column_name) - .where(_wherePk(parentTable.primaryKeys, childId)) + .whereIn(parentTable.primaryKey.column_name,childIds) .first(), [vChildCol.column_name]: this.dbDriver(childTn) .select(childColumn.column_name) @@ -3651,7 +3655,8 @@ class BaseModelSqlv2 { // .where(parentTable.primaryKey.cn, rowId) // .first() // }) - .where(_wherePk(childTable.primaryKeys, childId)) + // .where(_wherePk(childTable.primaryKeys, childId)) + .whereIn(childTable.primaryKey.column_name, childIds) .update({ [childColumn.column_name]: null }); } break; @@ -3664,15 +3669,16 @@ class BaseModelSqlv2 { // .where(parentTable.primaryKey.cn, childId) // .first() // }) - .where(_wherePk(childTable.primaryKeys, rowId)) + // .where(_wherePk(childTable.primaryKeys, rowId)) + .where(childTable.primaryKey.column_name, rowId) .update({ [childColumn.column_name]: null }); } break; } - const newData = await this.readByPk(rowId); - await this.afterUpdate(prevData, newData, this.dbDriver, cookie); - await this.afterRemoveChild(rowId, childId, cookie); + // const newData = await this.readByPk(rowId); + // await this.afterUpdate(prevData, newData, this.dbDriver, cookie); + // await this.afterRemoveChild(rowId, childIds, cookie); } } From 8611b53a620018bf395378fa46a361f0ce8edc15 Mon Sep 17 00:00:00 2001 From: Pranav C Date: Thu, 15 Jun 2023 01:29:15 +0530 Subject: [PATCH 04/13] fix: pass values as array Signed-off-by: Pranav C --- packages/nocodb/src/db/BaseModelSqlv2.ts | 6 +++--- packages/nocodb/src/services/data-table.service.ts | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/nocodb/src/db/BaseModelSqlv2.ts b/packages/nocodb/src/db/BaseModelSqlv2.ts index c324f796a1..e73528f4fe 100644 --- a/packages/nocodb/src/db/BaseModelSqlv2.ts +++ b/packages/nocodb/src/db/BaseModelSqlv2.ts @@ -3491,7 +3491,7 @@ class BaseModelSqlv2 { rowId, }: { cookie: any; - childIds: (string|number)[]; + childIds: (string | number)[]; colId: string; rowId: string; }) { @@ -3599,7 +3599,7 @@ class BaseModelSqlv2 { rowId, }: { cookie: any; - childIds: (string|number)[]; + childIds: (string | number)[]; colId: string; rowId: string; }) { @@ -3636,7 +3636,7 @@ class BaseModelSqlv2 { .where({ [vParentCol.column_name]: this.dbDriver(parentTn) .select(parentColumn.column_name) - .whereIn(parentTable.primaryKey.column_name,childIds) + .whereIn(parentTable.primaryKey.column_name, childIds) .first(), [vChildCol.column_name]: this.dbDriver(childTn) .select(childColumn.column_name) diff --git a/packages/nocodb/src/services/data-table.service.ts b/packages/nocodb/src/services/data-table.service.ts index ddfb72efde..be733a49a5 100644 --- a/packages/nocodb/src/services/data-table.service.ts +++ b/packages/nocodb/src/services/data-table.service.ts @@ -317,7 +317,7 @@ export class DataTableService { await baseModel.addLinks({ colId: column.id, - childIds: param.refRowIds, + childIds: Array.isArray(param.refRowIds) ? param.refRowIds : [param.refRowIds], rowId: param.rowId, cookie: param.cookie, }); @@ -340,7 +340,7 @@ export class DataTableService { await baseModel.removeLinks({ colId: column.id, - childIds: param.refRowIds, + childIds: Array.isArray(param.refRowIds) ? param.refRowIds : [param.refRowIds], rowId: param.rowId, cookie: param.cookie, }); From 77f4b18723a0b93d23f39cd52c689572935ced24 Mon Sep 17 00:00:00 2001 From: Pranav C Date: Thu, 15 Jun 2023 13:17:03 +0530 Subject: [PATCH 05/13] feat: ids validation (WIP) Signed-off-by: Pranav C --- packages/nocodb/src/db/BaseModelSqlv2.ts | 72 ++++++++++++++++++++++++ 1 file changed, 72 insertions(+) diff --git a/packages/nocodb/src/db/BaseModelSqlv2.ts b/packages/nocodb/src/db/BaseModelSqlv2.ts index e73528f4fe..aa3a7ab640 100644 --- a/packages/nocodb/src/db/BaseModelSqlv2.ts +++ b/packages/nocodb/src/db/BaseModelSqlv2.ts @@ -3501,6 +3501,13 @@ class BaseModelSqlv2 { if (!column || column.uidt !== UITypes.LinkToAnotherRecord) NcError.notFound('Column not found'); + // validate parentId + { + if (!this.dbDriver(this.tnPath).where(this._wherePk(rowId)).first()) { + NcError.notFound('Row not found'); + } + } + const colOptions = await column.getColOptions(); const childColumn = await colOptions.getChildColumn(); @@ -3522,6 +3529,30 @@ class BaseModelSqlv2 { const vTn = this.getTnPath(vTable); + // validate Ids + { + const childRowsQb = this.dbDriver(parentTn) + .select(parentColumn.column_name) + // .where(_wherePk(parentTable.primaryKeys, childId)) + .whereIn(parentTable.primaryKey.column_name, childIds); + + if (parentTable.primaryKey.column_name !== parentColumn.column_name) + childRowsQb.select(parentTable.primaryKey.column_name); + + const childRows = await childRowsQb; + + if (childRows.length !== childIds.length) { + const missingIds = childIds.filter( + (id) => + !childRows.find((r) => r[parentColumn.column_name] === id), + ); + + NcError.notFound( + `Child record with id ${missingIds.join(', ')} not found`, + ); + } + } + if (this.isSnowflake) { const parentPK = this.dbDriver(parentTn) .select(parentColumn.column_name) @@ -3555,6 +3586,26 @@ class BaseModelSqlv2 { break; case RelationTypes.HAS_MANY: { + // validate Ids + { + const childRowsQb = this.dbDriver(childTn) + .select(childTable.primaryKey.column_name) + .whereIn(childTable.primaryKey.column_name, childIds); + + const childRows = await childRowsQb; + + if (childRows.length !== childIds.length) { + const missingIds = childIds.filter( + (id) => + !childRows.find((r) => r[parentColumn.column_name] === id), + ); + + NcError.notFound( + `Child record with id ${missingIds.join(', ')} not found`, + ); + } + } + await this.dbDriver(childTn) .update({ [childColumn.column_name]: this.dbDriver.from( @@ -3571,6 +3622,27 @@ class BaseModelSqlv2 { break; case RelationTypes.BELONGS_TO: { + // validate Ids + { + const childRowsQb = this.dbDriver(parentTn) + .select(parentTable.primaryKey.column_name) + .whereIn(parentTable.primaryKey.column_name, childIds) + .first(); + + const childRows = await childRowsQb; + + if (childRows.length !== childIds.length) { + const missingIds = childIds.filter( + (id) => + !childRows.find((r) => r[parentColumn.column_name] === id), + ); + + NcError.notFound( + `Child record with id ${missingIds.join(', ')} not found`, + ); + } + } + await this.dbDriver(childTn) .update({ [childColumn.column_name]: this.dbDriver.from( From 2686dfd126a00b30aaaca630a6f77d5f200cadad Mon Sep 17 00:00:00 2001 From: Pranav C Date: Thu, 15 Jun 2023 14:35:08 +0530 Subject: [PATCH 06/13] feat: ids validation - unlink (WIP) Signed-off-by: Pranav C --- packages/nocodb/src/db/BaseModelSqlv2.ts | 69 ++++++++++++++++++++++++ 1 file changed, 69 insertions(+) diff --git a/packages/nocodb/src/db/BaseModelSqlv2.ts b/packages/nocodb/src/db/BaseModelSqlv2.ts index aa3a7ab640..b4150f94e5 100644 --- a/packages/nocodb/src/db/BaseModelSqlv2.ts +++ b/packages/nocodb/src/db/BaseModelSqlv2.ts @@ -3702,6 +3702,33 @@ class BaseModelSqlv2 { const vParentCol = await colOptions.getMMParentColumn(); const vTable = await colOptions.getMMModel(); + + + // validate Ids + { + const childRowsQb = this.dbDriver(parentTn) + .select(parentColumn.column_name) + // .where(_wherePk(parentTable.primaryKeys, childId)) + .whereIn(parentTable.primaryKey.column_name, childIds); + + if (parentTable.primaryKey.column_name !== parentColumn.column_name) + childRowsQb.select(parentTable.primaryKey.column_name); + + const childRows = await childRowsQb; + + if (childRows.length !== childIds.length) { + const missingIds = childIds.filter( + (id) => + !childRows.find((r) => r[parentColumn.column_name] === id), + ); + + NcError.notFound( + `Child record with id ${missingIds.join(', ')} not found`, + ); + } + } + + const vTn = this.getTnPath(vTable); await this.dbDriver(vTn) @@ -3720,6 +3747,27 @@ class BaseModelSqlv2 { break; case RelationTypes.HAS_MANY: { + + // validate Ids + { + const childRowsQb = this.dbDriver(childTn) + .select(childTable.primaryKey.column_name) + .whereIn(childTable.primaryKey.column_name, childIds); + + const childRows = await childRowsQb; + + if (childRows.length !== childIds.length) { + const missingIds = childIds.filter( + (id) => + !childRows.find((r) => r[parentColumn.column_name] === id), + ); + + NcError.notFound( + `Child record with id ${missingIds.join(', ')} not found`, + ); + } + } + await this.dbDriver(childTn) // .where({ // [childColumn.cn]: this.dbDriver(parentTable.tn) @@ -3734,6 +3782,27 @@ class BaseModelSqlv2 { break; case RelationTypes.BELONGS_TO: { + // validate Ids + { + const childRowsQb = this.dbDriver(parentTn) + .select(parentTable.primaryKey.column_name) + .whereIn(parentTable.primaryKey.column_name, childIds) + .first(); + + const childRows = await childRowsQb; + + if (childRows.length !== childIds.length) { + const missingIds = childIds.filter( + (id) => + !childRows.find((r) => r[parentColumn.column_name] === id), + ); + + NcError.notFound( + `Child record with id ${missingIds.join(', ')} not found`, + ); + } + } + await this.dbDriver(childTn) // .where({ // [childColumn.cn]: this.dbDriver(parentTable.tn) From bd1acf44268f5a950fb4da65c7e4346cddd8210b Mon Sep 17 00:00:00 2001 From: Pranav C Date: Thu, 15 Jun 2023 15:30:54 +0530 Subject: [PATCH 07/13] feat: skip existing links while inserting and consider composite pk as well (WIP) Signed-off-by: Pranav C --- packages/nocodb/src/db/BaseModelSqlv2.ts | 131 +++++++++++++++-------- 1 file changed, 85 insertions(+), 46 deletions(-) diff --git a/packages/nocodb/src/db/BaseModelSqlv2.ts b/packages/nocodb/src/db/BaseModelSqlv2.ts index b4150f94e5..f362a6b779 100644 --- a/packages/nocodb/src/db/BaseModelSqlv2.ts +++ b/packages/nocodb/src/db/BaseModelSqlv2.ts @@ -3501,11 +3501,13 @@ class BaseModelSqlv2 { if (!column || column.uidt !== UITypes.LinkToAnotherRecord) NcError.notFound('Column not found'); - // validate parentId - { - if (!this.dbDriver(this.tnPath).where(this._wherePk(rowId)).first()) { - NcError.notFound('Row not found'); - } + const row = await this.dbDriver(this.tnPath) + .where(this._wherePk(rowId)) + .first(); + + // validate rowId + if (!row) { + NcError.notFound('Row not found'); } const colOptions = await column.getColOptions(); @@ -3529,12 +3531,33 @@ class BaseModelSqlv2 { const vTn = this.getTnPath(vTable); + let insertData: Record[]; + // validate Ids { const childRowsQb = this.dbDriver(parentTn) .select(parentColumn.column_name) - // .where(_wherePk(parentTable.primaryKeys, childId)) - .whereIn(parentTable.primaryKey.column_name, childIds); + .select(`${vTable.table_name}.${vChildCol.column_name}`) + .leftJoin(vTn, (qb) => { + qb.on( + `${vTable.table_name}.${vParentCol.column_name}`, + `${parentTable.table_name}.${parentColumn.column_name}`, + ).orOn( + `${vTable.table_name}.${vChildCol.column_name}`, + row[childColumn.column_name], + ); + }); + // .where(_wherePk(parentTable.primaryKeys, childId)) + + if (parentTable.primaryKeys.length > 1) { + childRowsQb.where((qb) => { + for (const childId of childIds) { + qb.orWhere(_wherePk(parentTable.primaryKeys, childId)); + } + }); + } else { + childRowsQb.whereIn(parentTable.primaryKey.column_name, childIds); + } if (parentTable.primaryKey.column_name !== parentColumn.column_name) childRowsQb.select(parentTable.primaryKey.column_name); @@ -3551,46 +3574,66 @@ class BaseModelSqlv2 { `Child record with id ${missingIds.join(', ')} not found`, ); } - } - if (this.isSnowflake) { - const parentPK = this.dbDriver(parentTn) - .select(parentColumn.column_name) - // .where(_wherePk(parentTable.primaryKeys, childId)) - .whereIn(parentTable.primaryKey.column_name, childIds) - .first(); - - const childPK = this.dbDriver(childTn) - .select(childColumn.column_name) - .where(_wherePk(childTable.primaryKeys, rowId)) - .first(); - - await this.dbDriver.raw( - `INSERT INTO ?? (??, ??) SELECT (${parentPK.toQuery()}), (${childPK.toQuery()})`, - [vTn, vParentCol.column_name, vChildCol.column_name], - ); - } else { - await this.dbDriver(vTn).insert({ - [vParentCol.column_name]: this.dbDriver(parentTn) - .select(parentColumn.column_name) - // .where(_wherePk(parentTable.primaryKeys, childId)) - .where(parentTable.primaryKey.column_name, childIds) - .first(), - [vChildCol.column_name]: this.dbDriver(childTn) - .select(childColumn.column_name) - .where(_wherePk(childTable.primaryKeys, rowId)) - .first(), - }); + insertData = childRows + // skip existing links + .filter((childRow) => !childRow[vChildCol.column_name]) + // generate insert data for new links + .map((childRow) => ({ + [vParentCol.column_name]: childRow[parentColumn.column_name], + [vChildCol.column_name]: row[childColumn.column_name], + })); } + + // if (this.isSnowflake) { + // const parentPK = this.dbDriver(parentTn) + // .select(parentColumn.column_name) + // // .where(_wherePk(parentTable.primaryKeys, childId)) + // .whereIn(parentTable.primaryKey.column_name, childIds) + // .first(); + // + // const childPK = this.dbDriver(childTn) + // .select(childColumn.column_name) + // .where(_wherePk(childTable.primaryKeys, rowId)) + // .first(); + // + // await this.dbDriver.raw( + // `INSERT INTO ?? (??, ??) SELECT (${parentPK.toQuery()}), (${childPK.toQuery()})`, + // [vTn, vParentCol.column_name, vChildCol.column_name], + // ); + // } else { + // await this.dbDriver(vTn).insert({ + // [vParentCol.column_name]: this.dbDriver(parentTn) + // .select(parentColumn.column_name) + // // .where(_wherePk(parentTable.primaryKeys, childId)) + // .where(parentTable.primaryKey.column_name, childIds) + // .first(), + // [vChildCol.column_name]: this.dbDriver(childTn) + // .select(childColumn.column_name) + // .where(_wherePk(childTable.primaryKeys, rowId)) + // .first(), + // }); + await this.dbDriver(vTn).bulkInsert(insertData); + // } } break; case RelationTypes.HAS_MANY: { // validate Ids { - const childRowsQb = this.dbDriver(childTn) - .select(childTable.primaryKey.column_name) - .whereIn(childTable.primaryKey.column_name, childIds); + const childRowsQb = this.dbDriver(childTn).select( + childTable.primaryKey.column_name, + ); + + if (childTable.primaryKeys.length > 1) { + childRowsQb.where((qb) => { + for (const childId of childIds) { + qb.orWhere(_wherePk(childTable.primaryKeys, childId)); + } + }); + } else { + childRowsQb.whereIn(parentTable.primaryKey.column_name, childIds); + } const childRows = await childRowsQb; @@ -3626,7 +3669,7 @@ class BaseModelSqlv2 { { const childRowsQb = this.dbDriver(parentTn) .select(parentTable.primaryKey.column_name) - .whereIn(parentTable.primaryKey.column_name, childIds) + .where(_wherePk(parentTable.primaryKeys, childIds[0])) .first(); const childRows = await childRowsQb; @@ -3648,8 +3691,8 @@ class BaseModelSqlv2 { [childColumn.column_name]: this.dbDriver.from( this.dbDriver(parentTn) .select(parentColumn.column_name) - // .where(_wherePk(parentTable.primaryKeys, childId)) - .whereIn(parentTable.primaryKey.column_name, childIds) + .where(_wherePk(parentTable.primaryKeys, childIds[0])) + // .whereIn(parentTable.primaryKey.column_name, childIds) .first() .as('___cn_alias'), ), @@ -3702,8 +3745,6 @@ class BaseModelSqlv2 { const vParentCol = await colOptions.getMMParentColumn(); const vTable = await colOptions.getMMModel(); - - // validate Ids { const childRowsQb = this.dbDriver(parentTn) @@ -3728,7 +3769,6 @@ class BaseModelSqlv2 { } } - const vTn = this.getTnPath(vTable); await this.dbDriver(vTn) @@ -3747,7 +3787,6 @@ class BaseModelSqlv2 { break; case RelationTypes.HAS_MANY: { - // validate Ids { const childRowsQb = this.dbDriver(childTn) From c9ebfad4671c8107ad6227b4cc08437cd6eddea0 Mon Sep 17 00:00:00 2001 From: Pranav C Date: Thu, 15 Jun 2023 15:58:29 +0530 Subject: [PATCH 08/13] fix: mm unlink implementation Signed-off-by: Pranav C --- packages/nocodb/src/db/BaseModelSqlv2.ts | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/packages/nocodb/src/db/BaseModelSqlv2.ts b/packages/nocodb/src/db/BaseModelSqlv2.ts index f362a6b779..cae38e99ef 100644 --- a/packages/nocodb/src/db/BaseModelSqlv2.ts +++ b/packages/nocodb/src/db/BaseModelSqlv2.ts @@ -3773,15 +3773,18 @@ class BaseModelSqlv2 { await this.dbDriver(vTn) .where({ - [vParentCol.column_name]: this.dbDriver(parentTn) - .select(parentColumn.column_name) - .whereIn(parentTable.primaryKey.column_name, childIds) - .first(), [vChildCol.column_name]: this.dbDriver(childTn) .select(childColumn.column_name) .where(_wherePk(childTable.primaryKeys, rowId)) .first(), }) + .whereIn( + [vParentCol.column_name], + this.dbDriver(parentTn) + .select(parentColumn.column_name) + .whereIn(parentTable.primaryKey.column_name, childIds) + .first(), + ) .delete(); } break; From ae1a31b4032d745ab935909ada3e611f7112a9e5 Mon Sep 17 00:00:00 2001 From: Pranav C Date: Thu, 15 Jun 2023 16:52:23 +0530 Subject: [PATCH 09/13] fix: typo corrections Signed-off-by: Pranav C --- .../src/controllers/data-table.controller.ts | 2 + packages/nocodb/src/db/BaseModelSqlv2.ts | 4 +- .../nocodb/src/modules/datas/datas.module.ts | 2 +- .../nocodb/src/services/data-table.service.ts | 77 ++++++++++++------- 4 files changed, 57 insertions(+), 28 deletions(-) diff --git a/packages/nocodb/src/controllers/data-table.controller.ts b/packages/nocodb/src/controllers/data-table.controller.ts index eaac1c6e6a..ba235db747 100644 --- a/packages/nocodb/src/controllers/data-table.controller.ts +++ b/packages/nocodb/src/controllers/data-table.controller.ts @@ -164,6 +164,7 @@ export class DataTableController { viewId, columnId, refRowIds, + cookie: req, }); } @@ -185,6 +186,7 @@ export class DataTableController { viewId, columnId, refRowIds, + cookie: req, }); } } diff --git a/packages/nocodb/src/db/BaseModelSqlv2.ts b/packages/nocodb/src/db/BaseModelSqlv2.ts index cae38e99ef..016e697b72 100644 --- a/packages/nocodb/src/db/BaseModelSqlv2.ts +++ b/packages/nocodb/src/db/BaseModelSqlv2.ts @@ -3613,7 +3613,9 @@ class BaseModelSqlv2 { // .where(_wherePk(childTable.primaryKeys, rowId)) // .first(), // }); - await this.dbDriver(vTn).bulkInsert(insertData); + + // todo: use bulk insert + await this.dbDriver(vTn).insert(insertData); // } } break; diff --git a/packages/nocodb/src/modules/datas/datas.module.ts b/packages/nocodb/src/modules/datas/datas.module.ts index 3d72f2b1b4..9ee4adeecf 100644 --- a/packages/nocodb/src/modules/datas/datas.module.ts +++ b/packages/nocodb/src/modules/datas/datas.module.ts @@ -29,7 +29,7 @@ import { PublicDatasService } from '../../services/public-datas.service'; }), ], controllers: [ - process.env.NC_WORKER_CONTAINER !== 'true' + ...(process.env.NC_WORKER_CONTAINER !== 'true' ? [ DataTableController, DatasController, diff --git a/packages/nocodb/src/services/data-table.service.ts b/packages/nocodb/src/services/data-table.service.ts index be733a49a5..e301ed1267 100644 --- a/packages/nocodb/src/services/data-table.service.ts +++ b/packages/nocodb/src/services/data-table.service.ts @@ -1,11 +1,16 @@ import { Injectable } from '@nestjs/common'; -import { RelationTypes, UITypes } from 'nocodb-sdk' +import { RelationTypes, UITypes } from 'nocodb-sdk'; import { NcError } from '../helpers/catchError'; -import { PagedResponseImpl } from '../helpers/PagedResponse' -import { Base, Column, LinkToAnotherRecordColumn, Model, View } from '../models' -import { getColumnByIdOrName, getViewAndModelByAliasOrId, PathParams } from '../modules/datas/helpers' +import { PagedResponseImpl } from '../helpers/PagedResponse'; +import { Base, Column, Model, View } from '../models'; +import { + getColumnByIdOrName, + getViewAndModelByAliasOrId, + PathParams, +} from '../modules/datas/helpers'; import NcConnectionMgrv2 from '../utils/common/NcConnectionMgrv2'; import { DatasService } from './datas.service'; +import type { LinkToAnotherRecordColumn } from '../models'; @Injectable() export class DataTableService { @@ -250,14 +255,13 @@ export class DataTableService { viewId: view?.id, dbDriver: await NcConnectionMgrv2.get(base), }); - const column = await this.getColumn(param) + const column = await this.getColumn(param); - const colOptions = await column.getColOptions( - ) + const colOptions = await column.getColOptions(); - let data:any[] ; - let count:number; - if(colOptions.type === RelationTypes.MANY_TO_MANY) { + let data: any[]; + let count: number; + if (colOptions.type === RelationTypes.MANY_TO_MANY) { data = await baseModel.mmList( { colId: column.id, @@ -265,11 +269,11 @@ export class DataTableService { }, param.query as any, ); - count = await baseModel.mmListCount({ + count = (await baseModel.mmListCount({ colId: column.id, parentId: param.rowId, - }); - }else { + })) as number; + } else { data = await baseModel.hmList( { colId: column.id, @@ -277,10 +281,10 @@ export class DataTableService { }, param.query as any, ); - count = await baseModel.hmListCount({ + count = (await baseModel.hmListCount({ colId: column.id, id: param.rowId, - }); + })) as number; } return new PagedResponseImpl(data, { count, @@ -288,20 +292,28 @@ export class DataTableService { }); } - private async getColumn(param: {modelId: string; columnId: string }) { - const column = await Column.get({ colId: param.columnId }) + private async getColumn(param: { modelId: string; columnId: string }) { + const column = await Column.get({ colId: param.columnId }); - if (!column) NcError.badRequest('Column not found' + if (!column) NcError.badRequest('Column not found'); if (column.fk_model_id !== param.modelId) - NcError.badRequest('Column not belong to model') + NcError.badRequest('Column not belong to model'); if (column.uidt !== UITypes.LinkToAnotherRecord) - NcError.badRequest('Column is not LTAR') - return column + NcError.badRequest('Column is not LTAR'); + return column; } - async nestedLink(param: { cookie:any;viewId: string; modelId: string; columnId: string; query: any; refRowIds: string | string[] | number | number[]; rowId: string }) { + async nestedLink(param: { + cookie: any; + viewId: string; + modelId: string; + columnId: string; + query: any; + refRowIds: string | string[] | number | number[]; + rowId: string; + }) { const { model, view } = await this.getModelAndView(param); if (!model) NcError.notFound('Table not found'); @@ -317,14 +329,25 @@ export class DataTableService { await baseModel.addLinks({ colId: column.id, - childIds: Array.isArray(param.refRowIds) ? param.refRowIds : [param.refRowIds], + childIds: Array.isArray(param.refRowIds) + ? param.refRowIds + : [param.refRowIds], rowId: param.rowId, cookie: param.cookie, }); - return true; } + return true; + } - async nestedUnlink(param: { cookie:any;viewId: string; modelId: string; columnId: string; query: any; refRowIds: string | string[] | number | number[]; rowId: string }) { + async nestedUnlink(param: { + cookie: any; + viewId: string; + modelId: string; + columnId: string; + query: any; + refRowIds: string | string[] | number | number[]; + rowId: string; + }) { const { model, view } = await this.getModelAndView(param); if (!model) NcError.notFound('Table not found'); @@ -340,7 +363,9 @@ export class DataTableService { await baseModel.removeLinks({ colId: column.id, - childIds: Array.isArray(param.refRowIds) ? param.refRowIds : [param.refRowIds], + childIds: Array.isArray(param.refRowIds) + ? param.refRowIds + : [param.refRowIds], rowId: param.rowId, cookie: param.cookie, }); From efadf11e94ca4c63b05464ad4695ec8b047f1527 Mon Sep 17 00:00:00 2001 From: Pranav C Date: Thu, 15 Jun 2023 17:36:17 +0530 Subject: [PATCH 10/13] fix: typo correction Signed-off-by: Pranav C --- packages/nocodb/src/controllers/data-table.controller.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/nocodb/src/controllers/data-table.controller.ts b/packages/nocodb/src/controllers/data-table.controller.ts index ba235db747..a67526c6f9 100644 --- a/packages/nocodb/src/controllers/data-table.controller.ts +++ b/packages/nocodb/src/controllers/data-table.controller.ts @@ -147,8 +147,8 @@ export class DataTableController { } - @Get(['/api/v1/tables/:modelId/links/:columnId/row/:rowId']) - @Post('nestedDataLink') + @Post(['/api/v1/tables/:modelId/links/:columnId/row/:rowId']) + @Acl('nestedDataLink') async nestedLink( @Request() req, @Param('modelId') modelId: string, From 5b04a70d5b44ae617d1f85f8ba74a0b112e24a53 Mon Sep 17 00:00:00 2001 From: Pranav C Date: Thu, 15 Jun 2023 18:23:39 +0530 Subject: [PATCH 11/13] fix: link/unlink - mm Signed-off-by: Pranav C --- packages/nocodb/src/db/BaseModelSqlv2.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/packages/nocodb/src/db/BaseModelSqlv2.ts b/packages/nocodb/src/db/BaseModelSqlv2.ts index 016e697b72..6b7b6424c7 100644 --- a/packages/nocodb/src/db/BaseModelSqlv2.ts +++ b/packages/nocodb/src/db/BaseModelSqlv2.ts @@ -3542,7 +3542,7 @@ class BaseModelSqlv2 { qb.on( `${vTable.table_name}.${vParentCol.column_name}`, `${parentTable.table_name}.${parentColumn.column_name}`, - ).orOn( + ).andOn( `${vTable.table_name}.${vChildCol.column_name}`, row[childColumn.column_name], ); @@ -3583,6 +3583,9 @@ class BaseModelSqlv2 { [vParentCol.column_name]: childRow[parentColumn.column_name], [vChildCol.column_name]: row[childColumn.column_name], })); + + // if no new links, return true + if(!insertData.length) return true } // if (this.isSnowflake) { @@ -3785,7 +3788,6 @@ class BaseModelSqlv2 { this.dbDriver(parentTn) .select(parentColumn.column_name) .whereIn(parentTable.primaryKey.column_name, childIds) - .first(), ) .delete(); } From bb6f311b4873051e4b5c2cb86f1050476715f282 Mon Sep 17 00:00:00 2001 From: Pranav C Date: Thu, 15 Jun 2023 18:43:08 +0530 Subject: [PATCH 12/13] fix: add missing await and validation Signed-off-by: Pranav C --- packages/nocodb/src/db/BaseModelSqlv2.ts | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/packages/nocodb/src/db/BaseModelSqlv2.ts b/packages/nocodb/src/db/BaseModelSqlv2.ts index 6b7b6424c7..82ec7f99fc 100644 --- a/packages/nocodb/src/db/BaseModelSqlv2.ts +++ b/packages/nocodb/src/db/BaseModelSqlv2.ts @@ -3502,7 +3502,7 @@ class BaseModelSqlv2 { NcError.notFound('Column not found'); const row = await this.dbDriver(this.tnPath) - .where(this._wherePk(rowId)) + .where(await this._wherePk(rowId)) .first(); // validate rowId @@ -3585,7 +3585,7 @@ class BaseModelSqlv2 { })); // if no new links, return true - if(!insertData.length) return true + if (!insertData.length) return true; } // if (this.isSnowflake) { @@ -3729,6 +3729,15 @@ class BaseModelSqlv2 { if (!column || column.uidt !== UITypes.LinkToAnotherRecord) NcError.notFound('Column not found'); + const row = await this.dbDriver(this.tnPath) + .where(await this._wherePk(rowId)) + .first(); + + // validate rowId + if (!row) { + NcError.notFound('Row not found'); + } + const colOptions = await column.getColOptions(); const childColumn = await colOptions.getChildColumn(); @@ -3787,7 +3796,7 @@ class BaseModelSqlv2 { [vParentCol.column_name], this.dbDriver(parentTn) .select(parentColumn.column_name) - .whereIn(parentTable.primaryKey.column_name, childIds) + .whereIn(parentTable.primaryKey.column_name, childIds), ) .delete(); } From d5e41e7621bd82ed22b3182aa23d247e8068bbf9 Mon Sep 17 00:00:00 2001 From: Pranav C Date: Thu, 15 Jun 2023 18:57:16 +0530 Subject: [PATCH 13/13] fix: bt link api correction Signed-off-by: Pranav C --- packages/nocodb/src/db/BaseModelSqlv2.ts | 28 ++++++------------------ 1 file changed, 7 insertions(+), 21 deletions(-) diff --git a/packages/nocodb/src/db/BaseModelSqlv2.ts b/packages/nocodb/src/db/BaseModelSqlv2.ts index 82ec7f99fc..f161de2d7c 100644 --- a/packages/nocodb/src/db/BaseModelSqlv2.ts +++ b/packages/nocodb/src/db/BaseModelSqlv2.ts @@ -3677,17 +3677,10 @@ class BaseModelSqlv2 { .where(_wherePk(parentTable.primaryKeys, childIds[0])) .first(); - const childRows = await childRowsQb; + const childRow = await childRowsQb; - if (childRows.length !== childIds.length) { - const missingIds = childIds.filter( - (id) => - !childRows.find((r) => r[parentColumn.column_name] === id), - ); - - NcError.notFound( - `Child record with id ${missingIds.join(', ')} not found`, - ); + if (!childRow) { + NcError.notFound(`Child record with id ${childIds[0]} not found`); } } @@ -3841,20 +3834,13 @@ class BaseModelSqlv2 { { const childRowsQb = this.dbDriver(parentTn) .select(parentTable.primaryKey.column_name) - .whereIn(parentTable.primaryKey.column_name, childIds) + .where(_wherePk(parentTable.primaryKeys, childIds[0])) .first(); - const childRows = await childRowsQb; - - if (childRows.length !== childIds.length) { - const missingIds = childIds.filter( - (id) => - !childRows.find((r) => r[parentColumn.column_name] === id), - ); + const childRow = await childRowsQb; - NcError.notFound( - `Child record with id ${missingIds.join(', ')} not found`, - ); + if (!childRow) { + NcError.notFound(`Child record with id ${childIds[0]} not found`); } }