Browse Source

Merge pull request #6748 from nocodb/nc-fix/1579-api-v2

Data apis v2 - support array of objects with link/unlink api
pull/6749/head
Pranav C 1 year ago committed by GitHub
parent
commit
287802d65d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 4
      packages/nocodb/src/controllers/data-table.controller.ts
  2. 195
      packages/nocodb/src/db/BaseModelSqlv2.ts
  3. 4
      packages/nocodb/src/services/data-table.service.ts
  4. 48
      packages/nocodb/tests/unit/rest/tests/newDataApis.test.ts
  5. BIN
      packages/nocodb/tests/unit/test_meta.db

4
packages/nocodb/src/controllers/data-table.controller.ts

@ -151,7 +151,7 @@ export class DataTableController {
@Query('viewId') viewId: string,
@Param('columnId') columnId: string,
@Param('rowId') rowId: string,
@Body() refRowIds: string | string[] | number | number[],
@Body() refRowIds: string | string[] | number | number[] | Record<string, any>,
) {
return await this.dataTableService.nestedLink({
modelId,
@ -172,7 +172,7 @@ export class DataTableController {
@Query('viewId') viewId: string,
@Param('columnId') columnId: string,
@Param('rowId') rowId: string,
@Body() refRowIds: string | string[] | number | number[],
@Body() refRowIds: string | string[] | number | number[] | Record<string, any>,
) {
return await this.dataTableService.nestedUnlink({
modelId,

195
packages/nocodb/src/db/BaseModelSqlv2.ts

@ -4332,7 +4332,7 @@ class BaseModelSqlv2 {
rowId,
}: {
cookie: any;
childIds: (string | number)[];
childIds: (string | number | Record<string, any>)[];
colId: string;
rowId: string;
}) {
@ -4399,7 +4399,16 @@ class BaseModelSqlv2 {
}
});
} else {
childRowsQb.whereIn(parentTable.primaryKey.column_name, childIds);
childRowsQb.whereIn(
parentTable.primaryKey.column_name,
typeof childIds[0] === 'object'
? childIds.map(
(c) =>
c[parentTable.primaryKey.title] ||
c[parentTable.primaryKey.column_name],
)
: childIds,
);
}
if (parentTable.primaryKey.column_name !== parentColumn.column_name)
@ -4431,38 +4440,8 @@ class BaseModelSqlv2 {
if (!insertData.length) return true;
}
// 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(),
// });
// todo: use bulk insert
await this.dbDriver(vTn).insert(insertData);
// }
}
break;
case RelationTypes.HAS_MANY:
@ -4480,7 +4459,16 @@ class BaseModelSqlv2 {
}
});
} else {
childRowsQb.whereIn(parentTable.primaryKey.column_name, childIds);
childRowsQb.whereIn(
parentTable.primaryKey.column_name,
typeof childIds[0] === 'object'
? childIds.map(
(c) =>
c[parentTable.primaryKey.title] ||
c[parentTable.primaryKey.column_name],
)
: childIds,
);
}
const childRows = await childRowsQb;
@ -4496,19 +4484,34 @@ class BaseModelSqlv2 {
);
}
}
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));
.whereIn(childTable.primaryKey.column_name, childIds);
const updateQb = 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'),
),
});
if (childTable.primaryKeys.length > 1) {
updateQb.where((qb) => {
for (const childId of childIds) {
qb.orWhere(_wherePk(childTable.primaryKeys, childId));
}
});
} else {
updateQb.whereIn(
childTable.primaryKey.column_name,
typeof childIds[0] === 'object'
? childIds.map(
(c) =>
c[childTable.primaryKey.title] ||
c[childTable.primaryKey.column_name],
)
: childIds,
);
}
await updateQb;
}
break;
case RelationTypes.BELONGS_TO:
@ -4557,7 +4560,7 @@ class BaseModelSqlv2 {
rowId,
}: {
cookie: any;
childIds: (string | number)[];
childIds: (string | number | Record<string, any>)[];
colId: string;
rowId: string;
}) {
@ -4601,10 +4604,28 @@ class BaseModelSqlv2 {
// validate Ids
{
const childRowsQb = this.dbDriver(parentTn)
.select(parentColumn.column_name)
// .where(_wherePk(parentTable.primaryKeys, childId))
.whereIn(parentTable.primaryKey.column_name, childIds);
const childRowsQb = this.dbDriver(parentTn).select(
parentColumn.column_name,
);
if (parentTable.primaryKeys.length > 1) {
childRowsQb.where((qb) => {
for (const childId of childIds) {
qb.orWhere(_wherePk(parentTable.primaryKeys, childId));
}
});
} else if (typeof childIds[0] === 'object') {
childRowsQb.whereIn(
parentTable.primaryKey.column_name,
childIds.map(
(c) =>
c[parentTable.primaryKey.title] ||
c[parentTable.primaryKey.column_name],
),
);
} else {
childRowsQb.whereIn(parentTable.primaryKey.column_name, childIds);
}
if (parentTable.primaryKey.column_name !== parentColumn.column_name)
childRowsQb.select(parentTable.primaryKey.column_name);
@ -4625,20 +4646,26 @@ class BaseModelSqlv2 {
const vTn = this.getTnPath(vTable);
await this.dbDriver(vTn)
const delQb = this.dbDriver(vTn)
.where({
[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),
)
.delete();
delQb.whereIn(
vParentCol.column_name,
typeof childIds[0] === 'object'
? childIds.map(
(c) =>
c[parentTable.primaryKey.title] ||
c[parentTable.primaryKey.column_name],
)
: childIds,
);
await delQb;
}
break;
case RelationTypes.HAS_MANY:
@ -4663,16 +4690,28 @@ class BaseModelSqlv2 {
}
}
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))
.whereIn(childTable.primaryKey.column_name, childIds)
.update({ [childColumn.column_name]: null });
const childRowsQb = this.dbDriver(childTn);
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,
typeof childIds[0] === 'object'
? childIds.map(
(c) =>
c[parentTable.primaryKey.title] ||
c[parentTable.primaryKey.column_name],
)
: childIds,
);
}
await childRowsQb.update({ [childColumn.column_name]: null });
}
break;
case RelationTypes.BELONGS_TO:
@ -4973,13 +5012,27 @@ function applyPaginate(
}
export function _wherePk(primaryKeys: Column[], id: unknown | unknown[]) {
const where = {};
// if id object is provided use as it is
if (id && typeof id === 'object') {
if (id && typeof id === 'object' && !Array.isArray(id)) {
// verify all pk columns are present in id object
for (const pk of primaryKeys) {
if (pk.title in id) {
where[pk.column_name] = id[pk.title];
} else if (pk.column_name in id) {
where[pk.column_name] = id[pk.column_name];
} else {
NcError.badRequest(
`Primary key column ${pk.title} not found in id object`,
);
}
}
return id;
}
const ids = Array.isArray(id) ? id : (id + '').split('___');
const where = {};
for (let i = 0; i < primaryKeys.length; ++i) {
if (primaryKeys[i].dt === 'bytea') {
// if column is bytea, then we need to encode the id to hex based on format
@ -5064,7 +5117,7 @@ export function getListArgs(
{ ignoreAssigningWildcardSelect = false } = {},
): XcFilter {
const obj: XcFilter = {};
obj.where = args.where || args.w || '';
obj.where = args.where || args.filter || args.w || '';
obj.having = args.having || args.h || '';
obj.shuffle = args.shuffle || args.r || '';
obj.condition = args.condition || args.c || {};

4
packages/nocodb/src/services/data-table.service.ts

@ -368,7 +368,7 @@ export class DataTableService {
modelId: string;
columnId: string;
query: any;
refRowIds: string | string[] | number | number[];
refRowIds: string | string[] | number | number[]| Record<string, any>;
rowId: string;
}) {
this.validateIds(param.refRowIds);
@ -403,7 +403,7 @@ export class DataTableService {
modelId: string;
columnId: string;
query: any;
refRowIds: string | string[] | number | number[];
refRowIds: string | string[] | number | number[]| Record<string, any>;
rowId: string;
}) {
this.validateIds(param.refRowIds);

48
packages/nocodb/tests/unit/rest/tests/newDataApis.test.ts

@ -1947,7 +1947,26 @@ function linkBased() {
rowId: 1,
},
body: [
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20,
{ Id: 1 },
{ Id: 2 },
{ Id: 3 },
{ Id: 4 },
{ Id: 5 },
{ Id: 6 },
{ Id: 7 },
{ Id: 8 },
{ Id: 9 },
{ Id: 10 },
{ Id: 11 },
{ Id: 12 },
{ Id: 13 },
{ Id: 14 },
{ Id: 15 },
{ Id: 16 },
{ Id: 17 },
{ Id: 18 },
{ Id: 19 },
{ Id: 20 },
],
});
await ncAxiosLinkAdd({
@ -1957,7 +1976,26 @@ function linkBased() {
rowId: 1,
},
body: [
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20,
{ Id: 1 },
{ Id: 2 },
{ Id: 3 },
{ Id: 4 },
{ Id: 5 },
{ Id: 6 },
{ Id: 7 },
{ Id: 8 },
{ Id: 9 },
{ Id: 10 },
{ Id: 11 },
{ Id: 12 },
{ Id: 13 },
{ Id: 14 },
{ Id: 15 },
{ Id: 16 },
{ Id: 17 },
{ Id: 18 },
{ Id: 19 },
{ Id: 20 },
],
});
@ -2071,7 +2109,7 @@ function linkBased() {
linkId: getColumnId(columnsActor, 'Films'),
rowId: 1,
},
body: [1, 3, 5, 7, 9, 11, 13, 15, 17, 19, 21, 23, 25, 27, 29],
body: [{Id:1}, {Id:3}, {Id:5}, {Id:7}, {Id:9}, {Id:11}, {Id:13}, {Id:15}, {Id:17}, {Id:19}, {Id:21}, {Id:23}, {Id:25}, {Id:27}, {Id:29}],
});
// verify in Actor table
@ -2121,7 +2159,7 @@ function linkBased() {
linkId: getColumnId(columnsCountry, 'Cities'),
rowId: 1,
},
body: [1, 2, 3],
body: [{Id:1}, {Id:2}, {Id:3}],
});
// update the link
@ -2131,7 +2169,7 @@ function linkBased() {
linkId: getColumnId(columnsCountry, 'Cities'),
rowId: 2,
},
body: [2, 3],
body: [{Id:2}, {Id:3}],
});
// verify record 1

BIN
packages/nocodb/tests/unit/test_meta.db

Binary file not shown.
Loading…
Cancel
Save