From cf2452b04aa752e708eceed49333a04b258aff4a Mon Sep 17 00:00:00 2001 From: Pranav C <61551451+pranavxc@users.noreply.github.com> Date: Wed, 7 Jul 2021 19:32:45 +0530 Subject: [PATCH] feat: GQL - M2M many to many Signed-off-by: Pranav C <61551451+pranavxc@users.noreply.github.com> --- .../project/spreadsheet/apis/gqlApi.js | 6 +- .../components/virtualCell/hasManyCell.vue | 8 +- .../components/virtualCell/manyToManyCell.vue | 22 +- .../project/spreadsheet/mixins/spreadsheet.js | 8 + .../project/spreadsheet/rowsXcDataTable.vue | 17 +- .../project/spreadsheet/views/xcGridView.vue | 223 ++++++++--------- packages/nc-gui/components/project/table.vue | 82 ++++--- packages/nc-gui/layouts/default.vue | 4 +- packages/nc-gui/nuxt.config.js | 14 +- packages/nc-gui/store/sqlMgr.js | 16 +- packages/nc-gui/store/windows.js | 6 +- .../lib/dataMapper/lib/sql/BaseModelSql.ts | 6 +- .../src/lib/noco/common/BaseApiBuilder.ts | 10 + .../nocodb/src/lib/noco/gql/GqlApiBuilder.ts | 214 ++++++++++++---- .../nocodb/src/lib/noco/gql/GqlResolver.ts | 5 +- .../src/lib/noco/rest/RestApiBuilder.ts | 3 +- .../gql-schema/xc-ts/BaseGqlXcTsSchema.ts | 184 +++++++++++++- .../gql-schema/xc-ts/GqlXcTsSchemaMssql.ts | 43 ++-- .../gql-schema/xc-ts/GqlXcTsSchemaMysql.ts | 46 +--- .../gql-schema/xc-ts/GqlXcTsSchemaOracle.ts | 29 ++- .../code/gql-schema/xc-ts/GqlXcTsSchemaPg.ts | 80 +++--- .../gql-schema/xc-ts/GqlXcTsSchemaSqlite.ts | 232 ++++++++---------- 22 files changed, 786 insertions(+), 472 deletions(-) diff --git a/packages/nc-gui/components/project/spreadsheet/apis/gqlApi.js b/packages/nc-gui/components/project/spreadsheet/apis/gqlApi.js index c0818e333f..de21811959 100644 --- a/packages/nc-gui/components/project/spreadsheet/apis/gqlApi.js +++ b/packages/nc-gui/components/project/spreadsheet/apis/gqlApi.js @@ -74,15 +74,15 @@ export default class GqlApi { } get gqlQueryListName() { - return `${this.table.replace(/(?:^|_)(.)/g, (_, m) => m.toUpperCase())}List`; + return `${this.meta._tn}List`; } get gqlQueryReadName() { - return `${this.table.replace(/(?:^|_)(.)/g, (_, m) => m.toUpperCase())}Read`; + return `${this.meta._tn}Read`; } get tableCamelized() { - return `${this.table.replace(/(?:^|_)(.)/g, (_, m) => m.toUpperCase())}`; + return `${this.meta._tn}`; } get gqlReqBody() { diff --git a/packages/nc-gui/components/project/spreadsheet/components/virtualCell/hasManyCell.vue b/packages/nc-gui/components/project/spreadsheet/components/virtualCell/hasManyCell.vue index 3cd87ddb25..e8d8d11308 100644 --- a/packages/nc-gui/components/project/spreadsheet/components/virtualCell/hasManyCell.vue +++ b/packages/nc-gui/components/project/spreadsheet/components/virtualCell/hasManyCell.vue @@ -12,6 +12,10 @@ @edit="editChild" @unlink="unlinkChild" > + + more... +
c.pv) || {})._cn @@ -297,7 +301,7 @@ export default { return this.childMeta && (this.childMeta.columns.find(c => c.pk) || {})._cn }, childForeignKey() { - return this.childMeta && this.childMeta.columns.find(c => c.cn === this.hm.cn)._cn + return this.childMeta && (this.childMeta.columns.find(c => c.cn === this.hm.cn) || {})._cn }, disabledChildColumns() { return {[this.childForeignKey]: true} diff --git a/packages/nc-gui/components/project/spreadsheet/components/virtualCell/manyToManyCell.vue b/packages/nc-gui/components/project/spreadsheet/components/virtualCell/manyToManyCell.vue index 11c92245b2..9c46fa3b2e 100644 --- a/packages/nc-gui/components/project/spreadsheet/components/virtualCell/manyToManyCell.vue +++ b/packages/nc-gui/components/project/spreadsheet/components/virtualCell/manyToManyCell.vue @@ -12,7 +12,7 @@ @unlink="unlinkChild" > - + more...
@@ -266,7 +266,7 @@ export default { this.newRecordModal = false; return } - const cid = this.childMeta.columns. filter((c) => c.pk).map(c => child[c._cn]).join('___'); + const cid = this.childMeta.columns.filter((c) => c.pk).map(c => child[c._cn]).join('___'); const pid = this.meta.columns.filter((c) => c.pk).map(c => this.row[c._cn]).join('___'); const vcidCol = this.assocMeta.columns.find(c => c.cn === this.mm.vrcn)._cn; @@ -310,16 +310,18 @@ export default { this.$refs.expandedForm && this.$refs.expandedForm.reload() }, 500) }, - getCellValue(cellObj) { - if (cellObj) { - if (this.parentMeta && this.childPrimaryCol) { - return cellObj[this.childPrimaryCol] - } - return Object.values(cellObj)[1] - } - } }, computed: { + getCellValue() { + return cellObj => { + if (cellObj) { + if (this.childPrimaryCol) { + return cellObj[this.childPrimaryCol] + } + return Object.values(cellObj)[1] + } + } + }, childMeta() { return this.$store.state.meta.metas[this.mm.rtn] }, diff --git a/packages/nc-gui/components/project/spreadsheet/mixins/spreadsheet.js b/packages/nc-gui/components/project/spreadsheet/mixins/spreadsheet.js index c213a6b018..f76737568f 100644 --- a/packages/nc-gui/components/project/spreadsheet/mixins/spreadsheet.js +++ b/packages/nc-gui/components/project/spreadsheet/mixins/spreadsheet.js @@ -57,6 +57,14 @@ export default { return c._cn; }) }, + realFieldList() { + return this.availableRealColumns.map(c => { + return c._cn; + }) + }, + availableRealColumns() { + return this.availableColumns && this.availableColumns.filter(c => !c.virtual) + }, availableColumns() { let columns = []; diff --git a/packages/nc-gui/components/project/spreadsheet/rowsXcDataTable.vue b/packages/nc-gui/components/project/spreadsheet/rowsXcDataTable.vue index ba82b50ed8..f5cdbc520d 100644 --- a/packages/nc-gui/components/project/spreadsheet/rowsXcDataTable.vue +++ b/packages/nc-gui/components/project/spreadsheet/rowsXcDataTable.vue @@ -18,7 +18,7 @@
- {{ col._cn }} @@ -53,8 +53,10 @@ + + - + mdi-reload @@ -153,6 +155,7 @@ + --> c.cn === columnObj.bt.cn); } - return (columnObj.rqd + return columnObj && (columnObj.rqd && (rowObj[columnObj._cn] === undefined || rowObj[columnObj._cn] === null) && !columnObj.default); }, diff --git a/packages/nc-gui/components/project/table.vue b/packages/nc-gui/components/project/table.vue index 1b718a363f..c26feeb4a0 100644 --- a/packages/nc-gui/components/project/table.vue +++ b/packages/nc-gui/components/project/table.vue @@ -9,6 +9,7 @@ v-model="active" :height="relationTabs && relationTabs.length ?38:0" class="table-tabs" + :class="{'hidden-tab':!relationTabs || !relationTabs.length}" @change="onTabChange" color="pink" > @@ -17,7 +18,7 @@ mdi-table-eye  Model + style="height:100%"> + style="height:100%"> Triggers + style="height:100%"> --> - + - - - - - - - + + + + + + + @@ -344,11 +345,11 @@ + Loading mdi-spin mdi-loading @@ -624,7 +625,8 @@ --> - + diff --git a/packages/nc-gui/nuxt.config.js b/packages/nc-gui/nuxt.config.js index 8cdaee470c..48bfe99fc4 100644 --- a/packages/nc-gui/nuxt.config.js +++ b/packages/nc-gui/nuxt.config.js @@ -192,12 +192,14 @@ export default { return config; } }, - loading: { - color: '#13f4ef', - height: '2px', - continuous: true, - duration: 3000 - }, + loading: false + // { + // color: '#13f4ef', + // height: '0px', + // continuous: true, + // duration: 3000 + // } + , css: [ // '@/assets/style/fonts.css', '@/assets/css/global.css', diff --git a/packages/nc-gui/store/sqlMgr.js b/packages/nc-gui/store/sqlMgr.js index acb723892c..6d09f70127 100644 --- a/packages/nc-gui/store/sqlMgr.js +++ b/packages/nc-gui/store/sqlMgr.js @@ -376,7 +376,7 @@ export const actions = { if (cusHeaders) { Object.assign(headers, cusHeaders) } - return (await this.$axios({ + const data = (await this.$axios({ url: '?q=sqlOp_' + op, baseURL: `${this.$axios.defaults.baseURL}/dashboard`, data: {api: op, ...args, ...params, args: opArgs}, @@ -386,6 +386,20 @@ export const actions = { ...(cusAxiosOptions || {}), })).data; + + if (op === 'tableXcModelGet') { + try { + const meta = JSON.parse(model.meta); + commit('meta/MutMeta', { + key: meta.tn, + value: meta + }, {root: true}) + } catch (e) { + // ignore + } + } + + return data; } catch (e) { const err = new Error(e.response.data.msg); err.response = e.response; diff --git a/packages/nc-gui/store/windows.js b/packages/nc-gui/store/windows.js index 54cfa4beae..5040a2a2c7 100644 --- a/packages/nc-gui/store/windows.js +++ b/packages/nc-gui/store/windows.js @@ -45,10 +45,14 @@ export const state = () => ({ nc: true, miniSponsorCard: 0, screensaver: true, - autoApplyFilter: true + autoApplyFilter: true, + apiLoading: false }); export const mutations = { + MutApiLoading(state, status) { + state.apiLoading = status + }, MutAutoApplyFilter(state, v) { state.autoApplyFilter = v; }, diff --git a/packages/nocodb/src/lib/dataMapper/lib/sql/BaseModelSql.ts b/packages/nocodb/src/lib/dataMapper/lib/sql/BaseModelSql.ts index 522c4fea22..aac2a03078 100644 --- a/packages/nocodb/src/lib/dataMapper/lib/sql/BaseModelSql.ts +++ b/packages/nocodb/src/lib/dataMapper/lib/sql/BaseModelSql.ts @@ -1130,7 +1130,7 @@ class BaseModelSql extends BaseModel { }), !this.isSqlite() )); - let gs = _.groupBy(childs, _cn); + const gs = _.groupBy(childs, _cn); parent.forEach(row => { row[`${this.dbModels?.[child]?._tn || child}List`] = gs[row[this.pks[0]._cn]] || []; }) @@ -1176,7 +1176,7 @@ class BaseModelSql extends BaseModel { this .dbDriver(child) .join(vtn, `${vtn}.${vrcn}`, `${rtn}.${rcn}`) - .where(`${vtn}.${vcn}`, id)//p[this.columnToAlias?.[this.pks[0].cn] || this.pks[0].cn]) + .where(`${vtn}.${vcn}`, id) // p[this.columnToAlias?.[this.pks[0].cn] || this.pks[0].cn]) .xwhere(where, this.dbModels[child].selectQuery('')) .select({[`${tn}_${vcn}`]: `${vtn}.${vcn}`, ...this.dbModels[child].selectQuery(fields)}) // ...fields.split(',')); @@ -1503,7 +1503,7 @@ class BaseModelSql extends BaseModel { this._paginateAndSort(query, {limit, offset}, child); return this.isSqlite() ? this.dbDriver.select().from(query) : query; }), !this.isSqlite() - ), {sort} as any, child)); + ), {sort,limit:1000} as any, child)); // return _.groupBy(childs, cn); diff --git a/packages/nocodb/src/lib/noco/common/BaseApiBuilder.ts b/packages/nocodb/src/lib/noco/common/BaseApiBuilder.ts index 8e9d9c1cb7..9178de1453 100644 --- a/packages/nocodb/src/lib/noco/common/BaseApiBuilder.ts +++ b/packages/nocodb/src/lib/noco/common/BaseApiBuilder.ts @@ -232,6 +232,16 @@ export default abstract class BaseApiBuilder implements XcDynami dr: onDelete, ur: onUpdate, }) + }else { + await this.xcMeta.metaUpdate(this.projectId, this.dbAlias, 'nc_relations', { + _tn: this.getTableNameAlias(tnc), + _rtn: this.getTableNameAlias(tnp), + },{ + tn: tnc, + cn: childColumn, + rtn: tnp, + rcn: parentColumn, + }) } } diff --git a/packages/nocodb/src/lib/noco/gql/GqlApiBuilder.ts b/packages/nocodb/src/lib/noco/gql/GqlApiBuilder.ts index 89f8014572..24887d21c0 100644 --- a/packages/nocodb/src/lib/noco/gql/GqlApiBuilder.ts +++ b/packages/nocodb/src/lib/noco/gql/GqlApiBuilder.ts @@ -324,8 +324,8 @@ export class GqlApiBuilder extends BaseApiBuilder implements XcMetaMgr { const colNameAlias = self.models[hm.rtn]?.columnToAlias[hm.rcn]; const middlewareBody = middlewaresArr.find(({title}) => title === hm.tn)?.functions?.[0]; - const countPropName = `${inflection.camelize(hm._tn, false)}Count`; - const listPropName = `${inflection.camelize(hm._tn, false)}List`; + const countPropName = `${hm._tn}Count`; + const listPropName = `${hm._tn}List`; if (listPropName in this.types[tn].prototype) { continue; @@ -893,6 +893,7 @@ export class GqlApiBuilder extends BaseApiBuilder implements XcMetaMgr { public async xcTableRename(oldTablename: string, newTablename: string): Promise { this.log(`xcTableRename : '%s' => '%s'`, oldTablename, newTablename); + //todo: verify the update queries // const metaArr = await (this.sqlClient.knex as XKnex)('nc_models').select(); @@ -1118,7 +1119,7 @@ export class GqlApiBuilder extends BaseApiBuilder implements XcMetaMgr { public async onRelationCreate(tnp: string, tnc: string, args): Promise { await super.onRelationCreate(tnp, tnc, args) this.log(`onRelationCreate : Within relation create event handler`); - const self = this; + // const self = this; const relations = await this.getXcRelationList(); // set table name alias @@ -1134,61 +1135,90 @@ export class GqlApiBuilder extends BaseApiBuilder implements XcMetaMgr { const hasMany = this.extractHasManyRelationsOfTable(relations, tnp); const belongsTo = this.extractBelongsToRelationsOfTable(relations, tnp); const ctx = this.generateContextForTable(tnp, columns, relations, hasMany, belongsTo); + ctx.manyToMany = this.metas?.[tnp]?.manyToMany; const meta = ModelXcMetaFactory.create(this.connectionConfig, {dir: '', ctx, filename: ''}).getObject(); - this.metas[tnp] = meta; + // this.metas[tnp] = meta; this.schemas[tnp] = GqlXcSchemaFactory.create(this.connectionConfig, this.generateRendererArgs(ctx)).getString(); // update old model meta with new details this.log(`onRelationCreate : Generating and updating model meta for parent table '%s'`, tnp); const existingModel = await this.xcMeta.metaGet(this.projectId, this.dbAlias, 'nc_models', {'title': tnp}); + let queryParams; + try { + queryParams = JSON.parse(existingModel.query_params); + } catch (e) { /* */ + } if (existingModel) { // todo: persisting old table_alias and columnAlias + // todo: get enable state of other relations const oldMeta = JSON.parse(existingModel.meta); + meta.hasMany.forEach(hm => { + hm.enabled = true; + }) Object.assign(oldMeta, { hasMany: meta.hasMany, }); + + /* Add new has many relation to virtual columns */ + oldMeta.v = oldMeta.v || []; + oldMeta.v.push({ + hm: meta.hasMany.find(hm => hm.rtn === tnp && hm.tn === tnc), + _cn: `${this.getTableNameAlias(tnp)} => ${this.getTableNameAlias(tnc)}` + }) + if (queryParams?.showFields) { + queryParams.showFields[`${this.getTableNameAlias(tnp)} => ${this.getTableNameAlias(tnc)}`] = true; + } + + this.models[tnp] = this.getBaseModel(oldMeta); + await this.xcMeta.metaUpdate(this.projectId, this.dbAlias, 'nc_models', { title: tnp, - meta: JSON.stringify(meta), + meta: JSON.stringify(oldMeta), schema: this.schemas[tnp] }, {'title': tnp}) } - const countPropName = `${inflection.camelize(this.getTableNameAlias(tnc), false)}Count`; - const listPropName = `${inflection.camelize(this.getTableNameAlias(tnc), false)}List`; + const countPropName = `${this.getTableNameAlias(tnc)}Count`; + const listPropName = `${this.getTableNameAlias(tnc)}List`; this.log(`onRelationCreate : Generating and inserting '%s' and '%s' loaders`, countPropName, listPropName); - /* has many relation list loader with middleware */ - const mw = new GqlMiddleware(this.acls, tnc, '', this.models); - const listLoader = new DataLoader( - BaseType.applyMiddlewareForLoader( - [mw.middleware], - async ids => { - const data = await this.models[tnp].hasManyListGQL({ - ids, - child: tnc - }) - return ids.map(id => data[id] ? data[id].map(c => new self.types[tnc](c)) : []); - }, - [mw.postLoaderMiddleware] - )); - - const currentRelation = hasMany.find(rel => rel.tn === tnc) - - /* defining HasMany list method within GQL Type class */ - Object.defineProperty(this.types[tnp].prototype, `${listPropName}`, { - async value(args, context, info): Promise { - return listLoader.load([this[currentRelation.rcn], args, context, info]); - }, - configurable: true - }) + const hm = hasMany.find(rel => rel.tn === tnc) + { + /* has many relation list loader with middleware */ + const mw = new GqlMiddleware(this.acls, tnc, '', this.models); + this.addHmListResolverMethodToType(tnp, hm, mw, {}, listPropName, this.models[hm.rtn]?.columnToAlias[hm.rcn]); + } + /* const listLoader = new DataLoader( + BaseType.applyMiddlewareForLoader( + [mw.middleware], + async ids => { + const data = await this.models[tnp].hasManyListGQL({ + ids, + child: tnc + }) + return ids.map(id => data[id] ? data[id].map(c => new self.types[tnc](c)) : []); + }, + [mw.postLoaderMiddleware] + )); + + + + /!* defining HasMany list method within GQL Type class *!/ + Object.defineProperty(this.types[tnp].prototype, `${listPropName}`, { + async value(args, context, info): Promise { + return listLoader.load([this[hm.rcn], args, context, info]); + }, + configurable: true + })*/ // create count loader with middleware { const mw = new GqlMiddleware(this.acls, tnc, '', this.models); - const countLoader = new DataLoader( + this.addHmListResolverMethodToType(tnp, hm, mw, {}, countPropName, this.models[hm.rtn]?.columnToAlias[hm.rcn]); + + /*const countLoader = new DataLoader( BaseType.applyMiddlewareForLoader( [mw.middleware], async ids => { @@ -1204,10 +1234,10 @@ export class GqlApiBuilder extends BaseApiBuilder implements XcMetaMgr { // defining HasMany count method within GQL Type class Object.defineProperty(this.types[tnp].prototype, `${countPropName}`, { async value(args, context, info): Promise { - return countLoader.load([this[currentRelation.rcn], args, context, info]); + return countLoader.load([this[hm.rcn], args, context, info]); }, configurable: true - }) + })*/ } await this.xcMeta.metaInsert(this.projectId, this.dbAlias, 'nc_loaders', { @@ -1233,36 +1263,62 @@ export class GqlApiBuilder extends BaseApiBuilder implements XcMetaMgr { const belongsTo = this.extractBelongsToRelationsOfTable(relations, tnc); const hasMany = this.extractHasManyRelationsOfTable(relations, tnc); const ctx = this.generateContextForTable(tnc, columns, relations, hasMany, belongsTo); + ctx.manyToMany = this.metas?.[tnc]?.manyToMany; const meta = ModelXcMetaFactory.create(this.connectionConfig, this.generateRendererArgs(ctx)).getObject(); - this.metas[tnc] = meta; - + // this.metas[tnc] = meta; this.schemas[tnc] = GqlXcSchemaFactory.create(this.connectionConfig, this.generateRendererArgs(ctx)).getString(); this.log(`onRelationCreate : Generating and updating model meta for child table '%s'`, tnc); // update old model meta with new details const existingModel = await this.xcMeta.metaGet(this.projectId, this.dbAlias, 'nc_models', {'title': tnc}); + let queryParams; + try { + queryParams = JSON.parse(existingModel.query_params); + } catch (e) { /* */ + } + if (existingModel) { // todo: persisting old table_alias and columnAlias const oldMeta = JSON.parse(existingModel.meta); Object.assign(oldMeta, { belongsTo: meta.belongsTo, }); + /* Add new belongs to relation to virtual columns */ + oldMeta.v = oldMeta.v || []; + oldMeta.v.push({ + bt: meta.belongsTo.find(hm => hm.rtn === tnp && hm.tn === tnc), + _cn: `${this.getTableNameAlias(tnp)} <= ${this.getTableNameAlias(tnc)}` + }) + + + if (queryParams?.showFields) { + queryParams.showFields[`${this.getTableNameAlias(tnp)} <= ${this.getTableNameAlias(tnc)}`] = true; + } + this.models[tnc] = this.getBaseModel(oldMeta); await this.xcMeta.metaUpdate(this.projectId, this.dbAlias, 'nc_models', { title: tnc, - meta: JSON.stringify(meta), + meta: JSON.stringify(oldMeta), schema: this.schemas[tnc] }, {'title': tnc}) } - const propName = `${inflection.camelize(this.getTableNameAlias(tnp), false)}Read`; + const propName = `${this.getTableNameAlias(tnp)}Read`; this.log(`onRelationCreate : Generating and inserting'%s' loader`, propName); const currentRelation = belongsTo.find(rel => rel.rtn === tnp) // create read loader with middleware const mw = new GqlMiddleware(this.acls, tnp, '', this.models); - const readLoader = new DataLoader( + this.adBtResolverMethodToType( + propName, + mw, + tnc, + currentRelation, + this.models[currentRelation.rtn]?.columnToAlias[currentRelation.rcn], + this.models[currentRelation.tn]?.columnToAlias[currentRelation?.cn] + ); + /*const readLoader = new DataLoader( BaseType.applyMiddlewareForLoader( [mw.middleware], async ids => { @@ -1282,7 +1338,7 @@ export class GqlApiBuilder extends BaseApiBuilder implements XcMetaMgr { return readLoader.load([this[currentRelation.cn], args, context, info]); }, configurable: true - }) + })*/ await this.xcMeta.metaInsert(this.projectId, this.dbAlias, 'nc_loaders', { title: `${tnc}Bt${tnp}`, @@ -1293,8 +1349,6 @@ export class GqlApiBuilder extends BaseApiBuilder implements XcMetaMgr { }); } - this.models[tnc] = this.getBaseModel(this.metas[tnc]); - this.models[tnp] = this.getBaseModel(this.metas[tnp]); await this.reInitializeGraphqlEndpoint(); } @@ -1761,6 +1815,82 @@ export class GqlApiBuilder extends BaseApiBuilder implements XcMetaMgr { private log(str, ...args): void { log(`${this.dbAlias} : ${str}`, ...args); } + + public async onManyToManyRelationCreate(parent: string, child: string, args?: any) { + await super.onManyToManyRelationCreate(parent, child, args); + for (const tn of [parent, child]) { + const meta = this.metas[tn]; + const {columns, hasMany, belongsTo, manyToMany} = meta; + const ctx = this.generateContextForTable(tn, columns, [...hasMany, ...belongsTo], hasMany, belongsTo); + ctx.manyToMany = manyToMany; + this.schemas[tn] = GqlXcSchemaFactory.create(this.connectionConfig, this.generateRendererArgs(ctx)).getString(); + // todo: update schema history + await this.xcMeta.metaUpdate(this.projectId, this.dbAlias, 'nc_models', { + schema: this.schemas[tn] + }, { + title: tn + }) + } + + + { + const listPropName = `${this.metas[child]._tn}MMList`; + this.log(`onRelationCreate : Generating and inserting '%s' loaders`, listPropName); + /* has many relation list loader with middleware */ + const mw = new GqlMiddleware(this.acls, parent, '', this.models); + this.addMMListResolverMethodToType(parent, {rtn: child}, mw, {}, listPropName, this.metas[parent].columns.find(c => c.pk)._cn) + } + + { + const listPropName = `${this.metas[parent]._tn}MMList`; + this.log(`onRelationCreate : Generating and inserting '%s' loaders`, listPropName); + /* has many relation list loader with middleware */ + const mw = new GqlMiddleware(this.acls, child, '', this.models); + this.addMMListResolverMethodToType(child, {rtn: parent}, mw, {}, listPropName, this.metas[child].columns.find(c => c.pk)._cn) + } + + + await this.xcMeta.metaInsert(this.projectId, this.dbAlias, 'nc_loaders', { + title: `${parent}Mm${child}List`, + parent, + child, + relation: 'mm', + resolver: 'list', + }); + await this.xcMeta.metaInsert(this.projectId, this.dbAlias, 'nc_loaders', { + title: `${child}Mm${parent}List`, + parent: child, + child: parent, + relation: 'mm', + resolver: 'list', + }); + + await this.reInitializeGraphqlEndpoint(); + + } + + public async onManyToManyRelationDelete(parent: string, child: string, args?: any) { + + await super.onManyToManyRelationDelete(parent, child, args) + + for (const tn of [parent, child]) { + const meta = this.metas[tn]; + const {columns, hasMany, belongsTo, manyToMany} = meta; + const ctx = this.generateContextForTable(tn, columns, [...hasMany, ...belongsTo], hasMany, belongsTo); + this.schemas[tn] = GqlXcSchemaFactory.create(this.connectionConfig, { + ...this.generateRendererArgs(ctx), + manyToMany + }).getString(); + // todo: update schema history + await this.xcMeta.metaUpdate(this.projectId, this.dbAlias, 'nc_models', { + schema: this.schemas[tn] + }, { + title: tn + }) + } + + await this.reInitializeGraphqlEndpoint(); + } } /** diff --git a/packages/nocodb/src/lib/noco/gql/GqlResolver.ts b/packages/nocodb/src/lib/noco/gql/GqlResolver.ts index 638ff288da..4e5638eb5f 100644 --- a/packages/nocodb/src/lib/noco/gql/GqlResolver.ts +++ b/packages/nocodb/src/lib/noco/gql/GqlResolver.ts @@ -4,7 +4,6 @@ import GqlMiddleware from "./GqlMiddleware"; import {Acls} from "../../../interface/config"; import GqlBaseResolver from "./GqlBaseResolver"; import Noco from "../Noco"; -import inflection from 'inflection'; function parseHrtimeToSeconds(hrtime) { const seconds = (hrtime[0] + (hrtime[1] / 1e6)).toFixed(3); @@ -41,7 +40,7 @@ export default class GqlResolver extends GqlBaseResolver { } public async list(args, {req, res}): Promise { - const startTime = process.hrtime(); + const startTime = process.hrtime(); try { if (args.conditionGraph && typeof args.conditionGraph === 'string') { args.conditionGraph = {models: this.models, condition: JSON.parse(args.conditionGraph)} @@ -150,7 +149,7 @@ export default class GqlResolver extends GqlBaseResolver { public mapResolvers(customResolver: any): any { const mw = new GqlMiddleware(this.acls, this.table, this.middlewareStringBody, this.models); // todo: replace with inflection - const name = inflection.camelize(this.model._tn); + const name = this.model._tn; return GqlResolver.applyMiddlewares([(_, {req}) => { req.models = this.models; req.model = this.model; diff --git a/packages/nocodb/src/lib/noco/rest/RestApiBuilder.ts b/packages/nocodb/src/lib/noco/rest/RestApiBuilder.ts index c6f1b90cb7..b3687ddd26 100644 --- a/packages/nocodb/src/lib/noco/rest/RestApiBuilder.ts +++ b/packages/nocodb/src/lib/noco/rest/RestApiBuilder.ts @@ -982,7 +982,6 @@ export class RestApiBuilder extends BaseApiBuilder { this.deleteRoutesForTables([tnp, tnc]) const relations = await this.getXcRelationList(); - { const swaggerArr = []; const columns = await this.getColumnList(tnp); @@ -1001,13 +1000,13 @@ export class RestApiBuilder extends BaseApiBuilder { // update old model meta with new details const existingModel = await this.xcMeta.metaGet(this.projectId, this.dbAlias, 'nc_models', {'title': tnp}); - let queryParams; try { queryParams = JSON.parse(existingModel.query_params); } catch (e) { /* */ } + swaggerArr.push(JSON.parse(existingModel.schema)); if (existingModel) { this.log(`onRelationCreate : Updating model metadata for parent table '%s'`, tnp); 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 ed99f9468e..6c75c8f414 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 @@ -1,6 +1,8 @@ import BaseRender from "../../BaseRender"; +import {AGG_DEFAULT_COLS, GROUPBY_DEFAULT_COLS} from "./schemaHelp"; +import lodash from "lodash"; -class BaseGqlXcTsSchema extends BaseRender { +abstract class BaseGqlXcTsSchema extends BaseRender { /** * @@ -11,10 +13,56 @@ class BaseGqlXcTsSchema extends BaseRender { * @param ctx.columns * @param ctx.relations */ - constructor({dir, filename, ctx}) { + protected constructor({dir, filename, ctx}) { super({dir, filename, ctx}); } + + /** + * Prepare variables used in code template + */ + public prepare(): any { + + const data: any = {}; + + data.columns = { + func: this._renderColumns.bind(this), + args: this.ctx + }; + + return data; + + } + + /** + * + * @param args + * @param args.columns + * @param args.relations + * @returns {string} + * @private + */ + public _renderColumns(args): string { + + let str = ''; + + str += ` + ${this._getInputType(args)}\r\n + ${this._getQuery(args)},\r\n + ${this._getMutation(args)}\r\n + ${this._getType(args)}\r\n + ` + + str += ''; + + return str; + + } + + public getString(): string { + return this._renderColumns(this.ctx); + } + protected generateManyToManyTypeProps(args: any): string { if (!args.manyToMany?.length) { return ''; @@ -26,6 +74,138 @@ class BaseGqlXcTsSchema extends BaseRender { return str; } + protected _getInputType(args): string { + let str = `input ${args._tn}Input { \r\n` + for (const column of args.columns) { + if (/\s/.test(column._cn)) { + console.log(`Skipping ${args.tn}.${column._cn}`); + } else { + str += `\t\t${column._cn}: ${this._getGraphqlType(column)},\r\n`; + } + + } + str += `\t}`; + return str; + } + + protected _getQuery(args): string { + let str = `type Query { \r\n` + str += `\t\t${args._tn}List(where: String,condition:Condition${args._tn}, limit: Int, offset: Int, sort: String,conditionGraph: String): [${args._tn}]\r\n` + str += `\t\t${args._tn}Read(id:String!): ${args._tn}\r\n` + str += `\t\t${args._tn}Exists(id: String!): Boolean\r\n` + str += `\t\t${args._tn}FindOne(where: String,condition:Condition${args._tn}): ${args._tn}\r\n` + str += `\t\t${args._tn}Count(where: String,condition:Condition${args._tn},conditionGraph: String): Int\r\n` + str += `\t\t${args._tn}Distinct(column_name: String, where: String,condition:Condition${args._tn}, limit: Int, offset: Int, sort: String): [${args._tn}]\r\n` + str += `\t\t${args._tn}GroupBy(fields: String, having: String, limit: Int, offset: Int, sort: String): [${args._tn}GroupBy]\r\n` + str += `\t\t${args._tn}Aggregate(column_name: String!, having: String, limit: Int, offset: Int, sort: String, func: String!): [${args._tn}Aggregate]\r\n` + str += `\t\t${args._tn}Distribution(min: Int, max: Int, step: Int, steps: String, column_name: String!): [distribution]\r\n` + str += `\t}\r\n` + return str; + } + + protected _getMutation(args): string { + let str = `type Mutation { \r\n` + str += `\t\t${args._tn}Create(data:${args._tn}Input): ${args._tn}\r\n` + str += `\t\t${args._tn}Update(id:String,data:${args._tn}Input): Int\r\n` // ${args._tn}\r\n` + str += `\t\t${args._tn}Delete(id:String): Int\r\n`// ${args._tn}\r\n` + str += `\t\t${args._tn}CreateBulk(data: [${args._tn}Input]): [Int]\r\n` + str += `\t\t${args._tn}UpdateBulk(data: [${args._tn}Input]): [Int]\r\n` + str += `\t\t${args._tn}DeleteBulk(data: [${args._tn}Input]): [Int]\r\n` + str += `\t},\r\n` + return str; + } + + protected _getType(args): string { + + let str = `type ${args._tn} { \r\n` + let strWhere = `input Condition${args._tn} { \r\n` + + for (const column of args.columns) { + if (column._cn.split(' ').length > 1) { + console.log(`Skipping ${args.tn}.${column._cn}`); + } else { + str += `\t\t${column._cn.replace(/ /g, '_')}: ${this._getGraphqlType(column)},\r\n`; + strWhere += `\t\t${column._cn .replace(/ /g, '_')}: ${this._getGraphqlConditionType(column)},\r\n`; + } + + } + + let hasManyRelations = args.hasMany; + if (hasManyRelations.length > 1) { + hasManyRelations = lodash.uniqBy(hasManyRelations, (e) => { + return [e.tn, e.rtn].join(); + }); + } + + str += hasManyRelations.length ? `\r\n` : ``; + // cityList in Country + for (const {_tn} of hasManyRelations) { + const childTable = _tn; + str += `\t\t${childTable}List: [${childTable}]\r\n`; + strWhere += `\t\t${childTable}List: Condition${childTable}\r\n`; + str += `\t\t${childTable}Count: Int\r\n`; + } + + + str += this.generateManyToManyTypeProps(args); + + let belongsToRelations = args.belongsTo; + if (belongsToRelations.length > 1) { + belongsToRelations = lodash.uniqBy(belongsToRelations, (e) => { + return [e.tn, e.rtn].join(); + }); + } + + str += belongsToRelations.length ? `\r\n` : ``; + // Country withi city - this is reverse + for (const {_rtn} of belongsToRelations) { + const parentTable = _rtn; + str += `\t\t${parentTable}Read(id:String): ${parentTable}\r\n`; + strWhere += `\t\t${parentTable}Read: Condition${parentTable}\r\n`; + } + + str += `\t}\r\n` + + const grpFields = {...GROUPBY_DEFAULT_COLS}; + + str += `type ${args._tn}GroupBy { \r\n` + for (const {_cn, ...rest} of args.columns) { + if (_cn in grpFields) { + grpFields[_cn] = `\t\t# ${_cn} - clashes with column in table\r\n`; + } else { + str += `\t\t${_cn.replace(/ /g, '_')}: ${this._getGraphqlType(rest)},\r\n`; + } + } + str += Object.values(grpFields).join(''); + str += `\t}\r\n` + + const aggFields = {...AGG_DEFAULT_COLS}; + + str += `type ${args._tn}Aggregate { \r\n` + for (const column of args.columns) { + if (column._cn in aggFields) { + aggFields[column._cn] = `\t\t# ${column._cn} - clashes with column in table\r\n`; + } else { + str += `\t\t${column._cn.replace(/ /g, '_')}: ${this._getGraphqlType(column)},\r\n`; + } + } + str += Object.values(aggFields).join(''); + + str += `\t}\r\n` + strWhere += ` + _or:[Condition${args._tn}] + _not:Condition${args._tn} + _and:[Condition${args._tn}] + + \t}\r\n` + + + return `${str}\r\n\r\n${strWhere}`; + } + + protected abstract _getGraphqlType(column: any): string; + + protected abstract _getGraphqlConditionType(columnObj): string; } diff --git a/packages/nocodb/src/lib/sqlMgr/code/gql-schema/xc-ts/GqlXcTsSchemaMssql.ts b/packages/nocodb/src/lib/sqlMgr/code/gql-schema/xc-ts/GqlXcTsSchemaMssql.ts index d10feb4445..398ce3b24e 100644 --- a/packages/nocodb/src/lib/sqlMgr/code/gql-schema/xc-ts/GqlXcTsSchemaMssql.ts +++ b/packages/nocodb/src/lib/sqlMgr/code/gql-schema/xc-ts/GqlXcTsSchemaMssql.ts @@ -1,10 +1,10 @@ -import BaseRender from "../../BaseRender"; import inflection from "inflection"; import lodash from "lodash"; import {AGG_DEFAULT_COLS, GROUPBY_DEFAULT_COLS} from "./schemaHelp"; +import BaseGqlXcTsSchema from "./BaseGqlXcTsSchema"; -class GqlXcTsSchemaMssql extends BaseRender { +class GqlXcTsSchemaMssql extends BaseGqlXcTsSchema { /** * @@ -19,26 +19,6 @@ class GqlXcTsSchemaMssql extends BaseRender { super({dir, filename, ctx}); } - /** - * Prepare variables used in code template - */ - prepare() { - - let data:any = {}; - - /* example of simple variable */ - data.tn = this.ctx.tn_camelize; - - data.columns = { - func: this._renderColumns.bind(this), - args: this.ctx - }; - - return data; - - } - - /** * @@ -97,7 +77,7 @@ class GqlXcTsSchemaMssql extends BaseRender { _getMutation(args) { let str = `type Mutation { \r\n` str += `\t\t${args.tn_camelize}Create(data:${args.tn_camelize}Input): ${args.tn_camelize}\r\n` - str += `\t\t${args.tn_camelize}Update(id:String,data:${args.tn_camelize}Input): Int\r\n` //${args.tn_camelize}\r\n` + str += `\t\t${args.tn_camelize}Update(id:String,data:${args.tn_camelize}Input): Int\r\n` //${args.tn_camelize}\r\n` str += `\t\t${args.tn_camelize}Delete(id:String): Int\r\n`//${args.tn_camelize}\r\n` str += `\t\t${args.tn_camelize}CreateBulk(data: [${args.tn_camelize}Input]): [Int]\r\n` str += `\t\t${args.tn_camelize}UpdateBulk(data: [${args.tn_camelize}Input]): [Int]\r\n` @@ -131,6 +111,8 @@ class GqlXcTsSchemaMssql extends BaseRender { str += `\t\t${childTable}Count: Int\r\n`; } + str += this.generateManyToManyTypeProps(args); + let belongsToRelations = args.relations.filter(r => r.tn === args.tn); if (belongsToRelations.length > 1) belongsToRelations = lodash.uniqBy(belongsToRelations, function (e) { @@ -188,7 +170,7 @@ class GqlXcTsSchemaMssql extends BaseRender { } - _getGraphqlType(columnObj):any { + _getGraphqlType(columnObj): any { switch (columnObj.dt) { @@ -217,14 +199,11 @@ class GqlXcTsSchemaMssql extends BaseRender { case 'datetime': case 'datetime2': case 'datetimeoffset': - case 'geography': - case 'geometry': case 'heirarchyid': case 'image': case 'nchar': case 'ntext': case 'nvarchar': - case 'json': case 'smalldatetime': case 'smallmoney': case 'sql_variant': @@ -240,11 +219,17 @@ class GqlXcTsSchemaMssql extends BaseRender { return "String"; break; + + case 'geography': + case 'geometry': + case 'json': + return 'JSON'; + } } - _getGraphqlConditionType(columnObj):any { + _getGraphqlConditionType(columnObj): any { switch (this._getGraphqlType(columnObj.dt)) { @@ -258,6 +243,8 @@ class GqlXcTsSchemaMssql extends BaseRender { return 'ConditionString' case "[String]": return 'ConditionString' + case "JSON": + return 'ConditionString' } } diff --git a/packages/nocodb/src/lib/sqlMgr/code/gql-schema/xc-ts/GqlXcTsSchemaMysql.ts b/packages/nocodb/src/lib/sqlMgr/code/gql-schema/xc-ts/GqlXcTsSchemaMysql.ts index da443368a3..abb6179931 100644 --- a/packages/nocodb/src/lib/sqlMgr/code/gql-schema/xc-ts/GqlXcTsSchemaMysql.ts +++ b/packages/nocodb/src/lib/sqlMgr/code/gql-schema/xc-ts/GqlXcTsSchemaMysql.ts @@ -1,6 +1,3 @@ -import inflection from "inflection"; -import lodash from "lodash"; -import {AGG_DEFAULT_COLS, GROUPBY_DEFAULT_COLS} from "./schemaHelp"; import BaseGqlXcTsSchema from "./BaseGqlXcTsSchema"; @@ -19,35 +16,14 @@ class GqlXcTsSchemaMysql extends BaseGqlXcTsSchema { super({dir, filename, ctx}); } - /** - * Prepare variables used in code template - */ - prepare() { - - const data:any = {}; - - /* example of simple variable */ - data.tn = this.ctx.tn_camelize; - - data.columns = { - func: this._renderColumns.bind(this), - args: this.ctx - }; - - return data; - - } - - - - /** + /*/!** * * @param args * @param args.columns * @param args.relations * @returns {string} * @private - */ + *!/ _renderColumns(args) { let str = ''; @@ -204,9 +180,9 @@ class GqlXcTsSchemaMysql extends BaseGqlXcTsSchema { return `${str}\r\n\r\n${strWhere}`; } +*/ - - _getGraphqlType(columnObj) { + protected _getGraphqlType(columnObj): string { switch (columnObj.dt) { @@ -251,14 +227,15 @@ class GqlXcTsSchemaMysql extends BaseGqlXcTsSchema { case "tinyblob": case "mediumblob": case "longblob": + case "enum": + case "time": return "String" break; case "set": return "[String]"; break; - case "enum": - case "time": + case "geometry": case "point": case "linestring": @@ -268,14 +245,14 @@ class GqlXcTsSchemaMysql extends BaseGqlXcTsSchema { case "multipolygon": case "json": default: - return "String" + return "JSON" break; } } - _getGraphqlConditionType(columnObj) { + protected _getGraphqlConditionType(columnObj): any { switch (this._getGraphqlType(columnObj.dt)) { @@ -286,6 +263,7 @@ class GqlXcTsSchemaMysql extends BaseGqlXcTsSchema { case "Boolean": return 'ConditionBoolean' case "String": + case "JSON": return 'ConditionString' case "[String]": return 'ConditionString' @@ -294,9 +272,9 @@ class GqlXcTsSchemaMysql extends BaseGqlXcTsSchema { } - getString() { + /*getString() { return this._renderColumns(this.ctx); - } + }*/ } diff --git a/packages/nocodb/src/lib/sqlMgr/code/gql-schema/xc-ts/GqlXcTsSchemaOracle.ts b/packages/nocodb/src/lib/sqlMgr/code/gql-schema/xc-ts/GqlXcTsSchemaOracle.ts index 203384ff69..d416349d7f 100644 --- a/packages/nocodb/src/lib/sqlMgr/code/gql-schema/xc-ts/GqlXcTsSchemaOracle.ts +++ b/packages/nocodb/src/lib/sqlMgr/code/gql-schema/xc-ts/GqlXcTsSchemaOracle.ts @@ -1,10 +1,7 @@ -import BaseRender from "../../BaseRender"; -import inflection from "inflection"; -import lodash from "lodash"; -import {AGG_DEFAULT_COLS, GROUPBY_DEFAULT_COLS} from "./schemaHelp"; +import BaseGqlXcTsSchema from "./BaseGqlXcTsSchema"; -class GqlXcSchemaOracle extends BaseRender { +class GqlXcSchemaOracle extends BaseGqlXcTsSchema { /** * @@ -19,14 +16,14 @@ class GqlXcSchemaOracle extends BaseRender { super({dir, filename, ctx}); } - /** + /*/!** * Prepare variables used in code template - */ + *!/ prepare() { const data:any = {}; - /* example of simple variable */ + /!* example of simple variable *!/ data.tn = this.ctx.tn_camelize; data.columns = { @@ -39,14 +36,14 @@ class GqlXcSchemaOracle extends BaseRender { } - /** + /!** * * @param args * @param args.columns * @param args.relations * @returns {string} * @private - */ + *!/ _renderColumns(args) { let str = ''; @@ -130,6 +127,8 @@ class GqlXcSchemaOracle extends BaseRender { str += `\t\t${childTable}Count: Int\r\n`; } + str+= this.generateManyToManyTypeProps(args); + let belongsToRelations = args.relations.filter(r => r.tn === args.tn); if (belongsToRelations.length > 1) belongsToRelations = lodash.uniqBy(belongsToRelations, function (e) { @@ -184,9 +183,9 @@ class GqlXcSchemaOracle extends BaseRender { return `${str}\r\n\r\n${strWhere}`; } +*/ - - _getGraphqlType(columnObj):any { + protected _getGraphqlType(columnObj):any { switch (columnObj.dt) { case "char": case "nchar": @@ -235,7 +234,7 @@ class GqlXcSchemaOracle extends BaseRender { } - _getGraphqlConditionType(columnObj):any { + protected _getGraphqlConditionType(columnObj):any { switch (this._getGraphqlType(columnObj.dt)) { @@ -252,9 +251,9 @@ class GqlXcSchemaOracle extends BaseRender { } } - getString(){ + /*getString(){ return this._renderColumns(this.ctx); - } + }*/ } diff --git a/packages/nocodb/src/lib/sqlMgr/code/gql-schema/xc-ts/GqlXcTsSchemaPg.ts b/packages/nocodb/src/lib/sqlMgr/code/gql-schema/xc-ts/GqlXcTsSchemaPg.ts index 173d949b7e..89b0c34545 100644 --- a/packages/nocodb/src/lib/sqlMgr/code/gql-schema/xc-ts/GqlXcTsSchemaPg.ts +++ b/packages/nocodb/src/lib/sqlMgr/code/gql-schema/xc-ts/GqlXcTsSchemaPg.ts @@ -1,11 +1,7 @@ -import BaseRender from "../../BaseRender"; -import inflection from "inflection"; -import lodash from "lodash"; -import {AGG_DEFAULT_COLS, GROUPBY_DEFAULT_COLS} from "./schemaHelp"; +import BaseGqlXcTsSchema from "./BaseGqlXcTsSchema"; -class GqlXcSchemaPg extends BaseRender { - +class GqlXcSchemaPg extends BaseGqlXcTsSchema { /** * * @param dir @@ -19,33 +15,16 @@ class GqlXcSchemaPg extends BaseRender { super({dir, filename, ctx}); } - /** - * Prepare variables used in code template - */ - prepare() { - - const data:any = {}; - - /* example of simple variable */ - data.tn = this.ctx.tn_camelize; - data.columns = { - func: this._renderColumns.bind(this), - args: this.ctx - }; - return data; - } - - - /** +/* /!** * * @param args * @param args.columns * @param args.relations * @returns {string} * @private - */ + *!/ _renderColumns(args) { let str = ''; @@ -61,9 +40,9 @@ class GqlXcSchemaPg extends BaseRender { return str; - } + }*/ - _getInputType(args) { +/* _getInputType(args) { let str = `input ${args.tn_camelize}Input { \r\n` for (let i = 0; i < args.columns.length; ++i) { if (args.columns[i]._cn.split(' ').length > 1) { @@ -75,9 +54,9 @@ class GqlXcSchemaPg extends BaseRender { } str += `\t}`; return str; - } + }*/ - _getQuery(args) { +/* _getQuery(args) { let str = `type Query { \r\n` str += `\t\t${args.tn_camelize}List(where: String,condition:Condition${args.tn_camelize}, limit: Int, offset: Int, sort: String): [${args.tn_camelize}]\r\n` str += `\t\t${args.tn_camelize}Read(id:String!): ${args.tn_camelize}\r\n` @@ -90,9 +69,9 @@ class GqlXcSchemaPg extends BaseRender { str += `\t\t${args.tn_camelize}Distribution(min: Int, max: Int, step: Int, steps: String, column_name: String!): [distribution]\r\n` str += `\t}\r\n` return str; - } + }*/ - _getMutation(args) { +/* _getMutation(args) { let str = `type Mutation { \r\n` str += `\t\t${args.tn_camelize}Create(data:${args.tn_camelize}Input): ${args.tn_camelize}\r\n` str += `\t\t${args.tn_camelize}Update(id:String,data:${args.tn_camelize}Input): Int\r\n` //${args.tn_camelize}\r\n` @@ -102,9 +81,9 @@ class GqlXcSchemaPg extends BaseRender { str += `\t\t${args.tn_camelize}DeleteBulk(data: [${args.tn_camelize}Input]): [Int]\r\n` str += `\t},\r\n` return str; - } + }*/ - _getType(args) { +/* _getType(args) { let str = `type ${args.tn_camelize} { \r\n` let strWhere = `input Condition${args.tn_camelize} { \r\n` @@ -134,6 +113,9 @@ class GqlXcSchemaPg extends BaseRender { str += `\t\t${childTable}Count: Int\r\n`; } + + str+= this.generateManyToManyTypeProps(args); + let belongsToRelations = args.relations.filter(r => r.tn === args.tn); if (belongsToRelations.length > 1) belongsToRelations = lodash.uniqBy(belongsToRelations, function (e) { @@ -185,7 +167,7 @@ class GqlXcSchemaPg extends BaseRender { return `${str}\r\n\r\n${strWhere}`; - } + }*/ _getGraphqlType(columnObj) { @@ -231,6 +213,19 @@ class GqlXcSchemaPg extends BaseRender { return "Float"; break; + + case "json": + case "jsonb": + case "anyenum": + case "anynonarray": + case "path": + case "point": + case "polygon": + if (columnObj.dtx === 'ARRAY') { + return "[JSON]" + } + return 'JSON'; + case "character": case "uuid": case "date": @@ -248,12 +243,8 @@ class GqlXcSchemaPg extends BaseRender { case "timetz": case "time with time zone": case "daterange": - case "json": - case "jsonb": case "gtsvector": case "index_am_handler": - case "anyenum": - case "anynonarray": case "anyrange": case "box": case "bpchar": @@ -274,12 +265,9 @@ class GqlXcSchemaPg extends BaseRender { case "numrange": case "oid": case "opaque": - case "path": case "pg_ddl_command": case "pg_lsn": case "pg_node_tree": - case "point": - case "polygon": case "record": case "refcursor": case "regclass": @@ -319,7 +307,7 @@ class GqlXcSchemaPg extends BaseRender { } - _getGraphqlConditionType(columnObj):any { + protected _getGraphqlConditionType(columnObj):any { switch (this._getGraphqlType(columnObj.dt)) { @@ -333,13 +321,17 @@ class GqlXcSchemaPg extends BaseRender { return 'ConditionString' case "[String]": return 'ConditionString' + case "[JSON]": + return 'ConditionString' + case "JSON": + return 'ConditionString' } } - getString(){ + /* getString(){ return this._renderColumns(this.ctx); - } + }*/ } diff --git a/packages/nocodb/src/lib/sqlMgr/code/gql-schema/xc-ts/GqlXcTsSchemaSqlite.ts b/packages/nocodb/src/lib/sqlMgr/code/gql-schema/xc-ts/GqlXcTsSchemaSqlite.ts index 838d44d63e..781bb8e7a5 100644 --- a/packages/nocodb/src/lib/sqlMgr/code/gql-schema/xc-ts/GqlXcTsSchemaSqlite.ts +++ b/packages/nocodb/src/lib/sqlMgr/code/gql-schema/xc-ts/GqlXcTsSchemaSqlite.ts @@ -1,10 +1,7 @@ -import BaseRender from "../../BaseRender"; -import inflection from "inflection"; -import lodash from "lodash"; -import {AGG_DEFAULT_COLS, GROUPBY_DEFAULT_COLS} from "./schemaHelp"; +import BaseGqlXcTsSchema from "./BaseGqlXcTsSchema"; -class GqlXcSchemaSqlite extends BaseRender { +class GqlXcSchemaSqlite extends BaseGqlXcTsSchema { /** * @@ -19,35 +16,16 @@ class GqlXcSchemaSqlite extends BaseRender { super({dir, filename, ctx}); } - /** - * Prepare variables used in code template - */ - prepare() { - - const data:any = {}; - - /* example of simple variable */ - data.tn = this.ctx.tn_camelize; - - data.columns = { - func: this._renderColumns.bind(this), - args: this.ctx - }; - return data; - - } - - - /** + /*/!** * * @param args * @param args.columns * @param args.relations * @returns {string} * @private - */ - _renderColumns(args) { + *!/ + public _renderColumns(args): string { let str = ''; @@ -62,23 +40,95 @@ class GqlXcSchemaSqlite extends BaseRender { return str; + }*/ + + protected _getGraphqlType(columnObj): any { + + switch (columnObj.dt) { + case 'int': + case 'integer': + case 'tinyint': + case 'smallint': + case 'mediumint': + case 'bigint': + case 'int2': + case 'int8': + return 'Int' + break; + + case 'character': + case 'blob sub_type text': + case 'blob': + return 'String'; + break; + + case 'real': + case 'double': + case 'double precision': + case 'float': + case 'numeric': + return 'Float' + break; + + case 'boolean': + return 'Boolean'; + break; + + case 'date': + case 'datetime': + case 'text': + case 'varchar': + case 'timestamp': + return 'String'; + break; + + default: + return 'String'; + break; + + } + } - _getInputType(args) { + + protected _getGraphqlConditionType(columnObj): any { + + switch (this._getGraphqlType(columnObj.dt)) { + + case "Int": + return 'ConditionInt' + case "Float": + return 'ConditionFloat'; + case "Boolean": + return 'ConditionBoolean' + case "String": + return 'ConditionString' + case "[String]": + return 'ConditionString' + } + + } + +/* + public getString(): string { + return this._renderColumns(this.ctx); + } +*/ +/* + protected _getInputType(args): string { let str = `input ${args.tn_camelize}Input { \r\n` - for (let i = 0; i < args.columns.length; ++i) { - if (args.columns[i]._cn.split(' ').length > 1) { - // str += `\t\t${args.columns[i]._cn}: ${this._getGraphqlType(args.columns[i])},\r\n`; + for (const column of args.columns) { + if (column._cn.split(' ').length > 1) { + // str += `\t\t${column._cn}: ${this._getGraphqlType(column)},\r\n`; } else { - str += `\t\t${args.columns[i]._cn}: ${this._getGraphqlType(args.columns[i])},\r\n`; + str += `\t\t${column._cn}: ${this._getGraphqlType(column)},\r\n`; } - } str += `\t}`; return str; } - _getQuery(args) { + protected _getQuery(args): string { let str = `type Query { \r\n` str += `\t\t${args.tn_camelize}List(where: String,condition:Condition${args.tn_camelize}, limit: Int, offset: Int, sort: String): [${args.tn_camelize}]\r\n` str += `\t\t${args.tn_camelize}Read(id:String!): ${args.tn_camelize}\r\n` @@ -93,11 +143,11 @@ class GqlXcSchemaSqlite extends BaseRender { return str; } - _getMutation(args) { + protected _getMutation(args): string { let str = `type Mutation { \r\n` str += `\t\t${args.tn_camelize}Create(data:${args.tn_camelize}Input): ${args.tn_camelize}\r\n` - str += `\t\t${args.tn_camelize}Update(id:String,data:${args.tn_camelize}Input): Int\r\n` //${args.tn_camelize}\r\n` - str += `\t\t${args.tn_camelize}Delete(id:String): Int\r\n`//${args.tn_camelize}\r\n` + str += `\t\t${args.tn_camelize}Update(id:String,data:${args.tn_camelize}Input): Int\r\n` // ${args.tn_camelize}\r\n` + str += `\t\t${args.tn_camelize}Delete(id:String): Int\r\n`// ${args.tn_camelize}\r\n` str += `\t\t${args.tn_camelize}CreateBulk(data: [${args.tn_camelize}Input]): [Int]\r\n` str += `\t\t${args.tn_camelize}UpdateBulk(data: [${args.tn_camelize}Input]): [Int]\r\n` str += `\t\t${args.tn_camelize}DeleteBulk(data: [${args.tn_camelize}Input]): [Int]\r\n` @@ -105,49 +155,51 @@ class GqlXcSchemaSqlite extends BaseRender { return str; } - _getType(args) { + protected _getType(args): string { let str = `type ${args.tn_camelize} { \r\n` let strWhere = `input Condition${args.tn_camelize} { \r\n` - for (let i = 0; i < args.columns.length; ++i) { - str += `\t\t${args.columns[i]._cn}: ${this._getGraphqlType(args.columns[i])},\r\n`; - strWhere += `\t\t${args.columns[i]._cn.replace(/ /g, '_')}: ${this._getGraphqlConditionType(args.columns[i])},\r\n`; + for (const column of args.columns.length) { + str += `\t\t${column._cn}: ${this._getGraphqlType(column)},\r\n`; + strWhere += `\t\t${column._cn.replace(/ /g, '_')}: ${this._getGraphqlConditionType(column)},\r\n`; } let hasManyRelations = args.relations.filter(r => r.rtn === args.tn); - if (hasManyRelations.length > 1) - hasManyRelations = lodash.uniqBy(hasManyRelations, function (e) { + if (hasManyRelations.length > 1) { + hasManyRelations = lodash.uniqBy(hasManyRelations, (e) => { return [e.tn, e.rtn].join(); }); - + } str += hasManyRelations.length ? `\r\n` : ``; // cityList in Country - for (let i = 0; i < hasManyRelations.length; ++i) { - let childTable = inflection.camelize(hasManyRelations[i]._tn) + for (const {_tn} of hasManyRelations.length) { + const childTable = inflection.camelize(_tn) str += `\t\t${childTable}List: [${childTable}]\r\n`; strWhere += `\t\t${childTable}List: Condition${childTable}\r\n`; str += `\t\t${childTable}Count: Int\r\n`; } + str += this.generateManyToManyTypeProps(args); + let belongsToRelations = args.relations.filter(r => r.tn === args.tn); - if (belongsToRelations.length > 1) - belongsToRelations = lodash.uniqBy(belongsToRelations, function (e) { + if (belongsToRelations.length > 1) { + belongsToRelations = lodash.uniqBy(belongsToRelations, (e) => { return [e.tn, e.rtn].join(); }); + } str += belongsToRelations.length ? `\r\n` : ``; // Country withi city - this is reverse - for (let i = 0; i < belongsToRelations.length; ++i) { - let parentTable = inflection.camelize(belongsToRelations[i]._rtn) + for (const {_rtn} of belongsToRelations.length) { + const parentTable = inflection.camelize(_rtn) str += `\t\t${parentTable}Read(id:String): ${parentTable}\r\n`; strWhere += `\t\t${parentTable}Read: Condition${parentTable}\r\n`; } str += `\t}\r\n` - - const grpFields = Object.assign({}, GROUPBY_DEFAULT_COLS); + const grpFields = {...GROUPBY_DEFAULT_COLS}; str += `type ${args.tn_camelize}GroupBy { \r\n` for (let i = 0; i < args.columns.length; ++i) { @@ -160,7 +212,7 @@ class GqlXcSchemaSqlite extends BaseRender { str += Object.values(grpFields).join(''); str += `\t}\r\n`; - const aggFields = Object.assign({}, AGG_DEFAULT_COLS); + const aggFields = {...AGG_DEFAULT_COLS}; str += `type ${args.tn_camelize}Aggregate { \r\n` for (let i = 0; i < args.columns.length; ++i) { @@ -183,79 +235,7 @@ class GqlXcSchemaSqlite extends BaseRender { return `${str}\r\n\r\n${strWhere}`; - } - - _getGraphqlType(columnObj):any { - - switch (columnObj.dt) { - case 'int': - case 'integer': - case 'tinyint': - case 'smallint': - case 'mediumint': - case 'bigint': - case 'int2': - case 'int8': - return 'Int' - break; - - case 'character': - case 'blob sub_type text': - case 'numeric': - case 'blob': - return 'String'; - break; - - case 'real': - case 'double': - case 'double precision': - case 'float': - case 'numeric': - return 'Float' - break; - - case 'boolean': - return 'Boolean'; - break; - - case 'date': - case 'datetime': - case 'text': - case 'varchar': - case 'timestamp': - return 'String'; - break; - - default: - return 'String'; - break; - - } - - } - - - _getGraphqlConditionType(columnObj):any { - - switch (this._getGraphqlType(columnObj.dt)) { - - case "Int": - return 'ConditionInt' - case "Float": - return 'ConditionFloat'; - case "Boolean": - return 'ConditionBoolean' - case "String": - return 'ConditionString' - case "[String]": - return 'ConditionString' - } - - } - - getString(){ - return this._renderColumns(this.ctx); - } + }*/ }