mirror of
https://github.com/hedgedoc/hedgedoc.git
synced 2025-05-14 07:04:45 -04:00
refactor: adapt for typeorm 0.3
Signed-off-by: David Mehren <git@herrmehren.de>
This commit is contained in:
parent
a07b5f54d1
commit
c4975e4783
21 changed files with 131 additions and 69 deletions
|
@ -108,7 +108,7 @@ describe('AuthService', () => {
|
||||||
});
|
});
|
||||||
describe('fails:', () => {
|
describe('fails:', () => {
|
||||||
it('AuthToken could not be found', async () => {
|
it('AuthToken could not be found', async () => {
|
||||||
jest.spyOn(authTokenRepo, 'findOne').mockResolvedValueOnce(undefined);
|
jest.spyOn(authTokenRepo, 'findOne').mockResolvedValueOnce(null);
|
||||||
await expect(
|
await expect(
|
||||||
service.getAuthTokenAndValidate(authToken.keyId, token),
|
service.getAuthTokenAndValidate(authToken.keyId, token),
|
||||||
).rejects.toThrow(NotInDBError);
|
).rejects.toThrow(NotInDBError);
|
||||||
|
@ -157,7 +157,7 @@ describe('AuthService', () => {
|
||||||
await service.setLastUsedToken(authToken.keyId);
|
await service.setLastUsedToken(authToken.keyId);
|
||||||
});
|
});
|
||||||
it('throws if the token is not in the database', async () => {
|
it('throws if the token is not in the database', async () => {
|
||||||
jest.spyOn(authTokenRepo, 'findOne').mockResolvedValueOnce(undefined);
|
jest.spyOn(authTokenRepo, 'findOne').mockResolvedValueOnce(null);
|
||||||
await expect(service.setLastUsedToken(authToken.keyId)).rejects.toThrow(
|
await expect(service.setLastUsedToken(authToken.keyId)).rejects.toThrow(
|
||||||
NotInDBError,
|
NotInDBError,
|
||||||
);
|
);
|
||||||
|
@ -226,7 +226,7 @@ describe('AuthService', () => {
|
||||||
await service.removeToken(authToken.keyId);
|
await service.removeToken(authToken.keyId);
|
||||||
});
|
});
|
||||||
it('throws if the token is not in the database', async () => {
|
it('throws if the token is not in the database', async () => {
|
||||||
jest.spyOn(authTokenRepo, 'findOne').mockResolvedValueOnce(undefined);
|
jest.spyOn(authTokenRepo, 'findOne').mockResolvedValueOnce(null);
|
||||||
await expect(service.removeToken(authToken.keyId)).rejects.toThrow(
|
await expect(service.removeToken(authToken.keyId)).rejects.toThrow(
|
||||||
NotInDBError,
|
NotInDBError,
|
||||||
);
|
);
|
||||||
|
|
|
@ -7,7 +7,7 @@ import { Injectable } from '@nestjs/common';
|
||||||
import { Cron, Timeout } from '@nestjs/schedule';
|
import { Cron, Timeout } from '@nestjs/schedule';
|
||||||
import { InjectRepository } from '@nestjs/typeorm';
|
import { InjectRepository } from '@nestjs/typeorm';
|
||||||
import crypto, { randomBytes } from 'crypto';
|
import crypto, { randomBytes } from 'crypto';
|
||||||
import { Repository } from 'typeorm';
|
import { Equal, Repository } from 'typeorm';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
NotInDBError,
|
NotInDBError,
|
||||||
|
@ -104,7 +104,7 @@ export class AuthService {
|
||||||
const accessToken = await this.authTokenRepository.findOne({
|
const accessToken = await this.authTokenRepository.findOne({
|
||||||
where: { keyId: keyId },
|
where: { keyId: keyId },
|
||||||
});
|
});
|
||||||
if (accessToken === undefined) {
|
if (accessToken === null) {
|
||||||
throw new NotInDBError(`AuthToken for key '${keyId}' not found`);
|
throw new NotInDBError(`AuthToken for key '${keyId}' not found`);
|
||||||
}
|
}
|
||||||
accessToken.lastUsedAt = new Date();
|
accessToken.lastUsedAt = new Date();
|
||||||
|
@ -119,7 +119,7 @@ export class AuthService {
|
||||||
where: { keyId: keyId },
|
where: { keyId: keyId },
|
||||||
relations: ['user'],
|
relations: ['user'],
|
||||||
});
|
});
|
||||||
if (accessToken === undefined) {
|
if (accessToken === null) {
|
||||||
throw new NotInDBError(`AuthToken '${token}' not found`);
|
throw new NotInDBError(`AuthToken '${token}' not found`);
|
||||||
}
|
}
|
||||||
// Hash the user-provided token
|
// Hash the user-provided token
|
||||||
|
@ -150,9 +150,9 @@ export class AuthService {
|
||||||
|
|
||||||
async getTokensByUser(user: User): Promise<AuthToken[]> {
|
async getTokensByUser(user: User): Promise<AuthToken[]> {
|
||||||
const tokens = await this.authTokenRepository.find({
|
const tokens = await this.authTokenRepository.find({
|
||||||
where: { user: user },
|
where: { user: Equal(user) },
|
||||||
});
|
});
|
||||||
if (tokens === undefined) {
|
if (tokens === null) {
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
return tokens;
|
return tokens;
|
||||||
|
@ -162,7 +162,7 @@ export class AuthService {
|
||||||
const token = await this.authTokenRepository.findOne({
|
const token = await this.authTokenRepository.findOne({
|
||||||
where: { keyId: keyId },
|
where: { keyId: keyId },
|
||||||
});
|
});
|
||||||
if (token === undefined) {
|
if (token === null) {
|
||||||
throw new NotInDBError(`AuthToken for key '${keyId}' not found`);
|
throw new NotInDBError(`AuthToken for key '${keyId}' not found`);
|
||||||
}
|
}
|
||||||
await this.authTokenRepository.remove(token);
|
await this.authTokenRepository.remove(token);
|
||||||
|
|
|
@ -82,7 +82,7 @@ describe('GroupsService', () => {
|
||||||
expect(foundGroup.special).toEqual(group.special);
|
expect(foundGroup.special).toEqual(group.special);
|
||||||
});
|
});
|
||||||
it('fails with non-existing group', async () => {
|
it('fails with non-existing group', async () => {
|
||||||
jest.spyOn(groupRepo, 'findOne').mockResolvedValueOnce(undefined);
|
jest.spyOn(groupRepo, 'findOne').mockResolvedValueOnce(null);
|
||||||
await expect(service.getGroupByName('i_dont_exist')).rejects.toThrow(
|
await expect(service.getGroupByName('i_dont_exist')).rejects.toThrow(
|
||||||
NotInDBError,
|
NotInDBError,
|
||||||
);
|
);
|
||||||
|
|
|
@ -60,7 +60,7 @@ export class GroupsService {
|
||||||
const group = await this.groupRepository.findOne({
|
const group = await this.groupRepository.findOne({
|
||||||
where: { name: name },
|
where: { name: name },
|
||||||
});
|
});
|
||||||
if (group === undefined) {
|
if (group === null) {
|
||||||
throw new NotInDBError(`Group with name '${name}' not found`);
|
throw new NotInDBError(`Group with name '${name}' not found`);
|
||||||
}
|
}
|
||||||
return group;
|
return group;
|
||||||
|
|
|
@ -3,7 +3,13 @@
|
||||||
*
|
*
|
||||||
* SPDX-License-Identifier: AGPL-3.0-only
|
* SPDX-License-Identifier: AGPL-3.0-only
|
||||||
*/
|
*/
|
||||||
import { Column, Entity, ManyToOne, UpdateDateColumn } from 'typeorm';
|
import {
|
||||||
|
Column,
|
||||||
|
Entity,
|
||||||
|
ManyToOne,
|
||||||
|
PrimaryColumn,
|
||||||
|
UpdateDateColumn,
|
||||||
|
} from 'typeorm';
|
||||||
|
|
||||||
import { Note } from '../notes/note.entity';
|
import { Note } from '../notes/note.entity';
|
||||||
import { User } from '../users/user.entity';
|
import { User } from '../users/user.entity';
|
||||||
|
@ -16,16 +22,19 @@ export class HistoryEntry {
|
||||||
* primary keys.
|
* primary keys.
|
||||||
* See https://github.com/typeorm/typeorm/issues/6908
|
* See https://github.com/typeorm/typeorm/issues/6908
|
||||||
*/
|
*/
|
||||||
|
@PrimaryColumn()
|
||||||
|
userId: string;
|
||||||
|
|
||||||
@ManyToOne((_) => User, (user) => user.historyEntries, {
|
@ManyToOne((_) => User, (user) => user.historyEntries, {
|
||||||
onDelete: 'CASCADE',
|
onDelete: 'CASCADE',
|
||||||
primary: true,
|
|
||||||
})
|
})
|
||||||
user: User;
|
user: User;
|
||||||
|
|
||||||
|
@PrimaryColumn()
|
||||||
|
noteId: string;
|
||||||
|
|
||||||
@ManyToOne((_) => Note, (note) => note.historyEntries, {
|
@ManyToOne((_) => Note, (note) => note.historyEntries, {
|
||||||
onDelete: 'CASCADE',
|
onDelete: 'CASCADE',
|
||||||
primary: true,
|
|
||||||
})
|
})
|
||||||
note: Note;
|
note: Note;
|
||||||
|
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
*/
|
*/
|
||||||
import { Injectable } from '@nestjs/common';
|
import { Injectable } from '@nestjs/common';
|
||||||
import { InjectConnection, InjectRepository } from '@nestjs/typeorm';
|
import { InjectConnection, InjectRepository } from '@nestjs/typeorm';
|
||||||
import { Connection, Repository } from 'typeorm';
|
import { Connection, Equal, Repository } from 'typeorm';
|
||||||
|
|
||||||
import { NotInDBError } from '../errors/errors';
|
import { NotInDBError } from '../errors/errors';
|
||||||
import { ConsoleLoggerService } from '../logger/console-logger.service';
|
import { ConsoleLoggerService } from '../logger/console-logger.service';
|
||||||
|
@ -41,7 +41,7 @@ export class HistoryService {
|
||||||
*/
|
*/
|
||||||
async getEntriesByUser(user: User): Promise<HistoryEntry[]> {
|
async getEntriesByUser(user: User): Promise<HistoryEntry[]> {
|
||||||
return await this.historyEntryRepository.find({
|
return await this.historyEntryRepository.find({
|
||||||
where: { user: user },
|
where: { user: Equal(user) },
|
||||||
relations: ['note', 'note.aliases', 'user'],
|
relations: ['note', 'note.aliases', 'user'],
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -56,8 +56,8 @@ export class HistoryService {
|
||||||
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: Equal(note),
|
||||||
user: user,
|
user: Equal(user),
|
||||||
},
|
},
|
||||||
relations: ['note', 'note.aliases', 'user'],
|
relations: ['note', 'note.aliases', 'user'],
|
||||||
});
|
});
|
||||||
|
@ -150,7 +150,7 @@ export class HistoryService {
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
await this.connection.transaction(async (manager) => {
|
await this.connection.transaction(async (manager) => {
|
||||||
const currentHistory = await manager.find<HistoryEntry>(HistoryEntry, {
|
const currentHistory = await manager.find<HistoryEntry>(HistoryEntry, {
|
||||||
where: { user: user },
|
where: { user: Equal(user) },
|
||||||
relations: ['note', 'note.aliases', 'user'],
|
relations: ['note', 'note.aliases', 'user'],
|
||||||
});
|
});
|
||||||
for (const entry of currentHistory) {
|
for (const entry of currentHistory) {
|
||||||
|
|
|
@ -210,7 +210,7 @@ describe('MediaService', () => {
|
||||||
});
|
});
|
||||||
it("fails: can't find mediaUpload", async () => {
|
it("fails: can't find mediaUpload", async () => {
|
||||||
const testFileName = 'testFilename';
|
const testFileName = 'testFilename';
|
||||||
jest.spyOn(mediaRepo, 'findOne').mockResolvedValueOnce(undefined);
|
jest.spyOn(mediaRepo, 'findOne').mockResolvedValueOnce(null);
|
||||||
await expect(service.findUploadByFilename(testFileName)).rejects.toThrow(
|
await expect(service.findUploadByFilename(testFileName)).rejects.toThrow(
|
||||||
NotInDBError,
|
NotInDBError,
|
||||||
);
|
);
|
||||||
|
@ -243,8 +243,8 @@ describe('MediaService', () => {
|
||||||
} as User);
|
} as User);
|
||||||
expect(mediaList).toEqual([]);
|
expect(mediaList).toEqual([]);
|
||||||
});
|
});
|
||||||
it('with error (undefined as return value of find)', async () => {
|
it('with error (null as return value of find)', async () => {
|
||||||
jest.spyOn(mediaRepo, 'find').mockResolvedValueOnce(undefined);
|
jest.spyOn(mediaRepo, 'find').mockResolvedValueOnce(null);
|
||||||
const mediaList = await service.listUploadsByUser({
|
const mediaList = await service.listUploadsByUser({
|
||||||
username: username,
|
username: username,
|
||||||
} as User);
|
} as User);
|
||||||
|
|
|
@ -8,7 +8,7 @@ import { ModuleRef } from '@nestjs/core';
|
||||||
import { InjectRepository } from '@nestjs/typeorm';
|
import { InjectRepository } from '@nestjs/typeorm';
|
||||||
import crypto from 'crypto';
|
import crypto from 'crypto';
|
||||||
import * as FileType from 'file-type';
|
import * as FileType from 'file-type';
|
||||||
import { Repository } from 'typeorm';
|
import { Equal, Repository } from 'typeorm';
|
||||||
|
|
||||||
import mediaConfiguration, { MediaConfig } from '../config/media.config';
|
import mediaConfiguration, { MediaConfig } from '../config/media.config';
|
||||||
import { ClientError, NotInDBError } from '../errors/errors';
|
import { ClientError, NotInDBError } from '../errors/errors';
|
||||||
|
@ -128,10 +128,11 @@ export class MediaService {
|
||||||
* @throws {MediaBackendError} - there was an error retrieving the url
|
* @throws {MediaBackendError} - there was an error retrieving the url
|
||||||
*/
|
*/
|
||||||
async findUploadByFilename(filename: string): Promise<MediaUpload> {
|
async findUploadByFilename(filename: string): Promise<MediaUpload> {
|
||||||
const mediaUpload = await this.mediaUploadRepository.findOne(filename, {
|
const mediaUpload = await this.mediaUploadRepository.findOne({
|
||||||
|
where: { id: filename },
|
||||||
relations: ['user'],
|
relations: ['user'],
|
||||||
});
|
});
|
||||||
if (mediaUpload === undefined) {
|
if (mediaUpload === null) {
|
||||||
throw new NotInDBError(
|
throw new NotInDBError(
|
||||||
`MediaUpload with filename '${filename}' not found`,
|
`MediaUpload with filename '${filename}' not found`,
|
||||||
);
|
);
|
||||||
|
@ -147,10 +148,10 @@ export class MediaService {
|
||||||
*/
|
*/
|
||||||
async listUploadsByUser(user: User): Promise<MediaUpload[]> {
|
async listUploadsByUser(user: User): Promise<MediaUpload[]> {
|
||||||
const mediaUploads = await this.mediaUploadRepository.find({
|
const mediaUploads = await this.mediaUploadRepository.find({
|
||||||
where: { user: user },
|
where: { user: Equal(user) },
|
||||||
relations: ['user', 'note'],
|
relations: ['user', 'note'],
|
||||||
});
|
});
|
||||||
if (mediaUploads === undefined) {
|
if (mediaUploads === null) {
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
return mediaUploads;
|
return mediaUploads;
|
||||||
|
@ -164,10 +165,10 @@ export class MediaService {
|
||||||
*/
|
*/
|
||||||
async listUploadsByNote(note: Note): Promise<MediaUpload[]> {
|
async listUploadsByNote(note: Note): Promise<MediaUpload[]> {
|
||||||
const mediaUploads = await this.mediaUploadRepository.find({
|
const mediaUploads = await this.mediaUploadRepository.find({
|
||||||
where: { note: note },
|
where: { note: Equal(note) },
|
||||||
relations: ['user', 'note'],
|
relations: ['user', 'note'],
|
||||||
});
|
});
|
||||||
if (mediaUploads === undefined) {
|
if (mediaUploads === null) {
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
return mediaUploads;
|
return mediaUploads;
|
||||||
|
|
|
@ -120,8 +120,8 @@ describe('AliasService', () => {
|
||||||
jest
|
jest
|
||||||
.spyOn(noteRepo, 'save')
|
.spyOn(noteRepo, 'save')
|
||||||
.mockImplementationOnce(async (note: Note): Promise<Note> => note);
|
.mockImplementationOnce(async (note: Note): Promise<Note> => note);
|
||||||
jest.spyOn(noteRepo, 'findOne').mockResolvedValueOnce(undefined);
|
jest.spyOn(noteRepo, 'findOne').mockResolvedValueOnce(null);
|
||||||
jest.spyOn(aliasRepo, 'findOne').mockResolvedValueOnce(undefined);
|
jest.spyOn(aliasRepo, 'findOne').mockResolvedValueOnce(null);
|
||||||
const savedAlias = await service.addAlias(note, alias);
|
const savedAlias = await service.addAlias(note, alias);
|
||||||
expect(savedAlias.name).toEqual(alias);
|
expect(savedAlias.name).toEqual(alias);
|
||||||
expect(savedAlias.primary).toBeTruthy();
|
expect(savedAlias.primary).toBeTruthy();
|
||||||
|
@ -131,8 +131,8 @@ describe('AliasService', () => {
|
||||||
jest
|
jest
|
||||||
.spyOn(noteRepo, 'save')
|
.spyOn(noteRepo, 'save')
|
||||||
.mockImplementationOnce(async (note: Note): Promise<Note> => note);
|
.mockImplementationOnce(async (note: Note): Promise<Note> => note);
|
||||||
jest.spyOn(noteRepo, 'findOne').mockResolvedValueOnce(undefined);
|
jest.spyOn(noteRepo, 'findOne').mockResolvedValueOnce(null);
|
||||||
jest.spyOn(aliasRepo, 'findOne').mockResolvedValueOnce(undefined);
|
jest.spyOn(aliasRepo, 'findOne').mockResolvedValueOnce(null);
|
||||||
const savedAlias = await service.addAlias(note, alias2);
|
const savedAlias = await service.addAlias(note, alias2);
|
||||||
expect(savedAlias.name).toEqual(alias2);
|
expect(savedAlias.name).toEqual(alias2);
|
||||||
expect(savedAlias.primary).toBeFalsy();
|
expect(savedAlias.primary).toBeFalsy();
|
||||||
|
@ -230,7 +230,7 @@ describe('AliasService', () => {
|
||||||
|
|
||||||
it('mark the alias as primary', async () => {
|
it('mark the alias as primary', async () => {
|
||||||
jest
|
jest
|
||||||
.spyOn(aliasRepo, 'findOne')
|
.spyOn(aliasRepo, 'findOneBy')
|
||||||
.mockResolvedValueOnce(alias)
|
.mockResolvedValueOnce(alias)
|
||||||
.mockResolvedValueOnce(alias2);
|
.mockResolvedValueOnce(alias2);
|
||||||
jest
|
jest
|
||||||
|
|
|
@ -45,7 +45,7 @@ export class AliasService {
|
||||||
const foundAlias = await this.aliasRepository.findOne({
|
const foundAlias = await this.aliasRepository.findOne({
|
||||||
where: { name: alias },
|
where: { name: alias },
|
||||||
});
|
});
|
||||||
if (foundAlias !== undefined) {
|
if (foundAlias !== null) {
|
||||||
this.logger.debug(`The alias '${alias}' is already used.`, 'addAlias');
|
this.logger.debug(`The alias '${alias}' is already used.`, 'addAlias');
|
||||||
throw new AlreadyInDBError(`The alias '${alias}' is already used.`);
|
throw new AlreadyInDBError(`The alias '${alias}' is already used.`);
|
||||||
}
|
}
|
||||||
|
@ -53,7 +53,7 @@ export class AliasService {
|
||||||
const foundNote = await this.noteRepository.findOne({
|
const foundNote = await this.noteRepository.findOne({
|
||||||
where: { publicId: alias },
|
where: { publicId: alias },
|
||||||
});
|
});
|
||||||
if (foundNote !== undefined) {
|
if (foundNote !== null) {
|
||||||
this.logger.debug(
|
this.logger.debug(
|
||||||
`The alias '${alias}' is already a public id.`,
|
`The alias '${alias}' is already a public id.`,
|
||||||
'addAlias',
|
'addAlias',
|
||||||
|
@ -113,8 +113,12 @@ export class AliasService {
|
||||||
throw new NotInDBError(`The alias '${alias}' is not used by this note.`);
|
throw new NotInDBError(`The alias '${alias}' is not used by this note.`);
|
||||||
}
|
}
|
||||||
|
|
||||||
const oldPrimary = await this.aliasRepository.findOne(oldPrimaryId);
|
const oldPrimary = await this.aliasRepository.findOneBy({
|
||||||
const newPrimary = await this.aliasRepository.findOne(newPrimaryId);
|
id: oldPrimaryId,
|
||||||
|
});
|
||||||
|
const newPrimary = await this.aliasRepository.findOneBy({
|
||||||
|
id: newPrimaryId,
|
||||||
|
});
|
||||||
|
|
||||||
if (!oldPrimary || !newPrimary) {
|
if (!oldPrimary || !newPrimary) {
|
||||||
throw new Error('This should not happen!');
|
throw new Error('This should not happen!');
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
import { ConfigModule, ConfigService } from '@nestjs/config';
|
import { ConfigModule, ConfigService } from '@nestjs/config';
|
||||||
import { Test, TestingModule } from '@nestjs/testing';
|
import { Test, TestingModule } from '@nestjs/testing';
|
||||||
import { getRepositoryToken } from '@nestjs/typeorm';
|
import { getRepositoryToken } from '@nestjs/typeorm';
|
||||||
import { Repository } from 'typeorm';
|
import { DataSource, EntityManager, Repository } from 'typeorm';
|
||||||
|
|
||||||
import { AuthToken } from '../auth/auth-token.entity';
|
import { AuthToken } from '../auth/auth-token.entity';
|
||||||
import { Author } from '../authors/author.entity';
|
import { Author } from '../authors/author.entity';
|
||||||
|
@ -121,7 +121,16 @@ describe('NotesService', () => {
|
||||||
* the overrideProvider call, as otherwise we have two instances
|
* the overrideProvider call, as otherwise we have two instances
|
||||||
* and the mock of createQueryBuilder replaces the wrong one
|
* and the mock of createQueryBuilder replaces the wrong one
|
||||||
* **/
|
* **/
|
||||||
userRepo = new Repository<User>();
|
userRepo = new Repository<User>(
|
||||||
|
'',
|
||||||
|
new EntityManager(
|
||||||
|
new DataSource({
|
||||||
|
type: 'sqlite',
|
||||||
|
database: ':memory:',
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
undefined,
|
||||||
|
);
|
||||||
const module: TestingModule = await Test.createTestingModule({
|
const module: TestingModule = await Test.createTestingModule({
|
||||||
providers: [
|
providers: [
|
||||||
NotesService,
|
NotesService,
|
||||||
|
@ -202,7 +211,7 @@ describe('NotesService', () => {
|
||||||
const note = Note.create(user, alias) as Note;
|
const note = Note.create(user, alias) as Note;
|
||||||
|
|
||||||
it('with no note', async () => {
|
it('with no note', async () => {
|
||||||
jest.spyOn(noteRepo, 'find').mockResolvedValueOnce(undefined);
|
jest.spyOn(noteRepo, 'find').mockResolvedValueOnce(null);
|
||||||
const notes = await service.getUserNotes(user);
|
const notes = await service.getUserNotes(user);
|
||||||
expect(notes).toEqual([]);
|
expect(notes).toEqual([]);
|
||||||
});
|
});
|
||||||
|
@ -374,7 +383,7 @@ describe('NotesService', () => {
|
||||||
where: () => createQueryBuilder,
|
where: () => createQueryBuilder,
|
||||||
orWhere: () => createQueryBuilder,
|
orWhere: () => createQueryBuilder,
|
||||||
setParameter: () => createQueryBuilder,
|
setParameter: () => createQueryBuilder,
|
||||||
getOne: () => undefined,
|
getOne: () => null,
|
||||||
};
|
};
|
||||||
jest
|
jest
|
||||||
.spyOn(noteRepo, 'createQueryBuilder')
|
.spyOn(noteRepo, 'createQueryBuilder')
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
*/
|
*/
|
||||||
import { forwardRef, Inject, Injectable } from '@nestjs/common';
|
import { forwardRef, Inject, Injectable } from '@nestjs/common';
|
||||||
import { InjectRepository } from '@nestjs/typeorm';
|
import { InjectRepository } from '@nestjs/typeorm';
|
||||||
import { Repository } from 'typeorm';
|
import { Equal, Repository } from 'typeorm';
|
||||||
|
|
||||||
import noteConfiguration, { NoteConfig } from '../config/note.config';
|
import noteConfiguration, { NoteConfig } from '../config/note.config';
|
||||||
import {
|
import {
|
||||||
|
@ -56,7 +56,7 @@ export class NotesService {
|
||||||
*/
|
*/
|
||||||
async getUserNotes(user: User): Promise<Note[]> {
|
async getUserNotes(user: User): Promise<Note[]> {
|
||||||
const notes = await this.noteRepository.find({
|
const notes = await this.noteRepository.find({
|
||||||
where: { owner: user },
|
where: { owner: Equal(user) },
|
||||||
relations: [
|
relations: [
|
||||||
'owner',
|
'owner',
|
||||||
'userPermissions',
|
'userPermissions',
|
||||||
|
@ -65,7 +65,7 @@ export class NotesService {
|
||||||
'aliases',
|
'aliases',
|
||||||
],
|
],
|
||||||
});
|
});
|
||||||
if (notes === undefined) {
|
if (notes === null) {
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
return notes;
|
return notes;
|
||||||
|
@ -188,7 +188,7 @@ export class NotesService {
|
||||||
.setParameter('noteIdOrAlias', noteIdOrAlias)
|
.setParameter('noteIdOrAlias', noteIdOrAlias)
|
||||||
.getOne();
|
.getOne();
|
||||||
|
|
||||||
if (note === undefined) {
|
if (note === null) {
|
||||||
this.logger.debug(
|
this.logger.debug(
|
||||||
`Could not find note '${noteIdOrAlias}'`,
|
`Could not find note '${noteIdOrAlias}'`,
|
||||||
'getNoteByIdOrAlias',
|
'getNoteByIdOrAlias',
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
*
|
*
|
||||||
* SPDX-License-Identifier: AGPL-3.0-only
|
* SPDX-License-Identifier: AGPL-3.0-only
|
||||||
*/
|
*/
|
||||||
import { Column, Entity, ManyToOne } from 'typeorm';
|
import { Column, Entity, ManyToOne, PrimaryColumn } from 'typeorm';
|
||||||
|
|
||||||
import { Group } from '../groups/group.entity';
|
import { Group } from '../groups/group.entity';
|
||||||
import { Note } from '../notes/note.entity';
|
import { Note } from '../notes/note.entity';
|
||||||
|
@ -17,14 +17,18 @@ export class NoteGroupPermission {
|
||||||
* See https://github.com/typeorm/typeorm/issues/6908
|
* See https://github.com/typeorm/typeorm/issues/6908
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
@PrimaryColumn()
|
||||||
|
groupId: number;
|
||||||
|
|
||||||
@ManyToOne((_) => Group, {
|
@ManyToOne((_) => Group, {
|
||||||
primary: true,
|
|
||||||
onDelete: 'CASCADE', // This deletes the NoteGroupPermission, when the associated Group is deleted
|
onDelete: 'CASCADE', // This deletes the NoteGroupPermission, when the associated Group is deleted
|
||||||
})
|
})
|
||||||
group: Group;
|
group: Group;
|
||||||
|
|
||||||
|
@PrimaryColumn()
|
||||||
|
noteId: string;
|
||||||
|
|
||||||
@ManyToOne((_) => Note, (note) => note.groupPermissions, {
|
@ManyToOne((_) => Note, (note) => note.groupPermissions, {
|
||||||
primary: true,
|
|
||||||
onDelete: 'CASCADE', // This deletes the NoteGroupPermission, when the associated Note is deleted
|
onDelete: 'CASCADE', // This deletes the NoteGroupPermission, when the associated Note is deleted
|
||||||
})
|
})
|
||||||
note: Note;
|
note: Note;
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
*
|
*
|
||||||
* SPDX-License-Identifier: AGPL-3.0-only
|
* SPDX-License-Identifier: AGPL-3.0-only
|
||||||
*/
|
*/
|
||||||
import { Column, Entity, ManyToOne } from 'typeorm';
|
import { Column, Entity, ManyToOne, PrimaryColumn } from 'typeorm';
|
||||||
|
|
||||||
import { Note } from '../notes/note.entity';
|
import { Note } from '../notes/note.entity';
|
||||||
import { User } from '../users/user.entity';
|
import { User } from '../users/user.entity';
|
||||||
|
@ -17,15 +17,19 @@ export class NoteUserPermission {
|
||||||
* See https://github.com/typeorm/typeorm/issues/6908
|
* See https://github.com/typeorm/typeorm/issues/6908
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
@PrimaryColumn()
|
||||||
|
userId: string;
|
||||||
|
|
||||||
@ManyToOne((_) => User, {
|
@ManyToOne((_) => User, {
|
||||||
primary: true,
|
|
||||||
onDelete: 'CASCADE', // This deletes the NoteUserPermission, when the associated Note is deleted
|
onDelete: 'CASCADE', // This deletes the NoteUserPermission, when the associated Note is deleted
|
||||||
orphanedRowAction: 'delete', // This ensures the whole row is deleted when the Permission stops being referenced
|
orphanedRowAction: 'delete', // This ensures the whole row is deleted when the Permission stops being referenced
|
||||||
})
|
})
|
||||||
user: User;
|
user: User;
|
||||||
|
|
||||||
|
@PrimaryColumn()
|
||||||
|
noteId: string;
|
||||||
|
|
||||||
@ManyToOne((_) => Note, (note) => note.userPermissions, {
|
@ManyToOne((_) => Note, (note) => note.userPermissions, {
|
||||||
primary: true,
|
|
||||||
onDelete: 'CASCADE', // This deletes the NoteUserPermission, when the associated Note is deleted
|
onDelete: 'CASCADE', // This deletes the NoteUserPermission, when the associated Note is deleted
|
||||||
orphanedRowAction: 'delete', // This ensures the whole row is deleted when the Permission stops being referenced
|
orphanedRowAction: 'delete', // This ensures the whole row is deleted when the Permission stops being referenced
|
||||||
})
|
})
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
import { ConfigModule } from '@nestjs/config';
|
import { ConfigModule } from '@nestjs/config';
|
||||||
import { Test, TestingModule } from '@nestjs/testing';
|
import { Test, TestingModule } from '@nestjs/testing';
|
||||||
import { getRepositoryToken } from '@nestjs/typeorm';
|
import { getRepositoryToken } from '@nestjs/typeorm';
|
||||||
import { Repository } from 'typeorm';
|
import { DataSource, EntityManager, Repository } from 'typeorm';
|
||||||
|
|
||||||
import { AuthToken } from '../auth/auth-token.entity';
|
import { AuthToken } from '../auth/auth-token.entity';
|
||||||
import { Author } from '../authors/author.entity';
|
import { Author } from '../authors/author.entity';
|
||||||
|
@ -49,8 +49,26 @@ describe('PermissionsService', () => {
|
||||||
* array and the overrideProvider call, as otherwise we have two instances
|
* array and the overrideProvider call, as otherwise we have two instances
|
||||||
* and the mock of createQueryBuilder replaces the wrong one
|
* and the mock of createQueryBuilder replaces the wrong one
|
||||||
* **/
|
* **/
|
||||||
userRepo = new Repository<User>();
|
userRepo = new Repository<User>(
|
||||||
noteRepo = new Repository<Note>();
|
'',
|
||||||
|
new EntityManager(
|
||||||
|
new DataSource({
|
||||||
|
type: 'sqlite',
|
||||||
|
database: ':memory:',
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
undefined,
|
||||||
|
);
|
||||||
|
noteRepo = new Repository<Note>(
|
||||||
|
'',
|
||||||
|
new EntityManager(
|
||||||
|
new DataSource({
|
||||||
|
type: 'sqlite',
|
||||||
|
database: ':memory:',
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
undefined,
|
||||||
|
);
|
||||||
const module: TestingModule = await Test.createTestingModule({
|
const module: TestingModule = await Test.createTestingModule({
|
||||||
providers: [
|
providers: [
|
||||||
PermissionsService,
|
PermissionsService,
|
||||||
|
@ -655,7 +673,9 @@ describe('PermissionsService', () => {
|
||||||
const noteWithPreexistingPermissions: Note = { ...note };
|
const noteWithPreexistingPermissions: Note = { ...note };
|
||||||
noteWithPreexistingPermissions.userPermissions = Promise.resolve([
|
noteWithPreexistingPermissions.userPermissions = Promise.resolve([
|
||||||
{
|
{
|
||||||
|
noteId: '',
|
||||||
note: noteWithPreexistingPermissions,
|
note: noteWithPreexistingPermissions,
|
||||||
|
userId: '',
|
||||||
user: user,
|
user: user,
|
||||||
canEdit: !userPermissionUpdate.canEdit,
|
canEdit: !userPermissionUpdate.canEdit,
|
||||||
},
|
},
|
||||||
|
@ -727,7 +747,9 @@ describe('PermissionsService', () => {
|
||||||
const noteWithUserPermission: Note = { ...note };
|
const noteWithUserPermission: Note = { ...note };
|
||||||
noteWithUserPermission.userPermissions = Promise.resolve([
|
noteWithUserPermission.userPermissions = Promise.resolve([
|
||||||
{
|
{
|
||||||
|
noteId: '',
|
||||||
note: noteWithUserPermission,
|
note: noteWithUserPermission,
|
||||||
|
userId: '',
|
||||||
user: user,
|
user: user,
|
||||||
canEdit: !userPermissionUpdate.canEdit,
|
canEdit: !userPermissionUpdate.canEdit,
|
||||||
},
|
},
|
||||||
|
@ -763,7 +785,9 @@ describe('PermissionsService', () => {
|
||||||
const noteWithPreexistingPermissions: Note = { ...note };
|
const noteWithPreexistingPermissions: Note = { ...note };
|
||||||
noteWithPreexistingPermissions.groupPermissions = Promise.resolve([
|
noteWithPreexistingPermissions.groupPermissions = Promise.resolve([
|
||||||
{
|
{
|
||||||
|
noteId: '',
|
||||||
note: noteWithPreexistingPermissions,
|
note: noteWithPreexistingPermissions,
|
||||||
|
groupId: 0,
|
||||||
group: group,
|
group: group,
|
||||||
canEdit: !groupPermissionUpdate.canEdit,
|
canEdit: !groupPermissionUpdate.canEdit,
|
||||||
},
|
},
|
||||||
|
@ -793,7 +817,9 @@ describe('PermissionsService', () => {
|
||||||
const noteWithPreexistingPermissions: Note = { ...note };
|
const noteWithPreexistingPermissions: Note = { ...note };
|
||||||
noteWithPreexistingPermissions.groupPermissions = Promise.resolve([
|
noteWithPreexistingPermissions.groupPermissions = Promise.resolve([
|
||||||
{
|
{
|
||||||
|
noteId: '',
|
||||||
note: noteWithPreexistingPermissions,
|
note: noteWithPreexistingPermissions,
|
||||||
|
groupId: 0,
|
||||||
group: group,
|
group: group,
|
||||||
canEdit: !groupPermissionUpdate.canEdit,
|
canEdit: !groupPermissionUpdate.canEdit,
|
||||||
},
|
},
|
||||||
|
@ -829,14 +855,18 @@ describe('PermissionsService', () => {
|
||||||
const noteWithPreexistingPermissions: Note = { ...note };
|
const noteWithPreexistingPermissions: Note = { ...note };
|
||||||
noteWithPreexistingPermissions.groupPermissions = Promise.resolve([
|
noteWithPreexistingPermissions.groupPermissions = Promise.resolve([
|
||||||
{
|
{
|
||||||
|
noteId: '',
|
||||||
note: noteWithPreexistingPermissions,
|
note: noteWithPreexistingPermissions,
|
||||||
|
groupId: 0,
|
||||||
group: group,
|
group: group,
|
||||||
canEdit: !groupPermissionUpdate.canEdit,
|
canEdit: !groupPermissionUpdate.canEdit,
|
||||||
},
|
},
|
||||||
]);
|
]);
|
||||||
noteWithPreexistingPermissions.userPermissions = Promise.resolve([
|
noteWithPreexistingPermissions.userPermissions = Promise.resolve([
|
||||||
{
|
{
|
||||||
|
noteId: '',
|
||||||
note: noteWithPreexistingPermissions,
|
note: noteWithPreexistingPermissions,
|
||||||
|
userId: '',
|
||||||
user: user,
|
user: user,
|
||||||
canEdit: !userPermissionUpdate.canEdit,
|
canEdit: !userPermissionUpdate.canEdit,
|
||||||
},
|
},
|
||||||
|
|
|
@ -98,7 +98,7 @@ describe('RevisionsService', () => {
|
||||||
expect(await service.getRevision({} as Note, 1)).toEqual(revision);
|
expect(await service.getRevision({} as Note, 1)).toEqual(revision);
|
||||||
});
|
});
|
||||||
it('throws if the revision is not in the databse', async () => {
|
it('throws if the revision is not in the databse', async () => {
|
||||||
jest.spyOn(revisionRepo, 'findOne').mockResolvedValueOnce(undefined);
|
jest.spyOn(revisionRepo, 'findOne').mockResolvedValueOnce(null);
|
||||||
await expect(service.getRevision({} as Note, 1)).rejects.toThrow(
|
await expect(service.getRevision({} as Note, 1)).rejects.toThrow(
|
||||||
NotInDBError,
|
NotInDBError,
|
||||||
);
|
);
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
*/
|
*/
|
||||||
import { forwardRef, Inject, Injectable } from '@nestjs/common';
|
import { forwardRef, Inject, Injectable } from '@nestjs/common';
|
||||||
import { InjectRepository } from '@nestjs/typeorm';
|
import { InjectRepository } from '@nestjs/typeorm';
|
||||||
import { Repository } from 'typeorm';
|
import { Equal, Repository } from 'typeorm';
|
||||||
|
|
||||||
import { NotInDBError } from '../errors/errors';
|
import { NotInDBError } from '../errors/errors';
|
||||||
import { ConsoleLoggerService } from '../logger/console-logger.service';
|
import { ConsoleLoggerService } from '../logger/console-logger.service';
|
||||||
|
@ -36,7 +36,7 @@ export class RevisionsService {
|
||||||
async getAllRevisions(note: Note): Promise<Revision[]> {
|
async getAllRevisions(note: Note): Promise<Revision[]> {
|
||||||
return await this.revisionRepository.find({
|
return await this.revisionRepository.find({
|
||||||
where: {
|
where: {
|
||||||
note: note,
|
note: Equal(note),
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -50,7 +50,7 @@ export class RevisionsService {
|
||||||
async purgeRevisions(note: Note): Promise<Revision[]> {
|
async purgeRevisions(note: Note): Promise<Revision[]> {
|
||||||
const revisions = await this.revisionRepository.find({
|
const revisions = await this.revisionRepository.find({
|
||||||
where: {
|
where: {
|
||||||
note: note,
|
note: Equal(note),
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
const latestRevison = await this.getLatestRevision(note);
|
const latestRevison = await this.getLatestRevision(note);
|
||||||
|
@ -66,10 +66,10 @@ export class RevisionsService {
|
||||||
const revision = await this.revisionRepository.findOne({
|
const revision = await this.revisionRepository.findOne({
|
||||||
where: {
|
where: {
|
||||||
id: revisionId,
|
id: revisionId,
|
||||||
note: note,
|
note: Equal(note),
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
if (revision === undefined) {
|
if (revision === null) {
|
||||||
throw new NotInDBError(
|
throw new NotInDBError(
|
||||||
`Revision with ID ${revisionId} for note ${note.id} not found.`,
|
`Revision with ID ${revisionId} for note ${note.id} not found.`,
|
||||||
);
|
);
|
||||||
|
@ -80,14 +80,14 @@ export class RevisionsService {
|
||||||
async getLatestRevision(note: Note): Promise<Revision> {
|
async getLatestRevision(note: Note): Promise<Revision> {
|
||||||
const revision = await this.revisionRepository.findOne({
|
const revision = await this.revisionRepository.findOne({
|
||||||
where: {
|
where: {
|
||||||
note: note,
|
note: Equal(note),
|
||||||
},
|
},
|
||||||
order: {
|
order: {
|
||||||
createdAt: 'DESC',
|
createdAt: 'DESC',
|
||||||
id: 'DESC',
|
id: 'DESC',
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
if (revision === undefined) {
|
if (revision === null) {
|
||||||
throw new NotInDBError(`Revision for note ${note.id} not found.`);
|
throw new NotInDBError(`Revision for note ${note.id} not found.`);
|
||||||
}
|
}
|
||||||
return revision;
|
return revision;
|
||||||
|
@ -96,13 +96,13 @@ export class RevisionsService {
|
||||||
async getFirstRevision(note: Note): Promise<Revision> {
|
async getFirstRevision(note: Note): Promise<Revision> {
|
||||||
const revision = await this.revisionRepository.findOne({
|
const revision = await this.revisionRepository.findOne({
|
||||||
where: {
|
where: {
|
||||||
note: note,
|
note: Equal(note),
|
||||||
},
|
},
|
||||||
order: {
|
order: {
|
||||||
createdAt: 'ASC',
|
createdAt: 'ASC',
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
if (revision === undefined) {
|
if (revision === null) {
|
||||||
throw new NotInDBError(`Revision for note ${note.id} not found.`);
|
throw new NotInDBError(`Revision for note ${note.id} not found.`);
|
||||||
}
|
}
|
||||||
return revision;
|
return revision;
|
||||||
|
|
|
@ -115,7 +115,7 @@ describe('UsersService', () => {
|
||||||
expect(getUser.displayName).toEqual(displayname);
|
expect(getUser.displayName).toEqual(displayname);
|
||||||
});
|
});
|
||||||
it('fails when user does not exits', async () => {
|
it('fails when user does not exits', async () => {
|
||||||
jest.spyOn(userRepo, 'findOne').mockResolvedValueOnce(undefined);
|
jest.spyOn(userRepo, 'findOne').mockResolvedValueOnce(null);
|
||||||
await expect(service.getUserByUsername(username)).rejects.toThrow(
|
await expect(service.getUserByUsername(username)).rejects.toThrow(
|
||||||
NotInDBError,
|
NotInDBError,
|
||||||
);
|
);
|
||||||
|
|
|
@ -89,7 +89,7 @@ export class UsersService {
|
||||||
where: { username: username },
|
where: { username: username },
|
||||||
relations: withRelations,
|
relations: withRelations,
|
||||||
});
|
});
|
||||||
if (user === undefined) {
|
if (user === null) {
|
||||||
throw new NotInDBError(`User with username '${username}' not found`);
|
throw new NotInDBError(`User with username '${username}' not found`);
|
||||||
}
|
}
|
||||||
return user;
|
return user;
|
||||||
|
|
|
@ -190,7 +190,7 @@ describe('History', () => {
|
||||||
const alias = (await entry.note.aliases).filter((alias) => alias.primary)[0]
|
const alias = (await entry.note.aliases).filter((alias) => alias.primary)[0]
|
||||||
.name;
|
.name;
|
||||||
await agent
|
await agent
|
||||||
.put(`/api/private/me/history/${alias || 'undefined'}`)
|
.put(`/api/private/me/history/${alias || 'null'}`)
|
||||||
.send({ pinStatus: true })
|
.send({ pinStatus: true })
|
||||||
.expect(200);
|
.expect(200);
|
||||||
const userEntries = await testSetup.historyService.getEntriesByUser(user);
|
const userEntries = await testSetup.historyService.getEntriesByUser(user);
|
||||||
|
@ -206,7 +206,7 @@ describe('History', () => {
|
||||||
const entry2 = await historyService.updateHistoryEntryTimestamp(note, user);
|
const entry2 = await historyService.updateHistoryEntryTimestamp(note, user);
|
||||||
const entryDto = await historyService.toHistoryEntryDto(entry2);
|
const entryDto = await historyService.toHistoryEntryDto(entry2);
|
||||||
await agent
|
await agent
|
||||||
.delete(`/api/private/me/history/${alias || 'undefined'}`)
|
.delete(`/api/private/me/history/${alias || 'null'}`)
|
||||||
.expect(204);
|
.expect(204);
|
||||||
const userEntries = await historyService.getEntriesByUser(user);
|
const userEntries = await historyService.getEntriesByUser(user);
|
||||||
expect(userEntries.length).toEqual(1);
|
expect(userEntries.length).toEqual(1);
|
||||||
|
|
|
@ -114,6 +114,7 @@ describe('Me', () => {
|
||||||
const history = await testSetup.historyService.getEntriesByUser(user);
|
const history = await testSetup.historyService.getEntriesByUser(user);
|
||||||
const historyEntry: HistoryEntryDto = response.body;
|
const historyEntry: HistoryEntryDto = response.body;
|
||||||
expect(historyEntry.pinStatus).toEqual(true);
|
expect(historyEntry.pinStatus).toEqual(true);
|
||||||
|
|
||||||
let theEntry: HistoryEntryDto;
|
let theEntry: HistoryEntryDto;
|
||||||
for (const entry of history) {
|
for (const entry of history) {
|
||||||
if (
|
if (
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue