Browse Source

feat: GQL - M2M many to many

Signed-off-by: Pranav C <61551451+pranavxc@users.noreply.github.com>
pull/341/head
Pranav C 3 years ago
parent
commit
cf2452b04a
  1. 6
      packages/nc-gui/components/project/spreadsheet/apis/gqlApi.js
  2. 8
      packages/nc-gui/components/project/spreadsheet/components/virtualCell/hasManyCell.vue
  3. 22
      packages/nc-gui/components/project/spreadsheet/components/virtualCell/manyToManyCell.vue
  4. 8
      packages/nc-gui/components/project/spreadsheet/mixins/spreadsheet.js
  5. 17
      packages/nc-gui/components/project/spreadsheet/rowsXcDataTable.vue
  6. 223
      packages/nc-gui/components/project/spreadsheet/views/xcGridView.vue
  7. 82
      packages/nc-gui/components/project/table.vue
  8. 4
      packages/nc-gui/layouts/default.vue
  9. 14
      packages/nc-gui/nuxt.config.js
  10. 16
      packages/nc-gui/store/sqlMgr.js
  11. 6
      packages/nc-gui/store/windows.js
  12. 6
      packages/nocodb/src/lib/dataMapper/lib/sql/BaseModelSql.ts
  13. 10
      packages/nocodb/src/lib/noco/common/BaseApiBuilder.ts
  14. 214
      packages/nocodb/src/lib/noco/gql/GqlApiBuilder.ts
  15. 5
      packages/nocodb/src/lib/noco/gql/GqlResolver.ts
  16. 3
      packages/nocodb/src/lib/noco/rest/RestApiBuilder.ts
  17. 184
      packages/nocodb/src/lib/sqlMgr/code/gql-schema/xc-ts/BaseGqlXcTsSchema.ts
  18. 43
      packages/nocodb/src/lib/sqlMgr/code/gql-schema/xc-ts/GqlXcTsSchemaMssql.ts
  19. 46
      packages/nocodb/src/lib/sqlMgr/code/gql-schema/xc-ts/GqlXcTsSchemaMysql.ts
  20. 29
      packages/nocodb/src/lib/sqlMgr/code/gql-schema/xc-ts/GqlXcTsSchemaOracle.ts
  21. 80
      packages/nocodb/src/lib/sqlMgr/code/gql-schema/xc-ts/GqlXcTsSchemaPg.ts
  22. 232
      packages/nocodb/src/lib/sqlMgr/code/gql-schema/xc-ts/GqlXcTsSchemaSqlite.ts

