feat: Add class-transformer and class-validator packages

- Added "@nestjs/mapped-types" package to package.json
- Added "class-transformer" and "class-validator" packages to package.json
- Imported ValidationPipe from "@nestjs/common" in main.ts and added it as a global pipe
- Added validation decorators to CreateUserInput, SetUserPasswordInput, and UpdateUserInput classes
- Created UserController with create, findAll, and removeUser methods
- Updated UserEntity class with @Exclude decorator for password field
- Updated UserModule to include UserController
- Updated UserService to use class-transformer for mapping Prisma entities to UserEntity class
- Updated UserService to return UserEntity instances instead of Prisma entities
- Updated UserService to handle NotFoundException for setPassword and remove methods
This commit is contained in:
M1000fr 2024-11-28 15:53:38 +01:00
parent 38105c3d43
commit b40249fdfb
10 changed files with 125 additions and 10 deletions

View File

@ -18,10 +18,13 @@
"@nestjs/config": "^3.3.0",
"@nestjs/core": "^10.0.0",
"@nestjs/jwt": "^10.2.0",
"@nestjs/mapped-types": "^2.0.6",
"@nestjs/passport": "^10.0.3",
"@nestjs/platform-express": "^10.4.11",
"@prisma/client": "5.22.0",
"axios": "^1.7.7",
"class-transformer": "^0.5.1",
"class-validator": "^0.14.1",
"passport": "^0.7.0",
"passport-discord": "^0.1.4",
"passport-jwt": "^4.0.1",

View File

@ -1,5 +1,6 @@
import { NestFactory } from "@nestjs/core";
import { AppModule } from "./app.module";
import { ValidationPipe } from "@nestjs/common";
async function bootstrap() {
const app = await NestFactory.create(AppModule);
@ -8,6 +9,8 @@ async function bootstrap() {
origin: "*",
});
app.useGlobalPipes(new ValidationPipe());
await app.listen(process.env.PORT ?? 3000, "0.0.0.0");
}
bootstrap();

View File

@ -1,4 +1,9 @@
import { IsBoolean, IsString } from "class-validator";
export class CreateUserInput {
@IsString()
username: string;
@IsBoolean()
isAdmin: boolean;
}

View File

@ -1,5 +1,9 @@
import { IsString } from "class-validator";
export class SetUserPasswordInput {
@IsString()
id: string;
@IsString()
password: string;
}

View File

