refactor(backend): use @hedgedoc/commons DTOs

Co-authored-by: Erik Michelson <github@erik.michelson.eu>
Signed-off-by: Erik Michelson <github@erik.michelson.eu>
Signed-off-by: Philip Molares <philip.molares@udo.edu>
This commit is contained in:
Philip Molares 2025-03-22 00:38:15 +01:00
parent 7285c2bc50
commit b11dbd51c8
94 changed files with 514 additions and 1642 deletions

View file

@ -1,77 +0,0 @@
/*
* SPDX-FileCopyrightText: 2024 The HedgeDoc developers (see AUTHORS file)
*
* SPDX-License-Identifier: AGPL-3.0-only
*/
import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger';
import { Type } from 'class-transformer';
import { IsLowercase, IsOptional, IsString } from 'class-validator';
import { BaseDto } from '../utils/base.dto.';
import { Username } from '../utils/username';
export class UserInfoDto extends BaseDto {
/**
* The username
* @example "john.smith"
*/
@Type(() => String)
@IsString()
@IsLowercase()
@ApiProperty()
username: Username;
/**
* The display name
* @example "John Smith"
*/
@IsString()
@ApiProperty()
displayName: string;
/**
* URL of the profile picture
* @example "https://hedgedoc.example.com/uploads/johnsmith.png"
*/
@ApiPropertyOptional({
format: 'uri',
})
@IsOptional()
@IsString()
photoUrl?: string;
}
/**
* This DTO contains all attributes of the standard UserInfoDto
* in addition to the email address.
*/
export class FullUserInfoDto extends UserInfoDto {
/**
* Email address of the user
* @example "john.smith@example.com"
*/
@ApiPropertyOptional({
format: 'email',
})
@IsOptional()
@IsString()
email?: string;
}
export class FullUserInfoWithIdDto extends FullUserInfoDto {
/**
* The user's ID
* @example 42
*/
@IsString()
id: string;
}
export class UserLoginInfoDto extends UserInfoDto {
/**
* Identifier of the auth provider that was used to log in
*/
@ApiProperty()
@IsString()
authProvider: string;
}

View file

@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2024 The HedgeDoc developers (see AUTHORS file)
* SPDX-FileCopyrightText: 2025 The HedgeDoc developers (see AUTHORS file)
*
* SPDX-License-Identifier: AGPL-3.0-only
*/
@ -20,7 +20,6 @@ import { Group } from '../groups/group.entity';
import { HistoryEntry } from '../history/history-entry.entity';
import { MediaUpload } from '../media/media-upload.entity';
import { Note } from '../notes/note.entity';
import { Username } from '../utils/username';
@Entity()
export class User {
@ -30,7 +29,7 @@ export class User {
@Column({
unique: true,
})
username: Username;
username: string;
@Column()
displayName: string;
@ -78,7 +77,7 @@ export class User {
private constructor() {}
public static create(
username: Username,
username: string,
displayName: string,
email?: string,
photoUrl?: string,

View file

@ -1,21 +0,0 @@
/*
* SPDX-FileCopyrightText: 2024 The HedgeDoc developers (see AUTHORS file)
*
* SPDX-License-Identifier: AGPL-3.0-only
*/
import { IsBoolean, IsLowercase, IsString } from 'class-validator';
import { BaseDto } from '../utils/base.dto.';
import { Username } from '../utils/username';
export class UsernameCheckDto extends BaseDto {
// eslint-disable-next-line @darraghor/nestjs-typed/validated-non-primitive-property-needs-type-decorator
@IsString()
@IsLowercase()
username: Username;
}
export class UsernameCheckResponseDto extends BaseDto {
@IsBoolean()
usernameAvailable: boolean;
}

View file

@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2024 The HedgeDoc developers (see AUTHORS file)
* SPDX-FileCopyrightText: 2025 The HedgeDoc developers (see AUTHORS file)
*
* SPDX-License-Identifier: AGPL-3.0-only
*/
@ -54,7 +54,7 @@ describe('UsersService', () => {
.mockImplementationOnce(async (user: User): Promise<User> => user);
});
it('successfully creates a user', async () => {
const user = await service.createUser(username, displayname);
const user = await service.createUser(username, displayname, null, null);
expect(user.username).toEqual(username);
expect(user.displayName).toEqual(displayname);
});
@ -64,11 +64,11 @@ describe('UsersService', () => {
throw new Error();
});
// create first user with username
await service.createUser(username, displayname);
await service.createUser(username, displayname, null, null);
// attempt to create second user with username
await expect(service.createUser(username, displayname)).rejects.toThrow(
AlreadyInDBError,
);
await expect(
service.createUser(username, displayname, null, null),
).rejects.toThrow(AlreadyInDBError);
});
});
@ -134,7 +134,7 @@ describe('UsersService', () => {
expect(photoUrl).toEqual(photo);
});
it('works if a user no photoUrl', () => {
user.photo = undefined;
user.photo = null;
const photoUrl = service.getPhotoUrl(user);
expect(photoUrl).toEqual('');
});

