feat(users): CRUD module users

This commit is contained in:
Matthieu Haineault 2025-07-15 13:36:04 -04:00
parent f9d77408f0
commit dc3fac76c1
14 changed files with 201 additions and 2 deletions

View File

@ -1,4 +1,5 @@
{
"singleQuote": true,
"trailingComma": "all"
"trailingComma": "all",
"endOfLine": "lf"
}

35
package-lock.json generated
View File

@ -15,6 +15,8 @@
"@nestjs/platform-express": "^11.0.1",
"@nestjs/swagger": "^11.2.0",
"@prisma/client": "^6.11.1",
"class-transformer": "^0.5.1",
"class-validator": "^0.14.2",
"reflect-metadata": "^0.2.2",
"rxjs": "^7.8.1"
},
@ -3380,6 +3382,11 @@
"@types/superagent": "^8.1.0"
}
},
"node_modules/@types/validator": {
"version": "13.15.2",
"resolved": "https://registry.npmjs.org/@types/validator/-/validator-13.15.2.tgz",
"integrity": "sha512-y7pa/oEJJ4iGYBxOpfAKn5b9+xuihvzDVnC/OSvlVnGxVg0pOqmjiMafiJ1KVNQEaPZf9HsEp5icEwGg8uIe5Q=="
},
"node_modules/@types/yargs": {
"version": "17.0.33",
"resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.33.tgz",
@ -5112,6 +5119,21 @@
"integrity": "sha512-9z8TZaGM1pfswYeXrUpzPrkx8UnWYdhJclsiYMm6x/w5+nN+8Tf/LnAgfLGQCm59qAOxU8WwHEq2vNwF6i4j+Q==",
"dev": true
},
"node_modules/class-transformer": {
"version": "0.5.1",
"resolved": "https://registry.npmjs.org/class-transformer/-/class-transformer-0.5.1.tgz",
"integrity": "sha512-SQa1Ws6hUbfC98vKGxZH3KFY0Y1lm5Zm0SY8XX9zbK7FJCyVEac3ATW0RIpwzW+oOfmHE5PMPufDG9hCfoEOMw=="
},
"node_modules/class-validator": {
"version": "0.14.2",
"resolved": "https://registry.npmjs.org/class-validator/-/class-validator-0.14.2.tgz",
"integrity": "sha512-3kMVRF2io8N8pY1IFIXlho9r8IPUUIfHe2hYVtiebvAzU2XeQFXTv+XI4WX+TnXmtwXMDcjngcpkiPM0O9PvLw==",
"dependencies": {
"@types/validator": "^13.11.8",
"libphonenumber-js": "^1.11.1",
"validator": "^13.9.0"
}
},
"node_modules/cli-cursor": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz",
@ -8041,6 +8063,11 @@
"node": ">= 0.8.0"
}
},
"node_modules/libphonenumber-js": {
"version": "1.12.10",
"resolved": "https://registry.npmjs.org/libphonenumber-js/-/libphonenumber-js-1.12.10.tgz",
"integrity": "sha512-E91vHJD61jekHHR/RF/E83T/CMoaLXT7cwYA75T4gim4FZjnM6hbJjVIGg7chqlSqRsSvQ3izGmOjHy1SQzcGQ=="
},
"node_modules/lines-and-columns": {
"version": "1.2.4",
"resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz",
@ -10711,6 +10738,14 @@
"node": ">=10.12.0"
}
},
"node_modules/validator": {
"version": "13.15.15",
"resolved": "https://registry.npmjs.org/validator/-/validator-13.15.15.tgz",
"integrity": "sha512-BgWVbCI72aIQy937xbawcs+hrVaN/CZ2UwutgaJ36hGqRrLNM+f5LUT/YPRbo8IV/ASeFzXszezV+y2+rq3l8A==",
"engines": {
"node": ">= 0.10"
}
},
"node_modules/vary": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",

View File

@ -26,6 +26,8 @@
"@nestjs/platform-express": "^11.0.1",
"@nestjs/swagger": "^11.2.0",
"@prisma/client": "^6.11.1",
"class-transformer": "^0.5.1",
"class-validator": "^0.14.2",
"reflect-metadata": "^0.2.2",
"rxjs": "^7.8.1"
},

View File

@ -4,9 +4,10 @@ import { AppService } from './app.service';
import { PrismaModule } from './prisma/prisma.module';
import { HealthModule } from './health/health.module';
import { HealthController } from './health/health.controller';
import { UsersModule } from './modules/users/users.module';
@Module({
imports: [PrismaModule, HealthModule],
imports: [PrismaModule, HealthModule, UsersModule],
controllers: [AppController, HealthController],
providers: [AppService],
})

