Browse Source

feat: GQL - M2M

Signed-off-by: Pranav C <61551451+pranavxc@users.noreply.github.com>
pull/341/head
Pranav C 3 years ago
parent
commit
3add4bb2d3
  1. 1
      packages/nc-gui/components/project/spreadsheet/apis/gqlApi.js
  2. 49
      packages/nc-gui/components/project/spreadsheet/components/editColumn/linkedToAnotherOptions.vue
  3. 6
      packages/nc-gui/components/project/spreadsheet/components/expandedForm.vue
  4. 9
      packages/nc-gui/components/project/spreadsheet/components/virtualCell/belogsToCell.vue
  5. 12
      packages/nc-gui/components/project/spreadsheet/components/virtualCell/hasManyCell.vue
  6. 8
      packages/nc-gui/components/project/spreadsheet/components/virtualCell/manyToManyCell.vue
  7. 18
      packages/nocodb/src/lib/noco/common/BaseApiBuilder.ts
  8. 19
      packages/nocodb/src/lib/noco/gql/GqlApiBuilder.ts
  9. 18
      packages/nocodb/src/lib/noco/meta/NcMetaMgr.ts
  10. 13
      packages/nocodb/src/lib/noco/rest/RestApiBuilder.ts
  11. 13
      packages/nocodb/src/lib/sqlMgr/code/gql-schema/xc-ts/GqlXcTsSchemaMssql.ts

1
packages/nc-gui/components/project/spreadsheet/apis/gqlApi.js

