Browse Source

feat: errors v2 (#7916)

* feat: NcBaseErrorv2

* feat: move existing errors to v2 (WIP)

* feat: use functions instead of replace for templates

* feat: use v2 for database errors

* feat: experimental extraction for non-matched db errors

* feat: improved error message formats

* test: NcBaseErrorv2

* fix: move string casting to handler
pull/7925/head
Mert E 7 months ago committed by GitHub
parent
commit
af49ed346f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 5
      packages/nc-gui/composables/useSharedFormViewStore.ts
  2. 26
      packages/nocodb-sdk/src/lib/globals.ts
  3. 2
      packages/nocodb/src/controllers/old-datas/old-datas.service.ts
  4. 10
      packages/nocodb/src/controllers/public-datas-export.controller.ts
  5. 94
      packages/nocodb/src/db/BaseModelSqlv2.ts
  6. 2
      packages/nocodb/src/db/conditionV2.ts
  7. 4
      packages/nocodb/src/db/generateLookupSelectQuery.ts
  8. 2
      packages/nocodb/src/db/sortV2.ts
  9. 20
      packages/nocodb/src/filters/global-exception/global-exception.filter.ts
  10. 6
      packages/nocodb/src/helpers/NcPluginMgrv2.ts
  11. 2
      packages/nocodb/src/helpers/PagedResponse.ts
  12. 298
      packages/nocodb/src/helpers/catchError.ts
  13. 2
      packages/nocodb/src/helpers/getAst.ts
  14. 2
      packages/nocodb/src/middlewares/extract-ids/extract-ids.middleware.ts
  15. 2
      packages/nocodb/src/models/Filter.ts
  16. 2
      packages/nocodb/src/models/Model.ts
  17. 2
      packages/nocodb/src/models/Source.ts
  18. 6
      packages/nocodb/src/models/User.ts
  19. 7
      packages/nocodb/src/modules/datas/helpers.ts
  20. 6
      packages/nocodb/src/modules/jobs/jobs/export-import/export.service.ts
  21. 11
      packages/nocodb/src/modules/jobs/jobs/export-import/import.service.ts
  22. 4
      packages/nocodb/src/services/api-docs/api-docs.service.ts
  23. 8
      packages/nocodb/src/services/base-users/base-users.service.ts
  24. 2
      packages/nocodb/src/services/bases.service.ts
  25. 18
      packages/nocodb/src/services/calendar-datas.service.ts
  26. 2
      packages/nocodb/src/services/calendars.service.ts
  27. 26
      packages/nocodb/src/services/columns.service.ts
  28. 14
      packages/nocodb/src/services/data-alias-nested.service.ts
  29. 39
      packages/nocodb/src/services/data-table.service.ts
  30. 31
      packages/nocodb/src/services/datas.service.ts
  31. 2
      packages/nocodb/src/services/forms.service.ts
  32. 2
      packages/nocodb/src/services/galleries.service.ts
  33. 2
      packages/nocodb/src/services/grids.service.ts
  34. 2
      packages/nocodb/src/services/kanbans.service.ts
  35. 2
      packages/nocodb/src/services/maps.service.ts
  36. 2
      packages/nocodb/src/services/model-visibilities.service.ts
  37. 4
      packages/nocodb/src/services/org-users.service.ts
  38. 35
      packages/nocodb/src/services/public-datas.service.ts
  39. 7
      packages/nocodb/src/services/public-metas.service.ts
  40. 8
      packages/nocodb/src/services/shared-bases.service.ts
  41. 2
      packages/nocodb/src/services/tables.service.ts
  42. 10
      packages/nocodb/src/services/views.service.ts
  43. 58
      packages/nocodb/tests/unit/rest/tests/newDataApis.test.ts
  44. 26
      packages/nocodb/tests/unit/rest/tests/tableRow.test.ts

5
packages/nc-gui/composables/useSharedFormViewStore.ts

@ -12,7 +12,7 @@ import type {
StringOrNullType,
TableType,
} from 'nocodb-sdk'
import { ErrorMessages, RelationTypes, UITypes, isLinksOrLTAR, isSystemColumn, isVirtualCol } from 'nocodb-sdk'
import { RelationTypes, UITypes, isLinksOrLTAR, isSystemColumn, isVirtualCol } from 'nocodb-sdk'
import { isString } from '@vue/shared'
import { filterNullOrUndefinedObjectProperties } from '~/helpers/parsers/parserHelpers'
import {
@ -178,7 +178,8 @@ const [useProvideSharedFormStore, useSharedFormStore] = useInjectionState((share
} catch (e: any) {
if (e.response && e.response.status === 404) {
notFound.value = true
} else if ((await extractSdkResponseErrorMsg(e)) === ErrorMessages.INVALID_SHARED_VIEW_PASSWORD) {
// TODO - handle invalidSharedViewPassword
} else if (await extractSdkResponseErrorMsg(e)) {
passwordDlg.value = true
if (password.value && password.value !== '') passwordError.value = 'Something went wrong. Please check your credentials.'

26
packages/nocodb-sdk/src/lib/globals.ts

@ -28,11 +28,6 @@ export enum ExportTypes {
CSV = 'csv',
}
export enum ErrorMessages {
INVALID_SHARED_VIEW_PASSWORD = 'INVALID_SHARED_VIEW_PASSWORD',
NOT_IMPLEMENTED = 'NOT_IMPLEMENTED',
}
export enum AuditOperationTypes {
COMMENT = 'COMMENT',
DATA = 'DATA',
@ -129,6 +124,27 @@ export enum NcDataErrorCodes {
NC_ERR_MM_MODEL_NOT_FOUND = 'NC_ERR_MM_MODEL_NOT_FOUND',
}
export enum NcErrorType {
AUTHENTICATION_REQUIRED = 'AUTHENTICATION_REQUIRED',
API_TOKEN_NOT_ALLOWED = 'API_TOKEN_NOT_ALLOWED',
WORKSPACE_NOT_FOUND = 'WORKSPACE_NOT_FOUND',
BASE_NOT_FOUND = 'BASE_NOT_FOUND',
SOURCE_NOT_FOUND = 'SOURCE_NOT_FOUND',
TABLE_NOT_FOUND = 'TABLE_NOT_FOUND',
VIEW_NOT_FOUND = 'VIEW_NOT_FOUND',
FIELD_NOT_FOUND = 'FIELD_NOT_FOUND',
RECORD_NOT_FOUND = 'RECORD_NOT_FOUND',
ERROR_DUPLICATE_RECORD = 'ERROR_DUPLICATE_RECORD',
USER_NOT_FOUND = 'USER_NOT_FOUND',
INVALID_OFFSET_VALUE = 'INVALID_OFFSET_VALUE',
INVALID_LIMIT_VALUE = 'INVALID_LIMIT_VALUE',
INVALID_FILTER = 'INVALID_FILTER',
INVALID_SHARED_VIEW_PASSWORD = 'INVALID_SHARED_VIEW_PASSWORD',
NOT_IMPLEMENTED = 'NOT_IMPLEMENTED',
INTERNAL_SERVER_ERROR = 'INTERNAL_SERVER_ERROR',
DATABASE_ERROR = 'DATABASE_ERROR',
}
type Roles = OrgUserRoles | ProjectRoles | WorkspaceUserRoles;
type RolesObj = Partial<Record<Roles, boolean>>;

2
packages/nocodb/src/controllers/old-datas/old-datas.service.ts

@ -136,7 +136,7 @@ export class OldDatasService {
titleOrId: req.params.viewName,
fk_model_id: model.id,
}));
if (!model) NcError.notFound('Table not found');
if (!model) NcError.tableNotFound(req.params.tableName);
return { model, view };
}
}

10
packages/nocodb/src/controllers/public-datas-export.controller.ts

@ -6,7 +6,7 @@ import {
Response,
UseGuards,
} from '@nestjs/common';
import { ErrorMessages, isSystemColumn, ViewTypes } from 'nocodb-sdk';
import { isSystemColumn, ViewTypes } from 'nocodb-sdk';
import * as XLSX from 'xlsx';
import { nocoExecute } from 'nc-help';
import papaparse from 'papaparse';
@ -35,7 +35,7 @@ export class PublicDatasExportController {
@Param('publicDataUuid') publicDataUuid: string,
) {
const view = await View.getByUUID(publicDataUuid);
if (!view) NcError.notFound('Not found');
if (!view) NcError.viewNotFound(publicDataUuid);
if (
view.type !== ViewTypes.GRID &&
view.type !== ViewTypes.KANBAN &&
@ -45,7 +45,7 @@ export class PublicDatasExportController {
NcError.notFound('Not found');
if (view.password && view.password !== req.headers?.['xc-password']) {
NcError.forbidden(ErrorMessages.INVALID_SHARED_VIEW_PASSWORD);
NcError.invalidSharedViewPassword();
}
const model = await view.getModelWithInfo();
@ -88,7 +88,7 @@ export class PublicDatasExportController {
const view = await View.getByUUID(req.params.publicDataUuid);
const fields = req.query.fields;
if (!view) NcError.notFound('Not found');
if (!view) NcError.viewNotFound(req.params.publicDataUuid);
if (
view.type !== ViewTypes.GRID &&
view.type !== ViewTypes.KANBAN &&
@ -98,7 +98,7 @@ export class PublicDatasExportController {
NcError.notFound('Not found');
if (view.password && view.password !== req.headers?.['xc-password']) {
NcError.forbidden(ErrorMessages.INVALID_SHARED_VIEW_PASSWORD);
NcError.invalidSharedViewPassword();
}
const model = await view.getModelWithInfo();

94
packages/nocodb/src/db/BaseModelSqlv2.ts

@ -632,7 +632,7 @@ class BaseModelSqlv2 {
args.column_name.split(',').map(async (col) => {
let column = cols.find((c) => c.column_name === col || c.title === col);
if (!column) {
throw NcError.notFound('Column not found');
throw NcError.fieldNotFound(col);
}
// if qrCode or Barcode replace it with value column nd keep the alias
@ -851,7 +851,7 @@ class BaseModelSqlv2 {
(c) => c.column_name === col || c.title === col,
);
if (!column) {
throw NcError.notFound('Column not found');
throw NcError.fieldNotFound(col);
}
// if qrCode or Barcode replace it with value column nd keep the alias
@ -3683,9 +3683,7 @@ class BaseModelSqlv2 {
if (!pkValues) {
// throw or skip if no pk provided
if (throwExceptionIfNotExist) {
NcError.unprocessableEntity(
`Record with pk ${JSON.stringify(pkValues)} not found`,
);
NcError.recordNotFound(JSON.stringify(pkValues));
}
continue;
}
@ -3696,9 +3694,7 @@ class BaseModelSqlv2 {
if (!oldRecord) {
// throw or skip if no record found
if (throwExceptionIfNotExist) {
NcError.unprocessableEntity(
`Record with pk ${JSON.stringify(pkValues)} not found`,
);
NcError.recordNotFound(JSON.stringify(pkValues));
}
continue;
}
@ -3853,9 +3849,7 @@ class BaseModelSqlv2 {
if (!pkValues) {
// throw or skip if no pk provided
if (throwExceptionIfNotExist) {
NcError.unprocessableEntity(
`Record with pk ${JSON.stringify(pkValues)} not found`,
);
NcError.recordNotFound(JSON.stringify(pkValues));
}
continue;
}
@ -3864,9 +3858,7 @@ class BaseModelSqlv2 {
if (!deletedRecord) {
// throw or skip if no record found
if (throwExceptionIfNotExist) {
NcError.unprocessableEntity(
`Record with pk ${JSON.stringify(pkValues)} not found`,
);
NcError.recordNotFound(JSON.stringify(pkValues));
}
continue;
}
@ -4430,7 +4422,7 @@ class BaseModelSqlv2 {
!column ||
![UITypes.LinkToAnotherRecord, UITypes.Links].includes(column.uidt)
)
NcError.notFound('Column not found');
NcError.fieldNotFound(colId);
const colOptions = await column.getColOptions<LinkToAnotherRecordColumn>();
@ -4650,7 +4642,7 @@ class BaseModelSqlv2 {
!column ||
![UITypes.LinkToAnotherRecord, UITypes.Links].includes(column.uidt)
)
NcError.notFound('Column not found');
NcError.fieldNotFound(colId);
const colOptions = await column.getColOptions<LinkToAnotherRecordColumn>();
@ -4818,9 +4810,9 @@ class BaseModelSqlv2 {
.getColumns()
.then((cols) => cols?.find((col) => col.id === args.groupColumnId));
if (!column) NcError.notFound('Column not found');
if (!column) NcError.fieldNotFound(args.groupColumnId);
if (isVirtualCol(column))
NcError.notImplemented('Grouping for virtual columns not implemented');
NcError.notImplemented('Grouping for virtual columns');
// extract distinct group column values
let groupingValues: Set<any>;
@ -4980,9 +4972,9 @@ class BaseModelSqlv2 {
.getColumns()
.then((cols) => cols?.find((col) => col.id === args.groupColumnId));
if (!column) NcError.notFound('Column not found');
if (!column) NcError.fieldNotFound(args.groupColumnId);
if (isVirtualCol(column))
NcError.notImplemented('Grouping for virtual columns not implemented');
NcError.notImplemented('Grouping for virtual columns');
const qb = this.dbDriver(this.tnPath)
.count('*', { as: 'count' })
@ -5594,8 +5586,7 @@ class BaseModelSqlv2 {
const columns = await this.model.getColumns();
const column = columns.find((c) => c.id === colId);
if (!column || !isLinksOrLTAR(column))
NcError.notFound(`Link column ${colId} not found`);
if (!column || !isLinksOrLTAR(column)) NcError.fieldNotFound(colId);
const row = await this.readByPk(
rowId,
@ -5606,7 +5597,7 @@ class BaseModelSqlv2 {
// validate rowId
if (!row) {
NcError.notFound(`Record with id '${rowId}' not found`);
NcError.recordNotFound(rowId);
}
if (!_childIds.length) return;
@ -5717,11 +5708,7 @@ class BaseModelSqlv2 {
!childRows.find((r) => r[parentColumn.column_name] === id),
);
NcError.unprocessableEntity(
`Child record with id [${extractIdsString(
missingIds,
)}] not found`,
);
NcError.recordNotFound(extractIds(missingIds));
}
insertData = childRows
@ -5794,11 +5781,7 @@ class BaseModelSqlv2 {
!childRows.find((r) => r[parentColumn.column_name] === id),
);
NcError.unprocessableEntity(
`Child record with id [${extractIdsString(
missingIds,
)}] not found`,
);
NcError.recordNotFound(extractIds(missingIds));
}
}
const updateQb = this.dbDriver(childTn).update({
@ -5852,12 +5835,7 @@ class BaseModelSqlv2 {
});
if (!childRow) {
NcError.unprocessableEntity(
`Child record with id [${extractIdsString(
childIds,
true,
)}] not found`,
);
NcError.recordNotFound(extractIds(childIds, true));
}
}
@ -5913,8 +5891,7 @@ class BaseModelSqlv2 {
const columns = await this.model.getColumns();
const column = columns.find((c) => c.id === colId);
if (!column || !isLinksOrLTAR(column))
NcError.notFound(`Link column ${colId} not found`);
if (!column || !isLinksOrLTAR(column)) NcError.fieldNotFound(colId);
const row = await this.readByPk(
rowId,
@ -5925,7 +5902,7 @@ class BaseModelSqlv2 {
// validate rowId
if (!row) {
NcError.notFound(`Record with id '${rowId}' not found`);
NcError.recordNotFound(rowId);
}
if (!childIds.length) return;
@ -5999,11 +5976,7 @@ class BaseModelSqlv2 {
),
);
NcError.unprocessableEntity(
`Child record with id [${extractIdsString(
missingIds,
)}] not found`,
);
NcError.recordNotFound(extractIds(missingIds));
}
}
@ -6086,11 +6059,7 @@ class BaseModelSqlv2 {
),
);
NcError.unprocessableEntity(
`Child record with id [${extractIdsString(
missingIds,
)}] not found`,
);
NcError.recordNotFound(extractIds(missingIds));
}
}
@ -6148,12 +6117,7 @@ class BaseModelSqlv2 {
});
if (!childRow) {
NcError.unprocessableEntity(
`Child record with id [${extractIdsString(
childIds,
true,
)}] not found`,
);
NcError.recordNotFound(extractIds(childIds, true));
}
}
@ -6213,7 +6177,7 @@ class BaseModelSqlv2 {
// validate rowId
if (!row) {
NcError.notFound(`Record with id ${id} not found`);
NcError.recordNotFound(id);
}
const parentCol = await (
@ -6501,7 +6465,7 @@ export function extractSortsObject(
else sort.fk_column_id = aliasColObjMap[s.replace(/^\+/, '')]?.id;
if (throwErrorIfInvalid && !sort.fk_column_id)
NcError.unprocessableEntity(`Invalid field: ${s.replace(/^[+-]/, '')}`);
NcError.fieldNotFound(s.replace(/^[+-]/, ''));
return new Sort(sort);
});
@ -6672,7 +6636,7 @@ export function extractCondition(
validateFilterComparison(aliasColObjMap[alias].uidt, op, sub_op);
} else if (throwErrorIfInvalid) {
NcError.unprocessableEntity(`Invalid field: ${alias}`);
NcError.invalidFilter(str);
}
return new Filter({
@ -6828,13 +6792,13 @@ export function getListArgs(
return obj;
}
function extractIdsString(
function extractIds(
childIds: (string | number | Record<string, any>)[],
isBt = false,
) {
return (isBt ? childIds.slice(0, 1) : childIds)
.map((r) => (typeof r === 'object' ? JSON.stringify(r) : r))
.join(', ');
return (isBt ? childIds.slice(0, 1) : childIds).map((r) =>
typeof r === 'object' ? JSON.stringify(r) : `${r}`,
);
}
export { BaseModelSqlv2 };

2
packages/nocodb/src/db/conditionV2.ts

@ -167,7 +167,7 @@ const parseConditionV2 = async (
const column = await getRefColumnIfAlias(await filter.getColumn());
if (!column) {
if (throwErrorIfInvalid) {
NcError.unprocessableEntity(`Invalid field: ${filter.fk_column_id}`);
NcError.fieldNotFound(filter.fk_column_id);
}
return;
}

4
packages/nocodb/src/db/generateLookupSelectQuery.ts

@ -434,8 +434,6 @@ export default async function generateLookupSelectQuery({
};
}
NcError.notImplemented(
'Database not supported this operation on Lookup/LTAR',
);
NcError.notImplemented('This operation on Lookup/LTAR for this database');
}
}

2
packages/nocodb/src/db/sortV2.ts

@ -33,7 +33,7 @@ export default async function sortV2(
const column = await getRefColumnIfAlias(await sort.getColumn());
if (!column) {
if (throwErrorIfInvalid) {
NcError.unprocessableEntity(`Invalid field: ${sort.fk_column_id}`);
NcError.fieldNotFound(sort.fk_column_id);
}
continue;
}

20
packages/nocodb/src/filters/global-exception/global-exception.filter.ts

@ -8,9 +8,8 @@ import {
BadRequest,
extractDBError,
Forbidden,
InternalServerError,
NcBaseErrorv2,
NotFound,
NotImplemented,
Unauthorized,
UnprocessableEntity,
} from '~/helpers/catchError';
@ -37,7 +36,6 @@ export class GlobalExceptionFilter implements ExceptionFilter {
exception instanceof Unauthorized ||
exception instanceof Forbidden ||
exception instanceof NotFound ||
exception instanceof NotImplemented ||
exception instanceof UnprocessableEntity ||
exception instanceof NotFoundException ||
exception instanceof ThrottlerException
@ -83,22 +81,18 @@ export class GlobalExceptionFilter implements ExceptionFilter {
exception.getStatus?.() === 404
) {
return response.status(404).json({ msg: exception.message });
} else if (
exception instanceof InternalServerError ||
exception.getStatus?.() === 500
) {
return response.status(500).json({ msg: exception.message });
} else if (
exception instanceof NotImplemented ||
exception.getStatus?.() === 501
) {
return response.status(501).json({ msg: exception.message });
} else if (exception instanceof AjvError) {
return response
.status(400)
.json({ msg: exception.message, errors: exception.errors });
} else if (exception instanceof UnprocessableEntity) {
return response.status(422).json({ msg: exception.message });
} else if (exception instanceof NcBaseErrorv2) {
return response.status(exception.code).json({
error: exception.error,
message: exception.message,
details: exception.details,
});
}
// handle different types of exceptions

6
packages/nocodb/src/helpers/NcPluginMgrv2.ts

@ -249,7 +249,7 @@ class NcPluginMgrv2 {
await tempPlugin.init(args?.input);
if (!tempPlugin?.getAdapter()?.test)
NcError.notImplemented('Plugin test is not implemented');
NcError.notImplemented('Plugin Test');
return tempPlugin?.getAdapter()?.test?.();
}
@ -263,7 +263,7 @@ class NcPluginMgrv2 {
await tempPlugin.init(args?.input);
if (!tempPlugin?.getAdapter()?.test)
NcError.notImplemented('Plugin test is not implemented');
NcError.notImplemented('Plugin Test');
return tempPlugin?.getAdapter()?.test?.();
}
@ -276,7 +276,7 @@ class NcPluginMgrv2 {
await tempPlugin.init(args?.input);
if (!tempPlugin?.getAdapter()?.test)
NcError.notImplemented('Plugin test is not implemented');
NcError.notImplemented('Plugin Test');
return tempPlugin?.getAdapter()?.test?.();
}

2
packages/nocodb/src/helpers/PagedResponse.ts

@ -42,7 +42,7 @@ export class PagedResponseImpl<T> {
if (additionalProps) Object.assign(this, additionalProps);
if (offset && offset >= +count) {
NcError.badRequest('Offset is beyond the total number of records');
NcError.invalidOffsetValue(offset);
}
}

298
packages/nocodb/src/helpers/catchError.ts

@ -1,5 +1,7 @@
import { NcErrorType } from 'nocodb-sdk';
import type { NextFunction, Request, Response } from 'express';
import type { ErrorObject } from 'ajv';
import { defaultLimitConfig } from '~/helpers/extractLimitAndOffset';
export enum DBError {
TABLE_EXIST = 'TABLE_EXIST',
@ -372,6 +374,12 @@ export function extractDBError(error): {
case 'EHOSTDOWN':
message = 'The host is down.';
break;
default:
// if error message contains -- then extract message after --
if (error.message && error.message.includes('--')) {
message = error.message.split('--')[1];
}
break;
}
if (message) {
@ -400,7 +408,6 @@ export default function (
e instanceof Unauthorized ||
e instanceof Forbidden ||
e instanceof NotFound ||
e instanceof NotImplemented ||
e instanceof UnprocessableEntity
)
)
@ -409,7 +416,15 @@ export default function (
const dbError = extractDBError(e);
if (dbError) {
return res.status(400).json(dbError);
const error = new NcBaseErrorv2(NcErrorType.DATABASE_ERROR, {
params: dbError.message,
details: dbError,
});
return res.status(error.code).json({
error: error.error,
message: error.message,
details: error.details,
});
}
if (e instanceof BadRequest) {
@ -420,16 +435,16 @@ export default function (
return res.status(403).json({ msg: e.message });
} else if (e instanceof NotFound) {
return res.status(404).json({ msg: e.message });
} else if (e instanceof InternalServerError) {
return res.status(500).json({ msg: e.message });
} else if (e instanceof NotImplemented) {
return res.status(501).json({ msg: e.message });
} else if (e instanceof AjvError) {
return res.status(400).json({ msg: e.message, errors: e.errors });
} else if (e instanceof UnprocessableEntity) {
return res.status(422).json({ msg: e.message });
} else if (e instanceof NotAllowed) {
return res.status(405).json({ msg: e.message });
} else if (e instanceof NcBaseErrorv2) {
return res
.status(e.code)
.json({ error: e.error, message: e.message, details: e.details });
}
// if some other error occurs then send 500 and a generic message
res.status(500).json({ msg: 'Internal server error' });
@ -453,10 +468,6 @@ export class Forbidden extends NcBaseError {}
export class NotFound extends NcBaseError {}
export class InternalServerError extends NcBaseError {}
export class NotImplemented extends NcBaseError {}
export class UnprocessableEntity extends NcBaseError {}
export class AjvError extends NcBaseError {
@ -468,7 +479,266 @@ export class AjvError extends NcBaseError {
errors: ErrorObject[];
}
const errorHelpers: {
[key in NcErrorType]: {
message: string | ((...params: string[]) => string);
code: number;
};
} = {
[NcErrorType.INTERNAL_SERVER_ERROR]: {
message: (message: string) => message || `Internal server error`,
code: 500,
},
[NcErrorType.DATABASE_ERROR]: {
message: (message: string) =>
message || `There was an error while running the query`,
code: 500,
},
[NcErrorType.AUTHENTICATION_REQUIRED]: {
message: 'Authentication required to access this resource',
code: 401,
},
[NcErrorType.API_TOKEN_NOT_ALLOWED]: {
message: 'This request is not allowed with API token',
code: 401,
},
[NcErrorType.WORKSPACE_NOT_FOUND]: {
message: (id: string) => `Workspace '${id}' not found`,
code: 404,
},
[NcErrorType.BASE_NOT_FOUND]: {
message: (id: string) => `Base '${id}' not found`,
code: 404,
},
[NcErrorType.SOURCE_NOT_FOUND]: {
message: (id: string) => `Source '${id}' not found`,
code: 404,
},
[NcErrorType.TABLE_NOT_FOUND]: {
message: (id: string) => `Table '${id}' not found`,
code: 404,
},
[NcErrorType.VIEW_NOT_FOUND]: {
message: (id: string) => `View '${id}' not found`,
code: 404,
},
[NcErrorType.FIELD_NOT_FOUND]: {
message: (id: string) => `Field '${id}' not found`,
code: 404,
},
[NcErrorType.RECORD_NOT_FOUND]: {
message: (...ids: string[]) => {
const isMultiple = Array.isArray(ids) && ids.length > 1;
return `Record${isMultiple ? 's' : ''} '${ids.join(', ')}' not found`;
},
code: 404,
},
[NcErrorType.ERROR_DUPLICATE_RECORD]: {
message: (...ids: string[]) => {
const isMultiple = Array.isArray(ids) && ids.length > 1;
return `Record${isMultiple ? 's' : ''} '${ids.join(
', ',
)}' already exists`;
},
code: 422,
},
[NcErrorType.USER_NOT_FOUND]: {
message: (idOrEmail: string) => {
const isEmail = idOrEmail.includes('@');
return `User ${
isEmail ? 'with email' : 'with id'
} '${idOrEmail}' not found`;
},
code: 404,
},
[NcErrorType.INVALID_OFFSET_VALUE]: {
message: (offset: string) => `Offset value '${offset}' is invalid`,
code: 422,
},
[NcErrorType.INVALID_LIMIT_VALUE]: {
message: `Limit value should be between ${defaultLimitConfig.limitMin} and ${defaultLimitConfig.limitMax}`,
code: 422,
},
[NcErrorType.INVALID_FILTER]: {
message: (filter: string) => `Filter '${filter}' is invalid`,
code: 422,
},
[NcErrorType.INVALID_SHARED_VIEW_PASSWORD]: {
message: 'Invalid shared view password',
code: 403,
},
[NcErrorType.NOT_IMPLEMENTED]: {
message: (feature: string) => `${feature} is not implemented`,
code: 501,
},
};
function generateError(
type: NcErrorType,
args?: NcErrorArgs,
): {
message: string;
code: number;
details?: any;
} {
const errorHelper = errorHelpers[type];
const { params, customMessage, details } = args || {};
if (!errorHelper) {
return {
message: 'An error occurred',
code: 500,
details: details,
};
}
let message: string;
const messageHelper = customMessage || errorHelper.message;
if (typeof messageHelper === 'function') {
message = messageHelper(...(Array.isArray(params) ? params : [params]));
} else {
message = messageHelper;
}
return {
message,
code: errorHelper.code,
details: details,
};
}
type NcErrorArgs = {
params?: string | string[];
customMessage?: string | ((...args: string[]) => string);
details?: any;
};
export class NcBaseErrorv2 extends NcBaseError {
error: NcErrorType;
code: number;
details?: any;
constructor(error: NcErrorType, args?: NcErrorArgs) {
const errorHelper = generateError(error, args);
super(errorHelper.message);
this.error = error;
this.code = errorHelper.code;
this.details = args?.details;
}
}
export class NcError {
static authenticationRequired(args?: NcErrorArgs) {
throw new NcBaseErrorv2(NcErrorType.AUTHENTICATION_REQUIRED, args);
}
static apiTokenNotAllowed(args?: NcErrorArgs) {
throw new NcBaseErrorv2(NcErrorType.API_TOKEN_NOT_ALLOWED, args);
}
static workspaceNotFound(id: string, args?: NcErrorArgs) {
throw new NcBaseErrorv2(NcErrorType.WORKSPACE_NOT_FOUND, {
params: id,
...args,
});
}
static baseNotFound(id: string, args?: NcErrorArgs) {
throw new NcBaseErrorv2(NcErrorType.BASE_NOT_FOUND, {
params: id,
...args,
});
}
static sourceNotFound(id: string, args?: NcErrorArgs) {
throw new NcBaseErrorv2(NcErrorType.SOURCE_NOT_FOUND, {
params: id,
...args,
});
}
static tableNotFound(id: string, args?: NcErrorArgs) {
throw new NcBaseErrorv2(NcErrorType.TABLE_NOT_FOUND, {
params: id,
...args,
});
}
static userNotFound(id: string, args?: NcErrorArgs) {
throw new NcBaseErrorv2(NcErrorType.USER_NOT_FOUND, {
params: id,
...args,
});
}
static viewNotFound(id: string, args?: NcErrorArgs) {
throw new NcBaseErrorv2(NcErrorType.VIEW_NOT_FOUND, {
params: id,
...args,
});
}
static recordNotFound(id: string | string[], args?: NcErrorArgs) {
throw new NcBaseErrorv2(NcErrorType.RECORD_NOT_FOUND, {
params: id,
...args,
});
}
static duplicateRecord(id: string | string[], args?: NcErrorArgs) {
throw new NcBaseErrorv2(NcErrorType.ERROR_DUPLICATE_RECORD, {
params: id,
...args,
});
}
static fieldNotFound(id: string, args?: NcErrorArgs) {
throw new NcBaseErrorv2(NcErrorType.FIELD_NOT_FOUND, {
params: id,
...args,
});
}
static invalidOffsetValue(offset: string | number, args?: NcErrorArgs) {
throw new NcBaseErrorv2(NcErrorType.INVALID_OFFSET_VALUE, {
params: `${offset}`,
...args,
});
}
static invalidLimitValue(args?: NcErrorArgs) {
throw new NcBaseErrorv2(NcErrorType.INVALID_LIMIT_VALUE, {
...args,
});
}
static invalidFilter(filter: string, args?: NcErrorArgs) {
throw new NcBaseErrorv2(NcErrorType.INVALID_FILTER, {
params: filter,
...args,
});
}
static invalidSharedViewPassword(args?: NcErrorArgs) {
throw new NcBaseErrorv2(NcErrorType.INVALID_SHARED_VIEW_PASSWORD, {
...args,
});
}
static notImplemented(feature: string = 'Feature', args?: NcErrorArgs) {
throw new NcBaseErrorv2(NcErrorType.NOT_IMPLEMENTED, {
params: feature,
...args,
});
}
static internalServerError(message: string, args?: NcErrorArgs) {
throw new NcBaseErrorv2(NcErrorType.INTERNAL_SERVER_ERROR, {
params: message,
...args,
});
}
static notFound(message = 'Not found') {
throw new NotFound(message);
}
@ -485,14 +755,6 @@ export class NcError {
throw new Forbidden(message);
}
static internalServerError(message = 'Internal server error') {
throw new InternalServerError(message);
}
static notImplemented(message = 'Not implemented') {
throw new NotImplemented(message);
}
static ajvValidationError(param: { message: string; errors: ErrorObject[] }) {
throw new AjvError(param);
}

2
packages/nocodb/src/helpers/getAst.ts

@ -115,7 +115,7 @@ const getAst = async ({
(f) => !colAliasMap[f] && !aliasColMap[f],
);
if (invalidFields.length) {
NcError.unprocessableEntity(`Invalid field: ${invalidFields[0]}`);
NcError.fieldNotFound(invalidFields.join(', '));
}
}
} else {

2
packages/nocodb/src/middlewares/extract-ids/extract-ids.middleware.ts

@ -253,7 +253,7 @@ export class AclMiddleware implements NestInterceptor {
const roles: Record<string, boolean> = extractRolesObj(userScopeRole);
if (req?.user?.is_api_token && blockApiTokenAccess) {
NcError.forbidden('Not allowed with API token');
NcError.apiTokenNotAllowed();
}
if (
(!allowedRoles || allowedRoles.some((role) => roles?.[role])) &&

2
packages/nocodb/src/models/Filter.ts

@ -96,7 +96,7 @@ export default class Filter implements FilterType {
} else if (filter.fk_column_id) {
model = await Column.get({ colId: filter.fk_column_id }, ncMeta);
} else {
NcError.badRequest('Invalid filter');
NcError.invalidFilter(JSON.stringify(filter));
}
if (model != null) {

2
packages/nocodb/src/models/Model.ts

@ -765,7 +765,7 @@ export default class Model implements TableType {
const model = await this.getWithInfo({ id: tableId });
const newPvCol = model.columns.find((c) => c.id === columnId);
if (!newPvCol) NcError.badRequest('Column not found');
if (!newPvCol) NcError.fieldNotFound(columnId);
// drop existing primary column/s
for (const col of model.columns?.filter((c) => c.pv) || []) {

2
packages/nocodb/src/models/Source.ts

@ -112,7 +112,7 @@ export default class Source implements SourceType {
) {
const oldBase = await Source.get(sourceId, false, ncMeta);
if (!oldBase) NcError.badRequest('Wrong source id!');
if (!oldBase) NcError.sourceNotFound(sourceId);
const updateObj = extractProps(source, [
'alias',

6
packages/nocodb/src/models/User.ts

@ -249,7 +249,7 @@ export default class User implements UserType {
const user = await this.get(userId, ncMeta);
if (!user) NcError.badRequest('User not found');
if (!user) NcError.userNotFound(userId);
// clear all user related cache
await this.clearCache(userId, ncMeta);
@ -267,7 +267,7 @@ export default class User implements UserType {
) {
const user = args.user ?? (await this.get(userId, ncMeta));
if (!user) NcError.badRequest('User not found');
if (!user) NcError.userNotFound(userId);
const baseRoles = await new Promise((resolve) => {
if (args.baseId) {
@ -295,7 +295,7 @@ export default class User implements UserType {
protected static async clearCache(userId: string, ncMeta = Noco.ncMeta) {
const user = await this.get(userId, ncMeta);
if (!user) NcError.badRequest('User not found');
if (!user) NcError.userNotFound(userId);
// todo: skip base user cache delete based on flag
const bases = await BaseUser.getProjectsList(userId, {}, ncMeta);

7
packages/nocodb/src/modules/datas/helpers.ts

@ -37,7 +37,7 @@ export async function getViewAndModelByAliasOrId(param: {
aliasOrId: param.tableName,
});
if (!model) NcError.notFound('Table not found');
if (!model) NcError.tableNotFound(param.tableName);
const view =
param.viewName &&
@ -45,7 +45,7 @@ export async function getViewAndModelByAliasOrId(param: {
titleOrId: param.viewName,
fk_model_id: model.id,
}));
if (param.viewName && !view) NcError.notFound('View not found');
if (param.viewName && !view) NcError.viewNotFound(param.viewName);
return { model, view };
}
@ -234,8 +234,7 @@ export async function getColumnByIdOrName(
c.column_name === columnNameOrId,
);
if (!column)
NcError.notFound(`Column with id/name '${columnNameOrId}' is not found`);
if (!column) NcError.fieldNotFound(columnNameOrId);
return column;
}

6
packages/nocodb/src/modules/jobs/jobs/export-import/export.service.ts

@ -47,8 +47,7 @@ export class ExportService {
let pgSerialLastVal;
if (!model)
return NcError.badRequest(`Model not found for id '${modelId}'`);
if (!model) return NcError.tableNotFound(modelId);
const fndProject = bases.find((p) => p.id === model.base_id);
const base = fndProject || (await Base.get(model.base_id));
@ -703,8 +702,7 @@ export class ExportService {
const source = await Source.get(param.sourceId);
if (!source)
throw NcError.badRequest(`Source not found for id '${param.sourceId}'`);
if (!source) NcError.sourceNotFound(param.sourceId);
const base = await Base.get(source.base_id);

11
packages/nocodb/src/modules/jobs/jobs/export-import/import.service.ts

@ -94,13 +94,11 @@ export class ImportService {
const base = await Base.get(param.baseId);
if (!base)
return NcError.badRequest(`Base not found for id '${param.baseId}'`);
if (!base) return NcError.baseNotFound(param.baseId);
const source = await Source.get(param.sourceId);
if (!source)
return NcError.badRequest(`Source not found for id '${param.sourceId}'`);
if (!source) return NcError.sourceNotFound(param.sourceId);
const tableReferences = new Map<string, Model>();
const linkMap = new Map<string, string>();
@ -1357,9 +1355,8 @@ export class ImportService {
const destProject = await Base.get(baseId);
const destBase = await Source.get(sourceId);
if (!destProject || !destBase) {
throw NcError.badRequest('Base or Source not found');
}
if (!destProject) return NcError.baseNotFound(baseId);
if (!destBase) return NcError.sourceNotFound(sourceId);
switch (src.type) {
case 'local': {

4
packages/nocodb/src/services/api-docs/api-docs.service.ts

@ -9,7 +9,7 @@ export class ApiDocsService {
async swaggerJson(param: { baseId: string; siteUrl: string }) {
const base = await Base.get(param.baseId);
if (!base) NcError.notFound();
if (!base) NcError.baseNotFound(param.baseId);
const models = await Model.list({
base_id: param.baseId,
@ -38,7 +38,7 @@ export class ApiDocsService {
async swaggerJsonV2(param: { baseId: string; siteUrl: string }) {
const base = await Base.get(param.baseId);
if (!base) NcError.notFound();
if (!base) NcError.baseNotFound(param.baseId);
const models = await Model.list({
base_id: param.baseId,

8
packages/nocodb/src/services/base-users/base-users.service.ts

@ -92,7 +92,7 @@ export class BaseUsersService {
const base = await Base.get(param.baseId);
if (!base) {
return NcError.badRequest('Invalid base id');
return NcError.baseNotFound(param.baseId);
}
if (user) {
@ -102,7 +102,7 @@ export class BaseUsersService {
const base = await Base.get(param.baseId);
if (!base) {
return NcError.badRequest('Invalid base id');
return NcError.baseNotFound(param.baseId);
}
if (baseUser && baseUser.roles) {
@ -200,7 +200,7 @@ export class BaseUsersService {
const base = await Base.get(param.baseId);
if (!base) {
return NcError.badRequest('Invalid base id');
return NcError.baseNotFound(param.baseId);
}
if (param.baseUser.roles.includes(ProjectRoles.OWNER)) {
@ -306,7 +306,7 @@ export class BaseUsersService {
const base = await Base.get(param.baseId);
if (!base) {
return NcError.badRequest('Invalid base id');
return NcError.baseNotFound(param.baseId);
}
const invite_token = uuidv4();

2
packages/nocodb/src/services/bases.service.ts

@ -111,7 +111,7 @@ export class BasesService {
const base = await Base.getWithInfo(param.baseId);
if (!base) {
NcError.notFound('Base not found');
NcError.baseNotFound(param.baseId);
}
await Base.softDelete(param.baseId);

18
packages/nocodb/src/services/calendar-datas.service.ts

@ -1,5 +1,5 @@
import { Injectable, Logger } from '@nestjs/common';
import { ErrorMessages, ViewTypes } from 'nocodb-sdk';
import { ViewTypes } from 'nocodb-sdk';
import dayjs from 'dayjs';
import type { CalendarRangeType, FilterType } from 'nocodb-sdk';
import { CalendarRange, Model, View } from '~/models';
@ -29,7 +29,7 @@ export class CalendarDatasService {
const view = await View.get(viewId);
if (!view) NcError.notFound('View not found');
if (!view) NcError.viewNotFound(viewId);
if (view.type !== ViewTypes.CALENDAR)
NcError.badRequest('View is not a calendar view');
@ -70,13 +70,13 @@ export class CalendarDatasService {
const { sharedViewUuid, password, query = {} } = param;
const view = await View.getByUUID(sharedViewUuid);
if (!view) NcError.notFound('Not found');
if (!view) NcError.viewNotFound(sharedViewUuid);
if (view.type !== ViewTypes.CALENDAR) {
NcError.notFound('Not found');
NcError.notFound('View is not a calendar view');
}
if (view.password && view.password !== password) {
return NcError.forbidden(ErrorMessages.INVALID_SHARED_VIEW_PASSWORD);
return NcError.invalidSharedViewPassword();
}
return this.getCalendarRecordCount({
@ -97,13 +97,13 @@ export class CalendarDatasService {
const { sharedViewUuid, password, query = {} } = param;
const view = await View.getByUUID(sharedViewUuid);
if (!view) NcError.notFound('Not found');
if (!view) NcError.viewNotFound(sharedViewUuid);
if (view.type !== ViewTypes.CALENDAR) {
NcError.notFound('Not found');
NcError.notFound('View is not a calendar view');
}
if (view.password && view.password !== password) {
return NcError.forbidden(ErrorMessages.INVALID_SHARED_VIEW_PASSWORD);
return NcError.invalidSharedViewPassword();
}
return this.getCalendarDataList({
@ -131,7 +131,7 @@ export class CalendarDatasService {
const view = await View.get(viewId);
if (!view) NcError.notFound('View not found');
if (!view) NcError.viewNotFound(viewId);
if (view.type !== ViewTypes.CALENDAR)
NcError.badRequest('View is not a calendar view');

2
packages/nocodb/src/services/calendars.service.ts

@ -77,7 +77,7 @@ export class CalendarsService {
const view = await View.get(param.calendarViewId);
if (!view) {
NcError.badRequest('View not found');
NcError.viewNotFound(param.calendarViewId);
}
const res = await CalendarView.update(param.calendarViewId, param.calendar);

26
packages/nocodb/src/services/columns.service.ts

@ -306,9 +306,7 @@ export class ColumnsService {
await this.updateRollupOrLookup(colBody, column);
} else {
NcError.notImplemented(
`Updating ${colBody.uidt} => ${colBody.uidt} is not implemented`,
);
NcError.notImplemented(`Updating ${colBody.uidt} => ${colBody.uidt}`);
}
} else if (
[
@ -321,9 +319,7 @@ export class ColumnsService {
UITypes.ForeignKey,
].includes(colBody.uidt)
) {
NcError.notImplemented(
`Updating ${colBody.uidt} => ${colBody.uidt} is not implemented`,
);
NcError.notImplemented(`Updating ${colBody.uidt} => ${colBody.uidt}`);
} else if (
[
UITypes.CreatedTime,
@ -1317,9 +1313,7 @@ export class ColumnsService {
...colBody,
});
} else {
NcError.notImplemented(
`Updating ${column.uidt} => ${colBody.uidt} is not supported at the moment`,
);
NcError.notImplemented(`Updating ${column.uidt} => ${colBody.uidt}`);
}
} else if (column.uidt === UITypes.User) {
if ([UITypes.SingleLineText, UITypes.Email].includes(colBody.uidt)) {
@ -1412,9 +1406,7 @@ export class ColumnsService {
...colBody,
});
} else {
NcError.notImplemented(
`Updating ${column.uidt} => ${colBody.uidt} is not supported at the moment`,
);
NcError.notImplemented(`Updating ${column.uidt} => ${colBody.uidt}`);
}
} else {
colBody = await getColumnPropsFromUIDT(colBody, source);
@ -2286,7 +2278,7 @@ export class ColumnsService {
});
break;
case UITypes.ForeignKey: {
NcError.notImplemented();
NcError.notImplemented(`Support for ${column.uidt}`);
break;
}
case UITypes.SingleSelect: {
@ -3135,7 +3127,7 @@ export class ColumnsService {
});
if (!table) {
NcError.badRequest('Table not found');
NcError.tableNotFound(tableId);
}
const columns = await table.getColumns();
@ -3163,7 +3155,7 @@ export class ColumnsService {
});
if (!table) {
NcError.badRequest('Table not found');
NcError.tableNotFound(tableId);
}
const columns = await table.getColumns();
@ -3177,13 +3169,13 @@ export class ColumnsService {
const source = await Source.get(table.source_id);
if (!source) {
NcError.badRequest('Source not found');
NcError.sourceNotFound(table.source_id);
}
const base = await source.getProject();
if (!base) {
NcError.badRequest('Base not found');
NcError.baseNotFound(source.base_id);
}
const dbDriver = await NcConnectionMgrv2.get(source);

14
packages/nocodb/src/services/data-alias-nested.service.ts

@ -22,7 +22,7 @@ export class DataAliasNestedService {
) {
const { model, view } = await getViewAndModelByAliasOrId(param);
if (!model) NcError.notFound('Table not found');
if (!model) NcError.tableNotFound(param.tableName);
const source = await Source.get(model.source_id);
@ -69,7 +69,7 @@ export class DataAliasNestedService {
},
) {
const { model, view } = await getViewAndModelByAliasOrId(param);
if (!model) NcError.notFound('Table not found');
if (!model) NcError.tableNotFound(param.tableName);
const source = await Source.get(model.source_id);
@ -111,7 +111,7 @@ export class DataAliasNestedService {
) {
const { model, view } = await getViewAndModelByAliasOrId(param);
if (!model) NcError.notFound('Table not found');
if (!model) NcError.tableNotFound(param.tableName);
const source = await Source.get(model.source_id);
@ -153,7 +153,7 @@ export class DataAliasNestedService {
},
) {
const { model, view } = await getViewAndModelByAliasOrId(param);
if (!model) NcError.notFound('Table not found');
if (!model) NcError.tableNotFound(param.tableName);
const source = await Source.get(model.source_id);
@ -238,7 +238,7 @@ export class DataAliasNestedService {
) {
const { model, view } = await getViewAndModelByAliasOrId(param);
if (!model) NcError.notFound('Table not found');
if (!model) NcError.tableNotFound(param.tableName);
const source = await Source.get(model.source_id);
@ -284,7 +284,7 @@ export class DataAliasNestedService {
},
) {
const { model, view } = await getViewAndModelByAliasOrId(param);
if (!model) NcError.notFound('Table not found');
if (!model) NcError.tableNotFound(param.tableName);
const source = await Source.get(model.source_id);
@ -316,7 +316,7 @@ export class DataAliasNestedService {
},
) {
const { model, view } = await getViewAndModelByAliasOrId(param);
if (!model) NcError.notFound('Table not found');
if (!model) NcError.tableNotFound(param.tableName);
const source = await Source.get(model.source_id);

39
packages/nocodb/src/services/data-table.service.ts

@ -52,7 +52,7 @@ export class DataTableService {
});
if (!row) {
NcError.notFound('Row not found');
NcError.recordNotFound(param.rowId);
}
return row;
@ -184,7 +184,7 @@ export class DataTableService {
const model = await Model.get(param.modelId);
if (!model) {
NcError.notFound(`Table with id '${param.modelId}' not found`);
NcError.tableNotFound(param.modelId);
}
if (param.baseId && model.base_id !== param.baseId) {
@ -196,7 +196,7 @@ export class DataTableService {
if (param.viewId) {
view = await View.get(param.viewId);
if (!view || (view.fk_model_id && view.fk_model_id !== param.modelId)) {
NcError.unprocessableEntity(`View with id '${param.viewId}' not found`);
NcError.viewNotFound(param.viewId);
}
}
@ -278,7 +278,7 @@ export class DataTableService {
});
if (!(await baseModel.exist(param.rowId))) {
NcError.notFound(`Record with id '${param.rowId}' not found`);
NcError.recordNotFound(`${param.rowId}`);
}
const column = await this.getColumn(param);
@ -356,8 +356,7 @@ export class DataTableService {
private async getColumn(param: { modelId: string; columnId: string }) {
const column = await Column.get({ colId: param.columnId });
if (!column)
NcError.notFound(`Column with id '${param.columnId}' not found`);
if (!column) NcError.fieldNotFound(param.columnId);
if (column.fk_model_id !== param.modelId)
NcError.badRequest('Column not belong to model');
@ -419,8 +418,7 @@ export class DataTableService {
this.validateIds(param.refRowIds);
const { model, view } = await this.getModelAndView(param);
if (!model)
NcError.notFound('Table with id ' + param.modelId + ' not found');
if (!model) NcError.tableNotFound(param.modelId);
const source = await Source.get(model.source_id);
@ -503,9 +501,7 @@ export class DataTableService {
operationMap.deleteAll &&
!(await baseModel.exist(operationMap.deleteAll.rowId))
) {
NcError.notFound(
`Record with id '${operationMap.deleteAll.rowId}' not found`,
);
NcError.recordNotFound(operationMap.deleteAll.rowId);
} else if (operationMap.copy && operationMap.paste) {
const [existsCopyRow, existsPasteRow] = await Promise.all([
baseModel.exist(operationMap.copy.rowId),
@ -513,17 +509,13 @@ export class DataTableService {
]);
if (!existsCopyRow && !existsPasteRow) {
NcError.notFound(
`Record with id '${operationMap.copy.rowId}' and '${operationMap.paste.rowId}' not found`,
NcError.recordNotFound(
`'${operationMap.copy.rowId}' and '${operationMap.paste.rowId}'`,
);
} else if (!existsCopyRow) {
NcError.notFound(
`Record with id '${operationMap.copy.rowId}' not found`,
);
NcError.recordNotFound(operationMap.copy.rowId);
} else if (!existsPasteRow) {
NcError.notFound(
`Record with id '${operationMap.paste.rowId}' not found`,
);
NcError.recordNotFound(operationMap.paste.rowId);
}
}
@ -640,7 +632,7 @@ export class DataTableService {
const set = new Set<string>();
for (const rowId of rowIds) {
if (rowId === undefined || rowId === null)
NcError.unprocessableEntity('Invalid row id ' + rowId);
NcError.recordNotFound(rowId);
if (map.has(rowId)) {
set.add(rowId);
} else {
@ -648,12 +640,9 @@ export class DataTableService {
}
}
if (set.size > 0)
NcError.unprocessableEntity(
'Child record with id [' + [...set].join(', ') + '] are duplicated',
);
if (set.size > 0) NcError.duplicateRecord([...set]);
} else if (rowIds === undefined || rowIds === null) {
NcError.unprocessableEntity('Invalid row id ' + rowIds);
NcError.recordNotFound(rowIds);
}
}

31
packages/nocodb/src/services/datas.service.ts

@ -295,7 +295,7 @@ export class DatasService {
});
if (!row) {
NcError.notFound('Row not found');
NcError.recordNotFound(param.rowId);
}
return row;
@ -390,7 +390,7 @@ export class DatasService {
id: view?.fk_model_id || param.viewId,
});
if (!model) NcError.notFound('Table not found');
if (!model) NcError.tableNotFound(view?.fk_model_id || param.viewId);
return await this.getDataList({ model, view, query: param.query });
}
@ -407,7 +407,7 @@ export class DatasService {
id: view?.fk_model_id || param.viewId,
});
if (!model) NcError.notFound('Table not found');
if (!model) NcError.tableNotFound(view?.fk_model_id || param.viewId);
const source = await Source.get(model.source_id);
@ -468,7 +468,7 @@ export class DatasService {
id: view?.fk_model_id || param.viewId,
});
if (!model) NcError.notFound('Table not found');
if (!model) NcError.tableNotFound(view?.fk_model_id || param.viewId);
const source = await Source.get(model.source_id);
@ -529,7 +529,7 @@ export class DatasService {
id: view?.fk_model_id || param.viewId,
});
if (!model) NcError.notFound('Table not found');
if (!model) NcError.tableNotFound(view?.fk_model_id || param.viewId);
const source = await Source.get(model.source_id);
@ -590,7 +590,7 @@ export class DatasService {
id: view?.fk_model_id || param.viewId,
});
if (!model) return NcError.notFound('Table not found');
if (!model) return NcError.tableNotFound(view?.fk_model_id || param.viewId);
const source = await Source.get(model.source_id);
@ -651,7 +651,7 @@ export class DatasService {
id: view?.fk_model_id || param.viewId,
});
if (!model) NcError.notFound('Table not found');
if (!model) NcError.tableNotFound(view?.fk_model_id || param.viewId);
const source = await Source.get(model.source_id);
@ -703,7 +703,7 @@ export class DatasService {
const model = await Model.getByIdOrName({
id: param.viewId,
});
if (!model) NcError.notFound('Table not found');
if (!model) NcError.tableNotFound(param.viewId);
const source = await Source.get(model.source_id);
@ -733,7 +733,7 @@ export class DatasService {
const model = await Model.getByIdOrName({
id: param.viewId,
});
if (!model) return NcError.notFound('Table not found');
if (!model) return NcError.tableNotFound(param.viewId);
const source = await Source.get(model.source_id);
@ -754,7 +754,7 @@ export class DatasService {
const model = await Model.getByIdOrName({
id: param.viewId,
});
if (!model) NcError.notFound('Table not found');
if (!model) NcError.tableNotFound(param.viewId);
const source = await Source.get(model.source_id);
@ -779,7 +779,7 @@ export class DatasService {
const model = await Model.getByIdOrName({
id: param.viewId,
});
if (!model) NcError.notFound('Table not found');
if (!model) NcError.tableNotFound(param.viewId);
const source = await Source.get(model.source_id);
@ -804,7 +804,7 @@ export class DatasService {
id: view?.fk_model_id || param.viewId,
});
if (!model) NcError.notFound('Table not found');
if (!model) NcError.tableNotFound(view?.fk_model_id || param.viewId);
const source = await Source.get(model.source_id);
@ -837,7 +837,7 @@ export class DatasService {
id: view?.fk_model_id || param.viewId,
});
if (!model) NcError.notFound('Table not found');
if (!model) NcError.tableNotFound(view?.fk_model_id || param.viewId);
const source = await Source.get(model.source_id);
@ -875,7 +875,7 @@ export class DatasService {
titleOrId: req.params.viewName,
fk_model_id: model.id,
}));
if (!model) NcError.notFound('Table not found');
if (!model) NcError.tableNotFound(req.params.tableName);
return { model, view };
}
@ -982,8 +982,7 @@ export class DatasService {
c.column_name === columnNameOrId,
);
if (!column)
NcError.notFound(`Column with id/name '${columnNameOrId}' is not found`);
if (!column) NcError.fieldNotFound(columnNameOrId);
return column;
}

2
packages/nocodb/src/services/forms.service.ts

@ -77,7 +77,7 @@ export class FormsService {
const view = await View.get(param.formViewId);
if (!view) {
NcError.badRequest('View not found');
NcError.viewNotFound(param.formViewId);
}
const res = await FormView.update(param.formViewId, param.form);

2
packages/nocodb/src/services/galleries.service.ts

@ -76,7 +76,7 @@ export class GalleriesService {
const view = await View.get(param.galleryViewId);
if (!view) {
NcError.badRequest('View not found');
NcError.viewNotFound(param.galleryViewId);
}
const res = await GalleryView.update(param.galleryViewId, param.gallery);

2
packages/nocodb/src/services/grids.service.ts

@ -67,7 +67,7 @@ export class GridsService {
const view = await View.get(param.viewId);
if (!view) {
NcError.badRequest('View not found');
NcError.viewNotFound(param.viewId);
}
const res = await GridView.update(param.viewId, param.grid);

2
packages/nocodb/src/services/kanbans.service.ts

@ -78,7 +78,7 @@ export class KanbansService {
const view = await View.get(param.kanbanViewId);
if (!view) {
NcError.badRequest('View not found');
NcError.viewNotFound(param.kanbanViewId);
}
const res = await KanbanView.update(param.kanbanViewId, param.kanban);

2
packages/nocodb/src/services/maps.service.ts

@ -70,7 +70,7 @@ export class MapsService {
const view = await View.get(param.mapViewId);
if (!view) {
NcError.badRequest('View not found');
NcError.viewNotFound(param.mapViewId);
}
const res = await MapView.update(param.mapViewId, param.map);

2
packages/nocodb/src/services/model-visibilities.service.ts

@ -24,7 +24,7 @@ export class ModelVisibilitiesService {
const base = await Base.getWithInfo(param.baseId);
if (!base) {
NcError.badRequest('Base not found');
NcError.baseNotFound(param.baseId);
}
for (const d of param.visibilityRule) {

4
packages/nocodb/src/services/org-users.service.ts

@ -201,7 +201,7 @@ export class OrgUsersService {
const user = await User.get(param.userId);
if (!user) {
NcError.badRequest(`User with id '${param.userId}' not found`);
NcError.userNotFound(param.userId);
}
const invite_token = uuidv4();
@ -247,7 +247,7 @@ export class OrgUsersService {
const user = await User.get(param.userId);
if (!user) {
NcError.badRequest(`User with id '${param.userId}' not found`);
NcError.userNotFound(param.userId);
}
const token = uuidv4();
await User.update(user.id, {

35
packages/nocodb/src/services/public-datas.service.ts

@ -1,12 +1,7 @@
import path from 'path';
import { Injectable } from '@nestjs/common';
import { nanoid } from 'nanoid';
import {
ErrorMessages,
populateUniqueFileName,
UITypes,
ViewTypes,
} from 'nocodb-sdk';
import { populateUniqueFileName, UITypes, ViewTypes } from 'nocodb-sdk';
import slash from 'slash';
import { nocoExecute } from 'nc-help';
@ -36,7 +31,7 @@ export class PublicDatasService {
const { sharedViewUuid, password, query = {} } = param;
const view = await View.getByUUID(sharedViewUuid);
if (!view) NcError.notFound('Not found');
if (!view) NcError.viewNotFound(sharedViewUuid);
if (
view.type !== ViewTypes.GRID &&
view.type !== ViewTypes.KANBAN &&
@ -48,7 +43,7 @@ export class PublicDatasService {
}
if (view.password && view.password !== password) {
return NcError.forbidden(ErrorMessages.INVALID_SHARED_VIEW_PASSWORD);
return NcError.invalidSharedViewPassword();
}
const model = await Model.getByIdOrName({
@ -104,7 +99,7 @@ export class PublicDatasService {
}) {
const view = await View.getByUUID(param.sharedViewUuid);
if (!view) NcError.notFound('Not found');
if (!view) NcError.viewNotFound(param.sharedViewUuid);
if (
view.type !== ViewTypes.GRID &&
@ -115,7 +110,7 @@ export class PublicDatasService {
}
if (view.password && view.password !== param.password) {
return NcError.forbidden(ErrorMessages.INVALID_SHARED_VIEW_PASSWORD);
return NcError.invalidSharedViewPassword();
}
const model = await Model.getByIdOrName({
@ -201,14 +196,14 @@ export class PublicDatasService {
}) {
const view = await View.getByUUID(param.sharedViewUuid);
if (!view) NcError.notFound('Not found');
if (!view) NcError.viewNotFound(param.sharedViewUuid);
if (view.type !== ViewTypes.GRID) {
NcError.notFound('Not found');
}
if (view.password && view.password !== param.password) {
return NcError.forbidden(ErrorMessages.INVALID_SHARED_VIEW_PASSWORD);
return NcError.invalidSharedViewPassword();
}
const model = await Model.getByIdOrName({
@ -261,11 +256,11 @@ export class PublicDatasService {
}) {
const view = await View.getByUUID(param.sharedViewUuid);
if (!view) NcError.notFound();
if (!view) NcError.viewNotFound(param.sharedViewUuid);
if (view.type !== ViewTypes.FORM) NcError.notFound();
if (view.password && view.password !== param.password) {
return NcError.forbidden(ErrorMessages.INVALID_SHARED_VIEW_PASSWORD);
return NcError.invalidSharedViewPassword();
}
const model = await Model.getByIdOrName({
@ -434,14 +429,14 @@ export class PublicDatasService {
}) {
const view = await View.getByUUID(param.sharedViewUuid);
if (!view) NcError.notFound('Not found');
if (!view) NcError.viewNotFound(param.sharedViewUuid);
if (view.type !== ViewTypes.FORM && view.type !== ViewTypes.GALLERY) {
NcError.notFound('Not found');
}
if (view.password && view.password !== param.password) {
NcError.forbidden(ErrorMessages.INVALID_SHARED_VIEW_PASSWORD);
NcError.invalidSharedViewPassword();
}
const column = await Column.get({ colId: param.columnId });
@ -492,7 +487,7 @@ export class PublicDatasService {
}) {
const view = await View.getByUUID(param.sharedViewUuid);
if (!view) NcError.notFound('Not found');
if (!view) NcError.viewNotFound(param.sharedViewUuid);
if (
view.type !== ViewTypes.GRID &&
view.type !== ViewTypes.KANBAN &&
@ -503,7 +498,7 @@ export class PublicDatasService {
}
if (view.password && view.password !== param.password) {
NcError.forbidden(ErrorMessages.INVALID_SHARED_VIEW_PASSWORD);
NcError.invalidSharedViewPassword();
}
const column = await getColumnByIdOrName(
@ -567,7 +562,7 @@ export class PublicDatasService {
}) {
const view = await View.getByUUID(param.sharedViewUuid);
if (!view) NcError.notFound('Not found');
if (!view) NcError.viewNotFound(param.sharedViewUuid);
if (
view.type !== ViewTypes.GRID &&
view.type !== ViewTypes.KANBAN &&
@ -578,7 +573,7 @@ export class PublicDatasService {
}
if (view.password && view.password !== param.password) {
NcError.forbidden(ErrorMessages.INVALID_SHARED_VIEW_PASSWORD);
NcError.invalidSharedViewPassword();
}
const column = await getColumnByIdOrName(

7
packages/nocodb/src/services/public-metas.service.ts

@ -1,6 +1,5 @@
import { Injectable } from '@nestjs/common';
import {
ErrorMessages,
isCreatedOrLastModifiedByCol,
RelationTypes,
UITypes,
@ -19,10 +18,10 @@ export class PublicMetasService {
client?: string;
} = await View.getByUUID(param.sharedViewUuid);
if (!view) NcError.notFound('Not found');
if (!view) NcError.viewNotFound(param.sharedViewUuid);
if (view.password && view.password !== param.password) {
NcError.forbidden(ErrorMessages.INVALID_SHARED_VIEW_PASSWORD);
NcError.invalidSharedViewPassword();
}
await view.getFilters();
@ -171,7 +170,7 @@ export class PublicMetasService {
const base = await Base.getByUuid(param.sharedBaseUuid);
if (!base) {
NcError.notFound();
NcError.baseNotFound(param.sharedBaseUuid);
}
return { base_id: base.id };

8
packages/nocodb/src/services/shared-bases.service.ts

@ -42,7 +42,7 @@ export class SharedBasesService {
}
if (!base) {
NcError.badRequest('Invalid base id');
NcError.baseNotFound(param.baseId);
}
const data: any = {
@ -86,7 +86,7 @@ export class SharedBasesService {
}
if (!base) {
NcError.badRequest('Invalid base id');
NcError.baseNotFound(param.baseId);
}
if (roles === 'editor' && process.env.NC_CLOUD === 'true') {
@ -137,7 +137,7 @@ export class SharedBasesService {
const base = await Base.get(param.baseId);
if (!base) {
NcError.badRequest('Invalid base id');
NcError.baseNotFound(param.baseId);
}
const data: any = {
uuid: null,
@ -159,7 +159,7 @@ export class SharedBasesService {
const base = await Base.get(param.baseId);
if (!base) {
NcError.badRequest('Invalid base id');
NcError.baseNotFound(param.baseId);
}
const data: any = {
uuid: base.uuid,

2
packages/nocodb/src/services/tables.service.ts

@ -296,7 +296,7 @@ export class TablesService {
});
if (!table) {
NcError.notFound('Table not found');
NcError.tableNotFound(param.tableId);
}
// todo: optimise

10
packages/nocodb/src/services/views.service.ts

@ -98,7 +98,7 @@ export class ViewsService {
const view = await View.get(param.viewId);
if (!view) {
NcError.badRequest('View not found');
NcError.viewNotFound(param.viewId);
}
this.appHooksService.emit(AppEvents.SHARED_VIEW_CREATE, {
@ -124,7 +124,7 @@ export class ViewsService {
const view = await View.get(param.viewId);
if (!view) {
NcError.badRequest('View not found');
NcError.viewNotFound(param.viewId);
}
const result = await View.update(param.viewId, param.view);
@ -145,7 +145,7 @@ export class ViewsService {
const view = await View.get(param.viewId);
if (!view) {
NcError.badRequest('View not found');
NcError.viewNotFound(param.viewId);
}
await View.delete(param.viewId);
@ -173,7 +173,7 @@ export class ViewsService {
const view = await View.get(param.viewId);
if (!view) {
NcError.badRequest('View not found');
NcError.viewNotFound(param.viewId);
}
const result = await View.update(param.viewId, param.sharedView);
@ -195,7 +195,7 @@ export class ViewsService {
const view = await View.get(param.viewId);
if (!view) {
NcError.badRequest('View not found');
NcError.viewNotFound(param.viewId);
}
await View.sharedViewDelete(param.viewId);

58
packages/nocodb/tests/unit/rest/tests/newDataApis.test.ts

@ -222,9 +222,9 @@ async function ncAxiosLinkGet({
// print error codes
if (debugMode && status !== 200) {
console.log('#### ', response.body.msg);
console.log('#### ', response.body.message || response.body.msg);
}
if (!debugMode && msg) expect(response.body.msg).to.equal(msg);
if (!debugMode && msg) expect(response.body.message || response.body.msg).to.equal(msg);
return response;
}
@ -248,10 +248,10 @@ async function ncAxiosLinkAdd({
// print error codes
if (debugMode && status !== 201) {
console.log('#### ', response.body.msg);
console.log('#### ', response.body.message || response.body.msg);
}
if (!debugMode && msg) expect(response.body.msg).to.equal(msg);
if (!debugMode && msg) expect(response.body.message || response.body.msg).to.equal(msg);
return response;
}
@ -274,9 +274,9 @@ async function ncAxiosLinkRemove({
// print error codes
if (debugMode && status !== 200) {
console.log('#### ', response.body.msg);
console.log('#### ', response.body.message || response.body.msg);
}
if (!debugMode && msg) expect(response.body.msg).to.equal(msg);
if (!debugMode && msg) expect(response.body.message || response.body.msg).to.equal(msg);
return response;
}
@ -831,7 +831,7 @@ function textBased() {
query: {
viewId: '123456789',
},
status: 422,
status: 404,
});
});
@ -883,10 +883,10 @@ function textBased() {
query: {
offset: 10000,
},
status: 400,
status: 422,
});
expect(rsp.body.msg).to.equal(
'Offset is beyond the total number of records',
expect(rsp.body.message).to.equal(
"Offset value '10000' is invalid",
);
});
@ -897,7 +897,7 @@ function textBased() {
query: {
sort: 'abc',
},
status: 422,
status: 404,
});
await ncAxiosGet({
query: {
@ -909,7 +909,7 @@ function textBased() {
query: {
fields: 'abc',
},
status: 422,
status: 404,
});
});
@ -1079,7 +1079,7 @@ function textBased() {
// Invalid row ID
await ncAxiosPatch({
body: { Id: 123456789, SingleLineText: 'some text' },
status: 422,
status: 404,
});
});
@ -1126,7 +1126,7 @@ function textBased() {
status: unauthorizedResponse,
});
// Invalid row ID
await ncAxiosDelete({ body: { Id: '123456789' }, status: 422 });
await ncAxiosDelete({ body: { Id: '123456789' }, status: 404 });
});
}
@ -2380,7 +2380,7 @@ function linkBased() {
...validParams,
urlParams: { ...validParams.urlParams, linkId: 9999 },
status: 404,
msg: "Column with id '9999' not found",
msg: "Field '9999' not found",
});
// Link Add: Invalid Source row ID
@ -2389,7 +2389,7 @@ function linkBased() {
...validParams,
urlParams: { ...validParams.urlParams, rowId: 9999 },
status: 404,
msg: "Record with id '9999' not found",
msg: "Record '9999' not found",
});
// Body parameter error
@ -2411,8 +2411,8 @@ function linkBased() {
await ncAxiosLinkAdd({
...validParams,
body: [999, 998],
status: 422,
msg: 'Child record with id [999] not found',
status: 404,
msg: 'Record \'999\' not found',
});
} else {
// Link Add: Invalid body parameter - row id invalid
@ -2421,8 +2421,8 @@ function linkBased() {
await ncAxiosLinkAdd({
...validParams,
body: [999, 998, 997],
status: 422,
msg: 'Child record with id [999, 998, 997] not found',
status: 404,
msg: 'Records \'999, 998, 997\' not found',
});
// Link Add: Invalid body parameter - repeated row id
@ -2432,7 +2432,7 @@ function linkBased() {
...validParams,
body: [1, 2, 1, 2],
status: 422,
msg: 'Child record with id [1, 2] are duplicated',
msg: "Records '1, 2' already exists",
});
}
}
@ -2452,7 +2452,7 @@ function linkBased() {
...validParams,
urlParams: { ...validParams.urlParams, linkId: 9999 },
status: 404,
msg: "Column with id '9999' not found",
msg: "Field '9999' not found",
});
// Link Remove: Invalid Source row ID
@ -2461,7 +2461,7 @@ function linkBased() {
...validParams,
urlParams: { ...validParams.urlParams, rowId: 9999 },
status: 404,
msg: "Record with id '9999' not found",
msg: "Record '9999' not found",
});
// Body parameter error
@ -2493,8 +2493,8 @@ function linkBased() {
await ncAxiosLinkRemove({
...validParams,
body: [999, 998],
status: 422,
msg: 'Child record with id [999, 998] not found',
status: 404,
msg: 'Records \'999, 998\' not found',
});
// Link Remove: Invalid body parameter - repeated row id
@ -2504,7 +2504,7 @@ function linkBased() {
...validParams,
body: [1, 2, 1, 2],
status: 422,
msg: 'Child record with id [1, 2] are duplicated',
msg: "Records '1, 2' already exists",
});
}
}
@ -2524,7 +2524,7 @@ function linkBased() {
...validParams,
urlParams: { ...validParams.urlParams, linkId: 9999 },
status: 404,
msg: "Column with id '9999' not found",
msg: "Field '9999' not found",
});
// Link List: Invalid Source row ID
@ -2533,7 +2533,7 @@ function linkBased() {
...validParams,
urlParams: { ...validParams.urlParams, rowId: 9999 },
status: 404,
msg: "Record with id '9999' not found",
msg: "Record '9999' not found",
});
// Query parameter error
@ -2566,7 +2566,7 @@ function linkBased() {
...validParams,
query: { ...validParams.query, offset: 9999 },
// for BT relation we use btRead so we don't apply offset & limit, also we don't return page info where this check is done
status: relationType === 'bt' ? 200 : 400,
status: relationType === 'bt' ? 200 : 422,
});
// Link List: Invalid query parameter - negative limit

26
packages/nocodb/tests/unit/rest/tests/tableRow.test.ts

@ -211,7 +211,7 @@ function tableStaticTest() {
})
.expect(404);
if (response.body.msg !== 'Table not found')
if (response.body.message !== "Table 'wrong-table-id' not found")
throw new Error('Wrong error message');
});
it('Find one sorted table data list with required columns', async function () {
@ -426,7 +426,7 @@ function tableStaticTest() {
.set('xc-auth', context.token)
.expect(404);
if (response.body['msg'] !== 'Table not found') {
if (response.body.message !== "Table 'wrong-id' not found") {
throw new Error('Wrong error message');
}
});
@ -500,7 +500,7 @@ function tableStaticTest() {
.set('xc-auth', context.token)
.expect(404);
if (response.body['msg'] !== 'Table not found') {
if (response.body.message !== "Table 'invalid-table-id' not found") {
console.log(response.body);
throw new Error('Wrong error message');
}
@ -518,7 +518,7 @@ function tableStaticTest() {
.set('xc-auth', context.token)
.expect(404);
if (response.body['msg'] !== 'Table not found') {
if (response.body.message !== "Table 'invalid-table-id' not found") {
throw new Error('Wrong error message');
}
});
@ -535,7 +535,7 @@ function tableStaticTest() {
.set('xc-auth', context.token)
.expect(404);
if (response.body['msg'] !== 'Column not found') {
if (response.body.message !== `Field '${firstNameColumn.id}' not found`) {
console.log(response.body);
throw new Error('Wrong error message');
}
@ -552,10 +552,10 @@ function tableStaticTest() {
.expect(404);
if (
response.body.msg !== "Column with id/name 'invalid-column' is not found"
response.body.message !== "Field 'invalid-column' not found"
) {
console.log(response.body);
throw new Error('Should error out');
throw new Error('Wrong error message');
}
});
it('List hm with non ltar column', async () => {
@ -2065,7 +2065,7 @@ function tableTest() {
.expect(404);
if (
response.body.msg !== "Column with id/name 'invalid-column' is not found"
response.body.message !== "Field 'invalid-column' not found"
) {
console.log(response.body);
throw new Error('Should error out');
@ -2089,7 +2089,7 @@ function tableTest() {
.set('xc-auth', context.token)
.expect(404);
if (response.body['msg'] !== 'Column not found') {
if (response.body.message !== `Field '${firstNameColumn.id}' not found`) {
console.log(response.body);
throw new Error('Wrong error message');
}
@ -2291,8 +2291,8 @@ function tableTest() {
.set('xc-auth', context.token)
.expect(404);
if (response.body['msg'] !== 'Table not found') {
console.log(response.body['msg']);
if (response.body.message !== "Table 'invalid-table-id' not found") {
console.log(response.body['message']);
throw new Error('Wrong error message');
}
});
@ -2313,8 +2313,8 @@ function tableTest() {
.set('xc-auth', context.token)
.expect(404);
if (response.body['msg'] !== 'Table not found') {
console.log(response.body['msg']);
if (response.body.message !== "Table 'invalid-table-id' not found") {
console.log(response.body['message']);
throw new Error('Wrong error message');
}
});

Loading…
Cancel
Save