6
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() {

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

@ -12,6 +12,10 @@
@edit="editChild"
@unlink="unlinkChild"
></item-chip>
<v-chip v-if="value && value.length === 10" class="caption pointer ml-1 grey--text"
@click="showChildListModal">more...
</v-chip>
</template>
</div>
<div class="actions align-center justify-center px-1 flex-shrink-1"
@ -285,7 +289,7 @@ export default {
childApi() {
return this.childMeta && this.childMeta._tn ?
ApiFactory.create(this.$store.getters['project/GtrProjectType'],
this.childMeta && this.childMeta._tn, this.childMeta && this.childMeta.columns, this,this.childMeta) : null;
this.childMeta && this.childMeta._tn, this.childMeta && this.childMeta.columns, this, this.childMeta) : null;
},
childPrimaryCol() {
return this.childMeta && (this.childMeta.columns.find(c => 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}

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

@ -12,7 +12,7 @@
@unlink="unlinkChild"
></item-chip>
</template>
</template> <v-chip v-if="value && value.length === 10" class="caption pointer ml-1 grey--text" @click="showChildListModal">more...</v-chip>
</div>
<div class="actions align-center justify-center px-1 flex-shrink-1"
:class="{'d-none': !active, 'd-flex':active }">
@ -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]
},

8
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 = [];

17
packages/nc-gui/components/project/spreadsheet/rowsXcDataTable.vue

@ -18,7 +18,7 @@
</div>
</template>
<v-list dense>
<v-list-item v-for="col in availableColumns" :key="col.cn"
<v-list-item v-for="col in availableRealColumns" :key="col.cn"
@click="searchField = col._cn">
<span class="caption">{{ col._cn }}</span>
</v-list-item>
@ -53,8 +53,10 @@
<v-spacer></v-spacer>
<lock-menu v-if="_isUIAllowed('view-type')" v-model="viewStatus.type"></lock-menu>
<x-btn tooltip="Reload view data" outlined small text @click="loadTableData">
<x-btn tooltip="Reload view data" outlined small text @click="reload">
<v-icon small class="mr-1" color="grey darken-3">mdi-reload</v-icon>
</x-btn>
<x-btn tooltip="Add new row" v-if="relationType !== 'bt'" :disabled="isLocked" outlined small text
@ -79,12 +81,12 @@
<sort-list
:is-locked="isLocked"
:field-list="fieldList"
:field-list="realFieldList"
v-model="sortList"
></sort-list>
<column-filter
:is-locked="isLocked"
:field-list="fieldList"
:field-list="realFieldList"
v-model="filters"
dense>
</column-filter>
@ -153,6 +155,7 @@
<v-skeleton-loader v-if="!dataLoaded && (loadingData || loadingMeta)" type="table"></v-skeleton-loader>
<template v-else-if="selectedView && (selectedView.type === 'table' || selectedView.show_as === 'grid' )">
<xc-grid-view
:key="key"
ref="ncgridview"
:relationType="relationType"
:columns-width.sync="columnsWidth"
@ -495,6 +498,7 @@ export default {
showTabs: [Boolean, Number]
},
data: () => ({
key:1,
dataLoaded: false,
searchQueryVal: '',
columnsWidth: null,
@ -600,6 +604,11 @@ export default {
...mapActions({
loadTablesFromChildTreeNode: "project/loadTablesFromChildTreeNode"
}),
async reload(){
this.$store.commit('meta/MutClear');
await this.loadTableData();
this.key=Math.random();
},
reloadComments() {
if (this.$refs.ncgridview) {
this.$refs.ncgridview.xcAuditModelCommentsCount();

223
packages/nc-gui/components/project/spreadsheet/views/xcGridView.vue

@ -165,120 +165,123 @@
:db-alias="nodes.dbAlias"
/>
<div v-else-if="columnObj.cn in hasMany" class="hasmany-col d-flex ">
{{ rowObj[columnObj._cn] }}
<v-spacer></v-spacer>
<v-menu open-on-hover>
<template v-slot:activator="{on}">
<v-icon v-on="on" class=" hasmany-col-menu-icon">mdi-menu-down</v-icon>
</template>
<v-list dense>
<v-list-item v-for="(rel,i) in hasMany[columnObj.cn]"
@click="addNewRelationTab(
rel,
table,
meta._tn,
rel.tn,
rel._tn,
rowObj[columnObj._cn],
'hm',
rowObj,
rowObj[primaryValueColumn]
)"
:key="i"
>
<v-chip small :color="colors[i % colors.length]">
<span class="caption text-capitalize"> {{ rel._tn }}</span>
</v-chip>
</v-list-item>
</v-list>
</v-menu>
</div>
<span v-else-if="columnObj._cn in belongsTo"
@click="addNewRelationTab(
belongsTo[columnObj._cn],
table,
meta._tn,
belongsTo[columnObj._cn].rtn,
belongsTo[columnObj._cn]._rtn,
rowObj[columnObj._cn],
'bt',
rowObj,
rowObj[primaryValueColumn]
)"
class="belongsto-col">{{ rowObj[columnObj._cn] }}</span>
<template v-else-if="primaryValueColumn === columnObj._cn">
<v-menu open-on-hover offset-y bottom>
<template v-slot:activator="{on}">
<!-- <v-chip v-on="on"
small
class="caption xc-bt-chip"
outlined
color="success">
{{ rowObj[columnObj.cn] }}
<v-icon v-on="on" class="hasmany-col-menu-icon pv">mdi-menu-down</v-icon>
</v-chip> -->
<span v-on="on"
class="caption xc-bt-chip primary--text">
<!--
<div v-else-if="columnObj.cn in hasMany" class="hasmany-col d-flex ">
{{ rowObj[columnObj._cn] }}
<v-icon v-on="on" class="hasmany-col-menu-icon pv">mdi-menu-down</v-icon>
</span>
</template>
<v-list dense>
<v-list-item dense v-if="haveHasManyrelation"><span class="grey--text caption text-center mt-n2">Has Many</span>
</v-list-item>
<template v-if="haveHasManyrelation">
<template v-for="(hm,idCol) in hasMany">
<template v-for="(rel,i) in hm">
<v-divider
:key="i + '_' + idCol + '_div'"></v-divider>
<v-list-item
class="py-1"
@click="addNewRelationTab(
rel,
table,
meta._tn,
rel.tn,
rel._tn,
rowObj[idCol],
'hm',
rowObj,
rowObj[primaryValueColumn]
)"
:key="i + '_' + idCol"
dense
>
<v-list-item-icon class="mx-1">
<v-icon class="has-many-icon mr-1" small :color="textColors[i % colors.length]">
mdi-source-fork
</v-icon>
</v-list-item-icon>
<!-- <v-chip small >-->
<!-- <v-list-item-title> -->
<span class="caption text-capitalize"> {{ rel._tn }}</span>
<!-- </v-list-item-title>-->
<!-- </v-chip>-->
</v-list-item>
<v-spacer></v-spacer>
<v-menu open-on-hover>
<template v-slot:activator="{on}">
<v-icon v-on="on" class=" hasmany-col-menu-icon">mdi-menu-down</v-icon>
</template>
<v-list dense>
<v-list-item v-for="(rel,i) in hasMany[columnObj.cn]"
@click="addNewRelationTab(
rel,
table,
meta._tn,
rel.tn,
rel._tn,
rowObj[columnObj._cn],
'hm',
rowObj,
rowObj[primaryValueColumn]
)"
:key="i"
>
<v-chip small :color="colors[i % colors.length]">
<span class="caption text-capitalize"> {{ rel._tn }}</span>
</v-chip>
</v-list-item>
</v-list>
</v-menu>
</div>
<span v-else-if="columnObj._cn in belongsTo"
@click="addNewRelationTab(
belongsTo[columnObj._cn],
table,
meta._tn,
belongsTo[columnObj._cn].rtn,
belongsTo[columnObj._cn]._rtn,
rowObj[columnObj._cn],
'bt',
rowObj,
rowObj[primaryValueColumn]
)"
class="belongsto-col">{{ rowObj[columnObj._cn] }}</span>
<template v-else-if="primaryValueColumn === columnObj._cn">
<v-menu open-on-hover offset-y bottom>
<template v-slot:activator="{on}">
&lt;!&ndash; <v-chip v-on="on"
small
class="caption xc-bt-chip"
outlined
color="success">
{{ rowObj[columnObj.cn] }}
<v-icon v-on="on" class="hasmany-col-menu-icon pv">mdi-menu-down</v-icon>
</v-chip> &ndash;&gt;
<span v-on="on"
class="caption xc-bt-chip primary&#45;&#45;text">
{{ rowObj[columnObj._cn] }}
<v-icon v-on="on" class="hasmany-col-menu-icon pv">mdi-menu-down</v-icon>
</span>
</template>
<v-list dense>
<v-list-item dense v-if="haveHasManyrelation"><span class="grey&#45;&#45;text caption text-center mt-n2">Has Many</span>
</v-list-item>
<template v-if="haveHasManyrelation">
<template v-for="(hm,idCol) in hasMany">
<template v-for="(rel,i) in hm">
<v-divider
:key="i + '_' + idCol + '_div'"></v-divider>
<v-list-item
class="py-1"
@click="addNewRelationTab(
rel,
table,
meta._tn,
rel.tn,
rel._tn,
rowObj[idCol],
'hm',
rowObj,
rowObj[primaryValueColumn]
)"
:key="i + '_' + idCol"
dense
>
<v-list-item-icon class="mx-1">
<v-icon class="has-many-icon mr-1" small :color="textColors[i % colors.length]">
mdi-source-fork
</v-icon>
</v-list-item-icon>
&lt;!&ndash; <v-chip small >&ndash;&gt;
&lt;!&ndash; <v-list-item-title> &ndash;&gt;
<span class="caption text-capitalize"> {{ rel._tn }}</span>
&lt;!&ndash; </v-list-item-title>&ndash;&gt;
&lt;!&ndash; </v-chip>&ndash;&gt;
</v-list-item>
</template>
</template>
</template>
<v-list-item v-else>
<span class="caption text-capitalize grey&#45;&#45;text font-weight-light"> No relation found</span>
</v-list-item>
</v-list>
</v-menu>
</template>
</template>
</template>
<v-list-item v-else>
<span class="caption text-capitalize grey--text font-weight-light"> No relation found</span>
</v-list-item>
</v-list>
</v-menu>
</template>
-->
<table-cell v-else
:class="{'primary--text' : primaryValueColumn === columnObj._cn}"
:selected="selected.col === col && selected.row === row"
:isLocked="isLocked"
@enableedit="makeSelected(col,row);makeEditable(col,row,columnObj.ai)"
@ -358,7 +361,7 @@ export default {
columnObj = this.meta.columns.find(c => c.cn === columnObj.bt.cn);
}
return (columnObj.rqd
return columnObj && (columnObj.rqd
&& (rowObj[columnObj._cn] === undefined || rowObj[columnObj._cn] === null)
&& !columnObj.default);
},