@ -90,7 +90,6 @@ export default class GqlApi {
}
async gqlRelationReqBody(params) {
let str = '';
if (params.childs) {
for (const child of params.childs.split(',')) {

49
packages/nc-gui/components/project/spreadsheet/components/editColumn/linkedToAnotherOptions.vue

@ -14,11 +14,12 @@
<v-container fluid class="wrapper">
<v-row>
<v-col cols="6">
<v-col cols="12">
<v-autocomplete
validate-on-blur
outlined
class="caption"
hide-details
hide-details="auto"
:loading="isRefTablesLoading"
label="Child Table"
:full-width="false"
@ -28,24 +29,25 @@
item-value="tn"
required
dense
:rules="tableRules"
></v-autocomplete>
</v-col
>
<v-col cols="6">
<v-text-field
outlined
class="caption"
hide-details
label="Child Column"
:full-width="false"
v-model="relation.childColumn"
required
dense
ref="childColumnRef"
@change="onColumnSelect"
></v-text-field>
</v-col
>
<!-- <v-col cols="6">
<v-text-field
outlined
class="caption"
hide-details
label="Child Column"
:full-width="false"
v-model="relation.childColumn"
required
dense
ref="childColumnRef"
@change="onColumnSelect"
></v-text-field>
</v-col
>-->
</v-row>
<template v-if="!isSQLite">
<v-row>
@ -103,7 +105,7 @@
<script>
export default {
name: "linked-to-another-options",
props: ['nodes', 'column', 'meta', 'isSQLite','alias'],
props: ['nodes', 'column', 'meta', 'isSQLite', 'alias'],
data: () => ({
type: 'hm',
refTables: [],
@ -132,6 +134,15 @@ export default {
type: 'real'
}
},
computed: {
tableRules() {
return []
// this.meta ? [
// v => this.type !== 'mm' || !this.meta.manyToMany.some(mm => mm.tn === v && mm.rtn === this.meta.tn || mm.rtn === v && mm.tn === this.meta.tn) || 'Duplicate relation is not allowed at the moment',
// v => this.type !== 'hm' || !this.meta.hasMany.some(hm => hm.tn === v) || 'Duplicate relation is not allowed at the moment'
// ] : []
}
},
methods: {
async loadColumnList() {
this.isRefColumnsLoading = true;
@ -179,7 +190,7 @@ export default {
},
'xcM2MRelationCreate',
{
_cn:this.alias,
_cn: this.alias,
...this.relation,
type: this.isSQLite || this.relation.type === 'virtual' ? 'virtual' : 'real',
parentTable: this.meta.tn,

6
packages/nc-gui/components/project/spreadsheet/components/expandedForm.vue

@ -1,5 +1,6 @@
<template>
<v-card width="1000" max-width="100%">
<v-toolbar height="55" class="elevation-1">
<div class="d-100 d-flex ">
<h5 class="title text-center">
@ -308,6 +309,7 @@ export default {
obj[col] = this.localState[col];
return obj;
}, {});
if (this.isNew) {
const data = await this.api.insert(updatedObj);
Object.assign(this.localState, data)
@ -379,7 +381,9 @@ export default {
if (this.showSystemFields) {
return this.meta.columns || [];
} else {
return (this.meta.columns.filter(c => !(c.pk && c.ai) && !hideCols.includes(c.cn))) || [];
return this.meta.columns.filter(c => !(c.pk && c.ai) && !hideCols.includes(c.cn)
&& !((this.meta.v || []).some(v => v.bt && v.bt.cn === c.cn))
) || [];
}
},
isChanged() {

9
packages/nc-gui/components/project/spreadsheet/components/virtualCell/belogsToCell.vue

@ -221,9 +221,10 @@ export default {
const pid = this.parentMeta.columns.filter((c) => c.pk).map(c => parent[c._cn]).join('___');
const id = this.meta.columns.filter((c) => c.pk).map(c => this.row[c._cn]).join('___');
const _cn = this.meta.columns.find(c => c.cn === this.bt.cn)._cn;
if (this.isNew) {
this.localState = parent;
this.$emit('updateCol', this.row, _cn, pid)
this.$emit('updateCol', this.row, _cn, +pid || pid)
this.newRecordModal = false;
return
}
@ -272,9 +273,9 @@ export default {
parentQueryParams() {
if (!this.parentMeta) return {}
return {
childs: (this.parentMeta && this.parentMeta.hasMany && this.parentMeta.hasMany.map(hm => hm.tn).join()) || '',
parents: (this.parentMeta && this.parentMeta.belongsTo && this.parentMeta.belongsTo.map(hm => hm.rtn).join()) || '',
many: (this.parentMeta && this.parentMeta.manyToMany && this.parentMeta.manyToMany.map(mm => mm.rtn).join()) || ''
childs: (this.parentMeta && this.parentMeta.v && this.parentMeta.v.filter(v => v.hm).map(({hm}) => hm.tn).join()) || '',
parents: (this.parentMeta && this.parentMeta.v && this.parentMeta.v.filter(v => v.bt).map(({bt}) => bt.rtn).join()) || '',
many: (this.parentMeta && this.parentMeta.v && this.parentMeta.v.filter(v => v.mm).map(({mm}) => mm.rtn).join()) || ''
}
},
parentAvailableColumns() {

12
packages/nc-gui/components/project/spreadsheet/components/virtualCell/hasManyCell.vue

@ -242,7 +242,7 @@ export default {
this.newRecordModal = false;
await this.childApi.update(id, {
[_cn]: +this.parentId
[_cn]: +this.parentId || this.parentId
}, {
[_cn]: child[this.childForeignKey]
});
@ -266,7 +266,7 @@ export default {
await this.loadChildMeta();
this.isNewChild = true;
this.selectedChild = {
[this.childForeignKey]: this.parentId
[this.childForeignKey]: +this.parentId || this.parentId
};
this.expandFormModal = true;
setTimeout(() => {
@ -316,7 +316,7 @@ export default {
const columns = [];
if (this.childMeta.columns) {
columns.push(...this.childMeta.columns.filter(c => !(c.pk && c.ai) && !hideCols.includes(c.cn)))
columns.push(...this.childMeta.columns.filter(c => !(c.pk && c.ai) && !hideCols.includes(c.cn) && !((this.childMeta.v || []).some(v => v.bt && v.bt.cn === c.cn))))
}
if (this.childMeta.v) {
columns.push(...this.childMeta.v.map(v => ({...v, virtual: 1})));
@ -326,9 +326,9 @@ export default {
childQueryParams() {
if (!this.childMeta) return {}
return {
childs: (this.childMeta && this.childMeta.hasMany && this.childMeta.hasMany.map(hm => hm.tn).join()) || '',
parents: (this.childMeta && this.childMeta.belongsTo && this.childMeta.belongsTo.map(hm => hm.rtn).join()) || '',
many: (this.childMeta && this.childMeta.manyToMany && this.childMeta.manyToMany.map(mm => mm.rtn).join()) || ''
childs: (this.childMeta && this.childMeta.v && this.childMeta.v.filter(v => v.hm).map(({hm}) => hm.tn).join()) || '',
parents: (this.childMeta && this.childMeta.v && this.childMeta.v.filter(v => v.bt).map(({bt}) => bt.rtn).join()) || '',
many: (this.childMeta && this.childMeta.v && this.childMeta.v.filter(v => v.mm).map(({mm}) => mm.rtn).join()) || ''
}
},
parentId() {

8
packages/nc-gui/components/project/spreadsheet/components/virtualCell/manyToManyCell.vue

@ -12,7 +12,7 @@
@unlink="unlinkChild"
></item-chip>
</template> <v-chip v-if="value && value.length === 10" class="caption pointer ml-1 grey--text" @click="showChildListModal">more...</v-chip>
</template> <span v-if="value && value.length === 10" class="caption pointer ml-1 grey--text" @click="showChildListModal">more...</span>
</div>
<div class="actions align-center justify-center px-1 flex-shrink-1"
:class="{'d-none': !active, 'd-flex':active }">
@ -360,9 +360,9 @@ export default {
childQueryParams() {
if (!this.childMeta) return {}
return {
childs: (this.childMeta && this.childMeta.hasMany && this.childMeta.hasMany.map(hm => hm.tn).join()) || '',
parents: (this.childMeta && this.childMeta.belongsTo && this.childMeta.belongsTo.map(hm => hm.rtn).join()) || '',
many: (this.childMeta && this.childMeta.manyToMany && this.childMeta.manyToMany.map(mm => mm.rtn).join()) || ''
childs: (this.childMeta && this.childMeta.v && this.childMeta.v.filter(v => v.hm).map(({hm}) => hm.tn).join()) || '',
parents: (this.childMeta && this.childMeta.v && this.childMeta.v.filter(v=>v.bt).map(({bt}) => bt.rtn).join()) || '',
many: (this.childMeta && this.childMeta.v && this.childMeta.v.filter(v=>v.mm).map(({mm}) => mm.rtn).join()) || ''
}
},
conditionGraph() {

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

@ -232,11 +232,11 @@ export default abstract class BaseApiBuilder<T extends Noco> implements XcDynami
dr: onDelete,
ur: onUpdate,
})
}else {
} 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,
@ -914,20 +914,22 @@ export default abstract class BaseApiBuilder<T extends Noco> implements XcDynami
}
protected generateContextForTable(tn: string, columns: any[], relations, hasMany: any[], belongsTo: any[], type = 'table', table_name_alias?: string): any {
protected generateContextForTable(tn: string, columns: any[], relations, hasMany: any[], belongsTo: any[], type = 'table', tableNameAlias?: string): any {
this.baseLog(`generateContextForTable : '%s' %s`, tn, type);
for (const col of columns) {
col._cn = this.getColumnNameAlias(col);
col._cn = col._cn || this.getColumnNameAlias(col);
}
const tableNameAlias = table_name_alias || this.getTableNameAlias(tn);
// tslint:disable-next-line:variable-name
const _tn = tableNameAlias || this.getTableNameAlias(tn)
const ctx = {
dbType: this.connectionConfig.client,
tn,
_tn: tableNameAlias,
tn_camelize: inflection.camelize(tableNameAlias),
tn_camelize_low: inflection.camelize(tableNameAlias, true),
_tn,
tn_camelize: inflection.camelize(_tn),
tn_camelize_low: inflection.camelize(_tn, true),
columns,
relations,
hasMany,

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

@ -525,7 +525,7 @@ export class GqlApiBuilder extends BaseApiBuilder<Noco> implements XcMetaMgr {
}>;
type?: 'table' | 'view',
columns?: {
[tn: string]: any
[key: string]: any
}
}): Promise<any> {
this.log('xcTablesPopulate : names - %o , type - %s', args?.tableNames, args?.type)
@ -535,8 +535,8 @@ export class GqlApiBuilder extends BaseApiBuilder<Noco> implements XcMetaMgr {
// set table name alias
relations.forEach(r => {
r._rtn = this.getTableNameAlias(r.rtn);
r._tn = this.getTableNameAlias(r.tn);
r._rtn = args?.tableNames?.find(t => t.tn === r.rtn)?._tn || this.getTableNameAlias(r.rtn);
r._tn = args?.tableNames?.find(t => t.tn === r.tn)?._tn || this.getTableNameAlias(r.tn);
r.enabled = true;
})
@ -778,7 +778,6 @@ export class GqlApiBuilder extends BaseApiBuilder<Noco> implements XcMetaMgr {
await this.getManyToManyRelations();
// generate schema of models
for (const meta of Object.values(this.metas)) {
/**************** prepare GQL: schemas, types, resolvers ****************/
@ -1131,7 +1130,7 @@ export class GqlApiBuilder extends BaseApiBuilder<Noco> implements XcMetaMgr {
/* update parent table meta and resolvers */
{
const columns = await this.getColumnList(tnp);
const columns = this.metas[tnp]?.columns;
const hasMany = this.extractHasManyRelationsOfTable(relations, tnp);
const belongsTo = this.extractBelongsToRelationsOfTable(relations, tnp);
const ctx = this.generateContextForTable(tnp, columns, relations, hasMany, belongsTo);
@ -1175,7 +1174,8 @@ export class GqlApiBuilder extends BaseApiBuilder<Noco> implements XcMetaMgr {
await this.xcMeta.metaUpdate(this.projectId, this.dbAlias, 'nc_models', {
title: tnp,
meta: JSON.stringify(oldMeta),
schema: this.schemas[tnp]
schema: this.schemas[tnp],
...(queryParams ? {query_params: JSON.stringify(queryParams)} : {})
}, {'title': tnp})
}
@ -1259,7 +1259,7 @@ export class GqlApiBuilder extends BaseApiBuilder<Noco> implements XcMetaMgr {
/* update child table meta and resolvers */
{
const columns = await this.getColumnList(tnc);
const columns = this.metas[tnc]?.columns;
const belongsTo = this.extractBelongsToRelationsOfTable(relations, tnc);
const hasMany = this.extractHasManyRelationsOfTable(relations, tnc);
const ctx = this.generateContextForTable(tnc, columns, relations, hasMany, belongsTo);
@ -1299,7 +1299,8 @@ export class GqlApiBuilder extends BaseApiBuilder<Noco> implements XcMetaMgr {
await this.xcMeta.metaUpdate(this.projectId, this.dbAlias, 'nc_models', {
title: tnc,
meta: JSON.stringify(oldMeta),
schema: this.schemas[tnc]
schema: this.schemas[tnc],
...(queryParams ? {query_params: JSON.stringify(queryParams)} : {})
}, {'title': tnc})
}
@ -1831,8 +1832,6 @@ export class GqlApiBuilder extends BaseApiBuilder<Noco> implements XcMetaMgr {
title: tn
})
}
{
const listPropName = `${this.metas[child]._tn}MMList`;
this.log(`onRelationCreate : Generating and inserting '%s' loaders`, listPropName);

18
packages/nocodb/src/lib/noco/meta/NcMetaMgr.ts

@ -2308,8 +2308,8 @@ export default class NcMetaMgr {
const associateTableCols = [];
associateTableCols.push({
cn: `${childMeta.tn}_id`,
_cn: `${childMeta.tn}_id`,
cn: `${childMeta.tn}_c_id`,
_cn: `${childMeta._tn}CId`,
rqd: true,
pk: true,
ai: false,
@ -2320,8 +2320,8 @@ export default class NcMetaMgr {
un: childPK.un,
altered: 1
}, {
cn: `${parentMeta.tn}_id`,
_cn: `${parentMeta.tn}_id`,
cn: `${parentMeta.tn}_p_id`,
_cn: `${parentMeta._tn}PId`,
rqd: true,
pk: true,
ai: false,
@ -2333,13 +2333,15 @@ export default class NcMetaMgr {
altered: 1
});
// todo: associative table naming
const aTn = `${this.projectConfigs[projectId]?.prefix ?? ''}_nc_m2m_${parentMeta.tn}_${childMeta.tn}_${Math.floor(Math.random() * 1000)}`;
const aTnAlias = `m2m${parentMeta._tn}_${childMeta._tn}`;
const out = await this.projectMgr.getSqlMgr({id: projectId}).handleRequest('tableCreate', {
...args,
args: {
tn: aTn,
_tn: aTn,
_tn: aTnAlias,
columns: associateTableCols
}
});
@ -2350,7 +2352,7 @@ export default class NcMetaMgr {
...args,
args: {
tn: aTn,
_tn: aTn,
_tn: aTnAlias,
columns: associateTableCols
}, api: 'tableCreate'
},
@ -2366,7 +2368,7 @@ export default class NcMetaMgr {
const rel1Args = {
...args.args,
childTable: aTn,
childColumn: `${parentMeta.tn}_id`,
childColumn: `${parentMeta.tn}_p_id`,
parentTable: parentMeta.tn,
parentColumn: parentPK.cn,
type: 'real'
@ -2374,7 +2376,7 @@ export default class NcMetaMgr {
const rel2Args = {
...args.args,
childTable: aTn,
childColumn: `${childMeta.tn}_id`,
childColumn: `${childMeta.tn}_c_id`,
parentTable: childMeta.tn,
parentColumn: childPK.cn,
type: 'real'

13
packages/nocodb/src/lib/noco/rest/RestApiBuilder.ts

@ -284,6 +284,15 @@ export class RestApiBuilder extends BaseApiBuilder<Noco> {
/* Get all relations */
const relations = await this.relationsSyncAndGet();
// set table name alias
relations.forEach(r => {
r._rtn = args?.tableNames?.find(t => t.tn === r.rtn)?._tn || this.getTableNameAlias(r.rtn);
r._tn = args?.tableNames?.find(t => t.tn === r.tn)?._tn || this.getTableNameAlias(r.tn);
r.enabled = true;
})
this.relationsCount = relations.length;
if (args?.tableNames?.length) {
@ -984,7 +993,7 @@ export class RestApiBuilder extends BaseApiBuilder<Noco> {
const relations = await this.getXcRelationList();
{
const swaggerArr = [];
const columns = await this.getColumnList(tnp);
const columns = this.metas[tnp]?.columns;
const hasMany = this.extractHasManyRelationsOfTable(relations, tnp);
// set table name alias
@ -1080,7 +1089,7 @@ export class RestApiBuilder extends BaseApiBuilder<Noco> {
}
{
const swaggerArr = [];
const columns = await this.getColumnList(tnp);
const columns = this.metas[tnc]?.columns;
const belongsTo = this.extractBelongsToRelationsOfTable(relations, tnc);
const ctx = this.generateContextForTable(tnc, columns, relations, [], belongsTo);
const meta = ModelXcMetaFactory.create(this.connectionConfig, this.generateRendererArgs(ctx)).getObject();

13
packages/nocodb/src/lib/sqlMgr/code/gql-schema/xc-ts/GqlXcTsSchemaMssql.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,15 +16,16 @@ class GqlXcTsSchemaMssql extends BaseGqlXcTsSchema {
super({dir, filename, ctx});
}
/*
/**
/!**
*
* @param args
* @param args.columns
* @param args.relations
* @returns {string}
* @private
*/
*!/
_renderColumns(args) {
let str = '';
@ -168,6 +166,7 @@ class GqlXcTsSchemaMssql extends BaseGqlXcTsSchema {
return `${str}\r\n\r\n${strWhere}`;
}
*/
_getGraphqlType(columnObj): any {
@ -249,9 +248,9 @@ class GqlXcTsSchemaMssql extends BaseGqlXcTsSchema {
}
getString() {
/* getString() {
return this._renderColumns(this.ctx);
}
}*/
}

Loading…
Cancel
Save