From 6ac267a2263c9d43176af01084aa952954ad8b23 Mon Sep 17 00:00:00 2001 From: Philip Molares Date: Thu, 1 Apr 2021 01:22:34 +0200 Subject: [PATCH] PrivateApi: Add option to keep media to DELETE /notes/{note} This adds a body to the route DELETE /notes/{note} of the private api to specify if the associated media uploads of the note should be kept or deleted. Signed-off-by: Philip Molares --- src/api/private/notes/notes.controller.ts | 11 +++++ test/private-api/notes.e2e-spec.ts | 51 ++++++++++++++++++++--- 2 files changed, 56 insertions(+), 6 deletions(-) diff --git a/src/api/private/notes/notes.controller.ts b/src/api/private/notes/notes.controller.ts index 752a150bc..bd9f1ab35 100644 --- a/src/api/private/notes/notes.controller.ts +++ b/src/api/private/notes/notes.controller.ts @@ -6,6 +6,7 @@ import { BadRequestException, + Body, Controller, Delete, Get, @@ -33,6 +34,7 @@ import { RevisionMetadataDto } from '../../../revisions/revision-metadata.dto'; import { RevisionDto } from '../../../revisions/revision.dto'; import { RevisionsService } from '../../../revisions/revisions.service'; import { MarkdownBody } from '../../utils/markdownbody-decorator'; +import { NoteMediaDeletionDto } from '../../../notes/note.media-deletion.dto'; @Controller('notes') export class NotesController { @@ -140,6 +142,7 @@ export class NotesController { @HttpCode(204) async deleteNote( @Param('noteIdOrAlias') noteIdOrAlias: string, + @Body() noteMediaDeletionDto: NoteMediaDeletionDto, ): Promise { try { // ToDo: use actual user here @@ -148,6 +151,14 @@ export class NotesController { if (!this.permissionsService.isOwner(user, note)) { throw new UnauthorizedException('Deleting note denied!'); } + const mediaUploads = await this.mediaService.listUploadsByNote(note); + for (const mediaUpload of mediaUploads) { + if (!noteMediaDeletionDto.keepMedia) { + await this.mediaService.deleteFile(mediaUpload); + } else { + await this.mediaService.removeNoteFromMediaUpload(mediaUpload); + } + } this.logger.debug('Deleting note: ' + noteIdOrAlias, 'deleteNote'); await this.noteService.deleteNote(note); this.logger.debug('Successfully deleted ' + noteIdOrAlias, 'deleteNote'); diff --git a/test/private-api/notes.e2e-spec.ts b/test/private-api/notes.e2e-spec.ts index e8fc2fa3c..1c29682a2 100644 --- a/test/private-api/notes.e2e-spec.ts +++ b/test/private-api/notes.e2e-spec.ts @@ -38,6 +38,7 @@ describe('Notes', () => { let content: string; let forbiddenNoteId: string; let uploadPath: string; + let testImage: Buffer; beforeAll(async () => { const moduleRef = await Test.createTestingModule({ @@ -80,6 +81,7 @@ describe('Notes', () => { user = await userService.createUser('hardcoded', 'Testy'); user2 = await userService.createUser('hardcoded2', 'Max Mustermann'); content = 'This is a test note.'; + testImage = await fs.readFile('test/public-api/fixtures/test.png'); }); it('POST /notes', async () => { @@ -152,12 +154,49 @@ describe('Notes', () => { }); describe('DELETE /notes/{note}', () => { - it('works with an existing alias', async () => { - await notesService.createNote(content, 'test3', user); - await request(app.getHttpServer()).delete('/notes/test3').expect(204); - await expect(notesService.getNoteByIdOrAlias('test3')).rejects.toEqual( - new NotInDBError("Note with id/alias 'test3' not found."), - ); + describe('works', () => { + it('with an existing alias and keepMedia false', async () => { + const noteId = 'test3'; + await notesService.createNote(content, noteId, user); + await mediaService.saveFile(testImage, user.userName, noteId); + await request(app.getHttpServer()) + .delete(`/notes/${noteId}`) + .set('Content-Type', 'application/json') + .send({ + keepMedia: false, + }) + .expect(204); + await expect(notesService.getNoteByIdOrAlias(noteId)).rejects.toEqual( + new NotInDBError(`Note with id/alias '${noteId}' not found.`), + ); + expect(await mediaService.listUploadsByUser(user)).toHaveLength(0); + await fs.rmdir(uploadPath); + }); + it('with an existing alias and keepMedia true', async () => { + const noteId = 'test3a'; + await notesService.createNote(content, noteId, user); + const url = await mediaService.saveFile( + testImage, + user.userName, + noteId, + ); + await request(app.getHttpServer()) + .delete(`/notes/${noteId}`) + .set('Content-Type', 'application/json') + .send({ + keepMedia: true, + }) + .expect(204); + await expect(notesService.getNoteByIdOrAlias(noteId)).rejects.toEqual( + new NotInDBError(`Note with id/alias '${noteId}' not found.`), + ); + expect(await mediaService.listUploadsByUser(user)).toHaveLength(1); + // Remove /upload/ from path as we just need the filename. + const fileName = url.replace('/uploads/', ''); + // delete the file afterwards + await fs.unlink(join(uploadPath, fileName)); + await fs.rmdir(uploadPath); + }); }); it('fails with a forbidden alias', async () => { await request(app.getHttpServer())