mirror of
https://github.com/hedgedoc/hedgedoc.git
synced 2024-11-28 14:10:57 -05:00
Add GET /me/media
Returns all media files uploaded by the authenticated user. Signed-off-by: Yannick Bungers <git@innay.de>
This commit is contained in:
parent
7a7b3d3a50
commit
f47d85b301
7 changed files with 174 additions and 10 deletions
|
@ -908,19 +908,21 @@ components:
|
|||
MediaUpload:
|
||||
type: object
|
||||
properties:
|
||||
note:
|
||||
type: string
|
||||
description: ID of the note the file was uploaded to
|
||||
user:
|
||||
type: string
|
||||
description: username of the user who uploaded the file
|
||||
url:
|
||||
type: string
|
||||
description: URL of the file
|
||||
owningNote:
|
||||
type: string
|
||||
description: ID of the note the file was uploaded to
|
||||
createdAt:
|
||||
type: string
|
||||
format: date-time
|
||||
description: Date when the file was upladed
|
||||
owningUser:
|
||||
type: string
|
||||
description: username of the user who uploaded the file
|
||||
|
||||
|
||||
examples:
|
||||
markdownExample:
|
||||
value: '# Some header\nSome normal text. **Some bold text**'
|
||||
|
|
|
@ -23,7 +23,10 @@ import { HistoryEntry } from '../../../history/history-entry.entity';
|
|||
import { NoteGroupPermission } from '../../../permissions/note-group-permission.entity';
|
||||
import { NoteUserPermission } from '../../../permissions/note-user-permission.entity';
|
||||
import { Group } from '../../../groups/group.entity';
|
||||
import { MediaModule } from '../../../media/media.module';
|
||||
import { MediaUpload } from '../../../media/media-upload.entity';
|
||||
import { ConfigModule } from '@nestjs/config';
|
||||
import mediaConfigMock from '../../../config/media.config.mock';
|
||||
import appConfigMock from '../../../config/app.config.mock';
|
||||
|
||||
describe('Me Controller', () => {
|
||||
|
@ -33,14 +36,19 @@ describe('Me Controller', () => {
|
|||
const module: TestingModule = await Test.createTestingModule({
|
||||
controllers: [MeController],
|
||||
imports: [
|
||||
UsersModule,
|
||||
HistoryModule,
|
||||
NotesModule,
|
||||
LoggerModule,
|
||||
ConfigModule.forRoot({
|
||||
isGlobal: true,
|
||||
load: [mediaConfigMock],
|
||||
}),
|
||||
ConfigModule.forRoot({
|
||||
isGlobal: true,
|
||||
load: [appConfigMock],
|
||||
}),
|
||||
UsersModule,
|
||||
HistoryModule,
|
||||
NotesModule,
|
||||
LoggerModule,
|
||||
MediaModule,
|
||||
],
|
||||
})
|
||||
.overrideProvider(getRepositoryToken(User))
|
||||
|
@ -67,6 +75,8 @@ describe('Me Controller', () => {
|
|||
.useValue({})
|
||||
.overrideProvider(getRepositoryToken(Group))
|
||||
.useValue({})
|
||||
.overrideProvider(getRepositoryToken(MediaUpload))
|
||||
.useValue({})
|
||||
.compile();
|
||||
|
||||
controller = module.get<MeController>(MeController);
|
||||
|
|
|
@ -28,6 +28,9 @@ import { HistoryEntryDto } from '../../../history/history-entry.dto';
|
|||
import { UserInfoDto } from '../../../users/user-info.dto';
|
||||
import { NotInDBError } from '../../../errors/errors';
|
||||
import { Request } from 'express';
|
||||
import { MediaService } from '../../../media/media.service';
|
||||
import { MediaUploadUrlDto } from '../../../media/media-upload-url.dto';
|
||||
import { MediaUploadDto } from '../../../media/media-upload.dto';
|
||||
|
||||
@ApiTags('me')
|
||||
@ApiSecurity('token')
|
||||
|
@ -38,6 +41,7 @@ export class MeController {
|
|||
private usersService: UsersService,
|
||||
private historyService: HistoryService,
|
||||
private notesService: NotesService,
|
||||
private mediaService: MediaService,
|
||||
) {
|
||||
this.logger.setContext(MeController.name);
|
||||
}
|
||||
|
@ -129,4 +133,11 @@ export class MeController {
|
|||
(await notes).map((note) => this.notesService.toNoteMetadataDto(note)),
|
||||
);
|
||||
}
|
||||
|
||||
@UseGuards(TokenAuthGuard)
|
||||
@Get('media')
|
||||
async getMyMedia(@Req() req: Request): Promise<MediaUploadDto[]> {
|
||||
const media = await this.mediaService.listUploadsByUser(req.user);
|
||||
return media.map((media) => this.mediaService.toMediaUploadDto(media));
|
||||
}
|
||||
}
|
||||
|
|
37
src/media/media-upload.dto.ts
Normal file
37
src/media/media-upload.dto.ts
Normal file
|
@ -0,0 +1,37 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file)
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
import { IsDate, IsString } from 'class-validator';
|
||||
|
||||
export class MediaUploadDto {
|
||||
/**
|
||||
* The link to the media file.
|
||||
* @example "https://example.com/uploads/testfile123.jpg"
|
||||
*/
|
||||
@IsString()
|
||||
url: string;
|
||||
|
||||
/**
|
||||
* The noteId of the note to which the uploaded file is linked to.
|
||||
* @example "noteId" TODO how looks a note id?
|
||||
*/
|
||||
@IsString()
|
||||
noteId: string;
|
||||
|
||||
/**
|
||||
* The date when the upload objects was created.
|
||||
* @example "2020-12-01 12:23:34"
|
||||
*/
|
||||
@IsDate()
|
||||
createdAt: Date;
|
||||
|
||||
/**
|
||||
* The userName of the user which uploaded the media file.
|
||||
* @example "testuser5"
|
||||
*/
|
||||
@IsString()
|
||||
userName: string;
|
||||
}
|
|
@ -229,4 +229,39 @@ describe('MediaService', () => {
|
|||
}
|
||||
});
|
||||
});
|
||||
|
||||
describe('listUploadsByUser', () => {
|
||||
describe('works', () => {
|
||||
it('with one upload from user', async () => {
|
||||
const mockMediaUploadEntry = {
|
||||
id: 'testMediaUpload',
|
||||
backendData: 'testBackendData',
|
||||
user: {
|
||||
userName: 'hardcoded',
|
||||
} as User,
|
||||
} as MediaUpload;
|
||||
jest
|
||||
.spyOn(mediaRepo, 'find')
|
||||
.mockResolvedValueOnce([mockMediaUploadEntry]);
|
||||
expect(
|
||||
await service.listUploadsByUser({ userName: 'hardcoded' } as User),
|
||||
).toEqual([mockMediaUploadEntry]);
|
||||
});
|
||||
|
||||
it('without uploads from user', async () => {
|
||||
jest.spyOn(mediaRepo, 'find').mockResolvedValueOnce([]);
|
||||
const mediaList = await service.listUploadsByUser({
|
||||
userName: 'hardcoded',
|
||||
} as User);
|
||||
expect(mediaList).toEqual([]);
|
||||
});
|
||||
it('with error (undefined as return value of find)', async () => {
|
||||
jest.spyOn(mediaRepo, 'find').mockResolvedValueOnce(undefined);
|
||||
const mediaList = await service.listUploadsByUser({
|
||||
userName: 'hardcoded',
|
||||
} as User);
|
||||
expect(mediaList).toEqual([]);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -22,6 +22,8 @@ import { MediaUploadUrlDto } from './media-upload-url.dto';
|
|||
import { S3Backend } from './backends/s3-backend';
|
||||
import { AzureBackend } from './backends/azure-backend';
|
||||
import { ImgurBackend } from './backends/imgur-backend';
|
||||
import { User } from '../users/user.entity';
|
||||
import { MediaUploadDto } from './media-upload.dto';
|
||||
|
||||
@Injectable()
|
||||
export class MediaService {
|
||||
|
@ -157,6 +159,23 @@ export class MediaService {
|
|||
return mediaUpload;
|
||||
}
|
||||
|
||||
/**
|
||||
* @async
|
||||
* List all uploads by a specific user
|
||||
* @param {User} user - the specific user
|
||||
* @return {MediaUpload[]} arary of media uploads owned by the user
|
||||
*/
|
||||
async listUploadsByUser(user: User): Promise<MediaUpload[]> {
|
||||
const mediaUploads = await this.mediaUploadRepository.find({
|
||||
where: { user: user },
|
||||
relations: ['user', 'note'],
|
||||
});
|
||||
if (mediaUploads === undefined) {
|
||||
return [];
|
||||
}
|
||||
return mediaUploads;
|
||||
}
|
||||
|
||||
private chooseBackendType(): BackendType {
|
||||
switch (this.mediaConfig.backend.use) {
|
||||
case 'filesystem':
|
||||
|
@ -183,6 +202,15 @@ export class MediaService {
|
|||
}
|
||||
}
|
||||
|
||||
toMediaUploadDto(mediaUpload: MediaUpload): MediaUploadDto {
|
||||
return {
|
||||
url: mediaUpload.fileUrl,
|
||||
noteId: mediaUpload.note.id,
|
||||
createdAt: mediaUpload.createdAt,
|
||||
userName: mediaUpload.user.userName,
|
||||
};
|
||||
}
|
||||
|
||||
toMediaUploadUrlDto(url: string): MediaUploadUrlDto {
|
||||
return {
|
||||
link: url,
|
||||
|
|
|
@ -33,6 +33,9 @@ import { ConfigModule } from '@nestjs/config';
|
|||
import mediaConfigMock from '../../src/config/media.config.mock';
|
||||
import appConfigMock from '../../src/config/app.config.mock';
|
||||
import { User } from '../../src/users/user.entity';
|
||||
import { MediaService } from '../../src/media/media.service';
|
||||
import { MediaModule } from '../../src/media/media.module';
|
||||
import { promises as fs } from 'fs';
|
||||
import { NoteMetadataDto } from '../../src/notes/note-metadata.dto';
|
||||
|
||||
// TODO Tests have to be reworked using UserService functions
|
||||
|
@ -42,6 +45,7 @@ describe('Notes', () => {
|
|||
let historyService: HistoryService;
|
||||
let notesService: NotesService;
|
||||
let userService: UsersService;
|
||||
let mediaService: MediaService;
|
||||
let user: User;
|
||||
|
||||
beforeAll(async () => {
|
||||
|
@ -66,6 +70,7 @@ describe('Notes', () => {
|
|||
AuthModule,
|
||||
UsersModule,
|
||||
HistoryModule,
|
||||
MediaModule,
|
||||
],
|
||||
})
|
||||
.overrideGuard(TokenAuthGuard)
|
||||
|
@ -75,6 +80,7 @@ describe('Notes', () => {
|
|||
notesService = moduleRef.get(NotesService);
|
||||
historyService = moduleRef.get(HistoryService);
|
||||
userService = moduleRef.get(UsersService);
|
||||
mediaService = moduleRef.get(MediaService);
|
||||
user = await userService.createUser('hardcoded', 'Testy');
|
||||
await app.init();
|
||||
});
|
||||
|
@ -222,6 +228,41 @@ describe('Notes', () => {
|
|||
expect(noteMetaDtos[0].updateUser.userName).toEqual(user.userName);
|
||||
});
|
||||
|
||||
it('GET /me/media', async () => {
|
||||
const note1 = await notesService.createNote(
|
||||
'This is a test note.',
|
||||
'test8',
|
||||
await userService.getUserByUsername('hardcoded'),
|
||||
);
|
||||
const note2 = await notesService.createNote(
|
||||
'This is a test note.',
|
||||
'test9',
|
||||
await userService.getUserByUsername('hardcoded'),
|
||||
);
|
||||
const httpServer = app.getHttpServer();
|
||||
const response1 = await request(httpServer)
|
||||
.get('/me/media/')
|
||||
.expect('Content-Type', /json/)
|
||||
.expect(200);
|
||||
expect(response1.body).toHaveLength(0);
|
||||
|
||||
const testImage = await fs.readFile('test/public-api/fixtures/test.png');
|
||||
const url0 = await mediaService.saveFile(testImage, 'hardcoded', note1.id);
|
||||
const url1 = await mediaService.saveFile(testImage, 'hardcoded', note1.id);
|
||||
const url2 = await mediaService.saveFile(testImage, 'hardcoded', note2.id);
|
||||
const url3 = await mediaService.saveFile(testImage, 'hardcoded', note2.id);
|
||||
|
||||
const response = await request(httpServer)
|
||||
.get('/me/media/')
|
||||
.expect('Content-Type', /json/)
|
||||
.expect(200);
|
||||
expect(response.body).toHaveLength(4);
|
||||
expect(response.body[0].url).toEqual(url0);
|
||||
expect(response.body[1].url).toEqual(url1);
|
||||
expect(response.body[2].url).toEqual(url2);
|
||||
expect(response.body[3].url).toEqual(url3);
|
||||
});
|
||||
|
||||
afterAll(async () => {
|
||||
await app.close();
|
||||
});
|
||||
|
|
Loading…
Reference in a new issue