Browse Source

feat: relation delete

Signed-off-by: Pranav C <61551451+pranavxc@users.noreply.github.com>
pull/341/head
Pranav C 3 years ago
parent
commit
a3a034812e
  1. 6
      packages/nc-gui/components/project/spreadsheet/components/editColumn.vue
  2. 121
      packages/nc-gui/components/project/spreadsheet/components/editVirtualColumn.vue
  3. 31
      packages/nc-gui/components/project/spreadsheet/components/virtualHeaderCell.vue
  4. 4
      packages/nc-gui/components/project/spreadsheet/views/xcGridView.vue
  5. 2
      packages/nocodb/src/lib/migrator/SqlMigrator/lib/KnexMigrator.ts
  6. 2
      packages/nocodb/src/lib/migrator/util/file.help.ts
  7. 16
      packages/nocodb/src/lib/noco/NcProjectBuilder.ts
  8. 14
      packages/nocodb/src/lib/noco/common/BaseApiBuilder.ts
  9. 331
      packages/nocodb/src/lib/noco/meta/NcMetaMgr.ts

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

@ -13,10 +13,14 @@
<v-col cols="12"> <v-col cols="12">
<v-text-field <v-text-field
ref="column" ref="column"
hide-details hide-details="auto"
color="primary" color="primary"
v-model="newColumn.cn" v-model="newColumn.cn"
@input="newColumn.altered = newColumn.altered || 8" @input="newColumn.altered = newColumn.altered || 8"
:rules="[
v => !!v || 'Required',
v => !meta || !meta.columns || !column || meta.columns.every(c => column && c.cn === column.cn || v !== c.cn ) && meta.v.every(c => v !== c._cn ) || 'Duplicate column name'
]"
class="caption" class="caption"
label="Column name" label="Column name"
dense outlined></v-text-field> dense outlined></v-text-field>

121
packages/nc-gui/components/project/spreadsheet/components/editVirtualColumn.vue

@ -0,0 +1,121 @@
<template>
<v-card min-width="300px" max-width="400px" max-height="95vh" style="overflow: auto"
class="elevation-0 card">
<v-form v-model="valid">
<v-container fluid @click.stop.prevent>
<v-row>
<v-col cols="12" class="d-flex pb-0">
<v-spacer></v-spacer>
<v-btn x-small outlined @click="close">Cancel</v-btn>
<v-btn x-small color="primary" @click="save" :disabled="!valid">Save</v-btn>
</v-col>
<v-col cols="12">
<v-text-field
ref="column"
hide-details="auto"
color="primary"
v-model="newColumn._cn"
class="caption"
label="Column name"
:rules="[
v => !!v || 'Required',
v => !meta || !meta.columns || !column ||meta.columns.every(c => v !== c.cn ) && meta.v.every(c => column && c._cn === column._cn || v !== c._cn ) || 'Duplicate column name'
]"
dense outlined></v-text-field>
</v-col>
</v-row>
</v-container>
</v-form>
</v-card>
</template>
<script>
export default {
name: "editVirtualColumn",
components: {},
props: {
nodes: Object,
meta: Object,
value: Boolean,
column: Object
},
data: () => ({
valid: false,
newColumn: {}
}),
async created() {
},
methods: {
close() {
this.$emit('close');
this.newColumn = {};
},
async save() {
try {
} catch (e) {
console.log(e)
}
this.$emit('close');
},
focusInput() {
setTimeout(() => {
if (this.$refs.column && this.$refs.column.$el) {
this.$refs.column.$el.querySelector('input').focus()
}
}, 100);
},
}, mounted() {
this.newColumn = {...this.column}
}, watch: {
column(c) {
this.newColumn = {...c}
}
}
}
</script>
<style scoped lang="scss">
::v-deep {
.v-input__slot {
min-height: auto !important;
}
.v-input:not(.v-input--is-focused) fieldset {
border-color: #7f828b33 !important;
}
.ui-type input {
height: 24px;
}
.v-input--selection-controls__input > i {
transform: scale(.83);
}
label {
font-size: 0.75rem !important
}
.v-text-field--outlined.v-input--dense .v-label:not(.v-label--active) {
top: 6px;
}
}
.card {
border: solid 2px #7f828b33;
}
</style>

