From f16cd2e909d8e4b97a994c6aa9239ad25fd50743 Mon Sep 17 00:00:00 2001 From: mertmit Date: Tue, 29 Mar 2022 20:23:55 +0300 Subject: [PATCH 1/2] fix: multiple after-update webhooks being triggered Signed-off-by: mertmit --- .../project/spreadsheet/rowsXcDataTable.vue | 8 ++++---- .../project/spreadsheet/views/xcGridView.vue | 18 +++++++++--------- packages/nc-gui/plugins/ncApis/restApi.js | 3 ++- .../src/lib/dataMapper/lib/sql/BaseModelSql.ts | 4 +++- .../nocodb/src/lib/noco/common/BaseModel.ts | 6 +++--- 5 files changed, 21 insertions(+), 18 deletions(-) diff --git a/packages/nc-gui/components/project/spreadsheet/rowsXcDataTable.vue b/packages/nc-gui/components/project/spreadsheet/rowsXcDataTable.vue index 22de323c49..e02eca41d8 100644 --- a/packages/nc-gui/components/project/spreadsheet/rowsXcDataTable.vue +++ b/packages/nc-gui/components/project/spreadsheet/rowsXcDataTable.vue @@ -1079,7 +1079,7 @@ export default { // onCellValueChange(col, row, column) { // this.onCellValueChangeFn(col, row, column) // }, - async onCellValueChange(col, row, column) { + async onCellValueChange(col, row, column, saved = false) { if (!this.data[row]) { return } @@ -1096,7 +1096,7 @@ export default { return } // return if there is no change - if (oldRow[column._cn] === rowObj[column._cn]) { + if (oldRow[column._cn] === rowObj[column._cn] && !saved) { return } const id = this.meta.columns.filter(c => c.pk).map(c => rowObj[c._cn]).join('___') @@ -1109,7 +1109,7 @@ export default { // eslint-disable-next-line promise/param-names const newData = await this.api.update(id, { [column._cn]: rowObj[column._cn] - }, { [column._cn]: oldRow[column._cn] }) + }, { [column._cn]: oldRow[column._cn] }, saved) this.$set(this.data[row], 'row', { ...rowObj, ...newData }) this.$set(oldRow, column._cn, rowObj[column._cn]) @@ -1178,7 +1178,7 @@ export default { return } this.$set(this.data[index].row, col._cn, null) - await this.onCellValueChange(colIndex, index, col) + await this.onCellValueChange(colIndex, index, col, true) }, async insertNewRow(atEnd = false, expand = false, presetValues = {}) { const isKanban = this.selectedView && this.selectedView.show_as === 'kanban' diff --git a/packages/nc-gui/components/project/spreadsheet/views/xcGridView.vue b/packages/nc-gui/components/project/spreadsheet/views/xcGridView.vue index 379d40f0d5..fc8fa6dbed 100644 --- a/packages/nc-gui/components/project/spreadsheet/views/xcGridView.vue +++ b/packages/nc-gui/components/project/spreadsheet/views/xcGridView.vue @@ -196,7 +196,7 @@ :is-new="rowMeta.new" v-on="$listeners" @updateCol="(...args) => updateCol(...args, columnObj.bt && meta.columns.find( c => c.cn === columnObj.bt.cn), col, row)" - @saveRow="onCellValueChange(col, row, columnObj)" + @saveRow="onCellValueChange(col, row, columnObj, true)" /> @@ -413,7 +413,7 @@ export default { }, updateCol(row, column, value, columnObj, colIndex, rowIndex) { this.$set(row, column, value) - this.onCellValueChange(colIndex, rowIndex, columnObj) + this.onCellValueChange(colIndex, rowIndex, columnObj, true) }, calculateColumnWidth() { // setTimeout(() => { @@ -501,7 +501,7 @@ export default { this.$set(rowObj, columnObj._cn, null) // update/save cell value - this.onCellValueChange(this.selected.col, this.selected.row, columnObj) + this.onCellValueChange(this.selected.col, this.selected.row, columnObj, true) } break // left @@ -597,8 +597,8 @@ export default { showRowContextMenu($event, rowObj, rowMeta, row, ...rest) { this.$emit('showRowContextMenu', $event, rowObj, rowMeta, row, ...rest) }, - onCellValueChange(col, row, column, ev) { - this.$emit('onCellValueChange', col, row, column, ev) + onCellValueChange(col, row, column, saved) { + this.$emit('onCellValueChange', col, row, column, saved) }, navigateToNext() { if (this.selected.row < this.rowLength - 1) { diff --git a/packages/nc-gui/plugins/ncApis/restApi.js b/packages/nc-gui/plugins/ncApis/restApi.js index 7bb2aff503..92efb64579 100644 --- a/packages/nc-gui/plugins/ncApis/restApi.js +++ b/packages/nc-gui/plugins/ncApis/restApi.js @@ -64,7 +64,8 @@ export default class RestApi { return { list, count } } - async update(id, data, oldData) { + async update(id, data, oldData, cellSaved = false) { + data._cellSaved = cellSaved const res = await this.$axios({ method: 'put', url: `/nc/${this.$ctx.projectId}/api/v1/${this.table}/${encodeURIComponent(id)}`, diff --git a/packages/nocodb/src/lib/dataMapper/lib/sql/BaseModelSql.ts b/packages/nocodb/src/lib/dataMapper/lib/sql/BaseModelSql.ts index ff2d9aa546..57680a0b7d 100644 --- a/packages/nocodb/src/lib/dataMapper/lib/sql/BaseModelSql.ts +++ b/packages/nocodb/src/lib/dataMapper/lib/sql/BaseModelSql.ts @@ -345,6 +345,7 @@ class BaseModelSql extends BaseModel { */ async updateByPk(id, data, trx = null, cookie?: any) { try { + data._cellSaved = (data._cellSaved === undefined)?true:data._cellSaved; const mappedData = this.mapAliasToColumn(data); await this.validate(data); @@ -360,7 +361,8 @@ class BaseModelSql extends BaseModel { .where(this._wherePk(id)) ); - const response = await this.nestedRead(id, this.defaultNestedQueryParams); + let response = await this.nestedRead(id, this.defaultNestedQueryParams); + response._cellSaved = data._cellSaved; await this.afterUpdate(response, trx, cookie); return response; } catch (e) { diff --git a/packages/nocodb/src/lib/noco/common/BaseModel.ts b/packages/nocodb/src/lib/noco/common/BaseModel.ts index 2f259e0938..c179dfb3e7 100644 --- a/packages/nocodb/src/lib/noco/common/BaseModel.ts +++ b/packages/nocodb/src/lib/noco/common/BaseModel.ts @@ -49,7 +49,7 @@ class BaseModel> extends BaseModelSql { public async beforeUpdate(data: any, _trx: any, req): Promise { req = req || {}; req['oldData'] = await this.readByPk(req['params'].id); - await this.handleHooks('before.update', data, req); + if(data._cellSaved) await this.handleHooks('before.update', data, req); } public async afterUpdate(data: any, _trx: any, req): Promise { @@ -73,8 +73,8 @@ class BaseModel> extends BaseModelSql { ip: req.clientIp, user: req.user?.email } - ); - await this.handleHooks('after.update', data, req); + ) + if(data._cellSaved) await this.handleHooks('after.update', data, req); } private _updateAuditDescription(id, oldData: any, data: any) { From ea5a936a11eb34f08ccd422cda010b44fd5363c4 Mon Sep 17 00:00:00 2001 From: mertmit Date: Wed, 30 Mar 2022 13:22:42 +0300 Subject: [PATCH 2/2] fix: extended fix for gql & improved ui part Signed-off-by: mertmit --- .../components/project/spreadsheet/rowsXcDataTable.vue | 8 +++++--- .../components/project/spreadsheet/views/xcGridView.vue | 4 ++-- packages/nc-gui/plugins/ncApis/gqlApi.js | 5 +++-- .../nocodb/src/lib/dataMapper/lib/sql/BaseModelSql.ts | 2 -- packages/nocodb/src/lib/noco/common/BaseModel.ts | 4 ++-- 5 files changed, 12 insertions(+), 11 deletions(-) diff --git a/packages/nc-gui/components/project/spreadsheet/rowsXcDataTable.vue b/packages/nc-gui/components/project/spreadsheet/rowsXcDataTable.vue index e02eca41d8..4700ddfdec 100644 --- a/packages/nc-gui/components/project/spreadsheet/rowsXcDataTable.vue +++ b/packages/nc-gui/components/project/spreadsheet/rowsXcDataTable.vue @@ -1079,11 +1079,12 @@ export default { // onCellValueChange(col, row, column) { // this.onCellValueChangeFn(col, row, column) // }, - async onCellValueChange(col, row, column, saved = false) { + async onCellValueChange(col, row, column, saved = true) { if (!this.data[row]) { return } - const { row: rowObj, rowMeta, oldRow, saving } = this.data[row] + const { row: rowObj, rowMeta, oldRow, saving, lastSave } = this.data[row] + if(!lastSave) this.$set(this.data[row], 'lastSave', rowObj[column._cn]); if (rowMeta.new) { // return if there is no change if (oldRow[column._cn] === rowObj[column._cn] || saving) { @@ -1096,9 +1097,10 @@ export default { return } // return if there is no change - if (oldRow[column._cn] === rowObj[column._cn] && !saved) { + if (oldRow[column._cn] === rowObj[column._cn] && ((lastSave || null) === rowObj[column._cn])) { return } + if(saved) this.$set(this.data[row], 'lastSave', oldRow[column._cn]); const id = this.meta.columns.filter(c => c.pk).map(c => rowObj[c._cn]).join('___') if (!id) { diff --git a/packages/nc-gui/components/project/spreadsheet/views/xcGridView.vue b/packages/nc-gui/components/project/spreadsheet/views/xcGridView.vue index fc8fa6dbed..4842ea450c 100644 --- a/packages/nc-gui/components/project/spreadsheet/views/xcGridView.vue +++ b/packages/nc-gui/components/project/spreadsheet/views/xcGridView.vue @@ -215,8 +215,8 @@ :db-alias="nodes.dbAlias" :is-locked="isLocked" :is-public="isPublicView" - @save="editEnabled = {}; onCellValueChange(col, row, columnObj, true);" - @cancel="editEnabled = {}; onCellValueChange(col, row, columnObj, true);" + @save="editEnabled = {};" + @cancel="editEnabled = {};" @update="onCellValueChange(col, row, columnObj, false)" @blur="onCellValueChange(col, row, columnObj, true)" @navigateToNext="navigateToNext" diff --git a/packages/nc-gui/plugins/ncApis/gqlApi.js b/packages/nc-gui/plugins/ncApis/gqlApi.js index 2d3b5e0e51..3fa0fe28f5 100644 --- a/packages/nc-gui/plugins/ncApis/gqlApi.js +++ b/packages/nc-gui/plugins/ncApis/gqlApi.js @@ -176,14 +176,15 @@ export default class GqlApi { return { list, count } } - async update(id, data, oldData, params = {}) { + async update(id, data, oldData, cellSaved = false, params = {}) { 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){${this.gqlReqBody}${await this.gqlRelationReqBody(params)}} }`, variables: { id, data - } + }, + _cellSaved: cellSaved }) const colName = Object.keys(data)[0] diff --git a/packages/nocodb/src/lib/dataMapper/lib/sql/BaseModelSql.ts b/packages/nocodb/src/lib/dataMapper/lib/sql/BaseModelSql.ts index 57680a0b7d..cb0b1b0b51 100644 --- a/packages/nocodb/src/lib/dataMapper/lib/sql/BaseModelSql.ts +++ b/packages/nocodb/src/lib/dataMapper/lib/sql/BaseModelSql.ts @@ -345,7 +345,6 @@ class BaseModelSql extends BaseModel { */ async updateByPk(id, data, trx = null, cookie?: any) { try { - data._cellSaved = (data._cellSaved === undefined)?true:data._cellSaved; const mappedData = this.mapAliasToColumn(data); await this.validate(data); @@ -362,7 +361,6 @@ class BaseModelSql extends BaseModel { ); let response = await this.nestedRead(id, this.defaultNestedQueryParams); - response._cellSaved = data._cellSaved; await this.afterUpdate(response, trx, cookie); return response; } catch (e) { diff --git a/packages/nocodb/src/lib/noco/common/BaseModel.ts b/packages/nocodb/src/lib/noco/common/BaseModel.ts index c179dfb3e7..d093843e6a 100644 --- a/packages/nocodb/src/lib/noco/common/BaseModel.ts +++ b/packages/nocodb/src/lib/noco/common/BaseModel.ts @@ -49,7 +49,7 @@ class BaseModel> extends BaseModelSql { public async beforeUpdate(data: any, _trx: any, req): Promise { req = req || {}; req['oldData'] = await this.readByPk(req['params'].id); - if(data._cellSaved) await this.handleHooks('before.update', data, req); + if(req.body?._cellSaved) await this.handleHooks('before.update', data, req); } public async afterUpdate(data: any, _trx: any, req): Promise { @@ -74,7 +74,7 @@ class BaseModel> extends BaseModelSql { user: req.user?.email } ) - if(data._cellSaved) await this.handleHooks('after.update', data, req); + if(req.body?._cellSaved) await this.handleHooks('after.update', data, req); } private _updateAuditDescription(id, oldData: any, data: any) {