From 4321fa7f28a0699e96f14787d6cacf301f0e58e8 Mon Sep 17 00:00:00 2001 From: Pranav C Date: Mon, 22 Jan 2024 07:33:56 +0000 Subject: [PATCH 01/18] refactor: bulk meta insert --- packages/nocodb/src/cache/RedisCacheMgr.ts | 2 +- packages/nocodb/src/meta/meta.service.ts | 19 +- packages/nocodb/src/models/Column.ts | 226 +++++++++++++++ packages/nocodb/src/models/Model.ts | 47 +-- packages/nocodb/src/models/View.ts | 267 ++++++++++++++++++ packages/nocodb/src/services/forms.service.ts | 43 ++- .../nocodb/src/services/galleries.service.ts | 38 ++- packages/nocodb/src/services/grids.service.ts | 38 ++- .../nocodb/src/services/kanbans.service.ts | 38 ++- packages/nocodb/src/services/maps.service.ts | 38 ++- 10 files changed, 691 insertions(+), 65 deletions(-) diff --git a/packages/nocodb/src/cache/RedisCacheMgr.ts b/packages/nocodb/src/cache/RedisCacheMgr.ts index 882eaedb9e..c7bfe94fb0 100644 --- a/packages/nocodb/src/cache/RedisCacheMgr.ts +++ b/packages/nocodb/src/cache/RedisCacheMgr.ts @@ -23,7 +23,7 @@ export default class RedisCacheMgr extends CacheMgr { process.env.NC_CLOUD !== 'true' ) { // flush the existing db with selected key (Default: 0) - this.client.flushdb(); + // this.client.flushdb(); } // TODO(cache): fetch orgs once it's implemented diff --git a/packages/nocodb/src/meta/meta.service.ts b/packages/nocodb/src/meta/meta.service.ts index 40548db54d..03c7ce18e4 100644 --- a/packages/nocodb/src/meta/meta.service.ts +++ b/packages/nocodb/src/meta/meta.service.ts @@ -118,26 +118,31 @@ export class MetaService { }); return insertObj; } - public async bulkMetaInsert( base_id: string, source_id: string, target: string, - data: any[], + data: any | any[], ignoreIdGeneration?: boolean, ): Promise { const insertObj = []; const at = this.now(); - for (const d of data) { + + const commonProps: Record = { + created_at: at, + updated_at: at, + }; + + if (source_id !== null) commonProps.source_id = source_id; + if (base_id !== null) commonProps.base_id = base_id; + + for (const d of Array.isArray(data) ? data : [data]) { const id = d?.id || (await this.genNanoid(target)); const tempObj = { ...d, ...(ignoreIdGeneration ? {} : { id }), - created_at: at, - updated_at: at, + ...commonProps, }; - if (source_id !== null) tempObj.source_id = source_id; - if (base_id !== null) tempObj.base_id = base_id; insertObj.push(tempObj); } diff --git a/packages/nocodb/src/models/Column.ts b/packages/nocodb/src/models/Column.ts index 35c2a49f1f..12273b278c 100644 --- a/packages/nocodb/src/models/Column.ts +++ b/packages/nocodb/src/models/Column.ts @@ -1330,4 +1330,230 @@ export default class Column implements ColumnType { colId, ); } + + static async bulkInsert( + param: { + columns: Column[]; + fk_model_id: any; + source_id: string; + base_id: string; + }, + ncMeta: MetaService = Noco.ncMeta, + ) { + const extractedColumnMetas = []; + + // add fk_model_id + for (const column of param.columns) { + extractedColumnMetas.push({ + ...extractProps(column as any, [ + 'id', + 'fk_model_id', + 'column_name', + 'title', + 'uidt', + 'dt', + 'np', + 'ns', + 'clen', + 'cop', + 'pk', + 'rqd', + 'un', + 'ct', + 'ai', + 'unique', + 'cdf', + 'cc', + 'csn', + 'dtx', + 'dtxp', + 'dtxs', + 'au', + 'pv', + 'order', + 'base_id', + 'source_id', + 'system', + 'meta', + ]), + fk_model_id: param.fk_model_id, + }); + } + + // bulk insert columns + const columns = await ncMeta.bulkMetaInsert( + null, + null, + MetaTable.COLUMNS, + extractedColumnMetas, + ); + + // insert column options if any + // for (const column of columns) { + await Column.bulkInsertColOption(columns, ncMeta); + // } + + return columns; + } + + private static async bulkInsertColOption( + columns: (Partial & { source_id?: string; [p: string]: any })[], + ncMeta = Noco.ncMeta, + ) { + const insertGroups = new Map[]>(); + + for (const column of columns) { + let insertArr = insertGroups.get(column.uidt=== UITypes.MultiSelect ? UITypes.SingleSelect : column.uidt ); + if (!insertArr) { + insertGroups.set(column.uidt, (insertArr = [])); + } + switch (column.uidt || column.ui_data_type) { + case UITypes.Lookup: + // LookupColumn.insert() + insertArr.push({ + fk_column_id: column.id, + fk_relation_column_id: column.fk_relation_column_id, + fk_lookup_column_id: column.fk_lookup_column_id, + }); + break; + + case UITypes.Rollup: { + insertArr.push({ + fk_column_id: column.id, + fk_relation_column_id: column.fk_relation_column_id, + + fk_rollup_column_id: column.fk_rollup_column_id, + rollup_function: column.rollup_function, + }); + break; + } + case UITypes.Links: + case UITypes.LinkToAnotherRecord: { + insertArr.push({ + fk_column_id: column.id, + + // ref_db_alias + type: column.type, + // db_type: + + fk_child_column_id: column.fk_child_column_id, + fk_parent_column_id: column.fk_parent_column_id, + + fk_mm_model_id: column.fk_mm_model_id, + fk_mm_child_column_id: column.fk_mm_child_column_id, + fk_mm_parent_column_id: column.fk_mm_parent_column_id, + + ur: column.ur, + dr: column.dr, + + fk_index_name: column.fk_index_name, + fk_related_model_id: column.fk_related_model_id, + + virtual: column.virtual, + }); + break; + } + case UITypes.QrCode: { + insertArr.push( + { + fk_column_id: column.id, + fk_qr_value_column_id: column.fk_qr_value_column_id, + }, + ncMeta, + ); + break; + } + case UITypes.Barcode: { + insertArr.push({ + fk_column_id: column.id, + fk_barcode_value_column_id: column.fk_barcode_value_column_id, + barcode_format: column.barcode_format, + }); + break; + } + case UITypes.Formula: { + insertArr.push({ + fk_column_id: column.id, + formula: column.formula, + formula_raw: column.formula_raw, + parsed_tree: column.parsed_tree, + }); + break; + } + case UITypes.MultiSelect: { + if (!column.colOptions?.options) { + for (const [i, option] of column.dtxp?.split(',').entries() || + [].entries()) { + insertArr.push({ + fk_column_id: column.id, + title: option.replace(/^'/, '').replace(/'$/, ''), + order: i + 1, + color: selectColors[i % selectColors.length], + }); + } + } else { + for (const [i, option] of column.colOptions.options.entries() || + [].entries()) { + // Trim end of enum/set + if (column.dt === 'enum' || column.dt === 'set') { + option.title = option.title.trimEnd(); + } + insertArr.push({ + color: selectColors[i % selectColors.length], // in case color is not provided + ...option, + fk_column_id: column.id, + order: i + 1, + }); + } + } + break; + } + case UITypes.SingleSelect: { + if (!column.colOptions?.options) { + for (const [i, option] of column.dtxp?.split(',').entries() || + [].entries()) { + insertArr.push({ + fk_column_id: column.id, + title: option.replace(/^'/, '').replace(/'$/, ''), + order: i + 1, + color: selectColors[i % selectColors.length], + }); + } + } else { + for (const [i, option] of column.colOptions.options.entries() || + [].entries()) { + // Trim end of enum/set + if (column.dt === 'enum' || column.dt === 'set') { + option.title = option.title.trimEnd(); + } + insertArr.push({ + color: selectColors[i % selectColors.length], // in case color is not provided + ...option, + fk_column_id: column.id, + order: i + 1, + }); + } + } + break; + } + } + } + + // bulk insert column options + for (const group of insertGroups.keys()) { + switch (group) { + case UITypes.SingleSelect: + case UITypes.MultiSelect: + await ncMeta.bulkMetaInsert( + null, + null, + MetaTable.COL_SELECT_OPTIONS, + insertGroups.get(group), + ); + break; + + // todo: handle rest of the cases + } + } + } } diff --git a/packages/nocodb/src/models/Model.ts b/packages/nocodb/src/models/Model.ts index 5f4243f6ae..39db675d08 100644 --- a/packages/nocodb/src/models/Model.ts +++ b/packages/nocodb/src/models/Model.ts @@ -147,37 +147,50 @@ export default class Model implements TableType { insertObj, ); - const view = await View.insert( + const insertedColumns = await Column.bulkInsert( + { + columns: (model?.columns || []) as Column[], + fk_model_id: id, + source_id: sourceId, + base_id: baseId, + }, + ncMeta, + ); + + await View.insertMetaOnly( { fk_model_id: id, title: model.title || model.table_name, is_default: true, type: ViewTypes.GRID, + base_id: baseId, + source_id: sourceId, + }, + { + getColumns: () => insertedColumns, }, ncMeta, ); - for (const column of model?.columns || []) { - await Column.insert({ ...column, fk_model_id: id, view } as any, ncMeta); - } + const model = await this.getWithInfo({ id }, ncMeta); - return this.getWithInfo({ id }, ncMeta).then(async (model) => { - if (sourceId) { - await NocoCache.appendToList( - CacheScope.MODEL, - [baseId, sourceId], - `${CacheScope.MODEL}:${id}`, - ); - } - // cater cases where sourceId is not required - // e.g. xcVisibilityMetaGet + // append to model list since model list cache will be there already + if (sourceId) { await NocoCache.appendToList( CacheScope.MODEL, - [baseId], + [baseId, sourceId], `${CacheScope.MODEL}:${id}`, ); - return model; - }); + } + // cater cases where sourceId is not required + // e.g. xcVisibilityMetaGet + await NocoCache.appendToList( + CacheScope.MODEL, + [baseId], + `${CacheScope.MODEL}:${id}`, + ); + + return model; } public static async list( diff --git a/packages/nocodb/src/models/View.ts b/packages/nocodb/src/models/View.ts index 1aa879cb7b..7b54e78687 100644 --- a/packages/nocodb/src/models/View.ts +++ b/packages/nocodb/src/models/View.ts @@ -1539,4 +1539,271 @@ export default class View implements ViewType { await NocoCache.del(deleteKeys); } + + static async bulkColumnInsertToViews( + { + columns, + viewColumns, + }: { + columns?: ({ + order?: number; + show?; + } & Column)[]; + viewColumns?: ( + | GridViewColumn + | GalleryViewColumn + | FormViewColumn + | KanbanViewColumn + | MapViewColumn + )[]; + }, + view: View, + ncMeta = Noco.ncMeta, + ) { + const insertObjs = []; + + if (viewColumns) { + for (let i = 0; i < viewColumns.length; i++) { + const column = columns[i]; + + insertObjs.push({ + ...column, + fk_view_id: view.id, + base_id: view.base_id, + source_id: view.source_id, + }); + } + } else { + if (!columns) { + columns = await Column.list({ fk_model_id: view.fk_model_id }); + } + + for (let i = 0; i < columns.length; i++) { + const column = columns[i]; + + insertObjs.push({ + fk_column_id: column.id, + order: column.order ?? i + 1, + show: column.show ?? true, + fk_view_id: view.id, + base_id: view.base_id, + source_id: view.source_id, + }); + } + } + + switch (view.type) { + case ViewTypes.GRID: + await ncMeta.bulkMetaInsert( + null, + null, + MetaTable.GRID_VIEW_COLUMNS, + insertObjs, + ); + break; + case ViewTypes.GALLERY: + await ncMeta.bulkMetaInsert( + null, + null, + MetaTable.GALLERY_VIEW_COLUMNS, + insertObjs, + ); + break; + case ViewTypes.MAP: + await ncMeta.bulkMetaInsert( + null, + null, + MetaTable.MAP_VIEW_COLUMNS, + insertObjs, + ); + break; + case ViewTypes.KANBAN: + await ncMeta.bulkMetaInsert( + null, + null, + MetaTable.KANBAN_VIEW_COLUMNS, + insertObjs, + ); + break; + } + } + + static async insertMetaOnly( + view: Partial & + Partial & { + copy_from_id?: string; + fk_grp_col_id?: string; + }, + model: { + getColumns: () => Promise; + }, + ncMeta = Noco.ncMeta, + ) { + const insertObj = extractProps(view, [ + 'id', + 'title', + 'is_default', + 'type', + 'fk_model_id', + 'base_id', + 'source_id', + 'meta', + ]); + + if (!insertObj.order) { + // get order value + insertObj.order = await ncMeta.metaGetNextOrder(MetaTable.VIEWS, { + fk_model_id: view.fk_model_id, + }); + } + + insertObj.show = true; + + if (!insertObj.meta) { + insertObj.meta = {}; + } + + insertObj.meta = stringifyMetaProp(insertObj); + + const copyFromView = + view.copy_from_id && (await View.get(view.copy_from_id, ncMeta)); + + // get base and base id if missing + if (!(view.base_id && view.source_id)) { + const model = await Model.getByIdOrName({ id: view.fk_model_id }, ncMeta); + insertObj.base_id = model.base_id; + insertObj.source_id = model.source_id; + } + + const insertedView = await ncMeta.metaInsert2( + null, + null, + MetaTable.VIEWS, + insertObj, + ); + + const { id: view_id } = insertedView; + + // insert view metadata based on view type + switch (view.type) { + case ViewTypes.GRID: + await GridView.insert( + { + ...((copyFromView?.view as GridView) || {}), + ...(view as GridView), + fk_view_id: view_id, + }, + ncMeta, + ); + break; + case ViewTypes.MAP: + await MapView.insert( + { + ...(view as MapView), + fk_view_id: view_id, + }, + ncMeta, + ); + break; + case ViewTypes.GALLERY: + await GalleryView.insert( + { + ...(copyFromView?.view || {}), + ...view, + fk_view_id: view_id, + }, + ncMeta, + ); + break; + case ViewTypes.FORM: + await FormView.insert( + { + heading: view.title, + ...(copyFromView?.view || {}), + ...view, + fk_view_id: view_id, + }, + ncMeta, + ); + break; + case ViewTypes.KANBAN: + // set grouping field + (view as KanbanView).fk_grp_col_id = view.fk_grp_col_id; + + await KanbanView.insert( + { + ...(copyFromView?.view || {}), + ...view, + fk_view_id: view_id, + }, + ncMeta, + ); + break; + } + + // copy from view + if (copyFromView) { + const sorts = await copyFromView.getSorts(ncMeta); + const filters = await Filter.rootFilterList( + { viewId: copyFromView.id }, + ncMeta, + ); + const viewColumns = await copyFromView.getColumns(ncMeta); + + const sortInsertObjs = []; + const filterInsertObjs = []; + + for (const sort of sorts) { + sortInsertObjs.push({ + ...sort, + fk_view_id: view_id, + id: undefined, + }); + } + + for (const filter of filters) { + const fn = async (filter, parentId: string = null) => { + const generatedId = await ncMeta.genNanoid(MetaTable.FILTER_EXP); + + const { children, ...filterProps } = filter; + + filterInsertObjs.push({ + ...filterProps, + fk_view_id: view_id, + id: generatedId, + fk_parent_id: parentId, + }); + if (filter.is_group) + await Promise.all( + ((await filter.getChildren()) || []).map(async (child) => { + await fn(child, generatedId); + }), + ); + }; + + await fn(filter); + } + + await ncMeta.bulkMetaInsert(null, null, MetaTable.SORT, sortInsertObjs); + + await ncMeta.bulkMetaInsert( + null, + null, + MetaTable.FILTER_EXP, + filterInsertObjs, + true, + ); + + // populate view columns + await View.bulkColumnInsertToViews({ viewColumns }, insertedView); + } else { + // populate view columns + await View.bulkColumnInsertToViews( + { columns: (await model.getColumns()) as any[] }, + insertedView, + ); + } + + return insertedView; + } } diff --git a/packages/nocodb/src/services/forms.service.ts b/packages/nocodb/src/services/forms.service.ts index 31cdb7ec21..f13cb7c655 100644 --- a/packages/nocodb/src/services/forms.service.ts +++ b/packages/nocodb/src/services/forms.service.ts @@ -9,7 +9,9 @@ import type { NcRequest } from '~/interface/config'; import { AppHooksService } from '~/services/app-hooks/app-hooks.service'; import { validatePayload } from '~/helpers'; import { NcError } from '~/helpers/catchError'; -import { FormView, View } from '~/models'; +import { FormView, Model, View } from '~/models'; +import NocoCache from '~/cache/NocoCache'; +import { CacheScope } from '~/utils/globals'; @Injectable() export class FormsService { @@ -31,22 +33,39 @@ export class FormsService { param.body, ); - const view = await View.insert({ - ...param.body, - // todo: sanitize - fk_model_id: param.tableId, - type: ViewTypes.FORM, - }); + // const view = await View.insert({ + // ...param.body, + // // todo: sanitize + // fk_model_id: param.tableId, + // type: ViewTypes.FORM, + // }); - this.appHooksService.emit(AppEvents.VIEW_CREATE, { - view, - showAs: 'form', - req: param.req, - }); + const model = await Model.get(param.tableId); + + const { id } = await View.insertMetaOnly( + { + ...param.body, + // todo: sanitize + fk_model_id: param.tableId, + type: ViewTypes.FORM, + base_id: model.base_id, + source_id: model.source_id, + }, + model, + ); + + // populate cache and add to list since the list cache already exist + const view = await View.get(id); + await NocoCache.appendToList( + CacheScope.VIEW, + [view.fk_model_id], + `${CacheScope.VIEW}:${id}`, + ); this.appHooksService.emit(AppEvents.VIEW_CREATE, { user: param.user, view, + showAs: 'form', req: param.req, }); diff --git a/packages/nocodb/src/services/galleries.service.ts b/packages/nocodb/src/services/galleries.service.ts index 32406ca4c4..98d7073d12 100644 --- a/packages/nocodb/src/services/galleries.service.ts +++ b/packages/nocodb/src/services/galleries.service.ts @@ -9,7 +9,9 @@ import type { NcRequest } from '~/interface/config'; import { AppHooksService } from '~/services/app-hooks/app-hooks.service'; import { validatePayload } from '~/helpers'; import { NcError } from '~/helpers/catchError'; -import { GalleryView, View } from '~/models'; +import { GalleryView, Model, View } from '~/models'; +import NocoCache from '~/cache/NocoCache'; +import { CacheScope } from '~/utils/globals'; @Injectable() export class GalleriesService { @@ -30,13 +32,35 @@ export class GalleriesService { 'swagger.json#/components/schemas/ViewCreateReq', param.gallery, ); + // + // const view = await View.insert({ + // ...param.gallery, + // // todo: sanitize + // fk_model_id: param.tableId, + // type: ViewTypes.GALLERY, + // }); - const view = await View.insert({ - ...param.gallery, - // todo: sanitize - fk_model_id: param.tableId, - type: ViewTypes.GALLERY, - }); + const model = await Model.get(param.tableId); + + const { id } = await View.insertMetaOnly( + { + ...param.gallery, + // todo: sanitize + fk_model_id: param.tableId, + type: ViewTypes.GALLERY, + base_id: model.base_id, + source_id: model.source_id, + }, + model, + ); + + // populate cache and add to list since the list cache already exist + const view = await View.get(id); + await NocoCache.appendToList( + CacheScope.VIEW, + [view.fk_model_id], + `${CacheScope.VIEW}:${id}`, + ); this.appHooksService.emit(AppEvents.VIEW_CREATE, { view, diff --git a/packages/nocodb/src/services/grids.service.ts b/packages/nocodb/src/services/grids.service.ts index 9c616e209d..171cfd7f10 100644 --- a/packages/nocodb/src/services/grids.service.ts +++ b/packages/nocodb/src/services/grids.service.ts @@ -5,7 +5,9 @@ import type { NcRequest } from '~/interface/config'; import { AppHooksService } from '~/services/app-hooks/app-hooks.service'; import { validatePayload } from '~/helpers'; import { NcError } from '~/helpers/catchError'; -import { GridView, View } from '~/models'; +import { Column, GridView, Model, View } from '~/models'; +import NocoCache from '~/cache/NocoCache'; +import { CacheScope } from '~/utils/globals'; @Injectable() export class GridsService { @@ -20,13 +22,35 @@ export class GridsService { 'swagger.json#/components/schemas/ViewCreateReq', param.grid, ); + // + // const view = await View.insert({ + // ...param.grid, + // // todo: sanitize + // fk_model_id: param.tableId, + // type: ViewTypes.GRID, + // }); - const view = await View.insert({ - ...param.grid, - // todo: sanitize - fk_model_id: param.tableId, - type: ViewTypes.GRID, - }); + const model = await Model.get(param.tableId); + + const { id } = await View.insertMetaOnly( + { + ...param.grid, + // todo: sanitize + fk_model_id: param.tableId, + type: ViewTypes.GRID, + base_id: model.base_id, + source_id: model.source_id, + }, + model, + ); + + // populate cache and add to list since the list cache already exist + const view = await View.get(id); + await NocoCache.appendToList( + CacheScope.VIEW, + [view.fk_model_id], + `${CacheScope.VIEW}:${id}`, + ); this.appHooksService.emit(AppEvents.VIEW_CREATE, { view, diff --git a/packages/nocodb/src/services/kanbans.service.ts b/packages/nocodb/src/services/kanbans.service.ts index c21880792d..06bc83b4d4 100644 --- a/packages/nocodb/src/services/kanbans.service.ts +++ b/packages/nocodb/src/services/kanbans.service.ts @@ -9,7 +9,9 @@ import type { NcRequest } from '~/interface/config'; import { AppHooksService } from '~/services/app-hooks/app-hooks.service'; import { validatePayload } from '~/helpers'; import { NcError } from '~/helpers/catchError'; -import { KanbanView, View } from '~/models'; +import { KanbanView, Model, View } from '~/models' +import NocoCache from '~/cache/NocoCache' +import { CacheScope } from '~/utils/globals' @Injectable() export class KanbansService { @@ -30,12 +32,34 @@ export class KanbansService { param.kanban, ); - const view = await View.insert({ - ...param.kanban, - // todo: sanitize - fk_model_id: param.tableId, - type: ViewTypes.KANBAN, - }); + // const view = await View.insert({ + // ...param.kanban, + // // todo: sanitize + // fk_model_id: param.tableId, + // type: ViewTypes.KANBAN, + // }); + + const model = await Model.get(param.tableId); + + const { id } = await View.insertMetaOnly( + { + ...param.kanban, + // todo: sanitize + fk_model_id: param.tableId, + type: ViewTypes.KANBAN, + base_id: model.base_id, + source_id: model.source_id, + }, + model, + ); + + // populate cache and add to list since the list cache already exist + const view = await View.get(id); + await NocoCache.appendToList( + CacheScope.VIEW, + [view.fk_model_id], + `${CacheScope.VIEW}:${id}`, + ); this.appHooksService.emit(AppEvents.VIEW_CREATE, { view, diff --git a/packages/nocodb/src/services/maps.service.ts b/packages/nocodb/src/services/maps.service.ts index 1ea51a41f3..b65779fbf6 100644 --- a/packages/nocodb/src/services/maps.service.ts +++ b/packages/nocodb/src/services/maps.service.ts @@ -5,7 +5,9 @@ import type { NcRequest } from '~/interface/config'; import { AppHooksService } from '~/services/app-hooks/app-hooks.service'; import { validatePayload } from '~/helpers'; import { NcError } from '~/helpers/catchError'; -import { MapView, View } from '~/models'; +import { MapView, Model, View } from '~/models'; +import { CacheScope } from '~/utils/globals'; +import NocoCache from '~/cache/NocoCache'; @Injectable() export class MapsService { @@ -25,12 +27,34 @@ export class MapsService { 'swagger.json#/components/schemas/ViewCreateReq', param.map, ); - const view = await View.insert({ - ...param.map, - // todo: sanitize - fk_model_id: param.tableId, - type: ViewTypes.MAP, - }); + // const view = await View.insert({ + // ...param.map, + // // todo: sanitize + // fk_model_id: param.tableId, + // type: ViewTypes.MAP, + // }); + + const model = await Model.get(param.tableId); + + const { id } = await View.insertMetaOnly( + { + ...param.map, + // todo: sanitize + fk_model_id: param.tableId, + type: ViewTypes.MAP, + base_id: model.base_id, + source_id: model.source_id, + }, + model, + ); + + // populate cache and add to list since the list cache already exist + const view = await View.get(id); + await NocoCache.appendToList( + CacheScope.VIEW, + [view.fk_model_id], + `${CacheScope.VIEW}:${id}`, + ); this.appHooksService.emit(AppEvents.VIEW_CREATE, { view, From 52cc9e98106376574d4dd5d6a708edf7fa1107db Mon Sep 17 00:00:00 2001 From: Pranav C Date: Mon, 22 Jan 2024 07:33:56 +0000 Subject: [PATCH 02/18] fix: corrections --- packages/nocodb/src/models/Column.ts | 2 +- packages/nocodb/src/models/Model.ts | 4 ++-- packages/nocodb/src/services/grids.service.ts | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/nocodb/src/models/Column.ts b/packages/nocodb/src/models/Column.ts index 12273b278c..b97295108e 100644 --- a/packages/nocodb/src/models/Column.ts +++ b/packages/nocodb/src/models/Column.ts @@ -1338,7 +1338,7 @@ export default class Column implements ColumnType { source_id: string; base_id: string; }, - ncMeta: MetaService = Noco.ncMeta, + ncMeta = Noco.ncMeta, ) { const extractedColumnMetas = []; diff --git a/packages/nocodb/src/models/Model.ts b/packages/nocodb/src/models/Model.ts index 39db675d08..81a12c0e34 100644 --- a/packages/nocodb/src/models/Model.ts +++ b/packages/nocodb/src/models/Model.ts @@ -172,7 +172,7 @@ export default class Model implements TableType { ncMeta, ); - const model = await this.getWithInfo({ id }, ncMeta); + const modelRes = await this.getWithInfo({ id }, ncMeta); // append to model list since model list cache will be there already if (sourceId) { @@ -190,7 +190,7 @@ export default class Model implements TableType { `${CacheScope.MODEL}:${id}`, ); - return model; + return modelRes; } public static async list( diff --git a/packages/nocodb/src/services/grids.service.ts b/packages/nocodb/src/services/grids.service.ts index 171cfd7f10..759d06c780 100644 --- a/packages/nocodb/src/services/grids.service.ts +++ b/packages/nocodb/src/services/grids.service.ts @@ -51,9 +51,9 @@ export class GridsService { [view.fk_model_id], `${CacheScope.VIEW}:${id}`, ); - this.appHooksService.emit(AppEvents.VIEW_CREATE, { view, + showAs: 'grid', req: param.req, }); From c72cf1a37611a6c01cc997f419667217a6c86914 Mon Sep 17 00:00:00 2001 From: Pranav C Date: Mon, 22 Jan 2024 07:33:56 +0000 Subject: [PATCH 03/18] fix: typo correction --- packages/nocodb/src/models/View.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/nocodb/src/models/View.ts b/packages/nocodb/src/models/View.ts index 7b54e78687..71478f805b 100644 --- a/packages/nocodb/src/models/View.ts +++ b/packages/nocodb/src/models/View.ts @@ -1564,7 +1564,7 @@ export default class View implements ViewType { if (viewColumns) { for (let i = 0; i < viewColumns.length; i++) { - const column = columns[i]; + const column = viewColumns[i]; insertObjs.push({ ...column, From ad667b739e261328cfddf35ad0aa90894d27b168 Mon Sep 17 00:00:00 2001 From: Pranav C Date: Mon, 22 Jan 2024 07:33:56 +0000 Subject: [PATCH 04/18] fix: exclude any extra props --- packages/nocodb/src/models/View.ts | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/packages/nocodb/src/models/View.ts b/packages/nocodb/src/models/View.ts index 71478f805b..3a3cbc5b5d 100644 --- a/packages/nocodb/src/models/View.ts +++ b/packages/nocodb/src/models/View.ts @@ -1567,7 +1567,18 @@ export default class View implements ViewType { const column = viewColumns[i]; insertObjs.push({ - ...column, + ...extractProps(column, [ + 'fk_view_id', + 'fk_column_id', + 'show', + 'base_id', + 'source_id', + 'order', + 'width', + 'group_by', + 'group_by_order', + 'group_by_sort', + ]), fk_view_id: view.id, base_id: view.base_id, source_id: view.source_id, From b3288af0a3d9872dc0daf272adec76a3cc162697 Mon Sep 17 00:00:00 2001 From: Pranav C Date: Mon, 22 Jan 2024 07:33:56 +0000 Subject: [PATCH 05/18] fix: view duplicate and creation related issues --- packages/nocodb/src/models/View.ts | 55 +++++++++++++++++++++++++++--- 1 file changed, 51 insertions(+), 4 deletions(-) diff --git a/packages/nocodb/src/models/View.ts b/packages/nocodb/src/models/View.ts index 3a3cbc5b5d..1d5da22539 100644 --- a/packages/nocodb/src/models/View.ts +++ b/packages/nocodb/src/models/View.ts @@ -1544,7 +1544,9 @@ export default class View implements ViewType { { columns, viewColumns, + copyFromView, }: { + copyFromView: View; columns?: ({ order?: number; show?; @@ -1589,13 +1591,58 @@ export default class View implements ViewType { columns = await Column.list({ fk_model_id: view.fk_model_id }); } + let order = 1; + let galleryShowLimit = 0; + let kanbanShowLimit = 0; + for (let i = 0; i < columns.length; i++) { const column = columns[i]; + let show = 'show' in column ? column.show : true; + + if (view.type === ViewTypes.GALLERY) { + const galleryView = await GalleryView.get(view.id, ncMeta); + if ( + column.id === galleryView.fk_cover_image_col_id || + column.pv || + galleryShowLimit < 3 + ) { + show = true; + galleryShowLimit++; + } else { + show = false; + } + } else if (view.type === ViewTypes.KANBAN && !copyFromView) { + const kanbanView = await KanbanView.get(view.id, ncMeta); + if (column.id === kanbanView?.fk_grp_col_id) { + // include grouping field if it exists + show = true; + } else if ( + column.id === kanbanView.fk_cover_image_col_id || + column.pv + ) { + // Show cover image or primary key + show = true; + kanbanShowLimit++; + } else if (kanbanShowLimit < 3 && !isSystemColumn(column)) { + // show at most 3 non-system columns + show = true; + kanbanShowLimit++; + } else { + // other columns will be hidden + show = false; + } + } else if (view.type === ViewTypes.MAP && !copyFromView) { + const mapView = await MapView.get(view.id, ncMeta); + if (column.id === mapView?.fk_geo_data_col_id) { + show = true; + } + } + insertObjs.push({ fk_column_id: column.id, - order: column.order ?? i + 1, - show: column.show ?? true, + order: order++, + show, fk_view_id: view.id, base_id: view.base_id, source_id: view.source_id, @@ -1806,11 +1853,11 @@ export default class View implements ViewType { ); // populate view columns - await View.bulkColumnInsertToViews({ viewColumns }, insertedView); + await View.bulkColumnInsertToViews({ viewColumns, copyFromView }, insertedView); } else { // populate view columns await View.bulkColumnInsertToViews( - { columns: (await model.getColumns()) as any[] }, + { columns: (await model.getColumns()) as any[], copyFromView }, insertedView, ); } From 3a405771c3993d639b2ae0746475b5f89f654ea6 Mon Sep 17 00:00:00 2001 From: Pranav C Date: Mon, 22 Jan 2024 07:33:57 +0000 Subject: [PATCH 06/18] fix: missing formview column insert --- packages/nocodb/src/meta/meta.service.ts | 4 ++++ packages/nocodb/src/models/View.ts | 20 +++++++++++++++++++- 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/packages/nocodb/src/meta/meta.service.ts b/packages/nocodb/src/meta/meta.service.ts index 03c7ce18e4..5074883b7d 100644 --- a/packages/nocodb/src/meta/meta.service.ts +++ b/packages/nocodb/src/meta/meta.service.ts @@ -125,6 +125,10 @@ export class MetaService { data: any | any[], ignoreIdGeneration?: boolean, ): Promise { + if (Array.isArray(data) ? !data.length : !data) { + return []; + } + const insertObj = []; const at = this.now(); diff --git a/packages/nocodb/src/models/View.ts b/packages/nocodb/src/models/View.ts index 1d5da22539..443929a098 100644 --- a/packages/nocodb/src/models/View.ts +++ b/packages/nocodb/src/models/View.ts @@ -1639,6 +1639,8 @@ export default class View implements ViewType { } } + if (isSystemColumn(column)) show = false; + insertObjs.push({ fk_column_id: column.id, order: order++, @@ -1683,6 +1685,14 @@ export default class View implements ViewType { insertObjs, ); break; + case ViewTypes.FORM: + await ncMeta.bulkMetaInsert( + null, + null, + MetaTable.FORM_VIEW_COLUMNS, + insertObjs, + ); + break; } } @@ -1853,7 +1863,10 @@ export default class View implements ViewType { ); // populate view columns - await View.bulkColumnInsertToViews({ viewColumns, copyFromView }, insertedView); + await View.bulkColumnInsertToViews( + { viewColumns, copyFromView }, + insertedView, + ); } else { // populate view columns await View.bulkColumnInsertToViews( @@ -1862,6 +1875,11 @@ export default class View implements ViewType { ); } + await Model.getNonDefaultViewsCountAndReset( + { modelId: view.fk_model_id }, + ncMeta, + ); + return insertedView; } } From fa57ab206502fcb0ff336f76049eb925620c405d Mon Sep 17 00:00:00 2001 From: Pranav C Date: Mon, 22 Jan 2024 07:33:57 +0000 Subject: [PATCH 07/18] fix: avoid including invalid props --- packages/nocodb/src/models/View.ts | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/packages/nocodb/src/models/View.ts b/packages/nocodb/src/models/View.ts index 443929a098..fca8b42985 100644 --- a/packages/nocodb/src/models/View.ts +++ b/packages/nocodb/src/models/View.ts @@ -388,7 +388,13 @@ export default class View implements ViewType { for (const sort of sorts) { await Sort.insert( { - ...sort, + ...extractProps(sort, [ + 'fk_column_id', + 'direction', + 'base_id', + 'source_id', + 'order', + ]), fk_view_id: view_id, id: null, }, @@ -399,7 +405,19 @@ export default class View implements ViewType { for (const filter of filters.children) { await Filter.insert( { - ...filter, + ...extractProps(filter, [ + 'id', + 'fk_column_id', + 'comparison_op', + 'comparison_sub_op', + 'value', + 'fk_parent_id', + 'is_group', + 'logical_op', + 'base_id', + 'source_id', + 'order', + ]), fk_view_id: view_id, id: null, }, From 0e99d04f0c05aa38670af4a606e3af39aeefd512 Mon Sep 17 00:00:00 2001 From: Pranav C Date: Mon, 22 Jan 2024 07:33:57 +0000 Subject: [PATCH 08/18] fix: pre-populate column id --- packages/nocodb/src/models/Column.ts | 17 +++++++++++++++-- packages/nocodb/src/models/Model.ts | 2 +- 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/packages/nocodb/src/models/Column.ts b/packages/nocodb/src/models/Column.ts index b97295108e..900598dcf9 100644 --- a/packages/nocodb/src/models/Column.ts +++ b/packages/nocodb/src/models/Column.ts @@ -1341,9 +1341,16 @@ export default class Column implements ColumnType { ncMeta = Noco.ncMeta, ) { const extractedColumnMetas = []; + const columns = []; // add fk_model_id for (const column of param.columns) { + // pre-populate column meta to use while inserting colOptions + const id = await ncMeta.genNanoid(MetaTable.COLUMNS); + columns.push({ + ...column, + id, + }); extractedColumnMetas.push({ ...extractProps(column as any, [ 'id', @@ -1376,16 +1383,18 @@ export default class Column implements ColumnType { 'system', 'meta', ]), + id, fk_model_id: param.fk_model_id, }); } // bulk insert columns - const columns = await ncMeta.bulkMetaInsert( + await ncMeta.bulkMetaInsert( null, null, MetaTable.COLUMNS, extractedColumnMetas, + true, ); // insert column options if any @@ -1403,7 +1412,11 @@ export default class Column implements ColumnType { const insertGroups = new Map[]>(); for (const column of columns) { - let insertArr = insertGroups.get(column.uidt=== UITypes.MultiSelect ? UITypes.SingleSelect : column.uidt ); + let insertArr = insertGroups.get( + column.uidt === UITypes.MultiSelect + ? UITypes.SingleSelect + : column.uidt, + ); if (!insertArr) { insertGroups.set(column.uidt, (insertArr = [])); } diff --git a/packages/nocodb/src/models/Model.ts b/packages/nocodb/src/models/Model.ts index 81a12c0e34..a00965d40d 100644 --- a/packages/nocodb/src/models/Model.ts +++ b/packages/nocodb/src/models/Model.ts @@ -167,7 +167,7 @@ export default class Model implements TableType { source_id: sourceId, }, { - getColumns: () => insertedColumns, + getColumns: async () => insertedColumns }, ncMeta, ); From e325e69d4bea592df346a64a3dd950a00fb38d06 Mon Sep 17 00:00:00 2001 From: Pranav C Date: Mon, 22 Jan 2024 07:33:57 +0000 Subject: [PATCH 09/18] fix: missing source_id and base_id in bulk column insert --- packages/nocodb/src/models/Column.ts | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/packages/nocodb/src/models/Column.ts b/packages/nocodb/src/models/Column.ts index 900598dcf9..61a58804a3 100644 --- a/packages/nocodb/src/models/Column.ts +++ b/packages/nocodb/src/models/Column.ts @@ -1347,12 +1347,15 @@ export default class Column implements ColumnType { for (const column of param.columns) { // pre-populate column meta to use while inserting colOptions const id = await ncMeta.genNanoid(MetaTable.COLUMNS); - columns.push({ + const colWithId = { ...column, id, - }); - extractedColumnMetas.push({ - ...extractProps(column as any, [ + base_id: param.base_id, + source_id: param.source_id, + fk_model_id: param.fk_model_id, + }; + extractedColumnMetas.push( + extractProps(colWithId as any, [ 'id', 'fk_model_id', 'column_name', @@ -1383,9 +1386,8 @@ export default class Column implements ColumnType { 'system', 'meta', ]), - id, - fk_model_id: param.fk_model_id, - }); + ); + columns.push(colWithId); } // bulk insert columns From 19c179c37892a8b283629fe136fc80cc781279ac Mon Sep 17 00:00:00 2001 From: Pranav C Date: Mon, 22 Jan 2024 07:33:57 +0000 Subject: [PATCH 10/18] fix: exclude invalid props --- packages/nocodb/src/models/Column.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/nocodb/src/models/Column.ts b/packages/nocodb/src/models/Column.ts index 61a58804a3..3e82cb6da8 100644 --- a/packages/nocodb/src/models/Column.ts +++ b/packages/nocodb/src/models/Column.ts @@ -1515,7 +1515,7 @@ export default class Column implements ColumnType { } insertArr.push({ color: selectColors[i % selectColors.length], // in case color is not provided - ...option, + ...extractProps(option, ['title', 'fk_column_id', 'color']), fk_column_id: column.id, order: i + 1, }); @@ -1543,7 +1543,7 @@ export default class Column implements ColumnType { } insertArr.push({ color: selectColors[i % selectColors.length], // in case color is not provided - ...option, + ...extractProps(option, ['title', 'fk_column_id', 'color']), fk_column_id: column.id, order: i + 1, }); From ad1d7707b4691001e4eaf48a59dbb5468ff305be Mon Sep 17 00:00:00 2001 From: Pranav C Date: Mon, 22 Jan 2024 07:33:57 +0000 Subject: [PATCH 11/18] fix: exclude invalid props --- packages/nocodb/src/models/View.ts | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/packages/nocodb/src/models/View.ts b/packages/nocodb/src/models/View.ts index fca8b42985..32c50f19b0 100644 --- a/packages/nocodb/src/models/View.ts +++ b/packages/nocodb/src/models/View.ts @@ -1841,7 +1841,12 @@ export default class View implements ViewType { for (const sort of sorts) { sortInsertObjs.push({ - ...sort, + ...extractProps(sort, [ + 'fk_column_id', + 'direction', + 'base_id', + 'source_id', + ]), fk_view_id: view_id, id: undefined, }); @@ -1851,10 +1856,16 @@ export default class View implements ViewType { const fn = async (filter, parentId: string = null) => { const generatedId = await ncMeta.genNanoid(MetaTable.FILTER_EXP); - const { children, ...filterProps } = filter; - filterInsertObjs.push({ - ...filterProps, + ...extractProps(filter, [ + 'fk_column_id', + 'comparison_op', + 'comparison_sub_op', + 'value', + 'fk_parent_id', + 'is_group', + 'logical_op', + ]), fk_view_id: view_id, id: generatedId, fk_parent_id: parentId, From aa5f01df7b1286dd580d186576e8d1e295ea5285 Mon Sep 17 00:00:00 2001 From: Pranav C Date: Mon, 22 Jan 2024 07:33:58 +0000 Subject: [PATCH 12/18] chore: cleanup --- packages/nocodb/src/cache/RedisCacheMgr.ts | 172 +++++------------- .../nocodb/src/services/tables.service.ts | 1 + .../db/features/keyboardShortcuts.spec.ts | 2 +- 3 files changed, 45 insertions(+), 130 deletions(-) diff --git a/packages/nocodb/src/cache/RedisCacheMgr.ts b/packages/nocodb/src/cache/RedisCacheMgr.ts index c7bfe94fb0..5e22447617 100644 --- a/packages/nocodb/src/cache/RedisCacheMgr.ts +++ b/packages/nocodb/src/cache/RedisCacheMgr.ts @@ -1,11 +1,7 @@ import debug from 'debug'; import Redis from 'ioredis'; import CacheMgr from './CacheMgr'; -import { - CacheDelDirection, - CacheGetType, - CacheListProp, -} from '~/utils/globals'; +import { CacheDelDirection, CacheGetType, CacheScope } from '~/utils/globals'; const log = debug('nc:cache'); @@ -23,7 +19,7 @@ export default class RedisCacheMgr extends CacheMgr { process.env.NC_CLOUD !== 'true' ) { // flush the existing db with selected key (Default: 0) - // this.client.flushdb(); + this.client.flushdb(); } // TODO(cache): fetch orgs once it's implemented @@ -46,15 +42,9 @@ export default class RedisCacheMgr extends CacheMgr { }; // @ts-ignore - async del(key: string[] | string): Promise { + async del(key: string): Promise { log(`RedisCacheMgr::del: deleting key ${key}`); - if (Array.isArray(key)) { - if (key.length) { - return this.client.del(key); - } - } else if (key) { - return this.client.del(key); - } + return this.client.del(key); } // @ts-ignore @@ -95,10 +85,6 @@ export default class RedisCacheMgr extends CacheMgr { if (Array.isArray(value) && value.length) { return this.client.sadd(key, value); } - const keyValue = await this.get(key, CacheGetType.TYPE_OBJECT); - if (keyValue) { - value = await this.prepareValue(value, this.getParents(keyValue)); - } return this.client.set( key, JSON.stringify(value, this.getCircularReplacer()), @@ -140,6 +126,30 @@ export default class RedisCacheMgr extends CacheMgr { return this.client.incrby(key, value); } + // @ts-ignore + async getAll(pattern: string): Promise { + return this.client.hgetall(pattern); + } + + // @ts-ignore + async delAll(scope: string, pattern: string): Promise { + // Example: nc::model:*: + const keys = await this.client.keys(`${this.prefix}:${scope}:${pattern}`); + log( + `RedisCacheMgr::delAll: deleting all keys with pattern ${this.prefix}:${scope}:${pattern}`, + ); + await Promise.all( + keys.map(async (k) => { + await this.deepDel(scope, k, CacheDelDirection.CHILD_TO_PARENT); + }), + ); + return Promise.all( + keys.map(async (k) => { + await this.del(k); + }), + ); + } + async getList( scope: string, subKeys: string[], @@ -159,28 +169,17 @@ export default class RedisCacheMgr extends CacheMgr { log(`RedisCacheMgr::getList: getting list with key ${key}`); const isNoneList = arr.length && arr.includes('NONE'); - if (isNoneList || !arr.length) { + if (isNoneList) { return Promise.resolve({ list: [], isNoneList, }); } - log(`RedisCacheMgr::getList: getting list with keys ${arr}`); - const values = await this.client.mget(arr); - return { - list: values.map((res) => { - try { - const o = JSON.parse(res); - if (typeof o === 'object') { - return o; - } - } catch (e) { - return res; - } - return res; - }), + list: await Promise.all( + arr.map(async (k) => await this.get(k, CacheGetType.TYPE_OBJECT)), + ), isNoneList, }; } @@ -213,20 +212,17 @@ export default class RedisCacheMgr extends CacheMgr { const propValues = props.map((p) => o[p]); // e.g. nc:::: getKey = `${this.prefix}:${scope}:${propValues.join(':')}`; - } - log(`RedisCacheMgr::setList: get key ${getKey}`); - // get Get Key - let value = await this.get(getKey, CacheGetType.TYPE_OBJECT); - if (value) { - log(`RedisCacheMgr::setList: preparing key ${getKey}`); - // prepare Get Key - value = await this.prepareValue(o, this.getParents(value), listKey); } else { - value = await this.prepareValue(o, [], listKey); + // e.g. nc::: + getKey = `${this.prefix}:${scope}:${o.id}`; + // special case - MODEL_ROLE_VISIBILITY + if (scope === CacheScope.MODEL_ROLE_VISIBILITY) { + getKey = `${this.prefix}:${scope}:${o.fk_view_id}:${o.role}`; + } } // set Get Key log(`RedisCacheMgr::setList: setting key ${getKey}`); - await this.set(getKey, JSON.stringify(value, this.getCircularReplacer())); + await this.set(getKey, JSON.stringify(o, this.getCircularReplacer())); // push Get Key to List listOfGetKeys.push(getKey); } @@ -240,11 +236,11 @@ export default class RedisCacheMgr extends CacheMgr { key: string, direction: string, ): Promise { + key = `${this.prefix}:${key}`; log(`RedisCacheMgr::deepDel: choose direction ${direction}`); if (direction === CacheDelDirection.CHILD_TO_PARENT) { - const childKey = await this.get(key, CacheGetType.TYPE_OBJECT); // given a child key, delete all keys in corresponding parent lists - const scopeList = this.getParents(childKey); + const scopeList = await this.client.keys(`${this.prefix}:${scope}*list`); for (const listKey of scopeList) { // get target list let list = (await this.get(listKey, CacheGetType.TYPE_ARRAY)) || []; @@ -259,17 +255,17 @@ export default class RedisCacheMgr extends CacheMgr { if (list.length) { // set target list log(`RedisCacheMgr::deepDel: set key ${listKey}`); + await this.del(listKey); await this.set(listKey, list); } } log(`RedisCacheMgr::deepDel: remove key ${key}`); return await this.del(key); } else if (direction === CacheDelDirection.PARENT_TO_CHILD) { - key = /:list$/.test(key) ? key : `${key}:list`; // given a list key, delete all the children const listOfChildren = await this.get(key, CacheGetType.TYPE_ARRAY); // delete each child key - await this.del(listOfChildren); + await Promise.all(listOfChildren.map(async (k) => await this.del(k))); // delete list key return await this.del(key); } else { @@ -301,92 +297,10 @@ export default class RedisCacheMgr extends CacheMgr { list = []; await this.del(listKey); } - - log(`RedisCacheMgr::appendToList: get key ${key}`); - // get Get Key - const value = await this.get(key, CacheGetType.TYPE_OBJECT); - log(`RedisCacheMgr::appendToList: preparing key ${key}`); - if (!value) { - // FALLBACK: this is to get rid of all keys that would be effected by this (should never happen) - console.error(`RedisCacheMgr::appendToList: value is empty for ${key}`); - const allParents = []; - // get all children - const listValues = await this.getList(scope, subListKeys); - // get all parents from children - listValues.list.forEach((v) => { - allParents.push(...this.getParents(v)); - }); - // remove duplicates - const uniqueParents = [...new Set(allParents)]; - // delete all parents and children - await Promise.all( - uniqueParents.map(async (p) => { - await this.deepDel(scope, p, CacheDelDirection.PARENT_TO_CHILD); - }), - ); - return false; - } - // prepare Get Key - const preparedValue = await this.prepareValue( - value, - this.getParents(value), - listKey, - ); - // set Get Key - log(`RedisCacheMgr::appendToList: setting key ${key}`); - await this.set( - key, - JSON.stringify(preparedValue, this.getCircularReplacer()), - ); - list.push(key); return this.set(listKey, list); } - prepareValue(value, listKeys = [], newParent?) { - if (newParent) { - listKeys.push(newParent); - } - - if (value && typeof value === 'object') { - value[CacheListProp] = listKeys; - } else if (value && typeof value === 'string') { - const keyHelper = value.split(CacheListProp); - if (listKeys.length) { - value = `${keyHelper[0]}${CacheListProp}${listKeys.join(',')}`; - } - } else if (value) { - console.error( - `RedisCacheMgr::prepareListKey: keyValue is not object or string`, - value, - ); - throw new Error( - `RedisCacheMgr::prepareListKey: keyValue is not object or string`, - ); - } - return value; - } - - getParents(value) { - if (value && typeof value === 'object') { - if (CacheListProp in value) { - const listsForKey = value[CacheListProp]; - if (listsForKey && listsForKey.length) { - return listsForKey; - } - } - } else if (value && typeof value === 'string') { - if (value.includes(CacheListProp)) { - const keyHelper = value.split(CacheListProp); - const listsForKey = keyHelper[1].split(','); - if (listsForKey.length) { - return listsForKey; - } - } - } - return []; - } - async destroy(): Promise { log('RedisCacheMgr::destroy: destroy redis'); return this.client.flushdb().then((r) => r === 'OK'); diff --git a/packages/nocodb/src/services/tables.service.ts b/packages/nocodb/src/services/tables.service.ts index f2e2846387..84aa0442f5 100644 --- a/packages/nocodb/src/services/tables.service.ts +++ b/packages/nocodb/src/services/tables.service.ts @@ -601,6 +601,7 @@ export class TablesService { column_name: c.column_name, })), ); + await sqlMgr.sqlOpPlus(source, 'tableCreate', { ...tableCreatePayLoad, tn: tableCreatePayLoad.table_name, diff --git a/tests/playwright/tests/db/features/keyboardShortcuts.spec.ts b/tests/playwright/tests/db/features/keyboardShortcuts.spec.ts index 73795f3c90..56494ebf7a 100644 --- a/tests/playwright/tests/db/features/keyboardShortcuts.spec.ts +++ b/tests/playwright/tests/db/features/keyboardShortcuts.spec.ts @@ -328,7 +328,7 @@ test.describe('Clipboard support', () => { test('multiple cells - horizontal, all data types', async ({ page }) => { // skip for local run (clipboard access issue in headless mode) if (!process.env.CI && config.use.headless) { - test.skip(); + // test.skip(); } // click first cell, press `Ctrl A` and `Ctrl C` From ff6ad5e3e17f15dec10f5cc46eab1bb2c7b6576d Mon Sep 17 00:00:00 2001 From: Pranav C Date: Mon, 22 Jan 2024 07:33:58 +0000 Subject: [PATCH 13/18] fix: kanban column order correction --- packages/nocodb/src/models/Column.ts | 77 ++++++++++++++++------------ packages/nocodb/src/models/View.ts | 24 +++++++++ 2 files changed, 68 insertions(+), 33 deletions(-) diff --git a/packages/nocodb/src/models/Column.ts b/packages/nocodb/src/models/Column.ts index 3e82cb6da8..869a776905 100644 --- a/packages/nocodb/src/models/Column.ts +++ b/packages/nocodb/src/models/Column.ts @@ -1354,39 +1354,50 @@ export default class Column implements ColumnType { source_id: param.source_id, fk_model_id: param.fk_model_id, }; - extractedColumnMetas.push( - extractProps(colWithId as any, [ - 'id', - 'fk_model_id', - 'column_name', - 'title', - 'uidt', - 'dt', - 'np', - 'ns', - 'clen', - 'cop', - 'pk', - 'rqd', - 'un', - 'ct', - 'ai', - 'unique', - 'cdf', - 'cc', - 'csn', - 'dtx', - 'dtxp', - 'dtxs', - 'au', - 'pv', - 'order', - 'base_id', - 'source_id', - 'system', - 'meta', - ]), - ); + + const insertObj = extractProps(colWithId as any, [ + 'id', + 'fk_model_id', + 'column_name', + 'title', + 'uidt', + 'dt', + 'np', + 'ns', + 'clen', + 'cop', + 'pk', + 'rqd', + 'un', + 'ct', + 'ai', + 'unique', + 'cdf', + 'cc', + 'csn', + 'dtx', + 'dtxp', + 'dtxs', + 'au', + 'pv', + 'order', + 'base_id', + 'source_id', + 'system', + 'meta', + ]); + + if (column.meta && typeof column.meta === 'object') { + insertObj.meta = JSON.stringify(column.meta); + } + + if (column.validate) { + if (typeof column.validate === 'string') + insertObj.validate = column.validate; + else insertObj.validate = JSON.stringify(column.validate); + } + extractedColumnMetas.push(insertObj); + columns.push(colWithId); } diff --git a/packages/nocodb/src/models/View.ts b/packages/nocodb/src/models/View.ts index 32c50f19b0..9ebbcb37ca 100644 --- a/packages/nocodb/src/models/View.ts +++ b/packages/nocodb/src/models/View.ts @@ -1609,6 +1609,30 @@ export default class View implements ViewType { columns = await Column.list({ fk_model_id: view.fk_model_id }); } + // todo: avoid duplicate code + if (view.type === ViewTypes.KANBAN && !copyFromView) { + // sort by display value & attachment first, then by singleLineText & Number + // so that later we can handle control `show` easily + columns.sort((a, b) => { + const displayValueOrder = +b.pv - +a.pv; + const attachmentOrder = + +(b.uidt === UITypes.Attachment) - +(a.uidt === UITypes.Attachment); + const singleLineTextOrder = + +(b.uidt === UITypes.SingleLineText) - + +(a.uidt === UITypes.SingleLineText); + const numberOrder = + +(b.uidt === UITypes.Number) - +(a.uidt === UITypes.Number); + const defaultOrder = b.order - a.order; + return ( + displayValueOrder || + attachmentOrder || + singleLineTextOrder || + numberOrder || + defaultOrder + ); + }); + } + let order = 1; let galleryShowLimit = 0; let kanbanShowLimit = 0; From daaf9ba467b7985c45b7002ffe480ff45af474a0 Mon Sep 17 00:00:00 2001 From: Pranav C Date: Mon, 22 Jan 2024 07:33:58 +0000 Subject: [PATCH 14/18] fix: set show/hide based on view type --- packages/nocodb/src/models/View.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/packages/nocodb/src/models/View.ts b/packages/nocodb/src/models/View.ts index 9ebbcb37ca..3045d99e41 100644 --- a/packages/nocodb/src/models/View.ts +++ b/packages/nocodb/src/models/View.ts @@ -1681,8 +1681,6 @@ export default class View implements ViewType { } } - if (isSystemColumn(column)) show = false; - insertObjs.push({ fk_column_id: column.id, order: order++, From f28167b5cc67db5f4fab788f9790b0971695d1d8 Mon Sep 17 00:00:00 2001 From: Pranav C Date: Mon, 22 Jan 2024 07:33:58 +0000 Subject: [PATCH 15/18] fix: set show/hide based on view type --- packages/nocodb/src/models/View.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/nocodb/src/models/View.ts b/packages/nocodb/src/models/View.ts index 3045d99e41..8a4b8cb65e 100644 --- a/packages/nocodb/src/models/View.ts +++ b/packages/nocodb/src/models/View.ts @@ -1679,6 +1679,8 @@ export default class View implements ViewType { if (column.id === mapView?.fk_geo_data_col_id) { show = true; } + } else if (view.type === ViewTypes.FORM && isSystemColumn(column)) { + show = false; } insertObjs.push({ From 6a043a66615c01374fd651ca501786ebfdf298a0 Mon Sep 17 00:00:00 2001 From: Pranav C Date: Mon, 22 Jan 2024 07:33:58 +0000 Subject: [PATCH 16/18] refactor: revert cache changes --- packages/nocodb/src/cache/RedisCacheMgr.ts | 170 ++++++++++++++++----- 1 file changed, 128 insertions(+), 42 deletions(-) diff --git a/packages/nocodb/src/cache/RedisCacheMgr.ts b/packages/nocodb/src/cache/RedisCacheMgr.ts index 5e22447617..882eaedb9e 100644 --- a/packages/nocodb/src/cache/RedisCacheMgr.ts +++ b/packages/nocodb/src/cache/RedisCacheMgr.ts @@ -1,7 +1,11 @@ import debug from 'debug'; import Redis from 'ioredis'; import CacheMgr from './CacheMgr'; -import { CacheDelDirection, CacheGetType, CacheScope } from '~/utils/globals'; +import { + CacheDelDirection, + CacheGetType, + CacheListProp, +} from '~/utils/globals'; const log = debug('nc:cache'); @@ -42,9 +46,15 @@ export default class RedisCacheMgr extends CacheMgr { }; // @ts-ignore - async del(key: string): Promise { + async del(key: string[] | string): Promise { log(`RedisCacheMgr::del: deleting key ${key}`); - return this.client.del(key); + if (Array.isArray(key)) { + if (key.length) { + return this.client.del(key); + } + } else if (key) { + return this.client.del(key); + } } // @ts-ignore @@ -85,6 +95,10 @@ export default class RedisCacheMgr extends CacheMgr { if (Array.isArray(value) && value.length) { return this.client.sadd(key, value); } + const keyValue = await this.get(key, CacheGetType.TYPE_OBJECT); + if (keyValue) { + value = await this.prepareValue(value, this.getParents(keyValue)); + } return this.client.set( key, JSON.stringify(value, this.getCircularReplacer()), @@ -126,30 +140,6 @@ export default class RedisCacheMgr extends CacheMgr { return this.client.incrby(key, value); } - // @ts-ignore - async getAll(pattern: string): Promise { - return this.client.hgetall(pattern); - } - - // @ts-ignore - async delAll(scope: string, pattern: string): Promise { - // Example: nc::model:*: - const keys = await this.client.keys(`${this.prefix}:${scope}:${pattern}`); - log( - `RedisCacheMgr::delAll: deleting all keys with pattern ${this.prefix}:${scope}:${pattern}`, - ); - await Promise.all( - keys.map(async (k) => { - await this.deepDel(scope, k, CacheDelDirection.CHILD_TO_PARENT); - }), - ); - return Promise.all( - keys.map(async (k) => { - await this.del(k); - }), - ); - } - async getList( scope: string, subKeys: string[], @@ -169,17 +159,28 @@ export default class RedisCacheMgr extends CacheMgr { log(`RedisCacheMgr::getList: getting list with key ${key}`); const isNoneList = arr.length && arr.includes('NONE'); - if (isNoneList) { + if (isNoneList || !arr.length) { return Promise.resolve({ list: [], isNoneList, }); } + log(`RedisCacheMgr::getList: getting list with keys ${arr}`); + const values = await this.client.mget(arr); + return { - list: await Promise.all( - arr.map(async (k) => await this.get(k, CacheGetType.TYPE_OBJECT)), - ), + list: values.map((res) => { + try { + const o = JSON.parse(res); + if (typeof o === 'object') { + return o; + } + } catch (e) { + return res; + } + return res; + }), isNoneList, }; } @@ -212,17 +213,20 @@ export default class RedisCacheMgr extends CacheMgr { const propValues = props.map((p) => o[p]); // e.g. nc:::: getKey = `${this.prefix}:${scope}:${propValues.join(':')}`; + } + log(`RedisCacheMgr::setList: get key ${getKey}`); + // get Get Key + let value = await this.get(getKey, CacheGetType.TYPE_OBJECT); + if (value) { + log(`RedisCacheMgr::setList: preparing key ${getKey}`); + // prepare Get Key + value = await this.prepareValue(o, this.getParents(value), listKey); } else { - // e.g. nc::: - getKey = `${this.prefix}:${scope}:${o.id}`; - // special case - MODEL_ROLE_VISIBILITY - if (scope === CacheScope.MODEL_ROLE_VISIBILITY) { - getKey = `${this.prefix}:${scope}:${o.fk_view_id}:${o.role}`; - } + value = await this.prepareValue(o, [], listKey); } // set Get Key log(`RedisCacheMgr::setList: setting key ${getKey}`); - await this.set(getKey, JSON.stringify(o, this.getCircularReplacer())); + await this.set(getKey, JSON.stringify(value, this.getCircularReplacer())); // push Get Key to List listOfGetKeys.push(getKey); } @@ -236,11 +240,11 @@ export default class RedisCacheMgr extends CacheMgr { key: string, direction: string, ): Promise { - key = `${this.prefix}:${key}`; log(`RedisCacheMgr::deepDel: choose direction ${direction}`); if (direction === CacheDelDirection.CHILD_TO_PARENT) { + const childKey = await this.get(key, CacheGetType.TYPE_OBJECT); // given a child key, delete all keys in corresponding parent lists - const scopeList = await this.client.keys(`${this.prefix}:${scope}*list`); + const scopeList = this.getParents(childKey); for (const listKey of scopeList) { // get target list let list = (await this.get(listKey, CacheGetType.TYPE_ARRAY)) || []; @@ -255,17 +259,17 @@ export default class RedisCacheMgr extends CacheMgr { if (list.length) { // set target list log(`RedisCacheMgr::deepDel: set key ${listKey}`); - await this.del(listKey); await this.set(listKey, list); } } log(`RedisCacheMgr::deepDel: remove key ${key}`); return await this.del(key); } else if (direction === CacheDelDirection.PARENT_TO_CHILD) { + key = /:list$/.test(key) ? key : `${key}:list`; // given a list key, delete all the children const listOfChildren = await this.get(key, CacheGetType.TYPE_ARRAY); // delete each child key - await Promise.all(listOfChildren.map(async (k) => await this.del(k))); + await this.del(listOfChildren); // delete list key return await this.del(key); } else { @@ -297,10 +301,92 @@ export default class RedisCacheMgr extends CacheMgr { list = []; await this.del(listKey); } + + log(`RedisCacheMgr::appendToList: get key ${key}`); + // get Get Key + const value = await this.get(key, CacheGetType.TYPE_OBJECT); + log(`RedisCacheMgr::appendToList: preparing key ${key}`); + if (!value) { + // FALLBACK: this is to get rid of all keys that would be effected by this (should never happen) + console.error(`RedisCacheMgr::appendToList: value is empty for ${key}`); + const allParents = []; + // get all children + const listValues = await this.getList(scope, subListKeys); + // get all parents from children + listValues.list.forEach((v) => { + allParents.push(...this.getParents(v)); + }); + // remove duplicates + const uniqueParents = [...new Set(allParents)]; + // delete all parents and children + await Promise.all( + uniqueParents.map(async (p) => { + await this.deepDel(scope, p, CacheDelDirection.PARENT_TO_CHILD); + }), + ); + return false; + } + // prepare Get Key + const preparedValue = await this.prepareValue( + value, + this.getParents(value), + listKey, + ); + // set Get Key + log(`RedisCacheMgr::appendToList: setting key ${key}`); + await this.set( + key, + JSON.stringify(preparedValue, this.getCircularReplacer()), + ); + list.push(key); return this.set(listKey, list); } + prepareValue(value, listKeys = [], newParent?) { + if (newParent) { + listKeys.push(newParent); + } + + if (value && typeof value === 'object') { + value[CacheListProp] = listKeys; + } else if (value && typeof value === 'string') { + const keyHelper = value.split(CacheListProp); + if (listKeys.length) { + value = `${keyHelper[0]}${CacheListProp}${listKeys.join(',')}`; + } + } else if (value) { + console.error( + `RedisCacheMgr::prepareListKey: keyValue is not object or string`, + value, + ); + throw new Error( + `RedisCacheMgr::prepareListKey: keyValue is not object or string`, + ); + } + return value; + } + + getParents(value) { + if (value && typeof value === 'object') { + if (CacheListProp in value) { + const listsForKey = value[CacheListProp]; + if (listsForKey && listsForKey.length) { + return listsForKey; + } + } + } else if (value && typeof value === 'string') { + if (value.includes(CacheListProp)) { + const keyHelper = value.split(CacheListProp); + const listsForKey = keyHelper[1].split(','); + if (listsForKey.length) { + return listsForKey; + } + } + } + return []; + } + async destroy(): Promise { log('RedisCacheMgr::destroy: destroy redis'); return this.client.flushdb().then((r) => r === 'OK'); From f15b92451656df39cab6611666e1f938669105a3 Mon Sep 17 00:00:00 2001 From: Pranav C Date: Mon, 22 Jan 2024 07:33:58 +0000 Subject: [PATCH 17/18] chore: cleanup & lint --- packages/nocodb/src/db/functionMappings/pg.ts | 4 +++- packages/nocodb/src/models/Model.ts | 2 +- packages/nocodb/src/models/View.ts | 4 ++-- packages/nocodb/src/services/forms.service.ts | 7 ------- packages/nocodb/src/services/galleries.service.ts | 7 ------- packages/nocodb/src/services/grids.service.ts | 9 +-------- packages/nocodb/src/services/kanbans.service.ts | 13 +++---------- packages/nocodb/src/services/maps.service.ts | 6 ------ packages/nocodb/src/services/tables.service.ts | 2 ++ .../tests/db/features/keyboardShortcuts.spec.ts | 2 +- 10 files changed, 13 insertions(+), 43 deletions(-) diff --git a/packages/nocodb/src/db/functionMappings/pg.ts b/packages/nocodb/src/db/functionMappings/pg.ts index 3e1c95a820..3d877855c3 100644 --- a/packages/nocodb/src/db/functionMappings/pg.ts +++ b/packages/nocodb/src/db/functionMappings/pg.ts @@ -210,7 +210,9 @@ const pg = { SUBSTR: async ({ fn, knex, pt, colAlias }: MapFnArgs) => { const str = (await fn(pt.arguments[0])).builder; const positionFrom = (await fn(pt.arguments[1] ?? 1)).builder; - const numberOfCharacters = pt.arguments[2] ? (await fn(pt.arguments[2])).builder : null; + const numberOfCharacters = pt.arguments[2] + ? (await fn(pt.arguments[2])).builder + : null; return { builder: knex.raw( `SUBSTR(${str}::TEXT, ${positionFrom}${ diff --git a/packages/nocodb/src/models/Model.ts b/packages/nocodb/src/models/Model.ts index a00965d40d..f21100562c 100644 --- a/packages/nocodb/src/models/Model.ts +++ b/packages/nocodb/src/models/Model.ts @@ -167,7 +167,7 @@ export default class Model implements TableType { source_id: sourceId, }, { - getColumns: async () => insertedColumns + getColumns: async () => insertedColumns, }, ncMeta, ); diff --git a/packages/nocodb/src/models/View.ts b/packages/nocodb/src/models/View.ts index 8a4b8cb65e..ea3e242a5c 100644 --- a/packages/nocodb/src/models/View.ts +++ b/packages/nocodb/src/models/View.ts @@ -1564,7 +1564,7 @@ export default class View implements ViewType { viewColumns, copyFromView, }: { - copyFromView: View; + copyFromView?: View; columns?: ({ order?: number; show?; @@ -1923,7 +1923,7 @@ export default class View implements ViewType { } else { // populate view columns await View.bulkColumnInsertToViews( - { columns: (await model.getColumns()) as any[], copyFromView }, + { columns: (await model.getColumns()) as any[] }, insertedView, ); } diff --git a/packages/nocodb/src/services/forms.service.ts b/packages/nocodb/src/services/forms.service.ts index f13cb7c655..08db6056b3 100644 --- a/packages/nocodb/src/services/forms.service.ts +++ b/packages/nocodb/src/services/forms.service.ts @@ -33,13 +33,6 @@ export class FormsService { param.body, ); - // const view = await View.insert({ - // ...param.body, - // // todo: sanitize - // fk_model_id: param.tableId, - // type: ViewTypes.FORM, - // }); - const model = await Model.get(param.tableId); const { id } = await View.insertMetaOnly( diff --git a/packages/nocodb/src/services/galleries.service.ts b/packages/nocodb/src/services/galleries.service.ts index 98d7073d12..6afb4eb361 100644 --- a/packages/nocodb/src/services/galleries.service.ts +++ b/packages/nocodb/src/services/galleries.service.ts @@ -32,13 +32,6 @@ export class GalleriesService { 'swagger.json#/components/schemas/ViewCreateReq', param.gallery, ); - // - // const view = await View.insert({ - // ...param.gallery, - // // todo: sanitize - // fk_model_id: param.tableId, - // type: ViewTypes.GALLERY, - // }); const model = await Model.get(param.tableId); diff --git a/packages/nocodb/src/services/grids.service.ts b/packages/nocodb/src/services/grids.service.ts index 759d06c780..1e027f1849 100644 --- a/packages/nocodb/src/services/grids.service.ts +++ b/packages/nocodb/src/services/grids.service.ts @@ -5,7 +5,7 @@ import type { NcRequest } from '~/interface/config'; import { AppHooksService } from '~/services/app-hooks/app-hooks.service'; import { validatePayload } from '~/helpers'; import { NcError } from '~/helpers/catchError'; -import { Column, GridView, Model, View } from '~/models'; +import { GridView, Model, View } from '~/models'; import NocoCache from '~/cache/NocoCache'; import { CacheScope } from '~/utils/globals'; @@ -22,13 +22,6 @@ export class GridsService { 'swagger.json#/components/schemas/ViewCreateReq', param.grid, ); - // - // const view = await View.insert({ - // ...param.grid, - // // todo: sanitize - // fk_model_id: param.tableId, - // type: ViewTypes.GRID, - // }); const model = await Model.get(param.tableId); diff --git a/packages/nocodb/src/services/kanbans.service.ts b/packages/nocodb/src/services/kanbans.service.ts index 06bc83b4d4..900ca2464a 100644 --- a/packages/nocodb/src/services/kanbans.service.ts +++ b/packages/nocodb/src/services/kanbans.service.ts @@ -9,9 +9,9 @@ import type { NcRequest } from '~/interface/config'; import { AppHooksService } from '~/services/app-hooks/app-hooks.service'; import { validatePayload } from '~/helpers'; import { NcError } from '~/helpers/catchError'; -import { KanbanView, Model, View } from '~/models' -import NocoCache from '~/cache/NocoCache' -import { CacheScope } from '~/utils/globals' +import { KanbanView, Model, View } from '~/models'; +import NocoCache from '~/cache/NocoCache'; +import { CacheScope } from '~/utils/globals'; @Injectable() export class KanbansService { @@ -32,13 +32,6 @@ export class KanbansService { param.kanban, ); - // const view = await View.insert({ - // ...param.kanban, - // // todo: sanitize - // fk_model_id: param.tableId, - // type: ViewTypes.KANBAN, - // }); - const model = await Model.get(param.tableId); const { id } = await View.insertMetaOnly( diff --git a/packages/nocodb/src/services/maps.service.ts b/packages/nocodb/src/services/maps.service.ts index b65779fbf6..0069268385 100644 --- a/packages/nocodb/src/services/maps.service.ts +++ b/packages/nocodb/src/services/maps.service.ts @@ -27,12 +27,6 @@ export class MapsService { 'swagger.json#/components/schemas/ViewCreateReq', param.map, ); - // const view = await View.insert({ - // ...param.map, - // // todo: sanitize - // fk_model_id: param.tableId, - // type: ViewTypes.MAP, - // }); const model = await Model.get(param.tableId); diff --git a/packages/nocodb/src/services/tables.service.ts b/packages/nocodb/src/services/tables.service.ts index 84aa0442f5..976d392d0b 100644 --- a/packages/nocodb/src/services/tables.service.ts +++ b/packages/nocodb/src/services/tables.service.ts @@ -613,6 +613,7 @@ export class TablesService { system?: boolean; } >; + if (!source.isMeta()) { columns = ( await sqlMgr.sqlOpPlus(source, 'columnList', { @@ -621,6 +622,7 @@ export class TablesService { }) )?.data?.list; } + const tables = await Model.list({ base_id: base.id, source_id: source.id, diff --git a/tests/playwright/tests/db/features/keyboardShortcuts.spec.ts b/tests/playwright/tests/db/features/keyboardShortcuts.spec.ts index 56494ebf7a..73795f3c90 100644 --- a/tests/playwright/tests/db/features/keyboardShortcuts.spec.ts +++ b/tests/playwright/tests/db/features/keyboardShortcuts.spec.ts @@ -328,7 +328,7 @@ test.describe('Clipboard support', () => { test('multiple cells - horizontal, all data types', async ({ page }) => { // skip for local run (clipboard access issue in headless mode) if (!process.env.CI && config.use.headless) { - // test.skip(); + test.skip(); } // click first cell, press `Ctrl A` and `Ctrl C` From ff1334637f77d86a82db03a5ca1ec37897c94e07 Mon Sep 17 00:00:00 2001 From: Pranav C Date: Mon, 22 Jan 2024 07:33:59 +0000 Subject: [PATCH 18/18] fix: add missing colOption insert --- packages/nocodb/src/models/Column.ts | 54 +++++++++++++++++++++++++--- 1 file changed, 50 insertions(+), 4 deletions(-) diff --git a/packages/nocodb/src/models/Column.ts b/packages/nocodb/src/models/Column.ts index 869a776905..fbce1f91a0 100644 --- a/packages/nocodb/src/models/Column.ts +++ b/packages/nocodb/src/models/Column.ts @@ -1457,10 +1457,7 @@ export default class Column implements ColumnType { case UITypes.LinkToAnotherRecord: { insertArr.push({ fk_column_id: column.id, - - // ref_db_alias type: column.type, - // db_type: fk_child_column_id: column.fk_child_column_id, fk_parent_column_id: column.fk_parent_column_id, @@ -1578,7 +1575,56 @@ export default class Column implements ColumnType { ); break; - // todo: handle rest of the cases + case UITypes.Lookup: + await ncMeta.bulkMetaInsert( + null, + null, + MetaTable.COL_LOOKUP, + insertGroups.get(group), + ); + break; + + case UITypes.Rollup: + await ncMeta.bulkMetaInsert( + null, + null, + MetaTable.COL_ROLLUP, + insertGroups.get(group), + ); + break; + case UITypes.Links: + case UITypes.LinkToAnotherRecord: + await ncMeta.bulkMetaInsert( + null, + null, + MetaTable.COL_RELATIONS, + insertGroups.get(group), + ); + break; + case UITypes.QrCode: + await ncMeta.bulkMetaInsert( + null, + null, + MetaTable.COL_QRCODE, + insertGroups.get(group), + ); + break; + case UITypes.Barcode: + await ncMeta.bulkMetaInsert( + null, + null, + MetaTable.COL_BARCODE, + insertGroups.get(group), + ); + break; + case UITypes.Formula: + await ncMeta.bulkMetaInsert( + null, + null, + MetaTable.COL_FORMULA, + insertGroups.get(group), + ); + break; } } }