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. 12
      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. 23
      packages/nc-gui/components/project/spreadsheet/views/xcGridView.vue
  7. 40
      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. 176
      packages/nocodb/src/lib/noco/gql/GqlApiBuilder.ts
  15. 3
      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. 41
      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() { get gqlQueryListName() {
return `${this.table.replace(/(?:^|_)(.)/g, (_, m) => m.toUpperCase())}List`; return `${this.meta._tn}List`;
} }
get gqlQueryReadName() { get gqlQueryReadName() {
return `${this.table.replace(/(?:^|_)(.)/g, (_, m) => m.toUpperCase())}Read`; return `${this.meta._tn}Read`;
} }
get tableCamelized() { get tableCamelized() {
return `${this.table.replace(/(?:^|_)(.)/g, (_, m) => m.toUpperCase())}`; return `${this.meta._tn}`;
} }
get gqlReqBody() { get gqlReqBody() {

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

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

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

@ -12,7 +12,7 @@
@unlink="unlinkChild" @unlink="unlinkChild"
></item-chip> ></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>
<div class="actions align-center justify-center px-1 flex-shrink-1" <div class="actions align-center justify-center px-1 flex-shrink-1"
:class="{'d-none': !active, 'd-flex':active }"> :class="{'d-none': !active, 'd-flex':active }">
@ -266,7 +266,7 @@ export default {
this.newRecordModal = false; this.newRecordModal = false;
return 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 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; 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() this.$refs.expandedForm && this.$refs.expandedForm.reload()
}, 500) }, 500)
}, },
getCellValue(cellObj) { },
computed: {
getCellValue() {
return cellObj => {
if (cellObj) { if (cellObj) {
if (this.parentMeta && this.childPrimaryCol) { if (this.childPrimaryCol) {
return cellObj[this.childPrimaryCol] return cellObj[this.childPrimaryCol]
} }
return Object.values(cellObj)[1] return Object.values(cellObj)[1]
} }
} }
}, },
computed: {
childMeta() { childMeta() {
return this.$store.state.meta.metas[this.mm.rtn] 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; return c._cn;
}) })
}, },
realFieldList() {
return this.availableRealColumns.map(c => {
return c._cn;
})
},
availableRealColumns() {
return this.availableColumns && this.availableColumns.filter(c => !c.virtual)
},
availableColumns() { availableColumns() {
let columns = []; let columns = [];

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

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

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

@ -165,6 +165,7 @@
:db-alias="nodes.dbAlias" :db-alias="nodes.dbAlias"
/> />
<!--
<div v-else-if="columnObj.cn in hasMany" class="hasmany-col d-flex "> <div v-else-if="columnObj.cn in hasMany" class="hasmany-col d-flex ">
{{ rowObj[columnObj._cn] }} {{ rowObj[columnObj._cn] }}
<v-spacer></v-spacer> <v-spacer></v-spacer>
@ -213,17 +214,17 @@
<template v-else-if="primaryValueColumn === columnObj._cn"> <template v-else-if="primaryValueColumn === columnObj._cn">
<v-menu open-on-hover offset-y bottom> <v-menu open-on-hover offset-y bottom>
<template v-slot:activator="{on}"> <template v-slot:activator="{on}">
<!-- <v-chip v-on="on" &lt;!&ndash; <v-chip v-on="on"
small small
class="caption xc-bt-chip" class="caption xc-bt-chip"
outlined outlined
color="success"> color="success">
{{ rowObj[columnObj.cn] }} {{ rowObj[columnObj.cn] }}
<v-icon v-on="on" class="hasmany-col-menu-icon pv">mdi-menu-down</v-icon> <v-icon v-on="on" class="hasmany-col-menu-icon pv">mdi-menu-down</v-icon>
</v-chip> --> </v-chip> &ndash;&gt;
<span v-on="on" <span v-on="on"
class="caption xc-bt-chip primary--text"> class="caption xc-bt-chip primary&#45;&#45;text">
{{ rowObj[columnObj._cn] }} {{ rowObj[columnObj._cn] }}
<v-icon v-on="on" class="hasmany-col-menu-icon pv">mdi-menu-down</v-icon> <v-icon v-on="on" class="hasmany-col-menu-icon pv">mdi-menu-down</v-icon>
</span> </span>
@ -231,7 +232,7 @@
</template> </template>
<v-list dense> <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 dense v-if="haveHasManyrelation"><span class="grey&#45;&#45;text caption text-center mt-n2">Has Many</span>
</v-list-item> </v-list-item>
<template v-if="haveHasManyrelation"> <template v-if="haveHasManyrelation">
<template v-for="(hm,idCol) in hasMany"> <template v-for="(hm,idCol) in hasMany">
@ -261,24 +262,26 @@
mdi-source-fork mdi-source-fork
</v-icon> </v-icon>
</v-list-item-icon> </v-list-item-icon>
<!-- <v-chip small >--> &lt;!&ndash; <v-chip small >&ndash;&gt;
<!-- <v-list-item-title> --> &lt;!&ndash; <v-list-item-title> &ndash;&gt;
<span class="caption text-capitalize"> {{ rel._tn }}</span> <span class="caption text-capitalize"> {{ rel._tn }}</span>
<!-- </v-list-item-title>--> &lt;!&ndash; </v-list-item-title>&ndash;&gt;
<!-- </v-chip>--> &lt;!&ndash; </v-chip>&ndash;&gt;
</v-list-item> </v-list-item>
</template> </template>
</template> </template>
</template> </template>
<v-list-item v-else> <v-list-item v-else>
<span class="caption text-capitalize grey--text font-weight-light"> No relation found</span> <span class="caption text-capitalize grey&#45;&#45;text font-weight-light"> No relation found</span>
</v-list-item> </v-list-item>
</v-list> </v-list>
</v-menu> </v-menu>
</template> </template>
-->
<table-cell v-else <table-cell v-else
:class="{'primary--text' : primaryValueColumn === columnObj._cn}"
:selected="selected.col === col && selected.row === row" :selected="selected.col === col && selected.row === row"
:isLocked="isLocked" :isLocked="isLocked"
@enableedit="makeSelected(col,row);makeEditable(col,row,columnObj.ai)" @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); 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) && (rowObj[columnObj._cn] === undefined || rowObj[columnObj._cn] === null)
&& !columnObj.default); && !columnObj.default);
}, },

