Browse Source

feat: support filter and pagination param in nested resolvers

Signed-off-by: Pranav C <pranavxc@gmail.com>
pull/936/head
Pranav C 3 years ago
parent
commit
e43d2cc49b
  1. 7
      packages/nc-gui/components/project/spreadsheet/components/editColumn.vue
  2. 14
      packages/nocodb/package-lock.json
  3. 2
      packages/nocodb/package.json
  4. 10
      packages/nocodb/src/lib/dataMapper/lib/sql/BaseModelSql.ts
  5. 5
      packages/nocodb/src/lib/noco/Noco.ts
  6. 6
      packages/nocodb/src/lib/noco/common/BaseApiBuilder.ts
  7. 88
      packages/nocodb/src/lib/noco/gql/GqlApiBuilder.ts
  8. 4
      packages/nocodb/src/lib/noco/rest/RestAuthCtrl.ts
  9. 2
      packages/nocodb/src/lib/noco/rest/RestAuthCtrlEE.ts
  10. 4
      packages/nocodb/src/lib/sqlMgr/code/gql-schema/xc-ts/BaseGqlXcTsSchema.ts

7
packages/nc-gui/components/project/spreadsheet/components/editColumn.vue

@ -99,13 +99,6 @@
</template>
</v-autocomplete>
<!-- <v-list dense max-height="calc(100vh - 300px)" style="overflow: auto">-->
<!-- <v-list-item v-for="item in uiTypes" @click.stop :key="item">-->
<!-- <span class="caption">{{ item }}</span>-->
<!-- </v-list-item>-->
<!-- </v-list>-->
<!-- v-if="column && column.uidt === 'MultiSelect' &&newColumn.uidt === 'SingleSelect'"-->
<v-alert
dense
type="warning"

14
packages/nocodb/package-lock.json generated

@ -66,7 +66,7 @@
"mysql2": "^2.2.5",
"nanoid": "^3.1.20",
"nc-common": "0.0.6",
"nc-help": "^0.2.23",
"nc-help": "^0.2.24",
"nc-lib-gui": "0.84.1",
"nc-plugin": "^0.1.1",
"ncp": "^2.0.0",
@ -15850,9 +15850,9 @@
}
},
"node_modules/nc-help": {
"version": "0.2.23",
"resolved": "https://registry.npmjs.org/nc-help/-/nc-help-0.2.23.tgz",
"integrity": "sha512-4enEuzR9EhslpuSVh/uk04ti4euo6qOh+rvkRHbJbgAX7CEu/c8mHGWTEFDoZCIFZ0S3maVpCGDWp9RzonzEbA==",
"version": "0.2.24",
"resolved": "https://registry.npmjs.org/nc-help/-/nc-help-0.2.24.tgz",
"integrity": "sha512-Pa/ANsusuOtGRtnINgynCzztnq3AyzSp7vkkwQbyV7fSFGQuvlO1LMT/e+WnTNAOQFKhkS6VETFlPah6aPYalQ==",
"dependencies": {
"axios": "^0.21.1",
"boxen": "^4.2.0",
@ -37147,9 +37147,9 @@
"integrity": "sha512-3AryS9uwa5NfISLxMciUonrH7YfXp+nlahB9T7girXIsLQrmwX4MdnuKs32akduCOGpKmjTJSWmATULbuMkbfw=="
},
"nc-help": {
"version": "0.2.23",
"resolved": "https://registry.npmjs.org/nc-help/-/nc-help-0.2.23.tgz",
"integrity": "sha512-4enEuzR9EhslpuSVh/uk04ti4euo6qOh+rvkRHbJbgAX7CEu/c8mHGWTEFDoZCIFZ0S3maVpCGDWp9RzonzEbA==",
"version": "0.2.24",
"resolved": "https://registry.npmjs.org/nc-help/-/nc-help-0.2.24.tgz",
"integrity": "sha512-Pa/ANsusuOtGRtnINgynCzztnq3AyzSp7vkkwQbyV7fSFGQuvlO1LMT/e+WnTNAOQFKhkS6VETFlPah6aPYalQ==",
"requires": {
"axios": "^0.21.1",
"boxen": "^4.2.0",

2
packages/nocodb/package.json

@ -147,7 +147,7 @@
"mysql2": "^2.2.5",
"nanoid": "^3.1.20",
"nc-common": "0.0.6",
"nc-help": "^0.2.23",
"nc-help": "^0.2.24",
"nc-lib-gui": "0.84.1",
"nc-plugin": "^0.1.1",
"ncp": "^2.0.0",

10
packages/nocodb/src/lib/dataMapper/lib/sql/BaseModelSql.ts

@ -1998,7 +1998,7 @@ class BaseModelSql extends BaseModel {
conditionGraph,
sort
// ...restArgs
} = this.dbModels[child]._getChildListArgs(rest);
} = this.dbModels[child]._getListArgs(rest);
// let { fields } = restArgs;
// todo: get only required fields
let fields = '*';
@ -2027,7 +2027,11 @@ class BaseModelSql extends BaseModel {
// .select(...fields.split(','));
.select(this.dbModels?.[child]?.selectQuery(fields));
this._paginateAndSort(query, { limit, offset }, child);
this.dbModels?.[child]?._paginateAndSort(
query,
{ limit, sort, offset },
child
);
return this.isSqlite()
? this.dbDriver.select().from(query)
: query;
@ -2036,7 +2040,7 @@ class BaseModelSql extends BaseModel {
)
.as('list')
),
{ sort, ignoreLimit: true } as any,
{ ignoreLimit: true } as any,
child
)
);

5
packages/nocodb/src/lib/noco/Noco.ts

@ -11,7 +11,6 @@ import * as express from 'express';
import { Router } from 'express';
import importFresh from 'import-fresh';
import morgan from 'morgan';
import { Tele } from 'nc-help';
import NcToolGui from 'nc-lib-gui';
import requestIp from 'request-ip';
import { v4 as uuidv4 } from 'uuid';
@ -90,7 +89,7 @@ export default class Noco {
constructor() {
process.env.PORT = process.env.PORT || '8080';
// todo: move
process.env.NC_VERSION = '0083008';
process.env.NC_VERSION = '0084002';
this.router = express.Router();
this.projectRouter = express.Router();
@ -244,8 +243,6 @@ export default class Noco {
next();
});
Tele.emit('evt_app_started', {});
return this.router;
}

6
packages/nocodb/src/lib/noco/common/BaseApiBuilder.ts

@ -1298,7 +1298,8 @@ export default abstract class BaseApiBuilder<T extends Noco>
{ 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<T extends Noco>
return metas;
}
protected async ncUpAddNestedResolverArgs(_ctx: any): Promise<any> {}
protected async ncUpManyToMany(_ctx: any): Promise<any> {
const models = await this.xcMeta.metaList(
this.projectId,
@ -3238,6 +3241,7 @@ interface NcMetaData {
[key: string]: any;
}
type XcTablesPopulateParams = {
tableNames?: Array<{
tn: string;

88
packages/nocodb/src/lib/noco/gql/GqlApiBuilder.ts

@ -565,12 +565,13 @@ export class GqlApiBuilder extends BaseApiBuilder<Noco> 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<Noco> 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<any> {
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<Noco> 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<Noco> 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<any> {
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<Noco> implements XcMetaMgr {
await this.reInitializeGraphqlEndpoint();
}
protected async ncUpAddNestedResolverArgs(_ctx: any): Promise<any> {
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<any> {
const metas = await super.ncUpManyToMany(ctx);

4
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');

2
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,

4
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`;
}

Loading…
Cancel
Save