31
packages/nc-gui/components/project/spreadsheet/components/virtualHeaderCell.vue

@ -21,11 +21,11 @@
<v-icon v-on="on" small>mdi-menu-down</v-icon> <v-icon v-on="on" small>mdi-menu-down</v-icon>
</template> </template>
<v-list dense> <v-list dense>
<!-- <v-list-item dense @click="editColumnMenu = true"> <v-list-item dense @click="editColumnMenu = true">
<x-icon small class="mr-1" color="primary">mdi-pencil</x-icon> <x-icon small class="mr-1" color="primary">mdi-pencil</x-icon>
<span class="caption">Edit</span> <span class="caption">Edit</span>
</v-list-item> </v-list-item>
<v-list-item dense @click="setAsPrimaryValue"> <!-- <v-list-item dense @click="setAsPrimaryValue">
<x-icon small class="mr-1" color="primary">mdi-key-star</x-icon> <x-icon small class="mr-1" color="primary">mdi-key-star</x-icon>
<v-tooltip bottom> <v-tooltip bottom>
<template v-slot:activator="{on}"> <template v-slot:activator="{on}">
@ -58,14 +58,31 @@
</v-card-actions> </v-card-actions>
</v-card> </v-card>
</v-dialog> </v-dialog>
<v-menu offset-y v-model="editColumnMenu" content-class="elevation-0" left>
<template v-slot:activator="{on}">
<span v-on="on"></span>
</template>
<edit-virtual-column
v-if="editColumnMenu"
:nodes="nodes"
:edit-column="true"
:column="column"
:meta="meta"
></edit-virtual-column>
</v-menu>
</div> </div>
</template> </template>
<script> <script>
import EditVirtualColumn from "@/components/project/spreadsheet/components/editVirtualColumn";
export default { export default {
props: ['column', 'nodes'], components: {EditVirtualColumn},
props: ['column', 'nodes', 'meta'],
name: "virtualHeaderCell", name: "virtualHeaderCell",
data: () => ({ data: () => ({
columnDeleteDialog: false columnDeleteDialog: false,
editColumnMenu: false
}), }),
computed: { computed: {
type() { type() {

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

@ -1,6 +1,5 @@
<template> <template>
<div> <div>
<table v-if="data" class="xc-row-table" <table v-if="data" class="xc-row-table"
style=" "> style=" ">
<thead> <thead>
@ -28,6 +27,7 @@
<virtual-header-cell v-if="col.virtual" <virtual-header-cell v-if="col.virtual"
:column="col" :column="col"
:nodes="nodes" :nodes="nodes"
:meta="meta"
@saved="onNewColCreation" @saved="onNewColCreation"
/> />
@ -432,7 +432,7 @@ export default {
if (this.editEnabled.col != null && this.editEnabled.row != null) { if (this.editEnabled.col != null && this.editEnabled.row != null) {
return; return;
} }
if(e.key && e.key.length === 1) { if (e.key && e.key.length === 1) {
this.$set(this.data[this.selected.row].row, this.availableColumns[this.selected.col]._cn, e.key) this.$set(this.data[this.selected.row].row, this.availableColumns[this.selected.col]._cn, e.key)
this.editEnabled = {...this.selected} this.editEnabled = {...this.selected}
} }

2
packages/nocodb/src/lib/migrator/SqlMigrator/lib/KnexMigrator.ts

@ -484,7 +484,7 @@ export default class KnexMigrator extends SqlMigrator {
filesDown = files = await this.metaDb('nc_migrations').where({ filesDown = files = await this.metaDb('nc_migrations').where({
project_id: this.project_id, project_id: this.project_id,
db_alias: args.dbAlias db_alias: args.dbAlias
}).orderBy('title', 'asc') }).orderBy('id', 'asc')
} else { } else {
files = await promisify(glob)(args.upFilesPattern); files = await promisify(glob)(args.upFilesPattern);
filesDown = await promisify(glob)(args.downFilesPattern); filesDown = await promisify(glob)(args.downFilesPattern);

2
packages/nocodb/src/lib/migrator/util/file.help.ts

@ -3,7 +3,7 @@ import dayjs from 'dayjs';
const getUniqFilenamePrefix = function () { const getUniqFilenamePrefix = function () {
return dayjs().format('YYYYMMDD_HHmmss') return dayjs().format('YYYYMMDD_HHmmssSSS')
}; };

16
packages/nocodb/src/lib/noco/NcProjectBuilder.ts

@ -128,23 +128,19 @@ export default class NcProjectBuilder {
}); });
console.log(`Added new relation between : ${data.req.args.parentTable} ==> ${data.req.args.childTable}`) console.log(`Added new relation between : ${data.req.args.parentTable} ==> ${data.req.args.childTable}`)
break; break;
case 'xcRelationColumnDelete': case 'xcVirtualRelationDelete':
await curBuilder.onRelationCreate(data.req.args.parentTable, data.req.args.childTable, { await curBuilder.onRelationDelete(data.req.args.parentTable, data.req.args.childTable, {
...data.req.args, ...data.req.args,
virtual: true virtual: true
}); });
console.log(`Added new relation between : ${data.req.args.parentTable} ==> ${data.req.args.childTable}`) console.log(`Added new relation between : ${data.req.args.parentTable} ==> ${data.req.args.childTable}`)
break; break;
case 'xcVirtualRelationDelete': case 'xcRelationColumnDelete':
if(data.req.args?.type === 'mm'){ if (data.req.args?.type === 'mm') {
curBuilder.onManyToManyRelationDelete(data.req.args.parentTable, data.req.args.childTable) await curBuilder.onManyToManyRelationDelete(data.req.args.parentTable, data.req.args.childTable)
} }
// await curBuilder.onRelationDelete(data.req.args.parentTable, data.req.args.childTable, {
// ...data.req.args,
// virtual: true
// });
// console.log(`Deleted relation between : ${data.req.args.parentTable} ==> ${data.req.args.childTable}`)
break; break;

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

@ -750,21 +750,20 @@ export default abstract class BaseApiBuilder<T extends Noco> implements XcDynami
return this.getManyToManyRelations({parent, child}) return this.getManyToManyRelations({parent, child})
} }
public async onManyToManyRelationDelete(parent: string, child: string, _args?: any) { public async onManyToManyRelationDelete(parent: string, child: string, _args?: any) {
const parentMeta = this.metas[parent]; const parentMeta = this.metas[parent];
const childMeta = this.metas[child]; const childMeta = this.metas[child];
parentMeta.manyToMany = parentMeta.manyToMany.filter(mm => !(mm.tn === parent && mm.rtn === child || mm.tn === child && mm.rtn === child)) parentMeta.manyToMany = parentMeta.manyToMany.filter(mm => !(mm.tn === parent && mm.rtn === child || mm.tn === child && mm.rtn === parent))
childMeta.manyToMany = childMeta.manyToMany.filter(mm => !(mm.tn === parent && mm.rtn === child || mm.tn === child && mm.rtn === child)) childMeta.manyToMany = childMeta.manyToMany.filter(mm => !(mm.tn === parent && mm.rtn === child || mm.tn === child && mm.rtn === parent))
parentMeta.v = parentMeta.v.filter(({mm}) => !mm || !(mm.tn === parent && mm.rtn === child || mm.tn === child && mm.rtn === child)) parentMeta.v = parentMeta.v.filter(({mm}) => !mm || !(mm.tn === parent && mm.rtn === child || mm.tn === child && mm.rtn === parent))
childMeta.v = childMeta.v.filter(({mm}) => !mm || !(mm.tn === parent && mm.rtn === child || mm.tn === child && mm.rtn === child)) childMeta.v = childMeta.v.filter(({mm}) => !mm || !(mm.tn === parent && mm.rtn === child || mm.tn === child && mm.rtn === parent))
for (const meta of [parentMeta, childMeta]) { for (const meta of [parentMeta, childMeta]) {
await this.xcMeta.metaUpdate(this.projectId, this.dbAlias, 'nc_models', { await this.xcMeta.metaUpdate(this.projectId, this.dbAlias, 'nc_models', {
mm: 1, meta: JSON.stringify(meta)
}, {title: meta.tn}) }, {title: meta.tn})
XcCache.del([this.projectId, this.dbAlias, 'table', meta.tn].join('::')); XcCache.del([this.projectId, this.dbAlias, 'table', meta.tn].join('::'));
this.models[meta.tn] = this.getBaseModel(meta) this.models[meta.tn] = this.getBaseModel(meta)
@ -979,6 +978,7 @@ export default abstract class BaseApiBuilder<T extends Noco> implements XcDynami
protected getBaseModel(meta): BaseModelSql { protected getBaseModel(meta): BaseModelSql {
this.baseLog(`getBaseModel : '%s'`); this.baseLog(`getBaseModel : '%s'`);
this.metas[meta.tn] = meta;
return new BaseModel({ return new BaseModel({
dbDriver: this.dbDriver, dbDriver: this.dbDriver,
...meta, ...meta,

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

@ -2333,7 +2333,7 @@ export default class NcMetaMgr {
altered: 1 altered: 1
}); });
const aTn = `nc_m2m_${parentMeta.tn}_${childMeta.tn}_${Math.floor(Math.random() * 1000)}`; const aTn = `${this.projectConfigs[projectId]?.prefix ?? ''}_nc_m2m_${parentMeta.tn}_${childMeta.tn}_${Math.floor(Math.random() * 1000)}`;
const out = await this.projectMgr.getSqlMgr({id: projectId}).handleRequest('tableCreate', { const out = await this.projectMgr.getSqlMgr({id: projectId}).handleRequest('tableCreate', {
...args, ...args,
@ -2456,7 +2456,7 @@ export default class NcMetaMgr {
// todo : transaction // todo : transaction
protected async xcRelationColumnDelete(args: any, req): Promise<any> { protected async xcRelationColumnDelete(args: any, req, deleteColumn = true): Promise<any> {
const dbAlias = this.getDbAlias(args); const dbAlias = this.getDbAlias(args);
const projectId = this.getProjectId(args); const projectId = this.getProjectId(args);
@ -2475,7 +2475,8 @@ export default class NcMetaMgr {
}); });
const childMeta = JSON.parse(child.meta); const childMeta = JSON.parse(child.meta);
const relation = childMeta.belongsTo.find(bt => bt.rtn === args.args.parentTable); const relation = childMeta.belongsTo.find(bt => bt.rtn === args.args.parentTable);
{ // todo: virtual relation delete
if(relation){
const opArgs = { const opArgs = {
...args, ...args,
args: { args: {
@ -2484,10 +2485,16 @@ export default class NcMetaMgr {
parentTable: relation.rtn, parentTable: relation.rtn,
parentColumn: relation.rcn parentColumn: relation.rcn
}, },
api: 'relationDelete' api: 'relationDelete',
sqlOpPlus: true,
}; };
const out = await this.projectMgr.getSqlMgr({id: projectId}).handleRequest('relationDelete', opArgs); let out;
if (relation?.type === 'virtual') {
opArgs.api = 'xcVirtualRelationDelete';
out = await this.xcVirtualRelationDelete(opArgs, req);
} else {
out = await this.projectMgr.getSqlMgr({id: projectId}).handleRequest('relationDelete', opArgs);
}
if (this.listener) { if (this.listener) {
await this.listener({ await this.listener({
req: opArgs, req: opArgs,
@ -2497,283 +2504,36 @@ export default class NcMetaMgr {
}); });
} }
} }
{ if (deleteColumn) {
const originalColumns = childMeta.columns; const originalColumns = childMeta.columns;
const columns = childMeta.columns.map(c => ({ const columns = childMeta.columns.map(c => ({
...c, ...(relation.cn === c.cn ? { ...c, ...(relation.cn === c.cn ? {
altered: 4, altered: 4,
cno: c.cn cno: c.cn
} : {cno: c.cn}) } : {cno: c.cn})
})) }))
const opArgs = { const opArgs = {
...args, ...args,
args: { args: {
columns, columns,
originalColumns, originalColumns,
tn: childMeta.tn, tn: childMeta.tn,
},
sqlOpPlus: true,
api: 'tableUpdate'
}
/*
*
* {
"tn": "sdhsdjhs",
"originalColumns": [
{
"validate": {
"func": [],
"args": [],
"msg": []
},
"cn": "id",
"_cn": "Id",
"type": "integer",
"dt": "int",
"uidt": "ID",
"uip": "",
"uicn": "",
"dtx": "integer",
"ct": "int(11)",
"nrqd": false,
"rqd": true,
"ck": false,
"pk": true,
"un": true,
"ai": true,
"cdf": null,
"clen": null,
"np": 11,
"ns": 0,
"dtxp": "11",
"dtxs": ""
},
{
"validate": {
"func": [],
"args": [],
"msg": []
},
"cn": "title",
"_cn": "Title",
"type": "string",
"dt": "varchar",
"uidt": "SingleLineText",
"uip": "",
"uicn": "",
"dtx": "specificType",
"ct": "varchar(45)",
"nrqd": true,
"rqd": false,
"ck": false,
"pk": false,
"un": false,
"ai": false,
"cdf": null,
"clen": 45,
"np": null,
"ns": null,
"dtxp": "45",
"dtxs": "",
"pv": true
},
{
"validate": {
"func": [],
"args": [],
"msg": []
},
"cn": "created_at",
"_cn": "CreatedAt",
"type": "timestamp",
"dt": "timestamp",
"uidt": "CreateTime",
"uip": "",
"uicn": "",
"dtx": "specificType",
"ct": "varchar(45)",
"nrqd": true,
"rqd": false,
"ck": false,
"pk": false,
"un": false,
"ai": false,
"cdf": "CURRENT_TIMESTAMP",
"clen": 45,
"np": null,
"ns": null,
"dtxp": "",
"dtxs": "",
"default": "CURRENT_TIMESTAMP",
"columnDefault": "CURRENT_TIMESTAMP"
},
{
"validate": {
"func": [],
"args": [],
"msg": []
},
"cn": "updated_at",
"_cn": "UpdatedAt",
"type": "timestamp",
"dt": "timestamp",
"uidt": "LastModifiedTime",
"uip": "",
"uicn": "",
"dtx": "specificType",
"ct": "varchar(45)",
"nrqd": true,
"rqd": false,
"ck": false,
"pk": false,
"un": false,
"ai": false,
"cdf": "CURRENT_TIMESTAMP on update CURRENT_TIMESTAMP",
"clen": 45,
"np": null,
"ns": null,
"dtxp": "",
"dtxs": "",
"default": "CURRENT_TIMESTAMP on update CURRENT_TIMESTAMP",
"columnDefault": "CURRENT_TIMESTAMP on update CURRENT_TIMESTAMP"
}
],
"columns": [
{
"validate": {
"func": [],
"args": [],
"msg": []
},
"cn": "id",
"_cn": "Id",
"type": "integer",
"dt": "int",
"uidt": "ID",
"uip": "",
"uicn": "",
"dtx": "integer",
"ct": "int(11)",
"nrqd": false,
"rqd": true,
"ck": false,
"pk": true,
"un": true,
"ai": true,
"cdf": null,
"clen": null,
"np": 11,
"ns": 0,
"dtxp": "11",
"dtxs": ""
},
{
"validate": {
"func": [],
"args": [],
"msg": []
},
"cn": "title",
"_cn": "Title",
"type": "string",
"dt": "varchar",
"uidt": "SingleLineText",
"uip": "",
"uicn": "",
"dtx": "specificType",
"ct": "varchar(45)",
"nrqd": true,
"rqd": false,
"ck": false,
"pk": false,
"un": false,
"ai": false,
"cdf": null,
"clen": 45,
"np": null,
"ns": null,
"dtxp": "45",
"dtxs": "",
"pv": true,
"cno": "title",
"altered": 4
},
{
"validate": {
"func": [],
"args": [],
"msg": []
},
"cn": "created_at",
"_cn": "CreatedAt",
"type": "timestamp",
"dt": "timestamp",
"uidt": "CreateTime",
"uip": "",
"uicn": "",
"dtx": "specificType",
"ct": "varchar(45)",
"nrqd": true,
"rqd": false,
"ck": false,
"pk": false,
"un": false,
"ai": false,
"cdf": "CURRENT_TIMESTAMP",
"clen": 45,
"np": null,
"ns": null,
"dtxp": "",
"dtxs": "",
"default": "CURRENT_TIMESTAMP",
"columnDefault": "CURRENT_TIMESTAMP"
}, },
{ sqlOpPlus: true,
"validate": { api: 'tableUpdate'
"func": [], }
"args": [], const out = await this.projectMgr.getSqlMgr({id: projectId}).handleRequest('tableUpdate', opArgs);
"msg": []
},
"cn": "updated_at",
"_cn": "UpdatedAt",
"type": "timestamp",
"dt": "timestamp",
"uidt": "LastModifiedTime",
"uip": "",
"uicn": "",
"dtx": "specificType",
"ct": "varchar(45)",
"nrqd": true,
"rqd": false,
"ck": false,
"pk": false,
"un": false,
"ai": false,
"cdf": "CURRENT_TIMESTAMP on update CURRENT_TIMESTAMP",
"clen": 45,
"np": null,
"ns": null,
"dtxp": "",
"dtxs": "",
"default": "CURRENT_TIMESTAMP on update CURRENT_TIMESTAMP",
"columnDefault": "CURRENT_TIMESTAMP on update CURRENT_TIMESTAMP"
}
]
}
*
* */
const out = await this.projectMgr.getSqlMgr({id: projectId}).handleRequest('tableUpdate', opArgs);
if (this.listener) { if (this.listener) {
await this.listener({ await this.listener({
req: opArgs, req: opArgs,
res: out, res: out,
user: req.user, user: req.user,
ctx: {req} ctx: {req}
}); });
}
} }
}
break; break;
case 'mm': { case 'mm': {
const assoc = await this.xcMeta.metaGet(projectId, dbAlias, 'nc_models', { const assoc = await this.xcMeta.metaGet(projectId, dbAlias, 'nc_models', {
@ -2781,7 +2541,7 @@ export default class NcMetaMgr {
}); });
const assocMeta = JSON.parse(assoc.meta); const assocMeta = JSON.parse(assoc.meta);
const rel1 = assocMeta.belongsTo.find(bt => bt.rtn === args.args.parentTable) const rel1 = assocMeta.belongsTo.find(bt => bt.rtn === args.args.parentTable)
const rel2 = assocMeta.belongsTo.find(bt => bt.rtn === args.args.parentTable) const rel2 = assocMeta.belongsTo.find(bt => bt.rtn === args.args.childTable)
await this.xcRelationColumnDelete({ await this.xcRelationColumnDelete({
...args, ...args,
args: { args: {
@ -2789,8 +2549,9 @@ export default class NcMetaMgr {
parentColumn: rel1.rcn, parentColumn: rel1.rcn,
childTable: rel1.tn, childTable: rel1.tn,
childColumn: rel1.cn, childColumn: rel1.cn,
type: 'bt',
} }
}, req) }, req, false)
await this.xcRelationColumnDelete({ await this.xcRelationColumnDelete({
...args, ...args,
args: { args: {
@ -2798,14 +2559,16 @@ export default class NcMetaMgr {
parentColumn: rel2.rcn, parentColumn: rel2.rcn,
childTable: rel2.tn, childTable: rel2.tn,
childColumn: rel2.cn, childColumn: rel2.cn,
type: 'bt',
} }
}, req); }, req, false);
const opArgs = { const opArgs = {
...args, ...args,
args: assocMeta, args: assocMeta,
api: 'tableDelete' api: 'tableDelete',
sqlOpPlus: true,
}; };
const out = await this.projectMgr.getSqlMgr({id: projectId}).handleRequest('tableDelete', opArgs); const out = await this.projectMgr.getSqlMgr({id: projectId}).handleRequest('tableDelete', opArgs);

Loading…
Cancel
Save