mirror of
https://github.com/hedgedoc/hedgedoc.git
synced 2025-05-18 17:25:16 -04:00
Merge pull request #1589 from hedgedoc/enhancement/getnotepipe
This commit is contained in:
commit
fe35f507b6
19 changed files with 237 additions and 394 deletions
|
@ -22,6 +22,8 @@ import { HistoryEntryUpdateDto } from '../../../../history/history-entry-update.
|
||||||
import { HistoryEntryDto } from '../../../../history/history-entry.dto';
|
import { HistoryEntryDto } from '../../../../history/history-entry.dto';
|
||||||
import { HistoryService } from '../../../../history/history.service';
|
import { HistoryService } from '../../../../history/history.service';
|
||||||
import { ConsoleLoggerService } from '../../../../logger/console-logger.service';
|
import { ConsoleLoggerService } from '../../../../logger/console-logger.service';
|
||||||
|
import { GetNotePipe } from '../../../../notes/get-note.pipe';
|
||||||
|
import { Note } from '../../../../notes/note.entity';
|
||||||
import { UsersService } from '../../../../users/users.service';
|
import { UsersService } from '../../../../users/users.service';
|
||||||
|
|
||||||
@ApiTags('history')
|
@ApiTags('history')
|
||||||
|
@ -84,14 +86,14 @@ export class HistoryController {
|
||||||
|
|
||||||
@Put(':note')
|
@Put(':note')
|
||||||
async updateHistoryEntry(
|
async updateHistoryEntry(
|
||||||
@Param('note') noteId: string,
|
@Param('note', GetNotePipe) note: Note,
|
||||||
@Body() entryUpdateDto: HistoryEntryUpdateDto,
|
@Body() entryUpdateDto: HistoryEntryUpdateDto,
|
||||||
): Promise<HistoryEntryDto> {
|
): Promise<HistoryEntryDto> {
|
||||||
try {
|
try {
|
||||||
// ToDo: use actual user here
|
// ToDo: use actual user here
|
||||||
const user = await this.userService.getUserByUsername('hardcoded');
|
const user = await this.userService.getUserByUsername('hardcoded');
|
||||||
const newEntry = await this.historyService.updateHistoryEntry(
|
const newEntry = await this.historyService.updateHistoryEntry(
|
||||||
noteId,
|
note,
|
||||||
user,
|
user,
|
||||||
entryUpdateDto,
|
entryUpdateDto,
|
||||||
);
|
);
|
||||||
|
@ -105,11 +107,13 @@ export class HistoryController {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Delete(':note')
|
@Delete(':note')
|
||||||
async deleteHistoryEntry(@Param('note') noteId: string): Promise<void> {
|
async deleteHistoryEntry(
|
||||||
|
@Param('note', GetNotePipe) note: Note,
|
||||||
|
): Promise<void> {
|
||||||
try {
|
try {
|
||||||
// ToDo: use actual user here
|
// ToDo: use actual user here
|
||||||
const user = await this.userService.getUserByUsername('hardcoded');
|
const user = await this.userService.getUserByUsername('hardcoded');
|
||||||
await this.historyService.deleteHistoryEntry(noteId, user);
|
await this.historyService.deleteHistoryEntry(note, user);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
if (e instanceof NotInDBError) {
|
if (e instanceof NotInDBError) {
|
||||||
throw new NotFoundException(e.message);
|
throw new NotFoundException(e.message);
|
||||||
|
|
|
@ -24,12 +24,18 @@ import { ConsoleLoggerService } from '../../../logger/console-logger.service';
|
||||||
import { MediaUploadUrlDto } from '../../../media/media-upload-url.dto';
|
import { MediaUploadUrlDto } from '../../../media/media-upload-url.dto';
|
||||||
import { MediaService } from '../../../media/media.service';
|
import { MediaService } from '../../../media/media.service';
|
||||||
import { MulterFile } from '../../../media/multer-file.interface';
|
import { MulterFile } from '../../../media/multer-file.interface';
|
||||||
|
import { Note } from '../../../notes/note.entity';
|
||||||
|
import { NotesService } from '../../../notes/notes.service';
|
||||||
|
import { User } from '../../../users/user.entity';
|
||||||
|
import { UsersService } from '../../../users/users.service';
|
||||||
|
|
||||||
@Controller('media')
|
@Controller('media')
|
||||||
export class MediaController {
|
export class MediaController {
|
||||||
constructor(
|
constructor(
|
||||||
private readonly logger: ConsoleLoggerService,
|
private readonly logger: ConsoleLoggerService,
|
||||||
private mediaService: MediaService,
|
private mediaService: MediaService,
|
||||||
|
private userService: UsersService,
|
||||||
|
private noteService: NotesService,
|
||||||
) {
|
) {
|
||||||
this.logger.setContext(MediaController.name);
|
this.logger.setContext(MediaController.name);
|
||||||
}
|
}
|
||||||
|
@ -42,17 +48,15 @@ export class MediaController {
|
||||||
@Headers('HedgeDoc-Note') noteId: string,
|
@Headers('HedgeDoc-Note') noteId: string,
|
||||||
): Promise<MediaUploadUrlDto> {
|
): Promise<MediaUploadUrlDto> {
|
||||||
// ToDo: Get real userName
|
// ToDo: Get real userName
|
||||||
const username = 'hardcoded';
|
const user: User = await this.userService.getUserByUsername('hardcoded');
|
||||||
this.logger.debug(
|
|
||||||
`Recieved filename '${file.originalname}' for note '${noteId}' from user '${username}'`,
|
|
||||||
'uploadMedia',
|
|
||||||
);
|
|
||||||
try {
|
try {
|
||||||
const url = await this.mediaService.saveFile(
|
// TODO: Move getting the Note object into a decorator
|
||||||
file.buffer,
|
const note: Note = await this.noteService.getNoteByIdOrAlias(noteId);
|
||||||
username,
|
this.logger.debug(
|
||||||
noteId,
|
`Recieved filename '${file.originalname}' for note '${noteId}' from user '${user.userName}'`,
|
||||||
|
'uploadMedia',
|
||||||
);
|
);
|
||||||
|
const url = await this.mediaService.saveFile(file.buffer, user, note);
|
||||||
return this.mediaService.toMediaUploadUrlDto(url);
|
return this.mediaService.toMediaUploadUrlDto(url);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
if (e instanceof ClientError || e instanceof NotInDBError) {
|
if (e instanceof ClientError || e instanceof NotInDBError) {
|
||||||
|
|
|
@ -25,6 +25,7 @@ import { HistoryService } from '../../../history/history.service';
|
||||||
import { ConsoleLoggerService } from '../../../logger/console-logger.service';
|
import { ConsoleLoggerService } from '../../../logger/console-logger.service';
|
||||||
import { MediaUploadDto } from '../../../media/media-upload.dto';
|
import { MediaUploadDto } from '../../../media/media-upload.dto';
|
||||||
import { MediaService } from '../../../media/media.service';
|
import { MediaService } from '../../../media/media.service';
|
||||||
|
import { GetNotePipe } from '../../../notes/get-note.pipe';
|
||||||
import { NoteDto } from '../../../notes/note.dto';
|
import { NoteDto } from '../../../notes/note.dto';
|
||||||
import { Note } from '../../../notes/note.entity';
|
import { Note } from '../../../notes/note.entity';
|
||||||
import { NoteMediaDeletionDto } from '../../../notes/note.media-deletion.dto';
|
import { NoteMediaDeletionDto } from '../../../notes/note.media-deletion.dto';
|
||||||
|
@ -52,37 +53,24 @@ export class NotesController {
|
||||||
|
|
||||||
@Get(':noteIdOrAlias')
|
@Get(':noteIdOrAlias')
|
||||||
async getNote(
|
async getNote(
|
||||||
@Param('noteIdOrAlias') noteIdOrAlias: string,
|
@Param('noteIdOrAlias', GetNotePipe) note: Note,
|
||||||
): Promise<NoteDto> {
|
): Promise<NoteDto> {
|
||||||
// ToDo: use actual user here
|
// ToDo: use actual user here
|
||||||
const user = await this.userService.getUserByUsername('hardcoded');
|
const user = await this.userService.getUserByUsername('hardcoded');
|
||||||
let note: Note;
|
|
||||||
try {
|
|
||||||
note = await this.noteService.getNoteByIdOrAlias(noteIdOrAlias);
|
|
||||||
} catch (e) {
|
|
||||||
if (e instanceof NotInDBError) {
|
|
||||||
throw new NotFoundException(e.message);
|
|
||||||
}
|
|
||||||
if (e instanceof ForbiddenIdError) {
|
|
||||||
throw new BadRequestException(e.message);
|
|
||||||
}
|
|
||||||
throw e;
|
|
||||||
}
|
|
||||||
if (!this.permissionsService.mayRead(user, note)) {
|
if (!this.permissionsService.mayRead(user, note)) {
|
||||||
throw new UnauthorizedException('Reading note denied!');
|
throw new UnauthorizedException('Reading note denied!');
|
||||||
}
|
}
|
||||||
await this.historyService.createOrUpdateHistoryEntry(note, user);
|
await this.historyService.updateHistoryEntryTimestamp(note, user);
|
||||||
return await this.noteService.toNoteDto(note);
|
return await this.noteService.toNoteDto(note);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Get(':noteIdOrAlias/media')
|
@Get(':noteIdOrAlias/media')
|
||||||
async getNotesMedia(
|
async getNotesMedia(
|
||||||
@Param('noteIdOrAlias') noteIdOrAlias: string,
|
@Param('noteIdOrAlias', GetNotePipe) note: Note,
|
||||||
): Promise<MediaUploadDto[]> {
|
): Promise<MediaUploadDto[]> {
|
||||||
try {
|
try {
|
||||||
// ToDo: use actual user here
|
// ToDo: use actual user here
|
||||||
const user = await this.userService.getUserByUsername('hardcoded');
|
const user = await this.userService.getUserByUsername('hardcoded');
|
||||||
const note = await this.noteService.getNoteByIdOrAlias(noteIdOrAlias);
|
|
||||||
if (!this.permissionsService.mayRead(user, note)) {
|
if (!this.permissionsService.mayRead(user, note)) {
|
||||||
throw new UnauthorizedException('Reading note denied!');
|
throw new UnauthorizedException('Reading note denied!');
|
||||||
}
|
}
|
||||||
|
@ -141,13 +129,12 @@ export class NotesController {
|
||||||
@Delete(':noteIdOrAlias')
|
@Delete(':noteIdOrAlias')
|
||||||
@HttpCode(204)
|
@HttpCode(204)
|
||||||
async deleteNote(
|
async deleteNote(
|
||||||
@Param('noteIdOrAlias') noteIdOrAlias: string,
|
@Param('noteIdOrAlias', GetNotePipe) note: Note,
|
||||||
@Body() noteMediaDeletionDto: NoteMediaDeletionDto,
|
@Body() noteMediaDeletionDto: NoteMediaDeletionDto,
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
try {
|
try {
|
||||||
// ToDo: use actual user here
|
// ToDo: use actual user here
|
||||||
const user = await this.userService.getUserByUsername('hardcoded');
|
const user = await this.userService.getUserByUsername('hardcoded');
|
||||||
const note = await this.noteService.getNoteByIdOrAlias(noteIdOrAlias);
|
|
||||||
if (!this.permissionsService.isOwner(user, note)) {
|
if (!this.permissionsService.isOwner(user, note)) {
|
||||||
throw new UnauthorizedException('Deleting note denied!');
|
throw new UnauthorizedException('Deleting note denied!');
|
||||||
}
|
}
|
||||||
|
@ -159,29 +146,25 @@ export class NotesController {
|
||||||
await this.mediaService.removeNoteFromMediaUpload(mediaUpload);
|
await this.mediaService.removeNoteFromMediaUpload(mediaUpload);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
this.logger.debug('Deleting note: ' + noteIdOrAlias, 'deleteNote');
|
this.logger.debug('Deleting note: ' + note.id, 'deleteNote');
|
||||||
await this.noteService.deleteNote(note);
|
await this.noteService.deleteNote(note);
|
||||||
this.logger.debug('Successfully deleted ' + noteIdOrAlias, 'deleteNote');
|
this.logger.debug('Successfully deleted ' + note.id, 'deleteNote');
|
||||||
return;
|
return;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
if (e instanceof NotInDBError) {
|
if (e instanceof NotInDBError) {
|
||||||
throw new NotFoundException(e.message);
|
throw new NotFoundException(e.message);
|
||||||
}
|
}
|
||||||
if (e instanceof ForbiddenIdError) {
|
|
||||||
throw new BadRequestException(e.message);
|
|
||||||
}
|
|
||||||
throw e;
|
throw e;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Get(':noteIdOrAlias/revisions')
|
@Get(':noteIdOrAlias/revisions')
|
||||||
async getNoteRevisions(
|
async getNoteRevisions(
|
||||||
@Param('noteIdOrAlias') noteIdOrAlias: string,
|
@Param('noteIdOrAlias', GetNotePipe) note: Note,
|
||||||
): Promise<RevisionMetadataDto[]> {
|
): Promise<RevisionMetadataDto[]> {
|
||||||
try {
|
try {
|
||||||
// ToDo: use actual user here
|
// ToDo: use actual user here
|
||||||
const user = await this.userService.getUserByUsername('hardcoded');
|
const user = await this.userService.getUserByUsername('hardcoded');
|
||||||
const note = await this.noteService.getNoteByIdOrAlias(noteIdOrAlias);
|
|
||||||
if (!this.permissionsService.mayRead(user, note)) {
|
if (!this.permissionsService.mayRead(user, note)) {
|
||||||
throw new UnauthorizedException('Reading note denied!');
|
throw new UnauthorizedException('Reading note denied!');
|
||||||
}
|
}
|
||||||
|
@ -195,22 +178,18 @@ export class NotesController {
|
||||||
if (e instanceof NotInDBError) {
|
if (e instanceof NotInDBError) {
|
||||||
throw new NotFoundException(e.message);
|
throw new NotFoundException(e.message);
|
||||||
}
|
}
|
||||||
if (e instanceof ForbiddenIdError) {
|
|
||||||
throw new BadRequestException(e.message);
|
|
||||||
}
|
|
||||||
throw e;
|
throw e;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Get(':noteIdOrAlias/revisions/:revisionId')
|
@Get(':noteIdOrAlias/revisions/:revisionId')
|
||||||
async getNoteRevision(
|
async getNoteRevision(
|
||||||
@Param('noteIdOrAlias') noteIdOrAlias: string,
|
@Param('noteIdOrAlias', GetNotePipe) note: Note,
|
||||||
@Param('revisionId') revisionId: number,
|
@Param('revisionId') revisionId: number,
|
||||||
): Promise<RevisionDto> {
|
): Promise<RevisionDto> {
|
||||||
try {
|
try {
|
||||||
// ToDo: use actual user here
|
// ToDo: use actual user here
|
||||||
const user = await this.userService.getUserByUsername('hardcoded');
|
const user = await this.userService.getUserByUsername('hardcoded');
|
||||||
const note = await this.noteService.getNoteByIdOrAlias(noteIdOrAlias);
|
|
||||||
if (!this.permissionsService.mayRead(user, note)) {
|
if (!this.permissionsService.mayRead(user, note)) {
|
||||||
throw new UnauthorizedException('Reading note denied!');
|
throw new UnauthorizedException('Reading note denied!');
|
||||||
}
|
}
|
||||||
|
@ -221,9 +200,6 @@ export class NotesController {
|
||||||
if (e instanceof NotInDBError) {
|
if (e instanceof NotInDBError) {
|
||||||
throw new NotFoundException(e.message);
|
throw new NotFoundException(e.message);
|
||||||
}
|
}
|
||||||
if (e instanceof ForbiddenIdError) {
|
|
||||||
throw new BadRequestException(e.message);
|
|
||||||
}
|
|
||||||
throw e;
|
throw e;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -31,7 +31,9 @@ import { HistoryService } from '../../../history/history.service';
|
||||||
import { ConsoleLoggerService } from '../../../logger/console-logger.service';
|
import { ConsoleLoggerService } from '../../../logger/console-logger.service';
|
||||||
import { MediaUploadDto } from '../../../media/media-upload.dto';
|
import { MediaUploadDto } from '../../../media/media-upload.dto';
|
||||||
import { MediaService } from '../../../media/media.service';
|
import { MediaService } from '../../../media/media.service';
|
||||||
|
import { GetNotePipe } from '../../../notes/get-note.pipe';
|
||||||
import { NoteMetadataDto } from '../../../notes/note-metadata.dto';
|
import { NoteMetadataDto } from '../../../notes/note-metadata.dto';
|
||||||
|
import { Note } from '../../../notes/note.entity';
|
||||||
import { NotesService } from '../../../notes/notes.service';
|
import { NotesService } from '../../../notes/notes.service';
|
||||||
import { UserInfoDto } from '../../../users/user-info.dto';
|
import { UserInfoDto } from '../../../users/user-info.dto';
|
||||||
import { User } from '../../../users/user.entity';
|
import { User } from '../../../users/user.entity';
|
||||||
|
@ -93,13 +95,10 @@ export class MeController {
|
||||||
@ApiNotFoundResponse({ description: notFoundDescription })
|
@ApiNotFoundResponse({ description: notFoundDescription })
|
||||||
async getHistoryEntry(
|
async getHistoryEntry(
|
||||||
@RequestUser() user: User,
|
@RequestUser() user: User,
|
||||||
@Param('note') note: string,
|
@Param('note', GetNotePipe) note: Note,
|
||||||
): Promise<HistoryEntryDto> {
|
): Promise<HistoryEntryDto> {
|
||||||
try {
|
try {
|
||||||
const foundEntry = await this.historyService.getEntryByNoteIdOrAlias(
|
const foundEntry = await this.historyService.getEntryByNote(note, user);
|
||||||
note,
|
|
||||||
user,
|
|
||||||
);
|
|
||||||
return this.historyService.toHistoryEntryDto(foundEntry);
|
return this.historyService.toHistoryEntryDto(foundEntry);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
if (e instanceof NotInDBError) {
|
if (e instanceof NotInDBError) {
|
||||||
|
@ -119,7 +118,7 @@ export class MeController {
|
||||||
@ApiNotFoundResponse({ description: notFoundDescription })
|
@ApiNotFoundResponse({ description: notFoundDescription })
|
||||||
async updateHistoryEntry(
|
async updateHistoryEntry(
|
||||||
@RequestUser() user: User,
|
@RequestUser() user: User,
|
||||||
@Param('note') note: string,
|
@Param('note', GetNotePipe) note: Note,
|
||||||
@Body() entryUpdateDto: HistoryEntryUpdateDto,
|
@Body() entryUpdateDto: HistoryEntryUpdateDto,
|
||||||
): Promise<HistoryEntryDto> {
|
): Promise<HistoryEntryDto> {
|
||||||
// ToDo: Check if user is allowed to pin this history entry
|
// ToDo: Check if user is allowed to pin this history entry
|
||||||
|
@ -147,7 +146,7 @@ export class MeController {
|
||||||
@ApiNotFoundResponse({ description: notFoundDescription })
|
@ApiNotFoundResponse({ description: notFoundDescription })
|
||||||
async deleteHistoryEntry(
|
async deleteHistoryEntry(
|
||||||
@RequestUser() user: User,
|
@RequestUser() user: User,
|
||||||
@Param('note') note: string,
|
@Param('note', GetNotePipe) note: Note,
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
// ToDo: Check if user is allowed to delete note
|
// ToDo: Check if user is allowed to delete note
|
||||||
try {
|
try {
|
||||||
|
|
|
@ -42,6 +42,8 @@ import { ConsoleLoggerService } from '../../../logger/console-logger.service';
|
||||||
import { MediaUploadUrlDto } from '../../../media/media-upload-url.dto';
|
import { MediaUploadUrlDto } from '../../../media/media-upload-url.dto';
|
||||||
import { MediaService } from '../../../media/media.service';
|
import { MediaService } from '../../../media/media.service';
|
||||||
import { MulterFile } from '../../../media/multer-file.interface';
|
import { MulterFile } from '../../../media/multer-file.interface';
|
||||||
|
import { Note } from '../../../notes/note.entity';
|
||||||
|
import { NotesService } from '../../../notes/notes.service';
|
||||||
import { User } from '../../../users/user.entity';
|
import { User } from '../../../users/user.entity';
|
||||||
import {
|
import {
|
||||||
forbiddenDescription,
|
forbiddenDescription,
|
||||||
|
@ -58,6 +60,7 @@ export class MediaController {
|
||||||
constructor(
|
constructor(
|
||||||
private readonly logger: ConsoleLoggerService,
|
private readonly logger: ConsoleLoggerService,
|
||||||
private mediaService: MediaService,
|
private mediaService: MediaService,
|
||||||
|
private noteService: NotesService,
|
||||||
) {
|
) {
|
||||||
this.logger.setContext(MediaController.name);
|
this.logger.setContext(MediaController.name);
|
||||||
}
|
}
|
||||||
|
@ -93,17 +96,14 @@ export class MediaController {
|
||||||
@UploadedFile() file: MulterFile,
|
@UploadedFile() file: MulterFile,
|
||||||
@Headers('HedgeDoc-Note') noteId: string,
|
@Headers('HedgeDoc-Note') noteId: string,
|
||||||
): Promise<MediaUploadUrlDto> {
|
): Promise<MediaUploadUrlDto> {
|
||||||
const username = user.userName;
|
|
||||||
this.logger.debug(
|
|
||||||
`Recieved filename '${file.originalname}' for note '${noteId}' from user '${username}'`,
|
|
||||||
'uploadMedia',
|
|
||||||
);
|
|
||||||
try {
|
try {
|
||||||
const url = await this.mediaService.saveFile(
|
// TODO: Move getting the Note object into a decorator
|
||||||
file.buffer,
|
const note: Note = await this.noteService.getNoteByIdOrAlias(noteId);
|
||||||
username,
|
this.logger.debug(
|
||||||
noteId,
|
`Recieved filename '${file.originalname}' for note '${noteId}' from user '${user.userName}'`,
|
||||||
|
'uploadMedia',
|
||||||
);
|
);
|
||||||
|
const url = await this.mediaService.saveFile(file.buffer, user, note);
|
||||||
return this.mediaService.toMediaUploadUrlDto(url);
|
return this.mediaService.toMediaUploadUrlDto(url);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
if (e instanceof ClientError || e instanceof NotInDBError) {
|
if (e instanceof ClientError || e instanceof NotInDBError) {
|
||||||
|
|
|
@ -34,12 +34,12 @@ import {
|
||||||
AlreadyInDBError,
|
AlreadyInDBError,
|
||||||
ForbiddenIdError,
|
ForbiddenIdError,
|
||||||
NotInDBError,
|
NotInDBError,
|
||||||
PermissionsUpdateInconsistentError,
|
|
||||||
} from '../../../errors/errors';
|
} from '../../../errors/errors';
|
||||||
import { HistoryService } from '../../../history/history.service';
|
import { HistoryService } from '../../../history/history.service';
|
||||||
import { ConsoleLoggerService } from '../../../logger/console-logger.service';
|
import { ConsoleLoggerService } from '../../../logger/console-logger.service';
|
||||||
import { MediaUploadDto } from '../../../media/media-upload.dto';
|
import { MediaUploadDto } from '../../../media/media-upload.dto';
|
||||||
import { MediaService } from '../../../media/media.service';
|
import { MediaService } from '../../../media/media.service';
|
||||||
|
import { GetNotePipe } from '../../../notes/get-note.pipe';
|
||||||
import { NoteMetadataDto } from '../../../notes/note-metadata.dto';
|
import { NoteMetadataDto } from '../../../notes/note-metadata.dto';
|
||||||
import {
|
import {
|
||||||
NotePermissionsDto,
|
NotePermissionsDto,
|
||||||
|
@ -106,24 +106,12 @@ export class NotesController {
|
||||||
@FullApi
|
@FullApi
|
||||||
async getNote(
|
async getNote(
|
||||||
@RequestUser() user: User,
|
@RequestUser() user: User,
|
||||||
@Param('noteIdOrAlias') noteIdOrAlias: string,
|
@Param('noteIdOrAlias', GetNotePipe) note: Note,
|
||||||
): Promise<NoteDto> {
|
): Promise<NoteDto> {
|
||||||
let note: Note;
|
|
||||||
try {
|
|
||||||
note = await this.noteService.getNoteByIdOrAlias(noteIdOrAlias);
|
|
||||||
} catch (e) {
|
|
||||||
if (e instanceof NotInDBError) {
|
|
||||||
throw new NotFoundException(e.message);
|
|
||||||
}
|
|
||||||
if (e instanceof ForbiddenIdError) {
|
|
||||||
throw new BadRequestException(e.message);
|
|
||||||
}
|
|
||||||
throw e;
|
|
||||||
}
|
|
||||||
if (!this.permissionsService.mayRead(user, note)) {
|
if (!this.permissionsService.mayRead(user, note)) {
|
||||||
throw new UnauthorizedException('Reading note denied!');
|
throw new UnauthorizedException('Reading note denied!');
|
||||||
}
|
}
|
||||||
await this.historyService.createOrUpdateHistoryEntry(note, user);
|
await this.historyService.updateHistoryEntryTimestamp(note, user);
|
||||||
return await this.noteService.toNoteDto(note);
|
return await this.noteService.toNoteDto(note);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -167,35 +155,24 @@ export class NotesController {
|
||||||
@FullApi
|
@FullApi
|
||||||
async deleteNote(
|
async deleteNote(
|
||||||
@RequestUser() user: User,
|
@RequestUser() user: User,
|
||||||
@Param('noteIdOrAlias') noteIdOrAlias: string,
|
@Param('noteIdOrAlias', GetNotePipe) note: Note,
|
||||||
@Body() noteMediaDeletionDto: NoteMediaDeletionDto,
|
@Body() noteMediaDeletionDto: NoteMediaDeletionDto,
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
try {
|
if (!this.permissionsService.isOwner(user, note)) {
|
||||||
const note = await this.noteService.getNoteByIdOrAlias(noteIdOrAlias);
|
throw new UnauthorizedException('Deleting note denied!');
|
||||||
if (!this.permissionsService.isOwner(user, note)) {
|
|
||||||
throw new UnauthorizedException('Deleting note denied!');
|
|
||||||
}
|
|
||||||
const mediaUploads = await this.mediaService.listUploadsByNote(note);
|
|
||||||
for (const mediaUpload of mediaUploads) {
|
|
||||||
if (!noteMediaDeletionDto.keepMedia) {
|
|
||||||
await this.mediaService.deleteFile(mediaUpload);
|
|
||||||
} else {
|
|
||||||
await this.mediaService.removeNoteFromMediaUpload(mediaUpload);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
this.logger.debug('Deleting note: ' + noteIdOrAlias, 'deleteNote');
|
|
||||||
await this.noteService.deleteNote(note);
|
|
||||||
this.logger.debug('Successfully deleted ' + noteIdOrAlias, 'deleteNote');
|
|
||||||
return;
|
|
||||||
} catch (e) {
|
|
||||||
if (e instanceof NotInDBError) {
|
|
||||||
throw new NotFoundException(e.message);
|
|
||||||
}
|
|
||||||
if (e instanceof ForbiddenIdError) {
|
|
||||||
throw new BadRequestException(e.message);
|
|
||||||
}
|
|
||||||
throw e;
|
|
||||||
}
|
}
|
||||||
|
const mediaUploads = await this.mediaService.listUploadsByNote(note);
|
||||||
|
for (const mediaUpload of mediaUploads) {
|
||||||
|
if (!noteMediaDeletionDto.keepMedia) {
|
||||||
|
await this.mediaService.deleteFile(mediaUpload);
|
||||||
|
} else {
|
||||||
|
await this.mediaService.removeNoteFromMediaUpload(mediaUpload);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.logger.debug('Deleting note: ' + note.id, 'deleteNote');
|
||||||
|
await this.noteService.deleteNote(note);
|
||||||
|
this.logger.debug('Successfully deleted ' + note.id, 'deleteNote');
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@UseGuards(TokenAuthGuard)
|
@UseGuards(TokenAuthGuard)
|
||||||
|
@ -207,27 +184,16 @@ export class NotesController {
|
||||||
@FullApi
|
@FullApi
|
||||||
async updateNote(
|
async updateNote(
|
||||||
@RequestUser() user: User,
|
@RequestUser() user: User,
|
||||||
@Param('noteIdOrAlias') noteIdOrAlias: string,
|
@Param('noteIdOrAlias', GetNotePipe) note: Note,
|
||||||
@MarkdownBody() text: string,
|
@MarkdownBody() text: string,
|
||||||
): Promise<NoteDto> {
|
): Promise<NoteDto> {
|
||||||
try {
|
if (!this.permissionsService.mayWrite(user, note)) {
|
||||||
const note = await this.noteService.getNoteByIdOrAlias(noteIdOrAlias);
|
throw new UnauthorizedException('Updating note denied!');
|
||||||
if (!this.permissionsService.mayWrite(user, note)) {
|
|
||||||
throw new UnauthorizedException('Updating note denied!');
|
|
||||||
}
|
|
||||||
this.logger.debug('Got raw markdown:\n' + text, 'updateNote');
|
|
||||||
return await this.noteService.toNoteDto(
|
|
||||||
await this.noteService.updateNote(note, text),
|
|
||||||
);
|
|
||||||
} catch (e) {
|
|
||||||
if (e instanceof NotInDBError) {
|
|
||||||
throw new NotFoundException(e.message);
|
|
||||||
}
|
|
||||||
if (e instanceof ForbiddenIdError) {
|
|
||||||
throw new BadRequestException(e.message);
|
|
||||||
}
|
|
||||||
throw e;
|
|
||||||
}
|
}
|
||||||
|
this.logger.debug('Got raw markdown:\n' + text, 'updateNote');
|
||||||
|
return await this.noteService.toNoteDto(
|
||||||
|
await this.noteService.updateNote(note, text),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@UseGuards(TokenAuthGuard)
|
@UseGuards(TokenAuthGuard)
|
||||||
|
@ -240,23 +206,12 @@ export class NotesController {
|
||||||
@Header('content-type', 'text/markdown')
|
@Header('content-type', 'text/markdown')
|
||||||
async getNoteContent(
|
async getNoteContent(
|
||||||
@RequestUser() user: User,
|
@RequestUser() user: User,
|
||||||
@Param('noteIdOrAlias') noteIdOrAlias: string,
|
@Param('noteIdOrAlias', GetNotePipe) note: Note,
|
||||||
): Promise<string> {
|
): Promise<string> {
|
||||||
try {
|
if (!this.permissionsService.mayRead(user, note)) {
|
||||||
const note = await this.noteService.getNoteByIdOrAlias(noteIdOrAlias);
|
throw new UnauthorizedException('Reading note denied!');
|
||||||
if (!this.permissionsService.mayRead(user, note)) {
|
|
||||||
throw new UnauthorizedException('Reading note denied!');
|
|
||||||
}
|
|
||||||
return await this.noteService.getNoteContent(note);
|
|
||||||
} catch (e) {
|
|
||||||
if (e instanceof NotInDBError) {
|
|
||||||
throw new NotFoundException(e.message);
|
|
||||||
}
|
|
||||||
if (e instanceof ForbiddenIdError) {
|
|
||||||
throw new BadRequestException(e.message);
|
|
||||||
}
|
|
||||||
throw e;
|
|
||||||
}
|
}
|
||||||
|
return await this.noteService.getNoteContent(note);
|
||||||
}
|
}
|
||||||
|
|
||||||
@UseGuards(TokenAuthGuard)
|
@UseGuards(TokenAuthGuard)
|
||||||
|
@ -268,26 +223,12 @@ export class NotesController {
|
||||||
@FullApi
|
@FullApi
|
||||||
async getNoteMetadata(
|
async getNoteMetadata(
|
||||||
@RequestUser() user: User,
|
@RequestUser() user: User,
|
||||||
@Param('noteIdOrAlias') noteIdOrAlias: string,
|
@Param('noteIdOrAlias', GetNotePipe) note: Note,
|
||||||
): Promise<NoteMetadataDto> {
|
): Promise<NoteMetadataDto> {
|
||||||
try {
|
if (!this.permissionsService.mayRead(user, note)) {
|
||||||
const note = await this.noteService.getNoteByIdOrAlias(noteIdOrAlias);
|
throw new UnauthorizedException('Reading note denied!');
|
||||||
if (!this.permissionsService.mayRead(user, note)) {
|
|
||||||
throw new UnauthorizedException('Reading note denied!');
|
|
||||||
}
|
|
||||||
return await this.noteService.toNoteMetadataDto(note);
|
|
||||||
} catch (e) {
|
|
||||||
if (e instanceof NotInDBError) {
|
|
||||||
throw new NotFoundException(e.message);
|
|
||||||
}
|
|
||||||
if (e instanceof PermissionsUpdateInconsistentError) {
|
|
||||||
throw new BadRequestException(e.message);
|
|
||||||
}
|
|
||||||
if (e instanceof ForbiddenIdError) {
|
|
||||||
throw new BadRequestException(e.message);
|
|
||||||
}
|
|
||||||
throw e;
|
|
||||||
}
|
}
|
||||||
|
return await this.noteService.toNoteMetadataDto(note);
|
||||||
}
|
}
|
||||||
|
|
||||||
@UseGuards(TokenAuthGuard)
|
@UseGuards(TokenAuthGuard)
|
||||||
|
@ -299,26 +240,15 @@ export class NotesController {
|
||||||
@FullApi
|
@FullApi
|
||||||
async updateNotePermissions(
|
async updateNotePermissions(
|
||||||
@RequestUser() user: User,
|
@RequestUser() user: User,
|
||||||
@Param('noteIdOrAlias') noteIdOrAlias: string,
|
@Param('noteIdOrAlias', GetNotePipe) note: Note,
|
||||||
@Body() updateDto: NotePermissionsUpdateDto,
|
@Body() updateDto: NotePermissionsUpdateDto,
|
||||||
): Promise<NotePermissionsDto> {
|
): Promise<NotePermissionsDto> {
|
||||||
try {
|
if (!this.permissionsService.isOwner(user, note)) {
|
||||||
const note = await this.noteService.getNoteByIdOrAlias(noteIdOrAlias);
|
throw new UnauthorizedException('Updating note denied!');
|
||||||
if (!this.permissionsService.isOwner(user, note)) {
|
|
||||||
throw new UnauthorizedException('Updating note denied!');
|
|
||||||
}
|
|
||||||
return this.noteService.toNotePermissionsDto(
|
|
||||||
await this.noteService.updateNotePermissions(note, updateDto),
|
|
||||||
);
|
|
||||||
} catch (e) {
|
|
||||||
if (e instanceof NotInDBError) {
|
|
||||||
throw new NotFoundException(e.message);
|
|
||||||
}
|
|
||||||
if (e instanceof ForbiddenIdError) {
|
|
||||||
throw new BadRequestException(e.message);
|
|
||||||
}
|
|
||||||
throw e;
|
|
||||||
}
|
}
|
||||||
|
return this.noteService.toNotePermissionsDto(
|
||||||
|
await this.noteService.updateNotePermissions(note, updateDto),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@UseGuards(TokenAuthGuard)
|
@UseGuards(TokenAuthGuard)
|
||||||
|
@ -331,28 +261,17 @@ export class NotesController {
|
||||||
@FullApi
|
@FullApi
|
||||||
async getNoteRevisions(
|
async getNoteRevisions(
|
||||||
@RequestUser() user: User,
|
@RequestUser() user: User,
|
||||||
@Param('noteIdOrAlias') noteIdOrAlias: string,
|
@Param('noteIdOrAlias', GetNotePipe) note: Note,
|
||||||
): Promise<RevisionMetadataDto[]> {
|
): Promise<RevisionMetadataDto[]> {
|
||||||
try {
|
if (!this.permissionsService.mayRead(user, note)) {
|
||||||
const note = await this.noteService.getNoteByIdOrAlias(noteIdOrAlias);
|
throw new UnauthorizedException('Reading note denied!');
|
||||||
if (!this.permissionsService.mayRead(user, note)) {
|
|
||||||
throw new UnauthorizedException('Reading note denied!');
|
|
||||||
}
|
|
||||||
const revisions = await this.revisionsService.getAllRevisions(note);
|
|
||||||
return await Promise.all(
|
|
||||||
revisions.map((revision) =>
|
|
||||||
this.revisionsService.toRevisionMetadataDto(revision),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
} catch (e) {
|
|
||||||
if (e instanceof NotInDBError) {
|
|
||||||
throw new NotFoundException(e.message);
|
|
||||||
}
|
|
||||||
if (e instanceof ForbiddenIdError) {
|
|
||||||
throw new BadRequestException(e.message);
|
|
||||||
}
|
|
||||||
throw e;
|
|
||||||
}
|
}
|
||||||
|
const revisions = await this.revisionsService.getAllRevisions(note);
|
||||||
|
return await Promise.all(
|
||||||
|
revisions.map((revision) =>
|
||||||
|
this.revisionsService.toRevisionMetadataDto(revision),
|
||||||
|
),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@UseGuards(TokenAuthGuard)
|
@UseGuards(TokenAuthGuard)
|
||||||
|
@ -364,14 +283,13 @@ export class NotesController {
|
||||||
@FullApi
|
@FullApi
|
||||||
async getNoteRevision(
|
async getNoteRevision(
|
||||||
@RequestUser() user: User,
|
@RequestUser() user: User,
|
||||||
@Param('noteIdOrAlias') noteIdOrAlias: string,
|
@Param('noteIdOrAlias', GetNotePipe) note: Note,
|
||||||
@Param('revisionId') revisionId: number,
|
@Param('revisionId') revisionId: number,
|
||||||
): Promise<RevisionDto> {
|
): Promise<RevisionDto> {
|
||||||
|
if (!this.permissionsService.mayRead(user, note)) {
|
||||||
|
throw new UnauthorizedException('Reading note denied!');
|
||||||
|
}
|
||||||
try {
|
try {
|
||||||
const note = await this.noteService.getNoteByIdOrAlias(noteIdOrAlias);
|
|
||||||
if (!this.permissionsService.mayRead(user, note)) {
|
|
||||||
throw new UnauthorizedException('Reading note denied!');
|
|
||||||
}
|
|
||||||
return this.revisionsService.toRevisionDto(
|
return this.revisionsService.toRevisionDto(
|
||||||
await this.revisionsService.getRevision(note, revisionId),
|
await this.revisionsService.getRevision(note, revisionId),
|
||||||
);
|
);
|
||||||
|
@ -379,9 +297,6 @@ export class NotesController {
|
||||||
if (e instanceof NotInDBError) {
|
if (e instanceof NotInDBError) {
|
||||||
throw new NotFoundException(e.message);
|
throw new NotFoundException(e.message);
|
||||||
}
|
}
|
||||||
if (e instanceof ForbiddenIdError) {
|
|
||||||
throw new BadRequestException(e.message);
|
|
||||||
}
|
|
||||||
throw e;
|
throw e;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -396,20 +311,12 @@ export class NotesController {
|
||||||
@ApiUnauthorizedResponse({ description: unauthorizedDescription })
|
@ApiUnauthorizedResponse({ description: unauthorizedDescription })
|
||||||
async getNotesMedia(
|
async getNotesMedia(
|
||||||
@RequestUser() user: User,
|
@RequestUser() user: User,
|
||||||
@Param('noteIdOrAlias') noteIdOrAlias: string,
|
@Param('noteIdOrAlias', GetNotePipe) note: Note,
|
||||||
): Promise<MediaUploadDto[]> {
|
): Promise<MediaUploadDto[]> {
|
||||||
try {
|
if (!this.permissionsService.mayRead(user, note)) {
|
||||||
const note = await this.noteService.getNoteByIdOrAlias(noteIdOrAlias);
|
throw new UnauthorizedException('Reading note denied!');
|
||||||
if (!this.permissionsService.mayRead(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;
|
|
||||||
}
|
}
|
||||||
|
const media = await this.mediaService.listUploadsByNote(note);
|
||||||
|
return media.map((media) => this.mediaService.toMediaUploadDto(media));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -135,31 +135,7 @@ describe('HistoryService', () => {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('getEntryByNoteIdOrAlias', () => {
|
describe('updateHistoryEntryTimestamp', () => {
|
||||||
const user = {} as User;
|
|
||||||
const alias = 'alias';
|
|
||||||
describe('works', () => {
|
|
||||||
it('with history entry', async () => {
|
|
||||||
const note = Note.create(user, alias);
|
|
||||||
const historyEntry = HistoryEntry.create(user, note);
|
|
||||||
jest.spyOn(historyRepo, 'findOne').mockResolvedValueOnce(historyEntry);
|
|
||||||
jest.spyOn(noteRepo, 'findOne').mockResolvedValueOnce(note);
|
|
||||||
expect(await service.getEntryByNoteIdOrAlias(alias, user)).toEqual(
|
|
||||||
historyEntry,
|
|
||||||
);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
describe('fails', () => {
|
|
||||||
it('with an non-existing note', async () => {
|
|
||||||
jest.spyOn(noteRepo, 'findOne').mockResolvedValueOnce(undefined);
|
|
||||||
await expect(
|
|
||||||
service.getEntryByNoteIdOrAlias(alias, {} as User),
|
|
||||||
).rejects.toThrow(NotInDBError);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('createOrUpdateHistoryEntry', () => {
|
|
||||||
describe('works', () => {
|
describe('works', () => {
|
||||||
const user = {} as User;
|
const user = {} as User;
|
||||||
const alias = 'alias';
|
const alias = 'alias';
|
||||||
|
@ -171,7 +147,7 @@ describe('HistoryService', () => {
|
||||||
.mockImplementation(
|
.mockImplementation(
|
||||||
async (entry: HistoryEntry): Promise<HistoryEntry> => entry,
|
async (entry: HistoryEntry): Promise<HistoryEntry> => entry,
|
||||||
);
|
);
|
||||||
const createHistoryEntry = await service.createOrUpdateHistoryEntry(
|
const createHistoryEntry = await service.updateHistoryEntryTimestamp(
|
||||||
Note.create(user, alias),
|
Note.create(user, alias),
|
||||||
user,
|
user,
|
||||||
);
|
);
|
||||||
|
@ -188,7 +164,7 @@ describe('HistoryService', () => {
|
||||||
.mockImplementation(
|
.mockImplementation(
|
||||||
async (entry: HistoryEntry): Promise<HistoryEntry> => entry,
|
async (entry: HistoryEntry): Promise<HistoryEntry> => entry,
|
||||||
);
|
);
|
||||||
const createHistoryEntry = await service.createOrUpdateHistoryEntry(
|
const createHistoryEntry = await service.updateHistoryEntryTimestamp(
|
||||||
Note.create(user, alias),
|
Note.create(user, alias),
|
||||||
user,
|
user,
|
||||||
);
|
);
|
||||||
|
@ -218,7 +194,7 @@ describe('HistoryService', () => {
|
||||||
async (entry: HistoryEntry): Promise<HistoryEntry> => entry,
|
async (entry: HistoryEntry): Promise<HistoryEntry> => entry,
|
||||||
);
|
);
|
||||||
const updatedHistoryEntry = await service.updateHistoryEntry(
|
const updatedHistoryEntry = await service.updateHistoryEntry(
|
||||||
alias,
|
note,
|
||||||
user,
|
user,
|
||||||
{
|
{
|
||||||
pinStatus: true,
|
pinStatus: true,
|
||||||
|
@ -237,7 +213,7 @@ describe('HistoryService', () => {
|
||||||
jest.spyOn(historyRepo, 'findOne').mockResolvedValueOnce(undefined);
|
jest.spyOn(historyRepo, 'findOne').mockResolvedValueOnce(undefined);
|
||||||
jest.spyOn(noteRepo, 'findOne').mockResolvedValueOnce(note);
|
jest.spyOn(noteRepo, 'findOne').mockResolvedValueOnce(note);
|
||||||
await expect(
|
await expect(
|
||||||
service.updateHistoryEntry(alias, user, {
|
service.updateHistoryEntry(note, user, {
|
||||||
pinStatus: true,
|
pinStatus: true,
|
||||||
}),
|
}),
|
||||||
).rejects.toThrow(NotInDBError);
|
).rejects.toThrow(NotInDBError);
|
||||||
|
@ -311,7 +287,7 @@ describe('HistoryService', () => {
|
||||||
return entry;
|
return entry;
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
await service.deleteHistoryEntry(alias, user);
|
await service.deleteHistoryEntry(note, user);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
describe('fails', () => {
|
describe('fails', () => {
|
||||||
|
@ -321,16 +297,10 @@ describe('HistoryService', () => {
|
||||||
const note = Note.create(user, alias);
|
const note = Note.create(user, alias);
|
||||||
jest.spyOn(historyRepo, 'findOne').mockResolvedValueOnce(undefined);
|
jest.spyOn(historyRepo, 'findOne').mockResolvedValueOnce(undefined);
|
||||||
jest.spyOn(noteRepo, 'findOne').mockResolvedValueOnce(note);
|
jest.spyOn(noteRepo, 'findOne').mockResolvedValueOnce(note);
|
||||||
await expect(service.deleteHistoryEntry(alias, user)).rejects.toThrow(
|
await expect(service.deleteHistoryEntry(note, user)).rejects.toThrow(
|
||||||
NotInDBError,
|
NotInDBError,
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
it('without a note', async () => {
|
|
||||||
jest.spyOn(noteRepo, 'findOne').mockResolvedValueOnce(undefined);
|
|
||||||
await expect(
|
|
||||||
service.getEntryByNoteIdOrAlias(alias, {} as User),
|
|
||||||
).rejects.toThrow(NotInDBError);
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -45,22 +45,6 @@ export class HistoryService {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @async
|
|
||||||
* Get a history entry by the user and note, which is specified via id or alias
|
|
||||||
* @param {string} noteIdOrAlias - the id or alias specifying the note
|
|
||||||
* @param {User} user - the user that the note belongs to
|
|
||||||
* @throws {NotInDBError} the specified note does not exist
|
|
||||||
* @return {HistoryEntry} the requested history entry
|
|
||||||
*/
|
|
||||||
async getEntryByNoteIdOrAlias(
|
|
||||||
noteIdOrAlias: string,
|
|
||||||
user: User,
|
|
||||||
): Promise<HistoryEntry> {
|
|
||||||
const note = await this.notesService.getNoteByIdOrAlias(noteIdOrAlias);
|
|
||||||
return await this.getEntryByNote(note, user);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @async
|
* @async
|
||||||
* Get a history entry by the user and note
|
* Get a history entry by the user and note
|
||||||
|
@ -68,7 +52,7 @@ export class HistoryService {
|
||||||
* @param {User} user - the user that the history entry belongs to
|
* @param {User} user - the user that the history entry belongs to
|
||||||
* @return {HistoryEntry} the requested history entry
|
* @return {HistoryEntry} the requested history entry
|
||||||
*/
|
*/
|
||||||
private async getEntryByNote(note: Note, user: User): Promise<HistoryEntry> {
|
async getEntryByNote(note: Note, user: User): Promise<HistoryEntry> {
|
||||||
const entry = await this.historyEntryRepository.findOne({
|
const entry = await this.historyEntryRepository.findOne({
|
||||||
where: {
|
where: {
|
||||||
note: note,
|
note: note,
|
||||||
|
@ -86,12 +70,13 @@ export class HistoryService {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @async
|
* @async
|
||||||
* Create or update a history entry by the user and note. If the entry is merely updated the updatedAt date is set to the current date.
|
* Updates the updatedAt timestamp of a HistoryEntry.
|
||||||
|
* If no history entry exists, it will be created.
|
||||||
* @param {Note} note - the note that the history entry belongs to
|
* @param {Note} note - the note that the history entry belongs to
|
||||||
* @param {User} user - the user that the history entry belongs to
|
* @param {User} user - the user that the history entry belongs to
|
||||||
* @return {HistoryEntry} the requested history entry
|
* @return {HistoryEntry} the requested history entry
|
||||||
*/
|
*/
|
||||||
async createOrUpdateHistoryEntry(
|
async updateHistoryEntryTimestamp(
|
||||||
note: Note,
|
note: Note,
|
||||||
user: User,
|
user: User,
|
||||||
): Promise<HistoryEntry> {
|
): Promise<HistoryEntry> {
|
||||||
|
@ -111,17 +96,17 @@ export class HistoryService {
|
||||||
/**
|
/**
|
||||||
* @async
|
* @async
|
||||||
* Update a history entry identified by the user and a note id or alias
|
* Update a history entry identified by the user and a note id or alias
|
||||||
* @param {string} noteIdOrAlias - the note that the history entry belongs to
|
* @param {Note} note - the note that the history entry belongs to
|
||||||
* @param {User} user - the user that the history entry belongs to
|
* @param {User} user - the user that the history entry belongs to
|
||||||
* @param {HistoryEntryUpdateDto} updateDto - the change that should be applied to the history entry
|
* @param {HistoryEntryUpdateDto} updateDto - the change that should be applied to the history entry
|
||||||
* @return {HistoryEntry} the requested history entry
|
* @return {HistoryEntry} the requested history entry
|
||||||
*/
|
*/
|
||||||
async updateHistoryEntry(
|
async updateHistoryEntry(
|
||||||
noteIdOrAlias: string,
|
note: Note,
|
||||||
user: User,
|
user: User,
|
||||||
updateDto: HistoryEntryUpdateDto,
|
updateDto: HistoryEntryUpdateDto,
|
||||||
): Promise<HistoryEntry> {
|
): Promise<HistoryEntry> {
|
||||||
const entry = await this.getEntryByNoteIdOrAlias(noteIdOrAlias, user);
|
const entry = await this.getEntryByNote(note, user);
|
||||||
entry.pinStatus = updateDto.pinStatus;
|
entry.pinStatus = updateDto.pinStatus;
|
||||||
return await this.historyEntryRepository.save(entry);
|
return await this.historyEntryRepository.save(entry);
|
||||||
}
|
}
|
||||||
|
@ -129,12 +114,12 @@ export class HistoryService {
|
||||||
/**
|
/**
|
||||||
* @async
|
* @async
|
||||||
* Delete the history entry identified by the user and a note id or alias
|
* Delete the history entry identified by the user and a note id or alias
|
||||||
* @param {string} noteIdOrAlias - the note that the history entry belongs to
|
* @param {Note} note - the note that the history entry belongs to
|
||||||
* @param {User} user - the user that the history entry belongs to
|
* @param {User} user - the user that the history entry belongs to
|
||||||
* @throws {NotInDBError} the specified history entry does not exist
|
* @throws {NotInDBError} the specified history entry does not exist
|
||||||
*/
|
*/
|
||||||
async deleteHistoryEntry(noteIdOrAlias: string, user: User): Promise<void> {
|
async deleteHistoryEntry(note: Note, user: User): Promise<void> {
|
||||||
const entry = await this.getEntryByNoteIdOrAlias(noteIdOrAlias, user);
|
const entry = await this.getEntryByNote(note, user);
|
||||||
await this.historyEntryRepository.remove(entry);
|
await this.historyEntryRepository.remove(entry);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
|
@ -98,10 +98,12 @@ describe('MediaService', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('saveFile', () => {
|
describe('saveFile', () => {
|
||||||
|
let user: User;
|
||||||
|
let note: Note;
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
const user = User.create('hardcoded', 'Testy') as User;
|
user = User.create('hardcoded', 'Testy') as User;
|
||||||
const alias = 'alias';
|
const alias = 'alias';
|
||||||
const note = Note.create(user, alias);
|
note = Note.create(user, alias);
|
||||||
jest.spyOn(userRepo, 'findOne').mockResolvedValueOnce(user);
|
jest.spyOn(userRepo, 'findOne').mockResolvedValueOnce(user);
|
||||||
jest.spyOn(noteRepo, 'findOne').mockResolvedValueOnce(note);
|
jest.spyOn(noteRepo, 'findOne').mockResolvedValueOnce(note);
|
||||||
});
|
});
|
||||||
|
@ -126,22 +128,22 @@ describe('MediaService', () => {
|
||||||
return [fileName, null];
|
return [fileName, null];
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
const url = await service.saveFile(testImage, 'hardcoded', 'test');
|
const url = await service.saveFile(testImage, user, note);
|
||||||
expect(url).toEqual(fileId);
|
expect(url).toEqual(fileId);
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('fails:', () => {
|
describe('fails:', () => {
|
||||||
it('MIME type not identifiable', async () => {
|
it('MIME type not identifiable', async () => {
|
||||||
await expect(
|
await expect(
|
||||||
service.saveFile(Buffer.alloc(1), 'hardcoded', 'test'),
|
service.saveFile(Buffer.alloc(1), user, note),
|
||||||
).rejects.toThrow(ClientError);
|
).rejects.toThrow(ClientError);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('MIME type not supported', async () => {
|
it('MIME type not supported', async () => {
|
||||||
const testText = await fs.readFile('test/public-api/fixtures/test.zip');
|
const testText = await fs.readFile('test/public-api/fixtures/test.zip');
|
||||||
await expect(
|
await expect(service.saveFile(testText, user, note)).rejects.toThrow(
|
||||||
service.saveFile(testText, 'hardcoded', 'test'),
|
ClientError,
|
||||||
).rejects.toThrow(ClientError);
|
);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -69,24 +69,18 @@ export class MediaService {
|
||||||
* @async
|
* @async
|
||||||
* Save the given buffer to the configured MediaBackend and create a MediaUploadEntity to track where the file is, who uploaded it and to which note.
|
* Save the given buffer to the configured MediaBackend and create a MediaUploadEntity to track where the file is, who uploaded it and to which note.
|
||||||
* @param {Buffer} fileBuffer - the buffer of the file to save.
|
* @param {Buffer} fileBuffer - the buffer of the file to save.
|
||||||
* @param {string} username - the username of the user who uploaded this file
|
* @param {User} user - the user who uploaded this file
|
||||||
* @param {string} noteId - the id or alias of the note which will be associated with the new file.
|
* @param {Note} note - the note which will be associated with the new file.
|
||||||
* @return {string} the url of the saved file
|
* @return {string} the url of the saved file
|
||||||
* @throws {ClientError} the MIME type of the file is not supported.
|
* @throws {ClientError} the MIME type of the file is not supported.
|
||||||
* @throws {NotInDBError} - the note or user is not in the database
|
* @throws {NotInDBError} - the note or user is not in the database
|
||||||
* @throws {MediaBackendError} - there was an error saving the file
|
* @throws {MediaBackendError} - there was an error saving the file
|
||||||
*/
|
*/
|
||||||
async saveFile(
|
async saveFile(fileBuffer: Buffer, user: User, note: Note): Promise<string> {
|
||||||
fileBuffer: Buffer,
|
|
||||||
username: string,
|
|
||||||
noteId: string,
|
|
||||||
): Promise<string> {
|
|
||||||
this.logger.debug(
|
this.logger.debug(
|
||||||
`Saving file for note '${noteId}' and user '${username}'`,
|
`Saving file for note '${note.id}' and user '${user.userName}'`,
|
||||||
'saveFile',
|
'saveFile',
|
||||||
);
|
);
|
||||||
const note = await this.notesService.getNoteByIdOrAlias(noteId);
|
|
||||||
const user = await this.usersService.getUserByUsername(username);
|
|
||||||
const fileTypeResult = await FileType.fromBuffer(fileBuffer);
|
const fileTypeResult = await FileType.fromBuffer(fileBuffer);
|
||||||
if (!fileTypeResult) {
|
if (!fileTypeResult) {
|
||||||
throw new ClientError('Could not detect file type.');
|
throw new ClientError('Could not detect file type.');
|
||||||
|
|
43
src/notes/get-note.pipe.ts
Normal file
43
src/notes/get-note.pipe.ts
Normal file
|
@ -0,0 +1,43 @@
|
||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file)
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
*/
|
||||||
|
import {
|
||||||
|
ArgumentMetadata,
|
||||||
|
BadRequestException,
|
||||||
|
Injectable,
|
||||||
|
NotFoundException,
|
||||||
|
PipeTransform,
|
||||||
|
} from '@nestjs/common';
|
||||||
|
|
||||||
|
import { ForbiddenIdError, NotInDBError } from '../errors/errors';
|
||||||
|
import { ConsoleLoggerService } from '../logger/console-logger.service';
|
||||||
|
import { Note } from './note.entity';
|
||||||
|
import { NotesService } from './notes.service';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class GetNotePipe implements PipeTransform<string, Promise<Note>> {
|
||||||
|
constructor(
|
||||||
|
private readonly logger: ConsoleLoggerService,
|
||||||
|
private noteService: NotesService,
|
||||||
|
) {
|
||||||
|
this.logger.setContext(GetNotePipe.name);
|
||||||
|
}
|
||||||
|
|
||||||
|
async transform(noteIdOrAlias: string, _: ArgumentMetadata): Promise<Note> {
|
||||||
|
let note: Note;
|
||||||
|
try {
|
||||||
|
note = await this.noteService.getNoteByIdOrAlias(noteIdOrAlias);
|
||||||
|
} catch (e) {
|
||||||
|
if (e instanceof NotInDBError) {
|
||||||
|
throw new NotFoundException(e.message);
|
||||||
|
}
|
||||||
|
if (e instanceof ForbiddenIdError) {
|
||||||
|
throw new BadRequestException(e.message);
|
||||||
|
}
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
return note;
|
||||||
|
}
|
||||||
|
}
|
|
@ -129,7 +129,7 @@ export class NotesService {
|
||||||
* @return {Revision} the first revision of the note
|
* @return {Revision} the first revision of the note
|
||||||
*/
|
*/
|
||||||
async getLatestRevision(note: Note): Promise<Revision> {
|
async getLatestRevision(note: Note): Promise<Revision> {
|
||||||
return await this.revisionsService.getLatestRevision(note.id);
|
return await this.revisionsService.getLatestRevision(note);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -139,7 +139,7 @@ export class NotesService {
|
||||||
* @return {Revision} the last revision of the note
|
* @return {Revision} the last revision of the note
|
||||||
*/
|
*/
|
||||||
async getFirstRevision(note: Note): Promise<Revision> {
|
async getFirstRevision(note: Note): Promise<Revision> {
|
||||||
return await this.revisionsService.getFirstRevision(note.id);
|
return await this.revisionsService.getFirstRevision(note);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -49,10 +49,10 @@ export class RevisionsService {
|
||||||
return revision;
|
return revision;
|
||||||
}
|
}
|
||||||
|
|
||||||
async getLatestRevision(noteId: string): Promise<Revision> {
|
async getLatestRevision(note: Note): Promise<Revision> {
|
||||||
const revision = await this.revisionRepository.findOne({
|
const revision = await this.revisionRepository.findOne({
|
||||||
where: {
|
where: {
|
||||||
note: noteId,
|
note: note,
|
||||||
},
|
},
|
||||||
order: {
|
order: {
|
||||||
createdAt: 'DESC',
|
createdAt: 'DESC',
|
||||||
|
@ -60,22 +60,22 @@ export class RevisionsService {
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
if (revision === undefined) {
|
if (revision === undefined) {
|
||||||
throw new NotInDBError(`Revision for note ${noteId} not found.`);
|
throw new NotInDBError(`Revision for note ${note.id} not found.`);
|
||||||
}
|
}
|
||||||
return revision;
|
return revision;
|
||||||
}
|
}
|
||||||
|
|
||||||
async getFirstRevision(noteId: string): Promise<Revision> {
|
async getFirstRevision(note: Note): Promise<Revision> {
|
||||||
const revision = await this.revisionRepository.findOne({
|
const revision = await this.revisionRepository.findOne({
|
||||||
where: {
|
where: {
|
||||||
note: noteId,
|
note: note,
|
||||||
},
|
},
|
||||||
order: {
|
order: {
|
||||||
createdAt: 'ASC',
|
createdAt: 'ASC',
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
if (revision === undefined) {
|
if (revision === undefined) {
|
||||||
throw new NotInDBError(`Revision for note ${noteId} not found.`);
|
throw new NotInDBError(`Revision for note ${note.id} not found.`);
|
||||||
}
|
}
|
||||||
return revision;
|
return revision;
|
||||||
}
|
}
|
||||||
|
|
|
@ -87,7 +87,7 @@ describe('History', () => {
|
||||||
.expect('Content-Type', /json/)
|
.expect('Content-Type', /json/)
|
||||||
.expect(200);
|
.expect(200);
|
||||||
expect(emptyResponse.body.length).toEqual(0);
|
expect(emptyResponse.body.length).toEqual(0);
|
||||||
const entry = await historyService.createOrUpdateHistoryEntry(note, user);
|
const entry = await historyService.updateHistoryEntryTimestamp(note, user);
|
||||||
const entryDto = historyService.toHistoryEntryDto(entry);
|
const entryDto = historyService.toHistoryEntryDto(entry);
|
||||||
const response = await request(app.getHttpServer())
|
const response = await request(app.getHttpServer())
|
||||||
.get('/me/history')
|
.get('/me/history')
|
||||||
|
@ -182,7 +182,7 @@ describe('History', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('PUT /me/history/:note', async () => {
|
it('PUT /me/history/:note', async () => {
|
||||||
const entry = await historyService.createOrUpdateHistoryEntry(note2, user);
|
const entry = await historyService.updateHistoryEntryTimestamp(note2, user);
|
||||||
expect(entry.pinStatus).toBeFalsy();
|
expect(entry.pinStatus).toBeFalsy();
|
||||||
await request(app.getHttpServer())
|
await request(app.getHttpServer())
|
||||||
.put(`/me/history/${entry.note.alias || 'undefined'}`)
|
.put(`/me/history/${entry.note.alias || 'undefined'}`)
|
||||||
|
@ -191,12 +191,12 @@ describe('History', () => {
|
||||||
const userEntries = await historyService.getEntriesByUser(user);
|
const userEntries = await historyService.getEntriesByUser(user);
|
||||||
expect(userEntries.length).toEqual(1);
|
expect(userEntries.length).toEqual(1);
|
||||||
expect(userEntries[0].pinStatus).toBeTruthy();
|
expect(userEntries[0].pinStatus).toBeTruthy();
|
||||||
await historyService.deleteHistoryEntry(note2.alias, user);
|
await historyService.deleteHistoryEntry(note2, user);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('DELETE /me/history/:note', async () => {
|
it('DELETE /me/history/:note', async () => {
|
||||||
const entry = await historyService.createOrUpdateHistoryEntry(note2, user);
|
const entry = await historyService.updateHistoryEntryTimestamp(note2, user);
|
||||||
const entry2 = await historyService.createOrUpdateHistoryEntry(note, user);
|
const entry2 = await historyService.updateHistoryEntryTimestamp(note, user);
|
||||||
const entryDto = historyService.toHistoryEntryDto(entry2);
|
const entryDto = historyService.toHistoryEntryDto(entry2);
|
||||||
await request(app.getHttpServer())
|
await request(app.getHttpServer())
|
||||||
.delete(`/me/history/${entry.note.alias || 'undefined'}`)
|
.delete(`/me/history/${entry.note.alias || 'undefined'}`)
|
||||||
|
|
|
@ -111,26 +111,10 @@ describe('Me', () => {
|
||||||
expect(responseBefore.body).toHaveLength(0);
|
expect(responseBefore.body).toHaveLength(0);
|
||||||
|
|
||||||
const testImage = await fs.readFile('test/public-api/fixtures/test.png');
|
const testImage = await fs.readFile('test/public-api/fixtures/test.png');
|
||||||
const url0 = await mediaService.saveFile(
|
const url0 = await mediaService.saveFile(testImage, user, note1);
|
||||||
testImage,
|
const url1 = await mediaService.saveFile(testImage, user, note1);
|
||||||
'hardcoded',
|
const url2 = await mediaService.saveFile(testImage, user, note2);
|
||||||
note1.publicId,
|
const url3 = await mediaService.saveFile(testImage, user, note2);
|
||||||
);
|
|
||||||
const url1 = await mediaService.saveFile(
|
|
||||||
testImage,
|
|
||||||
'hardcoded',
|
|
||||||
note1.publicId,
|
|
||||||
);
|
|
||||||
const url2 = await mediaService.saveFile(
|
|
||||||
testImage,
|
|
||||||
'hardcoded',
|
|
||||||
note2.alias ?? '',
|
|
||||||
);
|
|
||||||
const url3 = await mediaService.saveFile(
|
|
||||||
testImage,
|
|
||||||
'hardcoded',
|
|
||||||
note2.alias ?? '',
|
|
||||||
);
|
|
||||||
|
|
||||||
const response = await request(httpServer)
|
const response = await request(httpServer)
|
||||||
.get('/me/media/')
|
.get('/me/media/')
|
||||||
|
@ -163,11 +147,7 @@ describe('Me', () => {
|
||||||
|
|
||||||
it('DELETE /me', async () => {
|
it('DELETE /me', async () => {
|
||||||
const testImage = await fs.readFile('test/public-api/fixtures/test.png');
|
const testImage = await fs.readFile('test/public-api/fixtures/test.png');
|
||||||
const url0 = await mediaService.saveFile(
|
const url0 = await mediaService.saveFile(testImage, user, note1);
|
||||||
testImage,
|
|
||||||
'hardcoded',
|
|
||||||
note1.publicId,
|
|
||||||
);
|
|
||||||
const dbUser = await userService.getUserByUsername('hardcoded');
|
const dbUser = await userService.getUserByUsername('hardcoded');
|
||||||
expect(dbUser).toBeInstanceOf(User);
|
expect(dbUser).toBeInstanceOf(User);
|
||||||
const mediaUploads = await mediaService.listUploadsByUser(dbUser);
|
const mediaUploads = await mediaService.listUploadsByUser(dbUser);
|
||||||
|
|
|
@ -157,8 +157,8 @@ describe('Notes', () => {
|
||||||
describe('works', () => {
|
describe('works', () => {
|
||||||
it('with an existing alias and keepMedia false', async () => {
|
it('with an existing alias and keepMedia false', async () => {
|
||||||
const noteId = 'test3';
|
const noteId = 'test3';
|
||||||
await notesService.createNote(content, noteId, user);
|
const note = await notesService.createNote(content, noteId, user);
|
||||||
await mediaService.saveFile(testImage, user.userName, noteId);
|
await mediaService.saveFile(testImage, user, note);
|
||||||
await request(app.getHttpServer())
|
await request(app.getHttpServer())
|
||||||
.delete(`/notes/${noteId}`)
|
.delete(`/notes/${noteId}`)
|
||||||
.set('Content-Type', 'application/json')
|
.set('Content-Type', 'application/json')
|
||||||
|
@ -174,12 +174,8 @@ describe('Notes', () => {
|
||||||
});
|
});
|
||||||
it('with an existing alias and keepMedia true', async () => {
|
it('with an existing alias and keepMedia true', async () => {
|
||||||
const noteId = 'test3a';
|
const noteId = 'test3a';
|
||||||
await notesService.createNote(content, noteId, user);
|
const note = await notesService.createNote(content, noteId, user);
|
||||||
const url = await mediaService.saveFile(
|
const url = await mediaService.saveFile(testImage, user, note);
|
||||||
testImage,
|
|
||||||
user.userName,
|
|
||||||
noteId,
|
|
||||||
);
|
|
||||||
await request(app.getHttpServer())
|
await request(app.getHttpServer())
|
||||||
.delete(`/notes/${noteId}`)
|
.delete(`/notes/${noteId}`)
|
||||||
.set('Content-Type', 'application/json')
|
.set('Content-Type', 'application/json')
|
||||||
|
@ -263,8 +259,8 @@ describe('Notes', () => {
|
||||||
it('works', async () => {
|
it('works', async () => {
|
||||||
const alias = 'test6';
|
const alias = 'test6';
|
||||||
const extraAlias = 'test7';
|
const extraAlias = 'test7';
|
||||||
await notesService.createNote(content, alias, user);
|
const note1 = await notesService.createNote(content, alias, user);
|
||||||
await notesService.createNote(content, extraAlias, user);
|
const note2 = await notesService.createNote(content, extraAlias, user);
|
||||||
const httpServer = app.getHttpServer();
|
const httpServer = app.getHttpServer();
|
||||||
const response = await request(httpServer)
|
const response = await request(httpServer)
|
||||||
.get(`/notes/${alias}/media/`)
|
.get(`/notes/${alias}/media/`)
|
||||||
|
@ -273,12 +269,8 @@ describe('Notes', () => {
|
||||||
expect(response.body).toHaveLength(0);
|
expect(response.body).toHaveLength(0);
|
||||||
|
|
||||||
const testImage = await fs.readFile('test/private-api/fixtures/test.png');
|
const testImage = await fs.readFile('test/private-api/fixtures/test.png');
|
||||||
const url0 = await mediaService.saveFile(testImage, 'hardcoded', alias);
|
const url0 = await mediaService.saveFile(testImage, user, note1);
|
||||||
const url1 = await mediaService.saveFile(
|
const url1 = await mediaService.saveFile(testImage, user, note2);
|
||||||
testImage,
|
|
||||||
'hardcoded',
|
|
||||||
extraAlias,
|
|
||||||
);
|
|
||||||
|
|
||||||
const responseAfter = await request(httpServer)
|
const responseAfter = await request(httpServer)
|
||||||
.get(`/notes/${alias}/media/`)
|
.get(`/notes/${alias}/media/`)
|
||||||
|
|
|
@ -96,10 +96,8 @@ describe('Me', () => {
|
||||||
it(`GET /me/history`, async () => {
|
it(`GET /me/history`, async () => {
|
||||||
const noteName = 'testGetNoteHistory1';
|
const noteName = 'testGetNoteHistory1';
|
||||||
const note = await notesService.createNote('', noteName);
|
const note = await notesService.createNote('', noteName);
|
||||||
const createdHistoryEntry = await historyService.createOrUpdateHistoryEntry(
|
const createdHistoryEntry =
|
||||||
note,
|
await historyService.updateHistoryEntryTimestamp(note, user);
|
||||||
user,
|
|
||||||
);
|
|
||||||
const response = await request(app.getHttpServer())
|
const response = await request(app.getHttpServer())
|
||||||
.get('/me/history')
|
.get('/me/history')
|
||||||
.expect('Content-Type', /json/)
|
.expect('Content-Type', /json/)
|
||||||
|
@ -123,7 +121,7 @@ describe('Me', () => {
|
||||||
const noteName = 'testGetNoteHistory2';
|
const noteName = 'testGetNoteHistory2';
|
||||||
const note = await notesService.createNote('', noteName);
|
const note = await notesService.createNote('', noteName);
|
||||||
const createdHistoryEntry =
|
const createdHistoryEntry =
|
||||||
await historyService.createOrUpdateHistoryEntry(note, user);
|
await historyService.updateHistoryEntryTimestamp(note, user);
|
||||||
const response = await request(app.getHttpServer())
|
const response = await request(app.getHttpServer())
|
||||||
.get(`/me/history/${noteName}`)
|
.get(`/me/history/${noteName}`)
|
||||||
.expect('Content-Type', /json/)
|
.expect('Content-Type', /json/)
|
||||||
|
@ -151,7 +149,7 @@ describe('Me', () => {
|
||||||
it('works', async () => {
|
it('works', async () => {
|
||||||
const noteName = 'testGetNoteHistory3';
|
const noteName = 'testGetNoteHistory3';
|
||||||
const note = await notesService.createNote('', noteName);
|
const note = await notesService.createNote('', noteName);
|
||||||
await historyService.createOrUpdateHistoryEntry(note, user);
|
await historyService.updateHistoryEntryTimestamp(note, user);
|
||||||
const historyEntryUpdateDto = new HistoryEntryUpdateDto();
|
const historyEntryUpdateDto = new HistoryEntryUpdateDto();
|
||||||
historyEntryUpdateDto.pinStatus = true;
|
historyEntryUpdateDto.pinStatus = true;
|
||||||
const response = await request(app.getHttpServer())
|
const response = await request(app.getHttpServer())
|
||||||
|
@ -181,7 +179,7 @@ describe('Me', () => {
|
||||||
it('works', async () => {
|
it('works', async () => {
|
||||||
const noteName = 'testGetNoteHistory4';
|
const noteName = 'testGetNoteHistory4';
|
||||||
const note = await notesService.createNote('', noteName);
|
const note = await notesService.createNote('', noteName);
|
||||||
await historyService.createOrUpdateHistoryEntry(note, user);
|
await historyService.updateHistoryEntryTimestamp(note, user);
|
||||||
const response = await request(app.getHttpServer())
|
const response = await request(app.getHttpServer())
|
||||||
.delete(`/me/history/${noteName}`)
|
.delete(`/me/history/${noteName}`)
|
||||||
.expect(204);
|
.expect(204);
|
||||||
|
@ -243,26 +241,10 @@ describe('Me', () => {
|
||||||
expect(response1.body).toHaveLength(0);
|
expect(response1.body).toHaveLength(0);
|
||||||
|
|
||||||
const testImage = await fs.readFile('test/public-api/fixtures/test.png');
|
const testImage = await fs.readFile('test/public-api/fixtures/test.png');
|
||||||
const url0 = await mediaService.saveFile(
|
const url0 = await mediaService.saveFile(testImage, user, note1);
|
||||||
testImage,
|
const url1 = await mediaService.saveFile(testImage, user, note1);
|
||||||
'hardcoded',
|
const url2 = await mediaService.saveFile(testImage, user, note2);
|
||||||
note1.publicId,
|
const url3 = await mediaService.saveFile(testImage, user, note2);
|
||||||
);
|
|
||||||
const url1 = await mediaService.saveFile(
|
|
||||||
testImage,
|
|
||||||
'hardcoded',
|
|
||||||
note1.publicId,
|
|
||||||
);
|
|
||||||
const url2 = await mediaService.saveFile(
|
|
||||||
testImage,
|
|
||||||
'hardcoded',
|
|
||||||
note2.publicId,
|
|
||||||
);
|
|
||||||
const url3 = await mediaService.saveFile(
|
|
||||||
testImage,
|
|
||||||
'hardcoded',
|
|
||||||
note2.publicId,
|
|
||||||
);
|
|
||||||
|
|
||||||
const response = await request(httpServer)
|
const response = await request(httpServer)
|
||||||
.get('/me/media/')
|
.get('/me/media/')
|
||||||
|
|
|
@ -22,15 +22,20 @@ import { ConsoleLoggerService } from '../../src/logger/console-logger.service';
|
||||||
import { LoggerModule } from '../../src/logger/logger.module';
|
import { LoggerModule } from '../../src/logger/logger.module';
|
||||||
import { MediaModule } from '../../src/media/media.module';
|
import { MediaModule } from '../../src/media/media.module';
|
||||||
import { MediaService } from '../../src/media/media.service';
|
import { MediaService } from '../../src/media/media.service';
|
||||||
|
import { Note } from '../../src/notes/note.entity';
|
||||||
import { NotesModule } from '../../src/notes/notes.module';
|
import { NotesModule } from '../../src/notes/notes.module';
|
||||||
import { NotesService } from '../../src/notes/notes.service';
|
import { NotesService } from '../../src/notes/notes.service';
|
||||||
import { PermissionsModule } from '../../src/permissions/permissions.module';
|
import { PermissionsModule } from '../../src/permissions/permissions.module';
|
||||||
|
import { User } from '../../src/users/user.entity';
|
||||||
|
import { UsersService } from '../../src/users/users.service';
|
||||||
import { ensureDeleted } from '../utils';
|
import { ensureDeleted } from '../utils';
|
||||||
|
|
||||||
describe('Media', () => {
|
describe('Media', () => {
|
||||||
let app: NestExpressApplication;
|
let app: NestExpressApplication;
|
||||||
let mediaService: MediaService;
|
let mediaService: MediaService;
|
||||||
let uploadPath: string;
|
let uploadPath: string;
|
||||||
|
let testNote: Note;
|
||||||
|
let user: User;
|
||||||
|
|
||||||
beforeAll(async () => {
|
beforeAll(async () => {
|
||||||
const moduleRef = await Test.createTestingModule({
|
const moduleRef = await Test.createTestingModule({
|
||||||
|
@ -69,7 +74,12 @@ describe('Media', () => {
|
||||||
logger.log('Switching logger', 'AppBootstrap');
|
logger.log('Switching logger', 'AppBootstrap');
|
||||||
app.useLogger(logger);
|
app.useLogger(logger);
|
||||||
const notesService: NotesService = moduleRef.get(NotesService);
|
const notesService: NotesService = moduleRef.get(NotesService);
|
||||||
await notesService.createNote('test content', 'test_upload_media');
|
const userService = moduleRef.get(UsersService);
|
||||||
|
user = await userService.createUser('hardcoded', 'Testy');
|
||||||
|
testNote = await notesService.createNote(
|
||||||
|
'test content',
|
||||||
|
'test_upload_media',
|
||||||
|
);
|
||||||
mediaService = moduleRef.get(MediaService);
|
mediaService = moduleRef.get(MediaService);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -129,11 +139,7 @@ describe('Media', () => {
|
||||||
|
|
||||||
it('DELETE /media/{filename}', async () => {
|
it('DELETE /media/{filename}', async () => {
|
||||||
const testImage = await fs.readFile('test/public-api/fixtures/test.png');
|
const testImage = await fs.readFile('test/public-api/fixtures/test.png');
|
||||||
const url = await mediaService.saveFile(
|
const url = await mediaService.saveFile(testImage, user, testNote);
|
||||||
testImage,
|
|
||||||
'hardcoded',
|
|
||||||
'test_upload_media',
|
|
||||||
);
|
|
||||||
const filename = url.split('/').pop() || '';
|
const filename = url.split('/').pop() || '';
|
||||||
await request(app.getHttpServer())
|
await request(app.getHttpServer())
|
||||||
.delete('/media/' + filename)
|
.delete('/media/' + filename)
|
||||||
|
|
|
@ -113,6 +113,13 @@ describe('Notes', () => {
|
||||||
.expect('Content-Type', /json/)
|
.expect('Content-Type', /json/)
|
||||||
.expect(404);
|
.expect(404);
|
||||||
});
|
});
|
||||||
|
it('fails with a forbidden note id', async () => {
|
||||||
|
// check if a forbidden note correctly returns 400
|
||||||
|
await request(app.getHttpServer())
|
||||||
|
.get('/notes/forbiddenNoteId')
|
||||||
|
.expect('Content-Type', /json/)
|
||||||
|
.expect(400);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('POST /notes/{note}', () => {
|
describe('POST /notes/{note}', () => {
|
||||||
|
@ -154,8 +161,8 @@ describe('Notes', () => {
|
||||||
describe('works', () => {
|
describe('works', () => {
|
||||||
it('with an existing alias and keepMedia false', async () => {
|
it('with an existing alias and keepMedia false', async () => {
|
||||||
const noteId = 'test3';
|
const noteId = 'test3';
|
||||||
await notesService.createNote(content, noteId, user);
|
const note = await notesService.createNote(content, noteId, user);
|
||||||
await mediaService.saveFile(testImage, user.userName, noteId);
|
await mediaService.saveFile(testImage, user, note);
|
||||||
await request(app.getHttpServer())
|
await request(app.getHttpServer())
|
||||||
.delete(`/notes/${noteId}`)
|
.delete(`/notes/${noteId}`)
|
||||||
.set('Content-Type', 'application/json')
|
.set('Content-Type', 'application/json')
|
||||||
|
@ -170,12 +177,8 @@ describe('Notes', () => {
|
||||||
});
|
});
|
||||||
it('with an existing alias and keepMedia true', async () => {
|
it('with an existing alias and keepMedia true', async () => {
|
||||||
const noteId = 'test3a';
|
const noteId = 'test3a';
|
||||||
await notesService.createNote(content, noteId, user);
|
const note = await notesService.createNote(content, noteId, user);
|
||||||
const url = await mediaService.saveFile(
|
const url = await mediaService.saveFile(testImage, user, note);
|
||||||
testImage,
|
|
||||||
user.userName,
|
|
||||||
noteId,
|
|
||||||
);
|
|
||||||
await request(app.getHttpServer())
|
await request(app.getHttpServer())
|
||||||
.delete(`/notes/${noteId}`)
|
.delete(`/notes/${noteId}`)
|
||||||
.set('Content-Type', 'application/json')
|
.set('Content-Type', 'application/json')
|
||||||
|
@ -395,8 +398,8 @@ describe('Notes', () => {
|
||||||
it('works', async () => {
|
it('works', async () => {
|
||||||
const alias = 'test9';
|
const alias = 'test9';
|
||||||
const extraAlias = 'test10';
|
const extraAlias = 'test10';
|
||||||
await notesService.createNote(content, alias, user);
|
const note1 = await notesService.createNote(content, alias, user);
|
||||||
await notesService.createNote(content, extraAlias, user);
|
const note2 = await notesService.createNote(content, extraAlias, user);
|
||||||
const httpServer = app.getHttpServer();
|
const httpServer = app.getHttpServer();
|
||||||
const response = await request(httpServer)
|
const response = await request(httpServer)
|
||||||
.get(`/notes/${alias}/media/`)
|
.get(`/notes/${alias}/media/`)
|
||||||
|
@ -405,12 +408,8 @@ describe('Notes', () => {
|
||||||
expect(response.body).toHaveLength(0);
|
expect(response.body).toHaveLength(0);
|
||||||
|
|
||||||
const testImage = await fs.readFile('test/public-api/fixtures/test.png');
|
const testImage = await fs.readFile('test/public-api/fixtures/test.png');
|
||||||
const url0 = await mediaService.saveFile(testImage, 'hardcoded', alias);
|
const url0 = await mediaService.saveFile(testImage, user, note1);
|
||||||
const url1 = await mediaService.saveFile(
|
const url1 = await mediaService.saveFile(testImage, user, note2);
|
||||||
testImage,
|
|
||||||
'hardcoded',
|
|
||||||
extraAlias,
|
|
||||||
);
|
|
||||||
|
|
||||||
const responseAfter = await request(httpServer)
|
const responseAfter = await request(httpServer)
|
||||||
.get(`/notes/${alias}/media/`)
|
.get(`/notes/${alias}/media/`)
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue