refactor: adapt for typeorm 0.3

Signed-off-by: David Mehren <git@herrmehren.de>
This commit is contained in:
David Mehren 2022-03-20 22:40:41 +01:00
parent a07b5f54d1
commit c4975e4783
21 changed files with 131 additions and 69 deletions

View file

@ -108,7 +108,7 @@ describe('AuthService', () => {
});
describe('fails:', () => {
it('AuthToken could not be found', async () => {
jest.spyOn(authTokenRepo, 'findOne').mockResolvedValueOnce(undefined);
jest.spyOn(authTokenRepo, 'findOne').mockResolvedValueOnce(null);
await expect(
service.getAuthTokenAndValidate(authToken.keyId, token),
).rejects.toThrow(NotInDBError);
@ -157,7 +157,7 @@ describe('AuthService', () => {
await service.setLastUsedToken(authToken.keyId);
});
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(
NotInDBError,
);
@ -226,7 +226,7 @@ describe('AuthService', () => {
await service.removeToken(authToken.keyId);
});
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(
NotInDBError,
);

View file

@ -7,7 +7,7 @@ import { Injectable } from '@nestjs/common';
import { Cron, Timeout } from '@nestjs/schedule';
import { InjectRepository } from '@nestjs/typeorm';
import crypto, { randomBytes } from 'crypto';
import { Repository } from 'typeorm';
import { Equal, Repository } from 'typeorm';
import {
NotInDBError,
@ -104,7 +104,7 @@ export class AuthService {
const accessToken = await this.authTokenRepository.findOne({
where: { keyId: keyId },
});
if (accessToken === undefined) {
if (accessToken === null) {
throw new NotInDBError(`AuthToken for key '${keyId}' not found`);
}
accessToken.lastUsedAt = new Date();
@ -119,7 +119,7 @@ export class AuthService {
where: { keyId: keyId },
relations: ['user'],
});
if (accessToken === undefined) {
if (accessToken === null) {
throw new NotInDBError(`AuthToken '${token}' not found`);
}
// Hash the user-provided token
@ -150,9 +150,9 @@ export class AuthService {
async getTokensByUser(user: User): Promise<AuthToken[]> {
const tokens = await this.authTokenRepository.find({
where: { user: user },
where: { user: Equal(user) },
});
if (tokens === undefined) {
if (tokens === null) {
return [];
}
return tokens;
@ -162,7 +162,7 @@ export class AuthService {
const token = await this.authTokenRepository.findOne({
where: { keyId: keyId },
});
if (token === undefined) {
if (token === null) {
throw new NotInDBError(`AuthToken for key '${keyId}' not found`);
}
await this.authTokenRepository.remove(token);

View file

@ -82,7 +82,7 @@ describe('GroupsService', () => {
expect(foundGroup.special).toEqual(group.special);
});
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(
NotInDBError,
);

View file

@ -60,7 +60,7 @@ export class GroupsService {
const group = await this.groupRepository.findOne({
where: { name: name },
});
if (group === undefined) {
if (group === null) {
throw new NotInDBError(`Group with name '${name}' not found`);
}
return group;

View file

@ -3,7 +3,13 @@
*
* 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 { User } from '../users/user.entity';
@ -16,16 +22,19 @@ export class HistoryEntry {
* primary keys.
* See https://github.com/typeorm/typeorm/issues/6908
*/
@PrimaryColumn()
userId: string;
@ManyToOne((_) => User, (user) => user.historyEntries, {
onDelete: 'CASCADE',
primary: true,
})
user: User;
@PrimaryColumn()
noteId: string;
@ManyToOne((_) => Note, (note) => note.historyEntries, {
onDelete: 'CASCADE',
primary: true,
})
note: Note;

View file

@ -5,7 +5,7 @@
*/
import { Injectable } from '@nestjs/common';
import { InjectConnection, InjectRepository } from '@nestjs/typeorm';
import { Connection, Repository } from 'typeorm';
import { Connection, Equal, Repository } from 'typeorm';
import { NotInDBError } from '../errors/errors';
import { ConsoleLoggerService } from '../logger/console-logger.service';
@ -41,7 +41,7 @@ export class HistoryService {
*/
async getEntriesByUser(user: User): Promise<HistoryEntry[]> {
return await this.historyEntryRepository.find({
where: { user: user },
where: { user: Equal(user) },
relations: ['note', 'note.aliases', 'user'],
});
}
@ -56,8 +56,8 @@ export class HistoryService {
async getEntryByNote(note: Note, user: User): Promise<HistoryEntry> {
const entry = await this.historyEntryRepository.findOne({
where: {
note: note,
user: user,
note: Equal(note),
user: Equal(user),
},
relations: ['note', 'note.aliases', 'user'],
});
@ -150,7 +150,7 @@ export class HistoryService {
): Promise<void> {
await this.connection.transaction(async (manager) => {
const currentHistory = await manager.find<HistoryEntry>(HistoryEntry, {
where: { user: user },
where: { user: Equal(user) },
relations: ['note', 'note.aliases', 'user'],
});
for (const entry of currentHistory) {

View file

@ -210,7 +210,7 @@ describe('MediaService', () => {
});
it("fails: can't find mediaUpload", async () => {
const testFileName = 'testFilename';
jest.spyOn(mediaRepo, 'findOne').mockResolvedValueOnce(undefined);
jest.spyOn(mediaRepo, 'findOne').mockResolvedValueOnce(null);
await expect(service.findUploadByFilename(testFileName)).rejects.toThrow(
NotInDBError,
);
@ -243,8 +243,8 @@ describe('MediaService', () => {
} as User);
expect(mediaList).toEqual([]);
});
it('with error (undefined as return value of find)', async () => {
jest.spyOn(mediaRepo, 'find').mockResolvedValueOnce(undefined);
it('with error (null as return value of find)', async () => {
jest.spyOn(mediaRepo, 'find').mockResolvedValueOnce(null);
const mediaList = await service.listUploadsByUser({
username: username,
} as User);

View file

@ -8,7 +8,7 @@ import { ModuleRef } from '@nestjs/core';
import { InjectRepository } from '@nestjs/typeorm';
import crypto from 'crypto';
import * as FileType from 'file-type';
import { Repository } from 'typeorm';
import { Equal, Repository } from 'typeorm';
import mediaConfiguration, { MediaConfig } from '../config/media.config';
import { ClientError, NotInDBError } from '../errors/errors';
@ -128,10 +128,11 @@ export class MediaService {
* @throws {MediaBackendError} - there was an error retrieving the url
*/
async findUploadByFilename(filename: string): Promise<MediaUpload> {
const mediaUpload = await this.mediaUploadRepository.findOne(filename, {
const mediaUpload = await this.mediaUploadRepository.findOne({
where: { id: filename },
relations: ['user'],
});
if (mediaUpload === undefined) {
if (mediaUpload === null) {
throw new NotInDBError(
`MediaUpload with filename '${filename}' not found`,
);
@ -147,10 +148,10 @@ export class MediaService {
*/
async listUploadsByUser(user: User): Promise<MediaUpload[]> {
const mediaUploads = await this.mediaUploadRepository.find({
where: { user: user },
where: { user: Equal(user) },
relations: ['user', 'note'],
});
if (mediaUploads === undefined) {
if (mediaUploads === null) {
return [];
}
return mediaUploads;
@ -164,10 +165,10 @@ export class MediaService {
*/
async listUploadsByNote(note: Note): Promise<MediaUpload[]> {
const mediaUploads = await this.mediaUploadRepository.find({
where: { note: note },
where: { note: Equal(note) },
relations: ['user', 'note'],
});
if (mediaUploads === undefined) {
if (mediaUploads === null) {
return [];
}
return mediaUploads;

View file

@ -120,8 +120,8 @@ describe('AliasService', () => {
jest
.spyOn(noteRepo, 'save')
.mockImplementationOnce(async (note: Note): Promise<Note> => note);
jest.spyOn(noteRepo, 'findOne').mockResolvedValueOnce(undefined);
jest.spyOn(aliasRepo, 'findOne').mockResolvedValueOnce(undefined);
jest.spyOn(noteRepo, 'findOne').mockResolvedValueOnce(null);
jest.spyOn(aliasRepo, 'findOne').mockResolvedValueOnce(null);
const savedAlias = await service.addAlias(note, alias);
expect(savedAlias.name).toEqual(alias);
expect(savedAlias.primary).toBeTruthy();
@ -131,8 +131,8 @@ describe('AliasService', () => {
jest
.spyOn(noteRepo, 'save')
.mockImplementationOnce(async (note: Note): Promise<Note> => note);
jest.spyOn(noteRepo, 'findOne').mockResolvedValueOnce(undefined);
jest.spyOn(aliasRepo, 'findOne').mockResolvedValueOnce(undefined);
jest.spyOn(noteRepo, 'findOne').mockResolvedValueOnce(null);
jest.spyOn(aliasRepo, 'findOne').mockResolvedValueOnce(null);
const savedAlias = await service.addAlias(note, alias2);
expect(savedAlias.name).toEqual(alias2);
expect(savedAlias.primary).toBeFalsy();
@ -230,7 +230,7 @@ describe('AliasService', () => {
it('mark the alias as primary', async () => {
jest
.spyOn(aliasRepo, 'findOne')
.spyOn(aliasRepo, 'findOneBy')
.mockResolvedValueOnce(alias)
.mockResolvedValueOnce(alias2);
jest

View file

@ -45,7 +45,7 @@ export class AliasService {
const foundAlias = await this.aliasRepository.findOne({
where: { name: alias },
});
if (foundAlias !== undefined) {
if (foundAlias !== null) {
this.logger.debug(`The alias '${alias}' is already used.`, 'addAlias');
throw new AlreadyInDBError(`The alias '${alias}' is already used.`);
}
@ -53,7 +53,7 @@ export class AliasService {
const foundNote = await this.noteRepository.findOne({
where: { publicId: alias },
});
if (foundNote !== undefined) {
if (foundNote !== null) {
this.logger.debug(
`The alias '${alias}' is already a public id.`,
'addAlias',
@ -113,8 +113,12 @@ export class AliasService {
throw new NotInDBError(`The alias '${alias}' is not used by this note.`);
}
const oldPrimary = await this.aliasRepository.findOne(oldPrimaryId);
const newPrimary = await this.aliasRepository.findOne(newPrimaryId);
const oldPrimary = await this.aliasRepository.findOneBy({
id: oldPrimaryId,
});
const newPrimary = await this.aliasRepository.findOneBy({
id: newPrimaryId,
});
if (!oldPrimary || !newPrimary) {
throw new Error('This should not happen!');

View file

@ -6,7 +6,7 @@
import { ConfigModule, ConfigService } from '@nestjs/config';
import { Test, TestingModule } from '@nestjs/testing';
import { getRepositoryToken } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { DataSource, EntityManager, Repository } from 'typeorm';
import { AuthToken } from '../auth/auth-token.entity';
import { Author } from '../authors/author.entity';
@ -121,7 +121,16 @@ describe('NotesService', () => {
* the overrideProvider call, as otherwise we have two instances
* 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({
providers: [
NotesService,
@ -202,7 +211,7 @@ describe('NotesService', () => {
const note = Note.create(user, alias) as Note;
it('with no note', async () => {
jest.spyOn(noteRepo, 'find').mockResolvedValueOnce(undefined);
jest.spyOn(noteRepo, 'find').mockResolvedValueOnce(null);
const notes = await service.getUserNotes(user);
expect(notes).toEqual([]);
});
@ -374,7 +383,7 @@ describe('NotesService', () => {
where: () => createQueryBuilder,
orWhere: () => createQueryBuilder,
setParameter: () => createQueryBuilder,
getOne: () => undefined,
getOne: () => null,
};
jest
.spyOn(noteRepo, 'createQueryBuilder')

View file

@ -5,7 +5,7 @@
*/
import { forwardRef, Inject, Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { Equal, Repository } from 'typeorm';
import noteConfiguration, { NoteConfig } from '../config/note.config';
import {
@ -56,7 +56,7 @@ export class NotesService {
*/
async getUserNotes(user: User): Promise<Note[]> {
const notes = await this.noteRepository.find({
where: { owner: user },
where: { owner: Equal(user) },
relations: [
'owner',
'userPermissions',
@ -65,7 +65,7 @@ export class NotesService {
'aliases',
],
});
if (notes === undefined) {
if (notes === null) {
return [];
}
return notes;
@ -188,7 +188,7 @@ export class NotesService {
.setParameter('noteIdOrAlias', noteIdOrAlias)
.getOne();
if (note === undefined) {
if (note === null) {
this.logger.debug(
`Could not find note '${noteIdOrAlias}'`,
'getNoteByIdOrAlias',

View file

@ -3,7 +3,7 @@
*
* 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 { Note } from '../notes/note.entity';
@ -17,14 +17,18 @@ export class NoteGroupPermission {
* See https://github.com/typeorm/typeorm/issues/6908
*/
@PrimaryColumn()
groupId: number;
@ManyToOne((_) => Group, {
primary: true,
onDelete: 'CASCADE', // This deletes the NoteGroupPermission, when the associated Group is deleted
})
group: Group;
@PrimaryColumn()
noteId: string;
@ManyToOne((_) => Note, (note) => note.groupPermissions, {
primary: true,
onDelete: 'CASCADE', // This deletes the NoteGroupPermission, when the associated Note is deleted
})
note: Note;

View file

@ -3,7 +3,7 @@
*
* 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 { User } from '../users/user.entity';
@ -17,15 +17,19 @@ export class NoteUserPermission {
* See https://github.com/typeorm/typeorm/issues/6908
*/
@PrimaryColumn()
userId: string;
@ManyToOne((_) => User, {
primary: true,
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
})
user: User;
@PrimaryColumn()
noteId: string;
@ManyToOne((_) => Note, (note) => note.userPermissions, {
primary: true,
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
})

View file

@ -6,7 +6,7 @@
import { ConfigModule } from '@nestjs/config';
import { Test, TestingModule } from '@nestjs/testing';
import { getRepositoryToken } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { DataSource, EntityManager, Repository } from 'typeorm';
import { AuthToken } from '../auth/auth-token.entity';
import { Author } from '../authors/author.entity';
@ -49,8 +49,26 @@ describe('PermissionsService', () => {
* array and the overrideProvider call, as otherwise we have two instances
* and the mock of createQueryBuilder replaces the wrong one
* **/
userRepo = new Repository<User>();
noteRepo = new Repository<Note>();
userRepo = new Repository<User>(
'',
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({
providers: [
PermissionsService,
@ -655,7 +673,9 @@ describe('PermissionsService', () => {
const noteWithPreexistingPermissions: Note = { ...note };
noteWithPreexistingPermissions.userPermissions = Promise.resolve([
{
noteId: '',
note: noteWithPreexistingPermissions,
userId: '',
user: user,
canEdit: !userPermissionUpdate.canEdit,
},
@ -727,7 +747,9 @@ describe('PermissionsService', () => {
const noteWithUserPermission: Note = { ...note };
noteWithUserPermission.userPermissions = Promise.resolve([
{
noteId: '',
note: noteWithUserPermission,
userId: '',
user: user,
canEdit: !userPermissionUpdate.canEdit,
},
@ -763,7 +785,9 @@ describe('PermissionsService', () => {
const noteWithPreexistingPermissions: Note = { ...note };
noteWithPreexistingPermissions.groupPermissions = Promise.resolve([
{
noteId: '',
note: noteWithPreexistingPermissions,
groupId: 0,
group: group,
canEdit: !groupPermissionUpdate.canEdit,
},
@ -793,7 +817,9 @@ describe('PermissionsService', () => {
const noteWithPreexistingPermissions: Note = { ...note };
noteWithPreexistingPermissions.groupPermissions = Promise.resolve([
{
noteId: '',
note: noteWithPreexistingPermissions,
groupId: 0,
group: group,
canEdit: !groupPermissionUpdate.canEdit,
},
@ -829,14 +855,18 @@ describe('PermissionsService', () => {
const noteWithPreexistingPermissions: Note = { ...note };
noteWithPreexistingPermissions.groupPermissions = Promise.resolve([
{
noteId: '',
note: noteWithPreexistingPermissions,
groupId: 0,
group: group,
canEdit: !groupPermissionUpdate.canEdit,
},
]);
noteWithPreexistingPermissions.userPermissions = Promise.resolve([
{
noteId: '',
note: noteWithPreexistingPermissions,
userId: '',
user: user,
canEdit: !userPermissionUpdate.canEdit,
},

View file

@ -98,7 +98,7 @@ describe('RevisionsService', () => {
expect(await service.getRevision({} as Note, 1)).toEqual(revision);
});
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(
NotInDBError,
);

View file

@ -5,7 +5,7 @@
*/
import { forwardRef, Inject, Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { Equal, Repository } from 'typeorm';
import { NotInDBError } from '../errors/errors';
import { ConsoleLoggerService } from '../logger/console-logger.service';
@ -36,7 +36,7 @@ export class RevisionsService {
async getAllRevisions(note: Note): Promise<Revision[]> {
return await this.revisionRepository.find({
where: {
note: note,
note: Equal(note),
},
});
}
@ -50,7 +50,7 @@ export class RevisionsService {
async purgeRevisions(note: Note): Promise<Revision[]> {
const revisions = await this.revisionRepository.find({
where: {
note: note,
note: Equal(note),
},
});
const latestRevison = await this.getLatestRevision(note);
@ -66,10 +66,10 @@ export class RevisionsService {
const revision = await this.revisionRepository.findOne({
where: {
id: revisionId,
note: note,
note: Equal(note),
},
});
if (revision === undefined) {
if (revision === null) {
throw new NotInDBError(
`Revision with ID ${revisionId} for note ${note.id} not found.`,
);
@ -80,14 +80,14 @@ export class RevisionsService {
async getLatestRevision(note: Note): Promise<Revision> {
const revision = await this.revisionRepository.findOne({
where: {
note: note,
note: Equal(note),
},
order: {
createdAt: 'DESC',
id: 'DESC',
},
});
if (revision === undefined) {
if (revision === null) {
throw new NotInDBError(`Revision for note ${note.id} not found.`);
}
return revision;
@ -96,13 +96,13 @@ export class RevisionsService {
async getFirstRevision(note: Note): Promise<Revision> {
const revision = await this.revisionRepository.findOne({
where: {
note: note,
note: Equal(note),
},
order: {
createdAt: 'ASC',
},
});
if (revision === undefined) {
if (revision === null) {
throw new NotInDBError(`Revision for note ${note.id} not found.`);
}
return revision;

View file

@ -115,7 +115,7 @@ describe('UsersService', () => {
expect(getUser.displayName).toEqual(displayname);
});
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(
NotInDBError,
);

View file

@ -89,7 +89,7 @@ export class UsersService {
where: { username: username },
relations: withRelations,
});
if (user === undefined) {
if (user === null) {
throw new NotInDBError(`User with username '${username}' not found`);
}
return user;

View file

@ -190,7 +190,7 @@ describe('History', () => {
const alias = (await entry.note.aliases).filter((alias) => alias.primary)[0]
.name;
await agent
.put(`/api/private/me/history/${alias || 'undefined'}`)
.put(`/api/private/me/history/${alias || 'null'}`)
.send({ pinStatus: true })
.expect(200);
const userEntries = await testSetup.historyService.getEntriesByUser(user);
@ -206,7 +206,7 @@ describe('History', () => {
const entry2 = await historyService.updateHistoryEntryTimestamp(note, user);
const entryDto = await historyService.toHistoryEntryDto(entry2);
await agent
.delete(`/api/private/me/history/${alias || 'undefined'}`)
.delete(`/api/private/me/history/${alias || 'null'}`)
.expect(204);
const userEntries = await historyService.getEntriesByUser(user);
expect(userEntries.length).toEqual(1);

View file

@ -114,6 +114,7 @@ describe('Me', () => {
const history = await testSetup.historyService.getEntriesByUser(user);
const historyEntry: HistoryEntryDto = response.body;
expect(historyEntry.pinStatus).toEqual(true);
let theEntry: HistoryEntryDto;
for (const entry of history) {
if (