82
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 @@
<v-icon small>mdi-table-eye</v-icon>&nbsp;
<span class="caption text-capitalize font-weight-bold"> Model</span></v-tab>
<v-tab-item
style="height:100%">
style="height:100%">
<v-tabs
color="pink"
height="38"
@ -32,7 +33,7 @@
<v-tab-item
style="height:100%">
style="height:100%">
<columnList
ref="tabs0"
@ -68,7 +69,7 @@
<span class="caption font-weight-bold text-capitalize"> Triggers</span>
</v-tab>
<v-tab-item
style="height:100%">
style="height:100%">
<triggerList
ref="tabs2"
v-if="loadTriggerList"
@ -96,22 +97,22 @@
<!-- </v-tab-item>-->
<!-- </v-tabs>-->
<!-- </v-tabs>-->
<!-- </v-tab-item>
</template>
<template v-if="_isUIAllowed('api')">
<v-tab class="">
<v-icon small>mdi-code-braces</v-icon>&nbsp;
<span class="caption text-capitalize font-weight-bold"> APIs</span></v-tab>
<v-tab-item>-->
<!-- <v-tabs-->
<!-- height="38"-->
<!-- class="table-tabs"-->
<!-- ma-0-->
<!-- pa-0-->
<!-- style="height:100%">-->
<!-- </v-tab-item>
</template>
<template v-if="_isUIAllowed('api')">
<v-tab class="">
<v-icon small>mdi-code-braces</v-icon>&nbsp;
<span class="caption text-capitalize font-weight-bold"> APIs</span></v-tab>
<v-tab-item>-->
<!-- <v-tabs-->
<!-- height="38"-->
<!-- class="table-tabs"-->
<!-- ma-0-->
<!-- pa-0-->
<!-- style="height:100%">-->
<template v-if="!isMvc && !isMetaTable">
@ -310,22 +311,22 @@
</template>
<template v-if="_isUIAllowed('airTable')">
<v-tab v-show="relationTabs && relationTabs.length" class="" >
<v-tab v-show="relationTabs && relationTabs.length" class="">
<v-icon small>mdi-table-edit</v-icon>&nbsp;<span
class="caption text-capitalize font-weight-bold"> {{nodes._tn}}</span></v-tab>
class="caption text-capitalize font-weight-bold"> {{ nodes._tn }}</span></v-tab>
<v-tab-item
style="height:100%">
<rows-xc-data-table
ref="tabs7"
:show-tabs="relationTabs && relationTabs.length"
:table="nodes.tn"
:nodes="nodes"
:newTable="newTableCopy"
:mtdNewTableUpdate="mtdNewTableUpdate"
:deleteTable="deleteTable"
:is-meta-table="isMetaTable"
:addNewRelationTab="addNewRelationTab"
/>
style="height:100%">
<rows-xc-data-table
ref="tabs7"
:show-tabs="relationTabs && relationTabs.length"
:table="nodes.tn"
:nodes="nodes"
:newTable="newTableCopy"
:mtdNewTableUpdate="mtdNewTableUpdate"
:deleteTable="deleteTable"
:is-meta-table="isMetaTable"
:addNewRelationTab="addNewRelationTab"
/>
</v-tab-item>
</template>
<!-- Closable tabs : START -->
@ -344,11 +345,11 @@
<v-tooltip bottom nudge-bottom="">
<template v-slot:activator="{on}">
<div v-on="on">
<!-- <span class="rel-row-parent"> {{ refTable }} - {{ primaryValue }} </span>-->
<!-- <span class="rel-row-parent"> {{ refTable }} - {{ primaryValue }} </span>-->
<v-icon small>mdi-table-arrow-{{ relationType === 'hm' ? 'right' : 'left' }}</v-icon>&nbsp;
<span
class="caption font-weight-bold text-capitalize">
{{ refTableAlias }} ({{ ((primaryValue || '') + '').slice(0,13) }}) ->
{{ refTableAlias }} ({{ ((primaryValue || '') + '').slice(0, 13) }}) ->
{{ tableAlias }}
</span>
<v-icon icon @click="removeRelationTab(i)" x-small class="ml-2">mdi-close</v-icon>
@ -469,8 +470,18 @@ export default {
};
},
methods: {
addNewRelationTab(relation, refTable,refTableAlias, table,tableAlias, relationIdValue, relationType, relationRow, primaryValue) {
this.relationTabs.push({relation, refTable, table, relationIdValue, relationType, relationRow, primaryValue,refTableAlias,tableAlias});
addNewRelationTab(relation, refTable, refTableAlias, table, tableAlias, relationIdValue, relationType, relationRow, primaryValue) {
this.relationTabs.push({
relation,
refTable,
table,
relationIdValue,
relationType,
relationRow,
primaryValue,
refTableAlias,
tableAlias
});
this.active = 'relRow' + (this.relationTabs.length - 1);
},
removeRelationTab(i) {
@ -641,6 +652,7 @@ export default {
.table-tabs, /deep/ .table-tabs > .v-windows {
height: 100%;
}
/deep/ .v-window-item {
height: 100%
}

4
packages/nc-gui/layouts/default.vue

@ -34,6 +34,7 @@
</template>
</v-toolbar-title>
<span class="caption grey--text ml-3" v-show="$nuxt.$loading.show">Loading <v-icon small color="grey">mdi-spin mdi-loading</v-icon></span>
<!-- <gh-btns-star-->
<!-- @click.native="githubClickHandler"-->
@ -624,7 +625,8 @@
</v-list-item-title>
</v-list-item>-->
<v-list-item v-if="swaggerOrGraphiqlUrl" dense @click.stop="openUrl(`${$axios.defaults.baseURL}${swaggerOrGraphiqlUrl}`)">
<v-list-item v-if="swaggerOrGraphiqlUrl" dense
@click.stop="openUrl(`${$axios.defaults.baseURL}${swaggerOrGraphiqlUrl}`)">
<v-list-item-title>
<v-icon small key="terminal-dash">

14
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',

16
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;

6
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;
},

6
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);

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

@ -232,6 +232,16 @@ export default abstract class BaseApiBuilder<T extends Noco> 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,
})
}
}

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

@ -324,8 +324,8 @@ export class GqlApiBuilder extends BaseApiBuilder<Noco> 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<Noco> implements XcMetaMgr {
public async xcTableRename(oldTablename: string, newTablename: string): Promise<any> {
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<Noco> implements XcMetaMgr {
public async onRelationCreate(tnp: string, tnc: string, args): Promise<void> {
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<Noco> 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<any> {
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<any> {
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<Noco> implements XcMetaMgr {
// defining HasMany count method within GQL Type class
Object.defineProperty(this.types[tnp].prototype, `${countPropName}`, {
async value(args, context, info): Promise<any> {
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<Noco> 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<Noco> 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<Noco> 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<Noco> 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();
}
}
/**

5
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<any> {
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;

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

@ -982,7 +982,6 @@ export class RestApiBuilder extends BaseApiBuilder<Noco> {
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<Noco> {
// 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);

184
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;
}

43
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'
}
}

46
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);
}
}*/
}

29
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);
}
}*/
}

80
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);
}
}*/
}

232
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);
}
}*/
}

Loading…
Cancel
Save