40
packages/nc-gui/components/project/table.vue

@ -9,6 +9,7 @@
v-model="active" v-model="active"
:height="relationTabs && relationTabs.length ?38:0" :height="relationTabs && relationTabs.length ?38:0"
class="table-tabs" class="table-tabs"
:class="{'hidden-tab':!relationTabs || !relationTabs.length}"
@change="onTabChange" @change="onTabChange"
color="pink" color="pink"
> >
@ -96,22 +97,22 @@
<!-- </v-tab-item>--> <!-- </v-tab-item>-->
<!-- </v-tabs>--> <!-- </v-tabs>-->
<!-- </v-tab-item> <!-- </v-tab-item>
</template> </template>
<template v-if="_isUIAllowed('api')"> <template v-if="_isUIAllowed('api')">
<v-tab class=""> <v-tab class="">
<v-icon small>mdi-code-braces</v-icon>&nbsp; <v-icon small>mdi-code-braces</v-icon>&nbsp;
<span class="caption text-capitalize font-weight-bold"> APIs</span></v-tab> <span class="caption text-capitalize font-weight-bold"> APIs</span></v-tab>
<v-tab-item>--> <v-tab-item>-->
<!-- <v-tabs--> <!-- <v-tabs-->
<!-- height="38"--> <!-- height="38"-->
<!-- class="table-tabs"--> <!-- class="table-tabs"-->
<!-- ma-0--> <!-- ma-0-->
<!-- pa-0--> <!-- pa-0-->
<!-- style="height:100%">--> <!-- style="height:100%">-->
<template v-if="!isMvc && !isMetaTable"> <template v-if="!isMvc && !isMetaTable">
@ -310,9 +311,9 @@
</template> </template>
<template v-if="_isUIAllowed('airTable')"> <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 <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 <v-tab-item
style="height:100%"> style="height:100%">
<rows-xc-data-table <rows-xc-data-table
@ -344,11 +345,11 @@
<v-tooltip bottom nudge-bottom=""> <v-tooltip bottom nudge-bottom="">
<template v-slot:activator="{on}"> <template v-slot:activator="{on}">
<div v-on="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; <v-icon small>mdi-table-arrow-{{ relationType === 'hm' ? 'right' : 'left' }}</v-icon>&nbsp;
<span <span
class="caption font-weight-bold text-capitalize"> class="caption font-weight-bold text-capitalize">
{{ refTableAlias }} ({{ ((primaryValue || '') + '').slice(0,13) }}) -> {{ refTableAlias }} ({{ ((primaryValue || '') + '').slice(0, 13) }}) ->
{{ tableAlias }} {{ tableAlias }}
</span> </span>
<v-icon icon @click="removeRelationTab(i)" x-small class="ml-2">mdi-close</v-icon> <v-icon icon @click="removeRelationTab(i)" x-small class="ml-2">mdi-close</v-icon>
@ -469,8 +470,18 @@ export default {
}; };
}, },
methods: { methods: {
addNewRelationTab(relation, refTable,refTableAlias, table,tableAlias, relationIdValue, relationType, relationRow, primaryValue) { addNewRelationTab(relation, refTable, refTableAlias, table, tableAlias, relationIdValue, relationType, relationRow, primaryValue) {
this.relationTabs.push({relation, refTable, table, relationIdValue, relationType, relationRow, primaryValue,refTableAlias,tableAlias}); this.relationTabs.push({
relation,
refTable,
table,
relationIdValue,
relationType,
relationRow,
primaryValue,
refTableAlias,
tableAlias
});
this.active = 'relRow' + (this.relationTabs.length - 1); this.active = 'relRow' + (this.relationTabs.length - 1);
}, },
removeRelationTab(i) { removeRelationTab(i) {
@ -641,6 +652,7 @@ export default {
.table-tabs, /deep/ .table-tabs > .v-windows { .table-tabs, /deep/ .table-tabs > .v-windows {
height: 100%; height: 100%;
} }
/deep/ .v-window-item { /deep/ .v-window-item {
height: 100% height: 100%
} }

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

@ -34,6 +34,7 @@
</template> </template>
</v-toolbar-title> </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--> <!-- <gh-btns-star-->
<!-- @click.native="githubClickHandler"--> <!-- @click.native="githubClickHandler"-->
@ -624,7 +625,8 @@
</v-list-item-title> </v-list-item-title>
</v-list-item>--> </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-list-item-title>
<v-icon small key="terminal-dash"> <v-icon small key="terminal-dash">

14
packages/nc-gui/nuxt.config.js

@ -192,12 +192,14 @@ export default {
return config; return config;
} }
}, },
loading: { loading: false
color: '#13f4ef', // {
height: '2px', // color: '#13f4ef',
continuous: true, // height: '0px',
duration: 3000 // continuous: true,
}, // duration: 3000
// }
,
css: [ css: [
// '@/assets/style/fonts.css', // '@/assets/style/fonts.css',
'@/assets/css/global.css', '@/assets/css/global.css',

16
packages/nc-gui/store/sqlMgr.js

@ -376,7 +376,7 @@ export const actions = {
if (cusHeaders) { if (cusHeaders) {
Object.assign(headers, cusHeaders) Object.assign(headers, cusHeaders)
} }
return (await this.$axios({ const data = (await this.$axios({
url: '?q=sqlOp_' + op, url: '?q=sqlOp_' + op,
baseURL: `${this.$axios.defaults.baseURL}/dashboard`, baseURL: `${this.$axios.defaults.baseURL}/dashboard`,
data: {api: op, ...args, ...params, args: opArgs}, data: {api: op, ...args, ...params, args: opArgs},
@ -386,6 +386,20 @@ export const actions = {
...(cusAxiosOptions || {}), ...(cusAxiosOptions || {}),
})).data; })).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) { } catch (e) {
const err = new Error(e.response.data.msg); const err = new Error(e.response.data.msg);
err.response = e.response; err.response = e.response;

6
packages/nc-gui/store/windows.js

@ -45,10 +45,14 @@ export const state = () => ({
nc: true, nc: true,
miniSponsorCard: 0, miniSponsorCard: 0,
screensaver: true, screensaver: true,
autoApplyFilter: true autoApplyFilter: true,
apiLoading: false
}); });
export const mutations = { export const mutations = {
MutApiLoading(state, status) {
state.apiLoading = status
},
MutAutoApplyFilter(state, v) { MutAutoApplyFilter(state, v) {
state.autoApplyFilter = v; state.autoApplyFilter = v;
}, },

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

@ -1130,7 +1130,7 @@ class BaseModelSql extends BaseModel {
}), !this.isSqlite() }), !this.isSqlite()
)); ));
let gs = _.groupBy(childs, _cn); const gs = _.groupBy(childs, _cn);
parent.forEach(row => { parent.forEach(row => {
row[`${this.dbModels?.[child]?._tn || child}List`] = gs[row[this.pks[0]._cn]] || []; row[`${this.dbModels?.[child]?._tn || child}List`] = gs[row[this.pks[0]._cn]] || [];
}) })
@ -1176,7 +1176,7 @@ class BaseModelSql extends BaseModel {
this this
.dbDriver(child) .dbDriver(child)
.join(vtn, `${vtn}.${vrcn}`, `${rtn}.${rcn}`) .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('')) .xwhere(where, this.dbModels[child].selectQuery(''))
.select({[`${tn}_${vcn}`]: `${vtn}.${vcn}`, ...this.dbModels[child].selectQuery(fields)}) // ...fields.split(',')); .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); this._paginateAndSort(query, {limit, offset}, child);
return this.isSqlite() ? this.dbDriver.select().from(query) : query; return this.isSqlite() ? this.dbDriver.select().from(query) : query;
}), !this.isSqlite() }), !this.isSqlite()
), {sort} as any, child)); ), {sort,limit:1000} as any, child));
// return _.groupBy(childs, cn); // 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, dr: onDelete,
ur: onUpdate, 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,
})
} }
} }

