From 015bb5a44c8ec9bbbc96a59ea2f8fd1ec0f1814e Mon Sep 17 00:00:00 2001 From: kim Date: Thu, 26 Feb 2026 16:33:33 +0100 Subject: [PATCH 1/3] refactor: apply tsconfig strict true --- src/services/auth/plugins/passport/strategies/emailChange.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/services/auth/plugins/passport/strategies/emailChange.ts b/src/services/auth/plugins/passport/strategies/emailChange.ts index b7935efbe..9fb71b63a 100644 --- a/src/services/auth/plugins/passport/strategies/emailChange.ts +++ b/src/services/auth/plugins/passport/strategies/emailChange.ts @@ -52,7 +52,7 @@ export default ( return done( options?.propagateError ? buildError(error) : new UnauthorizedMember(), false, - ); + ); } }, ), From e48428819df54bbe5d39383e01d018c274567482 Mon Sep 17 00:00:00 2001 From: kim Date: Thu, 26 Feb 2026 16:26:52 +0000 Subject: [PATCH 2/3] feat: use fastify errors --- src/services/account/errors.ts | 38 ++-- src/services/action/utils/errors.ts | 22 +-- src/services/chat/errors.ts | 74 +++----- src/services/item/errors.ts | 23 +-- .../item/plugins/embeddedLink/errors.ts | 23 +-- .../item/plugins/geolocation/errors.ts | 86 +++------ src/services/item/plugins/html/errors.ts | 35 ++-- src/services/item/plugins/html/h5p/errors.ts | 36 ++-- .../item/plugins/html/h5p/test/errors.test.ts | 6 - .../item/plugins/html/html.service.ts | 8 +- .../item/plugins/importExport/errors.ts | 103 ++++------- .../item/plugins/invitation/utils/errors.ts | 167 ++++++------------ .../item/plugins/itemVisibility/errors.ts | 105 ++++------- 13 files changed, 215 insertions(+), 511 deletions(-) diff --git a/src/services/account/errors.ts b/src/services/account/errors.ts index 9d1000a06..12f514ab4 100644 --- a/src/services/account/errors.ts +++ b/src/services/account/errors.ts @@ -1,31 +1,17 @@ import { StatusCodes } from 'http-status-codes'; -import { ErrorFactory, FAILURE_MESSAGES } from '@graasp/sdk'; +import { createError } from '@fastify/error'; -export const GraaspAccountError = ErrorFactory('graasp-plugin-account'); +import { FAILURE_MESSAGES } from '@graasp/sdk'; -export class AccountNotFound extends GraaspAccountError { - constructor(data?: unknown) { - super( - { - code: 'GPAECCRR001', - statusCode: StatusCodes.NOT_FOUND, - message: FAILURE_MESSAGES.ACCOUNT_NOT_FOUND, - }, - data, - ); - } -} +export const AccountNotFound = createError( + 'GPAECCRR001', + FAILURE_MESSAGES.ACCOUNT_NOT_FOUND, + StatusCodes.NOT_FOUND, +); -export class NotMemberOrGuest extends GraaspAccountError { - constructor(data?: unknown) { - super( - { - code: 'GPAECCRR002', - statusCode: StatusCodes.FORBIDDEN, - message: FAILURE_MESSAGES.NOT_MEMBER_OR_GUEST, - }, - data, - ); - } -} +export const NotMemberOrGuest = createError( + 'GPAECCRR002', + FAILURE_MESSAGES.NOT_MEMBER_OR_GUEST, + StatusCodes.FORBIDDEN, +); diff --git a/src/services/action/utils/errors.ts b/src/services/action/utils/errors.ts index 481a3ee17..39a7c4344 100644 --- a/src/services/action/utils/errors.ts +++ b/src/services/action/utils/errors.ts @@ -1,19 +1,9 @@ import { StatusCodes } from 'http-status-codes'; -import { ErrorFactory } from '@graasp/sdk'; +import { createError } from '@fastify/error'; -import { PLUGIN_NAME } from '../constants'; - -export const GraaspActionError = ErrorFactory(PLUGIN_NAME); -export class CannotWriteFileError extends GraaspActionError { - constructor(data?: unknown) { - super( - { - code: 'GPAERR001', - statusCode: StatusCodes.NOT_FOUND, - message: 'A file was not created properly for the requested archive', - }, - data, - ); - } -} +export const CannotWriteFileError = createError( + 'GPAERR001', + 'A file was not created properly for the requested archive', + StatusCodes.NOT_FOUND, +); diff --git a/src/services/chat/errors.ts b/src/services/chat/errors.ts index 366f3e2cd..c0a9970dd 100644 --- a/src/services/chat/errors.ts +++ b/src/services/chat/errors.ts @@ -2,67 +2,33 @@ import { StatusCodes } from 'http-status-codes'; import { createError } from '@fastify/error'; -import { ErrorFactory } from '@graasp/sdk'; - -const PLUGIN_NAME = 'graasp-plugin-chatbox'; - /** * Errors thrown by the chat tasks */ -export const GraaspChatboxError = ErrorFactory(PLUGIN_NAME); - -export class ChatMessageNotFound extends GraaspChatboxError { - constructor(data?: unknown) { - super( - { - code: 'GICERR003', - statusCode: StatusCodes.NOT_FOUND, - message: 'Chat Message not found', - }, - data, - ); - } -} +export const ChatMessageNotFound = createError( + 'GICERR003', + 'Chat Message not found', + StatusCodes.NOT_FOUND, +); -export class MemberCannotEditMessage extends GraaspChatboxError { - constructor(data?: unknown) { - super( - { - code: 'GICERR002', - statusCode: StatusCodes.UNAUTHORIZED, - message: 'Member can only edit own messages', - }, - data, - ); - } -} +export const MemberCannotEditMessage = createError( + 'GICERR002', + 'Member can only edit own messages', + StatusCodes.UNAUTHORIZED, +); -export class MemberCannotDeleteMessage extends GraaspChatboxError { - constructor(data: { id: string }) { - super( - { - code: 'GICERR005', - statusCode: StatusCodes.UNAUTHORIZED, - message: 'Member can only delete own messages', - }, - data.id, - ); - } -} +export const MemberCannotDeleteMessage = createError( + 'GICERR005', + 'Member can only delete own messages', + StatusCodes.UNAUTHORIZED, +); -export class MemberCannotAccessMention extends GraaspChatboxError { - constructor(data: { id: string }) { - super( - { - code: 'GICERR004', - statusCode: StatusCodes.UNAUTHORIZED, - message: 'Member can only view own mentions', - }, - data.id, - ); - } -} +export const MemberCannotAccessMention = createError( + 'GICERR004', + 'Member can only view own mentions', + StatusCodes.UNAUTHORIZED, +); export const ChatMentionNotFound = createError( 'GICERR006', diff --git a/src/services/item/errors.ts b/src/services/item/errors.ts index 9cc96bcef..24c3d98b3 100644 --- a/src/services/item/errors.ts +++ b/src/services/item/errors.ts @@ -2,31 +2,18 @@ import { StatusCodes } from 'http-status-codes'; import { createError } from '@fastify/error'; -import { ErrorFactory } from '@graasp/sdk'; - -const PLUGIN_NAME = 'graasp-plugin-item'; - /** * Errors thrown by the item services */ -export const GraaspItemError = ErrorFactory(PLUGIN_NAME); - export const WrongItemTypeError = createError( 'GIERR001', 'Item does not have the correct type', StatusCodes.BAD_REQUEST, ); -export class ItemOrderingError extends GraaspItemError { - constructor(reason?: string) { - super( - { - code: 'GIERR002', - statusCode: StatusCodes.INTERNAL_SERVER_ERROR, - message: 'Error while rescaling', - }, - reason, - ); - } -} +export const ItemOrderingError = createError( + 'GIERR002', + 'Error while rescaling', + StatusCodes.INTERNAL_SERVER_ERROR, +); diff --git a/src/services/item/plugins/embeddedLink/errors.ts b/src/services/item/plugins/embeddedLink/errors.ts index c33b3176c..fc382dab3 100644 --- a/src/services/item/plugins/embeddedLink/errors.ts +++ b/src/services/item/plugins/embeddedLink/errors.ts @@ -1,20 +1,9 @@ import { StatusCodes } from 'http-status-codes'; -import { ErrorFactory } from '@graasp/sdk'; +import { createError } from '@fastify/error'; -const PLUGIN_NAME = 'graasp-plugin-item-embedded-link'; - -export const GraaspError = ErrorFactory(PLUGIN_NAME); - -export class InvalidUrl extends GraaspError { - constructor(data?: unknown) { - super( - { - code: 'GPIELERR002', - statusCode: StatusCodes.BAD_REQUEST, - message: `The URL is not valid.`, - }, - data, - ); - } -} +export const InvalidUrl = createError( + 'GPIELERR002', + `The URL is not valid.`, + StatusCodes.BAD_REQUEST, +); diff --git a/src/services/item/plugins/geolocation/errors.ts b/src/services/item/plugins/geolocation/errors.ts index 267f3f204..f386c4477 100644 --- a/src/services/item/plugins/geolocation/errors.ts +++ b/src/services/item/plugins/geolocation/errors.ts @@ -1,63 +1,27 @@ import { StatusCodes } from 'http-status-codes'; -import { ErrorFactory } from '@graasp/sdk'; - -const PLUGIN_NAME = 'graasp-plugin-item-geolocation'; - -/** - * Errors thrown by item geolocation - */ - -export const GraaspItemGeolocationError = ErrorFactory(PLUGIN_NAME); - -export class ItemGeolocationNotFound extends GraaspItemGeolocationError { - constructor(data?: unknown) { - super( - { - code: 'GIGEOERR001', - statusCode: StatusCodes.NOT_FOUND, - message: 'Geolocation not found', - }, - data, - ); - } -} - -export class PartialItemGeolocation extends GraaspItemGeolocationError { - constructor(data?: { lat?: unknown; lng?: unknown }) { - super( - { - code: 'GIGEOERR002', - statusCode: StatusCodes.BAD_REQUEST, - message: 'Geolocation should have both lat and lng', - }, - data, - ); - } -} - -export class MissingGeolocationSearchParams extends GraaspItemGeolocationError { - constructor(data?: unknown) { - super( - { - code: 'GIGEOERR003', - statusCode: StatusCodes.BAD_REQUEST, - message: 'Geolocation Search should include parent item, or all lat1, lat2, lng1 and lng2', - }, - data, - ); - } -} - -export class MissingGeolocationApiKey extends GraaspItemGeolocationError { - constructor(data?: unknown) { - super( - { - code: 'GIGEOERR004', - statusCode: StatusCodes.SERVICE_UNAVAILABLE, - message: 'Geolocation API key is not defined', - }, - data, - ); - } -} +import { createError } from '@fastify/error'; + +export const ItemGeolocationNotFound = createError( + 'GIGEOERR001', + 'Geolocation not found', + StatusCodes.NOT_FOUND, +); + +export const PartialItemGeolocation = createError( + 'GIGEOERR002', + 'Geolocation should have both lat and lng', + StatusCodes.BAD_REQUEST, +); + +export const MissingGeolocationSearchParams = createError( + 'GIGEOERR003', + 'Geolocation Search should include parent item, or all lat1, lat2, lng1 and lng2', + StatusCodes.BAD_REQUEST, +); + +export const MissingGeolocationApiKey = createError( + 'GIGEOERR004', + 'Geolocation API key is not defined', + StatusCodes.SERVICE_UNAVAILABLE, +); diff --git a/src/services/item/plugins/html/errors.ts b/src/services/item/plugins/html/errors.ts index 4de1ed34b..f05cd2c7e 100644 --- a/src/services/item/plugins/html/errors.ts +++ b/src/services/item/plugins/html/errors.ts @@ -1,31 +1,18 @@ import { StatusCodes } from 'http-status-codes'; -import { ErrorFactory } from '@graasp/sdk'; +import { createError } from '@fastify/error'; -export const GraaspHtmlError = ErrorFactory('graasp-plugin-html'); - -export class HtmlItemNotFoundError extends GraaspHtmlError { - constructor(data?: unknown) { - super( - { - code: 'GPHTMLERR002', - statusCode: StatusCodes.NOT_FOUND, - message: 'Html item not found', - }, - data, - ); - } -} +export const HtmlItemNotFoundError = createError( + 'GPHTMLERR002', + 'Html item not found', + StatusCodes.NOT_FOUND, +); /** * Fallback error on unexpected internal error, opaque to avoid leaking information */ -export class HtmlImportError extends GraaspHtmlError { - constructor() { - super({ - code: 'GPHTMLERR003', - statusCode: StatusCodes.INTERNAL_SERVER_ERROR, - message: 'Unexpected server error while importing Html', - }); - } -} +export const HtmlImportError = createError( + 'GPHTMLERR003', + 'Unexpected server error while importing Html', + StatusCodes.INTERNAL_SERVER_ERROR, +); diff --git a/src/services/item/plugins/html/h5p/errors.ts b/src/services/item/plugins/html/h5p/errors.ts index 273b15b77..69ada29aa 100644 --- a/src/services/item/plugins/html/h5p/errors.ts +++ b/src/services/item/plugins/html/h5p/errors.ts @@ -1,29 +1,15 @@ import { StatusCodes } from 'http-status-codes'; -import { GraaspHtmlError } from '../errors'; +import { createError } from '@fastify/error'; -export class H5PInvalidFileError extends GraaspHtmlError { - constructor(data?: unknown) { - super( - { - code: 'GPH5PERR001', - statusCode: StatusCodes.BAD_REQUEST, - message: 'File is not a valid H5P package', - }, - data, - ); - } -} +export const H5PInvalidFileError = createError( + 'GPH5PERR001', + 'File is not a valid H5P package', + StatusCodes.BAD_REQUEST, +); -export class H5PInvalidManifestError extends GraaspHtmlError { - constructor(data?: unknown) { - super( - { - code: 'GPH5PERR002', - statusCode: StatusCodes.BAD_REQUEST, - message: 'Invalid h5p.json manifest file', - }, - data, - ); - } -} +export const H5PInvalidManifestError = createError( + 'GPH5PERR002', + 'Invalid h5p.json manifest file', + StatusCodes.BAD_REQUEST, +); diff --git a/src/services/item/plugins/html/h5p/test/errors.test.ts b/src/services/item/plugins/html/h5p/test/errors.test.ts index d0547bd79..904ec1f3b 100644 --- a/src/services/item/plugins/html/h5p/test/errors.test.ts +++ b/src/services/item/plugins/html/h5p/test/errors.test.ts @@ -11,9 +11,7 @@ describe('Custom errors', () => { const error = new H5PInvalidFileError(MOCK_ITEM); expect(error.code).toEqual('GPH5PERR001'); - expect(error.data).toEqual(MOCK_ITEM); expect(error.message).toEqual('File is not a valid H5P package'); - expect(error.origin).toEqual('graasp-plugin-html'); expect(error.statusCode).toEqual(StatusCodes.BAD_REQUEST); }); @@ -21,9 +19,7 @@ describe('Custom errors', () => { const error = new HtmlItemNotFoundError(MOCK_ITEM); expect(error.code).toEqual('GPHTMLERR002'); - expect(error.data).toEqual(MOCK_ITEM); expect(error.message).toEqual('Html item not found'); - expect(error.origin).toEqual('graasp-plugin-html'); expect(error.statusCode).toEqual(StatusCodes.NOT_FOUND); }); @@ -31,9 +27,7 @@ describe('Custom errors', () => { const error = new HtmlImportError(); expect(error.code).toEqual('GPHTMLERR003'); - expect(error.data).toBeUndefined(); expect(error.message).toEqual('Unexpected server error while importing Html'); - expect(error.origin).toEqual('graasp-plugin-html'); expect(error.statusCode).toEqual(StatusCodes.INTERNAL_SERVER_ERROR); }); }); diff --git a/src/services/item/plugins/html/html.service.ts b/src/services/item/plugins/html/html.service.ts index e4d0914af..01e66e791 100644 --- a/src/services/item/plugins/html/html.service.ts +++ b/src/services/item/plugins/html/html.service.ts @@ -19,7 +19,7 @@ import type { FileStorageType } from '../../../file/types'; import { fileRepositoryFactory } from '../../../file/utils/factory'; import { StorageService } from '../../../member/plugins/storage/memberStorage.service'; import { type ItemRaw } from '../../item'; -import { GraaspHtmlError, HtmlImportError } from './errors'; +import { HtmlImportError } from './errors'; import { DEFAULT_MIME_TYPE } from './h5p/constants'; import type { HtmlValidator } from './validator'; @@ -214,10 +214,8 @@ export abstract class HtmlService { log?.error('graasp-plugin-html: unexpected error occured while importing Html:'); log?.error(error); // wrap into plugin error type if not ours - if (!(error instanceof GraaspHtmlError)) { - error = new HtmlImportError(); - } - throw error; + // if the caught error is not one of our plugin errors (codes start with GPHTMLERR) + throw new HtmlImportError(error); } finally { // in all cases, remove local temp folder await tmpDir.cleanup(); diff --git a/src/services/item/plugins/importExport/errors.ts b/src/services/item/plugins/importExport/errors.ts index df46be180..510feb2f8 100644 --- a/src/services/item/plugins/importExport/errors.ts +++ b/src/services/item/plugins/importExport/errors.ts @@ -1,72 +1,35 @@ import { StatusCodes } from 'http-status-codes'; -import { ErrorFactory, FAILURE_MESSAGES } from '@graasp/sdk'; - -import { PLUGIN_NAME } from './constants'; - -export const GraaspItemZipError = ErrorFactory(PLUGIN_NAME); - -export class FileIsInvalidArchiveError extends GraaspItemZipError { - constructor(data?: unknown) { - super( - { - code: 'GPIZERR001', - statusCode: StatusCodes.BAD_REQUEST, - message: FAILURE_MESSAGES.INVALID_ARCHIVE_FILE, - }, - data, - ); - } -} - -export class InvalidFileItemError extends GraaspItemZipError { - constructor(data?: unknown) { - super( - { - code: 'GPIZERR002', - statusCode: StatusCodes.BAD_REQUEST, - message: FAILURE_MESSAGES.INVALID_FILE_ITEM, - }, - data, - ); - } -} - -export class UnexpectedExportError extends GraaspItemZipError { - constructor(data?: unknown) { - super( - { - code: 'GPIZERR003', - statusCode: StatusCodes.INTERNAL_SERVER_ERROR, - message: FAILURE_MESSAGES.UNEXPECTED_EXPORT_ERROR, - }, - data, - ); - } -} - -export class InvalidItemTypeForDownloadError extends GraaspItemZipError { - constructor(data?: unknown) { - super( - { - code: 'GPIZERR004', - statusCode: StatusCodes.BAD_REQUEST, - message: FAILURE_MESSAGES.INVALID_ITEM_TYPE_FOR_DOWNLOAD, - }, - data, - ); - } -} - -export class GraaspExportInvalidFileError extends GraaspItemZipError { - constructor(data?: unknown) { - super( - { - code: 'GPIZERR005', - statusCode: StatusCodes.BAD_REQUEST, - message: FAILURE_MESSAGES.GRAASP_EXPORT_FILE_ERROR, - }, - data, - ); - } -} +import { createError } from '@fastify/error'; + +import { FAILURE_MESSAGES } from '@graasp/sdk'; + +export const FileIsInvalidArchiveError = createError( + 'GPIZERR001', + FAILURE_MESSAGES.INVALID_ARCHIVE_FILE, + StatusCodes.BAD_REQUEST, +); + +export const InvalidFileItemError = createError( + 'GPIZERR002', + FAILURE_MESSAGES.INVALID_FILE_ITEM, + StatusCodes.BAD_REQUEST, +); + +export const UnexpectedExportError = createError( + 'GPIZERR003', + FAILURE_MESSAGES.UNEXPECTED_EXPORT_ERROR, + StatusCodes.INTERNAL_SERVER_ERROR, +); + +export const InvalidItemTypeForDownloadError = createError( + 'GPIZERR004', + FAILURE_MESSAGES.INVALID_ITEM_TYPE_FOR_DOWNLOAD, + StatusCodes.BAD_REQUEST, +); + +export const GraaspExportInvalidFileError = createError( + 'GPIZERR005', + FAILURE_MESSAGES.GRAASP_EXPORT_FILE_ERROR, + StatusCodes.BAD_REQUEST, +); diff --git a/src/services/item/plugins/invitation/utils/errors.ts b/src/services/item/plugins/invitation/utils/errors.ts index bdc8737ef..5e6956d70 100644 --- a/src/services/item/plugins/invitation/utils/errors.ts +++ b/src/services/item/plugins/invitation/utils/errors.ts @@ -1,128 +1,65 @@ import { StatusCodes } from 'http-status-codes'; -import { ErrorFactory, FAILURE_MESSAGES } from '@graasp/sdk'; +import { createError } from '@fastify/error'; -import { PLUGIN_NAME } from './constants'; +import { FAILURE_MESSAGES } from '@graasp/sdk'; -export const GraaspInvitationsError = ErrorFactory(PLUGIN_NAME); +export const InvitationNotFound = createError( + 'GPINVERR003', + FAILURE_MESSAGES.INVITATION_NOT_FOUND, + StatusCodes.NOT_FOUND, +); -export class InvitationNotFound extends GraaspInvitationsError { - constructor(data?: unknown) { - super( - { - code: 'GPINVERR003', - statusCode: StatusCodes.NOT_FOUND, - message: FAILURE_MESSAGES.INVITATION_NOT_FOUND, - }, - data, - ); - } -} +export const MissingEmailColumnInCSVError = createError( + 'GPINVERR005', + FAILURE_MESSAGES.INVITATION_CSV_MISSING_EMAIL_COLUMN, + StatusCodes.BAD_REQUEST, +); -export class MissingEmailColumnInCSVError extends GraaspInvitationsError { - constructor() { - super({ - code: 'GPINVERR005', - statusCode: StatusCodes.BAD_REQUEST, - message: FAILURE_MESSAGES.INVITATION_CSV_MISSING_EMAIL_COLUMN, - }); - } -} +export const MissingGroupColumnInCSVError = createError( + 'GPINVERR006', + FAILURE_MESSAGES.INVITATION_CSV_MISSING_GROUP_COLUMN, + StatusCodes.BAD_REQUEST, +); -export class MissingGroupColumnInCSVError extends GraaspInvitationsError { - constructor() { - super({ - code: 'GPINVERR006', - statusCode: StatusCodes.BAD_REQUEST, - message: FAILURE_MESSAGES.INVITATION_CSV_MISSING_GROUP_COLUMN, - }); - } -} +export const MissingEmailInRowError = createError( + 'GPINVERR007', + FAILURE_MESSAGES.INVITATION_CSV_MISSING_EMAIL_IN_ROW, + StatusCodes.BAD_REQUEST, +); -export class MissingEmailInRowError extends GraaspInvitationsError { - constructor(data?: unknown) { - super( - { - code: 'GPINVERR007', - statusCode: StatusCodes.BAD_REQUEST, - message: FAILURE_MESSAGES.INVITATION_CSV_MISSING_EMAIL_IN_ROW, - }, - data, - ); - } -} +export const MissingGroupInRowError = createError( + 'GPINVERR008', + FAILURE_MESSAGES.INVITATION_CSV_MISSING_GROUP_IN_ROW, + StatusCodes.BAD_REQUEST, +); -export class MissingGroupInRowError extends GraaspInvitationsError { - constructor(data?: unknown) { - super( - { - code: 'GPINVERR008', - statusCode: StatusCodes.BAD_REQUEST, - message: FAILURE_MESSAGES.INVITATION_CSV_MISSING_GROUP_IN_ROW, - }, - data, - ); - } -} +export const NoFileProvidedForInvitations = createError( + 'GPINVERR011', + FAILURE_MESSAGES.INVITATION_CSV_NO_FILE_PROVIDED, + StatusCodes.BAD_REQUEST, +); -export class NoFileProvidedForInvitations extends GraaspInvitationsError { - constructor(data?: unknown) { - super( - { - code: 'GPINVERR011', - statusCode: StatusCodes.BAD_REQUEST, - message: FAILURE_MESSAGES.INVITATION_CSV_NO_FILE_PROVIDED, - }, - data, - ); - } -} +export const NoDataInFile = createError( + 'GPINVERR012', + FAILURE_MESSAGES.INVITATION_CSV_NO_DATA_IN_FILE, + StatusCodes.BAD_REQUEST, +); -export class NoDataInFile extends GraaspInvitationsError { - constructor(data?: unknown) { - super( - { - code: 'GPINVERR012', - statusCode: StatusCodes.BAD_REQUEST, - message: FAILURE_MESSAGES.INVITATION_CSV_NO_DATA_IN_FILE, - }, - data, - ); - } -} +export const CantCreateStructureInNoFolderItem = createError( + 'GPINVERR013', + FAILURE_MESSAGES.INVITATION_CANNOT_CREATE_STRUCTURE_IN_NON_FOLDER_ITEM, + StatusCodes.BAD_REQUEST, +); -export class CantCreateStructureInNoFolderItem extends GraaspInvitationsError { - constructor(data?: unknown) { - super( - { - code: 'GPINVERR013', - statusCode: StatusCodes.BAD_REQUEST, - message: FAILURE_MESSAGES.INVITATION_CANNOT_CREATE_STRUCTURE_IN_NON_FOLDER_ITEM, - }, - data, - ); - } -} +export const TemplateItemDoesNotExist = createError( + 'GPINVERR014', + FAILURE_MESSAGES.INVITATION_CSV_TEMPLATE_ITEM_DOES_NOT_EXIST, + StatusCodes.BAD_REQUEST, +); -export class TemplateItemDoesNotExist extends GraaspInvitationsError { - constructor(data?: unknown) { - super( - { - code: 'GPINVERR014', - statusCode: StatusCodes.BAD_REQUEST, - message: FAILURE_MESSAGES.INVITATION_CSV_TEMPLATE_ITEM_DOES_NOT_EXIST, - }, - data, - ); - } -} - -export class NoInvitationReceivedFound extends GraaspInvitationsError { - constructor() { - super({ - code: 'GPINVERR015', - statusCode: StatusCodes.BAD_REQUEST, - message: FAILURE_MESSAGES.NO_INVITATION_RECEIVED, - }); - } -} +export const NoInvitationReceivedFound = createError( + 'GPINVERR015', + FAILURE_MESSAGES.NO_INVITATION_RECEIVED, + StatusCodes.BAD_REQUEST, +); diff --git a/src/services/item/plugins/itemVisibility/errors.ts b/src/services/item/plugins/itemVisibility/errors.ts index 9c8c7a43a..59062e150 100644 --- a/src/services/item/plugins/itemVisibility/errors.ts +++ b/src/services/item/plugins/itemVisibility/errors.ts @@ -1,80 +1,37 @@ import { StatusCodes } from 'http-status-codes'; -import { ErrorFactory } from '@graasp/sdk'; +import { createError } from '@fastify/error'; -export const PLUGIN_NAME = 'graasp-plugin-item-visibilities'; +export const ItemHasVisibility = createError( + 'GITERR004', + 'Item already has visibility', + StatusCodes.BAD_REQUEST, +); +export const ItemVisibilityNotFound = createError( + 'GITERR005', + 'Item visibility not found', + StatusCodes.NOT_FOUND, +); +export const VisibilityNotFound = createError( + 'GITERR006', + 'Visibility not found', + StatusCodes.NOT_FOUND, +); -export const GraaspItemVisibilityError = ErrorFactory(PLUGIN_NAME); +export const CannotModifyParentVisibility = createError( + 'GITERR008', + 'Cannot modify inherited Visibility', + StatusCodes.FORBIDDEN, +); -export class ItemHasVisibility extends GraaspItemVisibilityError { - constructor(data?: unknown) { - super( - { - code: 'GITERR004', - statusCode: StatusCodes.BAD_REQUEST, - message: 'Item already has visibility', - }, - data, - ); - } -} -export class ItemVisibilityNotFound extends GraaspItemVisibilityError { - constructor(data?: unknown) { - super( - { - code: 'GITERR005', - statusCode: StatusCodes.NOT_FOUND, - message: 'Item visibility not found', - }, - data, - ); - } -} -export class VisibilityNotFound extends GraaspItemVisibilityError { - constructor(data?: unknown) { - super( - { code: 'GITERR006', statusCode: StatusCodes.NOT_FOUND, message: 'Visibility not found' }, - data, - ); - } -} +export const ConflictingVisibilitiesInTheHierarchy = createError( + 'GITERR007', + 'Visibility already present in the hierarchy - itself or ancestors', + StatusCodes.FORBIDDEN, +); -export class CannotModifyParentVisibility extends GraaspItemVisibilityError { - constructor(data?: unknown) { - super( - { - code: 'GITERR008', - statusCode: StatusCodes.FORBIDDEN, - message: 'Cannot modify inherited Visibility', - }, - data, - ); - } -} - -export class ConflictingVisibilitiesInTheHierarchy extends GraaspItemVisibilityError { - constructor(data?: unknown) { - super( - { - code: 'GITERR007', - statusCode: StatusCodes.FORBIDDEN, - message: 'Visibility already present in the hierarchy - itself or ancestors', - }, - data, - ); - } -} - -export class InvalidUseOfItemVisibilityRepository extends GraaspItemVisibilityError { - constructor(data?: unknown) { - super( - { - code: 'GITERR008', - statusCode: StatusCodes.BAD_REQUEST, - message: - 'ItemVisibilityRepository was not used correctly, this should not happen. Consider this an internal error. Contact your local developer team.', - }, - data, - ); - } -} +export const InvalidUseOfItemVisibilityRepository = createError( + 'GITERR008', + 'ItemVisibilityRepository was not used correctly, this should not happen. Consider this an internal error. Contact your local developer team.', + StatusCodes.BAD_REQUEST, +); From f35e7a7d125ed4bc1151c784eaea39f624bec475 Mon Sep 17 00:00:00 2001 From: kim Date: Fri, 6 Mar 2026 17:05:13 +0100 Subject: [PATCH 3/3] refactor: fix tests and more error files --- src/services/auth/plugins/captcha/errors.ts | 19 +- .../passport/strategies/emailChange.ts | 2 +- src/services/auth/plugins/password/errors.ts | 56 +- src/services/authentication.ts | 19 +- .../chat/chatMessage.controller.test.ts | 28 +- .../mentions/chatMention.controller.test.ts | 13 +- src/services/file/utils/errors.ts | 172 ++--- .../item/item.controller.update.test.ts | 4 +- src/services/item/item.service.ts | 2 +- src/services/item/plugins/action/errors.ts | 22 +- .../item/plugins/app/appAction/errors.ts | 26 +- .../item/plugins/app/appData/errors.ts | 102 +-- .../appSetting/appSetting.controller.test.ts | 12 +- .../item/plugins/app/appSetting/errors.ts | 58 +- src/services/item/plugins/app/errors.ts | 58 +- src/services/item/plugins/etherpad/errors.ts | 44 +- .../item/plugins/file/utils/errors.ts | 7 +- .../itemGeolocation.repository.spec.ts | 69 +- .../item/plugins/itemBookmark/errors.ts | 4 - .../itemLike/itemLike.controller.test.ts | 2 +- .../item/plugins/itemLike/itemLike.errors.ts | 5 +- .../itemVisibility.controller.test.ts | 22 +- .../itemVisibility.repository.ts | 2 +- .../plugins/publication/published/errors.ts | 63 +- .../itemPublished.controller.test.ts | 14 +- .../plugins/publication/validation/errors.ts | 177 ++--- .../itemValidation.controller.test.ts | 131 +--- .../item/plugins/recycled/recycled.errors.ts | 21 +- .../itemThumbnail.controller.test.ts | 4 +- src/services/itemLogin/errors.ts | 267 +++---- .../itemLogin/itemLogin.controller.test.ts | 12 +- .../membership.controller.test.ts | 2 +- .../plugins/MembershipRequest/error.ts | 57 +- src/services/member/error.ts | 57 +- src/services/member/plugins/profile/errors.ts | 47 +- .../member/plugins/thumbnail/utils/errors.ts | 26 +- src/utils/errors.ts | 657 ++++++------------ 37 files changed, 714 insertions(+), 1569 deletions(-) diff --git a/src/services/auth/plugins/captcha/errors.ts b/src/services/auth/plugins/captcha/errors.ts index 70f069187..600514562 100644 --- a/src/services/auth/plugins/captcha/errors.ts +++ b/src/services/auth/plugins/captcha/errors.ts @@ -1,16 +1,9 @@ import { StatusCodes } from 'http-status-codes'; -import { CoreError } from '../../../../utils/errors'; +import { createError } from '@fastify/error'; -export class AuthenticationError extends CoreError { - constructor(data?: unknown) { - super( - { - code: 'GERR028', - statusCode: StatusCodes.UNAUTHORIZED, - message: 'The authentication failed', - }, - data, - ); - } -} +export const AuthenticationError = createError( + 'GERR028', + 'The authentication failed', + StatusCodes.UNAUTHORIZED, +); diff --git a/src/services/auth/plugins/passport/strategies/emailChange.ts b/src/services/auth/plugins/passport/strategies/emailChange.ts index 9fb71b63a..b7935efbe 100644 --- a/src/services/auth/plugins/passport/strategies/emailChange.ts +++ b/src/services/auth/plugins/passport/strategies/emailChange.ts @@ -52,7 +52,7 @@ export default ( return done( options?.propagateError ? buildError(error) : new UnauthorizedMember(), false, - ); + ); } }, ), diff --git a/src/services/auth/plugins/password/errors.ts b/src/services/auth/plugins/password/errors.ts index 25de57767..d28bcb7a6 100644 --- a/src/services/auth/plugins/password/errors.ts +++ b/src/services/auth/plugins/password/errors.ts @@ -1,44 +1,22 @@ import { StatusCodes } from 'http-status-codes'; -import { ErrorFactory, FAILURE_MESSAGES } from '@graasp/sdk'; +import { createError } from '@fastify/error'; +import { FAILURE_MESSAGES } from '@graasp/sdk'; -export const GraaspPasswordError = ErrorFactory('graasp-plugin-password'); +export const PasswordNotDefined = createError( + 'GPPWDERR001', + FAILURE_MESSAGES.PASSWORD_NOT_DEFINED_ERROR, + StatusCodes.BAD_REQUEST, +); -export class PasswordNotDefined extends GraaspPasswordError { - constructor(data?: unknown) { - super( - { - code: 'GPPWDERR001', - statusCode: StatusCodes.BAD_REQUEST, - message: FAILURE_MESSAGES.PASSWORD_NOT_DEFINED_ERROR, - }, - data, - ); - } -} +export const PasswordNotStrong = createError( + 'GPPWDERR002', + FAILURE_MESSAGES.PASSWORD_WEAK_ERROR, + StatusCodes.BAD_REQUEST, +); -export class PasswordNotStrong extends GraaspPasswordError { - constructor(data?: unknown) { - super( - { - code: 'GPPWDERR002', - statusCode: StatusCodes.BAD_REQUEST, - message: FAILURE_MESSAGES.PASSWORD_WEAK_ERROR, - }, - data, - ); - } -} - -export class PasswordConflict extends GraaspPasswordError { - constructor(data?: unknown) { - super( - { - code: 'GPPWDERR003', - statusCode: StatusCodes.CONFLICT, - message: FAILURE_MESSAGES.PASSWORD_CONFLICT_ERROR, - }, - data, - ); - } -} +export const PasswordConflict = createError( + 'GPPWDERR003', + FAILURE_MESSAGES.PASSWORD_CONFLICT_ERROR, + StatusCodes.CONFLICT, +); diff --git a/src/services/authentication.ts b/src/services/authentication.ts index 7a3871781..9ca81db55 100644 --- a/src/services/authentication.ts +++ b/src/services/authentication.ts @@ -1,5 +1,7 @@ import { StatusCodes } from 'http-status-codes'; +import { createError } from '@fastify/error'; +import { FAILURE_MESSAGES } from '@graasp/sdk'; import type { AccountRaw, MemberRaw } from '../drizzle/types'; import { AccountType, @@ -8,7 +10,6 @@ import { type MinimalMember, } from '../types'; import { NotMemberOrGuest } from './account/errors'; -import { NotMember } from './member/error'; // TODO: allow AccountRow or apply DTO on all relations? export function isMember(account: AuthenticatedUser | AccountRaw): account is MinimalMember { @@ -20,6 +21,14 @@ export function isGuest(account: AuthenticatedUser | AccountRaw): account is Min return account.type === AccountType.Guest; } + +export const AssertNotMember = createError( + 'AGMERR003', + FAILURE_MESSAGES.NOT_A_MEMBER, + StatusCodes.INTERNAL_SERVER_ERROR, +); + + export function assertIsMember( account: AuthenticatedUser | AccountRaw, error?: new (...args: Args) => Err, @@ -29,9 +38,7 @@ export function assertIsMember( if (error) { throw new error(...args); } else { - const defaultError = new NotMember(); - defaultError.statusCode = StatusCodes.INTERNAL_SERVER_ERROR; - throw defaultError; + throw new AssertNotMember(); } } } @@ -45,9 +52,7 @@ export function assertIsMemberForTest if (error) { throw new error(...args); } else { - const defaultError = new NotMember(); - defaultError.statusCode = StatusCodes.INTERNAL_SERVER_ERROR; - throw defaultError; + throw new AssertNotMember(); } } } diff --git a/src/services/chat/chatMessage.controller.test.ts b/src/services/chat/chatMessage.controller.test.ts index 1508d4b81..87aa3191c 100644 --- a/src/services/chat/chatMessage.controller.test.ts +++ b/src/services/chat/chatMessage.controller.test.ts @@ -144,7 +144,7 @@ describe('Chat Message tests', () => { url: `${ITEMS_ROUTE_PREFIX}/${item.id}/chat`, }); - expect(response.json()).toMatchObject(new MemberCannotAccess(expect.anything())); + expect(response.json().message).toEqual(new MemberCannotAccess(item.id).message); }); }); @@ -326,7 +326,7 @@ describe('Chat Message tests', () => { payload, }); - expect(response.json()).toMatchObject(new MemberCannotAccess(expect.anything())); + expect(response.json().message).toEqual(new MemberCannotAccess(item.id).message); }); }); }); @@ -460,14 +460,15 @@ describe('Chat Message tests', () => { assertIsDefined(actor); mockAuthenticate(actor); + const id = v4(); const payload = { body: 'hello' }; const response = await app.inject({ method: HttpMethod.Patch, - url: `${ITEMS_ROUTE_PREFIX}/${item.id}/chat/${v4()}`, + url: `${ITEMS_ROUTE_PREFIX}/${item.id}/chat/${id}`, payload, }); - expect(response.json()).toMatchObject(new ChatMessageNotFound(expect.anything())); + expect(response.json().message).toEqual(new ChatMessageNotFound(id).message); }); it('Throws if member does not have access to item', async () => { @@ -488,7 +489,7 @@ describe('Chat Message tests', () => { payload, }); - expect(response.json()).toMatchObject(new MemberCannotAccess(expect.anything())); + expect(response.json().message).toEqual(new MemberCannotAccess(chatMessage.id).message); }); it('Throws if member does not have access to chat message', async () => { @@ -511,7 +512,7 @@ describe('Chat Message tests', () => { payload, }); const res = await response.json(); - expect(res).toMatchObject(new MemberCannotEditMessage(expect.anything())); + expect(res.message).toEqual(new MemberCannotEditMessage(chatMessage.id).message); }); }); }); @@ -611,12 +612,13 @@ describe('Chat Message tests', () => { assertIsDefined(actor); mockAuthenticate(actor); + const id = v4(); const response = await app.inject({ method: HttpMethod.Delete, - url: `${ITEMS_ROUTE_PREFIX}/${item.id}/chat/${v4()}`, + url: `${ITEMS_ROUTE_PREFIX}/${item.id}/chat/${id}`, }); - expect(response.json()).toMatchObject(new ChatMessageNotFound(expect.anything())); + expect(response.json().message).toEqual(new ChatMessageNotFound(id).message); }); it('Throws if member does not have access to item', async () => { @@ -635,7 +637,7 @@ describe('Chat Message tests', () => { url: `${ITEMS_ROUTE_PREFIX}/${item.id}/chat/${chatMessage.id}`, }); - expect(response.json()).toMatchObject(new MemberCannotAccess(item.id)); + expect(response.json().message).toEqual(new MemberCannotAccess(item.id).message); }); it('Throws if member does not have access to chat message', async () => { @@ -655,8 +657,10 @@ describe('Chat Message tests', () => { method: HttpMethod.Delete, url: `${ITEMS_ROUTE_PREFIX}/${item.id}/chat/${chatMessage.id}`, }); - const res = await response.json(); - expect(res).toMatchObject(new MemberCannotDeleteMessage({ id: expect.anything() })); + + const expectedError = new MemberCannotDeleteMessage({ id: chatMessage.id }); + expect(response.statusCode).toEqual(expectedError.statusCode); + expect(response.json().message).toEqual(expectedError.message); }); }); }); @@ -745,7 +749,7 @@ describe('Chat Message tests', () => { url: `${ITEMS_ROUTE_PREFIX}/${item.id}/chat`, }); - expect(response.json()).toMatchObject(new MemberCannotAccess(expect.anything())); + expect(response.json().message).toEqual(new MemberCannotAccess(item.id).message); }); }); }); diff --git a/src/services/chat/plugins/mentions/chatMention.controller.test.ts b/src/services/chat/plugins/mentions/chatMention.controller.test.ts index a9fb90166..f07d7a206 100644 --- a/src/services/chat/plugins/mentions/chatMention.controller.test.ts +++ b/src/services/chat/plugins/mentions/chatMention.controller.test.ts @@ -186,7 +186,9 @@ describe('Chat Mention tests', () => { payload, }); - expect(response.json()).toMatchObject(new MemberCannotAccessMention(mention)); + const expectedError = new MemberCannotAccessMention({ id: mention.id }); + expect(response.statusCode).toEqual(expectedError.statusCode); + expect(response.json().message).toEqual(expectedError.message); }); }); }); @@ -261,10 +263,11 @@ describe('Chat Mention tests', () => { }); it('Throws if member does not have access to chat message', async () => { + const chatMessageId = chatMessages[0].id; const [mention] = await db .insert(chatMentionsTable) .values({ - messageId: chatMessages[0].id, + messageId: chatMessageId, accountId: members[0].id, }) .returning(); @@ -274,9 +277,9 @@ describe('Chat Mention tests', () => { url: `${ITEMS_ROUTE_PREFIX}/mentions/${mention.id}`, }); - expect(response.json()).toMatchObject( - new MemberCannotAccessMention({ id: expect.anything() }), - ); + const expectedError = new MemberCannotAccessMention({ id: mention.id }); + expect(response.statusCode).toEqual(expectedError.statusCode); + expect(response.json().message).toEqual(expectedError.message); }); }); }); diff --git a/src/services/file/utils/errors.ts b/src/services/file/utils/errors.ts index 5a5392553..8845a3deb 100644 --- a/src/services/file/utils/errors.ts +++ b/src/services/file/utils/errors.ts @@ -2,113 +2,55 @@ import { StatusCodes } from 'http-status-codes'; import { createError } from '@fastify/error'; -import { ErrorFactory, FAILURE_MESSAGES } from '@graasp/sdk'; +import { FAILURE_MESSAGES } from '@graasp/sdk'; -import { PLUGIN_NAME } from './constants'; - -export const GraaspFileError = ErrorFactory(PLUGIN_NAME); - -export class UploadFileInvalidParameterError extends GraaspFileError { - constructor(data?: unknown) { - super( - { - code: 'GPFERR001', - statusCode: StatusCodes.BAD_REQUEST, - message: FAILURE_MESSAGES.INVALID_UPLOAD_PARAMETERS, - }, - data, - ); - } -} +export const UploadFileInvalidParameterError = createError( + 'GPFERR001', + FAILURE_MESSAGES.INVALID_UPLOAD_PARAMETERS, + StatusCodes.BAD_REQUEST, +); -export class CopyFileInvalidPathError extends GraaspFileError { - constructor(filepath?: unknown) { - super( - { - code: 'GPFERR002', - statusCode: StatusCodes.BAD_REQUEST, - message: FAILURE_MESSAGES.INVALID_FILE_PATH_FOR_COPY, - }, - filepath, - ); - } -} +export const CopyFileInvalidPathError = createError( + 'GPFERR002', + FAILURE_MESSAGES.INVALID_FILE_PATH_FOR_COPY, + StatusCodes.BAD_REQUEST, +); -export class CopyFolderInvalidPathError extends GraaspFileError { - constructor(filepath?: unknown) { - super( - { - code: 'GPFERR009', - statusCode: StatusCodes.BAD_REQUEST, - message: FAILURE_MESSAGES.INVALID_FILE_PATH_FOR_COPY, - }, - filepath, - ); - } -} +export const CopyFolderInvalidPathError = createError( + 'GPFERR009', + FAILURE_MESSAGES.INVALID_FILE_PATH_FOR_COPY, + StatusCodes.BAD_REQUEST, +); -export class DeleteFileInvalidPathError extends GraaspFileError { - constructor(filepath?: unknown) { - super( - { - code: 'GPFERR003', - statusCode: StatusCodes.BAD_REQUEST, - message: FAILURE_MESSAGES.INVALID_FILE_PATH_FOR_DELETE, - }, - filepath, - ); - } -} +export const DeleteFileInvalidPathError = createError( + 'GPFERR003', + FAILURE_MESSAGES.INVALID_FILE_PATH_FOR_DELETE, + StatusCodes.BAD_REQUEST, +); -export class DeleteFolderInvalidPathError extends GraaspFileError { - constructor(folderPath?: unknown) { - super( - { - code: 'GPFERR004', - statusCode: StatusCodes.BAD_REQUEST, - message: FAILURE_MESSAGES.INVALID_FOLDER_PATH_FOR_DELETE, - }, - folderPath, - ); - } -} +export const DeleteFolderInvalidPathError = createError( + 'GPFERR004', + FAILURE_MESSAGES.INVALID_FOLDER_PATH_FOR_DELETE, + StatusCodes.BAD_REQUEST, +); -export class DownloadFileInvalidParameterError extends GraaspFileError { - constructor() { - super({ - code: 'GPFERR005', - statusCode: StatusCodes.BAD_REQUEST, - // todo: change message to indicate the the filepath did not exist - message: FAILURE_MESSAGES.INVALID_DOWNLOAD_PARAMETERS, - }); - } -} +export const DownloadFileInvalidParameterError = createError( + 'GPFERR005', + FAILURE_MESSAGES.INVALID_DOWNLOAD_PARAMETERS, + StatusCodes.BAD_REQUEST, +); -export class LocalFileNotFound extends GraaspFileError { - constructor(data?: unknown) { - super( - { - code: 'GPFERR006', - statusCode: StatusCodes.NOT_FOUND, - message: FAILURE_MESSAGES.LOCAL_FILE_NOT_FOUND, - }, - data, - ); - } -} +export const LocalFileNotFound = createError( + 'GPFERR006', + FAILURE_MESSAGES.LOCAL_FILE_NOT_FOUND, + StatusCodes.NOT_FOUND, +); -export class S3FileNotFound extends GraaspFileError { - constructor(data?: unknown) { - super( - { - code: 'GPFERR007', - statusCode: StatusCodes.NOT_FOUND, - message: FAILURE_MESSAGES.S3_FILE_NOT_FOUND, - }, - data, - ); - } -} +export const S3FileNotFound = createError( + 'GPFERR007', + FAILURE_MESSAGES.S3_FILE_NOT_FOUND, + StatusCodes.NOT_FOUND, +); export const UploadEmptyFileError = createError( 'GPFERR008', @@ -122,26 +64,14 @@ export const UploadFileUnexpectedError = createError( StatusCodes.INTERNAL_SERVER_ERROR, ); -export class DownloadFileUnexpectedError extends GraaspFileError { - constructor(data?: unknown) { - super( - { - code: 'GPFERR011', - statusCode: StatusCodes.INTERNAL_SERVER_ERROR, - // TODO: change message - message: FAILURE_MESSAGES.DOWNLOAD_FILE_UNEXPECTED_ERROR, - }, - data, - ); - } -} +export const DownloadFileUnexpectedError = createError( + 'GPFERR011', + FAILURE_MESSAGES.DOWNLOAD_FILE_UNEXPECTED_ERROR, + StatusCodes.INTERNAL_SERVER_ERROR, +); -export class MalformedFileConfigError extends GraaspFileError { - constructor(message: string) { - super({ - code: 'GPFERR012', - statusCode: StatusCodes.INTERNAL_SERVER_ERROR, - message, - }); - } -} +export const MalformedFileConfigError = createError( + 'GPFERR012', + 'Malformed file configuration', + StatusCodes.INTERNAL_SERVER_ERROR, +); \ No newline at end of file diff --git a/src/services/item/item.controller.update.test.ts b/src/services/item/item.controller.update.test.ts index a02aa805d..da8678702 100644 --- a/src/services/item/item.controller.update.test.ts +++ b/src/services/item/item.controller.update.test.ts @@ -663,7 +663,7 @@ describe('Item routes tests', () => { }); expect(response.statusCode).toBe(StatusCodes.FORBIDDEN); - expect(response.json()).toEqual(new HierarchyTooDeep()); + expect(response.json().message).toEqual(new HierarchyTooDeep().message); }); it('Cannot create inside non-folder item', async () => { @@ -690,7 +690,7 @@ describe('Item routes tests', () => { }); expect(response.statusCode).toBe(StatusCodes.BAD_REQUEST); - expect(response.json()).toMatchObject(new ItemNotFolder({ id: parent.id })); + expect(response.json().message).toEqual(new ItemNotFolder({ id: parent.id }).message); }); }); }); diff --git a/src/services/item/item.service.ts b/src/services/item/item.service.ts index cbd1d8104..402d7b5f3 100644 --- a/src/services/item/item.service.ts +++ b/src/services/item/item.service.ts @@ -289,7 +289,7 @@ export class ItemService { // quick check, necessary for ts if (parentItem.type !== 'folder') { - throw new ItemNotFolder(parentItem); + throw new ItemNotFolder({ id: parentItem.id }); } this.itemRepository.checkHierarchyDepth(parentItem); diff --git a/src/services/item/plugins/action/errors.ts b/src/services/item/plugins/action/errors.ts index 83e14ce77..c9b26c8f3 100644 --- a/src/services/item/plugins/action/errors.ts +++ b/src/services/item/plugins/action/errors.ts @@ -1,24 +1,12 @@ import { StatusCodes } from 'http-status-codes'; -import { ErrorFactory } from '@graasp/sdk'; - -const PLUGIN_NAME = 'graasp-plugin-item-actions'; +import { createError } from '@fastify/error'; /** * Errors thrown by the action item plugin */ -export const GraaspItemActionError = ErrorFactory(PLUGIN_NAME); - -export class CannotPostAction extends GraaspItemActionError { - constructor(data?: unknown) { - super( - { - code: 'GIAERR003', - statusCode: StatusCodes.FORBIDDEN, - message: 'Cannot post action', - }, - data, - ); - } -} +export const CannotPostAction = createError('GIAERR003', + 'Cannot post action', + StatusCodes.FORBIDDEN, +); diff --git a/src/services/item/plugins/app/appAction/errors.ts b/src/services/item/plugins/app/appAction/errors.ts index 8d9a05ab8..bd40ce255 100644 --- a/src/services/item/plugins/app/appAction/errors.ts +++ b/src/services/item/plugins/app/appAction/errors.ts @@ -1,20 +1,10 @@ import { StatusCodes } from 'http-status-codes'; -import { ErrorFactory, FAILURE_MESSAGES } from '@graasp/sdk'; - -import { PLUGIN_NAME } from '../constants'; - -export const GraaspAppActionError = ErrorFactory(PLUGIN_NAME + '/app-action'); - -export class AppActionNotAccessible extends GraaspAppActionError { - constructor(data?: unknown) { - super( - { - code: 'GAERR006', - statusCode: StatusCodes.FORBIDDEN, - message: FAILURE_MESSAGES.APP_ACTION_NOT_ACCESSIBLE, - }, - data, - ); - } -} +import { createError } from '@fastify/error'; +import { FAILURE_MESSAGES } from '@graasp/sdk'; + +export const AppActionNotAccessible = createError( + 'GAERR006', + FAILURE_MESSAGES.APP_ACTION_NOT_ACCESSIBLE, + StatusCodes.FORBIDDEN, +); diff --git a/src/services/item/plugins/app/appData/errors.ts b/src/services/item/plugins/app/appData/errors.ts index b94db9aca..17c5688bf 100644 --- a/src/services/item/plugins/app/appData/errors.ts +++ b/src/services/item/plugins/app/appData/errors.ts @@ -1,72 +1,34 @@ import { StatusCodes } from 'http-status-codes'; -import { ErrorFactory, FAILURE_MESSAGES } from '@graasp/sdk'; - -import { PLUGIN_NAME } from '../constants'; - -export const GraaspAppDataError = ErrorFactory(PLUGIN_NAME + '/app-data'); - -export class AppDataNotFound extends GraaspAppDataError { - constructor(data?: unknown) { - super( - { - code: 'GAERR004', - statusCode: StatusCodes.NOT_FOUND, - message: FAILURE_MESSAGES.APP_DATA_NOT_FOUND, - }, - data, - ); - } -} - -export class AppDataNotAccessible extends GraaspAppDataError { - constructor(data?: unknown) { - super( - { - code: 'GAERR005', - statusCode: StatusCodes.FORBIDDEN, - message: FAILURE_MESSAGES.APP_DATA_NOT_ACCESSIBLE, - }, - data, - ); - } -} - -export class PreventUpdateAppDataFile extends GraaspAppDataError { - constructor(data?: unknown) { - super( - { - code: 'GAERR008', - statusCode: StatusCodes.FORBIDDEN, - message: FAILURE_MESSAGES.PREVENT_APP_DATA_FILE_UPDATE, - }, - data, - ); - } -} - -export class PreventUpdateOtherAppData extends GraaspAppDataError { - constructor(data?: unknown) { - super( - { - code: 'GAERR009', - statusCode: StatusCodes.FORBIDDEN, - message: FAILURE_MESSAGES.CANNOT_MODIFY_OTHER_MEMBERS, - }, - data, - ); - } -} - -export class NotAppDataFile extends GraaspAppDataError { - constructor(data?: unknown) { - super( - { - code: 'GAERR010', - statusCode: StatusCodes.BAD_REQUEST, - message: 'App data is not a file', - }, - data, - ); - } -} +import { createError } from '@fastify/error'; +import { FAILURE_MESSAGES } from '@graasp/sdk'; + +export const AppDataNotFound = createError( + 'GAERR004', + FAILURE_MESSAGES.APP_DATA_NOT_FOUND, + StatusCodes.NOT_FOUND, +); + +export const AppDataNotAccessible = createError( + 'GAERR005', + FAILURE_MESSAGES.APP_DATA_NOT_ACCESSIBLE, + StatusCodes.FORBIDDEN, +); + +export const PreventUpdateAppDataFile = createError( + 'GAERR008', + FAILURE_MESSAGES.PREVENT_APP_DATA_FILE_UPDATE, + StatusCodes.FORBIDDEN, +); + +export const PreventUpdateOtherAppData = createError( + 'GAERR009', + FAILURE_MESSAGES.CANNOT_MODIFY_OTHER_MEMBERS, + StatusCodes.FORBIDDEN, +); + +export const NotAppDataFile = createError( + 'GAERR010', + 'App data is not a file', + StatusCodes.BAD_REQUEST, +); diff --git a/src/services/item/plugins/app/appSetting/appSetting.controller.test.ts b/src/services/item/plugins/app/appSetting/appSetting.controller.test.ts index c6273a08b..054e431f0 100644 --- a/src/services/item/plugins/app/appSetting/appSetting.controller.test.ts +++ b/src/services/item/plugins/app/appSetting/appSetting.controller.test.ts @@ -358,7 +358,7 @@ describe('Apps Settings Tests', () => { }, payload: appSetting, }); - expect(response.json()).toMatchObject(new MemberCannotAdminItem(item.id)); + expect(response.json().message).toEqual(new MemberCannotAdminItem(item.id).message); }); it('Post app setting throws for write membership', async () => { const { apps } = await seedFromJson({ apps: [{}] }); @@ -388,7 +388,7 @@ describe('Apps Settings Tests', () => { }, payload: appSetting, }); - expect(response.json()).toMatchObject(new MemberCannotAdminItem(item.id)); + expect(response.json().message).toEqual(new MemberCannotAdminItem(item.id).message); }); it('Invalid item id throws', async () => { const { apps } = await seedFromJson({ apps: [{}] }); @@ -497,7 +497,7 @@ describe('Apps Settings Tests', () => { }, payload: { data: updatedSetting.data }, }); - expect(response.json()).toMatchObject(new MemberCannotAdminItem(item.id)); + expect(response.json().message).toEqual(new MemberCannotAdminItem(item.id).message); }); it('Patch app setting throws for write membership', async () => { const { apps } = await seedFromJson({ apps: [{}] }); @@ -529,7 +529,7 @@ describe('Apps Settings Tests', () => { }, payload: { data: updatedSetting.data }, }); - expect(response.json()).toMatchObject(new MemberCannotAdminItem(item.id)); + expect(response.json().message).toEqual(new MemberCannotAdminItem(item.id).message); }); it('Invalid item id throws bad request', async () => { const { @@ -730,7 +730,7 @@ describe('Apps Settings Tests', () => { Authorization: `Bearer ${token}`, }, }); - expect(response.json()).toMatchObject(new MemberCannotAdminItem(item.id)); + expect(response.json().message).toEqual(new MemberCannotAdminItem(item.id).message); }); it('Delete app setting throws for write membership', async () => { const { apps } = await seedFromJson({ apps: [{}] }); @@ -760,7 +760,7 @@ describe('Apps Settings Tests', () => { Authorization: `Bearer ${token}`, }, }); - expect(response.json()).toMatchObject(new MemberCannotAdminItem(item.id)); + expect(response.json().message).toEqual(new MemberCannotAdminItem(item.id).message); }); }); }); diff --git a/src/services/item/plugins/app/appSetting/errors.ts b/src/services/item/plugins/app/appSetting/errors.ts index 2a76fa065..127ba2f9e 100644 --- a/src/services/item/plugins/app/appSetting/errors.ts +++ b/src/services/item/plugins/app/appSetting/errors.ts @@ -1,46 +1,22 @@ import { StatusCodes } from 'http-status-codes'; -import { ErrorFactory, FAILURE_MESSAGES } from '@graasp/sdk'; +import { createError } from '@fastify/error'; +import { FAILURE_MESSAGES } from '@graasp/sdk'; -import { PLUGIN_NAME } from '../constants'; +export const AppSettingNotFound = createError( + 'GAERR007', + FAILURE_MESSAGES.APP_SETTING_NOT_FOUND, + StatusCodes.NOT_FOUND, +); -export const GraaspAppSettingError = ErrorFactory(PLUGIN_NAME + '/app-setting'); +export const PreventUpdateAppSettingFile = createError( + 'GAERR009', + FAILURE_MESSAGES.PREVENT_APP_SETTING_FILE_UPDATE, + StatusCodes.FORBIDDEN, +); -export class AppSettingNotFound extends GraaspAppSettingError { - constructor(data?: unknown) { - super( - { - code: 'GAERR007', - statusCode: StatusCodes.NOT_FOUND, - message: FAILURE_MESSAGES.APP_SETTING_NOT_FOUND, - }, - data, - ); - } -} - -export class PreventUpdateAppSettingFile extends GraaspAppSettingError { - constructor(data?: unknown) { - super( - { - code: 'GAERR009', - statusCode: StatusCodes.FORBIDDEN, - message: FAILURE_MESSAGES.PREVENT_APP_SETTING_FILE_UPDATE, - }, - data, - ); - } -} - -export class NotAppSettingFile extends GraaspAppSettingError { - constructor(data?: unknown) { - super( - { - code: 'GAERR010', - statusCode: StatusCodes.BAD_REQUEST, - message: 'App setting is not a file', - }, - data, - ); - } -} +export const NotAppSettingFile = createError( + 'GAERR010', + 'App setting is not a file', + StatusCodes.BAD_REQUEST, +); diff --git a/src/services/item/plugins/app/errors.ts b/src/services/item/plugins/app/errors.ts index 802aae617..732b717a9 100644 --- a/src/services/item/plugins/app/errors.ts +++ b/src/services/item/plugins/app/errors.ts @@ -1,46 +1,22 @@ import { StatusCodes } from 'http-status-codes'; -import { ErrorFactory, FAILURE_MESSAGES } from '@graasp/sdk'; +import { createError } from '@fastify/error'; +import { FAILURE_MESSAGES } from '@graasp/sdk'; -import { PLUGIN_NAME } from './constants'; +export const NotAppItem = createError( + 'GAERR001', + FAILURE_MESSAGES.NOT_APP_ITEM, + StatusCodes.BAD_REQUEST, +); -export const GraaspAppsError = ErrorFactory(PLUGIN_NAME); +export const InvalidApplicationOrigin = createError( + 'GAERR002', + FAILURE_MESSAGES.INVALID_APP_ORIGIN, + StatusCodes.FORBIDDEN, +); -export class NotAppItem extends GraaspAppsError { - constructor(data?: unknown) { - super( - { - code: 'GAERR001', - statusCode: StatusCodes.BAD_REQUEST, - message: FAILURE_MESSAGES.NOT_APP_ITEM, - }, - data, - ); - } -} - -export class InvalidApplicationOrigin extends GraaspAppsError { - constructor(data?: unknown) { - super( - { - code: 'GAERR002', - statusCode: StatusCodes.FORBIDDEN, - message: FAILURE_MESSAGES.INVALID_APP_ORIGIN, - }, - data, - ); - } -} - -export class TokenItemIdMismatch extends GraaspAppsError { - constructor(data?: unknown) { - super( - { - code: 'GAERR003', - statusCode: StatusCodes.UNAUTHORIZED, - message: FAILURE_MESSAGES.TOKEN_ITEM_ID_MISMATCH, - }, - data, - ); - } -} +export const TokenItemIdMismatch = createError( + 'GAERR003', + FAILURE_MESSAGES.TOKEN_ITEM_ID_MISMATCH, + StatusCodes.UNAUTHORIZED, +); \ No newline at end of file diff --git a/src/services/item/plugins/etherpad/errors.ts b/src/services/item/plugins/etherpad/errors.ts index 289f66231..4bc261af6 100644 --- a/src/services/item/plugins/etherpad/errors.ts +++ b/src/services/item/plugins/etherpad/errors.ts @@ -1,33 +1,15 @@ import { StatusCodes } from 'http-status-codes'; -import { ErrorFactory } from '@graasp/sdk'; - -import { PLUGIN_NAME } from './constants'; - -export const GraaspEtherpadError = ErrorFactory(PLUGIN_NAME); - -export class EtherpadServerError extends GraaspEtherpadError { - constructor(data?: unknown) { - super( - { - code: 'GPEPERR001', - statusCode: StatusCodes.INTERNAL_SERVER_ERROR, - message: 'Internal Etherpad server error', - }, - data, - ); - } -} - -export class ItemMissingExtraError extends GraaspEtherpadError { - constructor(data?: unknown) { - super( - { - code: 'GPEPERR003', - statusCode: StatusCodes.INTERNAL_SERVER_ERROR, - message: 'Item missing etherpad extra', - }, - data, - ); - } -} +import { createError } from '@fastify/error'; + +export const EtherpadServerError = createError( + 'GPEPERR001', + 'Internal Etherpad server error', + StatusCodes.INTERNAL_SERVER_ERROR, +); + +export const ItemMissingExtraError = createError( + 'GPEPERR003', + 'Item missing etherpad extra', + StatusCodes.INTERNAL_SERVER_ERROR, +); diff --git a/src/services/item/plugins/file/utils/errors.ts b/src/services/item/plugins/file/utils/errors.ts index b345474bf..87ebe1b5b 100644 --- a/src/services/item/plugins/file/utils/errors.ts +++ b/src/services/item/plugins/file/utils/errors.ts @@ -2,12 +2,9 @@ import { StatusCodes } from 'http-status-codes'; import { createError } from '@fastify/error'; -import { ErrorFactory, FAILURE_MESSAGES } from '@graasp/sdk'; - -import { PLUGIN_NAME } from './constants'; - -export const GraaspFileItemError = ErrorFactory(PLUGIN_NAME); +import { FAILURE_MESSAGES } from '@graasp/sdk'; + export const StorageExceeded = createError( 'GPFERR009', FAILURE_MESSAGES.STORAGE_EXCEEDED, diff --git a/src/services/item/plugins/geolocation/itemGeolocation.repository.spec.ts b/src/services/item/plugins/geolocation/itemGeolocation.repository.spec.ts index d240be4cc..a967dc5b6 100644 --- a/src/services/item/plugins/geolocation/itemGeolocation.repository.spec.ts +++ b/src/services/item/plugins/geolocation/itemGeolocation.repository.spec.ts @@ -816,39 +816,42 @@ describe('ItemGeolocationRepository', () => { assertIsDefined(actor); assertIsMemberForTest(actor); - repository - .getItemsIn(db, actor, { - lat1: null, - lat2: null, - lng1: null, - lng2: null, - // eslint-disable-next-line @typescript-eslint/no-explicit-any - } as any) - .catch((e) => { - expect(e).toMatchObject(new MissingGeolocationSearchParams(expect.anything())); - }); - repository - .getItemsIn(db, actor, { - lat1: 1, - lat2: 2, - lng1: 1, - lng2: null, - // eslint-disable-next-line @typescript-eslint/no-explicit-any - } as any) - .catch((e) => { - expect(e).toMatchObject(new MissingGeolocationSearchParams(expect.anything())); - }); - repository - .getItemsIn(db, actor, { - lat1: 1, - lat2: 2, - lng1: 1, - lng2: null, - // eslint-disable-next-line @typescript-eslint/no-explicit-any - } as any) - .catch((e) => { - expect(e).toMatchObject(new MissingGeolocationSearchParams(expect.anything())); - }); + const geoloc1 = { + lat1: null, + lat2: null, + lng1: null, + lng2: null, + // eslint-disable-next-line @typescript-eslint/no-explicit-any + } as any; + repository.getItemsIn(db, actor, geoloc1).catch((e) => { + expect(e.message).toMatchObject( + new MissingGeolocationSearchParams({ parentItem: undefined, ...geoloc1 }).message, + ); + }); + const geoloc2 = { + lat1: 1, + lat2: 2, + lng1: 1, + lng2: null, + // eslint-disable-next-line @typescript-eslint/no-explicit-any + } as any; + repository.getItemsIn(db, actor, geoloc2).catch((e) => { + expect(e.message).toMatchObject( + new MissingGeolocationSearchParams({ parentItem: undefined, ...geoloc2 }).message, + ); + }); + const geoloc3 = { + lat1: 1, + lat2: 2, + lng1: 1, + lng2: null, + // eslint-disable-next-line @typescript-eslint/no-explicit-any + } as any; + repository.getItemsIn(db, actor, geoloc3).catch((e) => { + expect(e.message).toMatchObject( + new MissingGeolocationSearchParams({ parentItem: undefined, ...geoloc3 }).message, + ); + }); }); }); describe('put', () => { diff --git a/src/services/item/plugins/itemBookmark/errors.ts b/src/services/item/plugins/itemBookmark/errors.ts index 15f6b429d..dbf560dbe 100644 --- a/src/services/item/plugins/itemBookmark/errors.ts +++ b/src/services/item/plugins/itemBookmark/errors.ts @@ -2,10 +2,6 @@ import { StatusCodes } from 'http-status-codes'; import { createError } from '@fastify/error'; -import { ErrorFactory } from '@graasp/sdk'; - -export const GraaspBookmarkError = ErrorFactory('graasp-plugin-bookmark'); - export const DuplicateBookmarkError = createError( 'GPCATERR001', 'This item is already bookmarked', diff --git a/src/services/item/plugins/itemLike/itemLike.controller.test.ts b/src/services/item/plugins/itemLike/itemLike.controller.test.ts index 044a33293..40b6a40a5 100644 --- a/src/services/item/plugins/itemLike/itemLike.controller.test.ts +++ b/src/services/item/plugins/itemLike/itemLike.controller.test.ts @@ -147,7 +147,7 @@ describe('Item Like', () => { url: `/api/items/${item.id}/likes`, }); - expect(response.json()).toMatchObject(new MemberCannotAccess(expect.anything())); + expect(response.json().message).toEqual(new MemberCannotAccess(item.id).message); }); }); diff --git a/src/services/item/plugins/itemLike/itemLike.errors.ts b/src/services/item/plugins/itemLike/itemLike.errors.ts index a6c0c8974..b8c479cfe 100644 --- a/src/services/item/plugins/itemLike/itemLike.errors.ts +++ b/src/services/item/plugins/itemLike/itemLike.errors.ts @@ -2,10 +2,7 @@ import { StatusCodes } from 'http-status-codes'; import { createError } from '@fastify/error'; -import { ErrorFactory } from '@graasp/sdk'; - -export const GraaspItemLikeError = ErrorFactory('graasp-plugin-item-like'); - + export const ItemLikeNotFound = createError( 'GPILERR001', 'Item Like not found', diff --git a/src/services/item/plugins/itemVisibility/itemVisibility.controller.test.ts b/src/services/item/plugins/itemVisibility/itemVisibility.controller.test.ts index 0abbb85c7..667858f8c 100644 --- a/src/services/item/plugins/itemVisibility/itemVisibility.controller.test.ts +++ b/src/services/item/plugins/itemVisibility/itemVisibility.controller.test.ts @@ -97,9 +97,12 @@ describe('Item Visibility', () => { method: HttpMethod.Post, url: `${ITEMS_ROUTE_PREFIX}/${item.id}/visibilities/${ItemVisibilityType.Hidden}`, }); - expect(res.json()).toMatchObject( - new ConflictingVisibilitiesInTheHierarchy(expect.anything()), - ); + const expectedError = new ConflictingVisibilitiesInTheHierarchy({ + itemPath: item.path, + type: ItemVisibilityType.Hidden, + }); + expect(res.statusCode).toEqual(expectedError.statusCode); + expect(res.json().message).toEqual(expectedError.message); }); it('Cannot create visibility if exists on parent', async () => { @@ -116,9 +119,12 @@ describe('Item Visibility', () => { method: HttpMethod.Post, url: `${ITEMS_ROUTE_PREFIX}/${child.id}/visibilities/${ItemVisibilityType.Hidden}`, }); - expect(res.json()).toMatchObject( - new ConflictingVisibilitiesInTheHierarchy(expect.anything()), - ); + const expectedError = new ConflictingVisibilitiesInTheHierarchy({ + itemPath: child.path, + type: ItemVisibilityType.Hidden, + }); + expect(res.statusCode).toEqual(expectedError.statusCode); + expect(res.json().message).toEqual(expectedError.message); }); it('Bad request if item id is invalid', async () => { @@ -215,7 +221,9 @@ describe('Item Visibility', () => { method: HttpMethod.Delete, url: `${ITEMS_ROUTE_PREFIX}/${child.id}/visibilities/${ItemVisibilityType.Public}`, }); - expect(res.json()).toMatchObject(new CannotModifyParentVisibility(expect.anything())); + const expectedError = new CannotModifyParentVisibility(child.id); + expect(res.statusCode).toEqual(expectedError.statusCode); + expect(res.json().message).toEqual(expectedError.message); }); it('Does not throw if visibility does not exist', async () => { const { diff --git a/src/services/item/plugins/itemVisibility/itemVisibility.repository.ts b/src/services/item/plugins/itemVisibility/itemVisibility.repository.ts index 18efcbe0f..d0eb8ea60 100644 --- a/src/services/item/plugins/itemVisibility/itemVisibility.repository.ts +++ b/src/services/item/plugins/itemVisibility/itemVisibility.repository.ts @@ -229,7 +229,7 @@ export class ItemVisibilityRepository { ): Promise { const entry = await this.getType(dbConnection, item.path, type); if (entry && entry.item.path !== item.path && shouldThrow) { - throw new CannotModifyParentVisibility(entry); + throw new CannotModifyParentVisibility(item.id); } } diff --git a/src/services/item/plugins/publication/published/errors.ts b/src/services/item/plugins/publication/published/errors.ts index 3afdc0d62..d1c543fef 100644 --- a/src/services/item/plugins/publication/published/errors.ts +++ b/src/services/item/plugins/publication/published/errors.ts @@ -2,11 +2,7 @@ import { StatusCodes } from 'http-status-codes'; import { createError } from '@fastify/error'; -import { ErrorFactory, PublishableItemTypeChecker } from '@graasp/sdk'; -import { ItemType } from '../../../../../schemas/global'; - -export const GraaspPublishedError = ErrorFactory('graasp-plugin-published-item'); export const ItemPublishedNotFound = createError( 'GPPIERR001', @@ -14,49 +10,20 @@ export const ItemPublishedNotFound = createError( StatusCodes.NOT_FOUND, ); -export class ItemTypeNotAllowedToPublish extends GraaspPublishedError { - constructor(itemId: string, itemType: ItemType) { - const allowedTypes = PublishableItemTypeChecker.getAllowedTypes(); - super( - { - code: 'GPPIERR002', - statusCode: StatusCodes.BAD_REQUEST, - message: `The type "${itemType}" of item "${itemId}" is not allowed to be published. Only these types are allowed: ${allowedTypes}.`, - }, - { - itemId, - itemType, - }, - ); - } -} +export const ItemTypeNotAllowedToPublish = createError( + 'GPPIERR002', + 'Item type not allowed to publish', + StatusCodes.BAD_REQUEST, +); -export class ItemPublicationAlreadyExists extends GraaspPublishedError { - constructor(itemId: string) { - super( - { - code: 'GPPIERR003', - statusCode: StatusCodes.BAD_REQUEST, - message: `The item "${itemId}" is already published.`, - }, - { - itemId, - }, - ); - } -} +export const ItemPublicationAlreadyExists = createError( + 'GPPIERR003', + 'Item is already published', + StatusCodes.BAD_REQUEST, +); -export class ItemIsNotValidated extends GraaspPublishedError { - constructor(itemId: string) { - super( - { - code: 'GPPIERR004', - statusCode: StatusCodes.BAD_REQUEST, - message: `The item "${itemId}" must be validated before publishing it in the Libary.`, - }, - { - itemId, - }, - ); - } -} +export const ItemIsNotValidated = createError( + 'GPPIERR004', + 'Item must be validated before publishing', + StatusCodes.BAD_REQUEST, +); diff --git a/src/services/item/plugins/publication/published/itemPublished.controller.test.ts b/src/services/item/plugins/publication/published/itemPublished.controller.test.ts index 68f2efcb7..bcf4a71d5 100644 --- a/src/services/item/plugins/publication/published/itemPublished.controller.test.ts +++ b/src/services/item/plugins/publication/published/itemPublished.controller.test.ts @@ -5,7 +5,7 @@ import waitForExpect from 'wait-for-expect'; import type { FastifyInstance } from 'fastify'; -import { HttpMethod, ItemValidationStatus } from '@graasp/sdk'; +import { HttpMethod, ItemValidationStatus, ItemVisibilityType } from '@graasp/sdk'; import build, { clearDatabase, @@ -367,7 +367,9 @@ describe('Item Published', () => { url: `${ITEMS_ROUTE_PREFIX}/collections/${item.id}/publish`, }); expect(res.statusCode).toBe(StatusCodes.NOT_FOUND); - expect(res.json()).toMatchObject(new ItemVisibilityNotFound(expect.anything())); + expect(res.json().message).toEqual( + new ItemVisibilityNotFound(ItemVisibilityType.Public).message, + ); }); it('Cannot publish item with write rights', async () => { @@ -389,7 +391,7 @@ describe('Item Published', () => { method: HttpMethod.Post, url: `${ITEMS_ROUTE_PREFIX}/collections/${item.id}/publish`, }); - expect(res.json()).toMatchObject(new MemberCannotAdminItem(expect.anything())); + expect(res.json().message).toEqual(new MemberCannotAdminItem(item.id).message); }); it('Cannot publish item with read rights', async () => { @@ -411,7 +413,7 @@ describe('Item Published', () => { method: HttpMethod.Post, url: `${ITEMS_ROUTE_PREFIX}/collections/${item.id}/publish`, }); - expect(res.json()).toMatchObject(new MemberCannotAdminItem(expect.anything())); + expect(res.json().message).toEqual(new MemberCannotAdminItem(item.id).message); }); it('Cannot publish non-folder item', async () => { @@ -574,7 +576,7 @@ describe('Item Published', () => { method: HttpMethod.Delete, url: `${ITEMS_ROUTE_PREFIX}/collections/${item.id}/unpublish`, }); - expect(res.json()).toMatchObject(new MemberCannotAdminItem(expect.anything())); + expect(res.json().message).toEqual(new MemberCannotAdminItem(item.id).message); }); it('Cannot publish item with read rights', async () => { const { @@ -597,7 +599,7 @@ describe('Item Published', () => { method: HttpMethod.Delete, url: `${ITEMS_ROUTE_PREFIX}/collections/${item.id}/unpublish`, }); - expect(res.json()).toMatchObject(new MemberCannotAdminItem(expect.anything())); + expect(res.json().message).toEqual(new MemberCannotAdminItem(item.id).message); }); it('Throws if item id is invalid', async () => { const { actor } = await seedFromJson(); diff --git a/src/services/item/plugins/publication/validation/errors.ts b/src/services/item/plugins/publication/validation/errors.ts index 632ddcdb9..d41b01181 100644 --- a/src/services/item/plugins/publication/validation/errors.ts +++ b/src/services/item/plugins/publication/validation/errors.ts @@ -1,124 +1,57 @@ import { StatusCodes } from 'http-status-codes'; -import { ErrorFactory } from '@graasp/sdk'; - -import { PLUGIN_NAME } from './constants'; - -const GraaspValidationError = ErrorFactory(PLUGIN_NAME); - -export class InvalidFileItemError extends GraaspValidationError { - constructor(data?: unknown) { - super( - { - code: 'GPVERR001', - statusCode: StatusCodes.BAD_REQUEST, - message: 'File properties are invalid.', - }, - data, - ); - } -} - -export class FailedImageClassificationRequestError extends GraaspValidationError { - constructor(data?: unknown) { - super( - { - code: 'GPVERR002', - statusCode: StatusCodes.BAD_REQUEST, - message: 'Image classification request failed', - }, - data, - ); - } -} - -export class ProcessNotFoundError extends GraaspValidationError { - constructor(data?: unknown) { - super( - { - code: 'GPVERR003', - statusCode: StatusCodes.BAD_REQUEST, - message: 'Process Not Found', - }, - data, - ); - } -} - -export class ProcessExecutionError extends GraaspValidationError { - constructor(process: string, data?: unknown) { - super( - { - code: 'GPVERR004', - statusCode: StatusCodes.BAD_REQUEST, - message: `An error occurs during execution process: ${process}`, - }, - data, - ); - } -} - -export class ItemValidationError extends GraaspValidationError { - constructor(data?: unknown) { - super( - { - code: 'GPVERR005', - statusCode: StatusCodes.BAD_REQUEST, - message: 'An error occurs while validating the item', - }, - data, - ); - } -} - -export class ItemValidationGroupNotFound extends GraaspValidationError { - constructor(data?: unknown) { - super( - { - code: 'GPVERR006', - statusCode: StatusCodes.NOT_FOUND, - message: 'Item validation group not found', - }, - data, - ); - } -} - -export class ItemValidationNotFound extends GraaspValidationError { - constructor(data?: unknown) { - super( - { - code: 'GPVERR007', - statusCode: StatusCodes.NOT_FOUND, - message: 'Item validation not found', - }, - data, - ); - } -} - -export class ItemValidationAlreadyExist extends GraaspValidationError { - constructor(itemId: string) { - super( - { - code: 'GPVERR008', - statusCode: StatusCodes.CONFLICT, - message: `Item validation already exists for item ${itemId}`, - }, - itemId, - ); - } -} - -export class ItemValidationReviewNotFound extends GraaspValidationError { - constructor(data?: unknown) { - super( - { - code: 'GPVERR009', - statusCode: StatusCodes.NOT_FOUND, - message: 'Item validation review not found', - }, - data, - ); - } -} +import { createError } from '@fastify/error'; + +export const InvalidFileItemError = createError( + 'GPVERR001', + 'File properties are invalid.', + StatusCodes.BAD_REQUEST, +); + +export const FailedImageClassificationRequestError = createError( + 'GPVERR002', + 'Image classification request failed', + StatusCodes.BAD_REQUEST, +); + +export const ProcessNotFoundError = createError( + 'GPVERR003', + 'Process Not Found', + StatusCodes.BAD_REQUEST, +); + +export const ProcessExecutionError = createError( + 'GPVERR004', + 'Execution process error', + StatusCodes.BAD_REQUEST, +); + +export const ItemValidationError = createError( + 'GPVERR005', + 'An error occurs while validating the item', + StatusCodes.BAD_REQUEST, +); + +export const ItemValidationGroupNotFound = createError( + 'GPVERR006', + 'Item validation group not found', + StatusCodes.NOT_FOUND, +); + +export const ItemValidationNotFound = createError( + 'GPVERR007', + 'Item validation not found', + StatusCodes.NOT_FOUND, +); + +export const ItemValidationAlreadyExist = createError( + 'GPVERR008', + 'Item validation already exists', + StatusCodes.CONFLICT, +); + +export const ItemValidationReviewNotFound = createError( + 'GPVERR009', + 'Item validation review not found', + StatusCodes.NOT_FOUND, +); diff --git a/src/services/item/plugins/publication/validation/itemValidation.controller.test.ts b/src/services/item/plugins/publication/validation/itemValidation.controller.test.ts index 0156e5461..71d691819 100644 --- a/src/services/item/plugins/publication/validation/itemValidation.controller.test.ts +++ b/src/services/item/plugins/publication/validation/itemValidation.controller.test.ts @@ -111,7 +111,7 @@ describe('Item Validation Tests', () => { method: HttpMethod.Get, url: `${ITEMS_ROUTE_PREFIX}/${item.id}/validations/latest`, }); - expect(res.json()).toMatchObject(new MemberCannotAdminItem(expect.anything())); + expect(res.json().message).toEqual(new MemberCannotAdminItem(item.id).message); }); it('Throws if has write permission', async () => { @@ -128,7 +128,7 @@ describe('Item Validation Tests', () => { method: HttpMethod.Get, url: `${ITEMS_ROUTE_PREFIX}/${item.id}/validations/latest`, }); - expect(res.json()).toMatchObject(new MemberCannotAdminItem(expect.anything())); + expect(res.json().message).toEqual(new MemberCannotAdminItem(item.id).message); // check no created entries }); @@ -159,133 +159,6 @@ describe('Item Validation Tests', () => { }); }); - // REMOVE? not used anymore - // describe('GET /:itemId/validations/:itemValidationGroupId', () => { - // it('Throws if signed out', async () => { - // const response = await app.inject({ - // method: HttpMethod.Get, - // url: `${ITEMS_ROUTE_PREFIX}/${v4()}/validations/${v4()}`, - // }); - - // expect(response.statusCode).toBe(StatusCodes.UNAUTHORIZED); - // }); - - // describe('Signed In', () => { - // it('Get item validation groups', async () => { - // const { - // items: [item], - // actor, - // itemValidationGroups: [itemValidationGroup], - // } = await seedFromJson({ - // items: [ - // { - // itemValidations: [{ groupName: 'name', status: ItemValidationStatus.Failure }], - // memberships: [{ account: 'actor', permission: "admin" }], - // }, - // ], - // }); - // assertIsDefined(actor); - // mockAuthenticate(actor); - - // const res = await app.inject({ - // method: HttpMethod.Get, - // url: `${ITEMS_ROUTE_PREFIX}/${item.id}/validations/${itemValidationGroup.id}`, - // }); - // expect(res.statusCode).toBe(StatusCodes.OK); - // expectItemValidation(res.json(), itemValidationGroup); - // }); - - // it('Throws if has read permission', async () => { - // const { - // items: [item], - // actor, - // itemValidationGroups: [itemValidationGroup], - // } = await seedFromJson({ - // items: [{ memberships: [{ account: 'actor', permission: "read" }] }], - // }); - // assertIsDefined(actor); - // mockAuthenticate(actor); - - // const res = await app.inject({ - // method: HttpMethod.Get, - // // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - // url: `${ITEMS_ROUTE_PREFIX}/${item.id}/validations/${itemValidationGroup!.id}`, - // }); - // expect(res.json()).toMatchObject(new MemberCannotAdminItem(expect.anything())); - // }); - - // it('Throws if has write permission', async () => { - // const { - // items: [item], - // actor, - // itemValidationGroups: [itemValidationGroup], - // } = await seedFromJson({ - // items: [{ memberships: [{ account: 'actor', permission: "write" }] }], - // }); - // assertIsDefined(actor); - // mockAuthenticate(actor); - - // const res = await app.inject({ - // method: HttpMethod.Get, - // // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - // url: `${ITEMS_ROUTE_PREFIX}/${item.id}/validations/${itemValidationGroup!.id}`, - // }); - // expect(res.json()).toMatchObject(new MemberCannotAdminItem(expect.anything())); - // }); - - // it('Bad request if id is invalid', async () => { - // const { actor } = await seedFromJson(); - // assertIsDefined(actor); - // mockAuthenticate(actor); - - // const res = await app.inject({ - // method: HttpMethod.Get, - // url: `${ITEMS_ROUTE_PREFIX}/invalid-id/validations/${v4()}`, - // }); - // expect(res.statusCode).toBe(StatusCodes.BAD_REQUEST); - // }); - - // it('Throws if item does not exist', async () => { - // const { actor } = await seedFromJson(); - // assertIsDefined(actor); - // mockAuthenticate(actor); - - // const res = await app.inject({ - // method: HttpMethod.Get, - // url: `${ITEMS_ROUTE_PREFIX}/${v4()}/validations/${v4()}`, - // }); - // expect(res.statusCode).toBe(StatusCodes.NOT_FOUND); - // }); - - // it('Bad request if group id is invalid', async () => { - // const { actor } = await seedFromJson(); - // assertIsDefined(actor); - // mockAuthenticate(actor); - - // const res = await app.inject({ - // method: HttpMethod.Get, - // url: `${ITEMS_ROUTE_PREFIX}/${v4()}/validations/invalid-id`, - // }); - // expect(res.statusCode).toBe(StatusCodes.BAD_REQUEST); - // }); - - // it('Throws if validation group does not exist', async () => { - // const { - // actor, - // items: [item], - // } = await seedFromJson({ items: [{}] }); - // assertIsDefined(actor); - // mockAuthenticate(actor); - - // const res = await app.inject({ - // method: HttpMethod.Get, - // url: `${ITEMS_ROUTE_PREFIX}/${item.id}/validations/${v4()}`, - // }); - // expect(res.json()).toMatchObject(new ItemValidationGroupNotFound(expect.anything())); - // }); - // }); - // }); - describe('POST /:itemId/validate', () => { it('Throws if signed out', async () => { const response = await app.inject({ diff --git a/src/services/item/plugins/recycled/recycled.errors.ts b/src/services/item/plugins/recycled/recycled.errors.ts index 851518945..e0f678fa1 100644 --- a/src/services/item/plugins/recycled/recycled.errors.ts +++ b/src/services/item/plugins/recycled/recycled.errors.ts @@ -1,18 +1,9 @@ import { StatusCodes } from 'http-status-codes'; -import { ErrorFactory } from '@graasp/sdk'; +import { createError } from '@fastify/error'; -export const GraaspRecyledItemsError = ErrorFactory('graasp-plugin-recycled-items'); - -export class CannotRestoreNonDeletedItem extends GraaspRecyledItemsError { - constructor(data?: unknown) { - super( - { - code: 'GPREIERR001', - statusCode: StatusCodes.BAD_REQUEST, - message: 'Item is not recycled', - }, - data, - ); - } -} +export const CannotRestoreNonDeletedItem = createError( + 'GPREIERR001', + 'Item is not recycled', + StatusCodes.BAD_REQUEST, +); diff --git a/src/services/item/plugins/thumbnail/itemThumbnail.controller.test.ts b/src/services/item/plugins/thumbnail/itemThumbnail.controller.test.ts index 48e298836..0e9a075cc 100644 --- a/src/services/item/plugins/thumbnail/itemThumbnail.controller.test.ts +++ b/src/services/item/plugins/thumbnail/itemThumbnail.controller.test.ts @@ -156,7 +156,7 @@ describe('Thumbnail Plugin Tests', () => { url: `${ITEMS_ROUTE_PREFIX}/${item.id}${THUMBNAILS_ROUTE_PREFIX}/${size}`, }); - expect(response.json()).toMatchObject(new MemberCannotAccess(expect.anything())); + expect(response.json().message).toEqual(new MemberCannotAccess(item.id).message); } }); @@ -262,7 +262,7 @@ describe('Thumbnail Plugin Tests', () => { payload: form3, headers: form3.getHeaders(), }); - expect(response.json()).toMatchObject(new MemberCannotAccess(expect.anything())); + expect(response.json().message).toEqual(new MemberCannotAccess(item.id).message); const savedItem = await db.query.itemsRawTable.findFirst({ where: eq(itemsRawTable.id, item.id), }); diff --git a/src/services/itemLogin/errors.ts b/src/services/itemLogin/errors.ts index 753ee763d..1eaf6bc9d 100644 --- a/src/services/itemLogin/errors.ts +++ b/src/services/itemLogin/errors.ts @@ -2,11 +2,7 @@ import { StatusCodes } from 'http-status-codes'; import { createError } from '@fastify/error'; -import { ErrorFactory, FAILURE_MESSAGES } from '@graasp/sdk'; - -const PLUGIN_NAME = 'graasp-plugin-item-login'; - -export const GraaspItemLoginError = ErrorFactory(PLUGIN_NAME); +import { FAILURE_MESSAGES } from '@graasp/sdk'; export const MemberIdentifierNotFound = createError( 'GILERR002', @@ -20,185 +16,82 @@ export const InvalidMember = createError( StatusCodes.NOT_FOUND, ); -export class MissingItemLoginSchema extends GraaspItemLoginError { - constructor(data?: unknown) { - super( - { - code: 'GILERR004', - statusCode: StatusCodes.INTERNAL_SERVER_ERROR, - message: 'Missing login schema', - }, - data, - ); - } -} - -export class MissingItemLoginTag extends GraaspItemLoginError { - constructor(data?: unknown) { - super( - { - code: 'GILERR005', - statusCode: StatusCodes.BAD_REQUEST, - message: 'Item does not possess the required visibility', - }, - data, - ); - } -} - -export class ValidMemberSession extends GraaspItemLoginError { - constructor(data?: unknown) { - super( - { - code: 'GILERR006', - statusCode: StatusCodes.BAD_REQUEST, - message: 'Member with valid session trying to (re)login', - }, - data, - ); - } -} - -export class InvalidCredentials extends GraaspItemLoginError { - constructor(data?: unknown) { - super( - { - code: 'GILERR007', - statusCode: StatusCodes.UNAUTHORIZED, - // eslint-disable-next-line quotes - message: "Provided credentials don't match member's", - }, - data, - ); - } -} - -export class MissingCredentialsForLoginSchema extends GraaspItemLoginError { - constructor(data?: unknown) { - super( - { - code: 'GILERR008', - statusCode: StatusCodes.BAD_REQUEST, - message: 'Missing credentials for set login schema', - }, - data, - ); - } -} - -export class UnnecessaryCredentialsForLoginSchema extends GraaspItemLoginError { - constructor(data?: unknown) { - super( - { - code: 'GILERR008', - statusCode: StatusCodes.BAD_REQUEST, - message: 'Unnecessary credentials for set login schema', - }, - data, - ); - } -} - -export class ItemLoginSchemaNotFound extends GraaspItemLoginError { - constructor(data?: unknown) { - super( - { - code: 'GILERR009', - statusCode: StatusCodes.NOT_FOUND, - message: 'Item login schema not found for item', - }, - data, - ); - } -} - -export class CannotNestItemLoginSchema extends GraaspItemLoginError { - constructor(data?: unknown) { - super( - { - code: 'GILERR010', - statusCode: StatusCodes.FORBIDDEN, - message: 'Item login schema already item exists in an ancestor', - }, - data, - ); - } -} - -export class NotGuest extends GraaspItemLoginError { - constructor(data?: unknown) { - super( - { - code: 'GILERR011', - statusCode: StatusCodes.FORBIDDEN, - message: FAILURE_MESSAGES.NOT_A_GUEST, - }, - data, - ); - } -} - -export class ItemLoginSchemaExists extends GraaspItemLoginError { - constructor(data?: unknown) { - super( - { - code: 'GILERR012', - statusCode: StatusCodes.CONFLICT, - message: FAILURE_MESSAGES.CANNOT_CREATE_MEMBERSHIP_CAUSE_ITEM_LOGIN_SCHEMA_EXISTS, - }, - data, - ); - } -} - -export class CannotEnrollItemWithoutItemLoginSchema extends GraaspItemLoginError { - constructor(data?: unknown) { - super( - { - code: 'GILERR013', - statusCode: StatusCodes.FORBIDDEN, - message: FAILURE_MESSAGES.CANNOT_ENROLL_ITEM_WITHOUT_ITEM_LOGIN_SCHEMA, - }, - data, - ); - } -} - -export class CannotRegisterOnFrozenItemLoginSchema extends GraaspItemLoginError { - constructor(data?: unknown) { - super( - { - code: 'GILERR014', - statusCode: StatusCodes.FORBIDDEN, - message: FAILURE_MESSAGES.CANNOT_REGISTER_ON_FROZEN_ITEM_LOGIN_SCHEMA, - }, - data, - ); - } -} - -export class CannotEnrollFrozenItemLoginSchema extends GraaspItemLoginError { - constructor(data?: unknown) { - super( - { - code: 'GILERR015', - statusCode: StatusCodes.FORBIDDEN, - message: FAILURE_MESSAGES.CANNOT_ENROLL_FROZEN_ITEM_LOGIN_SCHEMA, - }, - data, - ); - } -} - -export class GuestNotFound extends GraaspItemLoginError { - constructor(data?: unknown) { - super( - { - code: 'GILERR016', - statusCode: StatusCodes.NOT_FOUND, - message: 'GUEST_NOT_FOUND', - }, - data, - ); - } -} +export const MissingItemLoginSchema = createError( + 'GILERR004', + 'Missing login schema', + StatusCodes.INTERNAL_SERVER_ERROR, +); + +export const MissingItemLoginTag = createError( + 'GILERR005', + 'Item does not possess the required visibility', + StatusCodes.BAD_REQUEST, +); + +export const ValidMemberSession = createError( + 'GILERR006', + 'Member with valid session trying to (re)login', + StatusCodes.BAD_REQUEST, +); + +export const InvalidCredentials = createError( + 'GILERR007', + "Provided credentials don't match member's", + StatusCodes.UNAUTHORIZED, +); + +export const MissingCredentialsForLoginSchema = createError( + 'GILERR008', + 'Missing credentials for set login schema', + StatusCodes.BAD_REQUEST, +); + +export const UnnecessaryCredentialsForLoginSchema = createError( + 'GILERR008', + 'Unnecessary credentials for set login schema', + StatusCodes.BAD_REQUEST, +); + +export const ItemLoginSchemaNotFound = createError( + 'GILERR009', + 'Item login schema not found for item', + StatusCodes.NOT_FOUND, +); + +export const CannotNestItemLoginSchema = createError( + 'GILERR010', + 'Item login schema already item exists in an ancestor', + StatusCodes.FORBIDDEN, +); + +export const NotGuest = createError( + 'GILERR011', + FAILURE_MESSAGES.NOT_A_GUEST, + StatusCodes.FORBIDDEN, +); + +export const ItemLoginSchemaExists = createError( + 'GILERR012', + FAILURE_MESSAGES.CANNOT_CREATE_MEMBERSHIP_CAUSE_ITEM_LOGIN_SCHEMA_EXISTS, + StatusCodes.CONFLICT, +); + +export const CannotEnrollItemWithoutItemLoginSchema = createError( + 'GILERR013', + FAILURE_MESSAGES.CANNOT_ENROLL_ITEM_WITHOUT_ITEM_LOGIN_SCHEMA, + StatusCodes.FORBIDDEN, +); + +export const CannotRegisterOnFrozenItemLoginSchema = createError( + 'GILERR014', + FAILURE_MESSAGES.CANNOT_REGISTER_ON_FROZEN_ITEM_LOGIN_SCHEMA, + StatusCodes.FORBIDDEN, +); + +export const CannotEnrollFrozenItemLoginSchema = createError( + 'GILERR015', + FAILURE_MESSAGES.CANNOT_ENROLL_FROZEN_ITEM_LOGIN_SCHEMA, + StatusCodes.FORBIDDEN, +); + +export const GuestNotFound = createError('GILERR016', 'GUEST_NOT_FOUND', StatusCodes.NOT_FOUND); diff --git a/src/services/itemLogin/itemLogin.controller.test.ts b/src/services/itemLogin/itemLogin.controller.test.ts index c0db1cd7d..165a798d0 100644 --- a/src/services/itemLogin/itemLogin.controller.test.ts +++ b/src/services/itemLogin/itemLogin.controller.test.ts @@ -379,7 +379,7 @@ describe('Item Login Tests', () => { url: `${ITEMS_ROUTE_PREFIX}/${item.id}/login-schema`, }); - expect(res.json()).toMatchObject(new MemberCannotAdminItem(item.id)); + expect(res.json().message).toEqual(new MemberCannotAdminItem(item.id).message); }); it('Throws if id is not valid', async () => { @@ -417,7 +417,7 @@ describe('Item Login Tests', () => { url: `${ITEMS_ROUTE_PREFIX}/${item.id}/login`, payload: { username: faker.internet.username() }, }); - expect(res.json()).toMatchObject(new ValidMemberSession(expect.anything())); + expect(res.json().message).toEqual(new ValidMemberSession(actor).message); }); }); @@ -978,13 +978,13 @@ describe('Item Login Tests', () => { payload: { status: ItemLoginSchemaStatus.Freeze }, }); - expect(res.json()).toMatchObject(new MemberCannotAdminItem(expect.anything())); + expect(res.json().message).toEqual(new MemberCannotAdminItem(item.id).message); }); it('Cannot put item login schema if is inherited', async () => { const { actor, - items: [_parentItem, child], + items: [parentItem, child], } = await seedFromJson({ items: [ { @@ -1004,7 +1004,7 @@ describe('Item Login Tests', () => { payload: { status: ItemLoginSchemaStatus.Freeze }, }); - expect(res.json()).toMatchObject(new CannotNestItemLoginSchema(expect.anything())); + expect(res.json().message).toEqual(new CannotNestItemLoginSchema(parentItem.path).message); }); it('Throws if id is invalid', async () => { @@ -1127,7 +1127,7 @@ describe('Item Login Tests', () => { url: `${ITEMS_ROUTE_PREFIX}/${item.id}/login-schema`, }); - expect(res.json()).toMatchObject(new MemberCannotAdminItem(expect.anything())); + expect(res.json().message).toEqual(new MemberCannotAdminItem(item.id).message); }); }); }); diff --git a/src/services/itemMembership/membership.controller.test.ts b/src/services/itemMembership/membership.controller.test.ts index 3022f0ef4..ef81c8d6c 100644 --- a/src/services/itemMembership/membership.controller.test.ts +++ b/src/services/itemMembership/membership.controller.test.ts @@ -829,7 +829,7 @@ describe('Membership routes tests', () => { url: `/api/items/${item.id}/memberships/${membership.id}`, }); expect(response.statusCode).toEqual(StatusCodes.FORBIDDEN); - expect(response.json()).toMatchObject(new CannotDeleteOnlyAdmin({ id: expect.anything() })); + expect(response.json().message).toEqual(new CannotDeleteOnlyAdmin({ id: item.id }).message); expect(await getMembershipById(membership.id)).toBeDefined(); }); }); diff --git a/src/services/itemMembership/plugins/MembershipRequest/error.ts b/src/services/itemMembership/plugins/MembershipRequest/error.ts index 3e5257754..9ff42d4b7 100644 --- a/src/services/itemMembership/plugins/MembershipRequest/error.ts +++ b/src/services/itemMembership/plugins/MembershipRequest/error.ts @@ -1,45 +1,22 @@ import { StatusCodes } from 'http-status-codes'; -import { ErrorFactory, FAILURE_MESSAGES } from '@graasp/sdk'; +import { createError } from '@fastify/error'; +import { FAILURE_MESSAGES } from '@graasp/sdk'; -const PLUGIN_NAME = 'graasp-plugin-member'; +export const ItemMembershipAlreadyExists = createError( + 'GMRERR001', + FAILURE_MESSAGES.MEMBERSHIP_ALREADY_EXISTS, + StatusCodes.BAD_REQUEST, +); -export const GraaspMembershipRequestError = ErrorFactory(PLUGIN_NAME); -export class ItemMembershipAlreadyExists extends GraaspMembershipRequestError { - constructor(data?: unknown) { - super( - { - code: 'GMRERR001', - statusCode: StatusCodes.BAD_REQUEST, - message: FAILURE_MESSAGES.MEMBERSHIP_ALREADY_EXISTS, - }, - data, - ); - } -} +export const MembershipRequestAlreadyExists = createError( + 'GMRERR002', + FAILURE_MESSAGES.MEMBERSHIP_REQUEST_ALREADY_EXISTS, + StatusCodes.BAD_REQUEST, +); -export class MembershipRequestAlreadyExists extends GraaspMembershipRequestError { - constructor(data?: unknown) { - super( - { - code: 'GMRERR002', - statusCode: StatusCodes.BAD_REQUEST, - message: FAILURE_MESSAGES.MEMBERSHIP_REQUEST_ALREADY_EXISTS, - }, - data, - ); - } -} - -export class MembershipRequestNotFound extends GraaspMembershipRequestError { - constructor(data?: unknown) { - super( - { - code: 'GMRERR003', - statusCode: StatusCodes.NOT_FOUND, - message: FAILURE_MESSAGES.MEMBERSHIP_REQUEST_NOT_FOUND, - }, - data, - ); - } -} +export const MembershipRequestNotFound = createError( + 'GMRERR003', + FAILURE_MESSAGES.MEMBERSHIP_REQUEST_NOT_FOUND, + StatusCodes.NOT_FOUND, +); diff --git a/src/services/member/error.ts b/src/services/member/error.ts index 34acfa95c..a877eabf8 100644 --- a/src/services/member/error.ts +++ b/src/services/member/error.ts @@ -1,45 +1,22 @@ import { StatusCodes } from 'http-status-codes'; -import { ErrorFactory, FAILURE_MESSAGES } from '@graasp/sdk'; +import { createError } from '@fastify/error'; +import { FAILURE_MESSAGES } from '@graasp/sdk'; -const PLUGIN_NAME = 'graasp-plugin-member'; +export const EmailAlreadyTaken = createError( + 'GMERR001', + FAILURE_MESSAGES.EMAIL_ALREADY_TAKEN, + StatusCodes.CONFLICT, +); -export const GraaspMemberError = ErrorFactory(PLUGIN_NAME); -export class EmailAlreadyTaken extends GraaspMemberError { - constructor(data?: unknown) { - super( - { - code: 'GMERR001', - statusCode: StatusCodes.CONFLICT, - message: FAILURE_MESSAGES.EMAIL_ALREADY_TAKEN, - }, - data, - ); - } -} +export const NotValidatedMember = createError( + 'GMERR002', + FAILURE_MESSAGES.NOT_VALIDATED_MEMBER, + StatusCodes.FORBIDDEN, +); -export class NotValidatedMember extends GraaspMemberError { - constructor(data?: unknown) { - super( - { - code: 'GMERR002', - statusCode: StatusCodes.FORBIDDEN, - message: FAILURE_MESSAGES.NOT_VALIDATED_MEMBER, - }, - data, - ); - } -} - -export class NotMember extends GraaspMemberError { - constructor(data?: unknown) { - super( - { - code: 'GMERR003', - statusCode: StatusCodes.FORBIDDEN, - message: FAILURE_MESSAGES.NOT_A_MEMBER, - }, - data, - ); - } -} +export const NotMember = createError( + 'GMERR003', + FAILURE_MESSAGES.NOT_A_MEMBER, + StatusCodes.FORBIDDEN, +); diff --git a/src/services/member/plugins/profile/errors.ts b/src/services/member/plugins/profile/errors.ts index b3f2af075..7934b3051 100644 --- a/src/services/member/plugins/profile/errors.ts +++ b/src/services/member/plugins/profile/errors.ts @@ -1,35 +1,22 @@ import { StatusCodes } from 'http-status-codes'; -import { ErrorFactory, FAILURE_MESSAGES } from '@graasp/sdk'; +import { createError } from '@fastify/error'; +import { FAILURE_MESSAGES } from '@graasp/sdk'; -export const GraaspMemberProfileError = ErrorFactory('graasp-member-profile'); +export const MemberProfileNotFound = createError( + 'GMPERR001', + FAILURE_MESSAGES.MEMBER_PROFILE_NOT_FOUND, + StatusCodes.NOT_FOUND, +); -export class MemberProfileNotFound extends GraaspMemberProfileError { - constructor() { - super({ - code: 'GMPERR001', - statusCode: StatusCodes.NOT_FOUND, - message: FAILURE_MESSAGES.MEMBER_PROFILE_NOT_FOUND, - }); - } -} +export const MemberProfileCreationError = createError( + 'GMPERR002', + 'Could not create member profile. This is an internal server error', + StatusCodes.INTERNAL_SERVER_ERROR, +); -export class MemberProfileCreationError extends GraaspMemberProfileError { - constructor() { - super({ - code: 'GMPERR002', - statusCode: StatusCodes.INTERNAL_SERVER_ERROR, - message: 'Could not create member profile. This is an internal server error', - }); - } -} - -export class MemberProfilePropertiesEmpty extends GraaspMemberProfileError { - constructor() { - super({ - code: 'GMPERR003', - statusCode: StatusCodes.BAD_REQUEST, - message: 'All properties of the member profile are empty', - }); - } -} +export const MemberProfilePropertiesEmpty = createError( + 'GMPERR003', + 'All properties of the member profile are empty', + StatusCodes.BAD_REQUEST, +); diff --git a/src/services/member/plugins/thumbnail/utils/errors.ts b/src/services/member/plugins/thumbnail/utils/errors.ts index dddd7ab0f..59332707c 100644 --- a/src/services/member/plugins/thumbnail/utils/errors.ts +++ b/src/services/member/plugins/thumbnail/utils/errors.ts @@ -1,20 +1,10 @@ import { StatusCodes } from 'http-status-codes'; -import { ErrorFactory, FAILURE_MESSAGES } from '@graasp/sdk'; - -import { PLUGIN_NAME } from '../../../../thumbnail/constants'; - -const ThumbnailError = ErrorFactory(PLUGIN_NAME); - -export class UploadFileNotImageError extends ThumbnailError { - constructor(data?: unknown) { - super( - { - code: 'GPTERR001', - statusCode: StatusCodes.BAD_REQUEST, - message: FAILURE_MESSAGES.FILE_IS_NOT_IMAGE, - }, - data, - ); - } -} +import { createError } from '@fastify/error'; +import { FAILURE_MESSAGES } from '@graasp/sdk'; + +export const UploadFileNotImageError = createError( + 'GPTERR001', + FAILURE_MESSAGES.FILE_IS_NOT_IMAGE, + StatusCodes.BAD_REQUEST, +); diff --git a/src/utils/errors.ts b/src/utils/errors.ts index 14b0a8857..351b78615 100644 --- a/src/utils/errors.ts +++ b/src/utils/errors.ts @@ -2,7 +2,7 @@ import { StatusCodes } from 'http-status-codes'; import { createError } from '@fastify/error'; -import { ErrorFactory, FAILURE_MESSAGES } from '@graasp/sdk'; +import { FAILURE_MESSAGES } from '@graasp/sdk'; export function buildError(err: unknown) { if (err instanceof Error) { @@ -11,485 +11,252 @@ export function buildError(err: unknown) { return new Error(String(err)); } -export const ConfigurationError = ErrorFactory('config'); - -export const CoreError = ErrorFactory('core'); - export const ItemNotFound = createError( 'GERR001', FAILURE_MESSAGES.ITEM_NOT_FOUND, StatusCodes.NOT_FOUND, ); -export class MemberCannotReadItem extends CoreError { - constructor(itemId: string) { - super( - { - code: 'GERR002', - statusCode: StatusCodes.FORBIDDEN, - message: FAILURE_MESSAGES.USER_CANNOT_READ_ITEM, - }, - itemId, - ); - } -} -export class MemberCannotWriteItem extends CoreError { - constructor(itemId: string) { - super( - { - code: 'GERR003', - statusCode: StatusCodes.FORBIDDEN, - message: FAILURE_MESSAGES.USER_CANNOT_WRITE_ITEM, - }, - itemId, - ); - } -} -export class MemberCannotAdminItem extends CoreError { - constructor(itemId: string) { - super( - { - code: 'GERR004', - statusCode: StatusCodes.FORBIDDEN, - message: FAILURE_MESSAGES.USER_CANNOT_ADMIN_ITEM, - }, - itemId, - ); - } -} -export class InvalidMembership extends CoreError { - constructor(data?: { itemId: string; accountId: string; permission: string }) { - super( - { - code: 'GERR005', - statusCode: StatusCodes.BAD_REQUEST, - message: FAILURE_MESSAGES.INVALID_MEMBERSHIP, - }, - data, - ); - } -} -export class ItemMembershipNotFound extends CoreError { - constructor(data?: { id?: string; path?: string }) { - super( - { - code: 'GERR006', - statusCode: StatusCodes.NOT_FOUND, - message: FAILURE_MESSAGES.ITEM_MEMBERSHIP_NOT_FOUND, - }, - data, - ); - } -} -export class ModifyExistingMembership extends CoreError { - constructor(data?: { id: string }) { - super( - { - code: 'GERR007', - statusCode: StatusCodes.BAD_REQUEST, - message: FAILURE_MESSAGES.MODIFY_EXISTING, - }, - data, - ); - } -} -export class InvalidPermissionLevel extends CoreError { - constructor(data?: string) { - super( - { - code: 'GERR008', - statusCode: StatusCodes.BAD_REQUEST, - message: FAILURE_MESSAGES.INVALID_PERMISSION_LEVEL, - }, - data, - ); - } -} -export class HierarchyTooDeep extends CoreError { - constructor() { - super({ - code: 'GERR009', - statusCode: StatusCodes.FORBIDDEN, - message: FAILURE_MESSAGES.HIERARCHY_TOO_DEEP, - }); - } -} -export class TooManyChildren extends CoreError { - constructor() { - super({ - code: 'GERR010', - statusCode: StatusCodes.FORBIDDEN, - message: FAILURE_MESSAGES.TOO_MANY_CHILDREN, - }); - } -} -export class TooManyDescendants extends CoreError { - constructor(data: number) { - super( - { - code: 'GERR011', - statusCode: StatusCodes.FORBIDDEN, - message: FAILURE_MESSAGES.TOO_MANY_DESCENDANTS, - }, - data, - ); - } -} -export class InvalidMoveTarget extends CoreError { - constructor(data?: string) { - super( - { - code: 'GERR012', - statusCode: StatusCodes.BAD_REQUEST, - message: FAILURE_MESSAGES.INVALID_MOVE_TARGET, - }, - data, - ); - } -} +export const MemberCannotReadItem = createError( + 'GERR002', + FAILURE_MESSAGES.USER_CANNOT_READ_ITEM, + StatusCodes.FORBIDDEN, +); -export const MemberNotFound = createError('GERR013', 'MEMBER_NOT_FOUND', StatusCodes.NOT_FOUND); +export const MemberCannotWriteItem = createError( + 'GERR003', + FAILURE_MESSAGES.USER_CANNOT_WRITE_ITEM, + StatusCodes.FORBIDDEN, +); -export class CannotModifyOtherMembers extends CoreError { - constructor(member: { id: string }) { - super( - { - code: 'GERR014', - statusCode: StatusCodes.FORBIDDEN, - message: FAILURE_MESSAGES.CANNOT_MODIFY_OTHER_MEMBERS, - }, - member.id, - ); - } -} +export const MemberCannotAdminItem = createError( + 'GERR004', + FAILURE_MESSAGES.USER_CANNOT_ADMIN_ITEM, + StatusCodes.FORBIDDEN, +); -export class MemberCannotAccess extends CoreError { - constructor(itemId: string) { - super( - { - code: 'GERR016', - statusCode: StatusCodes.FORBIDDEN, - message: FAILURE_MESSAGES.MEMBER_CANNOT_ACCESS, - }, - itemId, - ); - } -} +export const InvalidMembership = createError( + 'GERR005', + FAILURE_MESSAGES.INVALID_MEMBERSHIP, + StatusCodes.BAD_REQUEST, +); -export class MemberAlreadySignedUp extends CoreError { - constructor(data: { email: string }) { - super( - { - code: 'GERR017', - statusCode: StatusCodes.CONFLICT, - message: FAILURE_MESSAGES.MEMBER_ALREADY_SIGNED_UP, - }, - data, - ); - } -} +export const ItemMembershipNotFound = createError( + 'GERR006', + FAILURE_MESSAGES.ITEM_MEMBERSHIP_NOT_FOUND, + StatusCodes.NOT_FOUND, +); -export class MemberNotSignedUp extends CoreError { - constructor(data: { email: string }) { - super( - { - code: 'GERR018', - statusCode: StatusCodes.NOT_FOUND, - message: FAILURE_MESSAGES.MEMBER_NOT_SIGNED_UP, - }, - data, - ); - } -} +export const ModifyExistingMembership = createError( + 'GERR007', + FAILURE_MESSAGES.MODIFY_EXISTING, + StatusCodes.BAD_REQUEST, +); -export class MemberWithoutPassword extends CoreError { - constructor() { - super({ - code: 'GERR019', - statusCode: StatusCodes.NOT_ACCEPTABLE, - message: FAILURE_MESSAGES.MEMBER_WITHOUT_PASSWORD, - }); - } -} +export const InvalidPermissionLevel = createError( + 'GERR008', + FAILURE_MESSAGES.INVALID_PERMISSION_LEVEL, + StatusCodes.BAD_REQUEST, +); -export class ChallengeFailed extends CoreError { - constructor(data?: unknown) { - // this status code is custom for the browser to know it needs to refresh its token - super( - { code: 'GERR021', statusCode: StatusCodes.UNAUTHORIZED, message: 'challenge fail' }, - data, - ); - } -} +export const HierarchyTooDeep = createError( + 'GERR009', + FAILURE_MESSAGES.HIERARCHY_TOO_DEEP, + StatusCodes.FORBIDDEN, +); -export class InvalidPassword extends CoreError { - constructor() { - // this status code is custom for the browser to know it needs to refresh its token - super({ - code: 'GERR025', - statusCode: StatusCodes.UNAUTHORIZED, - message: FAILURE_MESSAGES.INVALID_PASSWORD, - }); - } -} +export const TooManyChildren = createError( + 'GERR010', + FAILURE_MESSAGES.TOO_MANY_CHILDREN, + StatusCodes.FORBIDDEN, +); -export class EmptyCurrentPassword extends CoreError { - constructor() { - // this status code is custom for the browser to know it needs to refresh its token - super({ - code: 'GERR026', - statusCode: StatusCodes.BAD_REQUEST, - message: FAILURE_MESSAGES.EMPTY_CURRENT_PASSWORD, - }); - } -} +export const TooManyDescendants = createError( + 'GERR011', + FAILURE_MESSAGES.TOO_MANY_DESCENDANTS, + StatusCodes.FORBIDDEN, +); -export class UnauthorizedMember extends CoreError { - constructor() { - // this status code is custom for the browser to know it needs to refresh its token - super({ - code: 'GERR027', - statusCode: StatusCodes.UNAUTHORIZED, - message: 'Unauthorized member', - }); - } -} +export const InvalidMoveTarget = createError( + 'GERR012', + FAILURE_MESSAGES.INVALID_MOVE_TARGET, + StatusCodes.BAD_REQUEST, +); -export class AuthenticationError extends CoreError { - constructor() { - super({ - code: 'GERR028', - statusCode: StatusCodes.UNAUTHORIZED, - message: 'The authentication failed', - }); - } -} +export const MemberNotFound = createError('GERR013', 'MEMBER_NOT_FOUND', StatusCodes.NOT_FOUND); -export class ItemNotFolder extends CoreError { - constructor(parent: { id: string }) { - super( - { - code: 'GERR029', - statusCode: StatusCodes.BAD_REQUEST, - message: 'Item is not a folder', - }, - parent.id, - ); - } -} +export const CannotModifyOtherMembers = createError( + 'GERR014', + FAILURE_MESSAGES.CANNOT_MODIFY_OTHER_MEMBERS, + StatusCodes.FORBIDDEN, +); -export class CannotDeleteOnlyAdmin extends CoreError { - constructor(item: { id: string }) { - super( - { - code: 'GERR030', - statusCode: StatusCodes.FORBIDDEN, - message: 'Cannot delete the only admin on item', - }, - item.id, - ); - } -} +export const MemberCannotAccess = createError( + 'GERR016', + FAILURE_MESSAGES.MEMBER_CANNOT_ACCESS, + StatusCodes.FORBIDDEN, +); -export class MissingNameOrTypeForItemError extends CoreError { - constructor(data?: unknown) { - super( - { - code: 'GERR031', - statusCode: StatusCodes.BAD_REQUEST, - message: 'Name and type should be defined', - }, - data, - ); - } -} +export const MemberAlreadySignedUp = createError( + 'GERR017', + FAILURE_MESSAGES.MEMBER_ALREADY_SIGNED_UP, + StatusCodes.CONFLICT, +); -export class NoFileProvided extends CoreError { - constructor(data?: unknown) { - super( - { - code: 'GERR033', - statusCode: StatusCodes.BAD_REQUEST, - message: 'Expected a file to be present in the request, found none', - }, - data, - ); - } -} +export const MemberNotSignedUp = createError( + 'GERR018', + FAILURE_MESSAGES.MEMBER_NOT_SIGNED_UP, + StatusCodes.NOT_FOUND, +); -export class CannotReorderRootItem extends CoreError { - constructor(itemId: string) { - super( - { - code: 'GERR034', - statusCode: StatusCodes.BAD_REQUEST, - message: 'Cannot reorder items at root', - }, - itemId, - ); - } -} +export const MemberWithoutPassword = createError( + 'GERR019', + FAILURE_MESSAGES.MEMBER_WITHOUT_PASSWORD, + StatusCodes.NOT_ACCEPTABLE, +); -export class CannotModifyGuestItemMembership extends CoreError { - constructor(data?: unknown) { - super( - { - code: 'GERR035', - statusCode: StatusCodes.BAD_REQUEST, - message: FAILURE_MESSAGES.CANNOT_MODIFY_GUEST_ITEM_MEMBERSHIP, - }, - data, - ); - } -} +export const ChallengeFailed = createError('GERR021', 'challenge fail', StatusCodes.UNAUTHORIZED); -export class NothingToUpdateItem extends CoreError { - constructor() { - super({ - code: 'GERR036', - statusCode: StatusCodes.BAD_REQUEST, - message: FAILURE_MESSAGES.NOTHING_TO_UPDATE_ITEM, - }); - } -} +export const InvalidPassword = createError( + 'GERR025', + FAILURE_MESSAGES.INVALID_PASSWORD, + StatusCodes.UNAUTHORIZED, +); -export class BadCredentials extends CoreError { - constructor() { - super({ - code: 'GERR037', - statusCode: StatusCodes.UNAUTHORIZED, - message: FAILURE_MESSAGES.BAD_CREDENTIALS, - }); - } -} +export const EmptyCurrentPassword = createError( + 'GERR026', + FAILURE_MESSAGES.EMPTY_CURRENT_PASSWORD, + StatusCodes.BAD_REQUEST, +); -export class UnexpectedError extends CoreError { - constructor(data?: unknown) { - super( - { - code: 'GERR999', - statusCode: StatusCodes.INTERNAL_SERVER_ERROR, - message: FAILURE_MESSAGES.UNEXPECTED_ERROR, - }, - data, - ); - this.origin = 'unknown'; - } -} +export const UnauthorizedMember = createError( + 'GERR027', + 'Unauthorized member', + StatusCodes.UNAUTHORIZED, +); -interface OpenAIParamsError { - message?: string; - code?: string; - statusCode?: number; -} +export const AuthenticationError = createError( + 'GERR028', + 'The authentication failed', + StatusCodes.UNAUTHORIZED, +); -export class OpenAIBaseError extends CoreError { - constructor({ - message = 'An unknown error occured', - code = 'GERR1000', - statusCode = StatusCodes.INTERNAL_SERVER_ERROR, - }: OpenAIParamsError = {}) { - super({ code: code, statusCode: statusCode, message: message }); - this.origin = 'OpenAI'; - } -} +export const ItemNotFolder = createError( + 'GERR029', + 'Item is not a folder', + StatusCodes.BAD_REQUEST, +); -export class OpenAIUnknownStopError extends OpenAIBaseError { - constructor(stopReason: string) { - const message = `The stop reason "${stopReason}" is not a known OpenAI reason`; - super({ code: 'GERR1001', message: message }); - } -} +export const CannotDeleteOnlyAdmin = createError( + 'GERR030', + 'Cannot delete the only admin on item', + StatusCodes.FORBIDDEN, +); -export class OpenAILengthError extends OpenAIBaseError { - constructor() { - const message = 'Incomplete model output due to token limitation'; - super({ code: 'GERR1002', message: message }); - } -} +export const MissingNameOrTypeForItemError = createError( + 'GERR031', + 'Name and type should be defined', + StatusCodes.BAD_REQUEST, +); -export class OpenAITimeOutError extends OpenAIBaseError { - constructor() { - const message = 'The response takes too long to respond'; - super({ code: 'GERR1003', message: message }); - } -} +export const NoFileProvided = createError( + 'GERR033', + 'Expected a file to be present in the request, found none', + StatusCodes.BAD_REQUEST, +); -export class OpenAIQuotaError extends OpenAIBaseError { - constructor() { - const message = 'This token exceeded current quota, please check plan and billing details.'; - super({ code: 'GERR1004', message: message, statusCode: StatusCodes.TOO_MANY_REQUESTS }); - } -} +export const CannotReorderRootItem = createError( + 'GERR034', + 'Cannot reorder items at root', + StatusCodes.BAD_REQUEST, +); -export class OpenAIBadVersion extends OpenAIBaseError { - constructor(gptVersion: string, validVersions: string) { - const message = `The gpt-version '${gptVersion}' is not a valid version. Try one of these instead: "${validVersions}".`; - super({ code: 'GERR1005', message: message, statusCode: StatusCodes.BAD_REQUEST }); - } -} +export const CannotModifyGuestItemMembership = createError( + 'GERR035', + FAILURE_MESSAGES.CANNOT_MODIFY_GUEST_ITEM_MEMBERSHIP, + StatusCodes.BAD_REQUEST, +); -export class InvalidJWTItem extends CoreError { - constructor(jwtItemId: string, itemId: string) { - const message = `The JWT item id '${jwtItemId}' does not correspond with the accessed item ${itemId}.`; - super({ code: 'GERR1007', statusCode: StatusCodes.FORBIDDEN, message: message }); - } -} +export const NothingToUpdateItem = createError( + 'GERR036', + FAILURE_MESSAGES.NOTHING_TO_UPDATE_ITEM, + StatusCodes.BAD_REQUEST, +); -export class ShortLinkNotFound extends CoreError { - constructor(shortLink: string) { - super( - { - code: 'GERR1009', - statusCode: StatusCodes.NOT_FOUND, - message: FAILURE_MESSAGES.SHORT_LINK_NOT_FOUND, - }, - shortLink, - ); - this.origin = 'shortLink'; - } -} +export const BadCredentials = createError( + 'GERR037', + FAILURE_MESSAGES.BAD_CREDENTIALS, + StatusCodes.UNAUTHORIZED, +); -export class ShortLinkDuplication extends CoreError { - constructor(shortLink: string) { - super( - { - code: 'GERR1010', - statusCode: StatusCodes.CONFLICT, - message: FAILURE_MESSAGES.SHORT_LINK_ALREADY_EXISTS, - }, - shortLink, - ); - this.origin = 'shortLink'; - } -} +export const UnexpectedError = createError( + 'GERR999', + FAILURE_MESSAGES.UNEXPECTED_ERROR, + StatusCodes.INTERNAL_SERVER_ERROR, +); -export class ShortLinkLimitExceed extends CoreError { - constructor(itemId: string, platform: string) { - super( - { - code: 'GERR1011', - statusCode: StatusCodes.CONFLICT, - message: FAILURE_MESSAGES.SHORT_LINK_LIMIT_EXCEED, - }, - { - itemId, - platform, - }, - ); - this.origin = 'shortLink'; - } -} +export const OpenAIBaseError = createError( + 'GERR1000', + 'An unknown error occured', + StatusCodes.INTERNAL_SERVER_ERROR, +); -export class InsufficientPermission extends CoreError { - constructor(data?: unknown) { - super( - { - code: 'GERR1012', - statusCode: StatusCodes.FORBIDDEN, - message: FAILURE_MESSAGES.INSUFFICIENT_PERMISSION, - }, - data, - ); - } -} +export const OpenAIUnknownStopError = createError( + 'GERR1001', + 'The stop reason is not a known OpenAI reason', + StatusCodes.INTERNAL_SERVER_ERROR, +); + +export const OpenAILengthError = createError( + 'GERR1002', + 'Incomplete model output due to token limitation', + StatusCodes.INTERNAL_SERVER_ERROR, +); + +export const OpenAITimeOutError = createError( + 'GERR1003', + 'The response takes too long to respond', + StatusCodes.INTERNAL_SERVER_ERROR, +); + +export const OpenAIQuotaError = createError( + 'GERR1004', + 'This token exceeded current quota, please check plan and billing details.', + StatusCodes.TOO_MANY_REQUESTS, +); + +export const OpenAIBadVersion = createError( + 'GERR1005', + 'The gpt-version is not a valid version', + StatusCodes.BAD_REQUEST, +); + +export const InvalidJWTItem = createError( + 'GERR1007', + 'The JWT item id does not correspond with the accessed item', + StatusCodes.FORBIDDEN, +); + +export const ShortLinkNotFound = createError( + 'GERR1009', + FAILURE_MESSAGES.SHORT_LINK_NOT_FOUND, + StatusCodes.NOT_FOUND, +); + +export const ShortLinkDuplication = createError( + 'GERR1010', + FAILURE_MESSAGES.SHORT_LINK_ALREADY_EXISTS, + StatusCodes.CONFLICT, +); + +export const ShortLinkLimitExceed = createError( + 'GERR1011', + FAILURE_MESSAGES.SHORT_LINK_LIMIT_EXCEED, + StatusCodes.CONFLICT, +); + +export const InsufficientPermission = createError( + 'GERR1012', + FAILURE_MESSAGES.INSUFFICIENT_PERMISSION, + StatusCodes.FORBIDDEN, +);