Add nestjs-paginate and more relationship
This commit is contained in:
parent
7be9dd3e89
commit
2d492f3263
@ -30,6 +30,7 @@
|
|||||||
"class-validator": "^0.14.0",
|
"class-validator": "^0.14.0",
|
||||||
"mysql2": "^3.2.1",
|
"mysql2": "^3.2.1",
|
||||||
"nest-winston": "^1.9.1",
|
"nest-winston": "^1.9.1",
|
||||||
|
"nestjs-paginate": "^8.1.3",
|
||||||
"reflect-metadata": "^0.1.13",
|
"reflect-metadata": "^0.1.13",
|
||||||
"rxjs": "^7.2.0",
|
"rxjs": "^7.2.0",
|
||||||
"typeorm": "^0.3.15",
|
"typeorm": "^0.3.15",
|
||||||
|
@ -14,7 +14,7 @@ import { AllExceptionsFilter } from './logger/any-exception.filter';
|
|||||||
import loggerMiddleware from './logger/logger.middleware';
|
import loggerMiddleware from './logger/logger.middleware';
|
||||||
import { TypeOrmModule } from '@nestjs/typeorm';
|
import { TypeOrmModule } from '@nestjs/typeorm';
|
||||||
import { MySqlCompanyModule } from './mysqlcompany/mysqlcompany.module';
|
import { MySqlCompanyModule } from './mysqlcompany/mysqlcompany.module';
|
||||||
import { OrmMongoCompanyModule } from './ormmongocompany/ormmongocompany.module';
|
// import { OrmMongoCompanyModule } from './ormmongocompany/ormmongocompany.module';
|
||||||
|
|
||||||
@Module({
|
@Module({
|
||||||
imports: [
|
imports: [
|
||||||
@ -27,12 +27,12 @@ import { OrmMongoCompanyModule } from './ormmongocompany/ormmongocompany.module'
|
|||||||
port: 23306,
|
port: 23306,
|
||||||
username: 'root',
|
username: 'root',
|
||||||
password: 'example',
|
password: 'example',
|
||||||
database: 'example_nodejs_nest_crud_company',
|
database: 'boilerplate_nestjs_api_crud_company',
|
||||||
// entities: ['dist/modules/**/*.mysql.entity{.ts,.js}'],
|
// entities: ['dist/modules/**/*.mysql.entity{.ts,.js}'],
|
||||||
autoLoadEntities: true,
|
autoLoadEntities: true,
|
||||||
// IMPORTANT: disable sync
|
// IMPORTANT: disable sync
|
||||||
synchronize: false,
|
// synchronize: false,
|
||||||
// synchronize: true,
|
synchronize: true,
|
||||||
logging: true,
|
logging: true,
|
||||||
}),
|
}),
|
||||||
// Router resigration for module (2nd level) will be declared inside the module
|
// Router resigration for module (2nd level) will be declared inside the module
|
||||||
@ -40,7 +40,7 @@ import { OrmMongoCompanyModule } from './ormmongocompany/ormmongocompany.module'
|
|||||||
// ]),
|
// ]),
|
||||||
UsersModule,
|
UsersModule,
|
||||||
MySqlCompanyModule,
|
MySqlCompanyModule,
|
||||||
OrmMongoCompanyModule,
|
// OrmMongoCompanyModule,
|
||||||
],
|
],
|
||||||
controllers: [AppController],
|
controllers: [AppController],
|
||||||
providers: [
|
providers: [
|
||||||
|
@ -30,7 +30,8 @@ export class AllExceptionsFilter implements ExceptionFilter {
|
|||||||
message = exception.message;
|
message = exception.message;
|
||||||
// trim the stack lines
|
// trim the stack lines
|
||||||
if (exception.stack) {
|
if (exception.stack) {
|
||||||
stack = exception.stack.split('\n').slice(1, 3).join('\n');
|
// stack = exception.stack.split('\n').slice(1, 3).join('\n');
|
||||||
|
stack = exception.stack;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -55,15 +56,19 @@ export class AllExceptionsFilter implements ExceptionFilter {
|
|||||||
this.logger.error(logMsg, stack, AllExceptionsFilter.name);
|
this.logger.error(logMsg, stack, AllExceptionsFilter.name);
|
||||||
} else {
|
} else {
|
||||||
const requstBody =
|
const requstBody =
|
||||||
Object.keys(body).length > 0 ? ` Body=${JSON.stringify(body)}` : '';
|
Object.keys(body).length > 0 ? `Body=${JSON.stringify(body)}` : '';
|
||||||
|
|
||||||
const rawHeadersStr =
|
const rawHeadersStr =
|
||||||
Object.keys(rawHeaders).length > 0
|
Object.keys(rawHeaders).length > 0
|
||||||
? ` RawHeaders=${JSON.stringify(rawHeaders)}`
|
? `RawHeaders=${JSON.stringify(rawHeaders)}`
|
||||||
: '';
|
: '';
|
||||||
|
|
||||||
logMsg = `${message}\n${method.toLocaleUpperCase()} ${url}${requstBody}${rawHeadersStr}`;
|
logMsg = `${message}\n${method.toLocaleUpperCase()} ${url}${requstBody}${rawHeadersStr}`;
|
||||||
this.logger.error(logMsg, stack, AllExceptionsFilter.name);
|
// this.logger.error(logMsg, stack, AllExceptionsFilter.name);
|
||||||
|
this.logger.error(message);
|
||||||
|
this.logger.debug(
|
||||||
|
`Request: ${method.toLocaleUpperCase()} ${url}\n${requstBody}\n${rawHeadersStr}\n${stack}`,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
response.header('Content-Type', 'application/json; charset=utf-8');
|
response.header('Content-Type', 'application/json; charset=utf-8');
|
||||||
|
10
src/mysqlcompany/contract/README.md
Normal file
10
src/mysqlcompany/contract/README.md
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
# Contract 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
|
77
src/mysqlcompany/contract/contracts.controller.ts
Normal file
77
src/mysqlcompany/contract/contracts.controller.ts
Normal file
@ -0,0 +1,77 @@
|
|||||||
|
import {
|
||||||
|
Controller,
|
||||||
|
Get,
|
||||||
|
Post,
|
||||||
|
Body,
|
||||||
|
Patch,
|
||||||
|
Param,
|
||||||
|
Delete,
|
||||||
|
NotFoundException,
|
||||||
|
HttpCode,
|
||||||
|
BadRequestException,
|
||||||
|
} from '@nestjs/common';
|
||||||
|
import { ApiResponse } from '@nestjs/swagger';
|
||||||
|
import { ContractsService } from './contracts.service';
|
||||||
|
import { Contract } from './entities/contract.entity';
|
||||||
|
import { ContractCreateDto } from './dto/contract-create.dto';
|
||||||
|
import { ContractUpdateDto } from './dto/contract-update.dto';
|
||||||
|
import { EntityNotFoundError } from 'typeorm';
|
||||||
|
import { Paginate, PaginateQuery, Paginated } from 'nestjs-paginate';
|
||||||
|
|
||||||
|
@Controller()
|
||||||
|
export class ContractsController {
|
||||||
|
constructor(private readonly contractsService: ContractsService) {}
|
||||||
|
|
||||||
|
private handleEntityNotFoundError(id: number, e: Error) {
|
||||||
|
if (e instanceof EntityNotFoundError) {
|
||||||
|
throw new NotFoundException(`Not found: id=${id}`);
|
||||||
|
} else {
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Get()
|
||||||
|
@ApiResponse({ type: [Contract] })
|
||||||
|
findAll(@Paginate() query: PaginateQuery): Promise<Paginated<Contract>> {
|
||||||
|
return this.contractsService.findAll(query);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Get(':id')
|
||||||
|
@ApiResponse({ type: Contract })
|
||||||
|
async findOne(@Param('id') id: number): Promise<Contract | null> {
|
||||||
|
// NOTE: the + operator returns the numeric representation of the object.
|
||||||
|
return this.contractsService.findOne(+id).catch((err) => {
|
||||||
|
this.handleEntityNotFoundError(id, err);
|
||||||
|
return null;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Post()
|
||||||
|
@ApiResponse({ type: [Contract] })
|
||||||
|
create(@Body() contractCreateDto: ContractCreateDto) {
|
||||||
|
return this.contractsService.create(contractCreateDto);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Patch(':id')
|
||||||
|
// Status 204 because no content will be returned
|
||||||
|
@HttpCode(204)
|
||||||
|
async update(
|
||||||
|
@Param('id') id: number,
|
||||||
|
@Body() contractUpdateDto: ContractUpdateDto,
|
||||||
|
): Promise<void> {
|
||||||
|
if (Object.keys(contractUpdateDto).length === 0) {
|
||||||
|
throw new BadRequestException('Request body is empty');
|
||||||
|
}
|
||||||
|
await this.contractsService.update(+id, contractUpdateDto).catch((err) => {
|
||||||
|
this.handleEntityNotFoundError(id, err);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Delete(':id')
|
||||||
|
@HttpCode(204)
|
||||||
|
async remove(@Param('id') id: number): Promise<void> {
|
||||||
|
await this.contractsService.remove(+id).catch((err) => {
|
||||||
|
this.handleEntityNotFoundError(id, err);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
15
src/mysqlcompany/contract/contracts.module.ts
Normal file
15
src/mysqlcompany/contract/contracts.module.ts
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
import { Logger, Module } from '@nestjs/common';
|
||||||
|
import { TypeOrmModule } from '@nestjs/typeorm';
|
||||||
|
import { ContractsService } from './contracts.service';
|
||||||
|
import { ContractsController } from './contracts.controller';
|
||||||
|
import { Contract } from './entities/contract.entity';
|
||||||
|
|
||||||
|
@Module({
|
||||||
|
imports: [TypeOrmModule.forFeature([Contract])],
|
||||||
|
controllers: [ContractsController],
|
||||||
|
providers: [Logger, ContractsService],
|
||||||
|
// 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 ContractsModule {}
|
70
src/mysqlcompany/contract/contracts.service.ts
Normal file
70
src/mysqlcompany/contract/contracts.service.ts
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
import { Injectable, Logger } from '@nestjs/common';
|
||||||
|
import { InjectRepository } from '@nestjs/typeorm';
|
||||||
|
import { EntityNotFoundError, Repository } from 'typeorm';
|
||||||
|
import { Contract } from './entities/contract.entity';
|
||||||
|
import { ContractCreateDto } from './dto/contract-create.dto';
|
||||||
|
import { ContractUpdateDto } from './dto/contract-update.dto';
|
||||||
|
import {
|
||||||
|
FilterOperator,
|
||||||
|
FilterSuffix,
|
||||||
|
PaginateQuery,
|
||||||
|
Paginated,
|
||||||
|
paginate,
|
||||||
|
} from 'nestjs-paginate';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class ContractsService {
|
||||||
|
constructor(
|
||||||
|
private readonly logger: Logger,
|
||||||
|
@InjectRepository(Contract)
|
||||||
|
private contractsRepository: Repository<Contract>,
|
||||||
|
) {}
|
||||||
|
|
||||||
|
create(contractCreateDto: ContractCreateDto): Promise<Contract> {
|
||||||
|
// Use Repository.create() will copy the values to destination object.
|
||||||
|
const contract = this.contractsRepository.create(contractCreateDto);
|
||||||
|
return this.contractsRepository.save(contract);
|
||||||
|
}
|
||||||
|
|
||||||
|
findAll(query: PaginateQuery): Promise<Paginated<Contract>> {
|
||||||
|
return paginate(query, this.contractsRepository, {
|
||||||
|
relations: { staff: true },
|
||||||
|
sortableColumns: ['id', 'title', 'details', 'staff.name'],
|
||||||
|
select: ['id', 'title', 'details', 'staff.name'],
|
||||||
|
filterableColumns: {
|
||||||
|
title: [FilterOperator.EQ, FilterOperator.ILIKE, FilterSuffix.NOT],
|
||||||
|
// staff.name: [FilterOperator.EQ, FilterOperator.ILIKE, FilterSuffix.NOT],
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
findOne(id: number): Promise<Contract> {
|
||||||
|
return this.contractsRepository.findOneOrFail({ where: { id } });
|
||||||
|
}
|
||||||
|
|
||||||
|
async update(
|
||||||
|
id: number,
|
||||||
|
contractUpdateDto: ContractUpdateDto,
|
||||||
|
): Promise<void> {
|
||||||
|
await this.isExist(id);
|
||||||
|
await this.contractsRepository.update({ id }, contractUpdateDto);
|
||||||
|
}
|
||||||
|
|
||||||
|
async remove(id: number): Promise<void> {
|
||||||
|
await this.isExist(id);
|
||||||
|
await this.contractsRepository.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.contractsRepository.countBy({ id });
|
||||||
|
if (cnt > 0) {
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
return Promise.reject(
|
||||||
|
new EntityNotFoundError(Contract, { where: { id } }),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
30
src/mysqlcompany/contract/dto/contract-create.dto.ts
Normal file
30
src/mysqlcompany/contract/dto/contract-create.dto.ts
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
import { ApiProperty } from '@nestjs/swagger';
|
||||||
|
import { IsNotEmpty, IsString, IsNumber, IsOptional } from 'class-validator';
|
||||||
|
import { Staff } from 'src/mysqlcompany/staffs/entities/staff.entity';
|
||||||
|
export class ContractCreateDto {
|
||||||
|
// NOTE: Since the id is auto-inc, so no id for the creation
|
||||||
|
// id: number;
|
||||||
|
|
||||||
|
@ApiProperty({
|
||||||
|
description: 'Contract name',
|
||||||
|
})
|
||||||
|
@IsString()
|
||||||
|
@IsNotEmpty()
|
||||||
|
title: string;
|
||||||
|
|
||||||
|
@ApiProperty({
|
||||||
|
description: 'Contract details',
|
||||||
|
})
|
||||||
|
@IsString()
|
||||||
|
@IsOptional()
|
||||||
|
details: string;
|
||||||
|
|
||||||
|
@ApiProperty({
|
||||||
|
type: Staff,
|
||||||
|
name: 'departmentId',
|
||||||
|
description: 'Department ID',
|
||||||
|
required: false,
|
||||||
|
})
|
||||||
|
@IsOptional()
|
||||||
|
staff: Staff;
|
||||||
|
}
|
4
src/mysqlcompany/contract/dto/contract-update.dto.ts
Normal file
4
src/mysqlcompany/contract/dto/contract-update.dto.ts
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
import { PartialType } from '@nestjs/swagger';
|
||||||
|
import { ContractCreateDto } from './contract-create.dto';
|
||||||
|
|
||||||
|
export class ContractUpdateDto extends PartialType(ContractCreateDto) {}
|
52
src/mysqlcompany/contract/entities/contract.entity.ts
Normal file
52
src/mysqlcompany/contract/entities/contract.entity.ts
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
import { ApiProperty } from '@nestjs/swagger';
|
||||||
|
import { Staff } from 'src/mysqlcompany/staffs/entities/staff.entity';
|
||||||
|
import {
|
||||||
|
Column,
|
||||||
|
Entity,
|
||||||
|
JoinColumn,
|
||||||
|
OneToOne,
|
||||||
|
PrimaryGeneratedColumn,
|
||||||
|
} from 'typeorm';
|
||||||
|
|
||||||
|
@Entity('contracts')
|
||||||
|
export class Contract {
|
||||||
|
@ApiProperty({
|
||||||
|
type: Number,
|
||||||
|
name: '_id',
|
||||||
|
description: 'id of contract, auto-incremented',
|
||||||
|
})
|
||||||
|
// https://github.com/ppetzold/nestjs-paginate/issues/518
|
||||||
|
// when use 'id', will cause duplicated column issue
|
||||||
|
@PrimaryGeneratedColumn('increment', { name: '_id' })
|
||||||
|
id: number;
|
||||||
|
|
||||||
|
@ApiProperty({
|
||||||
|
type: String,
|
||||||
|
description: 'Contract title',
|
||||||
|
})
|
||||||
|
@Column({
|
||||||
|
type: 'varchar',
|
||||||
|
length: 255,
|
||||||
|
})
|
||||||
|
title: string;
|
||||||
|
|
||||||
|
@ApiProperty({
|
||||||
|
type: String,
|
||||||
|
name: 'details',
|
||||||
|
description: 'Contract details',
|
||||||
|
required: false,
|
||||||
|
})
|
||||||
|
@Column({
|
||||||
|
type: 'text',
|
||||||
|
nullable: true,
|
||||||
|
})
|
||||||
|
details: string;
|
||||||
|
|
||||||
|
@ApiProperty({
|
||||||
|
name: 'staff',
|
||||||
|
description: 'The staff of this contract',
|
||||||
|
type: () => Staff,
|
||||||
|
})
|
||||||
|
@OneToOne(() => Staff)
|
||||||
|
staff: Staff;
|
||||||
|
}
|
@ -16,6 +16,7 @@ import { Department } from './entities/department.entity';
|
|||||||
import { DepartmentCreateDto } from './dto/department-create.dto';
|
import { DepartmentCreateDto } from './dto/department-create.dto';
|
||||||
import { DepartmentUpdateDto } from './dto/department-update.dto';
|
import { DepartmentUpdateDto } from './dto/department-update.dto';
|
||||||
import { EntityNotFoundError } from 'typeorm';
|
import { EntityNotFoundError } from 'typeorm';
|
||||||
|
import { Paginate, PaginateQuery, Paginated } from 'nestjs-paginate';
|
||||||
|
|
||||||
@Controller()
|
@Controller()
|
||||||
export class DepartmentController {
|
export class DepartmentController {
|
||||||
@ -31,8 +32,8 @@ export class DepartmentController {
|
|||||||
|
|
||||||
@Get()
|
@Get()
|
||||||
@ApiResponse({ type: [Department] })
|
@ApiResponse({ type: [Department] })
|
||||||
findAll(): Promise<Department[]> {
|
findAll(@Paginate() query: PaginateQuery): Promise<Paginated<Department>> {
|
||||||
return this.departmentService.findAll();
|
return this.departmentService.findAll(query);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Get(':id')
|
@Get(':id')
|
||||||
|
@ -4,27 +4,38 @@ import { EntityNotFoundError, Repository } from 'typeorm';
|
|||||||
import { Department } from './entities/department.entity';
|
import { Department } from './entities/department.entity';
|
||||||
import { DepartmentCreateDto } from './dto/department-create.dto';
|
import { DepartmentCreateDto } from './dto/department-create.dto';
|
||||||
import { DepartmentUpdateDto } from './dto/department-update.dto';
|
import { DepartmentUpdateDto } from './dto/department-update.dto';
|
||||||
|
import { PaginateQuery, Paginated, paginate } from 'nestjs-paginate';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class DepartmentService {
|
export class DepartmentService {
|
||||||
constructor(
|
constructor(
|
||||||
private readonly logger: Logger,
|
private readonly logger: Logger,
|
||||||
@InjectRepository(Department)
|
@InjectRepository(Department)
|
||||||
private departmentRepository: Repository<Department>,
|
private departmentsRepository: Repository<Department>,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
create(departmentCreateDto: DepartmentCreateDto): Promise<Department> {
|
create(departmentCreateDto: DepartmentCreateDto): Promise<Department> {
|
||||||
// Use Repository.create() will copy the values to destination object.
|
// Use Repository.create() will copy the values to destination object.
|
||||||
const department = this.departmentRepository.create(departmentCreateDto);
|
const department = this.departmentsRepository.create(departmentCreateDto);
|
||||||
return this.departmentRepository.save(department);
|
return this.departmentsRepository.save(department);
|
||||||
}
|
}
|
||||||
|
|
||||||
findAll(): Promise<Department[]> {
|
findAll(query: PaginateQuery): Promise<Paginated<Department>> {
|
||||||
return this.departmentRepository.find();
|
return paginate(query, this.departmentsRepository, {
|
||||||
|
relations: { staffs: true },
|
||||||
|
sortableColumns: ['id', 'name'],
|
||||||
|
// select: ['id', 'name', 'staffs.name'],
|
||||||
|
// searchableColumns: ['name', 'department.name'],
|
||||||
|
// filterableColumns: {
|
||||||
|
// 'home.pillows.color': [FilterOperator.EQ],
|
||||||
|
// },
|
||||||
|
});
|
||||||
|
|
||||||
|
// return this.departmentsRepository.find();
|
||||||
}
|
}
|
||||||
|
|
||||||
findOne(id: number): Promise<Department> {
|
findOne(id: number): Promise<Department> {
|
||||||
return this.departmentRepository.findOneOrFail({ where: { id } });
|
return this.departmentsRepository.findOneOrFail({ where: { id } });
|
||||||
}
|
}
|
||||||
|
|
||||||
async update(
|
async update(
|
||||||
@ -32,18 +43,18 @@ export class DepartmentService {
|
|||||||
departmentUpdateDto: DepartmentUpdateDto,
|
departmentUpdateDto: DepartmentUpdateDto,
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
await this.isExist(id);
|
await this.isExist(id);
|
||||||
await this.departmentRepository.update({ id }, departmentUpdateDto);
|
await this.departmentsRepository.update({ id }, departmentUpdateDto);
|
||||||
}
|
}
|
||||||
|
|
||||||
async remove(id: number): Promise<void> {
|
async remove(id: number): Promise<void> {
|
||||||
await this.isExist(id);
|
await this.isExist(id);
|
||||||
await this.departmentRepository.delete(id);
|
await this.departmentsRepository.delete(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Helper function: Check if the entity exist.
|
// Helper function: Check if the entity exist.
|
||||||
// If entity does not exsit, return the Promise.reject()
|
// If entity does not exsit, return the Promise.reject()
|
||||||
private async isExist(id: number): Promise<void> {
|
private async isExist(id: number): Promise<void> {
|
||||||
const cnt = await this.departmentRepository.countBy({ id });
|
const cnt = await this.departmentsRepository.countBy({ id });
|
||||||
if (cnt > 0) {
|
if (cnt > 0) {
|
||||||
return;
|
return;
|
||||||
} else {
|
} else {
|
||||||
|
@ -1,17 +1,23 @@
|
|||||||
import { ApiProperty } from '@nestjs/swagger';
|
import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger';
|
||||||
import { Staff } from 'src/mysqlcompany/staffs/entities/staff.entity';
|
import { Staff } from 'src/mysqlcompany/staffs/entities/staff.entity';
|
||||||
import { Column, Entity, OneToMany, PrimaryGeneratedColumn } from 'typeorm';
|
import {
|
||||||
|
Column,
|
||||||
|
Entity,
|
||||||
|
JoinColumn,
|
||||||
|
OneToMany,
|
||||||
|
PrimaryGeneratedColumn,
|
||||||
|
} from 'typeorm';
|
||||||
|
|
||||||
// @Entity('departments')
|
// @Entity('departments')
|
||||||
@Entity('departments')
|
@Entity('departments')
|
||||||
export class Department {
|
export class Department {
|
||||||
@ApiProperty({
|
@ApiProperty({
|
||||||
type: Number,
|
type: Number,
|
||||||
name: 'id',
|
name: '_id',
|
||||||
description: 'id of department, auto-incremented',
|
description: 'id of department, auto-incremented',
|
||||||
required: true,
|
required: true,
|
||||||
})
|
})
|
||||||
@PrimaryGeneratedColumn('increment')
|
@PrimaryGeneratedColumn('increment', { name: '_id' })
|
||||||
id: number;
|
id: number;
|
||||||
|
|
||||||
@ApiProperty({
|
@ApiProperty({
|
||||||
@ -25,6 +31,13 @@ export class Department {
|
|||||||
})
|
})
|
||||||
name: string;
|
name: string;
|
||||||
|
|
||||||
// @OneToMany((type) => Staff, (staff) => staff.departmentId)
|
@OneToMany(() => Staff, (staff) => staff.department)
|
||||||
// staffs: Staff[];
|
@ApiProperty({
|
||||||
|
name: 'staffs',
|
||||||
|
description: 'The staffs under this department',
|
||||||
|
type: [Staff],
|
||||||
|
})
|
||||||
|
@ApiPropertyOptional()
|
||||||
|
// @JoinColumn({ name: 'id', referencedColumnName: 'department_id' })
|
||||||
|
staffs: Staff[];
|
||||||
}
|
}
|
||||||
|
@ -4,6 +4,7 @@ import { MySqlCompanyController } from './mysqlcompany.controller';
|
|||||||
import { DepartmentModule } from './department/department.module';
|
import { DepartmentModule } from './department/department.module';
|
||||||
import { StaffsModule } from './staffs/staffs.module';
|
import { StaffsModule } from './staffs/staffs.module';
|
||||||
import { RouterModule } from '@nestjs/core';
|
import { RouterModule } from '@nestjs/core';
|
||||||
|
import { ContractsModule } from './contract/contracts.module';
|
||||||
|
|
||||||
@Module({
|
@Module({
|
||||||
imports: [
|
imports: [
|
||||||
@ -23,11 +24,16 @@ import { RouterModule } from '@nestjs/core';
|
|||||||
path: 'staffs',
|
path: 'staffs',
|
||||||
module: StaffsModule,
|
module: StaffsModule,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: 'contracts',
|
||||||
|
module: ContractsModule,
|
||||||
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
]),
|
]),
|
||||||
DepartmentModule,
|
DepartmentModule,
|
||||||
StaffsModule,
|
StaffsModule,
|
||||||
|
ContractsModule,
|
||||||
],
|
],
|
||||||
controllers: [MySqlCompanyController],
|
controllers: [MySqlCompanyController],
|
||||||
providers: [Logger, MySqlCompanyService],
|
providers: [Logger, MySqlCompanyService],
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import { ApiProperty } from '@nestjs/swagger';
|
import { ApiProperty } from '@nestjs/swagger';
|
||||||
|
import { Contract } from 'src/mysqlcompany/contract/entities/contract.entity';
|
||||||
import { Department } from 'src/mysqlcompany/department/entities/department.entity';
|
import { Department } from 'src/mysqlcompany/department/entities/department.entity';
|
||||||
import {
|
import {
|
||||||
Column,
|
Column,
|
||||||
@ -6,11 +7,11 @@ import {
|
|||||||
JoinColumn,
|
JoinColumn,
|
||||||
JoinTable,
|
JoinTable,
|
||||||
ManyToOne,
|
ManyToOne,
|
||||||
|
OneToOne,
|
||||||
PrimaryGeneratedColumn,
|
PrimaryGeneratedColumn,
|
||||||
} from 'typeorm';
|
} from 'typeorm';
|
||||||
|
|
||||||
@Entity('staffs')
|
@Entity('staffs')
|
||||||
@Entity()
|
|
||||||
export class Staff {
|
export class Staff {
|
||||||
@ApiProperty({
|
@ApiProperty({
|
||||||
type: Number,
|
type: Number,
|
||||||
@ -41,27 +42,35 @@ export class Staff {
|
|||||||
@Column({
|
@Column({
|
||||||
name: 'staff_code',
|
name: 'staff_code',
|
||||||
type: 'varchar',
|
type: 'varchar',
|
||||||
length: 10,
|
length: 4,
|
||||||
nullable: true,
|
nullable: true,
|
||||||
})
|
})
|
||||||
staffCode: number;
|
staffCode: number;
|
||||||
|
|
||||||
@ApiProperty({
|
@ApiProperty({
|
||||||
type: Number,
|
name: 'department',
|
||||||
name: 'departmentId',
|
description: 'The Department that the staff is undered',
|
||||||
description: 'Department ID',
|
type: () => Department,
|
||||||
required: false,
|
required: false,
|
||||||
})
|
})
|
||||||
@Column({
|
@ManyToOne(() => Department, (department) => department.staffs, {
|
||||||
// Match existing column naming convention
|
nullable: false,
|
||||||
name: 'department_id',
|
eager: true,
|
||||||
type: 'tinyint',
|
|
||||||
unsigned: true,
|
|
||||||
nullable: true,
|
|
||||||
})
|
})
|
||||||
departmentId: number;
|
// @JoinColumn({ name: 'department_id', referencedColumnName: 'id' })
|
||||||
|
|
||||||
@ManyToOne(() => Department)
|
|
||||||
@JoinColumn({ name: 'department_id' })
|
@JoinColumn({ name: 'department_id' })
|
||||||
department: Department;
|
department: Department;
|
||||||
|
|
||||||
|
@ApiProperty({
|
||||||
|
name: 'contract',
|
||||||
|
description: 'The staff contract',
|
||||||
|
type: () => Contract,
|
||||||
|
})
|
||||||
|
@OneToOne(() => Contract, (contract) => contract.staff, {
|
||||||
|
nullable: false,
|
||||||
|
eager: true,
|
||||||
|
cascade: true,
|
||||||
|
})
|
||||||
|
@JoinColumn({ name: 'contract_id' })
|
||||||
|
contract: Contract;
|
||||||
}
|
}
|
||||||
|
@ -16,6 +16,7 @@ import { Staff } from './entities/staff.entity';
|
|||||||
import { StaffCreateDto } from './dto/staff-create.dto';
|
import { StaffCreateDto } from './dto/staff-create.dto';
|
||||||
import { StaffUpdateDto } from './dto/staff-update.dto';
|
import { StaffUpdateDto } from './dto/staff-update.dto';
|
||||||
import { EntityNotFoundError } from 'typeorm';
|
import { EntityNotFoundError } from 'typeorm';
|
||||||
|
import { Paginate, PaginateQuery, Paginated } from 'nestjs-paginate';
|
||||||
|
|
||||||
@Controller()
|
@Controller()
|
||||||
export class StaffsController {
|
export class StaffsController {
|
||||||
@ -31,8 +32,8 @@ export class StaffsController {
|
|||||||
|
|
||||||
@Get()
|
@Get()
|
||||||
@ApiResponse({ type: [Staff] })
|
@ApiResponse({ type: [Staff] })
|
||||||
findAll(): Promise<Staff[]> {
|
findAll(@Paginate() query: PaginateQuery): Promise<Paginated<Staff>> {
|
||||||
return this.staffsService.findAll();
|
return this.staffsService.findAll(query);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Get(':id')
|
@Get(':id')
|
||||||
|
@ -1,9 +1,16 @@
|
|||||||
import { Injectable, Logger, NotFoundException } from '@nestjs/common';
|
import { Injectable, Logger } from '@nestjs/common';
|
||||||
import { InjectRepository } from '@nestjs/typeorm';
|
import { InjectRepository } from '@nestjs/typeorm';
|
||||||
import { EntityNotFoundError, Repository } from 'typeorm';
|
import { EntityNotFoundError, Repository } from 'typeorm';
|
||||||
import { Staff } from './entities/staff.entity';
|
import { Staff } from './entities/staff.entity';
|
||||||
import { StaffCreateDto } from './dto/staff-create.dto';
|
import { StaffCreateDto } from './dto/staff-create.dto';
|
||||||
import { StaffUpdateDto } from './dto/staff-update.dto';
|
import { StaffUpdateDto } from './dto/staff-update.dto';
|
||||||
|
import {
|
||||||
|
FilterOperator,
|
||||||
|
FilterSuffix,
|
||||||
|
PaginateQuery,
|
||||||
|
Paginated,
|
||||||
|
paginate,
|
||||||
|
} from 'nestjs-paginate';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class StaffsService {
|
export class StaffsService {
|
||||||
@ -19,22 +26,47 @@ export class StaffsService {
|
|||||||
return this.staffsRepository.save(staff);
|
return this.staffsRepository.save(staff);
|
||||||
}
|
}
|
||||||
|
|
||||||
findAll(): Promise<Staff[]> {
|
findAll(query: PaginateQuery): Promise<Paginated<Staff>> {
|
||||||
return this.staffsRepository.find({
|
return paginate(query, this.staffsRepository, {
|
||||||
relations: {
|
// loadEagerRelations: true,
|
||||||
department: true,
|
// relations: { department: true, contract: true },
|
||||||
},
|
relations: ['contract', 'department'],
|
||||||
select: {
|
// sortableColumns: ['id', 'name', 'staffCode', 'department.name'],
|
||||||
id: true,
|
sortableColumns: ['id', 'name', 'department.name'],
|
||||||
name: true,
|
select: [
|
||||||
|
'id',
|
||||||
departmentId: false,
|
'name',
|
||||||
department: {
|
'staffCode',
|
||||||
id: false,
|
'department.id',
|
||||||
name: true,
|
'department.name',
|
||||||
},
|
'contract.id',
|
||||||
},
|
'contract.title',
|
||||||
|
'contract.details',
|
||||||
|
],
|
||||||
|
// searchableColumns: ['name', 'department.name'],
|
||||||
|
// filterableColumns: {
|
||||||
|
// 'department.name': [
|
||||||
|
// FilterOperator.EQ,
|
||||||
|
// FilterOperator.ILIKE,
|
||||||
|
// FilterSuffix.NOT,
|
||||||
|
// ],
|
||||||
|
// },
|
||||||
});
|
});
|
||||||
|
// return this.staffsRepository.find({
|
||||||
|
// relations: {
|
||||||
|
// department: true,
|
||||||
|
// },
|
||||||
|
// select: {
|
||||||
|
// id: true,
|
||||||
|
// name: true,
|
||||||
|
|
||||||
|
// departmentId: false,
|
||||||
|
// department: {
|
||||||
|
// id: false,
|
||||||
|
// name: true,
|
||||||
|
// },
|
||||||
|
// },
|
||||||
|
// });
|
||||||
}
|
}
|
||||||
|
|
||||||
findOne(id: number): Promise<Staff> {
|
findOne(id: number): Promise<Staff> {
|
||||||
|
@ -37,13 +37,42 @@
|
|||||||
"colId": "8a14d631-7522-4fcd-a3b8-9a68166e5496",
|
"colId": "8a14d631-7522-4fcd-a3b8-9a68166e5496",
|
||||||
"containerId": "",
|
"containerId": "",
|
||||||
"name": "Get MySQL Comany Staffs",
|
"name": "Get MySQL Comany Staffs",
|
||||||
"url": "http://localhost:3000/mysql-company/staffs/",
|
"url": "http://localhost:3000/mysql-company/staffs/?limit=1&select=id,name,staffCode,department.name,contract.title",
|
||||||
"method": "GET",
|
"method": "GET",
|
||||||
"sortNum": 15000,
|
"sortNum": 15000,
|
||||||
"created": "2023-04-20T13:01:50.431Z",
|
"created": "2023-04-20T13:01:50.431Z",
|
||||||
"modified": "2023-04-20T13:17:36.471Z",
|
"modified": "2023-04-22T14:09:13.798Z",
|
||||||
"headers": [],
|
"headers": [],
|
||||||
"params": [],
|
"params": [
|
||||||
|
{
|
||||||
|
"name": "limit",
|
||||||
|
"value": "1",
|
||||||
|
"isPath": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "select",
|
||||||
|
"value": "id,name,staffCode,department.name,contract.title",
|
||||||
|
"isPath": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "sortBy",
|
||||||
|
"value": "department.name:DESC",
|
||||||
|
"isDisabled": true,
|
||||||
|
"isPath": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "sortBy",
|
||||||
|
"value": "name:ASC",
|
||||||
|
"isDisabled": true,
|
||||||
|
"isPath": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "filter.department.name",
|
||||||
|
"value": "$ilike:Brown",
|
||||||
|
"isDisabled": true,
|
||||||
|
"isPath": false
|
||||||
|
}
|
||||||
|
],
|
||||||
"tests": []
|
"tests": []
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -51,13 +80,61 @@
|
|||||||
"colId": "8a14d631-7522-4fcd-a3b8-9a68166e5496",
|
"colId": "8a14d631-7522-4fcd-a3b8-9a68166e5496",
|
||||||
"containerId": "",
|
"containerId": "",
|
||||||
"name": "Get MySQL Comany Departments",
|
"name": "Get MySQL Comany Departments",
|
||||||
"url": "http://localhost:3000/ormmongo-company/staffs",
|
"url": "http://localhost:3000/mysql-company/departments/",
|
||||||
"method": "GET",
|
"method": "GET",
|
||||||
"sortNum": 17500,
|
"sortNum": 17500,
|
||||||
"created": "2023-04-20T13:17:46.507Z",
|
"created": "2023-04-20T13:17:46.507Z",
|
||||||
"modified": "2023-04-20T15:00:49.647Z",
|
"modified": "2023-04-22T08:37:35.984Z",
|
||||||
"headers": [],
|
"headers": [],
|
||||||
"params": [],
|
"params": [],
|
||||||
"tests": []
|
"tests": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"_id": "e4920ab9-8552-46d1-9431-a3aaaa187cce",
|
||||||
|
"colId": "8a14d631-7522-4fcd-a3b8-9a68166e5496",
|
||||||
|
"containerId": "",
|
||||||
|
"name": "Create MySQL Comany Staff",
|
||||||
|
"url": "http://localhost:3000/mysql-company/staffs/",
|
||||||
|
"method": "POST",
|
||||||
|
"sortNum": 16250,
|
||||||
|
"created": "2023-04-22T04:22:32.924Z",
|
||||||
|
"modified": "2023-04-22T13:45:24.200Z",
|
||||||
|
"headers": [
|
||||||
|
{
|
||||||
|
"name": "Content-Type",
|
||||||
|
"value": "application/json"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"params": [],
|
||||||
|
"body": {
|
||||||
|
"type": "json",
|
||||||
|
"raw": "{\n \"name\":\"{{#name}}\",\n \"department\":{ \"id\":2 },\n \"contract\": { \"title\":\"ZYX\" }\n}",
|
||||||
|
"form": []
|
||||||
|
},
|
||||||
|
"tests": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"_id": "97997fc9-e49a-4a96-871b-6292c92c7567",
|
||||||
|
"colId": "8a14d631-7522-4fcd-a3b8-9a68166e5496",
|
||||||
|
"containerId": "",
|
||||||
|
"name": "Create MySQL Comany Department",
|
||||||
|
"url": "http://localhost:3000/mysql-company/departments/",
|
||||||
|
"method": "POST",
|
||||||
|
"sortNum": 16875,
|
||||||
|
"created": "2023-04-22T07:37:29.132Z",
|
||||||
|
"modified": "2023-04-22T07:38:11.328Z",
|
||||||
|
"headers": [
|
||||||
|
{
|
||||||
|
"name": "Content-Type",
|
||||||
|
"value": "application/json"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"params": [],
|
||||||
|
"body": {
|
||||||
|
"type": "json",
|
||||||
|
"raw": "{\n \"name\": \"Dept: {{#name}}\"\n}",
|
||||||
|
"form": []
|
||||||
|
},
|
||||||
|
"tests": []
|
||||||
}
|
}
|
||||||
]
|
]
|
@ -3863,6 +3863,13 @@ nest-winston@^1.9.1:
|
|||||||
dependencies:
|
dependencies:
|
||||||
fast-safe-stringify "^2.1.1"
|
fast-safe-stringify "^2.1.1"
|
||||||
|
|
||||||
|
nestjs-paginate@^8.1.3:
|
||||||
|
version "8.1.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/nestjs-paginate/-/nestjs-paginate-8.1.3.tgz#89c6a4fe88e3b2c885c0ca1daa115e889981d4d0"
|
||||||
|
integrity sha512-dQ7VKfssee/FkIf4aI409x1LBkFV2KdKoVDVv6HIwQmVmSeF4n8mkOVLWNh2QSFjqe9IN15ezrrFGzuWvVooTQ==
|
||||||
|
dependencies:
|
||||||
|
lodash "^4.17.21"
|
||||||
|
|
||||||
node-abort-controller@^3.0.1:
|
node-abort-controller@^3.0.1:
|
||||||
version "3.1.1"
|
version "3.1.1"
|
||||||
resolved "https://registry.npmjs.org/node-abort-controller/-/node-abort-controller-3.1.1.tgz"
|
resolved "https://registry.npmjs.org/node-abort-controller/-/node-abort-controller-3.1.1.tgz"
|
||||||
|
Loading…
Reference in New Issue
Block a user