diff --git a/src/api/private/me/history/history.controller.ts b/src/api/private/me/history/history.controller.ts index 3274720c5..d74795847 100644 --- a/src/api/private/me/history/history.controller.ts +++ b/src/api/private/me/history/history.controller.ts @@ -65,7 +65,11 @@ export class HistoryController { const note = await this.noteService.getNoteByIdOrAlias( historyEntry.note, ); - await this.historyService.createOrUpdateHistoryEntry(note, user); + await this.historyService.createOrUpdateHistoryEntry( + note, + user, + historyEntry.pinStatus, + ); } } catch (e) { if (e instanceof NotInDBError) { diff --git a/src/history/history-entry-import.dto.ts b/src/history/history-entry-import.dto.ts index 122024f3f..edc8b7c1e 100644 --- a/src/history/history-entry-import.dto.ts +++ b/src/history/history-entry-import.dto.ts @@ -4,7 +4,7 @@ * SPDX-License-Identifier: AGPL-3.0-only */ -import { IsString } from 'class-validator'; +import { IsBoolean, IsString } from 'class-validator'; export class HistoryEntryImportDto { /** @@ -12,4 +12,10 @@ export class HistoryEntryImportDto { */ @IsString() note: string; + /** + * True if the note should be pinned + * @example true + */ + @IsBoolean() + pinStatus: boolean; } diff --git a/src/history/history.service.spec.ts b/src/history/history.service.spec.ts index b41361c86..98472c2aa 100644 --- a/src/history/history.service.spec.ts +++ b/src/history/history.service.spec.ts @@ -141,9 +141,10 @@ describe('HistoryService', () => { describe('createOrUpdateHistoryEntry', () => { describe('works', () => { - it('without an preexisting entry', async () => { - const user = {} as User; - const alias = 'alias'; + const user = {} as User; + const alias = 'alias'; + const pinStatus = true; + it('without an preexisting entry and without pinStatus', async () => { jest.spyOn(historyRepo, 'findOne').mockResolvedValueOnce(undefined); jest .spyOn(historyRepo, 'save') @@ -160,9 +161,25 @@ describe('HistoryService', () => { expect(createHistoryEntry.pinStatus).toEqual(false); }); - it('with an preexisting entry', async () => { - const user = {} as User; - const alias = 'alias'; + it('without an preexisting entry and with pinStatus', async () => { + jest.spyOn(historyRepo, 'findOne').mockResolvedValueOnce(undefined); + jest + .spyOn(historyRepo, 'save') + .mockImplementation( + async (entry: HistoryEntry): Promise => entry, + ); + const createHistoryEntry = await service.createOrUpdateHistoryEntry( + Note.create(user, alias), + user, + pinStatus, + ); + expect(createHistoryEntry.note.alias).toEqual(alias); + expect(createHistoryEntry.note.owner).toEqual(user); + expect(createHistoryEntry.user).toEqual(user); + expect(createHistoryEntry.pinStatus).toEqual(pinStatus); + }); + + it('with an preexisting entry and without pinStatus', async () => { const historyEntry = HistoryEntry.create( user, Note.create(user, alias), @@ -185,6 +202,32 @@ describe('HistoryService', () => { historyEntry.updatedAt.getTime(), ); }); + + it('with an preexisting entry and with pinStatus', async () => { + const historyEntry = HistoryEntry.create( + user, + Note.create(user, alias), + pinStatus, + ); + jest.spyOn(historyRepo, 'findOne').mockResolvedValueOnce(historyEntry); + jest + .spyOn(historyRepo, 'save') + .mockImplementation( + async (entry: HistoryEntry): Promise => entry, + ); + const createHistoryEntry = await service.createOrUpdateHistoryEntry( + Note.create(user, alias), + user, + pinStatus, + ); + expect(createHistoryEntry.note.alias).toEqual(alias); + expect(createHistoryEntry.note.owner).toEqual(user); + expect(createHistoryEntry.user).toEqual(user); + expect(createHistoryEntry.pinStatus).toEqual(pinStatus); + expect(createHistoryEntry.updatedAt.getTime()).toBeGreaterThanOrEqual( + historyEntry.updatedAt.getTime(), + ); + }); }); }); diff --git a/src/history/history.service.ts b/src/history/history.service.ts index d2793ac04..de4ad0264 100644 --- a/src/history/history.service.ts +++ b/src/history/history.service.ts @@ -80,15 +80,20 @@ export class HistoryService { * Create or update a history entry by the user and note. If the entry is merely updated the updatedAt date is set to the current date. * @param {Note} note - the note that the history entry belongs to * @param {User} user - the user that the history entry belongs to + * @param {boolean} pinStatus - if the pinStatus of the history entry should be set * @return {HistoryEntry} the requested history entry */ async createOrUpdateHistoryEntry( note: Note, user: User, + pinStatus?: boolean, ): Promise { let entry = await this.getEntryByNote(note, user); if (!entry) { entry = HistoryEntry.create(user, note); + if (pinStatus !== undefined) { + entry.pinStatus = pinStatus; + } } else { entry.updatedAt = new Date(); } diff --git a/test/private-api/history.e2e-spec.ts b/test/private-api/history.e2e-spec.ts index 0295e9c3d..32286912f 100644 --- a/test/private-api/history.e2e-spec.ts +++ b/test/private-api/history.e2e-spec.ts @@ -100,8 +100,10 @@ describe('History', () => { }); it('POST /me/history', async () => { + const pinStatus = true; const postEntryDto = new HistoryEntryImportDto(); postEntryDto.note = note2.alias; + postEntryDto.pinStatus = pinStatus; await request(app.getHttpServer()) .post('/me/history') .set('Content-Type', 'application/json') @@ -110,6 +112,8 @@ describe('History', () => { const userEntries = await historyService.getEntriesByUser(user); expect(userEntries.length).toEqual(1); expect(userEntries[0].note.alias).toEqual(note2.alias); + expect(userEntries[0].user.userName).toEqual(user.userName); + expect(userEntries[0].pinStatus).toEqual(pinStatus); }); it('DELETE /me/history', async () => {