From e43d2cc49b8d752e09878561404132fc855a32e3 Mon Sep 17 00:00:00 2001 From: Pranav C Date: Sat, 8 Jan 2022 12:05:37 +0530 Subject: [PATCH] feat: support filter and pagination param in nested resolvers Signed-off-by: Pranav C --- .../spreadsheet/components/editColumn.vue | 7 -- packages/nocodb/package-lock.json | 14 +-- packages/nocodb/package.json | 2 +- .../lib/dataMapper/lib/sql/BaseModelSql.ts | 10 ++- packages/nocodb/src/lib/noco/Noco.ts | 5 +- .../src/lib/noco/common/BaseApiBuilder.ts | 6 +- .../nocodb/src/lib/noco/gql/GqlApiBuilder.ts | 88 +++++++++++++++++-- .../nocodb/src/lib/noco/rest/RestAuthCtrl.ts | 4 +- .../src/lib/noco/rest/RestAuthCtrlEE.ts | 2 +- .../gql-schema/xc-ts/BaseGqlXcTsSchema.ts | 4 +- 10 files changed, 107 insertions(+), 35 deletions(-) diff --git a/packages/nc-gui/components/project/spreadsheet/components/editColumn.vue b/packages/nc-gui/components/project/spreadsheet/components/editColumn.vue index 5bb5b25ca2..3205f6bd4f 100644 --- a/packages/nc-gui/components/project/spreadsheet/components/editColumn.vue +++ b/packages/nc-gui/components/project/spreadsheet/components/editColumn.vue @@ -99,13 +99,6 @@ - - - - - - - { name: '0009044', handler: this.ncUpManyToMany.bind(this) }, { name: '0083006', handler: ncModelsOrderUpgrader }, { name: '0083007', handler: ncParentModelTitleUpgrader }, - { name: '0083008', handler: ncRemoveDuplicatedRelationRows } + { name: '0083008', handler: ncRemoveDuplicatedRelationRows }, + { name: '0084002', handler: this.ncUpAddNestedResolverArgs.bind(this) } ]; if (!(await this.xcMeta?.knex?.schema?.hasTable?.('nc_store'))) { return; @@ -2453,6 +2454,8 @@ export default abstract class BaseApiBuilder return metas; } + protected async ncUpAddNestedResolverArgs(_ctx: any): Promise {} + protected async ncUpManyToMany(_ctx: any): Promise { const models = await this.xcMeta.metaList( this.projectId, @@ -3238,6 +3241,7 @@ interface NcMetaData { [key: string]: any; } + type XcTablesPopulateParams = { tableNames?: Array<{ tn: string; diff --git a/packages/nocodb/src/lib/noco/gql/GqlApiBuilder.ts b/packages/nocodb/src/lib/noco/gql/GqlApiBuilder.ts index 059cace5ee..192e67cd77 100644 --- a/packages/nocodb/src/lib/noco/gql/GqlApiBuilder.ts +++ b/packages/nocodb/src/lib/noco/gql/GqlApiBuilder.ts @@ -565,12 +565,13 @@ export class GqlApiBuilder extends BaseApiBuilder implements XcMetaMgr { this.generateLoaderFromStringBody( loaderFunctionsObj[`${tn}Hm${hm.tn}List`] ) || - (async ids => { + (async idsAndArg => { const data = await this.models[tn].hasManyListGQL({ child: hm.tn, - ids + ids: idsAndArg.map(({ id }) => id), + ...(idsAndArg?.[0]?.args || {}) }); - return ids.map((id: string) => + return idsAndArg.map(({ id }) => data[id] ? data[id].map(c => new self.types[hm.tn](c)) : [] ); }), @@ -581,7 +582,12 @@ export class GqlApiBuilder extends BaseApiBuilder implements XcMetaMgr { /* defining HasMany list method within GQL Type class */ Object.defineProperty(this.types[tn].prototype, `${listPropName}`, { async value(args: any, context: any, info: any): Promise { - return listLoader.load([this[colNameAlias], args, context, info]); + return listLoader.load([ + { id: this[colNameAlias], args }, + args, + context, + info + ]); }, configurable: true }); @@ -605,14 +611,21 @@ export class GqlApiBuilder extends BaseApiBuilder implements XcMetaMgr { const listLoader = new DataLoader( BaseType.applyMiddlewareForLoader( [mw.middleware], - async parentIds => { + async parentIdsAndArg => { return ( await this.models[tn]._getGroupedManyToManyList({ - parentIds, + parentIds: parentIdsAndArg.map(({ id }) => id), child: mm.rtn, // todo: optimize - query only required fields rest: { - mfields1: '*' + mfields1: '*', + ...Object.entries(parentIdsAndArg?.[0]?.args || {}).reduce( + (params, [key, val]) => ({ + ...params, + [`m${key}1`]: val + }), + {} + ) } }) )?.map(child => child.map(c => new self.types[mm.rtn](c))); @@ -624,7 +637,12 @@ export class GqlApiBuilder extends BaseApiBuilder implements XcMetaMgr { /* defining HasMany list method within GQL Type class */ Object.defineProperty(this.types[tn].prototype, listPropName, { async value(args: any, context: any, info: any): Promise { - return listLoader.load([this[colNameAlias], args, context, info]); + return listLoader.load([ + { id: this[colNameAlias], args }, + args, + context, + info + ]); }, configurable: true }); @@ -2853,6 +2871,60 @@ export class GqlApiBuilder extends BaseApiBuilder implements XcMetaMgr { await this.reInitializeGraphqlEndpoint(); } + protected async ncUpAddNestedResolverArgs(_ctx: any): Promise { + const models = await this.xcMeta.metaList( + this.projectId, + this.dbAlias, + 'nc_models', + { + fields: ['meta'], + condition: { + type: 'table' + } + } + ); + if (!models.length) { + return; + } + // add virtual columns for relations + for (const metaObj of models) { + const meta = JSON.parse(metaObj.meta); + const ctx = this.generateContextForTable( + meta.tn, + meta.columns, + [], + meta.hasMany, + meta.belongsTo, + meta.type, + meta._tn + ); + + /* generate gql schema of the table */ + const schema = GqlXcSchemaFactory.create(this.connectionConfig, { + dir: '', + ctx: { + ...ctx, + manyToMany: meta.manyToMany + }, + filename: '' + }).getString(); + + /* update schema in metadb */ + await this.xcMeta.metaUpdate( + this.projectId, + this.dbAlias, + 'nc_models', + { + schema + }, + { + title: meta.tn, + type: 'table' + } + ); + } + } + protected async ncUpManyToMany(ctx: any): Promise { const metas = await super.ncUpManyToMany(ctx); diff --git a/packages/nocodb/src/lib/noco/rest/RestAuthCtrl.ts b/packages/nocodb/src/lib/noco/rest/RestAuthCtrl.ts index 8d67808a50..8443fea9e8 100644 --- a/packages/nocodb/src/lib/noco/rest/RestAuthCtrl.ts +++ b/packages/nocodb/src/lib/noco/rest/RestAuthCtrl.ts @@ -128,6 +128,7 @@ export default class RestAuthCtrl { await this.createAuthTableIfNotExists(); await this.initStrategies(); + Tele.emit('evt_app_started', await this.users.count('id as count').first()); this.app.router.use(passport.initialize()); const jwtMiddleware = passport.authenticate('jwt', { session: false }); @@ -911,6 +912,7 @@ export default class RestAuthCtrl { if (!(await this.users.first())) { // todo: update in nc_store // roles = 'owner,creator,editor' + Tele.emit('evt', { evt_type: 'user:first_signup' }); } else { if (process.env.NC_INVITE_ONLY_SIGNUP) { return next( @@ -1236,7 +1238,7 @@ export default class RestAuthCtrl { invite_token_expires: new Date(Date.now() + 24 * 60 * 60 * 1000), email }); - count = await this.users.count('id').first(); + count = await this.users.count('id as count').first(); const { id } = await this.users.where({ email }).first(); await this.xcMeta.projectAddUser(req.body.project_id, id, 'creator'); diff --git a/packages/nocodb/src/lib/noco/rest/RestAuthCtrlEE.ts b/packages/nocodb/src/lib/noco/rest/RestAuthCtrlEE.ts index 72426b2f7b..11340d4d7e 100644 --- a/packages/nocodb/src/lib/noco/rest/RestAuthCtrlEE.ts +++ b/packages/nocodb/src/lib/noco/rest/RestAuthCtrlEE.ts @@ -77,7 +77,7 @@ export default class RestAuthCtrlEE extends RestAuthCtrl { }); const { id } = await this.users.where({ email }).first(); - const count = await this.users.count('id').first(); + const count = await this.users.count('id as count').first(); // add user to project await this.xcMeta.projectAddUser( req.body.project_id, diff --git a/packages/nocodb/src/lib/sqlMgr/code/gql-schema/xc-ts/BaseGqlXcTsSchema.ts b/packages/nocodb/src/lib/sqlMgr/code/gql-schema/xc-ts/BaseGqlXcTsSchema.ts index 8a9d741820..9001cf30e8 100644 --- a/packages/nocodb/src/lib/sqlMgr/code/gql-schema/xc-ts/BaseGqlXcTsSchema.ts +++ b/packages/nocodb/src/lib/sqlMgr/code/gql-schema/xc-ts/BaseGqlXcTsSchema.ts @@ -65,7 +65,7 @@ abstract class BaseGqlXcTsSchema extends BaseRender { } let str = '\r\n'; for (const mm of args.manyToMany) { - str += `\t\t${mm._rtn}MMList: [${mm._rtn}]\r\n`; + str += `\t\t${mm._rtn}MMList(where: String,limit: Int, offset: Int, sort: String): [${mm._rtn}]\r\n`; } return str; } @@ -151,7 +151,7 @@ abstract class BaseGqlXcTsSchema extends BaseRender { // cityList in Country for (const { _tn } of hasManyRelations) { const childTable = _tn; - str += `\t\t${childTable}List: [${childTable}]\r\n`; + str += `\t\t${childTable}List(where: String,limit: Int, offset: Int, sort: String): [${childTable}]\r\n`; strWhere += `\t\t${childTable}List: Condition${childTable}\r\n`; str += `\t\t${childTable}Count: Int\r\n`; }