diff --git a/src/api/private/notes/notes.controller.ts b/src/api/private/notes/notes.controller.ts index 5ec9e45b6..c4d1ec8ab 100644 --- a/src/api/private/notes/notes.controller.ts +++ b/src/api/private/notes/notes.controller.ts @@ -88,7 +88,7 @@ export class NotesController { } @Post() - @OpenApi(201) + @OpenApi(201, 413) @Permissions(Permission.CREATE) async createNote( @RequestUser() user: User, @@ -101,7 +101,7 @@ export class NotesController { } @Post(':noteAlias') - @OpenApi(201, 400, 404, 409) + @OpenApi(201, 400, 404, 409, 413) @Permissions(Permission.CREATE) async createNamedNote( @RequestUser() user: User, diff --git a/src/api/public/notes/notes.controller.ts b/src/api/public/notes/notes.controller.ts index 7f388dd38..705a5f33b 100644 --- a/src/api/public/notes/notes.controller.ts +++ b/src/api/public/notes/notes.controller.ts @@ -69,7 +69,7 @@ export class NotesController { @Permissions(Permission.CREATE) @Post() - @OpenApi(201, 403, 409) + @OpenApi(201, 403, 409, 413) async createNote( @RequestUser() user: User, @MarkdownBody() text: string, @@ -112,6 +112,7 @@ export class NotesController { 400, 403, 409, + 413, ) async createNamedNote( @RequestUser() user: User, diff --git a/src/api/utils/descriptions.ts b/src/api/utils/descriptions.ts index e00b24c23..1ebd95a71 100644 --- a/src/api/utils/descriptions.ts +++ b/src/api/utils/descriptions.ts @@ -22,5 +22,7 @@ export const unprocessableEntityDescription = "The request change can't be processed"; export const conflictDescription = 'The request conflicts with the current state of the application'; +export const payloadTooLargeDescription = + 'The note is longer than the maximal allowed length of a note'; export const internalServerErrorDescription = 'The request triggered an internal server error.'; diff --git a/src/api/utils/openapi.decorator.ts b/src/api/utils/openapi.decorator.ts index a093be816..785a24393 100644 --- a/src/api/utils/openapi.decorator.ts +++ b/src/api/utils/openapi.decorator.ts @@ -25,6 +25,7 @@ import { noContentDescription, notFoundDescription, okDescription, + payloadTooLargeDescription, unauthorizedDescription, } from './descriptions'; @@ -37,6 +38,7 @@ export type HttpStatusCodes = | 403 | 404 | 409 + | 413 | 500; /** @@ -156,6 +158,13 @@ export const OpenApi = ( }), ); break; + case 413: + decoratorsToApply.push( + ApiConflictResponse({ + description: description ?? payloadTooLargeDescription, + }), + ); + break; case 500: decoratorsToApply.push( ApiInternalServerErrorResponse({ diff --git a/src/errors/error-mapping.ts b/src/errors/error-mapping.ts index bb512db26..92de02444 100644 --- a/src/errors/error-mapping.ts +++ b/src/errors/error-mapping.ts @@ -10,6 +10,7 @@ import { ConflictException, InternalServerErrorException, NotFoundException, + PayloadTooLargeException, UnauthorizedException, } from '@nestjs/common'; import { HttpException } from '@nestjs/common/exceptions/http.exception'; @@ -70,6 +71,10 @@ const mapOfHedgeDocErrorsToHttpErrors: Map = 'PasswordTooWeakError', (object): HttpException => new BadRequestException(object), ], + [ + 'MaximumDocumentLengthExceededError', + (object): HttpException => new PayloadTooLargeException(object), + ], ]); @Catch() diff --git a/src/errors/errors.ts b/src/errors/errors.ts index b4aaaddb8..edfe0936f 100644 --- a/src/errors/errors.ts +++ b/src/errors/errors.ts @@ -55,3 +55,7 @@ export class NoLocalIdentityError extends Error { export class PasswordTooWeakError extends Error { name = 'PasswordTooWeakError'; } + +export class MaximumDocumentLengthExceededError extends Error { + name = 'MaximumDocumentLengthExceededError'; +} diff --git a/src/notes/notes.service.ts b/src/notes/notes.service.ts index ed85fb941..60df45400 100644 --- a/src/notes/notes.service.ts +++ b/src/notes/notes.service.ts @@ -14,6 +14,7 @@ import noteConfiguration, { NoteConfig } from '../config/note.config'; import { AlreadyInDBError, ForbiddenIdError, + MaximumDocumentLengthExceededError, NotInDBError, } from '../errors/errors'; import { NoteEvent } from '../events'; @@ -79,11 +80,12 @@ export class NotesService { * @async * Create a new note. * @param {string} noteContent - the content the new note should have - * @param {string=} alias - a optional alias the note should have + * @param {string=} alias - an optional alias the note should have * @param {User=} owner - the owner of the note * @return {Note} the newly created note * @throws {AlreadyInDBError} a note with the requested id or alias already exists * @throws {ForbiddenIdError} the requested id or alias is forbidden + * @throws {MaximumDocumentLengthExceededError} the noteContent is longer than the maxDocumentLength */ async createNote( noteContent: string, @@ -94,6 +96,9 @@ export class NotesService { this.checkNoteIdOrAlias(alias); } const newNote = Note.create(owner, alias); + if (noteContent.length > this.noteConfig.maxDocumentLength) { + throw new MaximumDocumentLengthExceededError(); + } //TODO: Calculate patch newNote.revisions = Promise.resolve([ Revision.create(noteContent, noteContent, newNote as Note) as Revision,