From a93fa3e6782ce2bfb2b093b1f19677ae928e0217 Mon Sep 17 00:00:00 2001 From: Pranav C Date: Fri, 15 Nov 2024 10:52:01 +0000 Subject: [PATCH 1/8] fix: webhook payload corrections --- packages/nocodb/src/helpers/populateSamplePayload.ts | 2 -- packages/nocodb/src/services/hooks.service.ts | 2 +- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/packages/nocodb/src/helpers/populateSamplePayload.ts b/packages/nocodb/src/helpers/populateSamplePayload.ts index 16b1d29616..81459273ab 100644 --- a/packages/nocodb/src/helpers/populateSamplePayload.ts +++ b/packages/nocodb/src/helpers/populateSamplePayload.ts @@ -77,8 +77,6 @@ export async function populateSamplePayloadV2( data: { table_id: model.id, table_name: model.title, - view_id: model.views[0].id, - view_name: model.views[0].title, }, }; diff --git a/packages/nocodb/src/services/hooks.service.ts b/packages/nocodb/src/services/hooks.service.ts index 1bbb381a74..fc75923a4a 100644 --- a/packages/nocodb/src/services/hooks.service.ts +++ b/packages/nocodb/src/services/hooks.service.ts @@ -225,7 +225,7 @@ export class HooksService { model: model, view: null, prevData: null, - newData: data, + newData: data.rows, user: user, testFilters: (hook as any)?.filters, throwErrorOnFailure: true, From c3319c62b75e180ec85ba5658e8ededd0b58e53d Mon Sep 17 00:00:00 2001 From: Pranav C Date: Fri, 15 Nov 2024 10:52:01 +0000 Subject: [PATCH 2/8] fix: on edit formula extract initial datatype --- .../nc-gui/components/smartsheet/column/FormulaOptions.vue | 3 +++ 1 file changed, 3 insertions(+) diff --git a/packages/nc-gui/components/smartsheet/column/FormulaOptions.vue b/packages/nc-gui/components/smartsheet/column/FormulaOptions.vue index 431b33c9a9..ce0f47bca8 100644 --- a/packages/nc-gui/components/smartsheet/column/FormulaOptions.vue +++ b/packages/nc-gui/components/smartsheet/column/FormulaOptions.vue @@ -127,6 +127,9 @@ watch( () => { debouncedValidate() }, + { + immediate: true, + }, ) // set additional validations From 80ae0f1938fee6be5be8f1a1d6dec96b99174ae9 Mon Sep 17 00:00:00 2001 From: Pranav C Date: Fri, 15 Nov 2024 10:52:01 +0000 Subject: [PATCH 3/8] fix: recreate colOptions only if all required fields are included --- packages/nocodb/src/models/Column.ts | 284 ++++++++++++++------------- 1 file changed, 148 insertions(+), 136 deletions(-) diff --git a/packages/nocodb/src/models/Column.ts b/packages/nocodb/src/models/Column.ts index 76a02348e1..121337ef37 100644 --- a/packages/nocodb/src/models/Column.ts +++ b/packages/nocodb/src/models/Column.ts @@ -57,6 +57,27 @@ const selectColors = [ const logger = new Logger('Column'); +const requiredColumnsToRecreate = { + [UITypes.LinkToAnotherRecord]: [ + 'type', + 'fk_child_column_id', + 'fk_parent_column_id', + 'fk_related_model_id', + ], + [UITypes.Links]: [ + 'type', + 'fk_child_column_id', + 'fk_parent_column_id', + 'fk_related_model_id', + ], + [UITypes.Rollup]: ['fk_relation_column_id', 'fk_rollup_column_id'], + [UITypes.Lookup]: ['fk_relation_column_id', 'fk_lookup_column_id'], + [UITypes.QrCode]: ['fk_qr_value_column_id'], + [UITypes.Barcode]: ['fk_barcode_value_column_id'], + [UITypes.Button]: ['type', 'label'], + [UITypes.Formula]: ['formula'], +}; + export default class Column implements ColumnType { public fk_model_id: string; public fk_workspace_id?: string; @@ -1112,157 +1133,148 @@ export default class Column implements ColumnType { skipFormulaInvalidate = false, ) { const oldCol = await Column.get(context, { colId }, ncMeta); - let insertColOpt = true; - switch (oldCol.uidt) { - case UITypes.Lookup: { - // LookupColumn.insert() + const requiredColAvail = + !requiredColumnsToRecreate[oldCol.uidt] || + requiredColumnsToRecreate[oldCol.uidt].every((k) => column[k]); - await ncMeta.metaDelete( - context.workspace_id, - context.base_id, - MetaTable.COL_LOOKUP, - { - fk_column_id: colId, - }, - ); - await NocoCache.deepDel( - `${CacheScope.COL_LOOKUP}:${colId}`, - CacheDelDirection.CHILD_TO_PARENT, - ); - break; - } - case UITypes.Rollup: { - await ncMeta.metaDelete( - context.workspace_id, - context.base_id, - MetaTable.COL_ROLLUP, - { - fk_column_id: colId, - }, - ); - await NocoCache.deepDel( - `${CacheScope.COL_ROLLUP}:${colId}`, - CacheDelDirection.CHILD_TO_PARENT, - ); - break; - } + if (requiredColAvail) { + switch (oldCol.uidt) { + case UITypes.Lookup: { + // LookupColumn.insert() - case UITypes.Links: - case UITypes.LinkToAnotherRecord: { - // delete only if all required fields are present - if ( - [ - 'type', - 'fk_child_column_id', - 'fk_parent_column_id', - 'fk_related_model_id', - ].some((k) => !column[k]) - ) { - insertColOpt = false; + await ncMeta.metaDelete( + context.workspace_id, + context.base_id, + MetaTable.COL_LOOKUP, + { + fk_column_id: colId, + }, + ); + await NocoCache.deepDel( + `${CacheScope.COL_LOOKUP}:${colId}`, + CacheDelDirection.CHILD_TO_PARENT, + ); + break; + } + case UITypes.Rollup: { + await ncMeta.metaDelete( + context.workspace_id, + context.base_id, + MetaTable.COL_ROLLUP, + { + fk_column_id: colId, + }, + ); + await NocoCache.deepDel( + `${CacheScope.COL_ROLLUP}:${colId}`, + CacheDelDirection.CHILD_TO_PARENT, + ); break; } - await ncMeta.metaDelete( - context.workspace_id, - context.base_id, - MetaTable.COL_RELATIONS, - { - fk_column_id: colId, - }, - ); - await NocoCache.deepDel( - `${CacheScope.COL_RELATION}:${colId}`, - CacheDelDirection.CHILD_TO_PARENT, - ); - break; - } - case UITypes.Formula: { - await ncMeta.metaDelete( - context.workspace_id, - context.base_id, - MetaTable.COL_FORMULA, - { - fk_column_id: colId, - }, - ); + case UITypes.Links: + case UITypes.LinkToAnotherRecord: { + await ncMeta.metaDelete( + context.workspace_id, + context.base_id, + MetaTable.COL_RELATIONS, + { + fk_column_id: colId, + }, + ); + await NocoCache.deepDel( + `${CacheScope.COL_RELATION}:${colId}`, + CacheDelDirection.CHILD_TO_PARENT, + ); + break; + } + case UITypes.Formula: { + await ncMeta.metaDelete( + context.workspace_id, + context.base_id, + MetaTable.COL_FORMULA, + { + fk_column_id: colId, + }, + ); - await NocoCache.deepDel( - `${CacheScope.COL_FORMULA}:${colId}`, - CacheDelDirection.CHILD_TO_PARENT, - ); - break; - } + await NocoCache.deepDel( + `${CacheScope.COL_FORMULA}:${colId}`, + CacheDelDirection.CHILD_TO_PARENT, + ); + break; + } - case UITypes.Button: { - await ncMeta.metaDelete( - context.workspace_id, - context.base_id, - MetaTable.COL_BUTTON, - { - fk_column_id: colId, - }, - ); + case UITypes.Button: { + await ncMeta.metaDelete( + context.workspace_id, + context.base_id, + MetaTable.COL_BUTTON, + { + fk_column_id: colId, + }, + ); - await NocoCache.deepDel( - `${CacheScope.COL_BUTTON}:${colId}`, - CacheDelDirection.CHILD_TO_PARENT, - ); - break; - } + await NocoCache.deepDel( + `${CacheScope.COL_BUTTON}:${colId}`, + CacheDelDirection.CHILD_TO_PARENT, + ); + break; + } - case UITypes.QrCode: { - await ncMeta.metaDelete( - context.workspace_id, - context.base_id, - MetaTable.COL_QRCODE, - { - fk_column_id: colId, - }, - ); + case UITypes.QrCode: { + await ncMeta.metaDelete( + context.workspace_id, + context.base_id, + MetaTable.COL_QRCODE, + { + fk_column_id: colId, + }, + ); - await NocoCache.deepDel( - `${CacheScope.COL_QRCODE}:${colId}`, - CacheDelDirection.CHILD_TO_PARENT, - ); - break; - } + await NocoCache.deepDel( + `${CacheScope.COL_QRCODE}:${colId}`, + CacheDelDirection.CHILD_TO_PARENT, + ); + break; + } - case UITypes.Barcode: { - await ncMeta.metaDelete( - context.workspace_id, - context.base_id, - MetaTable.COL_BARCODE, - { - fk_column_id: colId, - }, - ); + case UITypes.Barcode: { + await ncMeta.metaDelete( + context.workspace_id, + context.base_id, + MetaTable.COL_BARCODE, + { + fk_column_id: colId, + }, + ); - await NocoCache.deepDel( - `${CacheScope.COL_BARCODE}:${colId}`, - CacheDelDirection.CHILD_TO_PARENT, - ); - break; - } + await NocoCache.deepDel( + `${CacheScope.COL_BARCODE}:${colId}`, + CacheDelDirection.CHILD_TO_PARENT, + ); + break; + } - case UITypes.MultiSelect: - case UITypes.SingleSelect: { - await ncMeta.metaDelete( - context.workspace_id, - context.base_id, - MetaTable.COL_SELECT_OPTIONS, - { - fk_column_id: colId, - }, - ); + case UITypes.MultiSelect: + case UITypes.SingleSelect: { + await ncMeta.metaDelete( + context.workspace_id, + context.base_id, + MetaTable.COL_SELECT_OPTIONS, + { + fk_column_id: colId, + }, + ); - await NocoCache.deepDel( - `${CacheScope.COL_SELECT_OPTION}:${colId}:list`, - CacheDelDirection.PARENT_TO_CHILD, - ); - break; + await NocoCache.deepDel( + `${CacheScope.COL_SELECT_OPTION}:${colId}:list`, + CacheDelDirection.PARENT_TO_CHILD, + ); + break; + } } } - const updateObj = extractProps(column, [ 'column_name', 'title', @@ -1362,7 +1374,7 @@ export default class Column implements ColumnType { ); // insert new col options only if existing colOption meta is deleted - if (insertColOpt) + if (requiredColAvail) await this.insertColOption(context, column, colId, ncMeta); // on column update, delete any optimised single query cache From 5e0968d121ba76e924a7fce7162d0873d54e4bc8 Mon Sep 17 00:00:00 2001 From: Pranav C Date: Fri, 15 Nov 2024 10:52:01 +0000 Subject: [PATCH 4/8] fix: handle insert when missing primary key --- packages/nocodb/src/db/BaseModelSqlv2.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/nocodb/src/db/BaseModelSqlv2.ts b/packages/nocodb/src/db/BaseModelSqlv2.ts index 294dcafa68..49760e9046 100644 --- a/packages/nocodb/src/db/BaseModelSqlv2.ts +++ b/packages/nocodb/src/db/BaseModelSqlv2.ts @@ -4973,7 +4973,7 @@ class BaseModelSqlv2 { let response; const query = this.dbDriver(this.tnPath).insert(insertObj); - if (this.isPg || this.isMssql) { + if ((this.isPg || this.isMssql) && this.model.primaryKey) { query.returning( `${this.model.primaryKey.column_name} as ${this.model.primaryKey.id}`, ); @@ -5053,7 +5053,7 @@ class BaseModelSqlv2 { await this.runOps(postInsertOps.map((f) => f(rowId))); - if (rowId !== null && rowId !== undefined) { + if (this.model.primaryKey && rowId !== null && rowId !== undefined) { response = await this.readRecord({ idOrRecord: rowId, validateFormula: false, From c0eeef4eff594978d329941feab4a96109a1ef9e Mon Sep 17 00:00:00 2001 From: Pranav C Date: Fri, 15 Nov 2024 10:52:01 +0000 Subject: [PATCH 5/8] fix: avoid showing red outline for required columns while loading data --- .../nc-gui/components/smartsheet/grid/InfiniteTable.vue | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/packages/nc-gui/components/smartsheet/grid/InfiniteTable.vue b/packages/nc-gui/components/smartsheet/grid/InfiniteTable.vue index 5772b68a4e..304a4e7e6d 100644 --- a/packages/nc-gui/components/smartsheet/grid/InfiniteTable.vue +++ b/packages/nc-gui/components/smartsheet/grid/InfiniteTable.vue @@ -2136,7 +2136,8 @@ watch( 'active-cell !after:h-[calc(100%-2px)]': (activeCell.row === row.rowMeta.rowIndex && activeCell.col === 0) || (selectedRange._start?.row === row.rowMeta.rowIndex && selectedRange._start?.col === 0), - 'nc-required-cell': cellMeta[index]?.[0]?.isColumnRequiredAndNull && !isPublicView, + 'nc-required-cell': + !row.rowMeta?.isLoading && cellMeta[index]?.[0]?.isColumnRequiredAndNull && !isPublicView, 'align-middle': !rowHeightEnum || rowHeightEnum === 1, 'align-top': rowHeightEnum && rowHeightEnum !== 1, 'filling': fillRangeMap[`${row.rowMeta.rowIndex}-0`], @@ -2207,7 +2208,8 @@ watch( 'active-cell': (activeCell.row === row.rowMeta.rowIndex && activeCell.col === colIndex) || (selectedRange._start?.row === row.rowMeta.rowIndex && selectedRange._start?.col === colIndex), - 'nc-required-cell': cellMeta[index][colIndex].isColumnRequiredAndNull && !isPublicView, + 'nc-required-cell': + !row.rowMeta?.isLoading && cellMeta[index][colIndex].isColumnRequiredAndNull && !isPublicView, 'align-middle': !rowHeightEnum || rowHeightEnum === 1, 'align-top': rowHeightEnum && rowHeightEnum !== 1, 'filling': fillRangeMap[`${row.rowMeta.rowIndex}-${colIndex}`], From 2146f93761fd88ff808cdaa731b375c1b8c51942 Mon Sep 17 00:00:00 2001 From: Pranav C Date: Fri, 15 Nov 2024 10:52:01 +0000 Subject: [PATCH 6/8] fix: add missing await and treat form submit success only if it returns valid response --- packages/nc-gui/components/smartsheet/Form.vue | 6 ++++-- .../components/virtual-cell/components/UnLinkedItems.vue | 2 +- packages/nc-gui/utils/dataUtils.ts | 1 + 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/packages/nc-gui/components/smartsheet/Form.vue b/packages/nc-gui/components/smartsheet/Form.vue index c6d610e3ff..73c69c3c5a 100644 --- a/packages/nc-gui/components/smartsheet/Form.vue +++ b/packages/nc-gui/components/smartsheet/Form.vue @@ -339,13 +339,15 @@ async function submitForm() { } } - await insertRow({ + const res = await insertRow({ row: { ...formState.value, ...state.value }, oldRow: {}, rowMeta: { new: true }, }) - submitted.value = true + if (res) { + submitted.value = true + } } async function clearForm() { diff --git a/packages/nc-gui/components/virtual-cell/components/UnLinkedItems.vue b/packages/nc-gui/components/virtual-cell/components/UnLinkedItems.vue index 1486d85348..9b6f1e5dfb 100644 --- a/packages/nc-gui/components/virtual-cell/components/UnLinkedItems.vue +++ b/packages/nc-gui/components/virtual-cell/components/UnLinkedItems.vue @@ -66,7 +66,7 @@ const relation = computed(() => { const linkRow = async (row: Record, id: number) => { if (isNew.value) { - addLTARRef(row, injectedColumn?.value as ColumnType) + await addLTARRef(row, injectedColumn?.value as ColumnType) if (relation.value === 'oo' || relation.value === 'bt') { isChildrenExcludedListLinked.value.forEach((isLinked, idx) => { if (isLinked) { diff --git a/packages/nc-gui/utils/dataUtils.ts b/packages/nc-gui/utils/dataUtils.ts index a365d13cfe..5628f492d0 100644 --- a/packages/nc-gui/utils/dataUtils.ts +++ b/packages/nc-gui/utils/dataUtils.ts @@ -81,6 +81,7 @@ export async function populateInsertObject({ throwError?: boolean undo?: boolean }) { + debugger const missingRequiredColumns = new Set() const insertObj = await meta.columns?.reduce(async (_o: Promise, col) => { const o = await _o From d4639c379baddf7fa816ebac7403992e88f982f1 Mon Sep 17 00:00:00 2001 From: Pranav C Date: Fri, 15 Nov 2024 10:52:01 +0000 Subject: [PATCH 7/8] fix: while populating insert object check row for nested object --- packages/nc-gui/components/general/Gift.vue | 2 +- packages/nc-gui/utils/dataUtils.ts | 9 ++++----- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/packages/nc-gui/components/general/Gift.vue b/packages/nc-gui/components/general/Gift.vue index 516afc6f9b..87ba937606 100644 --- a/packages/nc-gui/components/general/Gift.vue +++ b/packages/nc-gui/components/general/Gift.vue @@ -59,7 +59,7 @@ const closeAndShowAgain = () => {

Gifts Unlocked!

-
We are giving away $100 worth of amazon coupons to our pro open source users!
+
We are giving away $50 worth of amazon coupons to our pro open source users!
diff --git a/packages/nc-gui/utils/dataUtils.ts b/packages/nc-gui/utils/dataUtils.ts index 5628f492d0..2fa8fe2cce 100644 --- a/packages/nc-gui/utils/dataUtils.ts +++ b/packages/nc-gui/utils/dataUtils.ts @@ -81,7 +81,6 @@ export async function populateInsertObject({ throwError?: boolean undo?: boolean }) { - debugger const missingRequiredColumns = new Set() const insertObj = await meta.columns?.reduce(async (_o: Promise, col) => { const o = await _o @@ -92,14 +91,14 @@ export async function populateInsertObject({ col.uidt === UITypes.LinkToAnotherRecord && (col.colOptions).type === RelationTypes.BELONGS_TO ) { - if (ltarState[col.title!]) { + if (ltarState[col.title!] || row[col.title!]) { + const ltarVal = ltarState[col.title!] || row[col.title!] const colOpt = col.colOptions const childCol = meta.columns!.find((c) => colOpt.fk_child_column_id === c.id) const relatedTableMeta = (await getMeta(colOpt.fk_related_model_id!)) as TableType if (relatedTableMeta && childCol) { - o[childCol.title!] = - ltarState[col.title!][relatedTableMeta!.columns!.find((c) => c.id === colOpt.fk_parent_column_id)!.title!] - missingRequiredColumns.delete(childCol.title) + o[childCol.title!] = ltarVal[relatedTableMeta!.columns!.find((c) => c.id === colOpt.fk_parent_column_id)!.title!] + if (o[childCol.title!] !== null && o[childCol.title!] !== undefined) missingRequiredColumns.delete(childCol.title) } } } From 6d56193307e3c9f5949ab9e1cd5d2e1dc96c2448 Mon Sep 17 00:00:00 2001 From: Pranav C Date: Fri, 15 Nov 2024 22:36:11 +0530 Subject: [PATCH 8/8] fix: token refresh failed handle it Signed-off-by: Pranav C --- packages/nc-gui/composables/useApi/interceptors.ts | 8 ++++++++ packages/nc-gui/composables/useGlobal/types.ts | 6 +++++- packages/nocodb-sdk/src/lib/Api.ts | 4 ++++ 3 files changed, 17 insertions(+), 1 deletion(-) diff --git a/packages/nc-gui/composables/useApi/interceptors.ts b/packages/nc-gui/composables/useApi/interceptors.ts index b4e9914a52..0fde440f8a 100644 --- a/packages/nc-gui/composables/useApi/interceptors.ts +++ b/packages/nc-gui/composables/useApi/interceptors.ts @@ -67,6 +67,14 @@ export function addAxiosInterceptors(api: Api) { skipLogout: true, }) + if (!token) { + await state.signOut({ + redirectToSignin: !isSharedPage, + skipApiCall: true, + }) + return Promise.reject(error) + } + const config = error.config config.headers['xc-auth'] = token diff --git a/packages/nc-gui/composables/useGlobal/types.ts b/packages/nc-gui/composables/useGlobal/types.ts index 1ad3975705..8696bd0475 100644 --- a/packages/nc-gui/composables/useGlobal/types.ts +++ b/packages/nc-gui/composables/useGlobal/types.ts @@ -92,7 +92,11 @@ export interface SignOutParams { export interface Actions { signOut: (signOutParams?: SignOutParams) => Promise signIn: (token: string, keepProps?: boolean) => void - refreshToken: (params: { axiosInstance?: AxiosInstance; skipLogout?: boolean; cognitoOnly?: boolean }) => Promise + refreshToken: (params: { + axiosInstance?: AxiosInstance + skipLogout?: boolean + cognitoOnly?: boolean + }) => Promise loadAppInfo: () => void setIsMobileMode: (isMobileMode: boolean) => void navigateToProject: (params: { workspaceId?: string; baseId?: string; type?: NcProjectType; query?: any }) => void diff --git a/packages/nocodb-sdk/src/lib/Api.ts b/packages/nocodb-sdk/src/lib/Api.ts index 53368059b2..f77572d6d5 100644 --- a/packages/nocodb-sdk/src/lib/Api.ts +++ b/packages/nocodb-sdk/src/lib/Api.ts @@ -2846,6 +2846,8 @@ export interface ViewType { | MapType | CalendarType | (FormType & GalleryType & GridType & KanbanType & MapType & CalendarType); + /** ID of view owner user */ + owned_by?: IdType; } /** @@ -2918,6 +2920,8 @@ export interface ViewUpdateReqType { order?: number; /** Should this view show system fields? */ show_system_fields?: BoolType; + /** ID of view owner user */ + owned_by?: IdType; } /**