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-text-field
ref="column"
hide-details
hide-details="auto"
color="primary"
v-model="newColumn.cn"
@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"
label="Column name"
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>
</template>
<v-list dense>
<!-- <v-list-item dense @click="editColumnMenu = true">
<x-icon small class="mr-1" color="primary">mdi-pencil</x-icon>
<span class="caption">Edit</span>
</v-list-item>
<v-list-item dense @click="setAsPrimaryValue">
<v-list-item dense @click="editColumnMenu = true">
<x-icon small class="mr-1" color="primary">mdi-pencil</x-icon>
<span class="caption">Edit</span>
</v-list-item>
<!-- <v-list-item dense @click="setAsPrimaryValue">
<x-icon small class="mr-1" color="primary">mdi-key-star</x-icon>
<v-tooltip bottom>
<template v-slot:activator="{on}">
@ -58,14 +58,31 @@
</v-card-actions>
</v-card>
</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>
</template>
<script>
import EditVirtualColumn from "@/components/project/spreadsheet/components/editVirtualColumn";
export default {
props: ['column', 'nodes'],
components: {EditVirtualColumn},
props: ['column', 'nodes', 'meta'],
name: "virtualHeaderCell",
data: () => ({
columnDeleteDialog: false
columnDeleteDialog: false,
editColumnMenu: false
}),
computed: {
type() {

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

@ -1,6 +1,5 @@
<template>
<div>
<table v-if="data" class="xc-row-table"
style=" ">
<thead>
@ -28,6 +27,7 @@
<virtual-header-cell v-if="col.virtual"
:column="col"
:nodes="nodes"
:meta="meta"
@saved="onNewColCreation"
/>
@ -432,7 +432,7 @@ export default {
if (this.editEnabled.col != null && this.editEnabled.row != null) {
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.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({
project_id: this.project_id,
db_alias: args.dbAlias
}).orderBy('title', 'asc')
}).orderBy('id', 'asc')
} else {
files = await promisify(glob)(args.upFilesPattern);
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 () {
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}`)
break;
case 'xcRelationColumnDelete':
await curBuilder.onRelationCreate(data.req.args.parentTable, data.req.args.childTable, {
case 'xcVirtualRelationDelete':
await curBuilder.onRelationDelete(data.req.args.parentTable, data.req.args.childTable, {
...data.req.args,
virtual: true
});
console.log(`Added new relation between : ${data.req.args.parentTable} ==> ${data.req.args.childTable}`)
break;
case 'xcVirtualRelationDelete':
if(data.req.args?.type === 'mm'){
curBuilder.onManyToManyRelationDelete(data.req.args.parentTable, data.req.args.childTable)
case 'xcRelationColumnDelete':
if (data.req.args?.type === 'mm') {
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;

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})
}
public async onManyToManyRelationDelete(parent: string, child: string, _args?: any) {
public async onManyToManyRelationDelete(parent: string, child: string, _args?: any) {
const parentMeta = this.metas[parent];
const childMeta = this.metas[child];
parentMeta.manyToMany = parentMeta.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 === 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 === parent))
parentMeta.v = parentMeta.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 === 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 === parent))
for (const meta of [parentMeta, childMeta]) {
await this.xcMeta.metaUpdate(this.projectId, this.dbAlias, 'nc_models', {
mm: 1,
meta: JSON.stringify(meta)
}, {title: meta.tn})
XcCache.del([this.projectId, this.dbAlias, 'table', meta.tn].join('::'));
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 {
this.baseLog(`getBaseModel : '%s'`);
this.metas[meta.tn] = meta;
return new BaseModel({
dbDriver: this.dbDriver,
...meta,

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

@ -2333,7 +2333,7 @@ export default class NcMetaMgr {
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', {
...args,
@ -2456,7 +2456,7 @@ export default class NcMetaMgr {
// 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 projectId = this.getProjectId(args);
@ -2475,7 +2475,8 @@ export default class NcMetaMgr {
});
const childMeta = JSON.parse(child.meta);
const relation = childMeta.belongsTo.find(bt => bt.rtn === args.args.parentTable);
{
// todo: virtual relation delete
if(relation){
const opArgs = {
...args,
args: {
@ -2484,10 +2485,16 @@ export default class NcMetaMgr {
parentTable: relation.rtn,
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) {
await this.listener({
req: opArgs,
@ -2497,283 +2504,36 @@ export default class NcMetaMgr {
});
}
}
{
const originalColumns = childMeta.columns;
const columns = childMeta.columns.map(c => ({
...c, ...(relation.cn === c.cn ? {
altered: 4,
cno: c.cn
} : {cno: c.cn})
}))
const opArgs = {
...args,
args: {
columns,
originalColumns,
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"
if (deleteColumn) {
const originalColumns = childMeta.columns;
const columns = childMeta.columns.map(c => ({
...c, ...(relation.cn === c.cn ? {
altered: 4,
cno: c.cn
} : {cno: c.cn})
}))
const opArgs = {
...args,
args: {
columns,
originalColumns,
tn: childMeta.tn,
},
{
"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"
}
]
}
*
* */
const out = await this.projectMgr.getSqlMgr({id: projectId}).handleRequest('tableUpdate', opArgs);
sqlOpPlus: true,
api: 'tableUpdate'
}
const out = await this.projectMgr.getSqlMgr({id: projectId}).handleRequest('tableUpdate', opArgs);
if (this.listener) {
await this.listener({
req: opArgs,
res: out,
user: req.user,
ctx: {req}
});
if (this.listener) {
await this.listener({
req: opArgs,
res: out,
user: req.user,
ctx: {req}
});
}
}
}
break;
case 'mm': {
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 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({
...args,
args: {
@ -2789,8 +2549,9 @@ export default class NcMetaMgr {
parentColumn: rel1.rcn,
childTable: rel1.tn,
childColumn: rel1.cn,
type: 'bt',
}
}, req)
}, req, false)
await this.xcRelationColumnDelete({
...args,
args: {
@ -2798,14 +2559,16 @@ export default class NcMetaMgr {
parentColumn: rel2.rcn,
childTable: rel2.tn,
childColumn: rel2.cn,
type: 'bt',
}
}, req);
}, req, false);
const opArgs = {
...args,
args: assocMeta,
api: 'tableDelete'
api: 'tableDelete',
sqlOpPlus: true,
};
const out = await this.projectMgr.getSqlMgr({id: projectId}).handleRequest('tableDelete', opArgs);

Loading…
Cancel
Save