From e008f0eed2f6edb0793713efcbc25ab114447af8 Mon Sep 17 00:00:00 2001 From: Pranav C Date: Mon, 27 May 2024 15:24:46 +0000 Subject: [PATCH 1/6] fix: add proper type definition for xcCondition --- packages/nocodb/src/db/CustomKnex.ts | 40 +++++++++++++----------- packages/nocodb/src/meta/meta.service.ts | 18 ++++++----- 2 files changed, 33 insertions(+), 25 deletions(-) diff --git a/packages/nocodb/src/db/CustomKnex.ts b/packages/nocodb/src/db/CustomKnex.ts index cda64ad395..0c277239cd 100644 --- a/packages/nocodb/src/db/CustomKnex.ts +++ b/packages/nocodb/src/db/CustomKnex.ts @@ -492,23 +492,27 @@ const appendWhereCondition = function ( return knexRef; }; -type XcConditionObjVal = { - [key in 'eq' | 'neq' | 'lt' | 'gt' | 'ge' | 'le' | 'like' | 'nlike']: - | string - | number - | any; -}; - -interface XcXonditionObj { - _or: XcXonditionObj[]; - _and: XcXonditionObj[]; - _not: XcXonditionObj; - [key: string]: - | XcXonditionObj - | XcXonditionObj[] - | XcConditionObjVal - | XcConditionObjVal[]; +type AtLeastOne }> = Partial & + U[keyof U]; + +export type ConditionVal = AtLeastOne<{ + eq: string | number | any; + neq: string | number | any; + lt: string | number | any; + gt: string | number | any; + ge: string | number | any; + le: string | number | any; + like: string | number | any; + nlike: string | number | any; +}>; + +export interface Condition { + _or?: Condition[]; + _and?: Condition[]; + _not?: Condition; + + [key: string]: ConditionVal | Condition | Condition[]; } declare module 'knex' { @@ -527,7 +531,7 @@ declare module 'knex' { ): Knex.QueryBuilder; condition( - conditionObj: XcXonditionObj, + conditionObj: Condition, columnAliases?: { [columnAlias: string]: string; }, @@ -542,7 +546,7 @@ declare module 'knex' { ): Knex.QueryBuilder; conditionGraph(condition: { - condition: XcXonditionObj; + condition: Condition; models: { [key: string]: BaseModelSql }; }): Knex.QueryBuilder; diff --git a/packages/nocodb/src/meta/meta.service.ts b/packages/nocodb/src/meta/meta.service.ts index 758c4a7934..0d04879d71 100644 --- a/packages/nocodb/src/meta/meta.service.ts +++ b/packages/nocodb/src/meta/meta.service.ts @@ -6,11 +6,13 @@ import utc from 'dayjs/plugin/utc'; import timezone from 'dayjs/plugin/timezone'; import type * as knex from 'knex'; import type { Knex } from 'knex'; +import type { Condition } from '~/db/CustomKnex'; import XcMigrationSource from '~/meta/migrations/XcMigrationSource'; import XcMigrationSourcev2 from '~/meta/migrations/XcMigrationSourcev2'; import { XKnex } from '~/db/CustomKnex'; import { NcConfig } from '~/utils/nc-config'; import { MetaTable } from '~/utils/globals'; +import { NcError } from '~/helpers/catchError'; dayjs.extend(utc); dayjs.extend(timezone); @@ -119,6 +121,7 @@ export class MetaService { }); return insertObj; } + public async bulkMetaInsert( base_id: string, source_id: string, @@ -278,7 +281,7 @@ export class MetaService { condition?: { [key: string]: any }; limit?: number; offset?: number; - xcCondition?; + xcCondition?: Condition; fields?: string[]; sort?: { field: string; desc?: boolean }; }, @@ -370,7 +373,7 @@ export class MetaService { dbAlias: string, target: string, idOrCondition: string | { [p: string]: any }, - xcCondition?, + xcCondition?: Condition, ): Promise { const query = this.knexConnection(target); @@ -400,7 +403,7 @@ export class MetaService { target: string, idOrCondition: string | { [p: string]: any }, fields?: string[], - xcCondition?, + xcCondition?: Condition, ): Promise { const query = this.knexConnection(target); @@ -466,7 +469,7 @@ export class MetaService { condition?: { [p: string]: any }; limit?: number; offset?: number; - xcCondition?; + xcCondition?: Condition; fields?: string[]; orderBy?: { [key: string]: 'asc' | 'desc' }; }, @@ -513,7 +516,7 @@ export class MetaService { condition?: { [p: string]: any }; limit?: number; offset?: number; - xcCondition?; + xcCondition?: Condition; fields?: string[]; orderBy?: { [key: string]: 'asc' | 'desc' }; }, @@ -558,7 +561,7 @@ export class MetaService { target: string, args?: { condition?: { [p: string]: any }; - xcCondition?; + xcCondition?: Condition; aggField?: string; }, ): Promise { @@ -590,7 +593,7 @@ export class MetaService { target: string, data: any, idOrCondition?: string | { [p: string]: any }, - xcCondition?, + xcCondition?: Condition, ): Promise { const query = this.knexConnection(target); if (base_id !== null && base_id !== undefined) { @@ -619,6 +622,7 @@ export class MetaService { _project_id: string, _dbAlias: string, ): Promise { + NcError.notImplemented('metaDeleteAll'); // await this.knexConnection..dropTableIfExists('nc_roles').; // await this.knexConnection.schema.dropTableIfExists('nc_store').; // await this.knexConnection.schema.dropTableIfExists('nc_hooks').; From b67ecb313d1d30093b05be89043b9bcfe79458b9 Mon Sep 17 00:00:00 2001 From: Pranav C Date: Mon, 27 May 2024 15:24:47 +0000 Subject: [PATCH 2/6] fix: validate and confirm query contains condition when running delete/update query --- packages/nocodb/src/db/CustomKnex.ts | 27 ++- packages/nocodb/src/helpers/catchError.ts | 11 + packages/nocodb/src/meta/meta.service.ts | 249 +++++++++++++++++++++- 3 files changed, 277 insertions(+), 10 deletions(-) diff --git a/packages/nocodb/src/db/CustomKnex.ts b/packages/nocodb/src/db/CustomKnex.ts index 0c277239cd..9077ba8109 100644 --- a/packages/nocodb/src/db/CustomKnex.ts +++ b/packages/nocodb/src/db/CustomKnex.ts @@ -497,14 +497,14 @@ type AtLeastOne }> = Partial & U[keyof U]; export type ConditionVal = AtLeastOne<{ - eq: string | number | any; - neq: string | number | any; - lt: string | number | any; - gt: string | number | any; - ge: string | number | any; - le: string | number | any; - like: string | number | any; - nlike: string | number | any; + eq: string | number | boolean | Date; + neq: string | number | boolean | Date; + lt: number | string | Date; + gt: number | string | Date; + ge: number | string | Date; + le: number | string | Date; + like: string; + nlike: string; }>; export interface Condition { @@ -556,6 +556,8 @@ declare module 'knex' { [columnAlias: string]: string; }, ): Knex.QueryBuilder; + + hasWhere(): boolean; } } } @@ -1278,8 +1280,15 @@ function parseNestedConditionv2(obj, qb, pKey?, table?, tableAlias?) { return qb; } -// Conditionv2 +// extend the knex query builder with a method to check if a where clause exists +knex.QueryBuilder.extend('hasWhere', function () { + // Inspect the _statements array for 'where' clauses + return ( + this as unknown as { _statements: { grouping: string }[] } + )._statements.some((statement) => statement.grouping === 'where') as any; +}); +// Conditionv2 /** * Append custom where condition(nested object) to knex query builder */ diff --git a/packages/nocodb/src/helpers/catchError.ts b/packages/nocodb/src/helpers/catchError.ts index 0c7c6e2a4f..86269874c3 100644 --- a/packages/nocodb/src/helpers/catchError.ts +++ b/packages/nocodb/src/helpers/catchError.ts @@ -415,6 +415,13 @@ export class NotFound extends NcBaseError {} export class SsoError extends NcBaseError {} +export class MetaError extends NcBaseError { + constructor(param: { message: string; sql: string }) { + super(param.message); + Object.assign(this, param); + } +} + export class ExternalError extends NcBaseError { constructor(error: Error) { super(error.message); @@ -747,4 +754,8 @@ export class NcError { `Email domain ${domain} is not allowed for this organization`, ); } + + static metaError(param: { message: string; sql: string }) { + throw new MetaError(param); + } } diff --git a/packages/nocodb/src/meta/meta.service.ts b/packages/nocodb/src/meta/meta.service.ts index 0d04879d71..4d75c79437 100644 --- a/packages/nocodb/src/meta/meta.service.ts +++ b/packages/nocodb/src/meta/meta.service.ts @@ -13,7 +13,6 @@ import { XKnex } from '~/db/CustomKnex'; import { NcConfig } from '~/utils/nc-config'; import { MetaTable } from '~/utils/globals'; import { NcError } from '~/helpers/catchError'; - dayjs.extend(utc); dayjs.extend(timezone); @@ -58,6 +57,14 @@ export class MetaService { return this.knexConnection; } + /*** + * Get single record from meta data + * @param base_id - Base id + * @param dbAlias - Database alias + * @param target - Table name + * @param idOrCondition - If string, will get the record with the given id. If object, will get the record with the given condition. + * @param fields - Fields to be selected + */ public async metaGet( base_id: string, dbAlias: string, @@ -98,6 +105,14 @@ export class MetaService { return query.first(); } + /*** + * Insert record into meta data + * @param base_id - Base id + * @param dbAlias - Database alias + * @param target - Table name + * @param data - Data to be inserted + * @param ignoreIdGeneration - If true, will not generate id for the record + */ public async metaInsert2( base_id: string, source_id: string, @@ -122,6 +137,14 @@ export class MetaService { return insertObj; } + /*** + * Insert multiple records into meta data + * @param base_id - Base id + * @param source_id - Source id + * @param target - Table name + * @param data - Data to be inserted + * @param ignoreIdGeneration - If true, will not generate id for the record + */ public async bulkMetaInsert( base_id: string, source_id: string, @@ -159,6 +182,11 @@ export class MetaService { return insertObj; } + /*** + * Generate nanoid for the given target + * @param target - Table name + * @returns {string} - Generated nanoid + * */ public async genNanoid(target: string) { let prefix; switch (target) { @@ -273,6 +301,19 @@ export class MetaService { return `${prefix}${nanoidv2()}`; } + /*** + * Get paginated list of meta data + * @param baseId - Base id + * @param dbAlias - Database alias + * @param target - Table name + * @param args.condition - Condition to be applied + * @param args.limit - Limit of records + * @param args.offset - Offset of records + * @param args.xcCondition - Additional nested or complex condition to be added to the query. + * @param args.fields - Fields to be selected + * @param args.sort - Sort field and direction + * @returns {Promise<{list: any[]; count: number}>} - List of records and count + * */ public async metaPaginatedList( baseId: string, dbAlias: string, @@ -368,12 +409,22 @@ export class MetaService { // return true; // } + /*** + * Delete meta data + * @param base_id - Base id + * @param dbAlias - Database alias + * @param target - Table name + * @param idOrCondition - If string, will delete the record with the given id. If object, will delete the record with the given condition. + * @param xcCondition - Additional nested or complex condition to be added to the query. + * @param force - If true, will not check if a condition is present in the query builder and will execute the query as is. + */ public async metaDelete( base_id: string, dbAlias: string, target: string, idOrCondition: string | { [p: string]: any }, xcCondition?: Condition, + force = false, ): Promise { const query = this.knexConnection(target); @@ -394,9 +445,23 @@ export class MetaService { query.condition(xcCondition, {}); } + // Check if a condition is present in the query builder and throw an error if not. + if (!force) { + this.checkConditionPresent(query); + } + return query.del(); } + /*** + * Get meta data + * @param base_id - Base id + * @param sourceId - Source id + * @param target - Table name + * @param idOrCondition - If string, will get the record with the given id. If object, will get the record with the given condition. + * @param fields - Fields to be selected + * @param xcCondition - Additional nested or complex condition to be added to the query. + */ public async metaGet2( base_id: string, sourceId: string, @@ -434,6 +499,12 @@ export class MetaService { return query.first(); } + /*** + * Get order value for the next record + * @param target - Table name + * @param condition - Condition to be applied + * @returns {Promise} - Order value + * */ public async metaGetNextOrder( target: string, condition: { [key: string]: any }, @@ -508,6 +579,19 @@ export class MetaService { return query; } + /*** + * Get list of meta data + * @param base_id - Base id + * @param dbAlias - Database alias + * @param target - Table name + * @param args.condition - Condition to be applied + * @param args.limit - Limit of records + * @param args.offset - Offset of records + * @param args.xcCondition - Additional nested or complex condition to be added to the query. + * @param args.fields - Fields to be selected + * @param args.orderBy - Order by fields + * @returns {Promise} - List of records + * */ public async metaList2( base_id: string, dbAlias: string, @@ -552,9 +636,23 @@ export class MetaService { query.select(...args.fields); } + query.andWhere((qb) => { + qb.where({}); + }); + return query; } + /*** + * Get count of meta data + * @param base_id - Base id + * @param dbAlias - Database alias + * @param target - Table name + * @param args.condition - Condition to be applied + * @param args.xcCondition - Additional nested or complex condition to be added to the query. + * @param args.aggField - Field to be aggregated + * @returns {Promise} - Count of records + * */ public async metaCount( base_id: string, dbAlias: string, @@ -587,6 +685,16 @@ export class MetaService { return +(await query)?.['count'] || 0; } + /*** + * Update meta data + * @param base_id - Base id + * @param dbAlias - Database alias + * @param target - Table name + * @param data - Data to be updated + * @param idOrCondition - If string, will update the record with the given id. If object, will update the record with the given condition. + * @param xcCondition - Additional nested or complex condition to be added to the query. + * @param force - If true, will not check if a condition is present in the query builder and will execute the query as is. + */ public async metaUpdate( base_id: string, dbAlias: string, @@ -594,6 +702,7 @@ export class MetaService { data: any, idOrCondition?: string | { [p: string]: any }, xcCondition?: Condition, + force = false, ): Promise { const query = this.knexConnection(target); if (base_id !== null && base_id !== undefined) { @@ -615,6 +724,11 @@ export class MetaService { query.condition(xcCondition); } + // Check if a condition is present in the query builder and throw an error if not. + if (!force) { + this.checkConditionPresent(query); + } + return await query; } @@ -630,6 +744,12 @@ export class MetaService { // await this.knexConnection.schema.dropTableIfExists('nc_acl').; } + /*** + * Check table meta data exists for a given base id and db alias + * @param base_id - Base id + * @param dbAlias - Database alias + * @returns {Promise} - True if meta data exists, false otherwise + * */ public async isMetaDataExists( base_id: string, dbAlias: string, @@ -699,6 +819,14 @@ export class MetaService { } } + /*** + * Create a new base + * @param baseName - Base name + * @param config - Base config + * @param description - Base description + * @param meta - If true, will create a meta base + * @returns {Promise} - Created base + * */ public async baseCreate( baseName: string, config: any, @@ -744,7 +872,19 @@ export class MetaService { } } + /*** + * Update base config + * @param baseId - Base id + * @param config - Base config + * */ public async baseUpdate(baseId: string, config: any): Promise { + if (!baseId) { + NcError.metaError({ + message: 'Base Id is required to update base config', + sql: '', + }); + } + try { const base = { config: CryptoJS.AES.encrypt( @@ -761,6 +901,10 @@ export class MetaService { } } + /*** + * Get base list with decrypted config + * @returns {Promise} - List of bases + * */ public async baseList(): Promise { return (await this.knexConnection('nc_projects').select()).map((p) => { p.config = CryptoJS.AES.decrypt( @@ -771,6 +915,10 @@ export class MetaService { }); } + /*** + * Get base list with decrypted config for a user + * @returns {Promise} - List of bases + * */ public async userProjectList(userId: any): Promise { return ( await this.knexConnection('nc_projects') @@ -828,6 +976,12 @@ export class MetaService { }); } + /*** + * Check if user have access to a project + * @param baseId - Base id + * @param userId - User id + * @returns {Promise} - True if user have access, false otherwise + * */ public async isUserHaveAccessToProject( baseId: string, userId: any, @@ -840,6 +994,12 @@ export class MetaService { .first()); } + /*** + * Get base by name + * @param baseName - Base name + * @param encrypt - If true, will skip the decryption of config + * @returns {Promise} - Base + * */ public async baseGet(baseName: string, encrypt?): Promise { const base = await this.knexConnection('nc_projects') .where({ @@ -856,6 +1016,12 @@ export class MetaService { return base; } + /*** + * Get base by id + * @param baseId - Base id + * @param encrypt - If true, will skip the decryption of config + * @returns {Promise} - Base + * */ public async baseGetById(baseId: string, encrypt?): Promise { const base = await this.knexConnection('nc_projects') .where({ @@ -871,7 +1037,18 @@ export class MetaService { return base; } + /*** + * Delete base by name + * @param title - Base name + * */ public baseDelete(title: string): Promise { + if (!title) { + NcError.metaError({ + message: 'Base title is required to delete base', + sql: '', + }); + } + return this.knexConnection('nc_projects') .where({ title, @@ -879,7 +1056,17 @@ export class MetaService { .delete(); } + /*** + * Delete base by id + * @param id - Base id + * */ public baseDeleteById(id: string): Promise { + if (!id) { + NcError.metaError({ + message: 'Base id is required to delete base', + sql: '', + }); + } return this.knexConnection('nc_projects') .where({ id, @@ -887,7 +1074,19 @@ export class MetaService { .delete(); } + /*** + * Update base status + * @param baseId - Base id + * @param status - Base status + * */ public async baseStatusUpdate(baseId: string, status: string): Promise { + if (!baseId) { + NcError.metaError({ + message: 'Base id is required to update base status', + sql: '', + }); + } + return this.knexConnection('nc_projects') .update({ status, @@ -897,6 +1096,12 @@ export class MetaService { }); } + /*** + * Add user to base + * @param baseId - Base id + * @param userId - User id + * @param roles - User roles + * */ public async baseAddUser( baseId: string, userId: any, @@ -919,7 +1124,19 @@ export class MetaService { }); } + /*** + * Remove user from base + * @param baseId - Base id + * @param userId - User id + * */ public baseRemoveUser(baseId: string, userId: any): Promise { + if (!baseId || !userId) { + NcError.metaError({ + message: 'Base id and user id is required to remove user from base', + sql: '', + }); + } + return this.knexConnection('nc_projects_users') .where({ user_id: userId, @@ -929,6 +1146,13 @@ export class MetaService { } public removeXcUser(userId: any): Promise { + if (!userId) { + NcError.metaError({ + message: 'User id is required to remove user', + sql: '', + }); + } + return this.knexConnection('xc_users') .where({ id: userId, @@ -976,4 +1200,27 @@ export class MetaService { }); return true; } + + /** + * Checks if a condition is present in the query builder and throws an error if not. + * + * @param queryBuilder - The Knex QueryBuilder instance to check. + */ + private checkConditionPresent(queryBuilder: Knex.QueryBuilder) { + // Convert the query builder to a SQL string to inspect the presence of a WHERE clause. + const sql = queryBuilder.toString(); + + // Ensure that a WHERE condition is present in the query builder. + // Note: The `hasWhere` method alone is not sufficient since it can indicate an empty nested WHERE group. + // Therefore, also check the SQL string for the presence of the 'WHERE' keyword. + if (queryBuilder.hasWhere() && /\bWHERE\b/i.test(sql)) { + return; + } + + // Throw an error if no condition is found in the query builder. + NcError.metaError({ + message: 'Condition is required', + sql, + }); + } } From b779114cb1774d034a7522cd8101686673a7612d Mon Sep 17 00:00:00 2001 From: Pranav C Date: Mon, 27 May 2024 15:24:47 +0000 Subject: [PATCH 3/6] refactor: improved error message --- packages/nocodb/src/meta/meta.service.ts | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/packages/nocodb/src/meta/meta.service.ts b/packages/nocodb/src/meta/meta.service.ts index 4d75c79437..14a8cf6612 100644 --- a/packages/nocodb/src/meta/meta.service.ts +++ b/packages/nocodb/src/meta/meta.service.ts @@ -447,7 +447,7 @@ export class MetaService { // Check if a condition is present in the query builder and throw an error if not. if (!force) { - this.checkConditionPresent(query); + this.checkConditionPresent(query, 'delete'); } return query.del(); @@ -726,7 +726,7 @@ export class MetaService { // Check if a condition is present in the query builder and throw an error if not. if (!force) { - this.checkConditionPresent(query); + this.checkConditionPresent(query, 'update'); } return await query; @@ -1206,7 +1206,10 @@ export class MetaService { * * @param queryBuilder - The Knex QueryBuilder instance to check. */ - private checkConditionPresent(queryBuilder: Knex.QueryBuilder) { + private checkConditionPresent( + queryBuilder: Knex.QueryBuilder, + operation: 'delete' | 'update', + ) { // Convert the query builder to a SQL string to inspect the presence of a WHERE clause. const sql = queryBuilder.toString(); @@ -1219,7 +1222,7 @@ export class MetaService { // Throw an error if no condition is found in the query builder. NcError.metaError({ - message: 'Condition is required', + message: 'A condition is required to ' + operation + ' records.', sql, }); } From dff5454f1bf7ad01ffbe13cfe955ffa193eed9aa Mon Sep 17 00:00:00 2001 From: Pranav C Date: Mon, 27 May 2024 15:24:48 +0000 Subject: [PATCH 4/6] refactor: refactor genNanoid method --- packages/nocodb/src/meta/meta.service.ts | 147 ++++++----------------- 1 file changed, 38 insertions(+), 109 deletions(-) diff --git a/packages/nocodb/src/meta/meta.service.ts b/packages/nocodb/src/meta/meta.service.ts index 14a8cf6612..c288d45133 100644 --- a/packages/nocodb/src/meta/meta.service.ts +++ b/packages/nocodb/src/meta/meta.service.ts @@ -188,119 +188,48 @@ export class MetaService { * @returns {string} - Generated nanoid * */ public async genNanoid(target: string) { - let prefix; - switch (target) { - case MetaTable.PROJECT: - prefix = 'p'; - break; - case MetaTable.BASES: - prefix = 'b'; - break; - case MetaTable.MODELS: - prefix = 'm'; - break; - case MetaTable.COLUMNS: - prefix = 'c'; - break; - case MetaTable.COL_RELATIONS: - prefix = 'l'; - break; - case MetaTable.COL_SELECT_OPTIONS: - prefix = 's'; - break; - case MetaTable.COL_LOOKUP: - prefix = 'lk'; - break; - case MetaTable.COL_ROLLUP: - prefix = 'rl'; - break; - case MetaTable.COL_FORMULA: - prefix = 'f'; - break; - case MetaTable.FILTER_EXP: - prefix = 'fi'; - break; - case MetaTable.SORT: - prefix = 'so'; - break; - case MetaTable.SHARED_VIEWS: - prefix = 'sv'; - break; - case MetaTable.ACL: - prefix = 'ac'; - break; - case MetaTable.FORM_VIEW: - prefix = 'fv'; - break; - case MetaTable.FORM_VIEW_COLUMNS: - prefix = 'fvc'; - break; - case MetaTable.GALLERY_VIEW: - prefix = 'gv'; - break; - case MetaTable.GALLERY_VIEW_COLUMNS: - prefix = 'gvc'; - break; - case MetaTable.KANBAN_VIEW: - prefix = 'kv'; - break; - case MetaTable.KANBAN_VIEW_COLUMNS: - prefix = 'kvc'; - break; - case MetaTable.CALENDAR_VIEW: - prefix = 'cv'; - break; - case MetaTable.CALENDAR_VIEW_COLUMNS: - prefix = 'cvc'; - break; - case MetaTable.CALENDAR_VIEW_RANGE: - prefix = 'cvr'; - break; - case MetaTable.USERS: - prefix = 'us'; - break; - case MetaTable.ORGS_OLD: - prefix = 'org'; - break; - case MetaTable.TEAMS: - prefix = 'tm'; - break; - case MetaTable.VIEWS: - prefix = 'vw'; - break; - case MetaTable.HOOKS: - prefix = 'hk'; - break; - case MetaTable.HOOK_LOGS: - prefix = 'hkl'; - break; - case MetaTable.AUDIT: - prefix = 'adt'; - break; - case MetaTable.API_TOKENS: - prefix = 'tkn'; - break; - case MetaTable.EXTENSIONS: - prefix = 'ext'; - break; - case MetaTable.COMMENTS: - prefix = 'com'; - break; - case MetaTable.COMMENTS_REACTIONS: - prefix = 'cre'; - break; - case MetaTable.USER_COMMENTS_NOTIFICATIONS_PREFERENCE: - prefix = 'cnp'; - break; - default: - prefix = 'nc'; - break; - } + const prefixMap: { [key: string]: string } = { + [MetaTable.PROJECT]: 'p', + [MetaTable.BASES]: 'b', + [MetaTable.MODELS]: 'm', + [MetaTable.COLUMNS]: 'c', + [MetaTable.COL_RELATIONS]: 'l', + [MetaTable.COL_SELECT_OPTIONS]: 's', + [MetaTable.COL_LOOKUP]: 'lk', + [MetaTable.COL_ROLLUP]: 'rl', + [MetaTable.COL_FORMULA]: 'f', + [MetaTable.FILTER_EXP]: 'fi', + [MetaTable.SORT]: 'so', + [MetaTable.SHARED_VIEWS]: 'sv', + [MetaTable.ACL]: 'ac', + [MetaTable.FORM_VIEW]: 'fv', + [MetaTable.FORM_VIEW_COLUMNS]: 'fvc', + [MetaTable.GALLERY_VIEW]: 'gv', + [MetaTable.GALLERY_VIEW_COLUMNS]: 'gvc', + [MetaTable.KANBAN_VIEW]: 'kv', + [MetaTable.KANBAN_VIEW_COLUMNS]: 'kvc', + [MetaTable.CALENDAR_VIEW]: 'cv', + [MetaTable.CALENDAR_VIEW_COLUMNS]: 'cvc', + [MetaTable.CALENDAR_VIEW_RANGE]: 'cvr', + [MetaTable.USERS]: 'us', + [MetaTable.ORGS_OLD]: 'org', + [MetaTable.TEAMS]: 'tm', + [MetaTable.VIEWS]: 'vw', + [MetaTable.HOOKS]: 'hk', + [MetaTable.HOOK_LOGS]: 'hkl', + [MetaTable.AUDIT]: 'adt', + [MetaTable.API_TOKENS]: 'tkn', + [MetaTable.EXTENSIONS]: 'ext', + [MetaTable.COMMENTS]: 'com', + [MetaTable.COMMENTS_REACTIONS]: 'cre', + [MetaTable.USER_COMMENTS_NOTIFICATIONS_PREFERENCE]: 'cnp', + }; + + const prefix = prefixMap[target] || 'nc'; // using nanoid to avoid collision with existing ids when duplicating return `${prefix}${nanoidv2()}`; } - /*** * Get paginated list of meta data * @param baseId - Base id From 4aaadc043669364505a5421a4dfb31c41366fd61 Mon Sep 17 00:00:00 2001 From: Pranav C Date: Mon, 27 May 2024 15:24:48 +0000 Subject: [PATCH 5/6] refactor: coderabbit suggested changes --- .../src/filters/global-exception/global-exception.filter.ts | 5 ++--- packages/nocodb/src/meta/meta.service.ts | 4 ---- 2 files changed, 2 insertions(+), 7 deletions(-) diff --git a/packages/nocodb/src/filters/global-exception/global-exception.filter.ts b/packages/nocodb/src/filters/global-exception/global-exception.filter.ts index 2c4a7e4c46..352b4e160f 100644 --- a/packages/nocodb/src/filters/global-exception/global-exception.filter.ts +++ b/packages/nocodb/src/filters/global-exception/global-exception.filter.ts @@ -41,9 +41,8 @@ export class GlobalExceptionFilter implements ExceptionFilter { } // try to extract db error for unknown errors - const dbError = !(exception instanceof NcBaseError) - ? extractDBError(exception) - : null; + const dbError = + exception instanceof NcBaseError ? null : extractDBError(exception); // skip unnecessary error logging if ( diff --git a/packages/nocodb/src/meta/meta.service.ts b/packages/nocodb/src/meta/meta.service.ts index c288d45133..8a9d033338 100644 --- a/packages/nocodb/src/meta/meta.service.ts +++ b/packages/nocodb/src/meta/meta.service.ts @@ -565,10 +565,6 @@ export class MetaService { query.select(...args.fields); } - query.andWhere((qb) => { - qb.where({}); - }); - return query; } From 8948150a8a9f96b1c987f8e11989ad4f278a1186 Mon Sep 17 00:00:00 2001 From: Pranav C Date: Mon, 27 May 2024 15:24:49 +0000 Subject: [PATCH 6/6] refactor: cleanup - remove unused methods --- packages/nocodb/src/meta/meta.service.ts | 412 ----------------------- 1 file changed, 412 deletions(-) diff --git a/packages/nocodb/src/meta/meta.service.ts b/packages/nocodb/src/meta/meta.service.ts index 8a9d033338..bdbb378173 100644 --- a/packages/nocodb/src/meta/meta.service.ts +++ b/packages/nocodb/src/meta/meta.service.ts @@ -17,10 +17,6 @@ dayjs.extend(utc); dayjs.extend(timezone); const nanoid = customAlphabet('1234567890abcdefghijklmnopqrstuvwxyz_', 4); - -// todo: tobe fixed -const META_TABLES = []; - const nanoidv2 = customAlphabet('1234567890abcdefghijklmnopqrstuvwxyz', 14); @Injectable() @@ -300,44 +296,6 @@ export class MetaService { // todo: need to fix private trx: Knex.Transaction; - // constructor(app: Noco, config: NcConfig, trx = null) { - // super(app, config); - // - // if (this.config?.meta?.db) { - // this.connection = trx || XKnex(this.config?.meta?.db); - // } else { - // let dbIndex = this.config.envs?.[this.config.workingEnv]?.db.findIndex( - // (c) => c.meta.dbAlias === this.config?.auth?.jwt?.dbAlias - // ); - // dbIndex = dbIndex === -1 ? 0 : dbIndex; - // this.connection = XKnex( - // this.config.envs?.[this.config.workingEnv]?.db[dbIndex] as any - // ); - // } - // this.trx = trx; - // NcConnectionMgr.setXcMeta(this); - // } - - // public get knexConnection(): XKnex { - // return (this.trx || this.connection) as any; - // } - - // public updateKnex(connectionConfig): void { - // this.connection = XKnex(connectionConfig); - // } - - // public async metaInit(): Promise { - // await this.connection.migrate.latest({ - // migrationSource: new XcMigrationSource(), - // tableName: 'xc_knex_migrations', - // }); - // await this.connection.migrate.latest({ - // migrationSource: new XcMigrationSourcev2(), - // tableName: 'xc_knex_migrationsv2', - // }); - // return true; - // } - /*** * Delete meta data * @param base_id - Base id @@ -657,40 +615,6 @@ export class MetaService { return await query; } - public async metaDeleteAll( - _project_id: string, - _dbAlias: string, - ): Promise { - NcError.notImplemented('metaDeleteAll'); - // await this.knexConnection..dropTableIfExists('nc_roles').; - // await this.knexConnection.schema.dropTableIfExists('nc_store').; - // await this.knexConnection.schema.dropTableIfExists('nc_hooks').; - // await this.knexConnection.schema.dropTableIfExists('nc_cron').; - // await this.knexConnection.schema.dropTableIfExists('nc_acl').; - } - - /*** - * Check table meta data exists for a given base id and db alias - * @param base_id - Base id - * @param dbAlias - Database alias - * @returns {Promise} - True if meta data exists, false otherwise - * */ - public async isMetaDataExists( - base_id: string, - dbAlias: string, - ): Promise { - const query = this.knexConnection('nc_models'); - if (base_id !== null && base_id !== undefined) { - query.where('base_id', base_id); - } - if (dbAlias !== null && dbAlias !== undefined) { - query.where('db_alias', dbAlias); - } - const data = await query.first(); - - return !!data; - } - async commit() { if (this.trx) { await this.trx.commit(); @@ -718,85 +642,6 @@ export class MetaService { return new MetaService(this.config, trx); } - async metaReset( - base_id: string, - dbAlias: string, - apiType?: string, - ): Promise { - // const apiType: string = this.config?.envs?.[this.config.env || this.config.workingEnv]?.db.find(d => { - // return d.meta.dbAlias === dbAlias; - // })?.meta?.api?.type; - - if (apiType) { - await Promise.all( - META_TABLES?.[apiType]?.map((table) => { - return (async () => { - try { - await this.knexConnection(table) - .where({ db_alias: dbAlias, base_id }) - .del(); - } catch (e) { - console.warn(`Error: ${table} reset failed`); - } - })(); - }), - ); - } - } - - /*** - * Create a new base - * @param baseName - Base name - * @param config - Base config - * @param description - Base description - * @param meta - If true, will create a meta base - * @returns {Promise} - Created base - * */ - public async baseCreate( - baseName: string, - config: any, - description?: string, - meta?: boolean, - ): Promise { - try { - const ranId = this.getNanoId(); - const id = `${baseName.toLowerCase().replace(/\W+/g, '_')}_${ranId}`; - if (meta) { - config.prefix = `nc_${ranId}__`; - // if(config.envs._noco?.db?.[0]?.meta?.tn){ - // config.envs._noco.db[0].meta.tn += `_${prefix}` - // } - } - config.id = id; - const base: any = { - id, - title: baseName, - description, - config: CryptoJS.AES.encrypt( - JSON.stringify(config), - 'secret', // todo: tobe replaced - this.config?.auth?.jwt?.secret - ).toString(), - }; - // todo: check base name used or not - await this.knexConnection('nc_projects').insert({ - ...base, - created_at: this.now(), - updated_at: this.now(), - }); - - // todo - await this.knexConnection(MetaTable.PROJECT).insert({ - id, - title: baseName, - }); - - base.prefix = config.prefix; - return base; - } catch (e) { - console.log(e); - } - } - /*** * Update base config * @param baseId - Base id @@ -840,251 +685,6 @@ export class MetaService { }); } - /*** - * Get base list with decrypted config for a user - * @returns {Promise} - List of bases - * */ - public async userProjectList(userId: any): Promise { - return ( - await this.knexConnection('nc_projects') - .leftJoin( - this.knexConnection('nc_projects_users') - .where(`nc_projects_users.user_id`, userId) - .as('user'), - 'user.base_id', - 'nc_projects.id', - ) - .select('nc_projects.*') - .select('user.user_id') - .select( - this.knexConnection('xc_users') - .select('xc_users.email') - .innerJoin( - 'nc_projects_users', - 'nc_projects_users.user_id', - '=', - 'xc_users.id', - ) - .whereRaw('nc_projects.id = nc_projects_users.base_id') - .where('nc_projects_users.roles', 'like', '%owner%') - .first() - .as('owner'), - ) - .select( - this.knexConnection('xc_users') - .count('xc_users.id') - .innerJoin( - 'nc_projects_users', - 'nc_projects_users.user_id', - '=', - 'xc_users.id', - ) - .where((qb) => { - qb.where('nc_projects_users.roles', 'like', '%creator%').orWhere( - 'nc_projects_users.roles', - 'like', - '%owner%', - ); - }) - .whereRaw('nc_projects.id = nc_projects_users.base_id') - .andWhere('xc_users.id', userId) - .first() - .as('is_creator'), - ) - ).map((p) => { - p.allowed = p.user_id === userId; - p.config = CryptoJS.AES.decrypt( - p.config, - 'secret', // todo: tobe replaced - this.config?.auth?.jwt?.secret - ).toString(CryptoJS.enc.Utf8); - return p; - }); - } - - /*** - * Check if user have access to a project - * @param baseId - Base id - * @param userId - User id - * @returns {Promise} - True if user have access, false otherwise - * */ - public async isUserHaveAccessToProject( - baseId: string, - userId: any, - ): Promise { - return !!(await this.knexConnection('nc_projects_users') - .where({ - base_id: baseId, - user_id: userId, - }) - .first()); - } - - /*** - * Get base by name - * @param baseName - Base name - * @param encrypt - If true, will skip the decryption of config - * @returns {Promise} - Base - * */ - public async baseGet(baseName: string, encrypt?): Promise { - const base = await this.knexConnection('nc_projects') - .where({ - title: baseName, - }) - .first(); - - if (base && !encrypt) { - base.config = CryptoJS.AES.decrypt( - base.config, - 'secret', // todo: tobe replaced - this.config?.auth?.jwt?.secret - ).toString(CryptoJS.enc.Utf8); - } - return base; - } - - /*** - * Get base by id - * @param baseId - Base id - * @param encrypt - If true, will skip the decryption of config - * @returns {Promise} - Base - * */ - public async baseGetById(baseId: string, encrypt?): Promise { - const base = await this.knexConnection('nc_projects') - .where({ - id: baseId, - }) - .first(); - if (base && !encrypt) { - base.config = CryptoJS.AES.decrypt( - base.config, - 'secret', // todo: tobe replaced - this.config?.auth?.jwt?.secret - ).toString(CryptoJS.enc.Utf8); - } - return base; - } - - /*** - * Delete base by name - * @param title - Base name - * */ - public baseDelete(title: string): Promise { - if (!title) { - NcError.metaError({ - message: 'Base title is required to delete base', - sql: '', - }); - } - - return this.knexConnection('nc_projects') - .where({ - title, - }) - .delete(); - } - - /*** - * Delete base by id - * @param id - Base id - * */ - public baseDeleteById(id: string): Promise { - if (!id) { - NcError.metaError({ - message: 'Base id is required to delete base', - sql: '', - }); - } - return this.knexConnection('nc_projects') - .where({ - id, - }) - .delete(); - } - - /*** - * Update base status - * @param baseId - Base id - * @param status - Base status - * */ - public async baseStatusUpdate(baseId: string, status: string): Promise { - if (!baseId) { - NcError.metaError({ - message: 'Base id is required to update base status', - sql: '', - }); - } - - return this.knexConnection('nc_projects') - .update({ - status, - }) - .where({ - id: baseId, - }); - } - - /*** - * Add user to base - * @param baseId - Base id - * @param userId - User id - * @param roles - User roles - * */ - public async baseAddUser( - baseId: string, - userId: any, - roles: string, - ): Promise { - if ( - await this.knexConnection('nc_projects_users') - .where({ - user_id: userId, - base_id: baseId, - }) - .first() - ) { - return {}; - } - return this.knexConnection('nc_projects_users').insert({ - user_id: userId, - base_id: baseId, - roles, - }); - } - - /*** - * Remove user from base - * @param baseId - Base id - * @param userId - User id - * */ - public baseRemoveUser(baseId: string, userId: any): Promise { - if (!baseId || !userId) { - NcError.metaError({ - message: 'Base id and user id is required to remove user from base', - sql: '', - }); - } - - return this.knexConnection('nc_projects_users') - .where({ - user_id: userId, - base_id: baseId, - }) - .delete(); - } - - public removeXcUser(userId: any): Promise { - if (!userId) { - NcError.metaError({ - message: 'User id is required to remove user', - sql: '', - }); - } - - return this.knexConnection('xc_users') - .where({ - id: userId, - }) - .delete(); - } - private getNanoId() { return nanoid(); } @@ -1102,18 +702,6 @@ export class MetaService { .format(this.isMySQL() ? 'YYYY-MM-DD HH:mm:ss' : 'YYYY-MM-DD HH:mm:ssZ'); } - public async audit( - base_id: string, - dbAlias: string, - target: string, - data: any, - ): Promise { - if (['DATA', 'COMMENT'].includes(data?.op_type)) { - return Promise.resolve(undefined); - } - return this.metaInsert(base_id, dbAlias, target, data); - } - public async init(): Promise { await this.connection.migrate.latest({ migrationSource: new XcMigrationSource(),