refactor(media-upload): lazy-load relations

Signed-off-by: David Mehren <git@herrmehren.de>
This commit is contained in:
David Mehren 2021-11-18 18:47:12 +01:00
parent 4d57105853
commit 296d73c121
No known key found for this signature in database
GPG key ID: 185982BA4C42B7C3
9 changed files with 39 additions and 30 deletions

View file

@ -40,7 +40,9 @@ export class MeController {
@Get('media') @Get('media')
async getMyMedia(@RequestUser() user: User): Promise<MediaUploadDto[]> { async getMyMedia(@RequestUser() user: User): Promise<MediaUploadDto[]> {
const media = await this.mediaService.listUploadsByUser(user); const media = await this.mediaService.listUploadsByUser(user);
return media.map((media) => this.mediaService.toMediaUploadDto(media)); return await Promise.all(
media.map((media) => this.mediaService.toMediaUploadDto(media)),
);
} }
@Delete() @Delete()

View file

@ -131,7 +131,7 @@ export class MediaController {
const mediaUpload = await this.mediaService.findUploadByFilename( const mediaUpload = await this.mediaService.findUploadByFilename(
filename, filename,
); );
if (mediaUpload.user.username !== username) { if ((await mediaUpload.user).username !== username) {
this.logger.warn( this.logger.warn(
`${username} tried to delete '${filename}', but is not the owner`, `${username} tried to delete '${filename}', but is not the owner`,
'deleteMedia', 'deleteMedia',

View file

@ -72,7 +72,9 @@ export class NotesController {
@UseGuards(PermissionsGuard) @UseGuards(PermissionsGuard)
async getNotesMedia(@RequestNote() note: Note): Promise<MediaUploadDto[]> { async getNotesMedia(@RequestNote() note: Note): Promise<MediaUploadDto[]> {
const media = await this.mediaService.listUploadsByNote(note); const media = await this.mediaService.listUploadsByNote(note);
return media.map((media) => this.mediaService.toMediaUploadDto(media)); return await Promise.all(
media.map((media) => this.mediaService.toMediaUploadDto(media)),
);
} }
@Post() @Post()

View file

@ -188,6 +188,8 @@ export class MeController {
@ApiUnauthorizedResponse({ description: unauthorizedDescription }) @ApiUnauthorizedResponse({ description: unauthorizedDescription })
async getMyMedia(@RequestUser() user: User): Promise<MediaUploadDto[]> { async getMyMedia(@RequestUser() user: User): Promise<MediaUploadDto[]> {
const media = await this.mediaService.listUploadsByUser(user); const media = await this.mediaService.listUploadsByUser(user);
return media.map((media) => this.mediaService.toMediaUploadDto(media)); return await Promise.all(
media.map((media) => this.mediaService.toMediaUploadDto(media)),
);
} }
} }

View file

@ -136,7 +136,7 @@ export class MediaController {
const mediaUpload = await this.mediaService.findUploadByFilename( const mediaUpload = await this.mediaService.findUploadByFilename(
filename, filename,
); );
if (mediaUpload.user.username !== username) { if ((await mediaUpload.user).username !== username) {
this.logger.warn( this.logger.warn(
`${username} tried to delete '${filename}', but is not the owner`, `${username} tried to delete '${filename}', but is not the owner`,
'deleteMedia', 'deleteMedia',

View file

@ -298,6 +298,8 @@ export class NotesController {
@RequestNote() note: Note, @RequestNote() note: Note,
): Promise<MediaUploadDto[]> { ): Promise<MediaUploadDto[]> {
const media = await this.mediaService.listUploadsByNote(note); const media = await this.mediaService.listUploadsByNote(note);
return media.map((media) => this.mediaService.toMediaUploadDto(media)); return await Promise.all(
media.map((media) => this.mediaService.toMediaUploadDto(media)),
);
} }
} }

View file

@ -25,12 +25,12 @@ export class MediaUpload {
@ManyToOne((_) => Note, (note) => note.mediaUploads, { @ManyToOne((_) => Note, (note) => note.mediaUploads, {
nullable: true, nullable: true,
}) })
note: Note | null; note: Promise<Note | null>;
@ManyToOne((_) => User, (user) => user.mediaUploads, { @ManyToOne((_) => User, (user) => user.mediaUploads, {
nullable: false, nullable: false,
}) })
user: User; user: Promise<User>;
@Column({ @Column({
nullable: false, nullable: false,
@ -72,8 +72,8 @@ export class MediaUpload {
): Omit<MediaUpload, 'createdAt'> { ): Omit<MediaUpload, 'createdAt'> {
const upload = new MediaUpload(); const upload = new MediaUpload();
upload.id = id; upload.id = id;
upload.note = note; upload.note = Promise.resolve(note);
upload.user = user; upload.user = Promise.resolve(user);
upload.backendType = backendType; upload.backendType = backendType;
upload.backendData = null; upload.backendData = null;
upload.fileUrl = fileUrl; upload.fileUrl = fileUrl;

View file

@ -167,9 +167,9 @@ describe('MediaService', () => {
const mockMediaUploadEntry = { const mockMediaUploadEntry = {
id: 'testMediaUpload', id: 'testMediaUpload',
backendData: 'testBackendData', backendData: 'testBackendData',
user: { user: Promise.resolve({
username: 'hardcoded', username: 'hardcoded',
} as User, } as User),
} as MediaUpload; } as MediaUpload;
jest jest
.spyOn(service.mediaBackend, 'deleteFile') .spyOn(service.mediaBackend, 'deleteFile')
@ -196,15 +196,15 @@ describe('MediaService', () => {
const mockMediaUploadEntry = { const mockMediaUploadEntry = {
id: 'testMediaUpload', id: 'testMediaUpload',
backendData: backendData, backendData: backendData,
user: { user: Promise.resolve({
username: username, username: username,
} as User, } as User),
} as MediaUpload; } as MediaUpload;
jest jest
.spyOn(mediaRepo, 'findOne') .spyOn(mediaRepo, 'findOne')
.mockResolvedValueOnce(mockMediaUploadEntry); .mockResolvedValueOnce(mockMediaUploadEntry);
const mediaUpload = await service.findUploadByFilename(testFileName); const mediaUpload = await service.findUploadByFilename(testFileName);
expect(mediaUpload.user.username).toEqual(username); expect((await mediaUpload.user).username).toEqual(username);
expect(mediaUpload.backendData).toEqual(backendData); expect(mediaUpload.backendData).toEqual(backendData);
}); });
it("fails: can't find mediaUpload", async () => { it("fails: can't find mediaUpload", async () => {
@ -218,13 +218,14 @@ describe('MediaService', () => {
describe('listUploadsByUser', () => { describe('listUploadsByUser', () => {
describe('works', () => { describe('works', () => {
const username = 'hardcoded';
it('with one upload from user', async () => { it('with one upload from user', async () => {
const mockMediaUploadEntry = { const mockMediaUploadEntry = {
id: 'testMediaUpload', id: 'testMediaUpload',
backendData: 'testBackendData', backendData: 'testBackendData',
user: { user: Promise.resolve({
username: 'hardcoded', username: username,
} as User, } as User),
} as MediaUpload; } as MediaUpload;
jest jest
.spyOn(mediaRepo, 'find') .spyOn(mediaRepo, 'find')
@ -237,14 +238,14 @@ describe('MediaService', () => {
it('without uploads from user', async () => { it('without uploads from user', async () => {
jest.spyOn(mediaRepo, 'find').mockResolvedValueOnce([]); jest.spyOn(mediaRepo, 'find').mockResolvedValueOnce([]);
const mediaList = await service.listUploadsByUser({ const mediaList = await service.listUploadsByUser({
username: 'hardcoded', username: username,
} as User); } as User);
expect(mediaList).toEqual([]); expect(mediaList).toEqual([]);
}); });
it('with error (undefined as return value of find)', async () => { it('with error (undefined as return value of find)', async () => {
jest.spyOn(mediaRepo, 'find').mockResolvedValueOnce(undefined); jest.spyOn(mediaRepo, 'find').mockResolvedValueOnce(undefined);
const mediaList = await service.listUploadsByUser({ const mediaList = await service.listUploadsByUser({
username: 'hardcoded', username: username,
} as User); } as User);
expect(mediaList).toEqual([]); expect(mediaList).toEqual([]);
}); });
@ -257,9 +258,9 @@ describe('MediaService', () => {
const mockMediaUploadEntry = { const mockMediaUploadEntry = {
id: 'testMediaUpload', id: 'testMediaUpload',
backendData: 'testBackendData', backendData: 'testBackendData',
note: { note: Promise.resolve({
id: '123', id: '123',
} as Note, } as Note),
} as MediaUpload; } as MediaUpload;
jest jest
.spyOn(mediaRepo, 'find') .spyOn(mediaRepo, 'find')
@ -294,15 +295,15 @@ describe('MediaService', () => {
const mockMediaUploadEntry = { const mockMediaUploadEntry = {
id: 'testMediaUpload', id: 'testMediaUpload',
backendData: 'testBackendData', backendData: 'testBackendData',
note: mockNote, note: Promise.resolve(mockNote),
user: { user: Promise.resolve({
username: 'hardcoded', username: 'hardcoded',
} as User, } as User),
} as MediaUpload; } as MediaUpload;
jest jest
.spyOn(mediaRepo, 'save') .spyOn(mediaRepo, 'save')
.mockImplementationOnce(async (entry: MediaUpload) => { .mockImplementationOnce(async (entry: MediaUpload) => {
expect(entry.note).toBeNull(); expect(await entry.note).toBeNull();
return entry; return entry;
}); });
await service.removeNoteFromMediaUpload(mockMediaUploadEntry); await service.removeNoteFromMediaUpload(mockMediaUploadEntry);

View file

@ -181,7 +181,7 @@ export class MediaService {
'Setting note to null for mediaUpload: ' + mediaUpload.id, 'Setting note to null for mediaUpload: ' + mediaUpload.id,
'removeNoteFromMediaUpload', 'removeNoteFromMediaUpload',
); );
mediaUpload.note = null; mediaUpload.note = Promise.resolve(null);
await this.mediaUploadRepository.save(mediaUpload); await this.mediaUploadRepository.save(mediaUpload);
} }
@ -219,12 +219,12 @@ export class MediaService {
} }
} }
toMediaUploadDto(mediaUpload: MediaUpload): MediaUploadDto { async toMediaUploadDto(mediaUpload: MediaUpload): Promise<MediaUploadDto> {
return { return {
url: mediaUpload.fileUrl, url: mediaUpload.fileUrl,
noteId: mediaUpload.note?.id ?? null, noteId: (await mediaUpload.note)?.id ?? null,
createdAt: mediaUpload.createdAt, createdAt: mediaUpload.createdAt,
username: mediaUpload.user.username, username: (await mediaUpload.user).username,
}; };
} }