176
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 colNameAlias = self.models[hm.rtn]?.columnToAlias[hm.rcn];
const middlewareBody = middlewaresArr.find(({title}) => title === hm.tn)?.functions?.[0]; const middlewareBody = middlewaresArr.find(({title}) => title === hm.tn)?.functions?.[0];
const countPropName = `${inflection.camelize(hm._tn, false)}Count`; const countPropName = `${hm._tn}Count`;
const listPropName = `${inflection.camelize(hm._tn, false)}List`; const listPropName = `${hm._tn}List`;
if (listPropName in this.types[tn].prototype) { if (listPropName in this.types[tn].prototype) {
continue; continue;
@ -893,6 +893,7 @@ export class GqlApiBuilder extends BaseApiBuilder<Noco> implements XcMetaMgr {
public async xcTableRename(oldTablename: string, newTablename: string): Promise<any> { public async xcTableRename(oldTablename: string, newTablename: string): Promise<any> {
this.log(`xcTableRename : '%s' => '%s'`, oldTablename, newTablename); this.log(`xcTableRename : '%s' => '%s'`, oldTablename, newTablename);
//todo: verify the update queries //todo: verify the update queries
// const metaArr = await (this.sqlClient.knex as XKnex)('nc_models').select(); // 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> { public async onRelationCreate(tnp: string, tnc: string, args): Promise<void> {
await super.onRelationCreate(tnp, tnc, args) await super.onRelationCreate(tnp, tnc, args)
this.log(`onRelationCreate : Within relation create event handler`); this.log(`onRelationCreate : Within relation create event handler`);
const self = this; // const self = this;
const relations = await this.getXcRelationList(); const relations = await this.getXcRelationList();
// set table name alias // set table name alias
@ -1134,35 +1135,62 @@ export class GqlApiBuilder extends BaseApiBuilder<Noco> implements XcMetaMgr {
const hasMany = this.extractHasManyRelationsOfTable(relations, tnp); const hasMany = this.extractHasManyRelationsOfTable(relations, tnp);
const belongsTo = this.extractBelongsToRelationsOfTable(relations, tnp); const belongsTo = this.extractBelongsToRelationsOfTable(relations, tnp);
const ctx = this.generateContextForTable(tnp, columns, relations, hasMany, belongsTo); 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(); 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(); this.schemas[tnp] = GqlXcSchemaFactory.create(this.connectionConfig, this.generateRendererArgs(ctx)).getString();
// update old model meta with new details // update old model meta with new details
this.log(`onRelationCreate : Generating and updating model meta for parent table '%s'`, tnp); 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}); 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) { if (existingModel) {
// todo: persisting old table_alias and columnAlias // todo: persisting old table_alias and columnAlias
// todo: get enable state of other relations
const oldMeta = JSON.parse(existingModel.meta); const oldMeta = JSON.parse(existingModel.meta);
meta.hasMany.forEach(hm => {
hm.enabled = true;
})
Object.assign(oldMeta, { Object.assign(oldMeta, {
hasMany: meta.hasMany, 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', { await this.xcMeta.metaUpdate(this.projectId, this.dbAlias, 'nc_models', {
title: tnp, title: tnp,
meta: JSON.stringify(meta), meta: JSON.stringify(oldMeta),
schema: this.schemas[tnp] schema: this.schemas[tnp]
}, {'title': tnp}) }, {'title': tnp})
} }
const countPropName = `${inflection.camelize(this.getTableNameAlias(tnc), false)}Count`; const countPropName = `${this.getTableNameAlias(tnc)}Count`;
const listPropName = `${inflection.camelize(this.getTableNameAlias(tnc), false)}List`; const listPropName = `${this.getTableNameAlias(tnc)}List`;
this.log(`onRelationCreate : Generating and inserting '%s' and '%s' loaders`, countPropName, listPropName); this.log(`onRelationCreate : Generating and inserting '%s' and '%s' loaders`, countPropName, listPropName);
const hm = hasMany.find(rel => rel.tn === tnc)
{
/* has many relation list loader with middleware */ /* has many relation list loader with middleware */
const mw = new GqlMiddleware(this.acls, tnc, '', this.models); const mw = new GqlMiddleware(this.acls, tnc, '', this.models);
const listLoader = new DataLoader( this.addHmListResolverMethodToType(tnp, hm, mw, {}, listPropName, this.models[hm.rtn]?.columnToAlias[hm.rcn]);
}
/* const listLoader = new DataLoader(
BaseType.applyMiddlewareForLoader( BaseType.applyMiddlewareForLoader(
[mw.middleware], [mw.middleware],
async ids => { async ids => {
@ -1175,20 +1203,22 @@ export class GqlApiBuilder extends BaseApiBuilder<Noco> implements XcMetaMgr {
[mw.postLoaderMiddleware] [mw.postLoaderMiddleware]
)); ));
const currentRelation = hasMany.find(rel => rel.tn === tnc)
/* defining HasMany list method within GQL Type class */
/!* defining HasMany list method within GQL Type class *!/
Object.defineProperty(this.types[tnp].prototype, `${listPropName}`, { Object.defineProperty(this.types[tnp].prototype, `${listPropName}`, {
async value(args, context, info): Promise<any> { async value(args, context, info): Promise<any> {
return listLoader.load([this[currentRelation.rcn], args, context, info]); return listLoader.load([this[hm.rcn], args, context, info]);
}, },
configurable: true configurable: true
}) })*/
// create count loader with middleware // create count loader with middleware
{ {
const mw = new GqlMiddleware(this.acls, tnc, '', this.models); 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( BaseType.applyMiddlewareForLoader(
[mw.middleware], [mw.middleware],
async ids => { async ids => {
@ -1204,10 +1234,10 @@ export class GqlApiBuilder extends BaseApiBuilder<Noco> implements XcMetaMgr {
// defining HasMany count method within GQL Type class // defining HasMany count method within GQL Type class
Object.defineProperty(this.types[tnp].prototype, `${countPropName}`, { Object.defineProperty(this.types[tnp].prototype, `${countPropName}`, {
async value(args, context, info): Promise<any> { 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 configurable: true
}) })*/
} }
await this.xcMeta.metaInsert(this.projectId, this.dbAlias, 'nc_loaders', { 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 belongsTo = this.extractBelongsToRelationsOfTable(relations, tnc);
const hasMany = this.extractHasManyRelationsOfTable(relations, tnc); const hasMany = this.extractHasManyRelationsOfTable(relations, tnc);
const ctx = this.generateContextForTable(tnc, columns, relations, hasMany, belongsTo); 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(); 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.schemas[tnc] = GqlXcSchemaFactory.create(this.connectionConfig, this.generateRendererArgs(ctx)).getString();
this.log(`onRelationCreate : Generating and updating model meta for child table '%s'`, tnc); this.log(`onRelationCreate : Generating and updating model meta for child table '%s'`, tnc);
// update old model meta with new details // update old model meta with new details
const existingModel = await this.xcMeta.metaGet(this.projectId, this.dbAlias, 'nc_models', {'title': tnc}); 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) { if (existingModel) {
// todo: persisting old table_alias and columnAlias // todo: persisting old table_alias and columnAlias
const oldMeta = JSON.parse(existingModel.meta); const oldMeta = JSON.parse(existingModel.meta);
Object.assign(oldMeta, { Object.assign(oldMeta, {
belongsTo: meta.belongsTo, 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', { await this.xcMeta.metaUpdate(this.projectId, this.dbAlias, 'nc_models', {
title: tnc, title: tnc,
meta: JSON.stringify(meta), meta: JSON.stringify(oldMeta),
schema: this.schemas[tnc] schema: this.schemas[tnc]
}, {'title': 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); this.log(`onRelationCreate : Generating and inserting'%s' loader`, propName);
const currentRelation = belongsTo.find(rel => rel.rtn === tnp) const currentRelation = belongsTo.find(rel => rel.rtn === tnp)
// create read loader with middleware // create read loader with middleware
const mw = new GqlMiddleware(this.acls, tnp, '', this.models); 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( BaseType.applyMiddlewareForLoader(
[mw.middleware], [mw.middleware],
async ids => { async ids => {
@ -1282,7 +1338,7 @@ export class GqlApiBuilder extends BaseApiBuilder<Noco> implements XcMetaMgr {
return readLoader.load([this[currentRelation.cn], args, context, info]); return readLoader.load([this[currentRelation.cn], args, context, info]);
}, },
configurable: true configurable: true
}) })*/
await this.xcMeta.metaInsert(this.projectId, this.dbAlias, 'nc_loaders', { await this.xcMeta.metaInsert(this.projectId, this.dbAlias, 'nc_loaders', {
title: `${tnc}Bt${tnp}`, 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(); await this.reInitializeGraphqlEndpoint();
} }
@ -1761,6 +1815,82 @@ export class GqlApiBuilder extends BaseApiBuilder<Noco> implements XcMetaMgr {
private log(str, ...args): void { private log(str, ...args): void {
log(`${this.dbAlias} : ${str}`, ...args); 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();
}
} }
/** /**

3
packages/nocodb/src/lib/noco/gql/GqlResolver.ts

@ -4,7 +4,6 @@ import GqlMiddleware from "./GqlMiddleware";
import {Acls} from "../../../interface/config"; import {Acls} from "../../../interface/config";
import GqlBaseResolver from "./GqlBaseResolver"; import GqlBaseResolver from "./GqlBaseResolver";
import Noco from "../Noco"; import Noco from "../Noco";
import inflection from 'inflection';
function parseHrtimeToSeconds(hrtime) { function parseHrtimeToSeconds(hrtime) {
const seconds = (hrtime[0] + (hrtime[1] / 1e6)).toFixed(3); const seconds = (hrtime[0] + (hrtime[1] / 1e6)).toFixed(3);
@ -150,7 +149,7 @@ export default class GqlResolver extends GqlBaseResolver {
public mapResolvers(customResolver: any): any { public mapResolvers(customResolver: any): any {
const mw = new GqlMiddleware(this.acls, this.table, this.middlewareStringBody, this.models); const mw = new GqlMiddleware(this.acls, this.table, this.middlewareStringBody, this.models);
// todo: replace with inflection // todo: replace with inflection
const name = inflection.camelize(this.model._tn); const name = this.model._tn;
return GqlResolver.applyMiddlewares([(_, {req}) => { return GqlResolver.applyMiddlewares([(_, {req}) => {
req.models = this.models; req.models = this.models;
req.model = this.model; 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]) this.deleteRoutesForTables([tnp, tnc])
const relations = await this.getXcRelationList(); const relations = await this.getXcRelationList();
{ {
const swaggerArr = []; const swaggerArr = [];
const columns = await this.getColumnList(tnp); const columns = await this.getColumnList(tnp);
@ -1001,13 +1000,13 @@ export class RestApiBuilder extends BaseApiBuilder<Noco> {
// update old model meta with new details // update old model meta with new details
const existingModel = await this.xcMeta.metaGet(this.projectId, this.dbAlias, 'nc_models', {'title': tnp}); const existingModel = await this.xcMeta.metaGet(this.projectId, this.dbAlias, 'nc_models', {'title': tnp});
let queryParams; let queryParams;
try { try {
queryParams = JSON.parse(existingModel.query_params); queryParams = JSON.parse(existingModel.query_params);
} catch (e) { /* */ } catch (e) { /* */
} }
swaggerArr.push(JSON.parse(existingModel.schema)); swaggerArr.push(JSON.parse(existingModel.schema));
if (existingModel) { if (existingModel) {
this.log(`onRelationCreate : Updating model metadata for parent table '%s'`, tnp); 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 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.columns
* @param ctx.relations * @param ctx.relations
*/ */
constructor({dir, filename, ctx}) { protected constructor({dir, filename, ctx}) {
super({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 { protected generateManyToManyTypeProps(args: any): string {
if (!args.manyToMany?.length) { if (!args.manyToMany?.length) {
return ''; return '';
@ -26,6 +74,138 @@ class BaseGqlXcTsSchema extends BaseRender {
return str; 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;
} }

41
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 inflection from "inflection";
import lodash from "lodash"; import lodash from "lodash";
import {AGG_DEFAULT_COLS, GROUPBY_DEFAULT_COLS} from "./schemaHelp"; 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}); 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;
}
/** /**
* *
@ -131,6 +111,8 @@ class GqlXcTsSchemaMssql extends BaseRender {
str += `\t\t${childTable}Count: Int\r\n`; str += `\t\t${childTable}Count: Int\r\n`;
} }
str += this.generateManyToManyTypeProps(args);
let belongsToRelations = args.relations.filter(r => r.tn === args.tn); let belongsToRelations = args.relations.filter(r => r.tn === args.tn);
if (belongsToRelations.length > 1) if (belongsToRelations.length > 1)
belongsToRelations = lodash.uniqBy(belongsToRelations, function (e) { belongsToRelations = lodash.uniqBy(belongsToRelations, function (e) {
@ -188,7 +170,7 @@ class GqlXcTsSchemaMssql extends BaseRender {
} }
_getGraphqlType(columnObj):any { _getGraphqlType(columnObj): any {
switch (columnObj.dt) { switch (columnObj.dt) {
@ -217,14 +199,11 @@ class GqlXcTsSchemaMssql extends BaseRender {
case 'datetime': case 'datetime':
case 'datetime2': case 'datetime2':
case 'datetimeoffset': case 'datetimeoffset':
case 'geography':
case 'geometry':
case 'heirarchyid': case 'heirarchyid':
case 'image': case 'image':
case 'nchar': case 'nchar':
case 'ntext': case 'ntext':
case 'nvarchar': case 'nvarchar':
case 'json':
case 'smalldatetime': case 'smalldatetime':
case 'smallmoney': case 'smallmoney':
case 'sql_variant': case 'sql_variant':
@ -240,11 +219,17 @@ class GqlXcTsSchemaMssql extends BaseRender {
return "String"; return "String";
break; break;
case 'geography':
case 'geometry':
case 'json':
return 'JSON';
} }
} }
_getGraphqlConditionType(columnObj):any { _getGraphqlConditionType(columnObj): any {
switch (this._getGraphqlType(columnObj.dt)) { switch (this._getGraphqlType(columnObj.dt)) {
@ -258,6 +243,8 @@ class GqlXcTsSchemaMssql extends BaseRender {
return 'ConditionString' return 'ConditionString'
case "[String]": case "[String]":
return 'ConditionString' 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"; import BaseGqlXcTsSchema from "./BaseGqlXcTsSchema";
@ -19,35 +16,14 @@ class GqlXcTsSchemaMysql extends BaseGqlXcTsSchema {
super({dir, filename, ctx}); 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
* @param args.columns * @param args.columns
* @param args.relations * @param args.relations
* @returns {string} * @returns {string}
* @private * @private
*/ *!/
_renderColumns(args) { _renderColumns(args) {
let str = ''; let str = '';
@ -204,9 +180,9 @@ class GqlXcTsSchemaMysql extends BaseGqlXcTsSchema {
return `${str}\r\n\r\n${strWhere}`; return `${str}\r\n\r\n${strWhere}`;
} }
*/
protected _getGraphqlType(columnObj): string {
_getGraphqlType(columnObj) {
switch (columnObj.dt) { switch (columnObj.dt) {
@ -251,14 +227,15 @@ class GqlXcTsSchemaMysql extends BaseGqlXcTsSchema {
case "tinyblob": case "tinyblob":
case "mediumblob": case "mediumblob":
case "longblob": case "longblob":
case "enum":
case "time":
return "String" return "String"
break; break;
case "set": case "set":
return "[String]"; return "[String]";
break; break;
case "enum":
case "time":
case "geometry": case "geometry":
case "point": case "point":
case "linestring": case "linestring":
@ -268,14 +245,14 @@ class GqlXcTsSchemaMysql extends BaseGqlXcTsSchema {
case "multipolygon": case "multipolygon":
case "json": case "json":
default: default:
return "String" return "JSON"
break; break;
} }
} }
_getGraphqlConditionType(columnObj) { protected _getGraphqlConditionType(columnObj): any {
switch (this._getGraphqlType(columnObj.dt)) { switch (this._getGraphqlType(columnObj.dt)) {
@ -286,6 +263,7 @@ class GqlXcTsSchemaMysql extends BaseGqlXcTsSchema {
case "Boolean": case "Boolean":
return 'ConditionBoolean' return 'ConditionBoolean'
case "String": case "String":
case "JSON":
return 'ConditionString' return 'ConditionString'
case "[String]": case "[String]":
return 'ConditionString' return 'ConditionString'
@ -294,9 +272,9 @@ class GqlXcTsSchemaMysql extends BaseGqlXcTsSchema {
} }
getString() { /*getString() {
return this._renderColumns(this.ctx); 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 BaseGqlXcTsSchema from "./BaseGqlXcTsSchema";
import inflection from "inflection";
import lodash from "lodash";
import {AGG_DEFAULT_COLS, GROUPBY_DEFAULT_COLS} from "./schemaHelp";
class GqlXcSchemaOracle extends BaseRender { class GqlXcSchemaOracle extends BaseGqlXcTsSchema {
/** /**
* *
@ -19,14 +16,14 @@ class GqlXcSchemaOracle extends BaseRender {
super({dir, filename, ctx}); super({dir, filename, ctx});
} }
/** /*/!**
* Prepare variables used in code template * Prepare variables used in code template
*/ *!/
prepare() { prepare() {
const data:any = {}; const data:any = {};
/* example of simple variable */ /!* example of simple variable *!/
data.tn = this.ctx.tn_camelize; data.tn = this.ctx.tn_camelize;
data.columns = { data.columns = {
@ -39,14 +36,14 @@ class GqlXcSchemaOracle extends BaseRender {
} }
/** /!**
* *
* @param args * @param args
* @param args.columns * @param args.columns
* @param args.relations * @param args.relations
* @returns {string} * @returns {string}
* @private * @private
*/ *!/
_renderColumns(args) { _renderColumns(args) {
let str = ''; let str = '';
@ -130,6 +127,8 @@ class GqlXcSchemaOracle extends BaseRender {
str += `\t\t${childTable}Count: Int\r\n`; str += `\t\t${childTable}Count: Int\r\n`;
} }
str+= this.generateManyToManyTypeProps(args);
let belongsToRelations = args.relations.filter(r => r.tn === args.tn); let belongsToRelations = args.relations.filter(r => r.tn === args.tn);
if (belongsToRelations.length > 1) if (belongsToRelations.length > 1)
belongsToRelations = lodash.uniqBy(belongsToRelations, function (e) { belongsToRelations = lodash.uniqBy(belongsToRelations, function (e) {
@ -184,9 +183,9 @@ class GqlXcSchemaOracle extends BaseRender {
return `${str}\r\n\r\n${strWhere}`; return `${str}\r\n\r\n${strWhere}`;
} }
*/
protected _getGraphqlType(columnObj):any {
_getGraphqlType(columnObj):any {
switch (columnObj.dt) { switch (columnObj.dt) {
case "char": case "char":
case "nchar": case "nchar":
@ -235,7 +234,7 @@ class GqlXcSchemaOracle extends BaseRender {
} }
_getGraphqlConditionType(columnObj):any { protected _getGraphqlConditionType(columnObj):any {
switch (this._getGraphqlType(columnObj.dt)) { switch (this._getGraphqlType(columnObj.dt)) {
@ -252,9 +251,9 @@ class GqlXcSchemaOracle extends BaseRender {
} }
} }
getString(){ /*getString(){
return this._renderColumns(this.ctx); 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 BaseGqlXcTsSchema from "./BaseGqlXcTsSchema";
import inflection from "inflection";
import lodash from "lodash";
import {AGG_DEFAULT_COLS, GROUPBY_DEFAULT_COLS} from "./schemaHelp";
class GqlXcSchemaPg extends BaseRender { class GqlXcSchemaPg extends BaseGqlXcTsSchema {
/** /**
* *
* @param dir * @param dir
@ -19,33 +15,16 @@ class GqlXcSchemaPg extends BaseRender {
super({dir, filename, ctx}); 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
* @param args.columns * @param args.columns
* @param args.relations * @param args.relations
* @returns {string} * @returns {string}
* @private * @private
*/ *!/
_renderColumns(args) { _renderColumns(args) {
let str = ''; let str = '';
@ -61,9 +40,9 @@ class GqlXcSchemaPg extends BaseRender {
return str; return str;
} }*/
_getInputType(args) { /* _getInputType(args) {
let str = `input ${args.tn_camelize}Input { \r\n` let str = `input ${args.tn_camelize}Input { \r\n`
for (let i = 0; i < args.columns.length; ++i) { for (let i = 0; i < args.columns.length; ++i) {
if (args.columns[i]._cn.split(' ').length > 1) { if (args.columns[i]._cn.split(' ').length > 1) {
@ -75,9 +54,9 @@ class GqlXcSchemaPg extends BaseRender {
} }
str += `\t}`; str += `\t}`;
return str; return str;
} }*/
_getQuery(args) { /* _getQuery(args) {
let str = `type Query { \r\n` 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}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` 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\t${args.tn_camelize}Distribution(min: Int, max: Int, step: Int, steps: String, column_name: String!): [distribution]\r\n`
str += `\t}\r\n` str += `\t}\r\n`
return str; return str;
} }*/
_getMutation(args) { /* _getMutation(args) {
let str = `type Mutation { \r\n` 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}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`
@ -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\t${args.tn_camelize}DeleteBulk(data: [${args.tn_camelize}Input]): [Int]\r\n`
str += `\t},\r\n` str += `\t},\r\n`
return str; return str;
} }*/
_getType(args) { /* _getType(args) {
let str = `type ${args.tn_camelize} { \r\n` let str = `type ${args.tn_camelize} { \r\n`
let strWhere = `input Condition${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 += `\t\t${childTable}Count: Int\r\n`;
} }
str+= this.generateManyToManyTypeProps(args);
let belongsToRelations = args.relations.filter(r => r.tn === args.tn); let belongsToRelations = args.relations.filter(r => r.tn === args.tn);
if (belongsToRelations.length > 1) if (belongsToRelations.length > 1)
belongsToRelations = lodash.uniqBy(belongsToRelations, function (e) { belongsToRelations = lodash.uniqBy(belongsToRelations, function (e) {
@ -185,7 +167,7 @@ class GqlXcSchemaPg extends BaseRender {
return `${str}\r\n\r\n${strWhere}`; return `${str}\r\n\r\n${strWhere}`;
} }*/
_getGraphqlType(columnObj) { _getGraphqlType(columnObj) {
@ -231,6 +213,19 @@ class GqlXcSchemaPg extends BaseRender {
return "Float"; return "Float";
break; 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 "character":
case "uuid": case "uuid":
case "date": case "date":
@ -248,12 +243,8 @@ class GqlXcSchemaPg extends BaseRender {
case "timetz": case "timetz":
case "time with time zone": case "time with time zone":
case "daterange": case "daterange":
case "json":
case "jsonb":
case "gtsvector": case "gtsvector":
case "index_am_handler": case "index_am_handler":
case "anyenum":
case "anynonarray":
case "anyrange": case "anyrange":
case "box": case "box":
case "bpchar": case "bpchar":
@ -274,12 +265,9 @@ class GqlXcSchemaPg extends BaseRender {
case "numrange": case "numrange":
case "oid": case "oid":
case "opaque": case "opaque":
case "path":
case "pg_ddl_command": case "pg_ddl_command":
case "pg_lsn": case "pg_lsn":
case "pg_node_tree": case "pg_node_tree":
case "point":
case "polygon":
case "record": case "record":
case "refcursor": case "refcursor":
case "regclass": case "regclass":
@ -319,7 +307,7 @@ class GqlXcSchemaPg extends BaseRender {
} }
_getGraphqlConditionType(columnObj):any { protected _getGraphqlConditionType(columnObj):any {
switch (this._getGraphqlType(columnObj.dt)) { switch (this._getGraphqlType(columnObj.dt)) {
@ -333,13 +321,17 @@ class GqlXcSchemaPg extends BaseRender {
return 'ConditionString' return 'ConditionString'
case "[String]": case "[String]":
return 'ConditionString' return 'ConditionString'
case "[JSON]":
return 'ConditionString'
case "JSON":
return 'ConditionString'
} }
} }
getString(){ /* getString(){
return this._renderColumns(this.ctx); 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 BaseGqlXcTsSchema from "./BaseGqlXcTsSchema";
import inflection from "inflection";
import lodash from "lodash";
import {AGG_DEFAULT_COLS, GROUPBY_DEFAULT_COLS} from "./schemaHelp";
class GqlXcSchemaSqlite extends BaseRender { class GqlXcSchemaSqlite extends BaseGqlXcTsSchema {
/** /**
* *
@ -19,35 +16,16 @@ class GqlXcSchemaSqlite extends BaseRender {
super({dir, filename, ctx}); 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
* @param args.columns * @param args.columns
* @param args.relations * @param args.relations
* @returns {string} * @returns {string}
* @private * @private
*/ *!/
_renderColumns(args) { public _renderColumns(args): string {
let str = ''; let str = '';
@ -62,23 +40,95 @@ class GqlXcSchemaSqlite extends BaseRender {
return str; 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` let str = `input ${args.tn_camelize}Input { \r\n`
for (let i = 0; i < args.columns.length; ++i) { for (const column of args.columns) {
if (args.columns[i]._cn.split(' ').length > 1) { if (column._cn.split(' ').length > 1) {
// str += `\t\t${args.columns[i]._cn}: ${this._getGraphqlType(args.columns[i])},\r\n`; // str += `\t\t${column._cn}: ${this._getGraphqlType(column)},\r\n`;
} else { } 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}`; str += `\t}`;
return str; return str;
} }
_getQuery(args) { protected _getQuery(args): string {
let str = `type Query { \r\n` 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}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` str += `\t\t${args.tn_camelize}Read(id:String!): ${args.tn_camelize}\r\n`
@ -93,11 +143,11 @@ class GqlXcSchemaSqlite extends BaseRender {
return str; return str;
} }
_getMutation(args) { protected _getMutation(args): string {
let str = `type Mutation { \r\n` 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}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}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}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}UpdateBulk(data: [${args.tn_camelize}Input]): [Int]\r\n`
str += `\t\t${args.tn_camelize}DeleteBulk(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; return str;
} }
_getType(args) { protected _getType(args): string {
let str = `type ${args.tn_camelize} { \r\n` let str = `type ${args.tn_camelize} { \r\n`
let strWhere = `input Condition${args.tn_camelize} { \r\n` let strWhere = `input Condition${args.tn_camelize} { \r\n`
for (let i = 0; i < args.columns.length; ++i) { for (const column of args.columns.length) {
str += `\t\t${args.columns[i]._cn}: ${this._getGraphqlType(args.columns[i])},\r\n`; str += `\t\t${column._cn}: ${this._getGraphqlType(column)},\r\n`;
strWhere += `\t\t${args.columns[i]._cn.replace(/ /g, '_')}: ${this._getGraphqlConditionType(args.columns[i])},\r\n`; strWhere += `\t\t${column._cn.replace(/ /g, '_')}: ${this._getGraphqlConditionType(column)},\r\n`;
} }
let hasManyRelations = args.relations.filter(r => r.rtn === args.tn); let hasManyRelations = args.relations.filter(r => r.rtn === args.tn);
if (hasManyRelations.length > 1) if (hasManyRelations.length > 1) {
hasManyRelations = lodash.uniqBy(hasManyRelations, function (e) { hasManyRelations = lodash.uniqBy(hasManyRelations, (e) => {
return [e.tn, e.rtn].join(); return [e.tn, e.rtn].join();
}); });
}
str += hasManyRelations.length ? `\r\n` : ``; str += hasManyRelations.length ? `\r\n` : ``;
// cityList in Country // cityList in Country
for (let i = 0; i < hasManyRelations.length; ++i) { for (const {_tn} of hasManyRelations.length) {
let childTable = inflection.camelize(hasManyRelations[i]._tn) const childTable = inflection.camelize(_tn)
str += `\t\t${childTable}List: [${childTable}]\r\n`; str += `\t\t${childTable}List: [${childTable}]\r\n`;
strWhere += `\t\t${childTable}List: Condition${childTable}\r\n`; strWhere += `\t\t${childTable}List: Condition${childTable}\r\n`;
str += `\t\t${childTable}Count: Int\r\n`; str += `\t\t${childTable}Count: Int\r\n`;
} }
str += this.generateManyToManyTypeProps(args);
let belongsToRelations = args.relations.filter(r => r.tn === args.tn); let belongsToRelations = args.relations.filter(r => r.tn === args.tn);
if (belongsToRelations.length > 1) if (belongsToRelations.length > 1) {
belongsToRelations = lodash.uniqBy(belongsToRelations, function (e) { belongsToRelations = lodash.uniqBy(belongsToRelations, (e) => {
return [e.tn, e.rtn].join(); return [e.tn, e.rtn].join();
}); });
}
str += belongsToRelations.length ? `\r\n` : ``; str += belongsToRelations.length ? `\r\n` : ``;
// Country withi city - this is reverse // Country withi city - this is reverse
for (let i = 0; i < belongsToRelations.length; ++i) { for (const {_rtn} of belongsToRelations.length) {
let parentTable = inflection.camelize(belongsToRelations[i]._rtn) const parentTable = inflection.camelize(_rtn)
str += `\t\t${parentTable}Read(id:String): ${parentTable}\r\n`; str += `\t\t${parentTable}Read(id:String): ${parentTable}\r\n`;
strWhere += `\t\t${parentTable}Read: Condition${parentTable}\r\n`; strWhere += `\t\t${parentTable}Read: Condition${parentTable}\r\n`;
} }
str += `\t}\r\n` str += `\t}\r\n`
const grpFields = {...GROUPBY_DEFAULT_COLS};
const grpFields = Object.assign({}, GROUPBY_DEFAULT_COLS);
str += `type ${args.tn_camelize}GroupBy { \r\n` str += `type ${args.tn_camelize}GroupBy { \r\n`
for (let i = 0; i < args.columns.length; ++i) { for (let i = 0; i < args.columns.length; ++i) {
@ -160,7 +212,7 @@ class GqlXcSchemaSqlite extends BaseRender {
str += Object.values(grpFields).join(''); str += Object.values(grpFields).join('');
str += `\t}\r\n`; str += `\t}\r\n`;
const aggFields = Object.assign({}, AGG_DEFAULT_COLS); const aggFields = {...AGG_DEFAULT_COLS};
str += `type ${args.tn_camelize}Aggregate { \r\n` str += `type ${args.tn_camelize}Aggregate { \r\n`
for (let i = 0; i < args.columns.length; ++i) { for (let i = 0; i < args.columns.length; ++i) {
@ -183,79 +235,7 @@ class GqlXcSchemaSqlite extends BaseRender {
return `${str}\r\n\r\n${strWhere}`; 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