mirror of
https://github.com/hedgedoc/hedgedoc.git
synced 2025-04-08 07:03:34 +00:00
Merge pull request #1049 from hedgedoc/publicApi/noteMedia
This commit is contained in:
commit
6fab7583f0
5 changed files with 143 additions and 1 deletions
src
api/public/notes
media
test/public-api
|
@ -27,7 +27,10 @@ import { NoteUserPermission } from '../../../permissions/note-user-permission.en
|
|||
import { Group } from '../../../groups/group.entity';
|
||||
import { GroupsModule } from '../../../groups/groups.module';
|
||||
import { ConfigModule } from '@nestjs/config';
|
||||
import { MediaModule } from '../../../media/media.module';
|
||||
import { MediaUpload } from '../../../media/media-upload.entity';
|
||||
import appConfigMock from '../../../config/app.config.mock';
|
||||
import mediaConfigMock from '../../../config/media.config.mock';
|
||||
|
||||
describe('Notes Controller', () => {
|
||||
let controller: NotesController;
|
||||
|
@ -53,9 +56,10 @@ describe('Notes Controller', () => {
|
|||
LoggerModule,
|
||||
PermissionsModule,
|
||||
HistoryModule,
|
||||
MediaModule,
|
||||
ConfigModule.forRoot({
|
||||
isGlobal: true,
|
||||
load: [appConfigMock],
|
||||
load: [appConfigMock, mediaConfigMock],
|
||||
}),
|
||||
],
|
||||
})
|
||||
|
@ -85,6 +89,8 @@ describe('Notes Controller', () => {
|
|||
.useValue({})
|
||||
.overrideProvider(getRepositoryToken(Group))
|
||||
.useValue({})
|
||||
.overrideProvider(getRepositoryToken(MediaUpload))
|
||||
.useValue({})
|
||||
.compile();
|
||||
|
||||
controller = module.get<NotesController>(NotesController);
|
||||
|
|
|
@ -60,6 +60,8 @@ import {
|
|||
successfullyDeletedDescription,
|
||||
unauthorizedDescription,
|
||||
} from '../../utils/descriptions';
|
||||
import { MediaUploadDto } from '../../../media/media-upload.dto';
|
||||
import { MediaService } from '../../../media/media.service';
|
||||
|
||||
@ApiTags('notes')
|
||||
@ApiSecurity('token')
|
||||
|
@ -71,6 +73,7 @@ export class NotesController {
|
|||
private revisionsService: RevisionsService,
|
||||
private permissionsService: PermissionsService,
|
||||
private historyService: HistoryService,
|
||||
private mediaService: MediaService,
|
||||
) {
|
||||
this.logger.setContext(NotesController.name);
|
||||
}
|
||||
|
@ -389,4 +392,31 @@ export class NotesController {
|
|||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
@UseGuards(TokenAuthGuard)
|
||||
@Get(':noteIdOrAlias/media')
|
||||
@ApiOkResponse({
|
||||
description: 'All media uploads of the note',
|
||||
isArray: true,
|
||||
type: MediaUploadDto,
|
||||
})
|
||||
@ApiUnauthorizedResponse({ description: unauthorizedDescription })
|
||||
async getNotesMedia(
|
||||
@Req() req: Request,
|
||||
@Param('noteIdOrAlias') noteIdOrAlias: string,
|
||||
): Promise<MediaUploadDto[]> {
|
||||
try {
|
||||
const note = await this.noteService.getNoteByIdOrAlias(noteIdOrAlias);
|
||||
if (!this.permissionsService.mayRead(req.user, note)) {
|
||||
throw new UnauthorizedException('Reading note denied!');
|
||||
}
|
||||
const media = await this.mediaService.listUploadsByNote(note);
|
||||
return media.map((media) => this.mediaService.toMediaUploadDto(media));
|
||||
} catch (e) {
|
||||
if (e instanceof NotInDBError) {
|
||||
throw new NotFoundException(e.message);
|
||||
}
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -264,4 +264,40 @@ describe('MediaService', () => {
|
|||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('listUploadsByNote', () => {
|
||||
describe('works', () => {
|
||||
it('with one upload to note', async () => {
|
||||
const mockMediaUploadEntry = {
|
||||
id: 'testMediaUpload',
|
||||
backendData: 'testBackendData',
|
||||
note: {
|
||||
id: '123',
|
||||
} as Note,
|
||||
} as MediaUpload;
|
||||
jest
|
||||
.spyOn(mediaRepo, 'find')
|
||||
.mockResolvedValueOnce([mockMediaUploadEntry]);
|
||||
const mediaList = await service.listUploadsByNote({
|
||||
id: '123',
|
||||
} as Note);
|
||||
expect(mediaList).toEqual([mockMediaUploadEntry]);
|
||||
});
|
||||
|
||||
it('without uploads to note', async () => {
|
||||
jest.spyOn(mediaRepo, 'find').mockResolvedValueOnce([]);
|
||||
const mediaList = await service.listUploadsByNote({
|
||||
id: '123',
|
||||
} as Note);
|
||||
expect(mediaList).toEqual([]);
|
||||
});
|
||||
it('with error (undefined as return value of find)', async () => {
|
||||
jest.spyOn(mediaRepo, 'find').mockResolvedValueOnce(undefined);
|
||||
const mediaList = await service.listUploadsByNote({
|
||||
id: '123',
|
||||
} as Note);
|
||||
expect(mediaList).toEqual([]);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -24,6 +24,7 @@ import { AzureBackend } from './backends/azure-backend';
|
|||
import { ImgurBackend } from './backends/imgur-backend';
|
||||
import { User } from '../users/user.entity';
|
||||
import { MediaUploadDto } from './media-upload.dto';
|
||||
import { Note } from '../notes/note.entity';
|
||||
|
||||
@Injectable()
|
||||
export class MediaService {
|
||||
|
@ -175,6 +176,23 @@ export class MediaService {
|
|||
return mediaUploads;
|
||||
}
|
||||
|
||||
/**
|
||||
* @async
|
||||
* List all uploads by a specific note
|
||||
* @param {Note} note - the specific user
|
||||
* @return {MediaUpload[]} arary of media uploads owned by the user
|
||||
*/
|
||||
async listUploadsByNote(note: Note): Promise<MediaUpload[]> {
|
||||
const mediaUploads = await this.mediaUploadRepository.find({
|
||||
where: { note: note },
|
||||
relations: ['user', 'note'],
|
||||
});
|
||||
if (mediaUploads === undefined) {
|
||||
return [];
|
||||
}
|
||||
return mediaUploads;
|
||||
}
|
||||
|
||||
private chooseBackendType(): BackendType {
|
||||
switch (this.mediaConfig.backend.use) {
|
||||
case 'filesystem':
|
||||
|
|
|
@ -29,11 +29,15 @@ import { MockAuthGuard } from '../../src/auth/mock-auth.guard';
|
|||
import { UsersService } from '../../src/users/users.service';
|
||||
import { User } from '../../src/users/user.entity';
|
||||
import { UsersModule } from '../../src/users/users.module';
|
||||
import { promises as fs } from 'fs';
|
||||
import { MediaService } from '../../src/media/media.service';
|
||||
|
||||
describe('Notes', () => {
|
||||
let app: INestApplication;
|
||||
let notesService: NotesService;
|
||||
let mediaService: MediaService;
|
||||
let user: User;
|
||||
let user2: User;
|
||||
let content: string;
|
||||
let forbiddenNoteId: string;
|
||||
|
||||
|
@ -69,8 +73,10 @@ describe('Notes', () => {
|
|||
app = moduleRef.createNestApplication();
|
||||
await app.init();
|
||||
notesService = moduleRef.get(NotesService);
|
||||
mediaService = moduleRef.get(MediaService);
|
||||
const userService = moduleRef.get(UsersService);
|
||||
user = await userService.createUser('hardcoded', 'Testy');
|
||||
user2 = await userService.createUser('hardcoded2', 'Max Mustermann');
|
||||
content = 'This is a test note.';
|
||||
});
|
||||
|
||||
|
@ -322,6 +328,52 @@ describe('Notes', () => {
|
|||
});
|
||||
});
|
||||
|
||||
describe('GET /notes/{note}/media', () => {
|
||||
it('works', async () => {
|
||||
const note = await notesService.createNote(content, 'test9', user);
|
||||
const extraNote = await notesService.createNote(content, 'test10', user);
|
||||
const httpServer = app.getHttpServer();
|
||||
const response = await request(httpServer)
|
||||
.get(`/notes/${note.id}/media/`)
|
||||
.expect('Content-Type', /json/)
|
||||
.expect(200);
|
||||
expect(response.body).toHaveLength(0);
|
||||
|
||||
const testImage = await fs.readFile('test/public-api/fixtures/test.png');
|
||||
const url0 = await mediaService.saveFile(testImage, 'hardcoded', note.id);
|
||||
const url1 = await mediaService.saveFile(
|
||||
testImage,
|
||||
'hardcoded',
|
||||
extraNote.id,
|
||||
);
|
||||
|
||||
const responseAfter = await request(httpServer)
|
||||
.get(`/notes/${note.id}/media/`)
|
||||
.expect('Content-Type', /json/)
|
||||
.expect(200);
|
||||
expect(responseAfter.body).toHaveLength(1);
|
||||
expect(responseAfter.body[0].url).toEqual(url0);
|
||||
expect(responseAfter.body[0].url).not.toEqual(url1);
|
||||
});
|
||||
it('fails, when note does not exist', async () => {
|
||||
await request(app.getHttpServer())
|
||||
.get(`/notes/i_dont_exist/media/`)
|
||||
.expect('Content-Type', /json/)
|
||||
.expect(404);
|
||||
});
|
||||
it("fails, when user can't read note", async () => {
|
||||
const note = await notesService.createNote(
|
||||
'This is a test note.',
|
||||
'test11',
|
||||
user2,
|
||||
);
|
||||
await request(app.getHttpServer())
|
||||
.get(`/notes/${note.id}/media/`)
|
||||
.expect('Content-Type', /json/)
|
||||
.expect(401);
|
||||
});
|
||||
});
|
||||
|
||||
afterAll(async () => {
|
||||
await app.close();
|
||||
});
|
||||
|
|
Loading…
Add table
Reference in a new issue