Add Department module for relationship

This commit is contained in:
BadBUTA 2023-04-17 23:53:14 +08:00
parent 9ea2801f8f
commit 7fc284c2c0
12 changed files with 244 additions and 9 deletions

View File

@ -14,6 +14,7 @@ import { AllExceptionsFilter } from './logger/any-exception.filter';
import loggerMiddleware from './logger/logger.middleware';
import { TypeOrmModule } from '@nestjs/typeorm';
import { StaffsModule } from './staffs/staffs.module';
import { DepartmentModule } from './department/department.module';
@Module({
imports: [
@ -30,11 +31,13 @@ import { StaffsModule } from './staffs/staffs.module';
// entities: ['dist/modules/**/*.mysql.entity{.ts,.js}'],
autoLoadEntities: true,
// IMPORTANT: disable sync
// synchronize: false,
synchronize: true,
synchronize: false,
// synchronize: true,
logging: true,
}),
UsersModule,
StaffsModule,
DepartmentModule,
],
controllers: [AppController],
providers: [

10
src/department/README.md Normal file
View File

@ -0,0 +1,10 @@
# Department Module
- This module demonstrates using:
- CRUD
- TypeORM: MySQL
- TypeORM Repository
- No QueryBuilder
- TypeOrmModule.forFeature & TypeOrmModule.forRoot{ autoLoadEntities: true }
- The Entities
- DB and Web-API entities are shared

View File

@ -0,0 +1,78 @@
import {
Controller,
Get,
Post,
Body,
Patch,
Param,
Delete,
NotFoundException,
HttpCode,
BadRequestException,
} from '@nestjs/common';
import { ApiResponse } from '@nestjs/swagger';
import { DepartmentService } from './department.service';
import { Department } from './entities/department.entity';
import { DepartmentCreateDto } from './dto/department-create.dto';
import { DepartmentUpdateDto } from './dto/department-update.dto';
import { EntityNotFoundError } from 'typeorm';
@Controller('department')
export class DepartmentController {
constructor(private readonly departmentService: DepartmentService) {}
private handleEntityNotFoundError(id: number, e: Error) {
if (e instanceof EntityNotFoundError) {
throw new NotFoundException(`Not found: id=${id}`);
} else {
throw e;
}
}
@Get()
@ApiResponse({ type: [Department] })
findAll(): Promise<Department[]> {
return this.departmentService.findAll();
}
@Get(':id')
@ApiResponse({ type: Department })
async findOne(@Param('id') id: number): Promise<Department | null> {
// NOTE: the + operator returns the numeric representation of the object.
return this.departmentService.findOne(+id).catch((err) => {
this.handleEntityNotFoundError(id, err);
return null;
});
}
@Post()
@ApiResponse({ type: [Department] })
create(@Body() departmentCreateDto: DepartmentCreateDto) {
return this.departmentService.create(departmentCreateDto);
}
@Patch(':id')
// Status 204 because no content will be returned
@HttpCode(204)
async update(
@Param('id') id: number,
@Body() departmentUpdateDto: DepartmentUpdateDto,
): Promise<void> {
if (Object.keys(departmentUpdateDto).length === 0) {
throw new BadRequestException('Request body is empty');
}
await this.departmentService
.update(+id, departmentUpdateDto)
.catch((err) => {
this.handleEntityNotFoundError(id, err);
});
}
@Delete(':id')
@HttpCode(204)
async remove(@Param('id') id: number): Promise<void> {
await this.departmentService.remove(+id).catch((err) => {
this.handleEntityNotFoundError(id, err);
});
}
}

View File

@ -0,0 +1,15 @@
import { Logger, Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { DepartmentService } from './department.service';
import { DepartmentController } from './department.controller';
import { Department } from './entities/department.entity';
@Module({
imports: [TypeOrmModule.forFeature([Department])],
controllers: [DepartmentController],
providers: [Logger, DepartmentService],
// If you want to use the repository outside of the module
// which imports TypeOrmModule.forFeature, you'll need to re-export the providers generated by it.
// exports: [TypeOrmModule],
})
export class DepartmentModule {}

View File

@ -0,0 +1,55 @@
import { Injectable, Logger, NotFoundException } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { EntityNotFoundError, Repository } from 'typeorm';
import { Department } from './entities/department.entity';
import { DepartmentCreateDto } from './dto/department-create.dto';
import { DepartmentUpdateDto } from './dto/department-update.dto';
@Injectable()
export class DepartmentService {
constructor(
private readonly logger: Logger,
@InjectRepository(Department)
private departmentRepository: Repository<Department>,
) {}
create(departmentCreateDto: DepartmentCreateDto): Promise<Department> {
// Use Repository.create() will copy the values to destination object.
const department = this.departmentRepository.create(departmentCreateDto);
return this.departmentRepository.save(department);
}
findAll(): Promise<Department[]> {
return this.departmentRepository.find();
}
findOne(id: number): Promise<Department> {
return this.departmentRepository.findOneOrFail({ where: { id } });
}
async update(
id: number,
departmentUpdateDto: DepartmentUpdateDto,
): Promise<void> {
await this.isExist(id);
await this.departmentRepository.update({ id }, departmentUpdateDto);
}
async remove(id: number): Promise<void> {
await this.isExist(id);
await this.departmentRepository.delete(id);
}
// Helper function: Check if the entity exist.
// If entity does not exsit, return the Promise.reject()
private async isExist(id: number): Promise<void> {
const cnt = await this.departmentRepository.countBy({ id });
if (cnt > 0) {
return;
} else {
return Promise.reject(
new EntityNotFoundError(Department, { where: { id } }),
);
}
}
}

View File

@ -0,0 +1,16 @@
import { ApiProperty } from '@nestjs/swagger';
import { IsNotEmpty, IsString } from 'class-validator';
export class DepartmentCreateDto {
// NOTE: Since the id is auto-inc, so no id for the creation
// id: number;
@ApiProperty({
type: String,
name: 'name',
description: 'Department name',
required: true,
})
@IsString()
@IsNotEmpty()
name: string;
}

View File

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

View File

@ -0,0 +1,30 @@
import { ApiProperty } from '@nestjs/swagger';
import { Staff } from 'src/staffs/entities/staff.entity';
import { Column, Entity, OneToMany, PrimaryGeneratedColumn } from 'typeorm';
// @Entity('departments')
@Entity()
export class Department {
@ApiProperty({
type: Number,
name: 'id',
description: 'id of department, auto-incremented',
required: true,
})
@PrimaryGeneratedColumn('increment')
id: number;
@ApiProperty({
type: String,
name: 'name',
description: 'Department name',
required: true,
})
@Column({
type: 'varchar',
})
name: string;
// @OneToMany((type) => Staff, (staff) => staff.departmentId)
// staffs: Staff[];
}

View File

@ -1,11 +1,19 @@
import { ApiProperty } from '@nestjs/swagger';
import { Column, Entity, PrimaryGeneratedColumn } from 'typeorm';
import { Department } from 'src/department/entities/department.entity';
import {
Column,
Entity,
JoinColumn,
JoinTable,
ManyToOne,
PrimaryGeneratedColumn,
} from 'typeorm';
// @Entity('staffs')
@Entity()
export class Staff {
@ApiProperty({
type: 'number',
type: Number,
name: 'id',
description: 'id of staff, auto-incremented',
required: true,
@ -53,5 +61,7 @@ export class Staff {
})
departmentId: number;
// TODO: JOIN TABLE CASE
@ManyToOne(() => Department)
@JoinColumn({ name: 'department_id' })
department: Department;
}

View File

@ -20,7 +20,21 @@ export class StaffsService {
}
findAll(): Promise<Staff[]> {
return this.staffsRepository.find();
return this.staffsRepository.find({
relations: {
department: true,
},
select: {
id: true,
name: true,
departmentId: false,
department: {
id: false,
name: true,
},
},
});
}
findOne(id: number): Promise<Staff> {

View File

@ -10,7 +10,7 @@ import {
export class CreateUserDto {
// NOTE: Since the id is autoinc, so no id for user creation
// @ApiProperty({
// type: 'number',
// type: Number,
// name: 'id',
// description: 'id of user',
// required: false,

View File

@ -11,7 +11,7 @@ import { UpdateUserDto } from '../dto/update-user.dto';
// export class User implements IUser {
export class User {
@ApiProperty({
type: 'number',
type: Number,
name: 'id',
description: 'id of user',
required: false,
@ -19,7 +19,7 @@ export class User {
id: number;
@ApiProperty({
type: 'string',
type: String,
name: 'name',
description: 'name of user',
required: false,