mirror of https://github.com/nocodb/nocodb
20 changed files with 661 additions and 80 deletions
@ -0,0 +1,38 @@ |
|||||||
|
<template> |
||||||
|
<div> |
||||||
|
<v-icon small @click="metas = true"> |
||||||
|
mdi-bug-outline |
||||||
|
</v-icon> |
||||||
|
|
||||||
|
<v-dialog v-model="metas" max-width="800px"> |
||||||
|
<v-card> |
||||||
|
<v-tabs height="30"> |
||||||
|
<v-tab v-for="(meta,table) in metasObj" :key="table"> |
||||||
|
<span class="caption text-capitalize">{{ table }}</span> |
||||||
|
</v-tab> |
||||||
|
<v-tab-item v-for="(meta,table) in metasObj" :key="table"> |
||||||
|
<monaco-json-object-editor :value="meta" style="height:80vh" /> |
||||||
|
</v-tab-item> |
||||||
|
</v-tabs> |
||||||
|
</v-card> |
||||||
|
</v-dialog> |
||||||
|
</div> |
||||||
|
</template> |
||||||
|
|
||||||
|
<script> |
||||||
|
import MonacoJsonObjectEditor from '@/components/monaco/MonacoJsonObjectEditor' |
||||||
|
export default { |
||||||
|
name: 'DebugMetas', |
||||||
|
components: { MonacoJsonObjectEditor }, |
||||||
|
data: () => ({ metas: false, tab: 0 }), |
||||||
|
computed: { |
||||||
|
metasObj() { |
||||||
|
return this.$store.state.meta.metas || {} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
</script> |
||||||
|
|
||||||
|
<style scoped> |
||||||
|
|
||||||
|
</style> |
@ -0,0 +1,34 @@ |
|||||||
|
import RestApi from './restApi' |
||||||
|
import GqlApi from './gqlApi' |
||||||
|
|
||||||
|
export default class ApiFactory { |
||||||
|
static create(table, type, ctx) { |
||||||
|
if (type === 'graphql') { |
||||||
|
return new GqlApi(table, ctx) |
||||||
|
} else if (type === 'rest') { |
||||||
|
return new RestApi(table, ctx) |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
/** |
||||||
|
* @copyright Copyright (c) 2021, Xgene Cloud Ltd |
||||||
|
* |
||||||
|
* @author Naveen MR <oof1lab@gmail.com> |
||||||
|
* @author Pranav C Balan <pranavxc@gmail.com> |
||||||
|
* |
||||||
|
* @license GNU AGPL version 3 or any later version |
||||||
|
* |
||||||
|
* This program is free software: you can redistribute it and/or modify |
||||||
|
* it under the terms of the GNU Affero General Public License as |
||||||
|
* published by the Free Software Foundation, either version 3 of the |
||||||
|
* License, or (at your option) any later version. |
||||||
|
* |
||||||
|
* This program is distributed in the hope that it will be useful, |
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||||
|
* GNU Affero General Public License for more details. |
||||||
|
* |
||||||
|
* You should have received a copy of the GNU Affero General Public License |
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
* |
||||||
|
*/ |
@ -0,0 +1,263 @@ |
|||||||
|
export default class GqlApi { |
||||||
|
constructor(table, $ctx) { |
||||||
|
this.$ctx = $ctx |
||||||
|
this.table = $ctx.$store.state.meta.metas[table]._tn |
||||||
|
} |
||||||
|
|
||||||
|
get meta() { |
||||||
|
return this.$ctx.$store.state.meta.metas[this.$ctx.table] || {} |
||||||
|
} |
||||||
|
|
||||||
|
get columns() { |
||||||
|
return this.meta.columns || [] |
||||||
|
} |
||||||
|
|
||||||
|
// todo: - get version letter and use table alias
|
||||||
|
async list(params) { |
||||||
|
const data = await this.post(`/nc/${this.$ctx.projectId}/v1/graphql`, { |
||||||
|
query: await this.gqlQuery(params), |
||||||
|
variables: null |
||||||
|
}) |
||||||
|
return data.data.data[this.gqlQueryListName] |
||||||
|
} |
||||||
|
|
||||||
|
async count(params) { |
||||||
|
const data = await this.post(`/nc/${this.$ctx.projectId}/v1/graphql`, { |
||||||
|
query: this.gqlCountQuery(params), |
||||||
|
variables: null |
||||||
|
}) |
||||||
|
return data.data.data[this.gqlQueryCountName] |
||||||
|
} |
||||||
|
|
||||||
|
post(url, params) { |
||||||
|
return this.$axios({ |
||||||
|
url: `${this.$axios.defaults.baseURL}${url}`, |
||||||
|
method: 'post', |
||||||
|
data: params |
||||||
|
}) |
||||||
|
} |
||||||
|
|
||||||
|
generateQueryParams(params) { |
||||||
|
if (!params) { return '(where:"")' } |
||||||
|
const res = [] |
||||||
|
if ('limit' in params) { |
||||||
|
res.push(`limit: ${params.limit}`) |
||||||
|
} |
||||||
|
if ('offset' in params) { |
||||||
|
res.push(`offset: ${params.offset}`) |
||||||
|
} |
||||||
|
if ('where' in params) { |
||||||
|
res.push(`where: ${JSON.stringify(params.where)}`) |
||||||
|
} |
||||||
|
if ('sort' in params) { |
||||||
|
res.push(`sort: ${JSON.stringify(params.sort)}`) |
||||||
|
} |
||||||
|
if (params.condition) { |
||||||
|
res.push(`condition: ${JSON.stringify(params.condition)}`) |
||||||
|
} |
||||||
|
if (params.conditionGraph) { |
||||||
|
res.push(`conditionGraph: ${JSON.stringify(JSON.stringify(params.conditionGraph))}`) |
||||||
|
} |
||||||
|
return `(${res.join(',')})` |
||||||
|
} |
||||||
|
|
||||||
|
async gqlQuery(params) { |
||||||
|
return `{${this.gqlQueryListName}${this.generateQueryParams(params)}{${this.gqlReqBody}${await this.gqlRelationReqBody(params)}}}` |
||||||
|
} |
||||||
|
|
||||||
|
gqlReadQuery(id) { |
||||||
|
return `{${this.gqlQueryReadName}(id:"${id}"){${this.gqlReqBody}}}` |
||||||
|
} |
||||||
|
|
||||||
|
gqlCountQuery(params) { |
||||||
|
return `{${this.gqlQueryCountName}${this.generateQueryParams(params)}}` |
||||||
|
} |
||||||
|
|
||||||
|
get gqlQueryListName() { |
||||||
|
return `${this.meta._tn}List` |
||||||
|
} |
||||||
|
|
||||||
|
get gqlQueryReadName() { |
||||||
|
return `${this.meta._tn}Read` |
||||||
|
} |
||||||
|
|
||||||
|
get tableCamelized() { |
||||||
|
return `${this.meta._tn}` |
||||||
|
} |
||||||
|
|
||||||
|
get gqlReqBody() { |
||||||
|
return `\n${this.columns.map(c => c._cn).join('\n')}\n` |
||||||
|
} |
||||||
|
|
||||||
|
async gqlRelationReqBody(params) { |
||||||
|
let str = '' |
||||||
|
if (params.hm) { |
||||||
|
for (const child of params.hm.split(',')) { |
||||||
|
await this.$ctx.$store.dispatch('meta/ActLoadMeta', { |
||||||
|
dbAlias: this.$ctx.dbAlias, |
||||||
|
env: this.$ctx.env, |
||||||
|
tn: child |
||||||
|
}) |
||||||
|
const meta = this.$ctx.$store.state.meta.metas[child] |
||||||
|
if (meta) { |
||||||
|
str += `\n${meta._tn}List{\n${meta.columns.map(c => c._cn).join('\n')}\n}` |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
if (params.bt) { |
||||||
|
for (const parent of params.bt.split(',')) { |
||||||
|
await this.$ctx.$store.dispatch('meta/ActLoadMeta', { |
||||||
|
dbAlias: this.$ctx.dbAlias, |
||||||
|
env: this.$ctx.env, |
||||||
|
tn: parent |
||||||
|
}) |
||||||
|
const meta = this.$ctx.$store.state.meta.metas[parent] |
||||||
|
if (meta) { |
||||||
|
str += `\n${meta._tn}Read{\n${meta.columns.map(c => c._cn).join('\n')}\n}` |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
if (params.mm) { |
||||||
|
for (const mm of params.mm.split(',')) { |
||||||
|
await this.$ctx.$store.dispatch('meta/ActLoadMeta', { |
||||||
|
dbAlias: this.$ctx.dbAlias, |
||||||
|
env: this.$ctx.env, |
||||||
|
tn: mm |
||||||
|
}) |
||||||
|
const meta = this.$ctx.$store.state.meta.metas[mm] |
||||||
|
if (meta) { |
||||||
|
str += `\n${meta._tn}MMList{\n${meta.columns.map(c => c._cn).join('\n')}\n}` |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
return str |
||||||
|
} |
||||||
|
|
||||||
|
get gqlQueryCountName() { |
||||||
|
return `${this.tableCamelized}Count` |
||||||
|
} |
||||||
|
|
||||||
|
get gqlMutationCreateName() { |
||||||
|
return `${this.tableCamelized}Create` |
||||||
|
} |
||||||
|
|
||||||
|
get gqlMutationUpdateName() { |
||||||
|
return `${this.tableCamelized}Update` |
||||||
|
} |
||||||
|
|
||||||
|
get gqlMutationDeleteName() { |
||||||
|
return `${this.tableCamelized}Delete` |
||||||
|
} |
||||||
|
|
||||||
|
async paginatedList(params) { |
||||||
|
// const list = await this.list(params);
|
||||||
|
// const count = (await this.count({where: params.where || ''}));
|
||||||
|
const [list, count] = await Promise.all([ |
||||||
|
this.list(params), this.count({ |
||||||
|
where: params.where || '', |
||||||
|
conditionGraph: params.conditionGraph, |
||||||
|
condition: params.condition |
||||||
|
}) |
||||||
|
]) |
||||||
|
return { list, count } |
||||||
|
} |
||||||
|
|
||||||
|
async update(id, data, oldData) { |
||||||
|
const data1 = await this.post(`/nc/${this.$ctx.projectId}/v1/graphql`, { |
||||||
|
query: `mutation update($id:String!, $data:${this.tableCamelized}Input){
|
||||||
|
${this.gqlMutationUpdateName}(id: $id, data: $data) |
||||||
|
}`,
|
||||||
|
variables: { |
||||||
|
id, data |
||||||
|
} |
||||||
|
}) |
||||||
|
|
||||||
|
const colName = Object.keys(data)[0] |
||||||
|
this.$ctx.$store.dispatch('sqlMgr/ActSqlOp', [{ dbAlias: this.$ctx.dbAlias }, 'xcAuditCreate', { |
||||||
|
tn: this.table, |
||||||
|
cn: colName, |
||||||
|
pk: id, |
||||||
|
value: data[colName], |
||||||
|
prevValue: oldData[colName] |
||||||
|
}]) |
||||||
|
|
||||||
|
return data1.data.data[this.gqlMutationUpdateName] |
||||||
|
} |
||||||
|
|
||||||
|
async insert(data) { |
||||||
|
const data1 = await this.post(`/nc/${this.$ctx.projectId}/v1/graphql`, { |
||||||
|
query: `mutation create($data:${this.tableCamelized}Input){
|
||||||
|
${this.gqlMutationCreateName}(data: $data){${this.gqlReqBody}} |
||||||
|
}`,
|
||||||
|
variables: { |
||||||
|
data |
||||||
|
} |
||||||
|
}) |
||||||
|
return data1.data.data[this.gqlMutationCreateName] |
||||||
|
} |
||||||
|
|
||||||
|
async delete(id) { |
||||||
|
const data1 = await this.post(`/nc/${this.$ctx.projectId}/v1/graphql`, { |
||||||
|
query: `mutation delete($id:String!){
|
||||||
|
${this.gqlMutationDeleteName}(id: $id) |
||||||
|
}`,
|
||||||
|
variables: { id } |
||||||
|
}) |
||||||
|
|
||||||
|
return data1.data.data[this.gqlMutationDeleteName] |
||||||
|
} |
||||||
|
|
||||||
|
async read(id) { |
||||||
|
const data = await this.post(`/nc/${this.$ctx.projectId}/v1/graphql`, { |
||||||
|
query: this.gqlReadQuery(id), |
||||||
|
variables: null |
||||||
|
}) |
||||||
|
return data.data.data[this.gqlQueryReadName] |
||||||
|
} |
||||||
|
|
||||||
|
get $axios() { |
||||||
|
return this.$ctx.$axios |
||||||
|
} |
||||||
|
|
||||||
|
async paginatedM2mNotChildrenList(params, assoc, pid) { |
||||||
|
const list = await this.post(`/nc/${this.$ctx.projectId}/v1/graphql`, { |
||||||
|
query: `query m2mNotChildren($pid: String!,$assoc:String!,$parent:String!, $limit:Int, $offset:Int){
|
||||||
|
m2mNotChildren(pid: $pid,assoc:$assoc,parent:$parent,limit:$limit, offset:$offset) |
||||||
|
}`,
|
||||||
|
variables: { |
||||||
|
parent: this.meta.tn, assoc, pid: pid + '', ...params |
||||||
|
} |
||||||
|
}) |
||||||
|
const count = await this.post(`/nc/${this.$ctx.projectId}/v1/graphql`, { |
||||||
|
query: `query m2mNotChildrenCount($pid: String!,$assoc:String!,$parent:String!){
|
||||||
|
m2mNotChildrenCount(pid: $pid,assoc:$assoc,parent:$parent) |
||||||
|
}`,
|
||||||
|
variables: { |
||||||
|
parent: this.meta.tn, assoc, pid: pid + '' |
||||||
|
} |
||||||
|
}) |
||||||
|
return { list: list.data.data.m2mNotChildren, count: count.data.data.m2mNotChildrenCount.count } |
||||||
|
} |
||||||
|
} |
||||||
|
/** |
||||||
|
* @copyright Copyright (c) 2021, Xgene Cloud Ltd |
||||||
|
* |
||||||
|
* @author Naveen MR <oof1lab@gmail.com> |
||||||
|
* @author Pranav C Balan <pranavxc@gmail.com> |
||||||
|
* |
||||||
|
* @license GNU AGPL version 3 or any later version |
||||||
|
* |
||||||
|
* This program is free software: you can redistribute it and/or modify |
||||||
|
* it under the terms of the GNU Affero General Public License as |
||||||
|
* published by the Free Software Foundation, either version 3 of the |
||||||
|
* License, or (at your option) any later version. |
||||||
|
* |
||||||
|
* This program is distributed in the hope that it will be useful, |
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||||
|
* GNU Affero General Public License for more details. |
||||||
|
* |
||||||
|
* You should have received a copy of the GNU Affero General Public License |
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
* |
||||||
|
*/ |
@ -0,0 +1,35 @@ |
|||||||
|
import ApiFactory from '@/plugins/ncApis/apiFactory' |
||||||
|
|
||||||
|
export default function({ store: $store, $axios, ...rest }, inject) { |
||||||
|
let instanceRefs = {} |
||||||
|
let projectId = null |
||||||
|
|
||||||
|
inject('ncApis', { |
||||||
|
get: ({ table, dbAlias, env }) => { |
||||||
|
if (!$store.state.meta.metas[table]) { |
||||||
|
return |
||||||
|
} |
||||||
|
|
||||||
|
if (instanceRefs[env] && instanceRefs[env][dbAlias] && instanceRefs[env][dbAlias][table]) { |
||||||
|
return instanceRefs[env][dbAlias][table] |
||||||
|
} |
||||||
|
|
||||||
|
instanceRefs[env] = instanceRefs[env] || {} |
||||||
|
instanceRefs[env][dbAlias] = instanceRefs[env][dbAlias] || {} |
||||||
|
|
||||||
|
instanceRefs[env][dbAlias][table] = ApiFactory.create( |
||||||
|
table, |
||||||
|
$store.getters['project/GtrProjectType'], |
||||||
|
{ $store, $axios, projectId, dbAlias, env, table } |
||||||
|
) |
||||||
|
|
||||||
|
return instanceRefs[env][dbAlias][table] |
||||||
|
}, |
||||||
|
clear: () => { |
||||||
|
instanceRefs = {} |
||||||
|
}, |
||||||
|
setProjectId: (_projectId) => { |
||||||
|
projectId = _projectId |
||||||
|
} |
||||||
|
}) |
||||||
|
} |
@ -0,0 +1,128 @@ |
|||||||
|
export default class RestApi { |
||||||
|
constructor(table, $ctx) { |
||||||
|
this.$ctx = $ctx |
||||||
|
this.table = $ctx.$store.state.meta.metas[table]._tn |
||||||
|
} |
||||||
|
|
||||||
|
// todo: - get version letter and use table alias
|
||||||
|
async list(params) { |
||||||
|
// const data = await this.get(`/nc/${this.$ctx.projectId}/api/v1/${this.table}`, params)
|
||||||
|
const data = await this.get(`/nc/${this.$ctx.projectId}/api/v1/${this.table}`, params) |
||||||
|
return data.data |
||||||
|
} |
||||||
|
|
||||||
|
async read(id) { |
||||||
|
const data = await this.get(`/nc/${this.$ctx.projectId}/api/v1/${this.table}/${id}`) |
||||||
|
return data.data |
||||||
|
} |
||||||
|
|
||||||
|
async count(params) { |
||||||
|
if (this.timeout) { |
||||||
|
return this.timeout |
||||||
|
} |
||||||
|
try { |
||||||
|
const data = await this.get(`/nc/${this.$ctx.projectId}/api/v1/${this.table}/count`, params, { |
||||||
|
timeout: 10000 |
||||||
|
}) |
||||||
|
return data && data.data |
||||||
|
} catch (e) { |
||||||
|
if (e.code === 'ECONNABORTED') { |
||||||
|
// eslint-disable-next-line no-return-assign
|
||||||
|
return this.timeout = { count: Infinity } |
||||||
|
} else { |
||||||
|
throw e |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
get(url, params, extras = {}) { |
||||||
|
return this.$axios({ |
||||||
|
url, |
||||||
|
params, |
||||||
|
...extras |
||||||
|
}) |
||||||
|
} |
||||||
|
|
||||||
|
async paginatedList(params) { |
||||||
|
// const list = await this.list(params);
|
||||||
|
// const count = (await this.count({where: params.where || ''})).count;
|
||||||
|
const [list, { count }] = await Promise.all([this.list(params), this.count({ |
||||||
|
where: params.where || '', |
||||||
|
conditionGraph: params.conditionGraph |
||||||
|
})]) |
||||||
|
return { list, count } |
||||||
|
} |
||||||
|
|
||||||
|
async paginatedM2mNotChildrenList(params, assoc, pid) { |
||||||
|
/// api/v1/Film/m2mNotChildren/film_actor/44
|
||||||
|
// const list = await this.list(params);
|
||||||
|
// const count = (await this.count({where: params.where || ''})).count;
|
||||||
|
const { list, info: { count } } = (await this.get(`/nc/${this.$ctx.projectId}/api/v1/${this.table}/m2mNotChildren/${assoc}/${pid}`, params)).data |
||||||
|
return { list, count } |
||||||
|
} |
||||||
|
|
||||||
|
async update(id, data, oldData) { |
||||||
|
const res = await this.$axios({ |
||||||
|
method: 'put', |
||||||
|
url: `/nc/${this.$ctx.projectId}/api/v1/${this.table}/${id}`, |
||||||
|
data |
||||||
|
}) |
||||||
|
const colName = Object.keys(data)[0] |
||||||
|
this.$ctx.$store.dispatch('sqlMgr/ActSqlOp', [{ dbAlias: this.$ctx.dbAlias }, 'xcAuditCreate', { |
||||||
|
tn: this.table, |
||||||
|
cn: colName, |
||||||
|
pk: id, |
||||||
|
value: data[colName], |
||||||
|
prevValue: oldData[colName] |
||||||
|
}]) |
||||||
|
|
||||||
|
return res |
||||||
|
} |
||||||
|
|
||||||
|
async insert(data) { |
||||||
|
return (await this.$axios({ |
||||||
|
method: 'post', |
||||||
|
url: `/nc/${this.$ctx.projectId}/api/v1/${this.table}`, |
||||||
|
data |
||||||
|
})).data |
||||||
|
} |
||||||
|
|
||||||
|
async delete(id) { |
||||||
|
return this.$axios({ |
||||||
|
method: 'delete', |
||||||
|
url: `/nc/${this.$ctx.projectId}/api/v1/${this.table}/${id}` |
||||||
|
}) |
||||||
|
} |
||||||
|
|
||||||
|
get $axios() { |
||||||
|
return this.$ctx.$axios |
||||||
|
} |
||||||
|
|
||||||
|
get apiUrl() { |
||||||
|
return `${process.env.NODE_ENV === 'production' |
||||||
|
? `${window.location.protocol}//${window.location.hostname}${window.location.port ? `:${window.location.port}` : ''}` |
||||||
|
: 'http://localhost:8080'}/nc/${this.$ctx.projectId}/api/v1/${this.table}` |
||||||
|
} |
||||||
|
} |
||||||
|
/** |
||||||
|
* @copyright Copyright (c) 2021, Xgene Cloud Ltd |
||||||
|
* |
||||||
|
* @author Naveen MR <oof1lab@gmail.com> |
||||||
|
* @author Pranav C Balan <pranavxc@gmail.com> |
||||||
|
* |
||||||
|
* @license GNU AGPL version 3 or any later version |
||||||
|
* |
||||||
|
* This program is free software: you can redistribute it and/or modify |
||||||
|
* it under the terms of the GNU Affero General Public License as |
||||||
|
* published by the Free Software Foundation, either version 3 of the |
||||||
|
* License, or (at your option) any later version. |
||||||
|
* |
||||||
|
* This program is distributed in the hope that it will be useful, |
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||||
|
* GNU Affero General Public License for more details. |
||||||
|
* |
||||||
|
* You should have received a copy of the GNU Affero General Public License |
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
* |
||||||
|
*/ |
Loading…
Reference in new issue