From ab5aebc9c4c88bbd4a19caef47fbfa9273b67849 Mon Sep 17 00:00:00 2001 From: Tilman Vatteroth Date: Fri, 19 May 2023 13:32:56 +0200 Subject: [PATCH] refactor: extract "extract note from request" logic into separate function Signed-off-by: Tilman Vatteroth --- .../utils/extract-note-from-request.spec.ts | 87 +++++++++++++++++++ .../api/utils/extract-note-from-request.ts | 33 +++++++ backend/src/api/utils/get-note.interceptor.ts | 15 ++-- 3 files changed, 125 insertions(+), 10 deletions(-) create mode 100644 backend/src/api/utils/extract-note-from-request.spec.ts create mode 100644 backend/src/api/utils/extract-note-from-request.ts diff --git a/backend/src/api/utils/extract-note-from-request.spec.ts b/backend/src/api/utils/extract-note-from-request.spec.ts new file mode 100644 index 000000000..e9c16613b --- /dev/null +++ b/backend/src/api/utils/extract-note-from-request.spec.ts @@ -0,0 +1,87 @@ +/* + * SPDX-FileCopyrightText: 2023 The HedgeDoc developers (see AUTHORS file) + * + * SPDX-License-Identifier: AGPL-3.0-only + */ +import { Mock } from 'ts-mockery'; + +import { Note } from '../../notes/note.entity'; +import { NotesService } from '../../notes/notes.service'; +import { extractNoteFromRequest } from './extract-note-from-request'; +import { CompleteRequest } from './request.type'; + +describe('extract note from request', () => { + const mockNoteIdOrAlias1 = 'mockNoteIdOrAlias1'; + const mockNoteIdOrAlias2 = 'mockNoteIdOrAlias2'; + + const mockNote1 = Mock.of({ id: 1 }); + const mockNote2 = Mock.of({ id: 2 }); + + let notesService: NotesService; + + beforeEach(() => { + notesService = Mock.of({ + getNoteByIdOrAlias: async (id) => { + if (id === mockNoteIdOrAlias1) { + return mockNote1; + } else if (id === mockNoteIdOrAlias2) { + return mockNote2; + } else { + throw new Error('unknown note id'); + } + }, + }); + }); + + function createRequest( + parameterValue: string | undefined, + headerValue: string | string[] | undefined, + ): CompleteRequest { + return Mock.of({ + params: parameterValue + ? { + noteIdOrAlias: parameterValue, + } + : {}, + headers: headerValue + ? { + // eslint-disable-next-line @typescript-eslint/naming-convention + 'hedgedoc-note': headerValue, + } + : {}, + }); + } + + it('will return undefined if no id is present', async () => { + const request = createRequest(undefined, undefined); + expect(await extractNoteFromRequest(request, notesService)).toBe(undefined); + }); + + it('can extract an id from parameters', async () => { + const request = createRequest(mockNoteIdOrAlias1, undefined); + expect(await extractNoteFromRequest(request, notesService)).toBe(mockNote1); + }); + + it('can extract an id from headers if no parameter is given', async () => { + const request = createRequest(undefined, mockNoteIdOrAlias1); + expect(await extractNoteFromRequest(request, notesService)).toBe(mockNote1); + }); + + it('can extract the first id from multiple id headers', async () => { + const request = createRequest(undefined, [ + mockNoteIdOrAlias1, + mockNoteIdOrAlias2, + ]); + expect(await extractNoteFromRequest(request, notesService)).toBe(mockNote1); + }); + + it('will return undefined if no parameter and empty id header array', async () => { + const request = createRequest(undefined, []); + expect(await extractNoteFromRequest(request, notesService)).toBe(undefined); + }); + + it('will prefer the parameter over the header', async () => { + const request = createRequest(mockNoteIdOrAlias1, mockNoteIdOrAlias2); + expect(await extractNoteFromRequest(request, notesService)).toBe(mockNote1); + }); +}); diff --git a/backend/src/api/utils/extract-note-from-request.ts b/backend/src/api/utils/extract-note-from-request.ts new file mode 100644 index 000000000..3dc2af311 --- /dev/null +++ b/backend/src/api/utils/extract-note-from-request.ts @@ -0,0 +1,33 @@ +/* + * SPDX-FileCopyrightText: 2023 The HedgeDoc developers (see AUTHORS file) + * + * SPDX-License-Identifier: AGPL-3.0-only + */ +import { isArray } from 'class-validator'; + +import { Note } from '../../notes/note.entity'; +import { NotesService } from '../../notes/notes.service'; +import { CompleteRequest } from './request.type'; + +export async function extractNoteFromRequest( + request: CompleteRequest, + noteService: NotesService, +): Promise { + const noteIdOrAlias = extractNoteIdOrAlias(request); + if (noteIdOrAlias === undefined) { + return undefined; + } + return await noteService.getNoteByIdOrAlias(noteIdOrAlias); +} + +function extractNoteIdOrAlias(request: CompleteRequest): string | undefined { + const noteIdOrAlias = + request.params['noteIdOrAlias'] || request.headers['hedgedoc-note']; + if (noteIdOrAlias === undefined) { + return undefined; + } else if (isArray(noteIdOrAlias)) { + return noteIdOrAlias[0]; + } else { + return noteIdOrAlias; + } +} diff --git a/backend/src/api/utils/get-note.interceptor.ts b/backend/src/api/utils/get-note.interceptor.ts index 776564a8f..389f68875 100644 --- a/backend/src/api/utils/get-note.interceptor.ts +++ b/backend/src/api/utils/get-note.interceptor.ts @@ -11,8 +11,8 @@ import { } from '@nestjs/common'; import { Observable } from 'rxjs'; -import { Note } from '../../notes/note.entity'; import { NotesService } from '../../notes/notes.service'; +import { extractNoteFromRequest } from './extract-note-from-request'; import { CompleteRequest } from './request.type'; /** @@ -28,15 +28,10 @@ export class GetNoteInterceptor implements NestInterceptor { next: CallHandler, ): Promise> { const request: CompleteRequest = context.switchToHttp().getRequest(); - const noteIdOrAlias = request.params['noteIdOrAlias']; - request.note = await getNote(this.noteService, noteIdOrAlias); + const note = await extractNoteFromRequest(request, this.noteService); + if (note !== undefined) { + request.note = note; + } return next.handle(); } } - -export async function getNote( - noteService: NotesService, - noteIdOrAlias: string, -): Promise { - return await noteService.getNoteByIdOrAlias(noteIdOrAlias); -}