From b9279a5d205b705809863590aa9feb74b7e4927d Mon Sep 17 00:00:00 2001 From: David Mehren Date: Sat, 3 Oct 2020 11:02:03 +0200 Subject: [PATCH 01/13] Add note metadata to db schema Signed-off-by: David Mehren Co-authored-by: Yannick Bungers --- docs/dev/db-schema.plantuml | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/docs/dev/db-schema.plantuml b/docs/dev/db-schema.plantuml index 3ec0fb4a7..d44a56dd7 100644 --- a/docs/dev/db-schema.plantuml +++ b/docs/dev/db-schema.plantuml @@ -12,6 +12,8 @@ entity "Note" { *alias : text *viewcount : number *ownerId : uuid <> + description: text + title: text } entity "User" { @@ -49,7 +51,7 @@ entity "Identity" { passwordHash : text } -entity "Session" as seesion { +entity "Session" { *id : text -- *expiredAt : number @@ -108,13 +110,18 @@ entity "Group" { *special : boolean } - entity "NoteGroupPermission" { +entity "NoteGroupPermission" { *groupId : number <> *noteId : uuid <> -- *canEdit : boolean } +entity "Tag" { + *id: number <> + *name: text +} + entity "MediaUpload" { *id : text <> -- @@ -136,9 +143,10 @@ Note "1" -- "0..*" NoteGroupPermission NoteGroupPermission "0..*" -- "1" Group Identity "1..*" -- "1" User authToken "1..*" -- "1" User -seesion "1..*" -- "1" User +Session "1..*" -- "1" User Note "0..*" -- "0..*" User : color (Note, User) .. AuthorColors +Note "0..*" -- "0..*" Tag : tags MediaUpload "0..*" -- "1" Note MediaUpload "0..*" -- "1" User @enduml From b9dfd880f76325f1fc0020c4490e42652a24abd8 Mon Sep 17 00:00:00 2001 From: David Mehren Date: Sat, 3 Oct 2020 11:14:41 +0200 Subject: [PATCH 02/13] Note.alias should be optional in db schema Signed-off-by: David Mehren Co-authored-by: Yannick Bungers --- docs/dev/db-schema.plantuml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/dev/db-schema.plantuml b/docs/dev/db-schema.plantuml index d44a56dd7..c42827aea 100644 --- a/docs/dev/db-schema.plantuml +++ b/docs/dev/db-schema.plantuml @@ -9,7 +9,7 @@ entity "Note" { *id : uuid <> -- *shortid : text - *alias : text + alias : text *viewcount : number *ownerId : uuid <> description: text From f1f57eca54d57956d3bea050a59ed3205a3ef60b Mon Sep 17 00:00:00 2001 From: David Mehren Date: Sat, 3 Oct 2020 11:17:07 +0200 Subject: [PATCH 03/13] Add note metadata properties and Tag entity. These were planned to be parsed at runtime from the note-content in the database, but having to run a markdown parser in the backend was found to be a bad idea. Now the frontend (that already implements the parsing logic) has to set title, description and tags. Signed-off-by: David Mehren Co-authored-by: Yannick Bungers --- src/notes/note.entity.ts | 27 +++++++++++++++++++++++++-- src/notes/notes.module.ts | 3 ++- src/notes/tag.entity.ts | 19 +++++++++++++++++++ 3 files changed, 46 insertions(+), 3 deletions(-) create mode 100644 src/notes/tag.entity.ts diff --git a/src/notes/note.entity.ts b/src/notes/note.entity.ts index 815e7c643..1f412775b 100644 --- a/src/notes/note.entity.ts +++ b/src/notes/note.entity.ts @@ -2,6 +2,8 @@ import { generate as shortIdGenerate } from 'shortid'; import { Column, Entity, + JoinTable, + ManyToMany, ManyToOne, OneToMany, PrimaryGeneratedColumn, @@ -11,6 +13,7 @@ import { NoteUserPermission } from '../permissions/note-user-permission.entity'; import { Revision } from '../revisions/revision.entity'; import { User } from '../users/user.entity'; import { AuthorColor } from './author-color.entity'; +import { Tag } from './tag.entity'; @Entity('Notes') export class Note { @@ -25,7 +28,7 @@ export class Note { unique: true, nullable: true, }) - alias: string; + alias?: string; @OneToMany( _ => NoteGroupPermission, groupPermission => groupPermission.note, @@ -59,10 +62,26 @@ export class Note { ) authorColors: AuthorColor[]; + @Column({ + nullable: true, + }) + description?: string; + @Column({ + nullable: true, + }) + title?: string; + + @ManyToMany( + _ => Tag, + tag => tag.notes, + ) + @JoinTable() + tags: Tag[]; + // eslint-disable-next-line @typescript-eslint/no-empty-function private constructor() {} - public static create(owner?: User, alias?: string, shortid?: string) { + public static create(owner?: User, alias?: string, shortid?: string): Note { if (!shortid) { shortid = shortIdGenerate(); } @@ -74,6 +93,10 @@ export class Note { newNote.authorColors = []; newNote.userPermissions = []; newNote.groupPermissions = []; + newNote.revisions = Promise.resolve([]); + newNote.description = null; + newNote.title = null; + newNote.tags = []; return newNote; } } diff --git a/src/notes/notes.module.ts b/src/notes/notes.module.ts index f5a93a721..b35c300d3 100644 --- a/src/notes/notes.module.ts +++ b/src/notes/notes.module.ts @@ -6,10 +6,11 @@ import { UsersModule } from '../users/users.module'; import { AuthorColor } from './author-color.entity'; import { Note } from './note.entity'; import { NotesService } from './notes.service'; +import { Tag } from './tag.entity'; @Module({ imports: [ - TypeOrmModule.forFeature([Note, AuthorColor]), + TypeOrmModule.forFeature([Note, AuthorColor, Tag]), forwardRef(() => RevisionsModule), UsersModule, LoggerModule, diff --git a/src/notes/tag.entity.ts b/src/notes/tag.entity.ts new file mode 100644 index 000000000..fb3a1ad98 --- /dev/null +++ b/src/notes/tag.entity.ts @@ -0,0 +1,19 @@ +import { Column, Entity, ManyToMany, PrimaryGeneratedColumn } from 'typeorm'; +import { Note } from './note.entity'; + +@Entity() +export class Tag { + @PrimaryGeneratedColumn() + id: number; + + @Column({ + nullable: false, + }) + name: string; + + @ManyToMany( + _ => Note, + note => note.tags, + ) + notes: Note[]; +} From b349d25bd791074727af1fc90547ab8724a37c57 Mon Sep 17 00:00:00 2001 From: David Mehren Date: Sat, 3 Oct 2020 11:27:15 +0200 Subject: [PATCH 04/13] NotesService: Get metadata from database Signed-off-by: David Mehren Co-authored-by: Yannick Bungers --- src/notes/notes.service.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/notes/notes.service.ts b/src/notes/notes.service.ts index 8290bc04e..807c16d36 100644 --- a/src/notes/notes.service.ts +++ b/src/notes/notes.service.ts @@ -102,10 +102,10 @@ export class NotesService { // TODO: Convert DB UUID to base64 id: note.id, alias: note.alias, - title: NoteUtils.parseTitle(note), + title: note.title, // TODO: Get actual createTime createTime: new Date(), - description: NoteUtils.parseDescription(note), + description: note.description, editedBy: note.authorColors.map(authorColor => authorColor.user.userName), // TODO: Extract into method permissions: { @@ -119,7 +119,7 @@ export class NotesService { canEdit: noteGroupPermission.canEdit, })), }, - tags: NoteUtils.parseTags(note), + tags: note.tags.map(tag => tag.name), updateTime: (await this.getLastRevision(note)).createdAt, // TODO: Get actual updateUser updateUser: { From 943c8b4baba7b591e6938fc396996434f65efba2 Mon Sep 17 00:00:00 2001 From: David Mehren Date: Sat, 3 Oct 2020 15:37:57 +0200 Subject: [PATCH 05/13] NoteEntity: Enable eager loading and cascades for tags Signed-off-by: David Mehren Co-authored-by: Yannick Bungers --- src/notes/note.entity.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/notes/note.entity.ts b/src/notes/note.entity.ts index 1f412775b..817c857f9 100644 --- a/src/notes/note.entity.ts +++ b/src/notes/note.entity.ts @@ -74,6 +74,7 @@ export class Note { @ManyToMany( _ => Tag, tag => tag.notes, + { eager: true, cascade: true }, ) @JoinTable() tags: Tag[]; From 3726b278498171b7920b91ea6f52671b1b3ea56a Mon Sep 17 00:00:00 2001 From: David Mehren Date: Sat, 3 Oct 2020 15:52:49 +0200 Subject: [PATCH 06/13] NotesService: Implement `updateNoteMetadata` Signed-off-by: David Mehren Co-authored-by: Yannick Bungers --- src/notes/note-metadata.dto.ts | 10 ++++++++++ src/notes/notes.service.ts | 24 ++++++++++++++++++++++-- 2 files changed, 32 insertions(+), 2 deletions(-) diff --git a/src/notes/note-metadata.dto.ts b/src/notes/note-metadata.dto.ts index 2b26a37bb..fea43be93 100644 --- a/src/notes/note-metadata.dto.ts +++ b/src/notes/note-metadata.dto.ts @@ -34,3 +34,13 @@ export class NoteMetadataDto { @ValidateNested() permissions: NotePermissionsDto; } + +export class NoteMetadataUpdateDto { + @IsString() + title: string; + @IsString() + description: string; + @IsArray() + @IsString({ each: true }) + tags: string[]; +} diff --git a/src/notes/notes.service.ts b/src/notes/notes.service.ts index 807c16d36..092ad4339 100644 --- a/src/notes/notes.service.ts +++ b/src/notes/notes.service.ts @@ -7,20 +7,21 @@ import { Revision } from '../revisions/revision.entity'; import { RevisionsService } from '../revisions/revisions.service'; import { User } from '../users/user.entity'; import { UsersService } from '../users/users.service'; -import { NoteMetadataDto } from './note-metadata.dto'; +import { NoteMetadataDto, NoteMetadataUpdateDto } from './note-metadata.dto'; import { NotePermissionsDto, NotePermissionsUpdateDto, } from './note-permissions.dto'; import { NoteDto } from './note.dto'; import { Note } from './note.entity'; -import { NoteUtils } from './note.utils'; +import { Tag } from './tag.entity'; @Injectable() export class NotesService { constructor( private readonly logger: ConsoleLoggerService, @InjectRepository(Note) private noteRepository: Repository, + @InjectRepository(Tag) private tagRepository: Repository, @Inject(UsersService) private usersService: UsersService, @Inject(forwardRef(() => RevisionsService)) private revisionsService: RevisionsService, @@ -219,4 +220,23 @@ export class NotesService { editedByAtPosition: [], }; } + + async updateNoteMetadata( + noteIdOrAlias: string, + updateDto: NoteMetadataUpdateDto, + ) { + const note = await this.getNoteByIdOrAlias(noteIdOrAlias); + note.title = updateDto.title; + note.description = updateDto.description; + note.tags = await Promise.all( + updateDto.tags.map(async tag => { + let dbTag = await this.tagRepository.findOne({ where: { name: tag } }); + if (!dbTag) { + dbTag = await this.tagRepository.create({ name: tag }); + } + return dbTag; + }), + ); + await this.noteRepository.save(note); + } } From c1886ff1dca17f171e0648ad66744d2a8ce37b96 Mon Sep 17 00:00:00 2001 From: David Mehren Date: Sat, 3 Oct 2020 15:53:30 +0200 Subject: [PATCH 07/13] NotesController: Add `PUT :noteIdOrAlias/metadata` route Signed-off-by: David Mehren Co-authored-by: Yannick Bungers --- src/api/public/notes/notes.controller.ts | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/api/public/notes/notes.controller.ts b/src/api/public/notes/notes.controller.ts index fc3c7690e..79137ce35 100644 --- a/src/api/public/notes/notes.controller.ts +++ b/src/api/public/notes/notes.controller.ts @@ -9,6 +9,7 @@ import { Put, } from '@nestjs/common'; import { ConsoleLoggerService } from '../../../logger/console-logger.service'; +import { NoteMetadataUpdateDto } from '../../../notes/note-metadata.dto'; import { NotePermissionsUpdateDto } from '../../../notes/note-permissions.dto'; import { NotesService } from '../../../notes/notes.service'; import { RevisionsService } from '../../../revisions/revisions.service'; @@ -21,7 +22,7 @@ export class NotesController { private noteService: NotesService, private revisionsService: RevisionsService, ) { - this.logger.setContext(NotesController.name); + this.logger.setContext(NotesController.name); } @Post() @@ -72,6 +73,14 @@ export class NotesController { return this.noteService.getNoteMetadata(noteIdOrAlias); } + @Put(':noteIdOrAlias/metadata') + updateNoteMetadata( + @Param('noteIdOrAlias') noteIdOrAlias: string, + @Body() updateDto: NoteMetadataUpdateDto, + ) { + return this.noteService.updateNoteMetadata(noteIdOrAlias, updateDto); + } + @Put(':noteIdOrAlias/permissions') updateNotePermissions( @Param('noteIdOrAlias') noteIdOrAlias: string, From 731e5771587dc919e63cc5afd04ede8028679eba Mon Sep 17 00:00:00 2001 From: David Mehren Date: Sat, 3 Oct 2020 16:45:04 +0200 Subject: [PATCH 08/13] Add E2E tests for note metadata routes Signed-off-by: David Mehren Co-authored-by: Yannick Bungers --- test/public-api/notes.e2e-spec.ts | 93 ++++++++++++++++++++++++++++--- 1 file changed, 84 insertions(+), 9 deletions(-) diff --git a/test/public-api/notes.e2e-spec.ts b/test/public-api/notes.e2e-spec.ts index ce2190947..c93f6b36c 100644 --- a/test/public-api/notes.e2e-spec.ts +++ b/test/public-api/notes.e2e-spec.ts @@ -98,20 +98,48 @@ describe('Notes', () => { ).toEqual('New note text'); }); - it.skip(`PUT /notes/{note}/metadata`, () => { - // TODO - return request(app.getHttpServer()) - .post('/notes/test5/metadata') - .set('Content-Type', 'text/markdown') + it(`PUT /notes/{note}/metadata`, async () => { + await notesService.createNote('This is a test note.', 'test5'); + await request(app.getHttpServer()) + .put('/notes/test5/metadata') + .send({ + title: 'test title', + description: 'test description', + tags: ['test1', 'test2', 'test3'], + }) .expect(200); + const note5 = await notesService.getNoteByIdOrAlias('test5'); + expect(note5.title).toEqual('test title'); + expect(note5.description).toEqual('test description'); + expect(note5.tags.map(tag => tag.name)).toEqual([ + 'test1', + 'test2', + 'test3', + ]); }); - it.skip(`GET /notes/{note}/metadata`, () => { - notesService.createNote('This is a test note.', 'test6'); - return request(app.getHttpServer()) + it(`GET /notes/{note}/metadata`, async () => { + await notesService.createNote('This is a test note.', 'test6'); + const metadata = await request(app.getHttpServer()) .get('/notes/test6/metadata') .expect(200); - // TODO: Find out how to check the structure of the returned JSON + expect(typeof metadata.body.id).toEqual('string'); + expect(metadata.body.alias).toEqual('test6'); + expect(metadata.body.title).toBeNull(); + expect(metadata.body.description).toBeNull(); + expect(typeof metadata.body.createTime).toEqual('string'); + expect(metadata.body.editedBy).toEqual([]); + expect(metadata.body.permissions.owner).toBeNull(); + expect(metadata.body.permissions.sharedToUsers).toEqual([]); + expect(metadata.body.permissions.sharedToUsers).toEqual([]); + expect(metadata.body.tags).toEqual([]); + expect(typeof metadata.body.updateTime).toEqual('string'); + expect(typeof metadata.body.updateUser.displayName).toEqual('string'); + expect(typeof metadata.body.updateUser.userName).toEqual('string'); + expect(typeof metadata.body.updateUser.email).toEqual('string'); + expect(typeof metadata.body.updateUser.photo).toEqual('string'); + expect(typeof metadata.body.viewCount).toEqual('number'); + expect(metadata.body.editedBy).toEqual([]); }); it(`GET /notes/{note}/revisions`, async () => { @@ -141,6 +169,53 @@ describe('Notes', () => { expect(response.text).toEqual('This is a test note.'); }); + it(`2 notes with tags`, async () => { + //Create first nore + const content10 = 'This is the first test note.'; + const note10 = await request(app.getHttpServer()) + .post('/notes/test10') + .set('Content-Type', 'text/markdown') + .send(content10) + .expect('Content-Type', /json/) + .expect(201); + expect(note10.body.metadata?.id).toBeDefined(); + //Create second note + const content11 = 'This is the second test note.'; + const note11 = await request(app.getHttpServer()) + .post('/notes/test11') + .set('Content-Type', 'text/markdown') + .send(content11) + .expect('Content-Type', /json/) + .expect(201); + expect(note11.body.metadata?.id).toBeDefined(); + //Add tags to both notes + await request(app.getHttpServer()) + .put('/notes/test10/metadata') + .send({ + title: 'Test Note 10', + description: 'test description', + tags: ['test1', 'test2', 'test3'], + }) + .expect(200); + await request(app.getHttpServer()) + .put('/notes/test11/metadata') + .send({ + title: 'Test Note 11', + description: 'test description', + tags: ['test1', 'test2', 'test4'], + }) + .expect(200); + //Delete first note + await request(app.getHttpServer()) + .delete('/notes/test10') + .expect(200); + //Check if all tags are still present + const metadata11 = await request(app.getHttpServer()) + .get('/notes/test11/metadata') + .expect(200); + expect(metadata11.body.tags).toEqual(['test1', 'test2', 'test4']); + }); + afterAll(async () => { await app.close(); }); From b2085efb1d636aeb1df631c6553c9b8c0a798043 Mon Sep 17 00:00:00 2001 From: David Mehren Date: Sat, 3 Oct 2020 17:24:42 +0200 Subject: [PATCH 09/13] Add missing TagRepository provider in unit tests Signed-off-by: David Mehren Co-authored-by: Yannick Bungers --- src/api/public/me/me.controller.spec.ts | 3 +++ src/api/public/notes/notes.controller.spec.ts | 7 +++++++ src/notes/notes.service.spec.ts | 7 +++++++ src/revisions/revisions.service.spec.ts | 3 +++ 4 files changed, 20 insertions(+) diff --git a/src/api/public/me/me.controller.spec.ts b/src/api/public/me/me.controller.spec.ts index 7ea008b21..9831c6ef0 100644 --- a/src/api/public/me/me.controller.spec.ts +++ b/src/api/public/me/me.controller.spec.ts @@ -5,6 +5,7 @@ import { LoggerModule } from '../../../logger/logger.module'; import { AuthorColor } from '../../../notes/author-color.entity'; import { Note } from '../../../notes/note.entity'; import { NotesModule } from '../../../notes/notes.module'; +import { Tag } from '../../../notes/tag.entity'; import { Authorship } from '../../../revisions/authorship.entity'; import { Revision } from '../../../revisions/revision.entity'; import { AuthToken } from '../../../users/auth-token.entity'; @@ -35,6 +36,8 @@ describe('Me Controller', () => { .useValue({}) .overrideProvider(getRepositoryToken(Revision)) .useValue({}) + .overrideProvider(getRepositoryToken(Tag)) + .useValue({}) .compile(); controller = module.get(MeController); diff --git a/src/api/public/notes/notes.controller.spec.ts b/src/api/public/notes/notes.controller.spec.ts index 6cbf98960..806f2f40f 100644 --- a/src/api/public/notes/notes.controller.spec.ts +++ b/src/api/public/notes/notes.controller.spec.ts @@ -4,6 +4,7 @@ import { LoggerModule } from '../../../logger/logger.module'; import { AuthorColor } from '../../../notes/author-color.entity'; import { Note } from '../../../notes/note.entity'; import { NotesService } from '../../../notes/notes.service'; +import { Tag } from '../../../notes/tag.entity'; import { Authorship } from '../../../revisions/authorship.entity'; import { Revision } from '../../../revisions/revision.entity'; import { RevisionsModule } from '../../../revisions/revisions.module'; @@ -25,6 +26,10 @@ describe('Notes Controller', () => { provide: getRepositoryToken(Note), useValue: {}, }, + { + provide: getRepositoryToken(Tag), + useValue: {}, + }, ], imports: [RevisionsModule, UsersModule, LoggerModule], }) @@ -44,6 +49,8 @@ describe('Notes Controller', () => { .useValue({}) .overrideProvider(getRepositoryToken(Note)) .useValue({}) + .overrideProvider(getRepositoryToken(Tag)) + .useValue({}) .compile(); controller = module.get(NotesController); diff --git a/src/notes/notes.service.spec.ts b/src/notes/notes.service.spec.ts index 4d18ca55f..a3383572d 100644 --- a/src/notes/notes.service.spec.ts +++ b/src/notes/notes.service.spec.ts @@ -11,6 +11,7 @@ import { UsersModule } from '../users/users.module'; import { AuthorColor } from './author-color.entity'; import { Note } from './note.entity'; import { NotesService } from './notes.service'; +import { Tag } from './tag.entity'; describe('NotesService', () => { let service: NotesService; @@ -23,6 +24,10 @@ describe('NotesService', () => { provide: getRepositoryToken(Note), useValue: {}, }, + { + provide: getRepositoryToken(Tag), + useValue: {}, + }, ], imports: [UsersModule, RevisionsModule, LoggerModule], }) @@ -40,6 +45,8 @@ describe('NotesService', () => { .useValue({}) .overrideProvider(getRepositoryToken(Note)) .useValue({}) + .overrideProvider(getRepositoryToken(Tag)) + .useValue({}) .compile(); service = module.get(NotesService); }); diff --git a/src/revisions/revisions.service.spec.ts b/src/revisions/revisions.service.spec.ts index 04e929973..4eb976257 100644 --- a/src/revisions/revisions.service.spec.ts +++ b/src/revisions/revisions.service.spec.ts @@ -10,6 +10,7 @@ import { User } from '../users/user.entity'; import { Authorship } from './authorship.entity'; import { Revision } from './revision.entity'; import { RevisionsService } from './revisions.service'; +import { Tag } from '../notes/tag.entity'; describe('RevisionsService', () => { let service: RevisionsService; @@ -39,6 +40,8 @@ describe('RevisionsService', () => { .useValue({}) .overrideProvider(getRepositoryToken(Revision)) .useValue({}) + .overrideProvider(getRepositoryToken(Tag)) + .useValue({}) .compile(); service = module.get(RevisionsService); From 6a1da64cf65625bb3578bdf7456e75c3b1353e03 Mon Sep 17 00:00:00 2001 From: David Mehren Date: Sat, 3 Oct 2020 17:36:01 +0200 Subject: [PATCH 10/13] Remove NoteUtils class, as the planned parsing logic is not needed anymore Signed-off-by: David Mehren Co-authored-by: Yannick Bungers --- src/notes/note.utils.ts | 18 ------------------ 1 file changed, 18 deletions(-) delete mode 100644 src/notes/note.utils.ts diff --git a/src/notes/note.utils.ts b/src/notes/note.utils.ts deleted file mode 100644 index d84c4b182..000000000 --- a/src/notes/note.utils.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { Note } from './note.entity'; - -export class NoteUtils { - public static parseTitle(note: Note): string { - // TODO: Implement method - return 'Hardcoded note title'; - } - - public static parseDescription(note: Note): string { - // TODO: Implement method - return 'Hardcoded note description'; - } - - public static parseTags(note: Note): string[] { - // TODO: Implement method - return ['Hardcoded note tag']; - } -} From 241a577a02e24f9fa250b7c8ac514cd430b908ca Mon Sep 17 00:00:00 2001 From: David Mehren Date: Sat, 24 Oct 2020 20:55:31 +0200 Subject: [PATCH 11/13] DB Schema: Make layout pretty Signed-off-by: David Mehren Co-authored-by: Yannick Bungers --- docs/dev/db-schema.plantuml | 40 +++++++++++++++++++------------------ 1 file changed, 21 insertions(+), 19 deletions(-) diff --git a/docs/dev/db-schema.plantuml b/docs/dev/db-schema.plantuml index c42827aea..789eac254 100644 --- a/docs/dev/db-schema.plantuml +++ b/docs/dev/db-schema.plantuml @@ -1,9 +1,6 @@ @startuml -' hide the spot hide circle - -' avoid problems with angled crows feet -skinparam linetype ortho +skinparam nodesep 60 entity "Note" { *id : uuid <> @@ -132,21 +129,26 @@ entity "MediaUpload" { *createdAt : date } -Note "1" - "1..*" Revision +User "1" -- "0..*" Note: owner +User "1" -u- "1..*" Identity +User "1" - "1..*" authToken +User "1" -l- "1..*" Session +User "1" - "0..*" MediaUpload +User "0..*" -- "0..*" Note +User "1" - "0..*" Authorship + +(User, Note) . AuthorColors + Revision "0..*" - "0..*" Authorship (Revision, Authorship) .. RevisionAuthorship -Authorship "0..*" -- "1" User -Note "0..*" -- "1" User : owner -Note "1" -- "0..*" NoteUserPermission -NoteUserPermission "1" -- "1" User -Note "1" -- "0..*" NoteGroupPermission -NoteGroupPermission "0..*" -- "1" Group -Identity "1..*" -- "1" User -authToken "1..*" -- "1" User -Session "1..*" -- "1" User -Note "0..*" -- "0..*" User : color -(Note, User) .. AuthorColors -Note "0..*" -- "0..*" Tag : tags -MediaUpload "0..*" -- "1" Note -MediaUpload "0..*" -- "1" User + +MediaUpload "0..*" -- "1" Note +Note "1" - "1..*" Revision +Note "0..*" -l- "0..*" Tag +Note "0..*" -- "0..*" Group + +User "0..*" -- "0..*" Note +(User, Note) . NoteUserPermission +(Note, Group) . NoteGroupPermission + @enduml From 85ee6780ad9d768e62c5ac4493b7c04742fec9f2 Mon Sep 17 00:00:00 2001 From: David Mehren Date: Sat, 24 Oct 2020 21:11:06 +0200 Subject: [PATCH 12/13] Remove `PUT /notes/{note}/metadata` and corresponding service code Signed-off-by: David Mehren Co-authored-by: Yannick Bungers --- src/api/public/notes/notes.controller.ts | 8 --- src/notes/notes.service.ts | 19 ------- src/revisions/revisions.service.ts | 1 + test/public-api/notes.e2e-spec.ts | 67 ------------------------ 4 files changed, 1 insertion(+), 94 deletions(-) diff --git a/src/api/public/notes/notes.controller.ts b/src/api/public/notes/notes.controller.ts index 79137ce35..9000d63cb 100644 --- a/src/api/public/notes/notes.controller.ts +++ b/src/api/public/notes/notes.controller.ts @@ -73,14 +73,6 @@ export class NotesController { return this.noteService.getNoteMetadata(noteIdOrAlias); } - @Put(':noteIdOrAlias/metadata') - updateNoteMetadata( - @Param('noteIdOrAlias') noteIdOrAlias: string, - @Body() updateDto: NoteMetadataUpdateDto, - ) { - return this.noteService.updateNoteMetadata(noteIdOrAlias, updateDto); - } - @Put(':noteIdOrAlias/permissions') updateNotePermissions( @Param('noteIdOrAlias') noteIdOrAlias: string, diff --git a/src/notes/notes.service.ts b/src/notes/notes.service.ts index 092ad4339..c077f7d35 100644 --- a/src/notes/notes.service.ts +++ b/src/notes/notes.service.ts @@ -220,23 +220,4 @@ export class NotesService { editedByAtPosition: [], }; } - - async updateNoteMetadata( - noteIdOrAlias: string, - updateDto: NoteMetadataUpdateDto, - ) { - const note = await this.getNoteByIdOrAlias(noteIdOrAlias); - note.title = updateDto.title; - note.description = updateDto.description; - note.tags = await Promise.all( - updateDto.tags.map(async tag => { - let dbTag = await this.tagRepository.findOne({ where: { name: tag } }); - if (!dbTag) { - dbTag = await this.tagRepository.create({ name: tag }); - } - return dbTag; - }), - ); - await this.noteRepository.save(note); - } } diff --git a/src/revisions/revisions.service.ts b/src/revisions/revisions.service.ts index af0b5ce7a..cb58a1ded 100644 --- a/src/revisions/revisions.service.ts +++ b/src/revisions/revisions.service.ts @@ -76,6 +76,7 @@ export class RevisionsService { createRevision(content: string) { // TODO: Add previous revision // TODO: Calculate patch + // TODO: Save metadata return this.revisionRepository.create({ content: content, length: content.length, diff --git a/test/public-api/notes.e2e-spec.ts b/test/public-api/notes.e2e-spec.ts index c93f6b36c..1941dbd4b 100644 --- a/test/public-api/notes.e2e-spec.ts +++ b/test/public-api/notes.e2e-spec.ts @@ -98,26 +98,6 @@ describe('Notes', () => { ).toEqual('New note text'); }); - it(`PUT /notes/{note}/metadata`, async () => { - await notesService.createNote('This is a test note.', 'test5'); - await request(app.getHttpServer()) - .put('/notes/test5/metadata') - .send({ - title: 'test title', - description: 'test description', - tags: ['test1', 'test2', 'test3'], - }) - .expect(200); - const note5 = await notesService.getNoteByIdOrAlias('test5'); - expect(note5.title).toEqual('test title'); - expect(note5.description).toEqual('test description'); - expect(note5.tags.map(tag => tag.name)).toEqual([ - 'test1', - 'test2', - 'test3', - ]); - }); - it(`GET /notes/{note}/metadata`, async () => { await notesService.createNote('This is a test note.', 'test6'); const metadata = await request(app.getHttpServer()) @@ -169,53 +149,6 @@ describe('Notes', () => { expect(response.text).toEqual('This is a test note.'); }); - it(`2 notes with tags`, async () => { - //Create first nore - const content10 = 'This is the first test note.'; - const note10 = await request(app.getHttpServer()) - .post('/notes/test10') - .set('Content-Type', 'text/markdown') - .send(content10) - .expect('Content-Type', /json/) - .expect(201); - expect(note10.body.metadata?.id).toBeDefined(); - //Create second note - const content11 = 'This is the second test note.'; - const note11 = await request(app.getHttpServer()) - .post('/notes/test11') - .set('Content-Type', 'text/markdown') - .send(content11) - .expect('Content-Type', /json/) - .expect(201); - expect(note11.body.metadata?.id).toBeDefined(); - //Add tags to both notes - await request(app.getHttpServer()) - .put('/notes/test10/metadata') - .send({ - title: 'Test Note 10', - description: 'test description', - tags: ['test1', 'test2', 'test3'], - }) - .expect(200); - await request(app.getHttpServer()) - .put('/notes/test11/metadata') - .send({ - title: 'Test Note 11', - description: 'test description', - tags: ['test1', 'test2', 'test4'], - }) - .expect(200); - //Delete first note - await request(app.getHttpServer()) - .delete('/notes/test10') - .expect(200); - //Check if all tags are still present - const metadata11 = await request(app.getHttpServer()) - .get('/notes/test11/metadata') - .expect(200); - expect(metadata11.body.tags).toEqual(['test1', 'test2', 'test4']); - }); - afterAll(async () => { await app.close(); }); From 61e6020c6bc699c6ea2cac97f08c1d4f526c016b Mon Sep 17 00:00:00 2001 From: David Mehren Date: Sat, 24 Oct 2020 21:11:16 +0200 Subject: [PATCH 13/13] Fix tests Signed-off-by: David Mehren Co-authored-by: Yannick Bungers --- src/api/public/media/media.controller.spec.ts | 3 +++ src/media/media.service.spec.ts | 3 +++ 2 files changed, 6 insertions(+) diff --git a/src/api/public/media/media.controller.spec.ts b/src/api/public/media/media.controller.spec.ts index c5cf4914e..330cdeba4 100644 --- a/src/api/public/media/media.controller.spec.ts +++ b/src/api/public/media/media.controller.spec.ts @@ -6,6 +6,7 @@ import { MediaModule } from '../../../media/media.module'; import { AuthorColor } from '../../../notes/author-color.entity'; import { Note } from '../../../notes/note.entity'; import { NotesModule } from '../../../notes/notes.module'; +import { Tag } from '../../../notes/tag.entity'; import { Authorship } from '../../../revisions/authorship.entity'; import { Revision } from '../../../revisions/revision.entity'; import { AuthToken } from '../../../users/auth-token.entity'; @@ -37,6 +38,8 @@ describe('Media Controller', () => { .useValue({}) .overrideProvider(getRepositoryToken(User)) .useValue({}) + .overrideProvider(getRepositoryToken(Tag)) + .useValue({}) .compile(); controller = module.get(MediaController); diff --git a/src/media/media.service.spec.ts b/src/media/media.service.spec.ts index 19369f72d..2e7d44977 100644 --- a/src/media/media.service.spec.ts +++ b/src/media/media.service.spec.ts @@ -4,6 +4,7 @@ import { LoggerModule } from '../logger/logger.module'; import { AuthorColor } from '../notes/author-color.entity'; import { Note } from '../notes/note.entity'; import { NotesModule } from '../notes/notes.module'; +import { Tag } from '../notes/tag.entity'; import { Authorship } from '../revisions/authorship.entity'; import { Revision } from '../revisions/revision.entity'; import { AuthToken } from '../users/auth-token.entity'; @@ -43,6 +44,8 @@ describe('MediaService', () => { .useValue({}) .overrideProvider(getRepositoryToken(User)) .useValue({}) + .overrideProvider(getRepositoryToken(Tag)) + .useValue({}) .compile(); service = module.get(MediaService);