View file

@ -1,9 +1,15 @@
/*
* SPDX-FileCopyrightText: 2024 The HedgeDoc developers (see AUTHORS file)
* SPDX-FileCopyrightText: 2025 The HedgeDoc developers (see AUTHORS file)
*
* SPDX-License-Identifier: AGPL-3.0-only
*/
import { REGEX_USERNAME } from '@hedgedoc/commons';
import {
FullUserInfoDto,
LoginUserInfoDto,
ProviderType,
REGEX_USERNAME,
UserInfoDto,
} from '@hedgedoc/commons';
import { BadRequestException, Inject, Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
@ -11,12 +17,6 @@ import { Repository } from 'typeorm';
import AuthConfiguration, { AuthConfig } from '../config/auth.config';
import { AlreadyInDBError, NotInDBError } from '../errors/errors';
import { ConsoleLoggerService } from '../logger/console-logger.service';
import { Username } from '../utils/username';
import {
FullUserInfoDto,
UserInfoDto,
UserLoginInfoDto,
} from './user-info.dto';
import { UserRelationEnum } from './user-relation.enum';
import { User } from './user.entity';
@ -34,7 +34,7 @@ export class UsersService {
/**
* @async
* Create a new user with a given username and displayName
* @param {Username} username - the username the new user shall have
* @param {string} username - the username the new user shall have
* @param {string} displayName - the display name the new user shall have
* @param {string} [email] - the email the new user shall have
* @param {string} [photoUrl] - the photoUrl the new user shall have
@ -43,17 +43,22 @@ export class UsersService {
* @throws {AlreadyInDBError} the username is already taken.
*/
async createUser(
username: Username,
username: string,
displayName: string,
email?: string,
photoUrl?: string,
email: string | null,
photoUrl: string | null,
): Promise<User> {
if (!REGEX_USERNAME.test(username)) {
throw new BadRequestException(
`The username '${username}' is not a valid username.`,
);
}
const user = User.create(username, displayName, email, photoUrl);
const user = User.create(
username,
displayName,
email || undefined,
photoUrl || undefined,
);
try {
return await this.userRepository.save(user);
} catch {
@ -123,7 +128,7 @@ export class UsersService {
* @param username - the username to check
* @return {boolean} true if the user exists, false otherwise
*/
async checkIfUserExists(username: Username): Promise<boolean> {
async checkIfUserExists(username: string): Promise<boolean> {
const user = await this.userRepository.findOne({
where: { username: username },
});
@ -133,12 +138,12 @@ export class UsersService {
/**
* @async
* Get the user specified by the username
* @param {Username} username the username by which the user is specified
* @param {string} username the username by which the user is specified
* @param {UserRelationEnum[]} [withRelations=[]] if the returned user object should contain certain relations
* @return {User} the specified user
*/
async getUserByUsername(
username: Username,
username: string,
withRelations: UserRelationEnum[] = [],
): Promise<User> {
const user = await this.userRepository.findOne({
@ -191,7 +196,7 @@ export class UsersService {
};
}
toUserLoginInfoDto(user: User, authProvider: string): UserLoginInfoDto {
return { ...this.toUserDto(user), authProvider };
toLoginUserInfoDto(user: User, authProvider: ProviderType): LoginUserInfoDto {
return { ...this.toFullUserDto(user), authProvider };
}
}