@ -1,7 +1,12 @@
import { PartialType } from "@nestjs/mapped-types";
import { IsString } from "class-validator";
import { CreateUserInput } from "./create-user.input";
export class UpdateUserInput extends CreateUserInput {
export class UpdateUserInput extends PartialType(CreateUserInput) {
@IsString()
id: string;
@IsString()
username: string;
}

View File

@ -0,0 +1,26 @@
import { Body, Controller, Delete, Get, Param, Post, UseGuards } from "@nestjs/common";
import { AuthGuard } from "@nestjs/passport";
import { CreateUserInput } from "./dto/create-user.input";
import { UserService } from "./user.service";
@Controller("user")
@UseGuards(AuthGuard("jwt"))
export class UserController {
constructor(private readonly userService: UserService) {}
@Post()
create(@Body() createUserInput: CreateUserInput) {
return this.userService.create(createUserInput);
}
@Get()
findAll() {
return this.userService.findAll();
}
@Delete(":id")
removeUser(@Param("id") id: string) {
return this.userService.remove(id);
}
}

View File

@ -1,8 +1,12 @@
import { Exclude } from "class-transformer";
export class User {
export class UserEntity {
id: string;
username: string;
isAdmin: boolean;
@Exclude()
password: string;
}

View File

@ -3,8 +3,10 @@ import { Module } from "@nestjs/common";
import { UserService } from "./user.service";
import { PrismaService } from "src/prisma/prisma.service";
import { UserController } from './user.controller';
@Module({
providers: [UserService, PrismaService]
providers: [UserService, PrismaService],
controllers: [UserController]
})
export class UserModule {}

View File

@ -1,48 +1,77 @@
import { Injectable } from "@nestjs/common";
import { Injectable, NotFoundException } from "@nestjs/common";
import { plainToClass } from "class-transformer";
import { PrismaService } from "src/prisma/prisma.service";
import { CreateUserInput } from "./dto/create-user.input";
import { UpdateUserInput } from "./dto/update-user.input";
import { SetUserPasswordInput } from "./dto/setpassword-user.input";
import { UserEntity } from "./user.entity";
@Injectable()
export class UserService {
constructor(private readonly prisma: PrismaService) {}
async create(createUserInput: CreateUserInput) {
return await this.prisma.user.create({
const user = await this.prisma.user.create({
data: {
username: createUserInput.username,
isAdmin: createUserInput.isAdmin,
},
});
return plainToClass(UserEntity, user);
}
async update(updateUserInput: UpdateUserInput) {
return await this.prisma.user.update({
const user = await this.prisma.user.update({
where: { id: updateUserInput.id },
data: {
username: updateUserInput.username,
isAdmin: updateUserInput.isAdmin,
},
});
return plainToClass(UserEntity, user);
}
async setPassword(setUserPasswordInput: SetUserPasswordInput) {
return await this.prisma.user.update({
const exist = await this.prisma.user.findUnique({
where: { id: setUserPasswordInput.id },
});
if (!exist) throw new NotFoundException("User not found");
const user = await this.prisma.user.update({
where: { id: setUserPasswordInput.id },
data: {
password: setUserPasswordInput.password,
},
});
return plainToClass(UserEntity, user);
}
async findAll() {
return await this.prisma.user.findMany();
const users = await this.prisma.user.findMany();
return users.map((user) => plainToClass(UserEntity, user));
}
async findOne(id: string) {
return await this.prisma.user.findUnique({
const user = await this.prisma.user.findUnique({
where: { id },
});
return plainToClass(UserEntity, user);
}
async remove(id: string) {
const exist = await this.prisma.user.findUnique({ where: { id } });
if (!exist) throw new NotFoundException("User not found");
const user = await this.prisma.user.delete({
where: { id },
});
return plainToClass(UserEntity, user);
}
}

View File

@ -284,6 +284,11 @@
"@types/jsonwebtoken" "9.0.5"
jsonwebtoken "9.0.2"
"@nestjs/mapped-types@^2.0.6":
version "2.0.6"
resolved "https://registry.yarnpkg.com/@nestjs/mapped-types/-/mapped-types-2.0.6.tgz#d2d8523709fd5d872a9b9e0c38162746e2a7f44e"
integrity sha512-84ze+CPfp1OWdpRi1/lOu59hOhTz38eVzJvRKrg9ykRFwDz+XleKfMsG0gUqNZYFa6v53XYzeD+xItt8uDW7NQ==
"@nestjs/passport@^10.0.3":
version "10.0.3"
resolved "https://registry.yarnpkg.com/@nestjs/passport/-/passport-10.0.3.tgz#26ec5b2167d364e04962c115fcef80d10e185367"
@ -563,6 +568,11 @@
"@types/node" "*"
"@types/send" "*"
"@types/validator@^13.11.8":
version "13.12.2"
resolved "https://registry.yarnpkg.com/@types/validator/-/validator-13.12.2.tgz#760329e756e18a4aab82fc502b51ebdfebbe49f5"
integrity sha512-6SlHBzUW8Jhf3liqrGGXyTJSIFe4nqlJ5A5KaMZ2l/vbM3Wh3KSybots/wfWVzNLK4D1NZluDlSQIbIEPx6oyA==
"@typescript-eslint/eslint-plugin@^8.0.0":
version "8.16.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.16.0.tgz#ac56825bcdf3b392fc76a94b1315d4a162f201a6"
@ -1091,6 +1101,20 @@ chrome-trace-event@^1.0.2:
resolved "https://registry.yarnpkg.com/chrome-trace-event/-/chrome-trace-event-1.0.4.tgz#05bffd7ff928465093314708c93bdfa9bd1f0f5b"
integrity sha512-rNjApaLzuwaOTjCiT8lSDdGN1APCiqkChLMJxJPWLunPAt5fy8xgU9/jNOchV84wfIxrA0lRQB7oCT8jrn/wrQ==
class-transformer@^0.5.1:
version "0.5.1"
resolved "https://registry.yarnpkg.com/class-transformer/-/class-transformer-0.5.1.tgz#24147d5dffd2a6cea930a3250a677addf96ab336"
integrity sha512-SQa1Ws6hUbfC98vKGxZH3KFY0Y1lm5Zm0SY8XX9zbK7FJCyVEac3ATW0RIpwzW+oOfmHE5PMPufDG9hCfoEOMw==
class-validator@^0.14.1:
version "0.14.1"
resolved "https://registry.yarnpkg.com/class-validator/-/class-validator-0.14.1.tgz#ff2411ed8134e9d76acfeb14872884448be98110"
integrity sha512-2VEG9JICxIqTpoK1eMzZqaV+u/EiwEJkMGzTrZf6sU/fwsnOITVgYJ8yojSy6CaXtO9V0Cc6ZQZ8h8m4UBuLwQ==
dependencies:
"@types/validator" "^13.11.8"
libphonenumber-js "^1.10.53"
validator "^13.9.0"
cli-cursor@^3.1.0:
version "3.1.0"
resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-3.1.0.tgz#264305a7ae490d1d03bf0c9ba7c925d1753af307"
@ -2142,6 +2166,11 @@ levn@^0.4.1:
prelude-ls "^1.2.1"
type-check "~0.4.0"
libphonenumber-js@^1.10.53:
version "1.11.15"
resolved "https://registry.yarnpkg.com/libphonenumber-js/-/libphonenumber-js-1.11.15.tgz#0947ba02208cf6c44fdf3b07e097a98b3ec945f4"
integrity sha512-M7+rtYi9l5RvMmHyjyoF3BHHUpXTYdJ0PezZGHNs0GyW1lO+K7jxlXpbdIb7a56h0nqLYdjIw+E+z0ciGaJP7g==
lines-and-columns@^1.1.6:
version "1.2.4"
resolved "https://registry.yarnpkg.com/lines-and-columns/-/lines-and-columns-1.2.4.tgz#eca284f75d2965079309dc0ad9255abb2ebc1632"
@ -3214,6 +3243,11 @@ v8-compile-cache-lib@^3.0.1:
resolved "https://registry.yarnpkg.com/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz#6336e8d71965cb3d35a1bbb7868445a7c05264bf"
integrity sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==
validator@^13.9.0:
version "13.12.0"
resolved "https://registry.yarnpkg.com/validator/-/validator-13.12.0.tgz#7d78e76ba85504da3fee4fd1922b385914d4b35f"
integrity sha512-c1Q0mCiPlgdTVVVIJIrBuxNicYE+t/7oKeI9MWLj3fh/uq2Pxh/3eeWbVZ4OcGW1TUf53At0njHw5SMdA3tmMg==
vary@^1, vary@~1.1.2:
version "1.1.2"
resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc"