mirror of
https://github.com/hedgedoc/hedgedoc.git
synced 2025-03-21 03:24:19 +00:00
Merge pull request #848 from hedgedoc/feature/noteService
This commit is contained in:
commit
3f05fa4852
24 changed files with 1273 additions and 157 deletions
|
@ -22,6 +22,7 @@ import { MeController } from './me.controller';
|
|||
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';
|
||||
|
||||
describe('Me Controller', () => {
|
||||
let controller: MeController;
|
||||
|
@ -53,6 +54,8 @@ describe('Me Controller', () => {
|
|||
.useValue({})
|
||||
.overrideProvider(getRepositoryToken(NoteUserPermission))
|
||||
.useValue({})
|
||||
.overrideProvider(getRepositoryToken(Group))
|
||||
.useValue({})
|
||||
.compile();
|
||||
|
||||
controller = module.get<MeController>(MeController);
|
||||
|
|
|
@ -24,6 +24,7 @@ import { User } from '../../../users/user.entity';
|
|||
import { MediaController } from './media.controller';
|
||||
import { NoteGroupPermission } from '../../../permissions/note-group-permission.entity';
|
||||
import { NoteUserPermission } from '../../../permissions/note-user-permission.entity';
|
||||
import { Group } from '../../../groups/group.entity';
|
||||
|
||||
describe('Media Controller', () => {
|
||||
let controller: MediaController;
|
||||
|
@ -63,6 +64,8 @@ describe('Media Controller', () => {
|
|||
.useValue({})
|
||||
.overrideProvider(getRepositoryToken(NoteUserPermission))
|
||||
.useValue({})
|
||||
.overrideProvider(getRepositoryToken(Group))
|
||||
.useValue({})
|
||||
.compile();
|
||||
|
||||
controller = module.get<MediaController>(MediaController);
|
||||
|
|
|
@ -24,6 +24,8 @@ import { HistoryModule } from '../../../history/history.module';
|
|||
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 { GroupsModule } from '../../../groups/groups.module';
|
||||
|
||||
describe('Notes Controller', () => {
|
||||
let controller: NotesController;
|
||||
|
@ -45,6 +47,7 @@ describe('Notes Controller', () => {
|
|||
imports: [
|
||||
RevisionsModule,
|
||||
UsersModule,
|
||||
GroupsModule,
|
||||
LoggerModule,
|
||||
PermissionsModule,
|
||||
HistoryModule,
|
||||
|
@ -74,6 +77,8 @@ describe('Notes Controller', () => {
|
|||
.useValue({})
|
||||
.overrideProvider(getRepositoryToken(NoteUserPermission))
|
||||
.useValue({})
|
||||
.overrideProvider(getRepositoryToken(Group))
|
||||
.useValue({})
|
||||
.compile();
|
||||
|
||||
controller = module.get<NotesController>(NotesController);
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
*/
|
||||
|
||||
import {
|
||||
BadRequestException,
|
||||
Body,
|
||||
Controller,
|
||||
Delete,
|
||||
|
@ -18,7 +19,11 @@ import {
|
|||
UnauthorizedException,
|
||||
UseGuards,
|
||||
} from '@nestjs/common';
|
||||
import { NotInDBError } from '../../../errors/errors';
|
||||
import {
|
||||
AlreadyInDBError,
|
||||
NotInDBError,
|
||||
PermissionsUpdateInconsistentError,
|
||||
} from '../../../errors/errors';
|
||||
import { ConsoleLoggerService } from '../../../logger/console-logger.service';
|
||||
import {
|
||||
NotePermissionsDto,
|
||||
|
@ -100,9 +105,16 @@ export class NotesController {
|
|||
throw new UnauthorizedException('Creating note denied!');
|
||||
}
|
||||
this.logger.debug('Got raw markdown:\n' + text, 'createNamedNote');
|
||||
return this.noteService.toNoteDto(
|
||||
await this.noteService.createNote(text, noteAlias, req.user),
|
||||
);
|
||||
try {
|
||||
return this.noteService.toNoteDto(
|
||||
await this.noteService.createNote(text, noteAlias, req.user),
|
||||
);
|
||||
} catch (e) {
|
||||
if (e instanceof AlreadyInDBError) {
|
||||
throw new BadRequestException(e.message);
|
||||
}
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
@UseGuards(TokenAuthGuard)
|
||||
|
@ -117,7 +129,7 @@ export class NotesController {
|
|||
throw new UnauthorizedException('Deleting note denied!');
|
||||
}
|
||||
this.logger.debug('Deleting note: ' + noteIdOrAlias, 'deleteNote');
|
||||
await this.noteService.deleteNoteByIdOrAlias(noteIdOrAlias);
|
||||
await this.noteService.deleteNote(note);
|
||||
this.logger.debug('Successfully deleted ' + noteIdOrAlias, 'deleteNote');
|
||||
return;
|
||||
} catch (e) {
|
||||
|
@ -142,7 +154,7 @@ export class NotesController {
|
|||
}
|
||||
this.logger.debug('Got raw markdown:\n' + text, 'updateNote');
|
||||
return this.noteService.toNoteDto(
|
||||
await this.noteService.updateNoteByIdOrAlias(noteIdOrAlias, text),
|
||||
await this.noteService.updateNote(note, text),
|
||||
);
|
||||
} catch (e) {
|
||||
if (e instanceof NotInDBError) {
|
||||
|
@ -164,7 +176,7 @@ export class NotesController {
|
|||
if (!this.permissionsService.mayRead(req.user, note)) {
|
||||
throw new UnauthorizedException('Reading note denied!');
|
||||
}
|
||||
return await this.noteService.getNoteContent(noteIdOrAlias);
|
||||
return await this.noteService.getNoteContentByNote(note);
|
||||
} catch (e) {
|
||||
if (e instanceof NotInDBError) {
|
||||
throw new NotFoundException(e.message);
|
||||
|
@ -184,13 +196,14 @@ export class NotesController {
|
|||
if (!this.permissionsService.mayRead(req.user, note)) {
|
||||
throw new UnauthorizedException('Reading note denied!');
|
||||
}
|
||||
return this.noteService.toNoteMetadataDto(
|
||||
await this.noteService.getNoteByIdOrAlias(noteIdOrAlias),
|
||||
);
|
||||
return this.noteService.toNoteMetadataDto(note);
|
||||
} catch (e) {
|
||||
if (e instanceof NotInDBError) {
|
||||
throw new NotFoundException(e.message);
|
||||
}
|
||||
if (e instanceof PermissionsUpdateInconsistentError) {
|
||||
throw new BadRequestException(e.message);
|
||||
}
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
@ -208,7 +221,7 @@ export class NotesController {
|
|||
throw new UnauthorizedException('Updating note denied!');
|
||||
}
|
||||
return this.noteService.toNotePermissionsDto(
|
||||
await this.noteService.updateNotePermissions(noteIdOrAlias, updateDto),
|
||||
await this.noteService.updateNotePermissions(note, updateDto),
|
||||
);
|
||||
} catch (e) {
|
||||
if (e instanceof NotInDBError) {
|
||||
|
@ -229,9 +242,7 @@ export class NotesController {
|
|||
if (!this.permissionsService.mayRead(req.user, note)) {
|
||||
throw new UnauthorizedException('Reading note denied!');
|
||||
}
|
||||
const revisions = await this.revisionsService.getAllRevisions(
|
||||
noteIdOrAlias,
|
||||
);
|
||||
const revisions = await this.revisionsService.getAllRevisions(note);
|
||||
return Promise.all(
|
||||
revisions.map((revision) =>
|
||||
this.revisionsService.toRevisionMetadataDto(revision),
|
||||
|
@ -258,7 +269,7 @@ export class NotesController {
|
|||
throw new UnauthorizedException('Reading note denied!');
|
||||
}
|
||||
return this.revisionsService.toRevisionDto(
|
||||
await this.revisionsService.getRevision(noteIdOrAlias, revisionId),
|
||||
await this.revisionsService.getRevision(note, revisionId),
|
||||
);
|
||||
} catch (e) {
|
||||
if (e instanceof NotInDBError) {
|
||||
|
|
|
@ -8,6 +8,10 @@ export class NotInDBError extends Error {
|
|||
name = 'NotInDBError';
|
||||
}
|
||||
|
||||
export class AlreadyInDBError extends Error {
|
||||
name = 'AlreadyInDBError';
|
||||
}
|
||||
|
||||
export class ClientError extends Error {
|
||||
name = 'ClientError';
|
||||
}
|
||||
|
@ -23,3 +27,7 @@ export class TokenNotValidError extends Error {
|
|||
export class TooManyTokensError extends Error {
|
||||
name = 'TooManyTokensError';
|
||||
}
|
||||
|
||||
export class PermissionsUpdateInconsistentError extends Error {
|
||||
name = 'PermissionsUpdateInconsistentError';
|
||||
}
|
||||
|
|
30
src/groups/group-info.dto.ts
Normal file
30
src/groups/group-info.dto.ts
Normal file
|
@ -0,0 +1,30 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file)
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
import { IsBoolean, IsString } from 'class-validator';
|
||||
|
||||
export class GroupInfoDto {
|
||||
/**
|
||||
* Name of the group
|
||||
* @example "superheroes"
|
||||
*/
|
||||
@IsString()
|
||||
name: string;
|
||||
|
||||
/**
|
||||
* Display name of this group
|
||||
* @example "Superheroes"
|
||||
*/
|
||||
@IsString()
|
||||
displayName: string;
|
||||
|
||||
/**
|
||||
* True if this group must be specially handled
|
||||
* Used for e.g. "everybody", "all logged in users"
|
||||
* @example false
|
||||
*/
|
||||
@IsBoolean()
|
||||
special: boolean;
|
||||
}
|
|
@ -40,4 +40,15 @@ export class Group {
|
|||
})
|
||||
@JoinTable()
|
||||
members: User[];
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-empty-function
|
||||
private constructor() {}
|
||||
|
||||
public static create(name: string, displayName: string): Group {
|
||||
const newGroup = new Group();
|
||||
newGroup.special = false; // this attribute should only be true for the two special groups
|
||||
newGroup.name = name;
|
||||
newGroup.displayName = displayName;
|
||||
return newGroup;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,8 +7,12 @@
|
|||
import { Module } from '@nestjs/common';
|
||||
import { TypeOrmModule } from '@nestjs/typeorm';
|
||||
import { Group } from './group.entity';
|
||||
import { GroupsService } from './groups.service';
|
||||
import { LoggerModule } from '../logger/logger.module';
|
||||
|
||||
@Module({
|
||||
imports: [TypeOrmModule.forFeature([Group])],
|
||||
imports: [TypeOrmModule.forFeature([Group]), LoggerModule],
|
||||
providers: [GroupsService],
|
||||
exports: [GroupsService],
|
||||
})
|
||||
export class GroupsModule {}
|
||||
|
|
75
src/groups/groups.service.spec.ts
Normal file
75
src/groups/groups.service.spec.ts
Normal file
|
@ -0,0 +1,75 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file)
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
import { Test, TestingModule } from '@nestjs/testing';
|
||||
import { GroupsService } from './groups.service';
|
||||
import { getRepositoryToken } from '@nestjs/typeorm';
|
||||
import { Repository } from 'typeorm';
|
||||
import { Group } from './group.entity';
|
||||
import { NotInDBError } from '../errors/errors';
|
||||
import { LoggerModule } from '../logger/logger.module';
|
||||
|
||||
describe('GroupsService', () => {
|
||||
let service: GroupsService;
|
||||
let groupRepo: Repository<Group>;
|
||||
let group: Group;
|
||||
|
||||
beforeAll(async () => {
|
||||
const module: TestingModule = await Test.createTestingModule({
|
||||
providers: [
|
||||
GroupsService,
|
||||
{
|
||||
provide: getRepositoryToken(Group),
|
||||
useClass: Repository,
|
||||
},
|
||||
],
|
||||
imports: [LoggerModule],
|
||||
}).compile();
|
||||
|
||||
service = module.get<GroupsService>(GroupsService);
|
||||
groupRepo = module.get<Repository<Group>>(getRepositoryToken(Group));
|
||||
group = Group.create('testGroup', 'Superheros') as Group;
|
||||
});
|
||||
|
||||
it('should be defined', () => {
|
||||
expect(service).toBeDefined();
|
||||
});
|
||||
|
||||
describe('getGroupByName', () => {
|
||||
it('works', async () => {
|
||||
jest.spyOn(groupRepo, 'findOne').mockResolvedValueOnce(group);
|
||||
const foundGroup = await service.getGroupByName(group.name);
|
||||
expect(foundGroup.name).toEqual(group.name);
|
||||
expect(foundGroup.displayName).toEqual(group.displayName);
|
||||
expect(foundGroup.special).toEqual(group.special);
|
||||
});
|
||||
it('fails with non-existing group', async () => {
|
||||
jest.spyOn(groupRepo, 'findOne').mockResolvedValueOnce(undefined);
|
||||
try {
|
||||
await service.getGroupByName('i_dont_exist');
|
||||
} catch (e) {
|
||||
expect(e).toBeInstanceOf(NotInDBError);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
describe('toGroupDto', () => {
|
||||
it('works', () => {
|
||||
const groupDto = service.toGroupDto(group);
|
||||
expect(groupDto.displayName).toEqual(group.displayName);
|
||||
expect(groupDto.name).toEqual(group.name);
|
||||
expect(groupDto.special).toBeFalsy();
|
||||
});
|
||||
it('fails with null parameter', () => {
|
||||
const groupDto = service.toGroupDto(null);
|
||||
expect(groupDto).toBeNull();
|
||||
});
|
||||
it('fails with undefined parameter', () => {
|
||||
const groupDto = service.toGroupDto(undefined);
|
||||
expect(groupDto).toBeNull();
|
||||
});
|
||||
});
|
||||
});
|
57
src/groups/groups.service.ts
Normal file
57
src/groups/groups.service.ts
Normal file
|
@ -0,0 +1,57 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file)
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { ConsoleLoggerService } from '../logger/console-logger.service';
|
||||
import { InjectRepository } from '@nestjs/typeorm';
|
||||
import { Repository } from 'typeorm';
|
||||
import { Group } from './group.entity';
|
||||
import { NotInDBError } from '../errors/errors';
|
||||
import { GroupInfoDto } from './group-info.dto';
|
||||
|
||||
@Injectable()
|
||||
export class GroupsService {
|
||||
constructor(
|
||||
private readonly logger: ConsoleLoggerService,
|
||||
@InjectRepository(Group) private groupRepository: Repository<Group>,
|
||||
) {
|
||||
this.logger.setContext(GroupsService.name);
|
||||
}
|
||||
|
||||
/**
|
||||
* @async
|
||||
* Get a group by their name.
|
||||
* @param {string} name - the groups name
|
||||
* @return {Group} the group
|
||||
* @throws {NotInDBError} there is no group with this name
|
||||
*/
|
||||
async getGroupByName(name: string): Promise<Group> {
|
||||
const group = await this.groupRepository.findOne({
|
||||
where: { name: name },
|
||||
});
|
||||
if (group === undefined) {
|
||||
throw new NotInDBError(`Group with name '${name}' not found`);
|
||||
}
|
||||
return group;
|
||||
}
|
||||
|
||||
/**
|
||||
* Build GroupInfoDto from a group.
|
||||
* @param {Group} group - the group to use
|
||||
* @return {GroupInfoDto} the built GroupInfoDto
|
||||
*/
|
||||
toGroupDto(group: Group | null | undefined): GroupInfoDto | null {
|
||||
if (!group) {
|
||||
this.logger.warn(`Recieved ${group} argument!`, 'toGroupDto');
|
||||
return null;
|
||||
}
|
||||
return {
|
||||
name: group.name,
|
||||
displayName: group.displayName,
|
||||
special: group.special,
|
||||
};
|
||||
}
|
||||
}
|
|
@ -23,6 +23,7 @@ import { Repository } from 'typeorm';
|
|||
import { NotInDBError } from '../errors/errors';
|
||||
import { NoteGroupPermission } from '../permissions/note-group-permission.entity';
|
||||
import { NoteUserPermission } from '../permissions/note-user-permission.entity';
|
||||
import { Group } from '../groups/group.entity';
|
||||
|
||||
describe('HistoryService', () => {
|
||||
let service: HistoryService;
|
||||
|
@ -60,6 +61,8 @@ describe('HistoryService', () => {
|
|||
.useValue({})
|
||||
.overrideProvider(getRepositoryToken(NoteUserPermission))
|
||||
.useValue({})
|
||||
.overrideProvider(getRepositoryToken(Group))
|
||||
.useValue({})
|
||||
.compile();
|
||||
|
||||
service = module.get<HistoryService>(HistoryService);
|
||||
|
|
|
@ -27,6 +27,7 @@ import { promises as fs } from 'fs';
|
|||
import { ClientError, NotInDBError, PermissionError } from '../errors/errors';
|
||||
import { NoteGroupPermission } from '../permissions/note-group-permission.entity';
|
||||
import { NoteUserPermission } from '../permissions/note-user-permission.entity';
|
||||
import { Group } from '../groups/group.entity';
|
||||
|
||||
describe('MediaService', () => {
|
||||
let service: MediaService;
|
||||
|
@ -78,6 +79,8 @@ describe('MediaService', () => {
|
|||
.useValue({})
|
||||
.overrideProvider(getRepositoryToken(MediaUpload))
|
||||
.useClass(Repository)
|
||||
.overrideProvider(getRepositoryToken(Group))
|
||||
.useValue({})
|
||||
.compile();
|
||||
|
||||
service = module.get<MediaService>(MediaService);
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
|
||||
import { IsArray, IsBoolean, IsString, ValidateNested } from 'class-validator';
|
||||
import { UserInfoDto } from '../users/user-info.dto';
|
||||
import { GroupInfoDto } from '../groups/group-info.dto';
|
||||
|
||||
export class NoteUserPermissionEntryDto {
|
||||
/**
|
||||
|
@ -38,30 +39,6 @@ export class NoteUserPermissionUpdateDto {
|
|||
canEdit: boolean;
|
||||
}
|
||||
|
||||
export class GroupInfoDto {
|
||||
/**
|
||||
* Name of the group
|
||||
* @example "superheroes"
|
||||
*/
|
||||
@IsString()
|
||||
name: string;
|
||||
|
||||
/**
|
||||
* Display name of this group
|
||||
* @example "Superheroes"
|
||||
*/
|
||||
@IsString()
|
||||
displayName: string;
|
||||
|
||||
/**
|
||||
* True if this group must be specially handled
|
||||
* Used for e.g. "everybody", "all logged in users"
|
||||
* @example false
|
||||
*/
|
||||
@IsBoolean()
|
||||
special: boolean;
|
||||
}
|
||||
|
||||
export class NoteGroupPermissionEntryDto {
|
||||
/**
|
||||
* Group this permission applies to
|
||||
|
|
|
@ -15,6 +15,7 @@ import { NotesService } from './notes.service';
|
|||
import { Tag } from './tag.entity';
|
||||
import { NoteGroupPermission } from '../permissions/note-group-permission.entity';
|
||||
import { NoteUserPermission } from '../permissions/note-user-permission.entity';
|
||||
import { GroupsModule } from '../groups/groups.module';
|
||||
|
||||
@Module({
|
||||
imports: [
|
||||
|
@ -27,6 +28,7 @@ import { NoteUserPermission } from '../permissions/note-user-permission.entity';
|
|||
]),
|
||||
forwardRef(() => RevisionsModule),
|
||||
UsersModule,
|
||||
GroupsModule,
|
||||
LoggerModule,
|
||||
],
|
||||
controllers: [],
|
||||
|
|
|
@ -17,12 +17,27 @@ import { UsersModule } from '../users/users.module';
|
|||
import { AuthorColor } from './author-color.entity';
|
||||
import { Note } from './note.entity';
|
||||
import { NotesService } from './notes.service';
|
||||
import { Repository } from 'typeorm';
|
||||
import { Tag } from './tag.entity';
|
||||
import {
|
||||
NotInDBError,
|
||||
PermissionsUpdateInconsistentError,
|
||||
} from '../errors/errors';
|
||||
import {
|
||||
NoteGroupPermissionUpdateDto,
|
||||
NoteUserPermissionUpdateDto,
|
||||
} from './note-permissions.dto';
|
||||
import { NoteGroupPermission } from '../permissions/note-group-permission.entity';
|
||||
import { NoteUserPermission } from '../permissions/note-user-permission.entity';
|
||||
import { GroupsModule } from '../groups/groups.module';
|
||||
import { Group } from '../groups/group.entity';
|
||||
|
||||
describe('NotesService', () => {
|
||||
let service: NotesService;
|
||||
let noteRepo: Repository<Note>;
|
||||
let revisionRepo: Repository<Revision>;
|
||||
let userRepo: Repository<User>;
|
||||
let groupRepo: Repository<Group>;
|
||||
|
||||
beforeEach(async () => {
|
||||
const module: TestingModule = await Test.createTestingModule({
|
||||
|
@ -30,17 +45,21 @@ describe('NotesService', () => {
|
|||
NotesService,
|
||||
{
|
||||
provide: getRepositoryToken(Note),
|
||||
useValue: {},
|
||||
useClass: Repository,
|
||||
},
|
||||
{
|
||||
provide: getRepositoryToken(Tag),
|
||||
useValue: {},
|
||||
useClass: Repository,
|
||||
},
|
||||
],
|
||||
imports: [UsersModule, RevisionsModule, LoggerModule],
|
||||
imports: [LoggerModule, UsersModule, GroupsModule, RevisionsModule],
|
||||
})
|
||||
.overrideProvider(getRepositoryToken(Note))
|
||||
.useClass(Repository)
|
||||
.overrideProvider(getRepositoryToken(Tag))
|
||||
.useClass(Repository)
|
||||
.overrideProvider(getRepositoryToken(User))
|
||||
.useValue({})
|
||||
.useClass(Repository)
|
||||
.overrideProvider(getRepositoryToken(AuthToken))
|
||||
.useValue({})
|
||||
.overrideProvider(getRepositoryToken(Identity))
|
||||
|
@ -50,20 +69,764 @@ describe('NotesService', () => {
|
|||
.overrideProvider(getRepositoryToken(AuthorColor))
|
||||
.useValue({})
|
||||
.overrideProvider(getRepositoryToken(Revision))
|
||||
.useValue({})
|
||||
.overrideProvider(getRepositoryToken(Note))
|
||||
.useValue({})
|
||||
.overrideProvider(getRepositoryToken(Tag))
|
||||
.useValue({})
|
||||
.useClass(Repository)
|
||||
.overrideProvider(getRepositoryToken(NoteGroupPermission))
|
||||
.useValue({})
|
||||
.overrideProvider(getRepositoryToken(NoteUserPermission))
|
||||
.useValue({})
|
||||
.overrideProvider(getRepositoryToken(Group))
|
||||
.useClass(Repository)
|
||||
.compile();
|
||||
|
||||
service = module.get<NotesService>(NotesService);
|
||||
noteRepo = module.get<Repository<Note>>(getRepositoryToken(Note));
|
||||
revisionRepo = module.get<Repository<Revision>>(
|
||||
getRepositoryToken(Revision),
|
||||
);
|
||||
userRepo = module.get<Repository<User>>(getRepositoryToken(User));
|
||||
groupRepo = module.get<Repository<Group>>(getRepositoryToken(Group));
|
||||
});
|
||||
|
||||
it('should be defined', () => {
|
||||
expect(service).toBeDefined();
|
||||
});
|
||||
|
||||
describe('getUserNotes', () => {
|
||||
describe('works', () => {
|
||||
const user = User.create('hardcoded', 'Testy') as User;
|
||||
const alias = 'alias';
|
||||
const note = Note.create(user, alias);
|
||||
|
||||
it('with no note', async () => {
|
||||
jest.spyOn(noteRepo, 'find').mockResolvedValueOnce(undefined);
|
||||
const notes = await service.getUserNotes(user);
|
||||
expect(notes).toEqual([]);
|
||||
});
|
||||
|
||||
it('with one note', async () => {
|
||||
jest.spyOn(noteRepo, 'find').mockResolvedValueOnce([note]);
|
||||
const notes = await service.getUserNotes(user);
|
||||
expect(notes).toEqual([note]);
|
||||
});
|
||||
|
||||
it('with multiple note', async () => {
|
||||
jest.spyOn(noteRepo, 'find').mockResolvedValueOnce([note, note]);
|
||||
const notes = await service.getUserNotes(user);
|
||||
expect(notes).toEqual([note, note]);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('createNote', () => {
|
||||
describe('works', () => {
|
||||
const user = User.create('hardcoded', 'Testy') as User;
|
||||
const alias = 'alias';
|
||||
const content = 'testContent';
|
||||
beforeEach(() => {
|
||||
jest
|
||||
.spyOn(noteRepo, 'save')
|
||||
.mockImplementation(async (note: Note): Promise<Note> => note);
|
||||
});
|
||||
it('without alias, without owner', async () => {
|
||||
const newNote = await service.createNote(content);
|
||||
const revisions = await newNote.revisions;
|
||||
expect(revisions).toHaveLength(1);
|
||||
expect(revisions[0].content).toEqual(content);
|
||||
expect(newNote.historyEntries).toBeUndefined();
|
||||
expect(newNote.userPermissions).toHaveLength(0);
|
||||
expect(newNote.groupPermissions).toHaveLength(0);
|
||||
expect(newNote.tags).toHaveLength(0);
|
||||
expect(newNote.owner).toBeUndefined();
|
||||
expect(newNote.alias).toBeUndefined();
|
||||
});
|
||||
it('without alias, with owner', async () => {
|
||||
const newNote = await service.createNote(content, undefined, user);
|
||||
const revisions = await newNote.revisions;
|
||||
expect(revisions).toHaveLength(1);
|
||||
expect(revisions[0].content).toEqual(content);
|
||||
expect(newNote.historyEntries).toHaveLength(1);
|
||||
expect(newNote.historyEntries[0].user).toEqual(user);
|
||||
expect(newNote.userPermissions).toHaveLength(0);
|
||||
expect(newNote.groupPermissions).toHaveLength(0);
|
||||
expect(newNote.tags).toHaveLength(0);
|
||||
expect(newNote.owner).toEqual(user);
|
||||
expect(newNote.alias).toBeUndefined();
|
||||
});
|
||||
it('with alias, without owner', async () => {
|
||||
const newNote = await service.createNote(content, alias);
|
||||
const revisions = await newNote.revisions;
|
||||
expect(revisions).toHaveLength(1);
|
||||
expect(revisions[0].content).toEqual(content);
|
||||
expect(newNote.historyEntries).toBeUndefined();
|
||||
expect(newNote.userPermissions).toHaveLength(0);
|
||||
expect(newNote.groupPermissions).toHaveLength(0);
|
||||
expect(newNote.tags).toHaveLength(0);
|
||||
expect(newNote.owner).toBeUndefined();
|
||||
expect(newNote.alias).toEqual(alias);
|
||||
});
|
||||
it('with alias, with owner', async () => {
|
||||
const newNote = await service.createNote(content, alias, user);
|
||||
const revisions = await newNote.revisions;
|
||||
expect(revisions).toHaveLength(1);
|
||||
expect(revisions[0].content).toEqual(content);
|
||||
expect(newNote.historyEntries).toHaveLength(1);
|
||||
expect(newNote.historyEntries[0].user).toEqual(user);
|
||||
expect(newNote.userPermissions).toHaveLength(0);
|
||||
expect(newNote.groupPermissions).toHaveLength(0);
|
||||
expect(newNote.tags).toHaveLength(0);
|
||||
expect(newNote.owner).toEqual(user);
|
||||
expect(newNote.alias).toEqual(alias);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('getNoteContentByNote', () => {
|
||||
it('works', async () => {
|
||||
const content = 'testContent';
|
||||
jest
|
||||
.spyOn(noteRepo, 'save')
|
||||
.mockImplementation(async (note: Note): Promise<Note> => note);
|
||||
const newNote = await service.createNote(content);
|
||||
const revisions = await newNote.revisions;
|
||||
jest.spyOn(revisionRepo, 'findOne').mockResolvedValueOnce(revisions[0]);
|
||||
service.getNoteContentByNote(newNote).then((result) => {
|
||||
expect(result).toEqual(content);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('getLatestRevision', () => {
|
||||
it('works', async () => {
|
||||
const content = 'testContent';
|
||||
jest
|
||||
.spyOn(noteRepo, 'save')
|
||||
.mockImplementation(async (note: Note): Promise<Note> => note);
|
||||
const newNote = await service.createNote(content);
|
||||
const revisions = await newNote.revisions;
|
||||
jest.spyOn(revisionRepo, 'findOne').mockResolvedValueOnce(revisions[0]);
|
||||
service.getLatestRevision(newNote).then((result) => {
|
||||
expect(result).toEqual(revisions[0]);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('getFirstRevision', () => {
|
||||
it('works', async () => {
|
||||
const user = {} as User;
|
||||
user.userName = 'hardcoded';
|
||||
const content = 'testContent';
|
||||
jest
|
||||
.spyOn(noteRepo, 'save')
|
||||
.mockImplementation(async (note: Note): Promise<Note> => note);
|
||||
const newNote = await service.createNote(content);
|
||||
const revisions = await newNote.revisions;
|
||||
jest.spyOn(revisionRepo, 'findOne').mockResolvedValueOnce(revisions[0]);
|
||||
service.getLatestRevision(newNote).then((result) => {
|
||||
expect(result).toEqual(revisions[0]);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('getNoteByIdOrAlias', () => {
|
||||
it('works', async () => {
|
||||
const user = User.create('hardcoded', 'Testy') as User;
|
||||
const note = Note.create(user);
|
||||
jest.spyOn(noteRepo, 'findOne').mockResolvedValueOnce(note);
|
||||
const foundNote = await service.getNoteByIdOrAlias('noteThatExists');
|
||||
expect(foundNote).toEqual(note);
|
||||
});
|
||||
it('fails: no note found', async () => {
|
||||
jest.spyOn(noteRepo, 'findOne').mockResolvedValueOnce(undefined);
|
||||
try {
|
||||
await service.getNoteByIdOrAlias('noteThatDoesNoteExist');
|
||||
} catch (e) {
|
||||
expect(e).toBeInstanceOf(NotInDBError);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
describe('deleteNote', () => {
|
||||
it('works', async () => {
|
||||
const user = User.create('hardcoded', 'Testy') as User;
|
||||
const note = Note.create(user);
|
||||
jest
|
||||
.spyOn(noteRepo, 'remove')
|
||||
.mockImplementationOnce(async (entry, _) => {
|
||||
expect(entry).toEqual(note);
|
||||
return entry;
|
||||
});
|
||||
await service.deleteNote(note);
|
||||
});
|
||||
});
|
||||
|
||||
describe('updateNote', () => {
|
||||
it('works', async () => {
|
||||
const user = User.create('hardcoded', 'Testy') as User;
|
||||
const note = Note.create(user);
|
||||
const revisionLength = (await note.revisions).length;
|
||||
jest
|
||||
.spyOn(noteRepo, 'save')
|
||||
.mockImplementationOnce(async (entry: Note) => {
|
||||
return entry;
|
||||
});
|
||||
const updatedNote = await service.updateNote(note, 'newContent');
|
||||
expect(await updatedNote.revisions).toHaveLength(revisionLength + 1);
|
||||
});
|
||||
});
|
||||
|
||||
describe('updateNotePermissions', () => {
|
||||
const userPermissionUpdate = new NoteUserPermissionUpdateDto();
|
||||
userPermissionUpdate.username = 'hardcoded';
|
||||
userPermissionUpdate.canEdit = true;
|
||||
const groupPermissionUpate = new NoteGroupPermissionUpdateDto();
|
||||
groupPermissionUpate.groupname = 'testGroup';
|
||||
groupPermissionUpate.canEdit = false;
|
||||
const user = User.create(userPermissionUpdate.username, 'Testy') as User;
|
||||
const group = Group.create(
|
||||
groupPermissionUpate.groupname,
|
||||
groupPermissionUpate.groupname,
|
||||
);
|
||||
const note = Note.create(user);
|
||||
describe('works', () => {
|
||||
it('with empty GroupPermissions and with empty UserPermissions', async () => {
|
||||
jest
|
||||
.spyOn(noteRepo, 'save')
|
||||
.mockImplementationOnce(async (entry: Note) => {
|
||||
return entry;
|
||||
});
|
||||
const savedNote = await service.updateNotePermissions(note, {
|
||||
sharedToUsers: [],
|
||||
sharedToGroups: [],
|
||||
});
|
||||
expect(savedNote.userPermissions).toHaveLength(0);
|
||||
expect(savedNote.groupPermissions).toHaveLength(0);
|
||||
});
|
||||
it('with empty GroupPermissions and with new UserPermissions', async () => {
|
||||
jest
|
||||
.spyOn(noteRepo, 'save')
|
||||
.mockImplementationOnce(async (entry: Note) => {
|
||||
return entry;
|
||||
});
|
||||
jest.spyOn(userRepo, 'findOne').mockResolvedValueOnce(user);
|
||||
const savedNote = await service.updateNotePermissions(note, {
|
||||
sharedToUsers: [userPermissionUpdate],
|
||||
sharedToGroups: [],
|
||||
});
|
||||
expect(savedNote.userPermissions).toHaveLength(1);
|
||||
expect(savedNote.userPermissions[0].user.userName).toEqual(
|
||||
userPermissionUpdate.username,
|
||||
);
|
||||
expect(savedNote.userPermissions[0].canEdit).toEqual(
|
||||
userPermissionUpdate.canEdit,
|
||||
);
|
||||
expect(savedNote.groupPermissions).toHaveLength(0);
|
||||
});
|
||||
it('with empty GroupPermissions and with existing UserPermissions', async () => {
|
||||
const noteWithPreexistingPermissions: Note = { ...note };
|
||||
noteWithPreexistingPermissions.userPermissions = [
|
||||
{
|
||||
note: noteWithPreexistingPermissions,
|
||||
user: user,
|
||||
canEdit: !userPermissionUpdate.canEdit,
|
||||
},
|
||||
];
|
||||
jest
|
||||
.spyOn(noteRepo, 'save')
|
||||
.mockImplementationOnce(async (entry: Note) => {
|
||||
return entry;
|
||||
});
|
||||
jest.spyOn(userRepo, 'findOne').mockResolvedValueOnce(user);
|
||||
const savedNote = await service.updateNotePermissions(note, {
|
||||
sharedToUsers: [userPermissionUpdate],
|
||||
sharedToGroups: [],
|
||||
});
|
||||
expect(savedNote.userPermissions).toHaveLength(1);
|
||||
expect(savedNote.userPermissions[0].user.userName).toEqual(
|
||||
userPermissionUpdate.username,
|
||||
);
|
||||
expect(savedNote.userPermissions[0].canEdit).toEqual(
|
||||
userPermissionUpdate.canEdit,
|
||||
);
|
||||
expect(savedNote.groupPermissions).toHaveLength(0);
|
||||
});
|
||||
it('with new GroupPermissions and with empty UserPermissions', async () => {
|
||||
jest
|
||||
.spyOn(noteRepo, 'save')
|
||||
.mockImplementationOnce(async (entry: Note) => {
|
||||
return entry;
|
||||
});
|
||||
jest.spyOn(groupRepo, 'findOne').mockResolvedValueOnce(group);
|
||||
const savedNote = await service.updateNotePermissions(note, {
|
||||
sharedToUsers: [],
|
||||
sharedToGroups: [groupPermissionUpate],
|
||||
});
|
||||
expect(savedNote.userPermissions).toHaveLength(0);
|
||||
expect(savedNote.groupPermissions[0].group.name).toEqual(
|
||||
groupPermissionUpate.groupname,
|
||||
);
|
||||
expect(savedNote.groupPermissions[0].canEdit).toEqual(
|
||||
groupPermissionUpate.canEdit,
|
||||
);
|
||||
});
|
||||
it('with new GroupPermissions and with new UserPermissions', async () => {
|
||||
jest
|
||||
.spyOn(noteRepo, 'save')
|
||||
.mockImplementationOnce(async (entry: Note) => {
|
||||
return entry;
|
||||
});
|
||||
jest.spyOn(userRepo, 'findOne').mockResolvedValueOnce(user);
|
||||
jest.spyOn(groupRepo, 'findOne').mockResolvedValueOnce(group);
|
||||
const savedNote = await service.updateNotePermissions(note, {
|
||||
sharedToUsers: [userPermissionUpdate],
|
||||
sharedToGroups: [groupPermissionUpate],
|
||||
});
|
||||
expect(savedNote.userPermissions[0].user.userName).toEqual(
|
||||
userPermissionUpdate.username,
|
||||
);
|
||||
expect(savedNote.userPermissions[0].canEdit).toEqual(
|
||||
userPermissionUpdate.canEdit,
|
||||
);
|
||||
expect(savedNote.groupPermissions[0].group.name).toEqual(
|
||||
groupPermissionUpate.groupname,
|
||||
);
|
||||
expect(savedNote.groupPermissions[0].canEdit).toEqual(
|
||||
groupPermissionUpate.canEdit,
|
||||
);
|
||||
});
|
||||
it('with new GroupPermissions and with existing UserPermissions', async () => {
|
||||
const noteWithUserPermission: Note = { ...note };
|
||||
noteWithUserPermission.userPermissions = [
|
||||
{
|
||||
note: noteWithUserPermission,
|
||||
user: user,
|
||||
canEdit: !userPermissionUpdate.canEdit,
|
||||
},
|
||||
];
|
||||
jest
|
||||
.spyOn(noteRepo, 'save')
|
||||
.mockImplementationOnce(async (entry: Note) => {
|
||||
return entry;
|
||||
});
|
||||
jest.spyOn(userRepo, 'findOne').mockResolvedValueOnce(user);
|
||||
jest.spyOn(groupRepo, 'findOne').mockResolvedValueOnce(group);
|
||||
const savedNote = await service.updateNotePermissions(
|
||||
noteWithUserPermission,
|
||||
{
|
||||
sharedToUsers: [userPermissionUpdate],
|
||||
sharedToGroups: [groupPermissionUpate],
|
||||
},
|
||||
);
|
||||
expect(savedNote.userPermissions[0].user.userName).toEqual(
|
||||
userPermissionUpdate.username,
|
||||
);
|
||||
expect(savedNote.userPermissions[0].canEdit).toEqual(
|
||||
userPermissionUpdate.canEdit,
|
||||
);
|
||||
expect(savedNote.groupPermissions[0].group.name).toEqual(
|
||||
groupPermissionUpate.groupname,
|
||||
);
|
||||
expect(savedNote.groupPermissions[0].canEdit).toEqual(
|
||||
groupPermissionUpate.canEdit,
|
||||
);
|
||||
});
|
||||
it('with existing GroupPermissions and with empty UserPermissions', async () => {
|
||||
const noteWithPreexistingPermissions: Note = { ...note };
|
||||
noteWithPreexistingPermissions.groupPermissions = [
|
||||
{
|
||||
note: noteWithPreexistingPermissions,
|
||||
group: group,
|
||||
canEdit: !groupPermissionUpate.canEdit,
|
||||
},
|
||||
];
|
||||
jest.spyOn(groupRepo, 'findOne').mockResolvedValueOnce(group);
|
||||
jest
|
||||
.spyOn(noteRepo, 'save')
|
||||
.mockImplementationOnce(async (entry: Note) => {
|
||||
return entry;
|
||||
});
|
||||
const savedNote = await service.updateNotePermissions(
|
||||
noteWithPreexistingPermissions,
|
||||
{
|
||||
sharedToUsers: [],
|
||||
sharedToGroups: [groupPermissionUpate],
|
||||
},
|
||||
);
|
||||
expect(savedNote.userPermissions).toHaveLength(0);
|
||||
expect(savedNote.groupPermissions[0].group.name).toEqual(
|
||||
groupPermissionUpate.groupname,
|
||||
);
|
||||
expect(savedNote.groupPermissions[0].canEdit).toEqual(
|
||||
groupPermissionUpate.canEdit,
|
||||
);
|
||||
});
|
||||
it('with existing GroupPermissions and with new UserPermissions', async () => {
|
||||
const noteWithPreexistingPermissions: Note = { ...note };
|
||||
noteWithPreexistingPermissions.groupPermissions = [
|
||||
{
|
||||
note: noteWithPreexistingPermissions,
|
||||
group: group,
|
||||
canEdit: !groupPermissionUpate.canEdit,
|
||||
},
|
||||
];
|
||||
jest
|
||||
.spyOn(noteRepo, 'save')
|
||||
.mockImplementationOnce(async (entry: Note) => {
|
||||
return entry;
|
||||
});
|
||||
jest.spyOn(userRepo, 'findOne').mockResolvedValueOnce(user);
|
||||
jest.spyOn(groupRepo, 'findOne').mockResolvedValueOnce(group);
|
||||
const savedNote = await service.updateNotePermissions(
|
||||
noteWithPreexistingPermissions,
|
||||
{
|
||||
sharedToUsers: [userPermissionUpdate],
|
||||
sharedToGroups: [groupPermissionUpate],
|
||||
},
|
||||
);
|
||||
expect(savedNote.userPermissions[0].user.userName).toEqual(
|
||||
userPermissionUpdate.username,
|
||||
);
|
||||
expect(savedNote.userPermissions[0].canEdit).toEqual(
|
||||
userPermissionUpdate.canEdit,
|
||||
);
|
||||
expect(savedNote.groupPermissions[0].group.name).toEqual(
|
||||
groupPermissionUpate.groupname,
|
||||
);
|
||||
expect(savedNote.groupPermissions[0].canEdit).toEqual(
|
||||
groupPermissionUpate.canEdit,
|
||||
);
|
||||
});
|
||||
it('with existing GroupPermissions and with existing UserPermissions', async () => {
|
||||
const noteWithPreexistingPermissions: Note = { ...note };
|
||||
noteWithPreexistingPermissions.groupPermissions = [
|
||||
{
|
||||
note: noteWithPreexistingPermissions,
|
||||
group: group,
|
||||
canEdit: !groupPermissionUpate.canEdit,
|
||||
},
|
||||
];
|
||||
noteWithPreexistingPermissions.userPermissions = [
|
||||
{
|
||||
note: noteWithPreexistingPermissions,
|
||||
user: user,
|
||||
canEdit: !userPermissionUpdate.canEdit,
|
||||
},
|
||||
];
|
||||
jest
|
||||
.spyOn(noteRepo, 'save')
|
||||
.mockImplementationOnce(async (entry: Note) => {
|
||||
return entry;
|
||||
});
|
||||
jest.spyOn(userRepo, 'findOne').mockResolvedValueOnce(user);
|
||||
jest.spyOn(groupRepo, 'findOne').mockResolvedValueOnce(group);
|
||||
const savedNote = await service.updateNotePermissions(
|
||||
noteWithPreexistingPermissions,
|
||||
{
|
||||
sharedToUsers: [userPermissionUpdate],
|
||||
sharedToGroups: [groupPermissionUpate],
|
||||
},
|
||||
);
|
||||
expect(savedNote.userPermissions[0].user.userName).toEqual(
|
||||
userPermissionUpdate.username,
|
||||
);
|
||||
expect(savedNote.userPermissions[0].canEdit).toEqual(
|
||||
userPermissionUpdate.canEdit,
|
||||
);
|
||||
expect(savedNote.groupPermissions[0].group.name).toEqual(
|
||||
groupPermissionUpate.groupname,
|
||||
);
|
||||
expect(savedNote.groupPermissions[0].canEdit).toEqual(
|
||||
groupPermissionUpate.canEdit,
|
||||
);
|
||||
});
|
||||
});
|
||||
describe('fails:', () => {
|
||||
it('userPermissions has duplicate entries', async () => {
|
||||
try {
|
||||
await service.updateNotePermissions(note, {
|
||||
sharedToUsers: [userPermissionUpdate, userPermissionUpdate],
|
||||
sharedToGroups: [],
|
||||
});
|
||||
} catch (e) {
|
||||
expect(e).toBeInstanceOf(PermissionsUpdateInconsistentError);
|
||||
}
|
||||
});
|
||||
|
||||
it('groupPermissions has duplicate entries', async () => {
|
||||
try {
|
||||
await service.updateNotePermissions(note, {
|
||||
sharedToUsers: [],
|
||||
sharedToGroups: [groupPermissionUpate, groupPermissionUpate],
|
||||
});
|
||||
} catch (e) {
|
||||
expect(e).toBeInstanceOf(PermissionsUpdateInconsistentError);
|
||||
}
|
||||
});
|
||||
|
||||
it('userPermissions and groupPermissions have duplicate entries', async () => {
|
||||
try {
|
||||
await service.updateNotePermissions(note, {
|
||||
sharedToUsers: [userPermissionUpdate, userPermissionUpdate],
|
||||
sharedToGroups: [groupPermissionUpate, groupPermissionUpate],
|
||||
});
|
||||
} catch (e) {
|
||||
expect(e).toBeInstanceOf(PermissionsUpdateInconsistentError);
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('getNoteContentByIdOrAlias', () => {
|
||||
it('works', async () => {
|
||||
const content = 'testContent';
|
||||
jest
|
||||
.spyOn(noteRepo, 'save')
|
||||
.mockImplementation(async (note: Note): Promise<Note> => note);
|
||||
const newNote = await service.createNote(content);
|
||||
const revisions = await newNote.revisions;
|
||||
jest.spyOn(noteRepo, 'findOne').mockResolvedValueOnce(newNote);
|
||||
jest.spyOn(revisionRepo, 'findOne').mockResolvedValueOnce(revisions[0]);
|
||||
service.getNoteContentByIdOrAlias('noteThatExists').then((result) => {
|
||||
expect(result).toEqual(content);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('toTagList', () => {
|
||||
it('works', async () => {
|
||||
const note = {} as Note;
|
||||
note.tags = [
|
||||
{
|
||||
id: 1,
|
||||
name: 'testTag',
|
||||
notes: [note],
|
||||
},
|
||||
];
|
||||
const tagList = service.toTagList(note);
|
||||
expect(tagList).toHaveLength(1);
|
||||
expect(tagList[0]).toEqual(note.tags[0].name);
|
||||
});
|
||||
});
|
||||
|
||||
describe('toNotePermissionsDto', () => {
|
||||
it('works', async () => {
|
||||
const user = User.create('hardcoded', 'Testy') as User;
|
||||
const group = Group.create('testGroup', 'testGroup') as Group;
|
||||
const note = Note.create(user);
|
||||
note.userPermissions = [
|
||||
{
|
||||
note: note,
|
||||
user: user,
|
||||
canEdit: true,
|
||||
},
|
||||
];
|
||||
note.groupPermissions = [
|
||||
{
|
||||
note: note,
|
||||
group: group,
|
||||
canEdit: true,
|
||||
},
|
||||
];
|
||||
const permissions = await service.toNotePermissionsDto(note);
|
||||
expect(permissions.owner.userName).toEqual(user.userName);
|
||||
expect(permissions.sharedToUsers).toHaveLength(1);
|
||||
expect(permissions.sharedToUsers[0].user.userName).toEqual(user.userName);
|
||||
expect(permissions.sharedToUsers[0].canEdit).toEqual(true);
|
||||
expect(permissions.sharedToGroups).toHaveLength(1);
|
||||
expect(permissions.sharedToGroups[0].group.displayName).toEqual(
|
||||
group.displayName,
|
||||
);
|
||||
expect(permissions.sharedToGroups[0].canEdit).toEqual(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('toNoteMetadataDto', () => {
|
||||
it('works', async () => {
|
||||
const user = User.create('hardcoded', 'Testy') as User;
|
||||
const otherUser = User.create('other hardcoded', 'Testy2') as User;
|
||||
const group = Group.create('testGroup', 'testGroup') as Group;
|
||||
const content = 'testContent';
|
||||
jest
|
||||
.spyOn(noteRepo, 'save')
|
||||
.mockImplementation(async (note: Note): Promise<Note> => note);
|
||||
const note = await service.createNote(content);
|
||||
const revisions = await note.revisions;
|
||||
revisions[0].authorships = [
|
||||
{
|
||||
user: otherUser,
|
||||
revisions: revisions,
|
||||
startPos: 0,
|
||||
endPos: 1,
|
||||
updatedAt: new Date(1549312452000),
|
||||
} as Authorship,
|
||||
{
|
||||
user: user,
|
||||
revisions: revisions,
|
||||
startPos: 0,
|
||||
endPos: 1,
|
||||
updatedAt: new Date(1549312452001),
|
||||
} as Authorship,
|
||||
];
|
||||
revisions[0].createdAt = new Date(1549312452000);
|
||||
jest.spyOn(revisionRepo, 'findOne').mockResolvedValue(revisions[0]);
|
||||
note.id = 'testId';
|
||||
note.alias = 'testAlias';
|
||||
note.title = 'testTitle';
|
||||
note.description = 'testDescription';
|
||||
note.authorColors = [
|
||||
{
|
||||
note: note,
|
||||
user: user,
|
||||
color: 'red',
|
||||
} as AuthorColor,
|
||||
];
|
||||
note.owner = user;
|
||||
note.userPermissions = [
|
||||
{
|
||||
note: note,
|
||||
user: user,
|
||||
canEdit: true,
|
||||
},
|
||||
];
|
||||
note.groupPermissions = [
|
||||
{
|
||||
note: note,
|
||||
group: group,
|
||||
canEdit: true,
|
||||
},
|
||||
];
|
||||
note.tags = [
|
||||
{
|
||||
id: 1,
|
||||
name: 'testTag',
|
||||
notes: [note],
|
||||
},
|
||||
];
|
||||
note.viewcount = 1337;
|
||||
const metadataDto = await service.toNoteMetadataDto(note);
|
||||
expect(metadataDto.id).toEqual(note.id);
|
||||
expect(metadataDto.alias).toEqual(note.alias);
|
||||
expect(metadataDto.title).toEqual(note.title);
|
||||
expect(metadataDto.createTime).toEqual(revisions[0].createdAt);
|
||||
expect(metadataDto.description).toEqual(note.description);
|
||||
expect(metadataDto.editedBy).toHaveLength(1);
|
||||
expect(metadataDto.editedBy[0]).toEqual(user.userName);
|
||||
expect(metadataDto.permissions.owner.userName).toEqual(user.userName);
|
||||
expect(metadataDto.permissions.sharedToUsers).toHaveLength(1);
|
||||
expect(metadataDto.permissions.sharedToUsers[0].user.userName).toEqual(
|
||||
user.userName,
|
||||
);
|
||||
expect(metadataDto.permissions.sharedToUsers[0].canEdit).toEqual(true);
|
||||
expect(metadataDto.permissions.sharedToGroups).toHaveLength(1);
|
||||
expect(
|
||||
metadataDto.permissions.sharedToGroups[0].group.displayName,
|
||||
).toEqual(group.displayName);
|
||||
expect(metadataDto.permissions.sharedToGroups[0].canEdit).toEqual(true);
|
||||
expect(metadataDto.tags).toHaveLength(1);
|
||||
expect(metadataDto.tags[0]).toEqual(note.tags[0].name);
|
||||
expect(metadataDto.updateTime).toEqual(revisions[0].createdAt);
|
||||
expect(metadataDto.updateUser.userName).toEqual(user.userName);
|
||||
expect(metadataDto.viewCount).toEqual(note.viewcount);
|
||||
});
|
||||
});
|
||||
|
||||
describe('toNoteDto', () => {
|
||||
it('works', async () => {
|
||||
const user = User.create('hardcoded', 'Testy') as User;
|
||||
const otherUser = User.create('other hardcoded', 'Testy2') as User;
|
||||
otherUser.userName = 'other hardcoded user';
|
||||
const group = Group.create('testGroup', 'testGroup') as Group;
|
||||
const content = 'testContent';
|
||||
jest
|
||||
.spyOn(noteRepo, 'save')
|
||||
.mockImplementation(async (note: Note): Promise<Note> => note);
|
||||
const note = await service.createNote(content);
|
||||
const revisions = await note.revisions;
|
||||
revisions[0].authorships = [
|
||||
{
|
||||
user: otherUser,
|
||||
revisions: revisions,
|
||||
startPos: 0,
|
||||
endPos: 1,
|
||||
updatedAt: new Date(1549312452000),
|
||||
} as Authorship,
|
||||
{
|
||||
user: user,
|
||||
revisions: revisions,
|
||||
startPos: 0,
|
||||
endPos: 1,
|
||||
updatedAt: new Date(1549312452001),
|
||||
} as Authorship,
|
||||
];
|
||||
revisions[0].createdAt = new Date(1549312452000);
|
||||
jest
|
||||
.spyOn(revisionRepo, 'findOne')
|
||||
.mockResolvedValue(revisions[0])
|
||||
.mockResolvedValue(revisions[0]);
|
||||
note.id = 'testId';
|
||||
note.alias = 'testAlias';
|
||||
note.title = 'testTitle';
|
||||
note.description = 'testDescription';
|
||||
note.authorColors = [
|
||||
{
|
||||
note: note,
|
||||
user: user,
|
||||
color: 'red',
|
||||
} as AuthorColor,
|
||||
];
|
||||
note.owner = user;
|
||||
note.userPermissions = [
|
||||
{
|
||||
note: note,
|
||||
user: user,
|
||||
canEdit: true,
|
||||
},
|
||||
];
|
||||
note.groupPermissions = [
|
||||
{
|
||||
note: note,
|
||||
group: group,
|
||||
canEdit: true,
|
||||
},
|
||||
];
|
||||
note.tags = [
|
||||
{
|
||||
id: 1,
|
||||
name: 'testTag',
|
||||
notes: [note],
|
||||
},
|
||||
];
|
||||
note.viewcount = 1337;
|
||||
const noteDto = await service.toNoteDto(note);
|
||||
expect(noteDto.metadata.id).toEqual(note.id);
|
||||
expect(noteDto.metadata.alias).toEqual(note.alias);
|
||||
expect(noteDto.metadata.title).toEqual(note.title);
|
||||
expect(noteDto.metadata.createTime).toEqual(revisions[0].createdAt);
|
||||
expect(noteDto.metadata.description).toEqual(note.description);
|
||||
expect(noteDto.metadata.editedBy).toHaveLength(1);
|
||||
expect(noteDto.metadata.editedBy[0]).toEqual(user.userName);
|
||||
expect(noteDto.metadata.permissions.owner.userName).toEqual(
|
||||
user.userName,
|
||||
);
|
||||
expect(noteDto.metadata.permissions.sharedToUsers).toHaveLength(1);
|
||||
expect(
|
||||
noteDto.metadata.permissions.sharedToUsers[0].user.userName,
|
||||
).toEqual(user.userName);
|
||||
expect(noteDto.metadata.permissions.sharedToUsers[0].canEdit).toEqual(
|
||||
true,
|
||||
);
|
||||
expect(noteDto.metadata.permissions.sharedToGroups).toHaveLength(1);
|
||||
expect(
|
||||
noteDto.metadata.permissions.sharedToGroups[0].group.displayName,
|
||||
).toEqual(group.displayName);
|
||||
expect(noteDto.metadata.permissions.sharedToGroups[0].canEdit).toEqual(
|
||||
true,
|
||||
);
|
||||
expect(noteDto.metadata.tags).toHaveLength(1);
|
||||
expect(noteDto.metadata.tags[0]).toEqual(note.tags[0].name);
|
||||
expect(noteDto.metadata.updateTime).toEqual(revisions[0].createdAt);
|
||||
expect(noteDto.metadata.updateUser.userName).toEqual(user.userName);
|
||||
expect(noteDto.metadata.viewCount).toEqual(note.viewcount);
|
||||
expect(noteDto.content).toEqual(content);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -7,7 +7,11 @@
|
|||
import { forwardRef, Inject, Injectable } from '@nestjs/common';
|
||||
import { InjectRepository } from '@nestjs/typeorm';
|
||||
import { Repository } from 'typeorm';
|
||||
import { NotInDBError } from '../errors/errors';
|
||||
import {
|
||||
AlreadyInDBError,
|
||||
NotInDBError,
|
||||
PermissionsUpdateInconsistentError,
|
||||
} from '../errors/errors';
|
||||
import { ConsoleLoggerService } from '../logger/console-logger.service';
|
||||
import { Revision } from '../revisions/revision.entity';
|
||||
import { RevisionsService } from '../revisions/revisions.service';
|
||||
|
@ -22,6 +26,10 @@ import { NoteDto } from './note.dto';
|
|||
import { Note } from './note.entity';
|
||||
import { Tag } from './tag.entity';
|
||||
import { HistoryEntry } from '../history/history-entry.entity';
|
||||
import { NoteUserPermission } from '../permissions/note-user-permission.entity';
|
||||
import { NoteGroupPermission } from '../permissions/note-group-permission.entity';
|
||||
import { GroupsService } from '../groups/groups.service';
|
||||
import { checkArrayForDuplicates } from '../utils/arrayDuplicatCheck';
|
||||
|
||||
@Injectable()
|
||||
export class NotesService {
|
||||
|
@ -30,41 +38,52 @@ export class NotesService {
|
|||
@InjectRepository(Note) private noteRepository: Repository<Note>,
|
||||
@InjectRepository(Tag) private tagRepository: Repository<Tag>,
|
||||
@Inject(UsersService) private usersService: UsersService,
|
||||
@Inject(GroupsService) private groupsService: GroupsService,
|
||||
@Inject(forwardRef(() => RevisionsService))
|
||||
private revisionsService: RevisionsService,
|
||||
) {
|
||||
this.logger.setContext(NotesService.name);
|
||||
}
|
||||
|
||||
getUserNotes(user: User): Note[] {
|
||||
this.logger.warn('Using hardcoded data!');
|
||||
return [
|
||||
{
|
||||
id: 'foobar-barfoo',
|
||||
alias: null,
|
||||
shortid: 'abc',
|
||||
owner: user,
|
||||
description: 'Very descriptive text.',
|
||||
userPermissions: [],
|
||||
groupPermissions: [],
|
||||
historyEntries: [],
|
||||
tags: [],
|
||||
revisions: Promise.resolve([]),
|
||||
authorColors: [],
|
||||
title: 'Title!',
|
||||
viewcount: 42,
|
||||
},
|
||||
];
|
||||
/**
|
||||
* @async
|
||||
* Get all notes owned by a user.
|
||||
* @param {User} user - the user who owns the notes
|
||||
* @return {Note[]} arary of notes owned by the user
|
||||
*/
|
||||
async getUserNotes(user: User): Promise<Note[]> {
|
||||
const notes = await this.noteRepository.find({
|
||||
where: { owner: user },
|
||||
relations: [
|
||||
'owner',
|
||||
'userPermissions',
|
||||
'groupPermissions',
|
||||
'authorColors',
|
||||
'tags',
|
||||
],
|
||||
});
|
||||
if (notes === undefined) {
|
||||
return [];
|
||||
}
|
||||
return notes;
|
||||
}
|
||||
|
||||
/**
|
||||
* @async
|
||||
* Create a new note.
|
||||
* @param {string} noteContent - the content the new note should have
|
||||
* @param {string=} alias - a optional alias the note should have
|
||||
* @param {User=} owner - the owner of the note
|
||||
* @return {Note} the newly created note
|
||||
*/
|
||||
async createNote(
|
||||
noteContent: string,
|
||||
alias?: NoteMetadataDto['alias'],
|
||||
owner?: User,
|
||||
): Promise<Note> {
|
||||
const newNote = Note.create();
|
||||
//TODO: Calculate patch
|
||||
newNote.revisions = Promise.resolve([
|
||||
//TODO: Calculate patch
|
||||
Revision.create(noteContent, noteContent),
|
||||
]);
|
||||
if (alias) {
|
||||
|
@ -74,21 +93,56 @@ export class NotesService {
|
|||
newNote.historyEntries = [HistoryEntry.create(owner)];
|
||||
newNote.owner = owner;
|
||||
}
|
||||
return this.noteRepository.save(newNote);
|
||||
try {
|
||||
return await this.noteRepository.save(newNote);
|
||||
} catch {
|
||||
this.logger.debug(
|
||||
`A note with the alias '${alias}' already exists.`,
|
||||
'createNote',
|
||||
);
|
||||
throw new AlreadyInDBError(
|
||||
`A note with the alias '${alias}' already exists.`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
async getCurrentContent(note: Note): Promise<string> {
|
||||
/**
|
||||
* @async
|
||||
* Get the current content of the note.
|
||||
* @param {Note} note - the note to use
|
||||
* @return {string} the content of the note
|
||||
*/
|
||||
async getNoteContentByNote(note: Note): Promise<string> {
|
||||
return (await this.getLatestRevision(note)).content;
|
||||
}
|
||||
|
||||
/**
|
||||
* @async
|
||||
* Get the first revision of the note.
|
||||
* @param {Note} note - the note to use
|
||||
* @return {Revision} the first revision of the note
|
||||
*/
|
||||
async getLatestRevision(note: Note): Promise<Revision> {
|
||||
return this.revisionsService.getLatestRevision(note.id);
|
||||
return await this.revisionsService.getLatestRevision(note.id);
|
||||
}
|
||||
|
||||
/**
|
||||
* @async
|
||||
* Get the last revision of the note.
|
||||
* @param {Note} note - the note to use
|
||||
* @return {Revision} the last revision of the note
|
||||
*/
|
||||
async getFirstRevision(note: Note): Promise<Revision> {
|
||||
return this.revisionsService.getFirstRevision(note.id);
|
||||
return await this.revisionsService.getFirstRevision(note.id);
|
||||
}
|
||||
|
||||
/**
|
||||
* @async
|
||||
* Get a note by either their id or alias.
|
||||
* @param {string} noteIdOrAlias - the notes id or alias
|
||||
* @return {Note} the note
|
||||
* @throws {NotInDBError} there is no note with this id or alias
|
||||
*/
|
||||
async getNoteByIdOrAlias(noteIdOrAlias: string): Promise<Note> {
|
||||
this.logger.debug(
|
||||
`Trying to find note '${noteIdOrAlias}'`,
|
||||
|
@ -108,6 +162,7 @@ export class NotesService {
|
|||
'owner',
|
||||
'groupPermissions',
|
||||
'userPermissions',
|
||||
'tags',
|
||||
],
|
||||
});
|
||||
if (note === undefined) {
|
||||
|
@ -123,16 +178,26 @@ export class NotesService {
|
|||
return note;
|
||||
}
|
||||
|
||||
async deleteNoteByIdOrAlias(noteIdOrAlias: string) {
|
||||
const note = await this.getNoteByIdOrAlias(noteIdOrAlias);
|
||||
/**
|
||||
* @async
|
||||
* Delete a note
|
||||
* @param {Note} note - the note to delete
|
||||
* @return {Note} the note, that was deleted
|
||||
* @throws {NotInDBError} there is no note with this id or alias
|
||||
*/
|
||||
async deleteNote(note: Note): Promise<Note> {
|
||||
return await this.noteRepository.remove(note);
|
||||
}
|
||||
|
||||
async updateNoteByIdOrAlias(
|
||||
noteIdOrAlias: string,
|
||||
noteContent: string,
|
||||
): Promise<Note> {
|
||||
const note = await this.getNoteByIdOrAlias(noteIdOrAlias);
|
||||
/**
|
||||
* @async
|
||||
* Update a notes content.
|
||||
* @param {Note} note - the note
|
||||
* @param {string} noteContent - the new content
|
||||
* @return {Note} the note with a new revision and new content
|
||||
* @throws {NotInDBError} there is no note with this id or alias
|
||||
*/
|
||||
async updateNote(note: Note, noteContent: string): Promise<Note> {
|
||||
const revisions = await note.revisions;
|
||||
//TODO: Calculate patch
|
||||
revisions.push(Revision.create(noteContent, noteContent));
|
||||
|
@ -140,48 +205,112 @@ export class NotesService {
|
|||
return this.noteRepository.save(note);
|
||||
}
|
||||
|
||||
updateNotePermissions(
|
||||
noteIdOrAlias: string,
|
||||
/**
|
||||
* @async
|
||||
* Update a notes permissions.
|
||||
* @param {Note} note - the note
|
||||
* @param {NotePermissionsUpdateDto} newPermissions - the permissions the not should be set to
|
||||
* @return {Note} the note with the new permissions
|
||||
* @throws {NotInDBError} there is no note with this id or alias
|
||||
* @throws {PermissionsUpdateInconsistentError} the new permissions specify a user or group twice.
|
||||
*/
|
||||
async updateNotePermissions(
|
||||
note: Note,
|
||||
newPermissions: NotePermissionsUpdateDto,
|
||||
): Note {
|
||||
this.logger.warn('Using hardcoded data!', 'updateNotePermissions');
|
||||
return {
|
||||
id: 'foobar-barfoo',
|
||||
alias: null,
|
||||
shortid: 'abc',
|
||||
owner: {
|
||||
authTokens: [],
|
||||
createdAt: new Date(),
|
||||
displayName: 'hardcoded',
|
||||
id: '1',
|
||||
identities: [],
|
||||
ownedNotes: [],
|
||||
historyEntries: [],
|
||||
updatedAt: new Date(),
|
||||
userName: 'Testy',
|
||||
groups: [],
|
||||
},
|
||||
description: 'Very descriptive text.',
|
||||
userPermissions: [],
|
||||
groupPermissions: [],
|
||||
historyEntries: [],
|
||||
tags: [],
|
||||
revisions: Promise.resolve([]),
|
||||
authorColors: [],
|
||||
title: 'Title!',
|
||||
viewcount: 42,
|
||||
};
|
||||
): Promise<Note> {
|
||||
const users = newPermissions.sharedToUsers.map(
|
||||
(userPermission) => userPermission.username,
|
||||
);
|
||||
|
||||
const groups = newPermissions.sharedToGroups.map(
|
||||
(groupPermission) => groupPermission.groupname,
|
||||
);
|
||||
|
||||
if (checkArrayForDuplicates(users) || checkArrayForDuplicates(groups)) {
|
||||
this.logger.debug(
|
||||
`The PermissionUpdate requested specifies the same user or group multiple times.`,
|
||||
'updateNotePermissions',
|
||||
);
|
||||
throw new PermissionsUpdateInconsistentError(
|
||||
'The PermissionUpdate requested specifies the same user or group multiple times.',
|
||||
);
|
||||
}
|
||||
|
||||
note.userPermissions = [];
|
||||
note.groupPermissions = [];
|
||||
|
||||
// Create new userPermissions
|
||||
for (const newUserPermission of newPermissions.sharedToUsers) {
|
||||
const user = await this.usersService.getUserByUsername(
|
||||
newUserPermission.username,
|
||||
);
|
||||
const createdPermission = NoteUserPermission.create(
|
||||
user,
|
||||
newUserPermission.canEdit,
|
||||
);
|
||||
note.userPermissions.push(createdPermission);
|
||||
}
|
||||
|
||||
// Create groupPermissions
|
||||
for (const newGroupPermission of newPermissions.sharedToGroups) {
|
||||
const group = await this.groupsService.getGroupByName(
|
||||
newGroupPermission.groupname,
|
||||
);
|
||||
const createdPermission = NoteGroupPermission.create(
|
||||
group,
|
||||
newGroupPermission.canEdit,
|
||||
);
|
||||
note.groupPermissions.push(createdPermission);
|
||||
}
|
||||
|
||||
return await this.noteRepository.save(note);
|
||||
}
|
||||
|
||||
async getNoteContent(noteIdOrAlias: string): Promise<string> {
|
||||
/**
|
||||
* @async
|
||||
* Get the current content of the note by either their id or alias.
|
||||
* @param {string} noteIdOrAlias - the notes id or alias
|
||||
* @return {string} the content of the note
|
||||
*/
|
||||
async getNoteContentByIdOrAlias(noteIdOrAlias: string): Promise<string> {
|
||||
const note = await this.getNoteByIdOrAlias(noteIdOrAlias);
|
||||
return this.getCurrentContent(note);
|
||||
return this.getNoteContentByNote(note);
|
||||
}
|
||||
|
||||
/**
|
||||
* @async
|
||||
* Calculate the updateUser (for the NoteDto) for a Note.
|
||||
* @param {Note} note - the note to use
|
||||
* @return {User} user to be used as updateUser in the NoteDto
|
||||
*/
|
||||
async calculateUpdateUser(note: Note): Promise<User> {
|
||||
const lastRevision = await this.getLatestRevision(note);
|
||||
if (lastRevision && lastRevision.authorships) {
|
||||
// Sort the last Revisions Authorships by their updatedAt Date to get the latest one
|
||||
// the user of that Authorship is the updateUser
|
||||
return lastRevision.authorships.sort(
|
||||
(a, b) => b.updatedAt.getTime() - a.updatedAt.getTime(),
|
||||
)[0].user;
|
||||
}
|
||||
// If there are no Authorships, the owner is the updateUser
|
||||
return note.owner;
|
||||
}
|
||||
|
||||
/**
|
||||
* Map the tags of a note to a string array of the tags names.
|
||||
* @param {Note} note - the note to use
|
||||
* @return {string[]} string array of tags names
|
||||
*/
|
||||
toTagList(note: Note): string[] {
|
||||
return note.tags.map((tag) => tag.name);
|
||||
}
|
||||
|
||||
/**
|
||||
* @async
|
||||
* Build NotePermissionsDto from a note.
|
||||
* @param {Note} note - the note to use
|
||||
* @return {NotePermissionsDto} the built NotePermissionDto
|
||||
*/
|
||||
async toNotePermissionsDto(note: Note): Promise<NotePermissionsDto> {
|
||||
return {
|
||||
owner: this.usersService.toUserDto(note.owner),
|
||||
|
@ -190,12 +319,18 @@ export class NotesService {
|
|||
canEdit: noteUserPermission.canEdit,
|
||||
})),
|
||||
sharedToGroups: note.groupPermissions.map((noteGroupPermission) => ({
|
||||
group: noteGroupPermission.group,
|
||||
group: this.groupsService.toGroupDto(noteGroupPermission.group),
|
||||
canEdit: noteGroupPermission.canEdit,
|
||||
})),
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @async
|
||||
* Build NoteMetadataDto from a note.
|
||||
* @param {Note} note - the note to use
|
||||
* @return {NoteMetadataDto} the built NoteMetadataDto
|
||||
*/
|
||||
async toNoteMetadataDto(note: Note): Promise<NoteMetadataDto> {
|
||||
return {
|
||||
// TODO: Convert DB UUID to base64
|
||||
|
@ -207,24 +342,25 @@ export class NotesService {
|
|||
editedBy: note.authorColors.map(
|
||||
(authorColor) => authorColor.user.userName,
|
||||
),
|
||||
// TODO: Extract into method
|
||||
permissions: await this.toNotePermissionsDto(note),
|
||||
tags: this.toTagList(note),
|
||||
updateTime: (await this.getLatestRevision(note)).createdAt,
|
||||
// TODO: Get actual updateUser
|
||||
updateUser: {
|
||||
displayName: 'Hardcoded User',
|
||||
userName: 'hardcoded',
|
||||
email: 'foo@example.com',
|
||||
photo: '',
|
||||
},
|
||||
viewCount: 42,
|
||||
updateUser: this.usersService.toUserDto(
|
||||
await this.calculateUpdateUser(note),
|
||||
),
|
||||
viewCount: note.viewcount,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @async
|
||||
* Build NoteDto from a note.
|
||||
* @param {Note} note - the note to use
|
||||
* @return {NoteDto} the built NoteDto
|
||||
*/
|
||||
async toNoteDto(note: Note): Promise<NoteDto> {
|
||||
return {
|
||||
content: await this.getCurrentContent(note),
|
||||
content: await this.getNoteContentByNote(note),
|
||||
metadata: await this.toNoteMetadataDto(note),
|
||||
editedByAtPosition: [],
|
||||
};
|
||||
|
|
|
@ -18,4 +18,14 @@ export class NoteGroupPermission {
|
|||
|
||||
@Column()
|
||||
canEdit: boolean;
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-empty-function
|
||||
private constructor() {}
|
||||
|
||||
public static create(group: Group, canEdit: boolean): NoteGroupPermission {
|
||||
const groupPermission = new NoteGroupPermission();
|
||||
groupPermission.group = group;
|
||||
groupPermission.canEdit = canEdit;
|
||||
return groupPermission;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -18,4 +18,14 @@ export class NoteUserPermission {
|
|||
|
||||
@Column()
|
||||
canEdit: boolean;
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-empty-function
|
||||
private constructor() {}
|
||||
|
||||
public static create(user: User, canEdit: boolean): NoteUserPermission {
|
||||
const userPermission = new NoteUserPermission();
|
||||
userPermission.user = user;
|
||||
userPermission.canEdit = canEdit;
|
||||
return userPermission;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -23,11 +23,6 @@ import { Revision } from '../revisions/revision.entity';
|
|||
import { Tag } from '../notes/tag.entity';
|
||||
import { Group } from '../groups/group.entity';
|
||||
|
||||
jest.mock('../permissions/note-group-permission.entity.ts');
|
||||
jest.mock('../groups/group.entity.ts');
|
||||
jest.mock('../notes/note.entity.ts');
|
||||
jest.mock('../users/user.entity.ts');
|
||||
|
||||
describe('PermissionsService', () => {
|
||||
let permissionsService: PermissionsService;
|
||||
|
||||
|
@ -56,6 +51,8 @@ describe('PermissionsService', () => {
|
|||
.useValue({})
|
||||
.overrideProvider(getRepositoryToken(NoteUserPermission))
|
||||
.useValue({})
|
||||
.overrideProvider(getRepositoryToken(Group))
|
||||
.useValue({})
|
||||
.compile();
|
||||
permissionsService = module.get<PermissionsService>(PermissionsService);
|
||||
});
|
||||
|
@ -246,33 +243,27 @@ describe('PermissionsService', () => {
|
|||
function createGroups(): { [id: string]: Group } {
|
||||
const result: { [id: string]: Group } = {};
|
||||
|
||||
const everybody: Group = new Group();
|
||||
const everybody: Group = Group.create('everybody', 'Everybody');
|
||||
everybody.special = true;
|
||||
everybody.name = 'everybody';
|
||||
result['everybody'] = everybody;
|
||||
|
||||
const loggedIn = new Group();
|
||||
const loggedIn = Group.create('loggedIn', 'loggedIn');
|
||||
loggedIn.special = true;
|
||||
loggedIn.name = 'loggedIn';
|
||||
result['loggedIn'] = loggedIn;
|
||||
|
||||
const user1group = new Group();
|
||||
user1group.name = 'user1group';
|
||||
const user1group = Group.create('user1group', 'user1group');
|
||||
user1group.members = [user1];
|
||||
result['user1group'] = user1group;
|
||||
|
||||
const user2group = new Group();
|
||||
user2group.name = 'user2group';
|
||||
const user2group = Group.create('user2group', 'user2group');
|
||||
user2group.members = [user2];
|
||||
result['user2group'] = user2group;
|
||||
|
||||
const user1and2group = new Group();
|
||||
user1and2group.name = 'user1and2group';
|
||||
const user1and2group = Group.create('user1and2group', 'user1and2group');
|
||||
user1and2group.members = [user1, user2];
|
||||
result['user1and2group'] = user1and2group;
|
||||
|
||||
const user2and1group = new Group();
|
||||
user2and1group.name = 'user2and1group';
|
||||
const user2and1group = Group.create('user2and1group', 'user2and1group');
|
||||
user2and1group.members = [user2, user1];
|
||||
result['user2and1group'] = user2and1group;
|
||||
|
||||
|
@ -292,10 +283,7 @@ describe('PermissionsService', () => {
|
|||
group: Group,
|
||||
write: boolean,
|
||||
): NoteGroupPermission {
|
||||
const noteGroupPermission = new NoteGroupPermission();
|
||||
noteGroupPermission.canEdit = write;
|
||||
noteGroupPermission.group = group;
|
||||
return noteGroupPermission;
|
||||
return NoteGroupPermission.create(group, write);
|
||||
}
|
||||
|
||||
const everybodyRead = createNoteGroupPermission(groups['everybody'], false);
|
||||
|
|
|
@ -19,6 +19,7 @@ import { RevisionsService } from './revisions.service';
|
|||
import { Tag } from '../notes/tag.entity';
|
||||
import { NoteGroupPermission } from '../permissions/note-group-permission.entity';
|
||||
import { NoteUserPermission } from '../permissions/note-user-permission.entity';
|
||||
import { Group } from '../groups/group.entity';
|
||||
|
||||
describe('RevisionsService', () => {
|
||||
let service: RevisionsService;
|
||||
|
@ -54,6 +55,8 @@ describe('RevisionsService', () => {
|
|||
.useValue({})
|
||||
.overrideProvider(getRepositoryToken(NoteUserPermission))
|
||||
.useValue({})
|
||||
.overrideProvider(getRepositoryToken(Group))
|
||||
.useValue({})
|
||||
.compile();
|
||||
|
||||
service = module.get<RevisionsService>(RevisionsService);
|
||||
|
|
|
@ -12,6 +12,7 @@ import { NotesService } from '../notes/notes.service';
|
|||
import { RevisionMetadataDto } from './revision-metadata.dto';
|
||||
import { RevisionDto } from './revision.dto';
|
||||
import { Revision } from './revision.entity';
|
||||
import { Note } from '../notes/note.entity';
|
||||
|
||||
@Injectable()
|
||||
export class RevisionsService {
|
||||
|
@ -24,8 +25,7 @@ export class RevisionsService {
|
|||
this.logger.setContext(RevisionsService.name);
|
||||
}
|
||||
|
||||
async getAllRevisions(noteIdOrAlias: string): Promise<Revision[]> {
|
||||
const note = await this.notesService.getNoteByIdOrAlias(noteIdOrAlias);
|
||||
async getAllRevisions(note: Note): Promise<Revision[]> {
|
||||
return await this.revisionRepository.find({
|
||||
where: {
|
||||
note: note,
|
||||
|
@ -33,11 +33,7 @@ export class RevisionsService {
|
|||
});
|
||||
}
|
||||
|
||||
async getRevision(
|
||||
noteIdOrAlias: string,
|
||||
revisionId: number,
|
||||
): Promise<Revision> {
|
||||
const note = await this.notesService.getNoteByIdOrAlias(noteIdOrAlias);
|
||||
async getRevision(note: Note, revisionId: number): Promise<Revision> {
|
||||
return await this.revisionRepository.findOne({
|
||||
where: {
|
||||
id: revisionId,
|
||||
|
|
|
@ -63,7 +63,7 @@ export class UsersService {
|
|||
userName: user.userName,
|
||||
displayName: user.displayName,
|
||||
photo: this.getPhotoUrl(user),
|
||||
email: user.email,
|
||||
email: user.email ?? '',
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
9
src/utils/arrayDuplicatCheck.ts
Normal file
9
src/utils/arrayDuplicatCheck.ts
Normal file
|
@ -0,0 +1,9 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file)
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
export function checkArrayForDuplicates<T>(array: Array<T>): boolean {
|
||||
return new Set(array).size !== array.length;
|
||||
}
|
|
@ -74,7 +74,7 @@ describe('Notes', () => {
|
|||
.expect(201);
|
||||
expect(response.body.metadata?.id).toBeDefined();
|
||||
expect(
|
||||
await notesService.getCurrentContent(
|
||||
await notesService.getNoteContentByNote(
|
||||
await notesService.getNoteByIdOrAlias(response.body.metadata.id),
|
||||
),
|
||||
).toEqual(content);
|
||||
|
@ -109,11 +109,20 @@ describe('Notes', () => {
|
|||
.expect(201);
|
||||
expect(response.body.metadata?.id).toBeDefined();
|
||||
return expect(
|
||||
await notesService.getCurrentContent(
|
||||
await notesService.getNoteContentByNote(
|
||||
await notesService.getNoteByIdOrAlias(response.body.metadata?.id),
|
||||
),
|
||||
).toEqual(content);
|
||||
});
|
||||
|
||||
it('fails with a existing alias', async () => {
|
||||
await request(app.getHttpServer())
|
||||
.post('/notes/test2')
|
||||
.set('Content-Type', 'text/markdown')
|
||||
.send(content)
|
||||
.expect('Content-Type', /json/)
|
||||
.expect(400);
|
||||
});
|
||||
});
|
||||
|
||||
describe('DELETE /notes/{note}', () => {
|
||||
|
@ -141,7 +150,7 @@ describe('Notes', () => {
|
|||
.send(changedContent)
|
||||
.expect(200);
|
||||
await expect(
|
||||
await notesService.getCurrentContent(
|
||||
await notesService.getNoteContentByNote(
|
||||
await notesService.getNoteByIdOrAlias('test4'),
|
||||
),
|
||||
).toEqual(changedContent);
|
||||
|
@ -197,7 +206,7 @@ describe('Notes', () => {
|
|||
// wait one second
|
||||
await new Promise((r) => setTimeout(r, 1000));
|
||||
// update the note
|
||||
await notesService.updateNoteByIdOrAlias('test5a', 'More test content');
|
||||
await notesService.updateNote(note, 'More test content');
|
||||
const metadata = await request(app.getHttpServer())
|
||||
.get('/notes/test5a/metadata')
|
||||
.expect(200);
|
||||
|
|
Loading…
Reference in a new issue