View File

@ -1,3 +1,4 @@
import 'reflect-metadata';
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';

View File

@ -0,0 +1,18 @@
import { Test, TestingModule } from '@nestjs/testing';
import { UsersController } from './users.controller';
describe('UsersController', () => {
let controller: UsersController;
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
controllers: [UsersController],
}).compile();
controller = module.get<UsersController>(UsersController);
});
it('should be defined', () => {
expect(controller).toBeDefined();
});
});

View File

@ -0,0 +1,47 @@
import {
Controller,
Get,
Post,
Body,
Param,
Patch,
Delete,
ParseIntPipe,
} from '@nestjs/common';
import { UsersService } from '../services/users.service';
import { CreateUserDto } from '../dtos/create-user.dto';
import { UpdateUserDto } from '../dtos/update-user.dto';
import { Users } from '@prisma/client';
@Controller('users')
export class UsersController {
constructor(private readonly usersService: UsersService) {}
@Post()
create(@Body() dto: CreateUserDto): Promise<Users> {
return this.usersService.create(dto);
}
@Get()
findAll(): Promise<Users[]> {
return this.usersService.findAll();
}
@Get()
findOne(@Param('id', ParseIntPipe) user_id: number): Promise<Users> {
return this.usersService.findOne(user_id);
}
@Patch(':user_id')
update(
@Param('user_id', ParseIntPipe) user_id: number,
@Body() dto: UpdateUserDto,
): Promise<Users> {
return this.usersService.update(user_id, dto);
}
@Delete(':user_id')
remove(@Param('user_id', ParseIntPipe) user_id: number): Promise<Users> {
return this.usersService.remove(user_id);
}
}

View File

@ -0,0 +1,21 @@
import { IsString, IsNotEmpty, IsEmail, IsPhoneNumber } from 'class-validator';
export class CreateUserDto {
@IsString()
@IsNotEmpty()
first_name: string;
@IsString()
@IsNotEmpty()
last_name: string;
@IsEmail()
email: string;
@IsPhoneNumber()
@IsNotEmpty()
phone_number: number;
@IsString()
residence: string;
}

View File

@ -0,0 +1,4 @@
import { PartialType } from '@nestjs/swagger';
import { CreateUserDto } from './create-user.dto';
export class UpdateUserDto extends PartialType(CreateUserDto) {}

View File

@ -0,0 +1,18 @@
import { Test, TestingModule } from '@nestjs/testing';
import { UsersService } from './users.service';
describe('UsersService', () => {
let service: UsersService;
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
providers: [UsersService],
}).compile();
service = module.get<UsersService>(UsersService);
});
it('should be defined', () => {
expect(service).toBeDefined();
});
});

View File

@ -0,0 +1,39 @@
import { Injectable, NotFoundException } from '@nestjs/common';
import { CreateUserDto } from '../dtos/create-user.dto';
import { UpdateUserDto } from '../dtos/update-user.dto';
import { PrismaService } from 'src/prisma/prisma.service';
import { Users } from '@prisma/client';
@Injectable()
export class UsersService {
constructor(private readonly prisma: PrismaService) {}
create(dto: CreateUserDto): Promise<Users> {
return this.prisma.users.create({ data: dto });
}
findAll(): Promise<Users[]> {
return this.prisma.users.findMany();
}
async findOne(user_id: number): Promise<Users> {
const user = await this.prisma.users.findUnique({ where: { user_id } });
if (!user) {
throw new NotFoundException(`User #${user_id} not found`);
}
return user;
}
async update(user_id: number, dto: UpdateUserDto): Promise<Users> {
await this.findOne(user_id);
return this.prisma.users.update({
where: { user_id },
data: dto,
});
}
async remove(user_id: number): Promise<Users> {
await this.findOne(user_id);
return this.prisma.users.delete({ where: { user_id } });
}
}

View File

@ -0,0 +1,11 @@
import { Module } from '@nestjs/common';
import { UsersService } from './services/users.service';
import { UsersController } from './controllers/users.controller';
import { PrismaModule } from 'src/prisma/prisma.module';
@Module({
imports: [PrismaModule],
providers: [UsersService],
controllers: [UsersController],
})
export class UsersModule {}

View File

@ -1,6 +1,7 @@
{
"compilerOptions": {
"module": "commonjs",
"moduleResolution": "node",
"declaration": true,
"removeComments": true,
"emitDecoratorMetadata": true,