From 235e4f647cad7352193630d9f1c00a1ad7cf6b46 Mon Sep 17 00:00:00 2001 From: David Mehren Date: Tue, 30 Nov 2021 16:46:07 +0100 Subject: [PATCH] refactor(note): lazy-load relations Signed-off-by: David Mehren --- src/api/private/alias/alias.controller.ts | 6 +- .../private/me/history/history.controller.ts | 8 +- src/api/public/alias/alias.controller.ts | 6 +- src/api/public/me/me.controller.ts | 4 +- src/api/public/notes/notes.controller.ts | 19 +- src/history/history.service.spec.ts | 44 ++-- src/history/history.service.ts | 6 +- src/history/utils.spec.ts | 22 +- src/history/utils.ts | 7 +- src/media/media.service.spec.ts | 4 +- src/notes/alias.service.spec.ts | 43 ++-- src/notes/alias.service.ts | 18 +- src/notes/note.entity.ts | 30 +-- src/notes/notes.service.spec.ts | 204 +++++++++--------- src/notes/notes.service.ts | 43 ++-- src/notes/utils.spec.ts | 12 +- src/notes/utils.ts | 4 +- src/permissions/permissions.service.spec.ts | 52 ++--- src/permissions/permissions.service.ts | 23 +- src/seed.ts | 10 +- test/private-api/history.e2e-spec.ts | 28 +-- test/public-api/me.e2e-spec.ts | 24 ++- test/public-api/notes.e2e-spec.ts | 10 +- 23 files changed, 343 insertions(+), 284 deletions(-) diff --git a/src/api/private/alias/alias.controller.ts b/src/api/private/alias/alias.controller.ts index d32cc21cf..704143853 100644 --- a/src/api/private/alias/alias.controller.ts +++ b/src/api/private/alias/alias.controller.ts @@ -56,7 +56,7 @@ export class AliasController { const note = await this.noteService.getNoteByIdOrAlias( newAliasDto.noteIdOrAlias, ); - if (!this.permissionsService.isOwner(user, note)) { + if (!(await this.permissionsService.isOwner(user, note))) { throw new UnauthorizedException('Reading note denied!'); } const updatedAlias = await this.aliasService.addAlias( @@ -88,7 +88,7 @@ export class AliasController { } try { const note = await this.noteService.getNoteByIdOrAlias(alias); - if (!this.permissionsService.isOwner(user, note)) { + if (!(await this.permissionsService.isOwner(user, note))) { throw new UnauthorizedException('Reading note denied!'); } const updatedAlias = await this.aliasService.makeAliasPrimary( @@ -115,7 +115,7 @@ export class AliasController { ): Promise { try { const note = await this.noteService.getNoteByIdOrAlias(alias); - if (!this.permissionsService.isOwner(user, note)) { + if (!(await this.permissionsService.isOwner(user, note))) { throw new UnauthorizedException('Reading note denied!'); } await this.aliasService.removeAlias(note, alias); diff --git a/src/api/private/me/history/history.controller.ts b/src/api/private/me/history/history.controller.ts index 6b3ebe9b1..5c2b684dc 100644 --- a/src/api/private/me/history/history.controller.ts +++ b/src/api/private/me/history/history.controller.ts @@ -45,8 +45,10 @@ export class HistoryController { async getHistory(@RequestUser() user: User): Promise { try { const foundEntries = await this.historyService.getEntriesByUser(user); - return foundEntries.map((entry) => - this.historyService.toHistoryEntryDto(entry), + return await Promise.all( + foundEntries.map((entry) => + this.historyService.toHistoryEntryDto(entry), + ), ); } catch (e) { if (e instanceof NotInDBError) { @@ -96,7 +98,7 @@ export class HistoryController { user, entryUpdateDto, ); - return this.historyService.toHistoryEntryDto(newEntry); + return await this.historyService.toHistoryEntryDto(newEntry); } catch (e) { if (e instanceof NotInDBError) { throw new NotFoundException(e.message); diff --git a/src/api/public/alias/alias.controller.ts b/src/api/public/alias/alias.controller.ts index 35bf80676..c3faa10db 100644 --- a/src/api/public/alias/alias.controller.ts +++ b/src/api/public/alias/alias.controller.ts @@ -69,7 +69,7 @@ export class AliasController { const note = await this.noteService.getNoteByIdOrAlias( newAliasDto.noteIdOrAlias, ); - if (!this.permissionsService.isOwner(user, note)) { + if (!(await this.permissionsService.isOwner(user, note))) { throw new UnauthorizedException('Reading note denied!'); } const updatedAlias = await this.aliasService.addAlias( @@ -107,7 +107,7 @@ export class AliasController { } try { const note = await this.noteService.getNoteByIdOrAlias(alias); - if (!this.permissionsService.isOwner(user, note)) { + if (!(await this.permissionsService.isOwner(user, note))) { throw new UnauthorizedException('Reading note denied!'); } const updatedAlias = await this.aliasService.makeAliasPrimary( @@ -139,7 +139,7 @@ export class AliasController { ): Promise { try { const note = await this.noteService.getNoteByIdOrAlias(alias); - if (!this.permissionsService.isOwner(user, note)) { + if (!(await this.permissionsService.isOwner(user, note))) { throw new UnauthorizedException('Reading note denied!'); } await this.aliasService.removeAlias(note, alias); diff --git a/src/api/public/me/me.controller.ts b/src/api/public/me/me.controller.ts index 1e1d6c908..bb9c33089 100644 --- a/src/api/public/me/me.controller.ts +++ b/src/api/public/me/me.controller.ts @@ -101,7 +101,7 @@ export class MeController { ): Promise { try { const foundEntry = await this.historyService.getEntryByNote(note, user); - return this.historyService.toHistoryEntryDto(foundEntry); + return await this.historyService.toHistoryEntryDto(foundEntry); } catch (e) { if (e instanceof NotInDBError) { throw new NotFoundException(e.message); @@ -126,7 +126,7 @@ export class MeController { ): Promise { // ToDo: Check if user is allowed to pin this history entry try { - return this.historyService.toHistoryEntryDto( + return await this.historyService.toHistoryEntryDto( await this.historyService.updateHistoryEntry( note, user, diff --git a/src/api/public/notes/notes.controller.ts b/src/api/public/notes/notes.controller.ts index 28b2bd837..351d42f8d 100644 --- a/src/api/public/notes/notes.controller.ts +++ b/src/api/public/notes/notes.controller.ts @@ -16,7 +16,7 @@ import { Post, Put, UseGuards, - UseInterceptors + UseInterceptors, } from '@nestjs/common'; import { ApiCreatedResponse, @@ -26,17 +26,24 @@ import { ApiProduces, ApiSecurity, ApiTags, - ApiUnauthorizedResponse + ApiUnauthorizedResponse, } from '@nestjs/swagger'; import { TokenAuthGuard } from '../../../auth/token.strategy'; -import { AlreadyInDBError, ForbiddenIdError, NotInDBError } from '../../../errors/errors'; +import { + AlreadyInDBError, + ForbiddenIdError, + NotInDBError, +} from '../../../errors/errors'; import { HistoryService } from '../../../history/history.service'; import { ConsoleLoggerService } from '../../../logger/console-logger.service'; import { MediaUploadDto } from '../../../media/media-upload.dto'; import { MediaService } from '../../../media/media.service'; import { NoteMetadataDto } from '../../../notes/note-metadata.dto'; -import { NotePermissionsDto, NotePermissionsUpdateDto } from '../../../notes/note-permissions.dto'; +import { + NotePermissionsDto, + NotePermissionsUpdateDto, +} from '../../../notes/note-permissions.dto'; import { NoteDto } from '../../../notes/note.dto'; import { Note } from '../../../notes/note.entity'; import { NoteMediaDeletionDto } from '../../../notes/note.media-deletion.dto'; @@ -50,7 +57,7 @@ import { User } from '../../../users/user.entity'; import { forbiddenDescription, successfullyDeletedDescription, - unauthorizedDescription + unauthorizedDescription, } from '../../utils/descriptions'; import { FullApi } from '../../utils/fullapi-decorator'; import { GetNoteInterceptor } from '../../utils/get-note.interceptor'; @@ -230,7 +237,7 @@ export class NotesController { @RequestNote() note: Note, @Body() updateDto: NotePermissionsUpdateDto, ): Promise { - return this.noteService.toNotePermissionsDto( + return await this.noteService.toNotePermissionsDto( await this.noteService.updateNotePermissions(note, updateDto), ); } diff --git a/src/history/history.service.spec.ts b/src/history/history.service.spec.ts index bf4a02f68..84df3999f 100644 --- a/src/history/history.service.spec.ts +++ b/src/history/history.service.spec.ts @@ -159,9 +159,9 @@ describe('HistoryService', () => { Note.create(user, alias) as Note, user, ); - expect(createHistoryEntry.note.aliases).toHaveLength(1); - expect(createHistoryEntry.note.aliases[0].name).toEqual(alias); - expect(createHistoryEntry.note.owner).toEqual(user); + expect(await createHistoryEntry.note.aliases).toHaveLength(1); + expect((await createHistoryEntry.note.aliases)[0].name).toEqual(alias); + expect(await createHistoryEntry.note.owner).toEqual(user); expect(createHistoryEntry.user).toEqual(user); expect(createHistoryEntry.pinStatus).toEqual(false); }); @@ -177,9 +177,9 @@ describe('HistoryService', () => { Note.create(user, alias) as Note, user, ); - expect(createHistoryEntry.note.aliases).toHaveLength(1); - expect(createHistoryEntry.note.aliases[0].name).toEqual(alias); - expect(createHistoryEntry.note.owner).toEqual(user); + expect(await createHistoryEntry.note.aliases).toHaveLength(1); + expect((await createHistoryEntry.note.aliases)[0].name).toEqual(alias); + expect(await createHistoryEntry.note.owner).toEqual(user); expect(createHistoryEntry.user).toEqual(user); expect(createHistoryEntry.pinStatus).toEqual(false); expect(createHistoryEntry.updatedAt.getTime()).toBeGreaterThanOrEqual( @@ -223,9 +223,9 @@ describe('HistoryService', () => { pinStatus: true, }, ); - expect(updatedHistoryEntry.note.aliases).toHaveLength(1); - expect(updatedHistoryEntry.note.aliases[0].name).toEqual(alias); - expect(updatedHistoryEntry.note.owner).toEqual(user); + expect(await updatedHistoryEntry.note.aliases).toHaveLength(1); + expect((await updatedHistoryEntry.note.aliases)[0].name).toEqual(alias); + expect(await updatedHistoryEntry.note.owner).toEqual(user); expect(updatedHistoryEntry.user).toEqual(user); expect(updatedHistoryEntry.pinStatus).toEqual(true); }); @@ -371,11 +371,13 @@ describe('HistoryService', () => { const mockedManager = { find: jest.fn().mockResolvedValueOnce([historyEntry]), createQueryBuilder: () => createQueryBuilder, - remove: jest.fn().mockImplementationOnce((entry: HistoryEntry) => { - expect(entry.note.aliases).toHaveLength(1); - expect(entry.note.aliases[0].name).toEqual(alias); - expect(entry.pinStatus).toEqual(false); - }), + remove: jest + .fn() + .mockImplementationOnce(async (entry: HistoryEntry) => { + expect(await entry.note.aliases).toHaveLength(1); + expect((await entry.note.aliases)[0].name).toEqual(alias); + expect(entry.pinStatus).toEqual(false); + }), save: jest.fn().mockImplementationOnce((entry: HistoryEntry) => { expect(entry.note.aliases).toEqual( newlyCreatedHistoryEntry.note.aliases, @@ -402,11 +404,13 @@ describe('HistoryService', () => { const tags = ['tag1', 'tag2']; const note = Note.create(user, alias) as Note; note.title = title; - note.tags = tags.map((tag) => { - const newTag = new Tag(); - newTag.name = tag; - return newTag; - }); + note.tags = Promise.resolve( + tags.map((tag) => { + const newTag = new Tag(); + newTag.name = tag; + return newTag; + }), + ); const historyEntry = HistoryEntry.create(user, note) as HistoryEntry; historyEntry.pinStatus = true; const createQueryBuilder = { @@ -420,7 +424,7 @@ describe('HistoryService', () => { // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore .mockImplementation(() => createQueryBuilder); - const historyEntryDto = service.toHistoryEntryDto(historyEntry); + const historyEntryDto = await service.toHistoryEntryDto(historyEntry); expect(historyEntryDto.pinStatus).toEqual(true); expect(historyEntryDto.identifier).toEqual(alias); expect(historyEntryDto.tags).toEqual(tags); diff --git a/src/history/history.service.ts b/src/history/history.service.ts index a9b478b34..c4081d3bc 100644 --- a/src/history/history.service.ts +++ b/src/history/history.service.ts @@ -186,11 +186,11 @@ export class HistoryService { * @param {HistoryEntry} entry - the history entry to use * @return {HistoryEntryDto} the built HistoryEntryDto */ - toHistoryEntryDto(entry: HistoryEntry): HistoryEntryDto { + async toHistoryEntryDto(entry: HistoryEntry): Promise { return { - identifier: getIdentifier(entry), + identifier: await getIdentifier(entry), lastVisited: entry.updatedAt, - tags: this.notesService.toTagList(entry.note), + tags: await this.notesService.toTagList(entry.note), title: entry.note.title ?? '', pinStatus: entry.pinStatus, }; diff --git a/src/history/utils.spec.ts b/src/history/utils.spec.ts index 582d7d403..d65fc617b 100644 --- a/src/history/utils.spec.ts +++ b/src/history/utils.spec.ts @@ -18,19 +18,19 @@ describe('getIdentifier', () => { note = Note.create(user, alias) as Note; entry = HistoryEntry.create(user, note) as HistoryEntry; }); - it('returns the publicId if there are no aliases', () => { - note.aliases = undefined as unknown as Alias[]; - expect(getIdentifier(entry)).toEqual(note.publicId); + it('returns the publicId if there are no aliases', async () => { + note.aliases = Promise.resolve(undefined as unknown as Alias[]); + expect(await getIdentifier(entry)).toEqual(note.publicId); }); - it('returns the publicId, if the alias array is empty', () => { - note.aliases = []; - expect(getIdentifier(entry)).toEqual(note.publicId); + it('returns the publicId, if the alias array is empty', async () => { + note.aliases = Promise.resolve([]); + expect(await getIdentifier(entry)).toEqual(note.publicId); }); - it('returns the publicId, if the only alias is not primary', () => { - note.aliases[0].primary = false; - expect(getIdentifier(entry)).toEqual(note.publicId); + it('returns the publicId, if the only alias is not primary', async () => { + (await note.aliases)[0].primary = false; + expect(await getIdentifier(entry)).toEqual(note.publicId); }); - it('returns the primary alias, if one exists', () => { - expect(getIdentifier(entry)).toEqual(note.aliases[0].name); + it('returns the primary alias, if one exists', async () => { + expect(await getIdentifier(entry)).toEqual((await note.aliases)[0].name); }); }); diff --git a/src/history/utils.ts b/src/history/utils.ts index ea49cee63..5a11718d3 100644 --- a/src/history/utils.ts +++ b/src/history/utils.ts @@ -6,11 +6,12 @@ import { getPrimaryAlias } from '../notes/utils'; import { HistoryEntry } from './history-entry.entity'; -export function getIdentifier(entry: HistoryEntry): string { - if (!entry.note.aliases || entry.note.aliases.length === 0) { +export async function getIdentifier(entry: HistoryEntry): Promise { + const aliases = await entry.note.aliases; + if (!aliases || aliases.length === 0) { return entry.note.publicId; } - const primaryAlias = getPrimaryAlias(entry.note); + const primaryAlias = await getPrimaryAlias(entry.note); if (primaryAlias === undefined) { return entry.note.publicId; } diff --git a/src/media/media.service.spec.ts b/src/media/media.service.spec.ts index 3260a1ef6..0ce609ebd 100644 --- a/src/media/media.service.spec.ts +++ b/src/media/media.service.spec.ts @@ -291,7 +291,9 @@ describe('MediaService', () => { describe('removeNoteFromMediaUpload', () => { it('works', async () => { const mockNote = {} as Note; - mockNote.aliases = [Alias.create('test', mockNote, true) as Alias]; + mockNote.aliases = Promise.resolve([ + Alias.create('test', mockNote, true) as Alias, + ]); const mockMediaUploadEntry = { id: 'testMediaUpload', backendData: 'testBackendData', diff --git a/src/notes/alias.service.spec.ts b/src/notes/alias.service.spec.ts index 218b04c30..80cbd1a4a 100644 --- a/src/notes/alias.service.spec.ts +++ b/src/notes/alias.service.spec.ts @@ -158,8 +158,11 @@ describe('AliasService', () => { const alias2 = 'testAlias2'; const user = User.create('hardcoded', 'Testy') as User; describe('removes one alias correctly', () => { - const note = Note.create(user, alias) as Note; - note.aliases.push(Alias.create(alias2, note, false) as Alias); + let note: Note; + beforeAll(async () => { + note = Note.create(user, alias) as Note; + (await note.aliases).push(Alias.create(alias2, note, false) as Alias); + }); it('with two aliases', async () => { jest .spyOn(noteRepo, 'save') @@ -170,9 +173,10 @@ describe('AliasService', () => { async (alias: Alias): Promise => alias, ); const savedNote = await service.removeAlias(note, alias2); - expect(savedNote.aliases).toHaveLength(1); - expect(savedNote.aliases[0].name).toEqual(alias); - expect(savedNote.aliases[0].primary).toBeTruthy(); + const aliases = await savedNote.aliases; + expect(aliases).toHaveLength(1); + expect(aliases[0].name).toEqual(alias); + expect(aliases[0].primary).toBeTruthy(); }); it('with one alias, that is primary', async () => { jest @@ -184,12 +188,15 @@ describe('AliasService', () => { async (alias: Alias): Promise => alias, ); const savedNote = await service.removeAlias(note, alias); - expect(savedNote.aliases).toHaveLength(0); + expect(await savedNote.aliases).toHaveLength(0); }); }); describe('does not remove one alias', () => { - const note = Note.create(user, alias) as Note; - note.aliases.push(Alias.create(alias2, note, false) as Alias); + let note: Note; + beforeEach(async () => { + note = Note.create(user, alias) as Note; + (await note.aliases).push(Alias.create(alias2, note, false) as Alias); + }); it('if the alias is unknown', async () => { await expect(service.removeAlias(note, 'non existent')).rejects.toThrow( NotInDBError, @@ -206,10 +213,18 @@ describe('AliasService', () => { describe('makeAliasPrimary', () => { const user = User.create('hardcoded', 'Testy') as User; const aliasName = 'testAlias'; - const note = Note.create(user, aliasName) as Note; - const alias = Alias.create(aliasName, note, true) as Alias; - const alias2 = Alias.create('testAlias2', note, false) as Alias; - note.aliases.push(alias2); + let note: Note; + let alias: Alias; + let alias2: Alias; + beforeEach(async () => { + note = Note.create(user, aliasName) as Note; + alias = Alias.create(aliasName, note, true) as Alias; + alias2 = Alias.create('testAlias2', note, false) as Alias; + (await note.aliases).push( + Alias.create('testAlias2', note, false) as Alias, + ); + }); + it('mark the alias as primary', async () => { jest .spyOn(aliasRepo, 'findOne') @@ -224,10 +239,10 @@ describe('AliasService', () => { where: () => createQueryBuilder, orWhere: () => createQueryBuilder, setParameter: () => createQueryBuilder, - getOne: () => { + getOne: async () => { return { ...note, - aliases: note.aliases.map((anAlias) => { + aliases: (await note.aliases).map((anAlias) => { if (anAlias.primary) { anAlias.primary = false; } diff --git a/src/notes/alias.service.ts b/src/notes/alias.service.ts index 96f8c0d55..179541c7e 100644 --- a/src/notes/alias.service.ts +++ b/src/notes/alias.service.ts @@ -62,13 +62,13 @@ export class AliasService { ); } let newAlias; - if (note.aliases.length === 0) { + if ((await note.aliases).length === 0) { // the first alias is automatically made the primary alias newAlias = Alias.create(alias, note, true); } else { newAlias = Alias.create(alias, note, false); } - note.aliases.push(newAlias as Alias); + (await note.aliases).push(newAlias as Alias); await this.noteRepository.save(note); return newAlias as Alias; @@ -87,7 +87,7 @@ export class AliasService { let oldPrimaryId = ''; let newPrimaryId = ''; - for (const anAlias of note.aliases) { + for (const anAlias of await note.aliases) { // found old primary if (anAlias.primary) { oldPrimaryId = anAlias.id; @@ -134,9 +134,9 @@ export class AliasService { * @throws {PrimaryAliasDeletionForbiddenError} the primary alias can only be deleted if it's the only alias */ async removeAlias(note: Note, alias: string): Promise { - const primaryAlias = getPrimaryAlias(note); + const primaryAlias = await getPrimaryAlias(note); - if (primaryAlias === alias && note.aliases.length !== 1) { + if (primaryAlias === alias && (await note.aliases).length !== 1) { this.logger.debug( `The alias '${alias}' is the primary alias, which can only be removed if it's the only alias.`, 'removeAlias', @@ -146,10 +146,10 @@ export class AliasService { ); } - const filteredAliases = note.aliases.filter( + const filteredAliases = (await note.aliases).filter( (anAlias) => anAlias.name !== alias, ); - if (note.aliases.length === filteredAliases.length) { + if ((await note.aliases).length === filteredAliases.length) { this.logger.debug( `The alias '${alias}' is not used by this note or is the primary alias, which can't be removed.`, 'removeAlias', @@ -158,13 +158,13 @@ export class AliasService { `The alias '${alias}' is not used by this note or is the primary alias, which can't be removed.`, ); } - const aliasToDelete = note.aliases.find( + const aliasToDelete = (await note.aliases).find( (anAlias) => anAlias.name === alias, ); if (aliasToDelete !== undefined) { await this.aliasRepository.remove(aliasToDelete); } - note.aliases = filteredAliases; + note.aliases = Promise.resolve(filteredAliases); return await this.noteRepository.save(note); } diff --git a/src/notes/note.entity.ts b/src/notes/note.entity.ts index 42e697672..f099c2820 100644 --- a/src/notes/note.entity.ts +++ b/src/notes/note.entity.ts @@ -36,21 +36,21 @@ export class Note { (alias) => alias.note, { cascade: true }, // This ensures that embedded Aliases are automatically saved to the database ) - aliases: Alias[]; + aliases: Promise; @OneToMany( (_) => NoteGroupPermission, (groupPermission) => groupPermission.note, { cascade: true }, // This ensures that embedded NoteGroupPermissions are automatically saved to the database ) - groupPermissions: NoteGroupPermission[]; + groupPermissions: Promise; @OneToMany( (_) => NoteUserPermission, (userPermission) => userPermission.note, { cascade: true }, // This ensures that embedded NoteUserPermission are automatically saved to the database ) - userPermissions: NoteUserPermission[]; + userPermissions: Promise; @Column({ nullable: false, @@ -62,16 +62,16 @@ export class Note { onDelete: 'CASCADE', // This deletes the Note, when the associated User is deleted nullable: true, }) - owner: User | null; + owner: Promise; @OneToMany((_) => Revision, (revision) => revision.note, { cascade: true }) revisions: Promise; @OneToMany((_) => HistoryEntry, (historyEntry) => historyEntry.user) - historyEntries: HistoryEntry[]; + historyEntries: Promise; @OneToMany((_) => MediaUpload, (mediaUpload) => mediaUpload.note) - mediaUploads: MediaUpload[]; + mediaUploads: Promise; @Column({ nullable: true, @@ -87,7 +87,7 @@ export class Note { @ManyToMany((_) => Tag, (tag) => tag.notes, { eager: true, cascade: true }) @JoinTable() - tags: Tag[]; + tags: Promise; // eslint-disable-next-line @typescript-eslint/no-empty-function private constructor() {} @@ -101,18 +101,18 @@ export class Note { const newNote = new Note(); newNote.publicId = generatePublicId(); newNote.aliases = alias - ? [Alias.create(alias, newNote, true) as Alias] - : []; - newNote.userPermissions = []; - newNote.groupPermissions = []; + ? Promise.resolve([Alias.create(alias, newNote, true) as Alias]) + : Promise.resolve([]); + newNote.userPermissions = Promise.resolve([]); + newNote.groupPermissions = Promise.resolve([]); newNote.viewCount = 0; - newNote.owner = owner; + newNote.owner = Promise.resolve(owner); newNote.revisions = Promise.resolve([]); - newNote.historyEntries = []; - newNote.mediaUploads = []; + newNote.historyEntries = Promise.resolve([]); + newNote.mediaUploads = Promise.resolve([]); newNote.description = null; newNote.title = null; - newNote.tags = []; + newNote.tags = Promise.resolve([]); return newNote; } } diff --git a/src/notes/notes.service.spec.ts b/src/notes/notes.service.spec.ts index 686646ecd..d4a5154e6 100644 --- a/src/notes/notes.service.spec.ts +++ b/src/notes/notes.service.spec.ts @@ -168,51 +168,51 @@ describe('NotesService', () => { const revisions = await newNote.revisions; expect(revisions).toHaveLength(1); expect(revisions[0].content).toEqual(content); - expect(newNote.historyEntries).toHaveLength(0); - expect(newNote.userPermissions).toHaveLength(0); - expect(newNote.groupPermissions).toHaveLength(0); - expect(newNote.tags).toHaveLength(0); - expect(newNote.owner).toBeNull(); - expect(newNote.aliases).toHaveLength(0); + expect(await newNote.historyEntries).toHaveLength(0); + expect(await newNote.userPermissions).toHaveLength(0); + expect(await newNote.groupPermissions).toHaveLength(0); + expect(await newNote.tags).toHaveLength(0); + expect(await newNote.owner).toBeNull(); + expect(await newNote.aliases).toHaveLength(0); }); it('without alias, with owner', async () => { const newNote = await service.createNote(content, user); const revisions = await newNote.revisions; expect(revisions).toHaveLength(1); expect(revisions[0].content).toEqual(content); - expect(newNote.historyEntries).toHaveLength(1); - expect(newNote.historyEntries[0].user).toEqual(user); - expect(newNote.userPermissions).toHaveLength(0); - expect(newNote.groupPermissions).toHaveLength(0); - expect(newNote.tags).toHaveLength(0); - expect(newNote.owner).toEqual(user); - expect(newNote.aliases).toHaveLength(0); + expect(await newNote.historyEntries).toHaveLength(1); + expect((await newNote.historyEntries)[0].user).toEqual(user); + expect(await newNote.userPermissions).toHaveLength(0); + expect(await newNote.groupPermissions).toHaveLength(0); + expect(await newNote.tags).toHaveLength(0); + expect(await newNote.owner).toEqual(user); + expect(await newNote.aliases).toHaveLength(0); }); it('with alias, without owner', async () => { const newNote = await service.createNote(content, null, alias); const revisions = await newNote.revisions; expect(revisions).toHaveLength(1); expect(revisions[0].content).toEqual(content); - expect(newNote.historyEntries).toHaveLength(0); - expect(newNote.userPermissions).toHaveLength(0); - expect(newNote.groupPermissions).toHaveLength(0); - expect(newNote.tags).toHaveLength(0); - expect(newNote.owner).toBeNull(); - expect(newNote.aliases).toHaveLength(1); + expect(await newNote.historyEntries).toHaveLength(0); + expect(await newNote.userPermissions).toHaveLength(0); + expect(await newNote.groupPermissions).toHaveLength(0); + expect(await newNote.tags).toHaveLength(0); + expect(await newNote.owner).toBeNull(); + expect(await newNote.aliases).toHaveLength(1); }); it('with alias, with owner', async () => { const newNote = await service.createNote(content, user, alias); const revisions = await newNote.revisions; expect(revisions).toHaveLength(1); expect(revisions[0].content).toEqual(content); - expect(newNote.historyEntries).toHaveLength(1); - expect(newNote.historyEntries[0].user).toEqual(user); - expect(newNote.userPermissions).toHaveLength(0); - expect(newNote.groupPermissions).toHaveLength(0); - expect(newNote.tags).toHaveLength(0); - expect(newNote.owner).toEqual(user); - expect(newNote.aliases).toHaveLength(1); - expect(newNote.aliases[0].name).toEqual(alias); + expect(await newNote.historyEntries).toHaveLength(1); + expect((await newNote.historyEntries)[0].user).toEqual(user); + expect(await newNote.userPermissions).toHaveLength(0); + expect(await newNote.groupPermissions).toHaveLength(0); + expect(await newNote.tags).toHaveLength(0); + expect(await newNote.owner).toEqual(user); + expect(await newNote.aliases).toHaveLength(1); + expect((await newNote.aliases)[0].name).toEqual(alias); }); }); describe('fails:', () => { @@ -379,8 +379,8 @@ describe('NotesService', () => { sharedToUsers: [], sharedToGroups: [], }); - expect(savedNote.userPermissions).toHaveLength(0); - expect(savedNote.groupPermissions).toHaveLength(0); + expect(await savedNote.userPermissions).toHaveLength(0); + expect(await savedNote.groupPermissions).toHaveLength(0); }); it('with empty GroupPermissions and with new UserPermissions', async () => { jest @@ -393,24 +393,24 @@ describe('NotesService', () => { sharedToUsers: [userPermissionUpdate], sharedToGroups: [], }); - expect(savedNote.userPermissions).toHaveLength(1); - expect(savedNote.userPermissions[0].user.username).toEqual( + expect(await savedNote.userPermissions).toHaveLength(1); + expect((await savedNote.userPermissions)[0].user.username).toEqual( userPermissionUpdate.username, ); - expect(savedNote.userPermissions[0].canEdit).toEqual( + expect((await savedNote.userPermissions)[0].canEdit).toEqual( userPermissionUpdate.canEdit, ); - expect(savedNote.groupPermissions).toHaveLength(0); + expect(await savedNote.groupPermissions).toHaveLength(0); }); it('with empty GroupPermissions and with existing UserPermissions', async () => { const noteWithPreexistingPermissions: Note = { ...note }; - noteWithPreexistingPermissions.userPermissions = [ + noteWithPreexistingPermissions.userPermissions = Promise.resolve([ { note: noteWithPreexistingPermissions, user: user, canEdit: !userPermissionUpdate.canEdit, }, - ]; + ]); jest .spyOn(noteRepo, 'save') .mockImplementationOnce(async (entry: Note) => { @@ -421,14 +421,14 @@ describe('NotesService', () => { sharedToUsers: [userPermissionUpdate], sharedToGroups: [], }); - expect(savedNote.userPermissions).toHaveLength(1); - expect(savedNote.userPermissions[0].user.username).toEqual( + expect(await savedNote.userPermissions).toHaveLength(1); + expect((await savedNote.userPermissions)[0].user.username).toEqual( userPermissionUpdate.username, ); - expect(savedNote.userPermissions[0].canEdit).toEqual( + expect((await savedNote.userPermissions)[0].canEdit).toEqual( userPermissionUpdate.canEdit, ); - expect(savedNote.groupPermissions).toHaveLength(0); + expect(await savedNote.groupPermissions).toHaveLength(0); }); it('with new GroupPermissions and with empty UserPermissions', async () => { jest @@ -441,11 +441,11 @@ describe('NotesService', () => { sharedToUsers: [], sharedToGroups: [groupPermissionUpate], }); - expect(savedNote.userPermissions).toHaveLength(0); - expect(savedNote.groupPermissions[0].group.name).toEqual( + expect(await savedNote.userPermissions).toHaveLength(0); + expect((await savedNote.groupPermissions)[0].group.name).toEqual( groupPermissionUpate.groupname, ); - expect(savedNote.groupPermissions[0].canEdit).toEqual( + expect((await savedNote.groupPermissions)[0].canEdit).toEqual( groupPermissionUpate.canEdit, ); }); @@ -461,28 +461,28 @@ describe('NotesService', () => { sharedToUsers: [userPermissionUpdate], sharedToGroups: [groupPermissionUpate], }); - expect(savedNote.userPermissions[0].user.username).toEqual( + expect((await savedNote.userPermissions)[0].user.username).toEqual( userPermissionUpdate.username, ); - expect(savedNote.userPermissions[0].canEdit).toEqual( + expect((await savedNote.userPermissions)[0].canEdit).toEqual( userPermissionUpdate.canEdit, ); - expect(savedNote.groupPermissions[0].group.name).toEqual( + expect((await savedNote.groupPermissions)[0].group.name).toEqual( groupPermissionUpate.groupname, ); - expect(savedNote.groupPermissions[0].canEdit).toEqual( + expect((await savedNote.groupPermissions)[0].canEdit).toEqual( groupPermissionUpate.canEdit, ); }); it('with new GroupPermissions and with existing UserPermissions', async () => { const noteWithUserPermission: Note = { ...note }; - noteWithUserPermission.userPermissions = [ + noteWithUserPermission.userPermissions = Promise.resolve([ { note: noteWithUserPermission, user: user, canEdit: !userPermissionUpdate.canEdit, }, - ]; + ]); jest .spyOn(noteRepo, 'save') .mockImplementationOnce(async (entry: Note) => { @@ -497,28 +497,28 @@ describe('NotesService', () => { sharedToGroups: [groupPermissionUpate], }, ); - expect(savedNote.userPermissions[0].user.username).toEqual( + expect((await savedNote.userPermissions)[0].user.username).toEqual( userPermissionUpdate.username, ); - expect(savedNote.userPermissions[0].canEdit).toEqual( + expect((await savedNote.userPermissions)[0].canEdit).toEqual( userPermissionUpdate.canEdit, ); - expect(savedNote.groupPermissions[0].group.name).toEqual( + expect((await savedNote.groupPermissions)[0].group.name).toEqual( groupPermissionUpate.groupname, ); - expect(savedNote.groupPermissions[0].canEdit).toEqual( + expect((await savedNote.groupPermissions)[0].canEdit).toEqual( groupPermissionUpate.canEdit, ); }); it('with existing GroupPermissions and with empty UserPermissions', async () => { const noteWithPreexistingPermissions: Note = { ...note }; - noteWithPreexistingPermissions.groupPermissions = [ + noteWithPreexistingPermissions.groupPermissions = Promise.resolve([ { note: noteWithPreexistingPermissions, group: group, canEdit: !groupPermissionUpate.canEdit, }, - ]; + ]); jest.spyOn(groupRepo, 'findOne').mockResolvedValueOnce(group); jest .spyOn(noteRepo, 'save') @@ -532,23 +532,23 @@ describe('NotesService', () => { sharedToGroups: [groupPermissionUpate], }, ); - expect(savedNote.userPermissions).toHaveLength(0); - expect(savedNote.groupPermissions[0].group.name).toEqual( + expect(await savedNote.userPermissions).toHaveLength(0); + expect((await savedNote.groupPermissions)[0].group.name).toEqual( groupPermissionUpate.groupname, ); - expect(savedNote.groupPermissions[0].canEdit).toEqual( + expect((await savedNote.groupPermissions)[0].canEdit).toEqual( groupPermissionUpate.canEdit, ); }); it('with existing GroupPermissions and with new UserPermissions', async () => { const noteWithPreexistingPermissions: Note = { ...note }; - noteWithPreexistingPermissions.groupPermissions = [ + noteWithPreexistingPermissions.groupPermissions = Promise.resolve([ { note: noteWithPreexistingPermissions, group: group, canEdit: !groupPermissionUpate.canEdit, }, - ]; + ]); jest .spyOn(noteRepo, 'save') .mockImplementationOnce(async (entry: Note) => { @@ -563,35 +563,35 @@ describe('NotesService', () => { sharedToGroups: [groupPermissionUpate], }, ); - expect(savedNote.userPermissions[0].user.username).toEqual( + expect((await savedNote.userPermissions)[0].user.username).toEqual( userPermissionUpdate.username, ); - expect(savedNote.userPermissions[0].canEdit).toEqual( + expect((await savedNote.userPermissions)[0].canEdit).toEqual( userPermissionUpdate.canEdit, ); - expect(savedNote.groupPermissions[0].group.name).toEqual( + expect((await savedNote.groupPermissions)[0].group.name).toEqual( groupPermissionUpate.groupname, ); - expect(savedNote.groupPermissions[0].canEdit).toEqual( + expect((await savedNote.groupPermissions)[0].canEdit).toEqual( groupPermissionUpate.canEdit, ); }); it('with existing GroupPermissions and with existing UserPermissions', async () => { const noteWithPreexistingPermissions: Note = { ...note }; - noteWithPreexistingPermissions.groupPermissions = [ + noteWithPreexistingPermissions.groupPermissions = Promise.resolve([ { note: noteWithPreexistingPermissions, group: group, canEdit: !groupPermissionUpate.canEdit, }, - ]; - noteWithPreexistingPermissions.userPermissions = [ + ]); + noteWithPreexistingPermissions.userPermissions = Promise.resolve([ { note: noteWithPreexistingPermissions, user: user, canEdit: !userPermissionUpdate.canEdit, }, - ]; + ]); jest .spyOn(noteRepo, 'save') .mockImplementationOnce(async (entry: Note) => { @@ -606,16 +606,16 @@ describe('NotesService', () => { sharedToGroups: [groupPermissionUpate], }, ); - expect(savedNote.userPermissions[0].user.username).toEqual( + expect((await savedNote.userPermissions)[0].user.username).toEqual( userPermissionUpdate.username, ); - expect(savedNote.userPermissions[0].canEdit).toEqual( + expect((await savedNote.userPermissions)[0].canEdit).toEqual( userPermissionUpdate.canEdit, ); - expect(savedNote.groupPermissions[0].group.name).toEqual( + expect((await savedNote.groupPermissions)[0].group.name).toEqual( groupPermissionUpate.groupname, ); - expect(savedNote.groupPermissions[0].canEdit).toEqual( + expect((await savedNote.groupPermissions)[0].canEdit).toEqual( groupPermissionUpate.canEdit, ); }); @@ -653,16 +653,16 @@ describe('NotesService', () => { describe('toTagList', () => { it('works', async () => { const note = {} as Note; - note.tags = [ + note.tags = Promise.resolve([ { id: 1, name: 'testTag', notes: [note], }, - ]; - const tagList = service.toTagList(note); + ]); + const tagList = await service.toTagList(note); expect(tagList).toHaveLength(1); - expect(tagList[0]).toEqual(note.tags[0].name); + expect(tagList[0]).toEqual((await note.tags)[0].name); }); }); @@ -671,21 +671,21 @@ describe('NotesService', () => { const user = User.create('hardcoded', 'Testy') as User; const group = Group.create('testGroup', 'testGroup', false) as Group; const note = Note.create(user) as Note; - note.userPermissions = [ + note.userPermissions = Promise.resolve([ { note: note, user: user, canEdit: true, }, - ]; - note.groupPermissions = [ + ]); + note.groupPermissions = Promise.resolve([ { note: note, group: group, canEdit: true, }, - ]; - const permissions = service.toNotePermissionsDto(note); + ]); + const permissions = await service.toNotePermissionsDto(note); expect(permissions.owner).not.toEqual(null); expect(permissions.owner?.username).toEqual(user.username); expect(permissions.sharedToUsers).toHaveLength(1); @@ -740,36 +740,38 @@ describe('NotesService', () => { // @ts-ignore .mockImplementation(() => createQueryBuilder); note.publicId = 'testId'; - note.aliases = [Alias.create('testAlias', note, true) as Alias]; + note.aliases = Promise.resolve([ + Alias.create('testAlias', note, true) as Alias, + ]); note.title = 'testTitle'; note.description = 'testDescription'; - note.owner = user; - note.userPermissions = [ + note.owner = Promise.resolve(user); + note.userPermissions = Promise.resolve([ { note: note, user: user, canEdit: true, }, - ]; - note.groupPermissions = [ + ]); + note.groupPermissions = Promise.resolve([ { note: note, group: group, canEdit: true, }, - ]; - note.tags = [ + ]); + note.tags = Promise.resolve([ { id: 1, name: 'testTag', notes: [note], }, - ]; + ]); note.viewCount = 1337; const metadataDto = await service.toNoteMetadataDto(note); expect(metadataDto.id).toEqual(note.publicId); expect(metadataDto.aliases).toHaveLength(1); - expect(metadataDto.aliases[0]).toEqual(note.aliases[0].name); + expect(metadataDto.aliases[0]).toEqual((await note.aliases)[0].name); expect(metadataDto.title).toEqual(note.title); expect(metadataDto.createTime).toEqual(revisions[0].createdAt); expect(metadataDto.description).toEqual(note.description); @@ -787,7 +789,7 @@ describe('NotesService', () => { ).toEqual(group.displayName); expect(metadataDto.permissions.sharedToGroups[0].canEdit).toEqual(true); expect(metadataDto.tags).toHaveLength(1); - expect(metadataDto.tags[0]).toEqual(note.tags[0].name); + expect(metadataDto.tags[0]).toEqual((await note.tags)[0].name); expect(metadataDto.updateTime).toEqual(revisions[0].createdAt); expect(metadataDto.updateUser.username).toEqual(user.username); expect(metadataDto.viewCount).toEqual(note.viewCount); @@ -840,36 +842,38 @@ describe('NotesService', () => { // @ts-ignore .mockImplementation(() => createQueryBuilder); note.publicId = 'testId'; - note.aliases = [Alias.create('testAlias', note, true) as Alias]; + note.aliases = Promise.resolve([ + Alias.create('testAlias', note, true) as Alias, + ]); note.title = 'testTitle'; note.description = 'testDescription'; - note.owner = user; - note.userPermissions = [ + note.owner = Promise.resolve(user); + note.userPermissions = Promise.resolve([ { note: note, user: user, canEdit: true, }, - ]; - note.groupPermissions = [ + ]); + note.groupPermissions = Promise.resolve([ { note: note, group: group, canEdit: true, }, - ]; - note.tags = [ + ]); + note.tags = Promise.resolve([ { id: 1, name: 'testTag', notes: [note], }, - ]; + ]); note.viewCount = 1337; const noteDto = await service.toNoteDto(note); expect(noteDto.metadata.id).toEqual(note.publicId); expect(noteDto.metadata.aliases).toHaveLength(1); - expect(noteDto.metadata.aliases[0]).toEqual(note.aliases[0].name); + expect(noteDto.metadata.aliases[0]).toEqual((await note.aliases)[0].name); expect(noteDto.metadata.title).toEqual(note.title); expect(noteDto.metadata.createTime).toEqual(revisions[0].createdAt); expect(noteDto.metadata.description).toEqual(note.description); @@ -893,7 +897,7 @@ describe('NotesService', () => { true, ); expect(noteDto.metadata.tags).toHaveLength(1); - expect(noteDto.metadata.tags[0]).toEqual(note.tags[0].name); + expect(noteDto.metadata.tags[0]).toEqual((await note.tags)[0].name); expect(noteDto.metadata.updateTime).toEqual(revisions[0].createdAt); expect(noteDto.metadata.updateUser.username).toEqual(user.username); expect(noteDto.metadata.viewCount).toEqual(note.viewCount); diff --git a/src/notes/notes.service.ts b/src/notes/notes.service.ts index 05f8d2efc..51a24d07e 100644 --- a/src/notes/notes.service.ts +++ b/src/notes/notes.service.ts @@ -100,9 +100,9 @@ export class NotesService { Revision.create(noteContent, noteContent, newNote as Note) as Revision, ]); if (owner) { - newNote.historyEntries = [ + newNote.historyEntries = Promise.resolve([ HistoryEntry.create(owner, newNote as Note) as HistoryEntry, - ]; + ]); } try { return await this.noteRepository.save(newNote); @@ -262,8 +262,8 @@ export class NotesService { //TODO: Calculate patch revisions.push(Revision.create(noteContent, noteContent, note) as Revision); note.revisions = Promise.resolve(revisions); - note.userPermissions = []; - note.groupPermissions = []; + note.userPermissions = Promise.resolve([]); + note.groupPermissions = Promise.resolve([]); return await this.noteRepository.save(note); } @@ -298,8 +298,8 @@ export class NotesService { ); } - note.userPermissions = []; - note.groupPermissions = []; + note.userPermissions = Promise.resolve([]); + note.groupPermissions = Promise.resolve([]); // Create new userPermissions for (const newUserPermission of newPermissions.sharedToUsers) { @@ -312,7 +312,7 @@ export class NotesService { newUserPermission.canEdit, ); createdPermission.note = note; - note.userPermissions.push(createdPermission); + (await note.userPermissions).push(createdPermission); } // Create groupPermissions @@ -326,7 +326,7 @@ export class NotesService { newGroupPermission.canEdit, ); createdPermission.note = note; - note.groupPermissions.push(createdPermission); + (await note.groupPermissions).push(createdPermission); } return await this.noteRepository.save(note); @@ -348,7 +348,7 @@ export class NotesService { )[0].author.user; } // If there are no Edits, the owner is the updateUser - return note.owner; + return await note.owner; } /** @@ -356,8 +356,8 @@ export class NotesService { * @param {Note} note - the note to use * @return {string[]} string array of tags names */ - toTagList(note: Note): string[] { - return note.tags.map((tag) => tag.name); + async toTagList(note: Note): Promise { + return (await note.tags).map((tag) => tag.name); } /** @@ -365,14 +365,17 @@ export class NotesService { * @param {Note} note - the note to use * @return {NotePermissionsDto} the built NotePermissionDto */ - toNotePermissionsDto(note: Note): NotePermissionsDto { + async toNotePermissionsDto(note: Note): Promise { + const owner = await note.owner; + const userPermissions = await note.userPermissions; + const groupPermissions = await note.groupPermissions; return { - owner: note.owner ? this.usersService.toUserDto(note.owner) : null, - sharedToUsers: note.userPermissions.map((noteUserPermission) => ({ + owner: owner ? this.usersService.toUserDto(owner) : null, + sharedToUsers: userPermissions.map((noteUserPermission) => ({ user: this.usersService.toUserDto(noteUserPermission.user), canEdit: noteUserPermission.canEdit, })), - sharedToGroups: note.groupPermissions.map((noteGroupPermission) => ({ + sharedToGroups: groupPermissions.map((noteGroupPermission) => ({ group: this.groupsService.toGroupDto(noteGroupPermission.group), canEdit: noteGroupPermission.canEdit, })), @@ -389,14 +392,16 @@ export class NotesService { const updateUser = await this.calculateUpdateUser(note); return { id: note.publicId, - aliases: note.aliases.map((alias) => alias.name), - primaryAlias: getPrimaryAlias(note) ?? null, + aliases: await Promise.all( + (await note.aliases).map((alias) => alias.name), + ), + primaryAlias: (await getPrimaryAlias(note)) ?? null, title: note.title ?? '', createTime: (await this.getFirstRevision(note)).createdAt, description: note.description ?? '', editedBy: (await this.getAuthorUsers(note)).map((user) => user.username), - permissions: this.toNotePermissionsDto(note), - tags: this.toTagList(note), + permissions: await this.toNotePermissionsDto(note), + tags: await this.toTagList(note), updateTime: (await this.getLatestRevision(note)).createdAt, updateUser: updateUser ? this.usersService.toUserDto(updateUser) : null, viewCount: note.viewCount, diff --git a/src/notes/utils.spec.ts b/src/notes/utils.spec.ts index c84f6cc76..38c9ff35b 100644 --- a/src/notes/utils.spec.ts +++ b/src/notes/utils.spec.ts @@ -29,12 +29,12 @@ describe('getPrimaryAlias', () => { const user = User.create('hardcoded', 'Testy') as User; note = Note.create(user, alias) as Note; }); - it('finds correct primary alias', () => { - note.aliases.push(Alias.create('annother', note, false) as Alias); - expect(getPrimaryAlias(note)).toEqual(alias); + it('finds correct primary alias', async () => { + (await note.aliases).push(Alias.create('annother', note, false) as Alias); + expect(await getPrimaryAlias(note)).toEqual(alias); }); - it('returns undefined if there is no alias', () => { - note.aliases[0].primary = false; - expect(getPrimaryAlias(note)).toEqual(undefined); + it('returns undefined if there is no alias', async () => { + (await note.aliases)[0].primary = false; + expect(await getPrimaryAlias(note)).toEqual(undefined); }); }); diff --git a/src/notes/utils.ts b/src/notes/utils.ts index 7f6229fe7..ed87d1cfc 100644 --- a/src/notes/utils.ts +++ b/src/notes/utils.ts @@ -22,8 +22,8 @@ export function generatePublicId(): string { * Extract the primary alias from a aliases of a note * @param {Note} note - the note from which the primary alias should be extracted */ -export function getPrimaryAlias(note: Note): string | undefined { - const listWithPrimaryAlias = note.aliases.filter( +export async function getPrimaryAlias(note: Note): Promise { + const listWithPrimaryAlias = (await note.aliases).filter( (alias: Alias) => alias.primary, ); if (listWithPrimaryAlias.length !== 1) { diff --git a/src/permissions/permissions.service.spec.ts b/src/permissions/permissions.service.spec.ts index 3bf1833d7..0e60e2eb6 100644 --- a/src/permissions/permissions.service.spec.ts +++ b/src/permissions/permissions.service.spec.ts @@ -30,6 +30,7 @@ import { GuestPermission, PermissionsService } from './permissions.service'; describe('PermissionsService', () => { let permissionsService: PermissionsService; + let notes: Note[]; beforeAll(async () => { const module: TestingModule = await Test.createTestingModule({ @@ -73,6 +74,7 @@ describe('PermissionsService', () => { .useValue({}) .compile(); permissionsService = module.get(PermissionsService); + notes = await createNoteUserPermissionNotes(); }); // The two users we test with: @@ -87,16 +89,16 @@ describe('PermissionsService', () => { function createNote(owner: User): Note { const note = {} as Note; - note.userPermissions = []; - note.groupPermissions = []; - note.owner = owner; + note.userPermissions = Promise.resolve([]); + note.groupPermissions = Promise.resolve([]); + note.owner = Promise.resolve(owner); return note; } /* * Creates the permission objects for UserPermission for two users with write and with out write permission */ - function createNoteUserPermissionNotes(): Note[] { + async function createNoteUserPermissionNotes(): Promise { const note0 = createNote(user1); const note1 = createNote(user2); const note2 = createNote(user2); @@ -116,23 +118,23 @@ describe('PermissionsService', () => { noteUserPermission4.user = user2; noteUserPermission4.canEdit = true; - note1.userPermissions.push(noteUserPermission1); + (await note1.userPermissions).push(noteUserPermission1); - note2.userPermissions.push(noteUserPermission1); - note2.userPermissions.push(noteUserPermission2); + (await note2.userPermissions).push(noteUserPermission1); + (await note2.userPermissions).push(noteUserPermission2); - note3.userPermissions.push(noteUserPermission2); - note3.userPermissions.push(noteUserPermission1); + (await note3.userPermissions).push(noteUserPermission2); + (await note3.userPermissions).push(noteUserPermission1); - note4.userPermissions.push(noteUserPermission3); + (await note4.userPermissions).push(noteUserPermission3); - note5.userPermissions.push(noteUserPermission3); - note5.userPermissions.push(noteUserPermission4); + (await note5.userPermissions).push(noteUserPermission3); + (await note5.userPermissions).push(noteUserPermission4); - note6.userPermissions.push(noteUserPermission4); - note6.userPermissions.push(noteUserPermission3); + (await note6.userPermissions).push(noteUserPermission4); + (await note6.userPermissions).push(noteUserPermission3); - note7.userPermissions.push(noteUserPermission2); + (await note7.userPermissions).push(noteUserPermission2); const everybody = {} as Group; everybody.name = SpecialGroup.EVERYONE; @@ -143,7 +145,9 @@ describe('PermissionsService', () => { noteGroupPermissionRead.group = everybody; noteGroupPermissionRead.canEdit = false; noteGroupPermissionRead.note = noteEverybodyRead; - noteEverybodyRead.groupPermissions = [noteGroupPermissionRead]; + noteEverybodyRead.groupPermissions = Promise.resolve([ + noteGroupPermissionRead, + ]); const noteEverybodyWrite = createNote(user1); @@ -151,7 +155,9 @@ describe('PermissionsService', () => { noteGroupPermissionWrite.group = everybody; noteGroupPermissionWrite.canEdit = true; noteGroupPermissionWrite.note = noteEverybodyWrite; - noteEverybodyWrite.groupPermissions = [noteGroupPermissionWrite]; + noteEverybodyWrite.groupPermissions = Promise.resolve([ + noteGroupPermissionWrite, + ]); return [ note0, @@ -167,8 +173,6 @@ describe('PermissionsService', () => { ]; } - const notes = createNoteUserPermissionNotes(); - describe('mayRead works with', () => { it('Owner', async () => { permissionsService.guestPermission = GuestPermission.DENY; @@ -501,7 +505,7 @@ describe('PermissionsService', () => { let i = 0; for (const permission of permissions) { const note = createNote(user2); - note.groupPermissions = permission.permissions; + note.groupPermissions = Promise.resolve(permission.permissions); let permissionString = ''; for (const perm of permission.permissions) { permissionString += ` ${perm.group.name}:${String(perm.canEdit)}`; @@ -550,13 +554,13 @@ describe('PermissionsService', () => { }); describe('isOwner works', () => { - it('for positive case', () => { + it('for positive case', async () => { permissionsService.guestPermission = GuestPermission.DENY; - expect(permissionsService.isOwner(user1, notes[0])).toBeTruthy(); + expect(await permissionsService.isOwner(user1, notes[0])).toBeTruthy(); }); - it('for negative case', () => { + it('for negative case', async () => { permissionsService.guestPermission = GuestPermission.DENY; - expect(permissionsService.isOwner(user1, notes[1])).toBeFalsy(); + expect(await permissionsService.isOwner(user1, notes[1])).toBeFalsy(); }); }); }); diff --git a/src/permissions/permissions.service.ts b/src/permissions/permissions.service.ts index 5b059ac93..5f3cfd25e 100644 --- a/src/permissions/permissions.service.ts +++ b/src/permissions/permissions.service.ts @@ -22,9 +22,9 @@ export enum GuestPermission { export class PermissionsService { public guestPermission: GuestPermission; // TODO change to configOption async mayRead(user: User | null, note: Note): Promise { - if (this.isOwner(user, note)) return true; + if (await this.isOwner(user, note)) return true; - if (this.hasPermissionUser(user, note, false)) return true; + if (await this.hasPermissionUser(user, note, false)) return true; // noinspection RedundantIfStatementJS if (await this.hasPermissionGroup(user, note, false)) return true; @@ -33,9 +33,9 @@ export class PermissionsService { } async mayWrite(user: User | null, note: Note): Promise { - if (this.isOwner(user, note)) return true; + if (await this.isOwner(user, note)) return true; - if (this.hasPermissionUser(user, note, true)) return true; + if (await this.hasPermissionUser(user, note, true)) return true; // noinspection RedundantIfStatementJS if (await this.hasPermissionGroup(user, note, true)) return true; @@ -58,21 +58,22 @@ export class PermissionsService { return false; } - isOwner(user: User | null, note: Note): boolean { + async isOwner(user: User | null, note: Note): Promise { if (!user) return false; - if (!note.owner) return false; - return note.owner.id === user.id; + const owner = await note.owner; + if (!owner) return false; + return owner.id === user.id; } - private hasPermissionUser( + private async hasPermissionUser( user: User | null, note: Note, wantEdit: boolean, - ): boolean { + ): Promise { if (!user) { return false; } - for (const userPermission of note.userPermissions) { + for (const userPermission of await note.userPermissions) { if ( userPermission.user.id === user.id && (userPermission.canEdit || !wantEdit) @@ -99,7 +100,7 @@ export class PermissionsService { case GuestPermission.READ: guestsAllowed = !wantEdit; } - for (const groupPermission of note.groupPermissions) { + for (const groupPermission of await note.groupPermissions) { if (groupPermission.canEdit || !wantEdit) { // Handle special groups if (groupPermission.group.special) { diff --git a/src/seed.ts b/src/seed.ts index c280cacde..edd56a769 100644 --- a/src/seed.ts +++ b/src/seed.ts @@ -76,8 +76,8 @@ createConnection({ const edit = Edit.create(author, 1, 42) as Edit; revision.edits = [edit]; notes[i].revisions = Promise.all([revision]); - notes[i].userPermissions = []; - notes[i].groupPermissions = []; + notes[i].userPermissions = Promise.resolve([]); + notes[i].groupPermissions = Promise.resolve([]); user.ownedNotes = [notes[i]]; await connection.manager.save([ notes[i], @@ -99,7 +99,7 @@ createConnection({ throw new Error('Could not find freshly seeded notes. Aborting.'); } for (const note of foundNotes) { - if (!note.aliases[0]) { + if (!(await note.aliases)[0]) { throw new Error( 'Could not find alias of freshly seeded notes. Aborting.', ); @@ -111,7 +111,7 @@ createConnection({ ); } for (const note of foundNotes) { - console.log(`Created Note '${note.aliases[0].name ?? ''}'`); + console.log(`Created Note '${(await note.aliases)[0].name ?? ''}'`); } for (const user of foundUsers) { for (const note of foundNotes) { @@ -119,7 +119,7 @@ createConnection({ await connection.manager.save(historyEntry); console.log( `Created HistoryEntry for user '${user.username}' and note '${ - note.aliases[0].name ?? '' + (await note.aliases)[0].name ?? '' }'`, ); } diff --git a/test/private-api/history.e2e-spec.ts b/test/private-api/history.e2e-spec.ts index de07bee27..0da6cf547 100644 --- a/test/private-api/history.e2e-spec.ts +++ b/test/private-api/history.e2e-spec.ts @@ -69,7 +69,7 @@ describe('History', () => { note, user, ); - const entryDto = testSetup.historyService.toHistoryEntryDto(entry); + const entryDto = await testSetup.historyService.toHistoryEntryDto(entry); const response = await agent .get('/api/private/me/history') .expect('Content-Type', /json/) @@ -92,7 +92,7 @@ describe('History', () => { const pinStatus = true; const lastVisited = new Date('2020-12-01 12:23:34'); const postEntryDto = new HistoryEntryImportDto(); - postEntryDto.note = note2.aliases.filter( + postEntryDto.note = (await note2.aliases).filter( (alias) => alias.primary, )[0].name; postEntryDto.pinStatus = pinStatus; @@ -104,13 +104,15 @@ describe('History', () => { .expect(201); const userEntries = await testSetup.historyService.getEntriesByUser(user); expect(userEntries.length).toEqual(1); - expect(userEntries[0].note.aliases[0].name).toEqual( - note2.aliases[0].name, + expect((await userEntries[0].note.aliases)[0].name).toEqual( + (await note2.aliases)[0].name, ); - expect(userEntries[0].note.aliases[0].primary).toEqual( - note2.aliases[0].primary, + expect((await userEntries[0].note.aliases)[0].primary).toEqual( + (await note2.aliases)[0].primary, + ); + expect((await userEntries[0].note.aliases)[0].id).toEqual( + (await note2.aliases)[0].id, ); - expect(userEntries[0].note.aliases[0].id).toEqual(note2.aliases[0].id); expect(userEntries[0].user.username).toEqual(user.username); expect(userEntries[0].pinStatus).toEqual(pinStatus); expect(userEntries[0].updatedAt).toEqual(lastVisited); @@ -129,7 +131,7 @@ describe('History', () => { pinStatus = !previousHistory[0].pinStatus; lastVisited = new Date('2020-12-01 23:34:45'); postEntryDto = new HistoryEntryImportDto(); - postEntryDto.note = note2.aliases.filter( + postEntryDto.note = (await note2.aliases).filter( (alias) => alias.primary, )[0].name; postEntryDto.pinStatus = pinStatus; @@ -188,7 +190,8 @@ describe('History', () => { user, ); expect(entry.pinStatus).toBeFalsy(); - const alias = entry.note.aliases.filter((alias) => alias.primary)[0].name; + const alias = (await entry.note.aliases).filter((alias) => alias.primary)[0] + .name; await agent .put(`/api/private/me/history/${alias || 'undefined'}`) .send({ pinStatus: true }) @@ -201,15 +204,16 @@ describe('History', () => { it('DELETE /me/history/:note', async () => { const entry = await historyService.updateHistoryEntryTimestamp(note2, user); - const alias = entry.note.aliases.filter((alias) => alias.primary)[0].name; + const alias = (await entry.note.aliases).filter((alias) => alias.primary)[0] + .name; const entry2 = await historyService.updateHistoryEntryTimestamp(note, user); - const entryDto = historyService.toHistoryEntryDto(entry2); + const entryDto = await historyService.toHistoryEntryDto(entry2); await agent .delete(`/api/private/me/history/${alias || 'undefined'}`) .expect(200); const userEntries = await historyService.getEntriesByUser(user); expect(userEntries.length).toEqual(1); - const userEntryDto = historyService.toHistoryEntryDto(userEntries[0]); + const userEntryDto = await historyService.toHistoryEntryDto(userEntries[0]); expect(userEntryDto.identifier).toEqual(entryDto.identifier); expect(userEntryDto.title).toEqual(entryDto.title); expect(userEntryDto.tags).toEqual(entryDto.tags); diff --git a/test/public-api/me.e2e-spec.ts b/test/public-api/me.e2e-spec.ts index 2e02fc6aa..3e947933b 100644 --- a/test/public-api/me.e2e-spec.ts +++ b/test/public-api/me.e2e-spec.ts @@ -51,8 +51,9 @@ describe('Me', () => { .expect(200); const history: HistoryEntryDto[] = response.body; expect(history.length).toEqual(1); - const historyDto = - testSetup.historyService.toHistoryEntryDto(createdHistoryEntry); + const historyDto = await testSetup.historyService.toHistoryEntryDto( + createdHistoryEntry, + ); for (const historyEntry of history) { expect(historyEntry.identifier).toEqual(historyDto.identifier); expect(historyEntry.title).toEqual(historyDto.title); @@ -75,8 +76,9 @@ describe('Me', () => { .expect('Content-Type', /json/) .expect(200); const historyEntry: HistoryEntryDto = response.body; - const historyEntryDto = - testSetup.historyService.toHistoryEntryDto(createdHistoryEntry); + const historyEntryDto = await testSetup.historyService.toHistoryEntryDto( + createdHistoryEntry, + ); expect(historyEntry.identifier).toEqual(historyEntryDto.identifier); expect(historyEntry.title).toEqual(historyEntryDto.title); expect(historyEntry.tags).toEqual(historyEntryDto.tags); @@ -109,8 +111,12 @@ describe('Me', () => { expect(historyEntry.pinStatus).toEqual(true); let theEntry: HistoryEntryDto; for (const entry of history) { - if (entry.note.aliases.find((element) => element.name === noteName)) { - theEntry = testSetup.historyService.toHistoryEntryDto(entry); + if ( + (await entry.note.aliases).find( + (element) => element.name === noteName, + ) + ) { + theEntry = await testSetup.historyService.toHistoryEntryDto(entry); } } expect(theEntry.pinStatus).toEqual(true); @@ -134,7 +140,11 @@ describe('Me', () => { expect(response.body).toEqual({}); const history = await testSetup.historyService.getEntriesByUser(user); for (const entry of history) { - if (entry.note.aliases.find((element) => element.name === noteName)) { + if ( + (await entry.note.aliases).find( + (element) => element.name === noteName, + ) + ) { throw new Error('Deleted history entry still in history'); } } diff --git a/test/public-api/notes.e2e-spec.ts b/test/public-api/notes.e2e-spec.ts index bc8093e6d..d5dd441bc 100644 --- a/test/public-api/notes.e2e-spec.ts +++ b/test/public-api/notes.e2e-spec.ts @@ -200,16 +200,16 @@ describe('Notes', () => { updateNotePermission, ); const updatedNote = await testSetup.notesService.getNoteByIdOrAlias( - note.aliases.filter((alias) => alias.primary)[0].name, + (await note.aliases).filter((alias) => alias.primary)[0].name, ); - expect(updatedNote.userPermissions).toHaveLength(1); - expect(updatedNote.userPermissions[0].canEdit).toEqual( + expect(await updatedNote.userPermissions).toHaveLength(1); + expect((await updatedNote.userPermissions)[0].canEdit).toEqual( updateNotePermission.sharedToUsers[0].canEdit, ); - expect(updatedNote.userPermissions[0].user.username).toEqual( + expect((await updatedNote.userPermissions)[0].user.username).toEqual( user.username, ); - expect(updatedNote.groupPermissions).toHaveLength(0); + expect(await updatedNote.groupPermissions).toHaveLength(0); await request(testSetup.app.getHttpServer()) .delete('/api/v2/notes/test3') .expect(204);