Compare commits
No commits in common. "main" and "nestjs-paginate" have entirely different histories.
main
...
nestjs-pag
3
.vscode/settings.json
vendored
3
.vscode/settings.json
vendored
@ -1,6 +1,5 @@
|
||||
{
|
||||
"typescript.format.enable": false,
|
||||
"javascript.format.enable": false,
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode",
|
||||
"thunder-client.saveToWorkspace": true
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
||||
}
|
72
README.md
72
README.md
@ -9,8 +9,6 @@ This project is based on the [Basic Docker Project with Git CI](https://git.pric
|
||||
1. docker-compose setup for local development
|
||||
1. Minimal API implementation (version, healthcheck)
|
||||
|
||||
---
|
||||
|
||||
## Local development using docker-compose and attach with VSCode
|
||||
|
||||
### Preparation
|
||||
@ -49,76 +47,6 @@ This project is based on the [Basic Docker Project with Git CI](https://git.pric
|
||||
1. To use the VSCode debugger, click the debug and launch the `Debug Nest Framework`
|
||||
(See `launch.json`) for details
|
||||
|
||||
---
|
||||
|
||||
## Databases
|
||||
|
||||
This project requires connecting to the database (MySQL, MongoDB). There are docker-compose files contain the database service.
|
||||
|
||||
### Standalone DB service
|
||||
|
||||
If you try this project with the local Node.js environment, you can use standalone database service which provided by the docker-compose.db.yml.
|
||||
|
||||
It comes with these service:
|
||||
|
||||
1. MySQL/MariaDB server
|
||||
1. Adminer - MySQL Web GUI Client
|
||||
1. MonogoDB server
|
||||
1. Monogo Express Web GUI Client
|
||||
|
||||
Use the following command to manage the docker services/stack:
|
||||
|
||||
```bash
|
||||
# To start the service as daemon
|
||||
docker-compose -f docker-compose.db.yml up -d
|
||||
|
||||
# To stop and down the service (and network resources)
|
||||
docker-compose -f docker-compose.db.yml down
|
||||
```
|
||||
|
||||
#### Adminer
|
||||
|
||||
Adminer is the Web GUI for MySQL/Mariadb. Once the docker-compose.db.yml is started, you may access it by:
|
||||
|
||||
`http://localhost:33306`
|
||||
|
||||
#### Mongo Express
|
||||
|
||||
Mongo Express is the Web GUI for MongoDB. Once the docker-compose.db.yml is started, you may access it by:
|
||||
|
||||
`http://localhost:32017`
|
||||
|
||||
### Troubleshooting
|
||||
|
||||
#### Port binding issue
|
||||
|
||||
In WSL2 environment, you may get the following error sometime:
|
||||
|
||||
> Error response from daemon: Ports are not available: exposing port TCP 0.0.0.0:3306 -> 0.0.0.0:0: listen tcp 0.0.0.0:3306: bind: An attempt was made to access a socket in a way forbidden by its access permissions.
|
||||
|
||||
First of all, you can check if there is stopped container that occupied the port, you can use the prune command to clean up the container:
|
||||
|
||||
```bash
|
||||
docker container prune
|
||||
```
|
||||
|
||||
__NOTE:__
|
||||
>In this project, we change the exposed ports to high-port, not low-port like 3306.
|
||||
|
||||
Sometime, Windows firewall will block the port/port-range. It can be checked and add a rule via Powershell
|
||||
|
||||
```powershell
|
||||
# Check if 3306 is excluded:
|
||||
netsh int ipv4 show excludedportrange protocol=tcp
|
||||
|
||||
# Add excluded range/port: (Need to run-as admin)
|
||||
netsh int ipv4 add excludedportrange protocol=tcp startport=3306 numberofports=1
|
||||
#
|
||||
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Swagger
|
||||
|
||||
The Nest Swagger framework is added into this project. For details, please see [Nestjs OpenAPI](https://docs.nestjs.com/openapi/introduction) documentation.
|
||||
|
@ -17,7 +17,7 @@ services:
|
||||
environment:
|
||||
MARIADB_ROOT_PASSWORD: example
|
||||
ports:
|
||||
- "23306:3306"
|
||||
- "3306:3306"
|
||||
volumes:
|
||||
- ./data/mysql:/var/lib/mysql
|
||||
# Without this, will get error when start up in WSL2
|
||||
|
@ -28,10 +28,8 @@
|
||||
"@nestjs/typeorm": "^9.0.1",
|
||||
"class-transformer": "^0.5.1",
|
||||
"class-validator": "^0.14.0",
|
||||
"mongo": "^0.1.0",
|
||||
"mysql2": "^3.2.1",
|
||||
"nest-winston": "^1.9.1",
|
||||
"nestjs-paginate": "^8.1.3",
|
||||
"reflect-metadata": "^0.1.13",
|
||||
"rxjs": "^7.2.0",
|
||||
"typeorm": "^0.3.15",
|
||||
|
@ -13,9 +13,8 @@ import { UsersModule } from './users/users.module';
|
||||
import { AllExceptionsFilter } from './logger/any-exception.filter';
|
||||
import loggerMiddleware from './logger/logger.middleware';
|
||||
import { TypeOrmModule } from '@nestjs/typeorm';
|
||||
import { MySqlCompanyModule } from './mysqlcompany/mysqlcompany.module';
|
||||
import { ZgcModule } from './zgc/zgc.module';
|
||||
// import { OrmMongoCompanyModule } from './ormmongocompany/ormmongocompany.module';
|
||||
import { StaffsModule } from './staffs/staffs.module';
|
||||
import { DepartmentModule } from './department/department.module';
|
||||
|
||||
@Module({
|
||||
imports: [
|
||||
@ -23,39 +22,22 @@ import { ZgcModule } from './zgc/zgc.module';
|
||||
ConfigModule.forRoot(),
|
||||
// For ORM-MySQL
|
||||
TypeOrmModule.forRoot({
|
||||
// ---- Mongo
|
||||
// To set auth, run mongo shell: mongosh -u root -p example
|
||||
// use boilerplate_nestjs_api_crud_company
|
||||
// db.createUser( { user: "root", pwd: "example", roles: ["dbOwner"] })
|
||||
// ---
|
||||
// type: 'mongodb',
|
||||
// host: 'localhost',
|
||||
// port: 27017,
|
||||
// database: 'boilerplate_nestjs_api_crud_company',
|
||||
// username: 'root',
|
||||
// password: 'example',
|
||||
// ---- MYSQL:
|
||||
type: 'mysql',
|
||||
host: 'localhost',
|
||||
port: 23306,
|
||||
port: 3306,
|
||||
username: 'root',
|
||||
password: 'example',
|
||||
database: 'boilerplate_nestjs_api_crud_company',
|
||||
// ------
|
||||
database: 'example_nodejs_nest_crud',
|
||||
// entities: ['dist/modules/**/*.mysql.entity{.ts,.js}'],
|
||||
autoLoadEntities: true,
|
||||
// IMPORTANT: disable sync
|
||||
// synchronize: false,
|
||||
synchronize: true,
|
||||
synchronize: false,
|
||||
// synchronize: true,
|
||||
logging: true,
|
||||
}),
|
||||
// Router resigration for module (2nd level) will be declared inside the module
|
||||
// RouterModule.register([
|
||||
// ]),
|
||||
UsersModule,
|
||||
MySqlCompanyModule,
|
||||
// OrmMongoCompanyModule,
|
||||
ZgcModule,
|
||||
StaffsModule,
|
||||
DepartmentModule,
|
||||
],
|
||||
controllers: [AppController],
|
||||
providers: [
|
||||
|
@ -17,7 +17,7 @@ import { DepartmentCreateDto } from './dto/department-create.dto';
|
||||
import { DepartmentUpdateDto } from './dto/department-update.dto';
|
||||
import { EntityNotFoundError } from 'typeorm';
|
||||
|
||||
@Controller()
|
||||
@Controller('department')
|
||||
export class DepartmentController {
|
||||
constructor(private readonly departmentService: DepartmentService) {}
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { Injectable, Logger } from '@nestjs/common';
|
||||
import { Injectable, Logger, NotFoundException } from '@nestjs/common';
|
||||
import { InjectRepository } from '@nestjs/typeorm';
|
||||
import { EntityNotFoundError, Repository } from 'typeorm';
|
||||
import { Department } from './entities/department.entity';
|
@ -1,9 +1,9 @@
|
||||
import { ApiProperty } from '@nestjs/swagger';
|
||||
import { Staff } from 'src/mysqlcompany/staffs/entities/staff.entity';
|
||||
import { Staff } from 'src/staffs/entities/staff.entity';
|
||||
import { Column, Entity, OneToMany, PrimaryGeneratedColumn } from 'typeorm';
|
||||
|
||||
// @Entity('departments')
|
||||
@Entity('departments')
|
||||
@Entity()
|
||||
export class Department {
|
||||
@ApiProperty({
|
||||
type: Number,
|
@ -30,8 +30,7 @@ export class AllExceptionsFilter implements ExceptionFilter {
|
||||
message = exception.message;
|
||||
// trim the stack lines
|
||||
if (exception.stack) {
|
||||
// stack = exception.stack.split('\n').slice(1, 3).join('\n');
|
||||
stack = exception.stack;
|
||||
stack = exception.stack.split('\n').slice(1, 3).join('\n');
|
||||
}
|
||||
}
|
||||
|
||||
@ -56,19 +55,15 @@ export class AllExceptionsFilter implements ExceptionFilter {
|
||||
this.logger.error(logMsg, stack, AllExceptionsFilter.name);
|
||||
} else {
|
||||
const requstBody =
|
||||
Object.keys(body).length > 0 ? `Body=${JSON.stringify(body)}` : '';
|
||||
Object.keys(body).length > 0 ? ` Body=${JSON.stringify(body)}` : '';
|
||||
|
||||
const rawHeadersStr =
|
||||
Object.keys(rawHeaders).length > 0
|
||||
? `RawHeaders=${JSON.stringify(rawHeaders)}`
|
||||
? ` RawHeaders=${JSON.stringify(rawHeaders)}`
|
||||
: '';
|
||||
|
||||
logMsg = `${message}\n${method.toLocaleUpperCase()} ${url}${requstBody}${rawHeadersStr}`;
|
||||
// this.logger.error(logMsg, stack, AllExceptionsFilter.name);
|
||||
this.logger.error(message);
|
||||
this.logger.debug(
|
||||
`Request: ${method.toLocaleUpperCase()} ${url}\n${requstBody}\n${rawHeadersStr}\n${stack}`,
|
||||
);
|
||||
this.logger.error(logMsg, stack, AllExceptionsFilter.name);
|
||||
}
|
||||
|
||||
response.header('Content-Type', 'application/json; charset=utf-8');
|
||||
|
@ -1,7 +0,0 @@
|
||||
# MysqlComany Module
|
||||
|
||||
MySQL-Comany module uses TypeORM + MySQL
|
||||
|
||||
This module demonstrates database relationships, including:
|
||||
- One-to-many
|
||||
- Many-to-many
|
@ -1,10 +0,0 @@
|
||||
# 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
|
@ -1,77 +0,0 @@
|
||||
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);
|
||||
});
|
||||
}
|
||||
}
|
@ -1,15 +0,0 @@
|
||||
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 {}
|
@ -1,70 +0,0 @@
|
||||
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 } }),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,30 +0,0 @@
|
||||
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;
|
||||
}
|
@ -1,4 +0,0 @@
|
||||
import { PartialType } from '@nestjs/swagger';
|
||||
import { ContractCreateDto } from './contract-create.dto';
|
||||
|
||||
export class ContractUpdateDto extends PartialType(ContractCreateDto) {}
|
@ -1,52 +0,0 @@
|
||||
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;
|
||||
}
|
@ -1,79 +0,0 @@
|
||||
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';
|
||||
import { Paginate, PaginateQuery, Paginated } from 'nestjs-paginate';
|
||||
|
||||
@Controller()
|
||||
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(@Paginate() query: PaginateQuery): Promise<Paginated<Department>> {
|
||||
return this.departmentService.findAll(query);
|
||||
}
|
||||
|
||||
@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);
|
||||
});
|
||||
}
|
||||
}
|
@ -1,66 +0,0 @@
|
||||
import { Injectable, Logger } 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';
|
||||
import { PaginateQuery, Paginated, paginate } from 'nestjs-paginate';
|
||||
|
||||
@Injectable()
|
||||
export class DepartmentService {
|
||||
constructor(
|
||||
private readonly logger: Logger,
|
||||
@InjectRepository(Department)
|
||||
private departmentsRepository: Repository<Department>,
|
||||
) {}
|
||||
|
||||
create(departmentCreateDto: DepartmentCreateDto): Promise<Department> {
|
||||
// Use Repository.create() will copy the values to destination object.
|
||||
const department = this.departmentsRepository.create(departmentCreateDto);
|
||||
return this.departmentsRepository.save(department);
|
||||
}
|
||||
|
||||
findAll(query: PaginateQuery): Promise<Paginated<Department>> {
|
||||
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> {
|
||||
return this.departmentsRepository.findOneOrFail({ where: { id } });
|
||||
}
|
||||
|
||||
async update(
|
||||
id: number,
|
||||
departmentUpdateDto: DepartmentUpdateDto,
|
||||
): Promise<void> {
|
||||
await this.isExist(id);
|
||||
await this.departmentsRepository.update({ id }, departmentUpdateDto);
|
||||
}
|
||||
|
||||
async remove(id: number): Promise<void> {
|
||||
await this.isExist(id);
|
||||
await this.departmentsRepository.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.departmentsRepository.countBy({ id });
|
||||
if (cnt > 0) {
|
||||
return;
|
||||
} else {
|
||||
return Promise.reject(
|
||||
new EntityNotFoundError(Department, { where: { id } }),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,43 +0,0 @@
|
||||
import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger';
|
||||
import { Staff } from 'src/mysqlcompany/staffs/entities/staff.entity';
|
||||
import {
|
||||
Column,
|
||||
Entity,
|
||||
JoinColumn,
|
||||
OneToMany,
|
||||
PrimaryGeneratedColumn,
|
||||
} from 'typeorm';
|
||||
|
||||
// @Entity('departments')
|
||||
@Entity('departments')
|
||||
export class Department {
|
||||
@ApiProperty({
|
||||
type: Number,
|
||||
name: '_id',
|
||||
description: 'id of department, auto-incremented',
|
||||
required: true,
|
||||
})
|
||||
@PrimaryGeneratedColumn('increment', { name: '_id' })
|
||||
id: number;
|
||||
|
||||
@ApiProperty({
|
||||
type: String,
|
||||
name: 'name',
|
||||
description: 'Department name',
|
||||
required: true,
|
||||
})
|
||||
@Column({
|
||||
type: 'varchar',
|
||||
})
|
||||
name: string;
|
||||
|
||||
@OneToMany(() => Staff, (staff) => staff.department)
|
||||
@ApiProperty({
|
||||
name: 'staffs',
|
||||
description: 'The staffs under this department',
|
||||
type: [Staff],
|
||||
})
|
||||
@ApiPropertyOptional()
|
||||
// @JoinColumn({ name: 'id', referencedColumnName: 'department_id' })
|
||||
staffs: Staff[];
|
||||
}
|
@ -1,7 +0,0 @@
|
||||
import { Controller } from '@nestjs/common';
|
||||
import { MySqlCompanyService } from './mysqlcompany.service';
|
||||
|
||||
@Controller()
|
||||
export class MySqlCompanyController {
|
||||
constructor(private readonly mySqlCompanyService: MySqlCompanyService) {}
|
||||
}
|
@ -1,41 +0,0 @@
|
||||
import { Logger, Module } from '@nestjs/common';
|
||||
import { MySqlCompanyService } from './mysqlcompany.service';
|
||||
import { MySqlCompanyController } from './mysqlcompany.controller';
|
||||
import { DepartmentModule } from './department/department.module';
|
||||
import { StaffsModule } from './staffs/staffs.module';
|
||||
import { RouterModule } from '@nestjs/core';
|
||||
import { ContractsModule } from './contract/contracts.module';
|
||||
|
||||
@Module({
|
||||
imports: [
|
||||
// The router register is commonly placed in app.module
|
||||
// However, I suggest to put registation in the 2nd level module only
|
||||
// for easy management (i.e. not for root and children level)
|
||||
RouterModule.register([
|
||||
{
|
||||
path: 'mysql-company',
|
||||
module: MySqlCompanyModule,
|
||||
children: [
|
||||
{
|
||||
path: 'departments',
|
||||
module: DepartmentModule,
|
||||
},
|
||||
{
|
||||
path: 'staffs',
|
||||
module: StaffsModule,
|
||||
},
|
||||
{
|
||||
path: 'contracts',
|
||||
module: ContractsModule,
|
||||
},
|
||||
],
|
||||
},
|
||||
]),
|
||||
DepartmentModule,
|
||||
StaffsModule,
|
||||
ContractsModule,
|
||||
],
|
||||
controllers: [MySqlCompanyController],
|
||||
providers: [Logger, MySqlCompanyService],
|
||||
})
|
||||
export class MySqlCompanyModule {}
|
@ -1,6 +0,0 @@
|
||||
import { Injectable, Logger } from '@nestjs/common';
|
||||
|
||||
@Injectable()
|
||||
export class MySqlCompanyService {
|
||||
constructor(private readonly logger: Logger) {}
|
||||
}
|
@ -1,76 +0,0 @@
|
||||
import { ApiProperty } from '@nestjs/swagger';
|
||||
import { Contract } from 'src/mysqlcompany/contract/entities/contract.entity';
|
||||
import { Department } from 'src/mysqlcompany/department/entities/department.entity';
|
||||
import {
|
||||
Column,
|
||||
Entity,
|
||||
JoinColumn,
|
||||
JoinTable,
|
||||
ManyToOne,
|
||||
OneToOne,
|
||||
PrimaryGeneratedColumn,
|
||||
} from 'typeorm';
|
||||
|
||||
@Entity('staffs')
|
||||
export class Staff {
|
||||
@ApiProperty({
|
||||
type: Number,
|
||||
name: 'id',
|
||||
description: 'id of staff, auto-incremented',
|
||||
required: true,
|
||||
})
|
||||
@PrimaryGeneratedColumn('increment')
|
||||
id: number;
|
||||
|
||||
@ApiProperty({
|
||||
type: String,
|
||||
name: 'name',
|
||||
description: 'Staff name',
|
||||
required: true,
|
||||
})
|
||||
@Column({
|
||||
type: 'varchar',
|
||||
})
|
||||
name: string;
|
||||
|
||||
@ApiProperty({
|
||||
type: Number,
|
||||
name: 'staffCode',
|
||||
description: 'Staff code/ID',
|
||||
required: false,
|
||||
})
|
||||
@Column({
|
||||
name: 'staff_code',
|
||||
type: 'varchar',
|
||||
length: 4,
|
||||
nullable: true,
|
||||
})
|
||||
staffCode: number;
|
||||
|
||||
@ApiProperty({
|
||||
name: 'department',
|
||||
description: 'The Department that the staff is undered',
|
||||
type: () => Department,
|
||||
required: false,
|
||||
})
|
||||
@ManyToOne(() => Department, (department) => department.staffs, {
|
||||
nullable: false,
|
||||
eager: true,
|
||||
})
|
||||
// @JoinColumn({ name: 'department_id', referencedColumnName: 'id' })
|
||||
@JoinColumn({ name: 'department_id' })
|
||||
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;
|
||||
}
|
@ -1,77 +0,0 @@
|
||||
import {
|
||||
Controller,
|
||||
Get,
|
||||
Post,
|
||||
Body,
|
||||
Patch,
|
||||
Param,
|
||||
Delete,
|
||||
NotFoundException,
|
||||
HttpCode,
|
||||
BadRequestException,
|
||||
} from '@nestjs/common';
|
||||
import { ApiResponse } from '@nestjs/swagger';
|
||||
import { StaffsService } from './staffs.service';
|
||||
import { Staff } from './entities/staff.entity';
|
||||
import { StaffCreateDto } from './dto/staff-create.dto';
|
||||
import { StaffUpdateDto } from './dto/staff-update.dto';
|
||||
import { EntityNotFoundError } from 'typeorm';
|
||||
import { Paginate, PaginateQuery, Paginated } from 'nestjs-paginate';
|
||||
|
||||
@Controller()
|
||||
export class StaffsController {
|
||||
constructor(private readonly staffsService: StaffsService) {}
|
||||
|
||||
private handleEntityNotFoundError(id: number, e: Error) {
|
||||
if (e instanceof EntityNotFoundError) {
|
||||
throw new NotFoundException(`Not found: id=${id}`);
|
||||
} else {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
@Get()
|
||||
@ApiResponse({ type: [Staff] })
|
||||
findAll(@Paginate() query: PaginateQuery): Promise<Paginated<Staff>> {
|
||||
return this.staffsService.findAll(query);
|
||||
}
|
||||
|
||||
@Get(':id')
|
||||
@ApiResponse({ type: Staff })
|
||||
async findOne(@Param('id') id: number): Promise<Staff | null> {
|
||||
// NOTE: the + operator returns the numeric representation of the object.
|
||||
return this.staffsService.findOne(+id).catch((err) => {
|
||||
this.handleEntityNotFoundError(id, err);
|
||||
return null;
|
||||
});
|
||||
}
|
||||
|
||||
@Post()
|
||||
@ApiResponse({ type: [Staff] })
|
||||
create(@Body() staffCreateDto: StaffCreateDto) {
|
||||
return this.staffsService.create(staffCreateDto);
|
||||
}
|
||||
|
||||
@Patch(':id')
|
||||
// Status 204 because no content will be returned
|
||||
@HttpCode(204)
|
||||
async update(
|
||||
@Param('id') id: number,
|
||||
@Body() staffUpdateDto: StaffUpdateDto,
|
||||
): Promise<void> {
|
||||
if (Object.keys(staffUpdateDto).length === 0) {
|
||||
throw new BadRequestException('Request body is empty');
|
||||
}
|
||||
await this.staffsService.update(+id, staffUpdateDto).catch((err) => {
|
||||
this.handleEntityNotFoundError(id, err);
|
||||
});
|
||||
}
|
||||
|
||||
@Delete(':id')
|
||||
@HttpCode(204)
|
||||
async remove(@Param('id') id: number): Promise<void> {
|
||||
await this.staffsService.remove(+id).catch((err) => {
|
||||
this.handleEntityNotFoundError(id, err);
|
||||
});
|
||||
}
|
||||
}
|
@ -1,96 +0,0 @@
|
||||
import { Injectable, Logger } from '@nestjs/common';
|
||||
import { InjectRepository } from '@nestjs/typeorm';
|
||||
import { EntityNotFoundError, Repository } from 'typeorm';
|
||||
import { Staff } from './entities/staff.entity';
|
||||
import { StaffCreateDto } from './dto/staff-create.dto';
|
||||
import { StaffUpdateDto } from './dto/staff-update.dto';
|
||||
import {
|
||||
FilterOperator,
|
||||
FilterSuffix,
|
||||
PaginateQuery,
|
||||
Paginated,
|
||||
paginate,
|
||||
} from 'nestjs-paginate';
|
||||
|
||||
@Injectable()
|
||||
export class StaffsService {
|
||||
constructor(
|
||||
private readonly logger: Logger,
|
||||
@InjectRepository(Staff)
|
||||
private staffsRepository: Repository<Staff>,
|
||||
) {}
|
||||
|
||||
create(staffCreateDto: StaffCreateDto): Promise<Staff> {
|
||||
// Use Repository.create() will copy the values to destination object.
|
||||
const staff = this.staffsRepository.create(staffCreateDto);
|
||||
return this.staffsRepository.save(staff);
|
||||
}
|
||||
|
||||
findAll(query: PaginateQuery): Promise<Paginated<Staff>> {
|
||||
return paginate(query, this.staffsRepository, {
|
||||
// loadEagerRelations: true,
|
||||
// relations: { department: true, contract: true },
|
||||
relations: ['contract', 'department'],
|
||||
// sortableColumns: ['id', 'name', 'staffCode', 'department.name'],
|
||||
sortableColumns: ['id', 'name', 'department.name'],
|
||||
select: [
|
||||
'id',
|
||||
'name',
|
||||
'staffCode',
|
||||
'department.id',
|
||||
'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> {
|
||||
return this.staffsRepository.findOneOrFail({ where: { id } });
|
||||
}
|
||||
|
||||
async update(id: number, staffUpdateDto: StaffUpdateDto): Promise<void> {
|
||||
await this.isExist(id);
|
||||
await this.staffsRepository.update({ id }, staffUpdateDto);
|
||||
}
|
||||
|
||||
async remove(id: number): Promise<void> {
|
||||
await this.isExist(id);
|
||||
await this.staffsRepository.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.staffsRepository.countBy({ id });
|
||||
if (cnt > 0) {
|
||||
return;
|
||||
} else {
|
||||
return Promise.reject(new EntityNotFoundError(Staff, { where: { id } }));
|
||||
}
|
||||
}
|
||||
}
|
@ -1,7 +0,0 @@
|
||||
# MysqlComany Module
|
||||
|
||||
MySQL-Comany module uses TypeORM + MySQL
|
||||
|
||||
This module demonstrates database relationships, including:
|
||||
- One-to-many
|
||||
- Many-to-many
|
@ -1,10 +0,0 @@
|
||||
# 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
|
@ -1,15 +0,0 @@
|
||||
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 {}
|
@ -1,16 +0,0 @@
|
||||
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;
|
||||
}
|
@ -1,4 +0,0 @@
|
||||
import { PartialType } from '@nestjs/swagger';
|
||||
import { DepartmentCreateDto } from './department-create.dto';
|
||||
|
||||
export class DepartmentUpdateDto extends PartialType(DepartmentCreateDto) {}
|
@ -1,9 +0,0 @@
|
||||
import { Controller } from '@nestjs/common';
|
||||
import { OrmMongoCompanyService } from './ormmongocompany.service';
|
||||
|
||||
@Controller()
|
||||
export class OrmMongoCompanyController {
|
||||
constructor(
|
||||
private readonly ormMongoCompanyService: OrmMongoCompanyService,
|
||||
) {}
|
||||
}
|
@ -1,35 +0,0 @@
|
||||
import { Logger, Module } from '@nestjs/common';
|
||||
import { OrmMongoCompanyService } from './ormmongocompany.service';
|
||||
import { OrmMongoCompanyController } from './ormmongocompany.controller';
|
||||
import { DepartmentModule } from './department/department.module';
|
||||
import { StaffsModule } from './staffs/staffs.module';
|
||||
import { RouterModule } from '@nestjs/core';
|
||||
|
||||
@Module({
|
||||
imports: [
|
||||
// The router register is commonly placed in app.module
|
||||
// However, I suggest to put registation in the 2nd level module only
|
||||
// for easy management (i.e. not for root and children level)
|
||||
RouterModule.register([
|
||||
{
|
||||
path: 'ormmongo-company',
|
||||
module: OrmMongoCompanyModule,
|
||||
children: [
|
||||
{
|
||||
path: 'departments',
|
||||
module: DepartmentModule,
|
||||
},
|
||||
{
|
||||
path: 'staffs',
|
||||
module: StaffsModule,
|
||||
},
|
||||
],
|
||||
},
|
||||
]),
|
||||
DepartmentModule,
|
||||
StaffsModule,
|
||||
],
|
||||
controllers: [OrmMongoCompanyController],
|
||||
providers: [Logger, OrmMongoCompanyService],
|
||||
})
|
||||
export class OrmMongoCompanyModule {}
|
@ -1,6 +0,0 @@
|
||||
import { Injectable, Logger } from '@nestjs/common';
|
||||
|
||||
@Injectable()
|
||||
export class OrmMongoCompanyService {
|
||||
constructor(private readonly logger: Logger) {}
|
||||
}
|
@ -1,10 +0,0 @@
|
||||
# Staffs 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
|
@ -1,36 +0,0 @@
|
||||
import { ApiProperty } from '@nestjs/swagger';
|
||||
import { IsNotEmpty, IsString, IsNumber, IsOptional } from 'class-validator';
|
||||
export class StaffCreateDto {
|
||||
// NOTE: Since the id is auto-inc, so no id for the creation
|
||||
// id: number;
|
||||
|
||||
@ApiProperty({
|
||||
type: String,
|
||||
name: 'name',
|
||||
description: 'Staff name',
|
||||
required: true,
|
||||
})
|
||||
@IsString()
|
||||
@IsNotEmpty()
|
||||
name: string;
|
||||
|
||||
@ApiProperty({
|
||||
type: Number,
|
||||
name: 'staffCode',
|
||||
description: 'Staff code/ID',
|
||||
required: false,
|
||||
})
|
||||
@IsNumber()
|
||||
@IsOptional()
|
||||
staffCode: number;
|
||||
|
||||
@ApiProperty({
|
||||
type: Number,
|
||||
name: 'departmentId',
|
||||
description: 'Department ID',
|
||||
required: false,
|
||||
})
|
||||
@IsNumber()
|
||||
@IsOptional()
|
||||
departmentId: number;
|
||||
}
|
@ -1,4 +0,0 @@
|
||||
import { PartialType } from '@nestjs/swagger';
|
||||
import { StaffCreateDto } from './staff-create.dto';
|
||||
|
||||
export class StaffUpdateDto extends PartialType(StaffCreateDto) {}
|
@ -1,15 +0,0 @@
|
||||
import { Logger, Module } from '@nestjs/common';
|
||||
import { TypeOrmModule } from '@nestjs/typeorm';
|
||||
import { StaffsService } from './staffs.service';
|
||||
import { StaffsController } from './staffs.controller';
|
||||
import { Staff } from './entities/staff.entity';
|
||||
|
||||
@Module({
|
||||
imports: [TypeOrmModule.forFeature([Staff])],
|
||||
controllers: [StaffsController],
|
||||
providers: [Logger, StaffsService],
|
||||
// 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 StaffsModule {}
|
@ -1,5 +1,5 @@
|
||||
import { ApiProperty } from '@nestjs/swagger';
|
||||
import { Department } from 'src/mysqlcompany/department/entities/department.entity';
|
||||
import { Department } from 'src/department/entities/department.entity';
|
||||
import {
|
||||
Column,
|
||||
Entity,
|
||||
@ -9,7 +9,7 @@ import {
|
||||
PrimaryGeneratedColumn,
|
||||
} from 'typeorm';
|
||||
|
||||
@Entity('staffs')
|
||||
// @Entity('staffs')
|
||||
@Entity()
|
||||
export class Staff {
|
||||
@ApiProperty({
|
@ -17,7 +17,7 @@ import { StaffCreateDto } from './dto/staff-create.dto';
|
||||
import { StaffUpdateDto } from './dto/staff-update.dto';
|
||||
import { EntityNotFoundError } from 'typeorm';
|
||||
|
||||
@Controller()
|
||||
@Controller('staffs')
|
||||
export class StaffsController {
|
||||
constructor(private readonly staffsService: StaffsService) {}
|
||||
|
@ -1,7 +0,0 @@
|
||||
# User Module
|
||||
|
||||
User module use Repository class to handle the 'database' representation.
|
||||
|
||||
The Respository class uses a map object (in-memory) as a database. It is not persistence storage.
|
||||
|
||||
I am trying to apply the Repository pattern concept here and abstract the data layer out of the business logic.
|
@ -1,25 +0,0 @@
|
||||
import { ApiProperty } from '@nestjs/swagger';
|
||||
import { IsNumber, IsOptional } from 'class-validator';
|
||||
export class CategoryCreateDto {
|
||||
// NOTE: Since the id is auto-inc, so no id for the creation
|
||||
// id: number;
|
||||
// ----------------------------------------
|
||||
@ApiProperty({
|
||||
type: String,
|
||||
name: 'name',
|
||||
description: 'Group name',
|
||||
required: true,
|
||||
})
|
||||
name: string;
|
||||
|
||||
// ----------------------------------------
|
||||
@ApiProperty({
|
||||
type: Number,
|
||||
name: 'displayOrder',
|
||||
description: 'Display order',
|
||||
required: false,
|
||||
})
|
||||
@IsNumber()
|
||||
@IsOptional()
|
||||
displayOrder: number | 0;
|
||||
}
|
@ -1,4 +0,0 @@
|
||||
import { PartialType } from '@nestjs/swagger';
|
||||
import { CategoryCreateDto } from './category-create.dto';
|
||||
|
||||
export class CategoryUpdateDto extends PartialType(CategoryCreateDto) {}
|
@ -1,25 +0,0 @@
|
||||
import { ApiProperty } from '@nestjs/swagger';
|
||||
import { IsNumber, IsOptional } from 'class-validator';
|
||||
export class GroupCreateDto {
|
||||
// NOTE: Since the id is auto-inc, so no id for the creation
|
||||
// id: number;
|
||||
// ----------------------------------------
|
||||
@ApiProperty({
|
||||
type: String,
|
||||
name: 'name',
|
||||
description: 'Group name',
|
||||
required: true,
|
||||
})
|
||||
name: string;
|
||||
|
||||
// ----------------------------------------
|
||||
@ApiProperty({
|
||||
type: Number,
|
||||
name: 'displayOrder',
|
||||
description: 'Display order',
|
||||
required: false,
|
||||
})
|
||||
@IsNumber()
|
||||
@IsOptional()
|
||||
displayOrder: number | 0;
|
||||
}
|
@ -1,4 +0,0 @@
|
||||
import { PartialType } from '@nestjs/swagger';
|
||||
import { GroupCreateDto } from './group-create.dto';
|
||||
|
||||
export class GroupUpdateDto extends PartialType(GroupCreateDto) {}
|
@ -1,25 +0,0 @@
|
||||
import { ApiProperty } from '@nestjs/swagger';
|
||||
import { IsNumber, IsOptional } from 'class-validator';
|
||||
export class ZoneCreateDto {
|
||||
// NOTE: Since the id is auto-inc, so no id for the creation
|
||||
// id: number;
|
||||
// ----------------------------------------
|
||||
@ApiProperty({
|
||||
type: String,
|
||||
name: 'name',
|
||||
description: 'Zone name',
|
||||
required: true,
|
||||
})
|
||||
name: string;
|
||||
|
||||
// ----------------------------------------
|
||||
@ApiProperty({
|
||||
type: Number,
|
||||
name: 'displayOrder',
|
||||
description: 'Display order',
|
||||
required: false,
|
||||
})
|
||||
@IsNumber()
|
||||
@IsOptional()
|
||||
displayOrder: number | 0;
|
||||
}
|
@ -1,4 +0,0 @@
|
||||
import { PartialType } from '@nestjs/swagger';
|
||||
import { ZoneCreateDto } from './zone-create.dto';
|
||||
|
||||
export class ZoneUpdateDto extends PartialType(ZoneCreateDto) {}
|
@ -1,62 +0,0 @@
|
||||
import { ApiProperty } from '@nestjs/swagger';
|
||||
import {
|
||||
Column,
|
||||
Entity,
|
||||
JoinColumn,
|
||||
ManyToOne,
|
||||
PrimaryGeneratedColumn,
|
||||
} from 'typeorm';
|
||||
import { Group } from './group.entity';
|
||||
|
||||
@Entity('categories')
|
||||
export class Category {
|
||||
// ----------------------------------------
|
||||
@ApiProperty({
|
||||
type: Number,
|
||||
name: 'id',
|
||||
description: 'id of category, auto-incremented',
|
||||
required: true,
|
||||
})
|
||||
@PrimaryGeneratedColumn('increment')
|
||||
id: number;
|
||||
|
||||
// ----------------------------------------
|
||||
@ApiProperty({
|
||||
type: String,
|
||||
name: 'name',
|
||||
description: 'Category name',
|
||||
required: true,
|
||||
})
|
||||
@Column({
|
||||
type: 'varchar',
|
||||
})
|
||||
name: string;
|
||||
|
||||
// ----------------------------------------
|
||||
@ApiProperty({
|
||||
type: Number,
|
||||
name: 'displayOrder',
|
||||
description: 'Display order',
|
||||
required: false,
|
||||
})
|
||||
@Column({
|
||||
type: 'tinyint',
|
||||
default: 0,
|
||||
})
|
||||
displayOrder: number;
|
||||
|
||||
// ----------------------------------------
|
||||
@ApiProperty({
|
||||
name: 'group',
|
||||
description: 'Parent node: group',
|
||||
type: () => Group,
|
||||
})
|
||||
// @ApiPropertyOptional()
|
||||
@ManyToOne(() => Group, (group) => group.categories, {
|
||||
nullable: false,
|
||||
eager: true,
|
||||
})
|
||||
// @JoinColumn({ name: 'group_id', referencedColumnName: 'id' })
|
||||
@JoinColumn({ name: 'xref_group_id' })
|
||||
group: Group;
|
||||
}
|
@ -1,73 +0,0 @@
|
||||
import { ApiProperty } from '@nestjs/swagger';
|
||||
import {
|
||||
Column,
|
||||
Entity,
|
||||
JoinColumn,
|
||||
ManyToOne,
|
||||
OneToMany,
|
||||
PrimaryGeneratedColumn,
|
||||
} from 'typeorm';
|
||||
import { Zone } from './zone.entity';
|
||||
import { Category } from './category.entity';
|
||||
|
||||
@Entity('groups')
|
||||
export class Group {
|
||||
// ----------------------------------------
|
||||
@ApiProperty({
|
||||
type: Number,
|
||||
name: 'id',
|
||||
description: 'id of group, auto-incremented',
|
||||
required: true,
|
||||
})
|
||||
@PrimaryGeneratedColumn('increment')
|
||||
id: number;
|
||||
|
||||
// ----------------------------------------
|
||||
@ApiProperty({
|
||||
type: String,
|
||||
name: 'name',
|
||||
description: 'Group name',
|
||||
required: true,
|
||||
})
|
||||
@Column({
|
||||
type: 'varchar',
|
||||
})
|
||||
name: string;
|
||||
|
||||
// ----------------------------------------
|
||||
@ApiProperty({
|
||||
type: Number,
|
||||
name: 'displayOrder',
|
||||
description: 'Display order',
|
||||
required: false,
|
||||
})
|
||||
@Column({
|
||||
type: 'tinyint',
|
||||
default: 0,
|
||||
})
|
||||
displayOrder: number;
|
||||
|
||||
// ----------------------------------------
|
||||
@ApiProperty({
|
||||
name: 'zone',
|
||||
description: 'Parent node: zone',
|
||||
type: () => Zone,
|
||||
})
|
||||
// @ApiPropertyOptional()
|
||||
@ManyToOne(() => Zone, (zone) => zone.groups, {
|
||||
nullable: false,
|
||||
eager: true,
|
||||
})
|
||||
@JoinColumn({ name: 'xref_zone_id' })
|
||||
zone: Zone;
|
||||
|
||||
// ----------------------------------------
|
||||
@ApiProperty({
|
||||
name: 'categories',
|
||||
description: 'Children: categories',
|
||||
type: () => [Category],
|
||||
})
|
||||
// @ApiPropertyOptional()
|
||||
@OneToMany(() => Category, (category) => category.group)
|
||||
categories: Category[];
|
||||
}
|
@ -1,51 +0,0 @@
|
||||
import { ApiProperty } from '@nestjs/swagger';
|
||||
import { Column, Entity, OneToMany, PrimaryGeneratedColumn } from 'typeorm';
|
||||
import { Group } from './group.entity';
|
||||
|
||||
@Entity('zones')
|
||||
export class Zone {
|
||||
// ----------------------------------------
|
||||
@ApiProperty({
|
||||
type: Number,
|
||||
name: 'id',
|
||||
description: 'id of zone, auto-incremented',
|
||||
required: true,
|
||||
})
|
||||
@PrimaryGeneratedColumn('increment')
|
||||
id: number;
|
||||
|
||||
// ----------------------------------------
|
||||
@ApiProperty({
|
||||
type: String,
|
||||
name: 'name',
|
||||
description: 'Zone name',
|
||||
required: true,
|
||||
})
|
||||
@Column({
|
||||
type: 'varchar',
|
||||
})
|
||||
name: string;
|
||||
|
||||
// ----------------------------------------
|
||||
@ApiProperty({
|
||||
type: Number,
|
||||
name: 'displayOrder',
|
||||
description: 'Display order',
|
||||
required: false,
|
||||
})
|
||||
@Column({
|
||||
type: 'tinyint',
|
||||
default: 0,
|
||||
})
|
||||
displayOrder: number;
|
||||
|
||||
// ----------------------------------------
|
||||
@ApiProperty({
|
||||
name: 'groups',
|
||||
description: 'Children: groups',
|
||||
type: () => [Group],
|
||||
})
|
||||
// @ApiPropertyOptional()
|
||||
@OneToMany(() => Group, (group) => group.zone)
|
||||
groups: Group[];
|
||||
}
|
@ -1,181 +0,0 @@
|
||||
import {
|
||||
Controller,
|
||||
Get,
|
||||
Post,
|
||||
Body,
|
||||
Patch,
|
||||
Param,
|
||||
Delete,
|
||||
NotFoundException,
|
||||
HttpCode,
|
||||
BadRequestException,
|
||||
} from '@nestjs/common';
|
||||
import { ApiResponse } from '@nestjs/swagger';
|
||||
import { EntityNotFoundError } from 'typeorm';
|
||||
import { Paginate, PaginateQuery, Paginated } from 'nestjs-paginate';
|
||||
import { ZgcService } from './zgc.service';
|
||||
import { ZoneCreateDto } from './dto/zone-create.dto';
|
||||
import { ZoneUpdateDto } from './dto/zone-update.dto';
|
||||
import { Zone } from './entities/zone.entity';
|
||||
import { GroupCreateDto } from './dto/group-create.dto';
|
||||
import { GroupUpdateDto } from './dto/group-update.dto';
|
||||
import { Group } from './entities/group.entity';
|
||||
import { CategoryCreateDto } from './dto/category-create.dto';
|
||||
import { CategoryUpdateDto } from './dto/category-update.dto';
|
||||
import { Category } from './entities/category.entity';
|
||||
|
||||
@Controller('zgc')
|
||||
export class ZgcController {
|
||||
constructor(private readonly service: ZgcService) {}
|
||||
|
||||
private handleEntityNotFoundError(id: number, e: Error) {
|
||||
if (e instanceof EntityNotFoundError) {
|
||||
throw new NotFoundException(`Not found: id=${id}`);
|
||||
} else {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
// -------------------- Zone --------------------
|
||||
// --
|
||||
@Get('/zones')
|
||||
@ApiResponse({ type: [Zone] })
|
||||
findZones(@Paginate() query: PaginateQuery): Promise<Paginated<Zone>> {
|
||||
return this.service.findZones(query);
|
||||
}
|
||||
|
||||
@Get('/zones/:id')
|
||||
@ApiResponse({ type: Zone })
|
||||
async findZone(@Param('id') id: number): Promise<Zone | null> {
|
||||
// NOTE: the + operator returns the numeric representation of the object.
|
||||
return this.service.findZone(+id).catch((err) => {
|
||||
this.handleEntityNotFoundError(id, err);
|
||||
return null;
|
||||
});
|
||||
}
|
||||
|
||||
@Post('/zones')
|
||||
@ApiResponse({ type: [Zone] })
|
||||
createZone(@Body() zoneCreateDto: ZoneCreateDto) {
|
||||
return this.service.createZone(zoneCreateDto);
|
||||
}
|
||||
|
||||
@Patch('/zones/:id')
|
||||
// Status 204 because no content will be returned
|
||||
@HttpCode(204)
|
||||
async updateZone(
|
||||
@Param('id') id: number,
|
||||
@Body() zoneUpdateDto: ZoneUpdateDto,
|
||||
): Promise<void> {
|
||||
if (Object.keys(zoneUpdateDto).length === 0) {
|
||||
throw new BadRequestException('Request body is empty');
|
||||
}
|
||||
await this.service.updateZone(+id, zoneUpdateDto).catch((err) => {
|
||||
this.handleEntityNotFoundError(id, err);
|
||||
});
|
||||
}
|
||||
|
||||
@Delete('/zones/:id')
|
||||
@HttpCode(204)
|
||||
async deleteZone(@Param('id') id: number): Promise<void> {
|
||||
await this.service.deleteZone(+id).catch((err) => {
|
||||
this.handleEntityNotFoundError(id, err);
|
||||
});
|
||||
}
|
||||
|
||||
// -------------------- Group --------------------
|
||||
// --
|
||||
@Get('/groups')
|
||||
@ApiResponse({ type: [Group] })
|
||||
findGroups(@Paginate() query: PaginateQuery): Promise<Paginated<Group>> {
|
||||
return this.service.findGroups(query);
|
||||
}
|
||||
|
||||
@Get('/groups/:id')
|
||||
@ApiResponse({ type: Group })
|
||||
async findGroup(@Param('id') id: number): Promise<Group | null> {
|
||||
// NOTE: the + operator returns the numeric representation of the object.
|
||||
return this.service.findGroup(+id).catch((err) => {
|
||||
this.handleEntityNotFoundError(id, err);
|
||||
return null;
|
||||
});
|
||||
}
|
||||
|
||||
@Post('/groups')
|
||||
@ApiResponse({ type: [Group] })
|
||||
createGroup(@Body() zoneCreateDto: GroupCreateDto) {
|
||||
return this.service.createGroup(zoneCreateDto);
|
||||
}
|
||||
|
||||
@Patch('/groups/:id')
|
||||
// Status 204 because no content will be returned
|
||||
@HttpCode(204)
|
||||
async updateGroup(
|
||||
@Param('id') id: number,
|
||||
@Body() zoneUpdateDto: GroupUpdateDto,
|
||||
): Promise<void> {
|
||||
if (Object.keys(zoneUpdateDto).length === 0) {
|
||||
throw new BadRequestException('Request body is empty');
|
||||
}
|
||||
await this.service.updateGroup(+id, zoneUpdateDto).catch((err) => {
|
||||
this.handleEntityNotFoundError(id, err);
|
||||
});
|
||||
}
|
||||
|
||||
@Delete('/groups/:id')
|
||||
@HttpCode(204)
|
||||
async deleteGroup(@Param('id') id: number): Promise<void> {
|
||||
await this.service.deleteGroup(+id).catch((err) => {
|
||||
this.handleEntityNotFoundError(id, err);
|
||||
});
|
||||
}
|
||||
|
||||
// -------------------- Category --------------------
|
||||
// --
|
||||
@Get('/categories')
|
||||
@ApiResponse({ type: [Category] })
|
||||
findCategories(
|
||||
@Paginate() query: PaginateQuery,
|
||||
): Promise<Paginated<Category>> {
|
||||
return this.service.findCategories(query);
|
||||
}
|
||||
|
||||
@Get('/categories/:id')
|
||||
@ApiResponse({ type: Category })
|
||||
async findCategory(@Param('id') id: number): Promise<Category | null> {
|
||||
// NOTE: the + operator returns the numeric representation of the object.
|
||||
return this.service.findCategory(+id).catch((err) => {
|
||||
this.handleEntityNotFoundError(id, err);
|
||||
return null;
|
||||
});
|
||||
}
|
||||
|
||||
@Post('/categories')
|
||||
@ApiResponse({ type: [Category] })
|
||||
createCategory(@Body() categoryCreateDto: CategoryCreateDto) {
|
||||
return this.service.createCategory(categoryCreateDto);
|
||||
}
|
||||
|
||||
@Patch('/categories/:id')
|
||||
// Status 204 because no content will be returned
|
||||
@HttpCode(204)
|
||||
async updateCategory(
|
||||
@Param('id') id: number,
|
||||
@Body() categoryUpdateDto: CategoryUpdateDto,
|
||||
): Promise<void> {
|
||||
if (Object.keys(categoryUpdateDto).length === 0) {
|
||||
throw new BadRequestException('Request body is empty');
|
||||
}
|
||||
await this.service.updateCategory(+id, categoryUpdateDto).catch((err) => {
|
||||
this.handleEntityNotFoundError(id, err);
|
||||
});
|
||||
}
|
||||
|
||||
@Delete('/categories/:id')
|
||||
@HttpCode(204)
|
||||
async deleteCategory(@Param('id') id: number): Promise<void> {
|
||||
await this.service.deleteCategory(+id).catch((err) => {
|
||||
this.handleEntityNotFoundError(id, err);
|
||||
});
|
||||
}
|
||||
}
|
@ -1,17 +0,0 @@
|
||||
import { Logger, Module } from '@nestjs/common';
|
||||
import { TypeOrmModule } from '@nestjs/typeorm';
|
||||
import { ZgcService } from './zgc.service';
|
||||
import { ZgcController } from './zgc.controller';
|
||||
import { Zone } from './entities/zone.entity';
|
||||
import { Group } from './entities/group.entity';
|
||||
import { Category } from './entities/category.entity';
|
||||
|
||||
@Module({
|
||||
imports: [TypeOrmModule.forFeature([Zone, Group, Category])],
|
||||
controllers: [ZgcController],
|
||||
providers: [Logger, ZgcService],
|
||||
// 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 ZgcModule {}
|
@ -1,221 +0,0 @@
|
||||
import { Injectable, Logger } from '@nestjs/common';
|
||||
import { InjectRepository } from '@nestjs/typeorm';
|
||||
import { EntityNotFoundError, Repository } from 'typeorm';
|
||||
import { PaginateQuery, Paginated, paginate } from 'nestjs-paginate';
|
||||
import { ZoneCreateDto } from './dto/zone-create.dto';
|
||||
import { ZoneUpdateDto } from './dto/zone-update.dto';
|
||||
import { Zone } from './entities/zone.entity';
|
||||
import { GroupCreateDto } from './dto/group-create.dto';
|
||||
import { GroupUpdateDto } from './dto/group-update.dto';
|
||||
import { Group } from './entities/group.entity';
|
||||
import { Category } from './entities/category.entity';
|
||||
import { CategoryCreateDto } from './dto/category-create.dto';
|
||||
import { CategoryUpdateDto } from './dto/category-update.dto';
|
||||
|
||||
@Injectable()
|
||||
export class ZgcService {
|
||||
constructor(
|
||||
private readonly logger: Logger,
|
||||
@InjectRepository(Zone)
|
||||
private zonesRepository: Repository<Zone>,
|
||||
@InjectRepository(Group)
|
||||
private groupsRepository: Repository<Group>,
|
||||
@InjectRepository(Category)
|
||||
private categoriesRepository: Repository<Category>,
|
||||
) {}
|
||||
|
||||
// -------------------- Zone --------------------
|
||||
// --
|
||||
createZone(zoneCreateDto: ZoneCreateDto): Promise<Zone> {
|
||||
// Use Repository.create() will copy the values to destination object.
|
||||
const zone = this.zonesRepository.create(zoneCreateDto);
|
||||
return this.zonesRepository.save(zone);
|
||||
}
|
||||
|
||||
findZones(query: PaginateQuery): Promise<Paginated<Zone>> {
|
||||
return paginate(query, this.zonesRepository, {
|
||||
// loadEagerRelations: true,
|
||||
// relations: { groups: true },
|
||||
relations: ['groups'],
|
||||
sortableColumns: ['id', 'name', 'displayOrder'],
|
||||
defaultSortBy: [
|
||||
['displayOrder', 'ASC'],
|
||||
['id', 'ASC'],
|
||||
['groups.displayOrder', 'ASC'],
|
||||
['groups.id', 'ASC'],
|
||||
],
|
||||
select: [
|
||||
'id',
|
||||
'name',
|
||||
'displayOrder',
|
||||
'groups.id',
|
||||
'groups.name',
|
||||
'groups.displayOrder',
|
||||
// 'groups.categories.id',
|
||||
// 'groups.categories.name',
|
||||
// 'groups.categories.displayOrder',
|
||||
],
|
||||
// searchableColumns: ['id', 'name'],
|
||||
// filterableColumns: {
|
||||
// 'id': [
|
||||
// FilterOperator.EQ,
|
||||
// FilterSuffix.NOT,
|
||||
// ],
|
||||
// 'name': [
|
||||
// FilterOperator.EQ,
|
||||
// FilterOperator.ILIKE,
|
||||
// FilterSuffix.NOT,
|
||||
// ],
|
||||
// },
|
||||
});
|
||||
}
|
||||
|
||||
findZone(id: number): Promise<Zone> {
|
||||
return this.zonesRepository.findOneOrFail({ where: { id } });
|
||||
}
|
||||
|
||||
async updateZone(id: number, zoneUpdateDto: ZoneUpdateDto): Promise<void> {
|
||||
await this.isZoneExist(id);
|
||||
await this.zonesRepository.update({ id }, zoneUpdateDto);
|
||||
}
|
||||
|
||||
async deleteZone(id: number): Promise<void> {
|
||||
await this.isZoneExist(id);
|
||||
await this.zonesRepository.delete(id);
|
||||
}
|
||||
|
||||
// Helper function: Check if the entity exist.
|
||||
// If entity does not exsit, return the Promise.reject()
|
||||
private async isZoneExist(id: number): Promise<void> {
|
||||
const cnt = await this.zonesRepository.countBy({ id });
|
||||
if (cnt > 0) {
|
||||
return;
|
||||
} else {
|
||||
return Promise.reject(new EntityNotFoundError(Zone, { where: { id } }));
|
||||
}
|
||||
}
|
||||
|
||||
// -------------------- Group --------------------
|
||||
// --
|
||||
createGroup(groupCreateDto: GroupCreateDto): Promise<Group> {
|
||||
// Use Repository.create() will copy the values to destination object.
|
||||
const group = this.groupsRepository.create(groupCreateDto);
|
||||
return this.groupsRepository.save(group);
|
||||
}
|
||||
|
||||
findGroups(query: PaginateQuery): Promise<Paginated<Group>> {
|
||||
return paginate(query, this.groupsRepository, {
|
||||
// loadEagerRelations: true,
|
||||
// relations: { groups: true },
|
||||
relations: ['zone', 'categories'],
|
||||
sortableColumns: ['id', 'name', 'displayOrder'],
|
||||
select: [
|
||||
'id',
|
||||
'name',
|
||||
'displayOrder',
|
||||
'zone.id',
|
||||
'zone.name',
|
||||
'categories.id',
|
||||
'categories.name',
|
||||
'categories.displayOrder',
|
||||
],
|
||||
// searchableColumns: ['id', 'name'],
|
||||
// filterableColumns: {
|
||||
// 'id': [
|
||||
// FilterOperator.EQ,
|
||||
// FilterSuffix.NOT,
|
||||
// ],
|
||||
// 'name': [
|
||||
// FilterOperator.EQ,
|
||||
// FilterOperator.ILIKE,
|
||||
// FilterSuffix.NOT,
|
||||
// ],
|
||||
// },
|
||||
});
|
||||
}
|
||||
|
||||
findGroup(id: number): Promise<Group> {
|
||||
return this.groupsRepository.findOneOrFail({ where: { id } });
|
||||
}
|
||||
|
||||
async updateGroup(id: number, groupUpdateDto: GroupUpdateDto): Promise<void> {
|
||||
await this.isGroupExist(id);
|
||||
await this.groupsRepository.update({ id }, groupUpdateDto);
|
||||
}
|
||||
|
||||
async deleteGroup(id: number): Promise<void> {
|
||||
await this.isGroupExist(id);
|
||||
await this.groupsRepository.delete(id);
|
||||
}
|
||||
|
||||
// Helper function: Check if the entity exist.
|
||||
// If entity does not exsit, return the Promise.reject()
|
||||
private async isGroupExist(id: number): Promise<void> {
|
||||
const cnt = await this.groupsRepository.countBy({ id });
|
||||
if (cnt > 0) {
|
||||
return;
|
||||
} else {
|
||||
return Promise.reject(new EntityNotFoundError(Group, { where: { id } }));
|
||||
}
|
||||
}
|
||||
|
||||
// -------------------- Category --------------------
|
||||
// --
|
||||
createCategory(cateogyCreateDto: CategoryCreateDto): Promise<Category> {
|
||||
// Use Repository.create() will copy the values to destination object.
|
||||
const cateogy = this.categoriesRepository.create(cateogyCreateDto);
|
||||
return this.categoriesRepository.save(cateogy);
|
||||
}
|
||||
|
||||
findCategories(query: PaginateQuery): Promise<Paginated<Category>> {
|
||||
return paginate(query, this.categoriesRepository, {
|
||||
// loadEagerRelations: true,
|
||||
// relations: { groups: true },
|
||||
relations: ['group'],
|
||||
sortableColumns: ['id', 'name', 'displayOrder'],
|
||||
select: ['id', 'name', 'displayOrder', 'group.id', 'group.name'],
|
||||
searchableColumns: ['id', 'name'],
|
||||
// filterableColumns: {
|
||||
// 'id': [
|
||||
// FilterOperator.EQ,
|
||||
// FilterSuffix.NOT,
|
||||
// ],
|
||||
// 'name': [
|
||||
// FilterOperator.EQ,
|
||||
// FilterOperator.ILIKE,
|
||||
// FilterSuffix.NOT,
|
||||
// ],
|
||||
// },
|
||||
});
|
||||
}
|
||||
|
||||
findCategory(id: number): Promise<Category> {
|
||||
return this.categoriesRepository.findOneOrFail({ where: { id } });
|
||||
}
|
||||
|
||||
async updateCategory(
|
||||
id: number,
|
||||
categoryUpdateDto: CategoryUpdateDto,
|
||||
): Promise<void> {
|
||||
await this.isCategoryExist(id);
|
||||
await this.categoriesRepository.update({ id }, categoryUpdateDto);
|
||||
}
|
||||
|
||||
async deleteCategory(id: number): Promise<void> {
|
||||
await this.isCategoryExist(id);
|
||||
await this.categoriesRepository.delete(id);
|
||||
}
|
||||
|
||||
// Helper function: Check if the entity exist.
|
||||
// If entity does not exsit, return the Promise.reject()
|
||||
private async isCategoryExist(id: number): Promise<void> {
|
||||
const cnt = await this.categoriesRepository.countBy({ id });
|
||||
if (cnt > 0) {
|
||||
return;
|
||||
} else {
|
||||
return Promise.reject(
|
||||
new EntityNotFoundError(Category, { where: { id } }),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
@ -1 +0,0 @@
|
||||
[]
|
@ -1,38 +0,0 @@
|
||||
[
|
||||
{
|
||||
"_id": "8a14d631-7522-4fcd-a3b8-9a68166e5496",
|
||||
"colName": "Local NestJS App",
|
||||
"created": "2023-04-20T12:42:42.547Z",
|
||||
"sortNum": 10000,
|
||||
"folders": []
|
||||
},
|
||||
{
|
||||
"_id": "1d5b8a54-f8ba-480a-96bd-8faf7d7cb4ba",
|
||||
"colName": "Local NestJS App - ZGC",
|
||||
"created": "2023-04-24T16:41:46.695Z",
|
||||
"sortNum": 20000,
|
||||
"folders": [
|
||||
{
|
||||
"_id": "10f85be8-b759-4364-8949-5338d329703a",
|
||||
"name": "Zone",
|
||||
"containerId": "",
|
||||
"created": "2023-04-24T16:41:57.395Z",
|
||||
"sortNum": 10000
|
||||
},
|
||||
{
|
||||
"_id": "b4b39ee3-0da2-4ec0-b2d4-ace00dd8280d",
|
||||
"name": "Group",
|
||||
"containerId": "",
|
||||
"created": "2023-04-24T17:04:34.098Z",
|
||||
"sortNum": 20000
|
||||
},
|
||||
{
|
||||
"_id": "a16fc112-ae03-47e0-8eb2-eaf97bcbe36f",
|
||||
"name": "Categories",
|
||||
"containerId": "",
|
||||
"created": "2023-04-24T17:04:47.131Z",
|
||||
"sortNum": 30000
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
@ -1,13 +0,0 @@
|
||||
[
|
||||
{
|
||||
"_id": "af943170-fabe-4f13-9725-3bd52e585aa5",
|
||||
"name": "(Local Env)",
|
||||
"default": false,
|
||||
"global": true,
|
||||
"local": true,
|
||||
"sortNum": -1,
|
||||
"created": "2023-04-20T12:43:21.880Z",
|
||||
"modified": "2023-04-20T12:43:21.880Z",
|
||||
"data": []
|
||||
}
|
||||
]
|
@ -1,260 +0,0 @@
|
||||
[
|
||||
{
|
||||
"_id": "901d7d10-2ba0-481d-9230-182df284741e",
|
||||
"colId": "8a14d631-7522-4fcd-a3b8-9a68166e5496",
|
||||
"containerId": "",
|
||||
"name": "Get Users",
|
||||
"url": "http://localhost:3000/users",
|
||||
"method": "GET",
|
||||
"sortNum": 10000,
|
||||
"created": "2023-04-20T12:42:42.550Z",
|
||||
"modified": "2023-04-20T13:17:30.029Z",
|
||||
"headers": [],
|
||||
"params": [],
|
||||
"tests": []
|
||||
},
|
||||
{
|
||||
"_id": "db11f3a6-418b-43ae-9a1d-f2f667b5d0e6",
|
||||
"colId": "8a14d631-7522-4fcd-a3b8-9a68166e5496",
|
||||
"containerId": "",
|
||||
"name": "Add User",
|
||||
"url": "http://localhost:3000/users/",
|
||||
"method": "POST",
|
||||
"sortNum": 12500,
|
||||
"created": "2023-04-20T12:47:21.164Z",
|
||||
"modified": "2023-04-20T13:18:12.522Z",
|
||||
"headers": [],
|
||||
"params": [],
|
||||
"body": {
|
||||
"type": "json",
|
||||
"raw": "{\n \"name\": \"{{#name}} - {{#number, 100, 999}}\"\n}",
|
||||
"form": []
|
||||
},
|
||||
"tests": []
|
||||
},
|
||||
{
|
||||
"_id": "858c3b4a-186d-4338-9dab-4eb00a450808",
|
||||
"colId": "8a14d631-7522-4fcd-a3b8-9a68166e5496",
|
||||
"containerId": "",
|
||||
"name": "Get MySQL Comany Staffs",
|
||||
"url": "http://localhost:3000/mysql-company/staffs/?limit=1&select=id,name,staffCode,department.name,contract.title",
|
||||
"method": "GET",
|
||||
"sortNum": 15000,
|
||||
"created": "2023-04-20T13:01:50.431Z",
|
||||
"modified": "2023-04-22T14:09:13.798Z",
|
||||
"headers": [],
|
||||
"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": []
|
||||
},
|
||||
{
|
||||
"_id": "017f39a8-137d-4ab9-bb7e-f0dbef38c899",
|
||||
"colId": "8a14d631-7522-4fcd-a3b8-9a68166e5496",
|
||||
"containerId": "",
|
||||
"name": "Get MySQL Comany Departments",
|
||||
"url": "http://localhost:3000/mysql-company/departments/",
|
||||
"method": "GET",
|
||||
"sortNum": 17500,
|
||||
"created": "2023-04-20T13:17:46.507Z",
|
||||
"modified": "2023-04-22T08:37:35.984Z",
|
||||
"headers": [],
|
||||
"params": [],
|
||||
"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": []
|
||||
},
|
||||
{
|
||||
"_id": "ee150c28-07bd-4792-b6a8-6551e3e6169b",
|
||||
"colId": "1d5b8a54-f8ba-480a-96bd-8faf7d7cb4ba",
|
||||
"containerId": "10f85be8-b759-4364-8949-5338d329703a",
|
||||
"name": "Get Zones",
|
||||
"url": "http://localhost:3000/zgc/zones/?limit=3",
|
||||
"method": "GET",
|
||||
"sortNum": 10000,
|
||||
"created": "2023-04-24T16:42:10.562Z",
|
||||
"modified": "2023-04-24T17:24:36.920Z",
|
||||
"headers": [],
|
||||
"params": [
|
||||
{
|
||||
"name": "limit",
|
||||
"value": "3",
|
||||
"isPath": false
|
||||
}
|
||||
],
|
||||
"tests": []
|
||||
},
|
||||
{
|
||||
"_id": "16119da3-4863-4650-86f5-7fac55269cea",
|
||||
"colId": "1d5b8a54-f8ba-480a-96bd-8faf7d7cb4ba",
|
||||
"containerId": "b4b39ee3-0da2-4ec0-b2d4-ace00dd8280d",
|
||||
"name": "Get Groups",
|
||||
"url": "http://localhost:3000/zgc/groups/",
|
||||
"method": "GET",
|
||||
"sortNum": 10000,
|
||||
"created": "2023-04-24T16:44:34.640Z",
|
||||
"modified": "2023-04-24T17:04:37.342Z",
|
||||
"headers": [],
|
||||
"params": [],
|
||||
"tests": []
|
||||
},
|
||||
{
|
||||
"_id": "27716e9b-e0a7-47d4-9fb6-ede300275c5c",
|
||||
"colId": "1d5b8a54-f8ba-480a-96bd-8faf7d7cb4ba",
|
||||
"containerId": "a16fc112-ae03-47e0-8eb2-eaf97bcbe36f",
|
||||
"name": "Get Categories",
|
||||
"url": "http://localhost:3000/zgc/categories/",
|
||||
"method": "GET",
|
||||
"sortNum": 10000,
|
||||
"created": "2023-04-24T16:47:23.727Z",
|
||||
"modified": "2023-04-24T17:04:50.733Z",
|
||||
"headers": [],
|
||||
"params": [],
|
||||
"tests": []
|
||||
},
|
||||
{
|
||||
"_id": "d9148936-364a-4e2d-aeb4-ab26adb10d63",
|
||||
"colId": "1d5b8a54-f8ba-480a-96bd-8faf7d7cb4ba",
|
||||
"containerId": "a16fc112-ae03-47e0-8eb2-eaf97bcbe36f",
|
||||
"name": "Add Category",
|
||||
"url": "http://localhost:3000/zgc/categories/",
|
||||
"method": "POST",
|
||||
"sortNum": 20000,
|
||||
"created": "2023-04-24T17:04:59.718Z",
|
||||
"modified": "2023-04-24T17:18:30.236Z",
|
||||
"headers": [
|
||||
{
|
||||
"name": "Content-Type",
|
||||
"value": "application/json"
|
||||
}
|
||||
],
|
||||
"params": [],
|
||||
"body": {
|
||||
"type": "json",
|
||||
"raw": "{\n \"name\": \"Cat {{#name}} - {{#number, 100, 999}}\",\n \"group\": {\n \"id\": 1\n }\n}",
|
||||
"form": []
|
||||
},
|
||||
"tests": []
|
||||
},
|
||||
{
|
||||
"_id": "55d7d81e-4779-4d78-aa71-dfa120c9d741",
|
||||
"colId": "1d5b8a54-f8ba-480a-96bd-8faf7d7cb4ba",
|
||||
"containerId": "10f85be8-b759-4364-8949-5338d329703a",
|
||||
"name": "Add Zone",
|
||||
"url": "http://localhost:3000/zgc/zones/",
|
||||
"method": "POST",
|
||||
"sortNum": 20000,
|
||||
"created": "2023-04-24T17:12:09.466Z",
|
||||
"modified": "2023-04-24T17:15:32.575Z",
|
||||
"headers": [
|
||||
{
|
||||
"name": "Content-Type",
|
||||
"value": "application/json"
|
||||
}
|
||||
],
|
||||
"params": [],
|
||||
"body": {
|
||||
"type": "json",
|
||||
"raw": "{\n \"name\": \"Zone {{#name}} - {{#number, 100, 999}}\"\n}",
|
||||
"form": []
|
||||
},
|
||||
"tests": []
|
||||
},
|
||||
{
|
||||
"_id": "594e6cf8-5eb6-49cb-b446-4848c36461be",
|
||||
"colId": "1d5b8a54-f8ba-480a-96bd-8faf7d7cb4ba",
|
||||
"containerId": "b4b39ee3-0da2-4ec0-b2d4-ace00dd8280d",
|
||||
"name": "Add Group",
|
||||
"url": "http://localhost:3000/zgc/groups/",
|
||||
"method": "POST",
|
||||
"sortNum": 20000,
|
||||
"created": "2023-04-24T17:15:59.691Z",
|
||||
"modified": "2023-04-24T17:24:11.934Z",
|
||||
"headers": [
|
||||
{
|
||||
"name": "Content-Type",
|
||||
"value": "application/json"
|
||||
}
|
||||
],
|
||||
"params": [],
|
||||
"body": {
|
||||
"type": "json",
|
||||
"raw": "{\n \"name\": \"Group {{#name}} - {{#number, 100, 999}}\",\n \"zone\": {\n \"id\": 7\n }\n}",
|
||||
"form": []
|
||||
},
|
||||
"tests": []
|
||||
}
|
||||
]
|
@ -1,10 +1,4 @@
|
||||
{
|
||||
"extends": "./tsconfig.json",
|
||||
"exclude": [
|
||||
"node_modules",
|
||||
"test",
|
||||
"dist",
|
||||
"**/*spec.ts",
|
||||
"data"
|
||||
]
|
||||
}
|
||||
"exclude": ["node_modules", "test", "dist", "**/*spec.ts", "data"]
|
||||
}
|
||||
|
@ -18,4 +18,4 @@
|
||||
"forceConsistentCasingInFileNames": false,
|
||||
"noFallthroughCasesInSwitch": false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
110
yarn.lock
110
yarn.lock
@ -1091,19 +1091,6 @@
|
||||
resolved "https://registry.npmjs.org/@types/validator/-/validator-13.7.14.tgz"
|
||||
integrity sha512-J6OAed6rhN6zyqL9Of6ZMamhlsOEU/poBVvbHr/dKOYKTeuYYMlDkMv+b6UUV0o2i0tw73cgyv/97WTWaUl0/g==
|
||||
|
||||
"@types/webidl-conversions@*":
|
||||
version "7.0.0"
|
||||
resolved "https://registry.yarnpkg.com/@types/webidl-conversions/-/webidl-conversions-7.0.0.tgz#2b8e60e33906459219aa587e9d1a612ae994cfe7"
|
||||
integrity sha512-xTE1E+YF4aWPJJeUzaZI5DRntlkY3+BCVJi0axFptnjGmAoWxkyREIh/XMrfxVLejwQxMCfDXdICo0VLxThrog==
|
||||
|
||||
"@types/whatwg-url@^8.2.1":
|
||||
version "8.2.2"
|
||||
resolved "https://registry.yarnpkg.com/@types/whatwg-url/-/whatwg-url-8.2.2.tgz#749d5b3873e845897ada99be4448041d4cc39e63"
|
||||
integrity sha512-FtQu10RWgn3D9U4aazdwIE2yzphmTJREDqNdODHrbrZmmMqI0vMheC/6NE/J1Yveaj8H+ela+YwWTjq5PGmuhA==
|
||||
dependencies:
|
||||
"@types/node" "*"
|
||||
"@types/webidl-conversions" "*"
|
||||
|
||||
"@types/yargs-parser@*":
|
||||
version "21.0.0"
|
||||
resolved "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.0.tgz"
|
||||
@ -1668,11 +1655,6 @@ bser@2.1.1:
|
||||
dependencies:
|
||||
node-int64 "^0.4.0"
|
||||
|
||||
bson@^5.2.0:
|
||||
version "5.2.0"
|
||||
resolved "https://registry.yarnpkg.com/bson/-/bson-5.2.0.tgz#c81d35dd30e2798203e5422a639780ea98dd25ba"
|
||||
integrity sha512-HevkSpDbpUfsrHWmWiAsNavANKYIErV2ePXllp1bwq5CDreAaFVj6RVlZpJnxK4WWDCJ/5jMUpaY6G526q3Hjg==
|
||||
|
||||
buffer-from@^1.0.0:
|
||||
version "1.1.2"
|
||||
resolved "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz"
|
||||
@ -2950,11 +2932,6 @@ interpret@^1.0.0:
|
||||
resolved "https://registry.npmjs.org/interpret/-/interpret-1.4.0.tgz"
|
||||
integrity sha512-agE4QfB2Lkp9uICn7BAqoscw4SZP9kTE2hxiFI3jBPmXJfdqiahTbUuKGsMoN2GtqL9AxhYioAcVvgsb1HvRbA==
|
||||
|
||||
ip@^2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/ip/-/ip-2.0.0.tgz#4cf4ab182fee2314c75ede1276f8c80b479936da"
|
||||
integrity sha512-WKa+XuLG1A1R0UWhl2+1XQSi+fZWMsYKffMZTTYsiZaUD8k2yDAj5atimTUD2TZkyCkNEeYE5NhFZmupOGtjYQ==
|
||||
|
||||
ipaddr.js@1.9.1:
|
||||
version "1.9.1"
|
||||
resolved "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz"
|
||||
@ -3698,11 +3675,6 @@ memfs@^3.4.1:
|
||||
dependencies:
|
||||
fs-monkey "^1.0.3"
|
||||
|
||||
memory-pager@^1.0.2:
|
||||
version "1.5.0"
|
||||
resolved "https://registry.yarnpkg.com/memory-pager/-/memory-pager-1.5.0.tgz#d8751655d22d384682741c972f2c3d6dfa3e66b5"
|
||||
integrity sha512-ZS4Bp4r/Zoeq6+NLJpP+0Zzm0pR8whtGPf1XExKLJBAczGMnSi3It14OiNCStjQjM6NU1okjQGSxgEZN8eBYKg==
|
||||
|
||||
merge-descriptors@1.0.1:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz"
|
||||
@ -3801,32 +3773,6 @@ mkdirp@^2.1.3:
|
||||
resolved "https://registry.npmjs.org/mkdirp/-/mkdirp-2.1.6.tgz"
|
||||
integrity sha512-+hEnITedc8LAtIP9u3HJDFIdcLV2vXP33sqLLIzkv1Db1zO/1OxbvYf0Y1OC/S/Qo5dxHXepofhmxL02PsKe+A==
|
||||
|
||||
mongo@^0.1.0:
|
||||
version "0.1.0"
|
||||
resolved "https://registry.yarnpkg.com/mongo/-/mongo-0.1.0.tgz#c8af0f8df98d4894b772b37342987c3becf3718c"
|
||||
integrity sha512-2MPq+GCNKhah0V/g/HIQI/S1h6Ycd87KPuXAITkeXWT6wncvABFxOaXdzCKlRvLSQRUkDimllrRrhoHUTD8usg==
|
||||
dependencies:
|
||||
mongodb "*"
|
||||
|
||||
mongodb-connection-string-url@^2.6.0:
|
||||
version "2.6.0"
|
||||
resolved "https://registry.yarnpkg.com/mongodb-connection-string-url/-/mongodb-connection-string-url-2.6.0.tgz#57901bf352372abdde812c81be47b75c6b2ec5cf"
|
||||
integrity sha512-WvTZlI9ab0QYtTYnuMLgobULWhokRjtC7db9LtcVfJ+Hsnyr5eo6ZtNAt3Ly24XZScGMelOcGtm7lSn0332tPQ==
|
||||
dependencies:
|
||||
"@types/whatwg-url" "^8.2.1"
|
||||
whatwg-url "^11.0.0"
|
||||
|
||||
mongodb@*:
|
||||
version "5.3.0"
|
||||
resolved "https://registry.yarnpkg.com/mongodb/-/mongodb-5.3.0.tgz#9bef3ff35511a66fb7d9aafb7b06112787138db1"
|
||||
integrity sha512-Wy/sbahguL8c3TXQWXmuBabiLD+iVmz+tOgQf+FwkCjhUIorqbAxRbbz00g4ZoN4sXIPwpAlTANMaGRjGGTikQ==
|
||||
dependencies:
|
||||
bson "^5.2.0"
|
||||
mongodb-connection-string-url "^2.6.0"
|
||||
socks "^2.7.1"
|
||||
optionalDependencies:
|
||||
saslprep "^1.0.3"
|
||||
|
||||
ms@2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz"
|
||||
@ -3917,13 +3863,6 @@ nest-winston@^1.9.1:
|
||||
dependencies:
|
||||
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:
|
||||
version "3.1.1"
|
||||
resolved "https://registry.npmjs.org/node-abort-controller/-/node-abort-controller-3.1.1.tgz"
|
||||
@ -4240,7 +4179,7 @@ pump@^3.0.0:
|
||||
end-of-stream "^1.1.0"
|
||||
once "^1.3.1"
|
||||
|
||||
punycode@^2.1.0, punycode@^2.1.1:
|
||||
punycode@^2.1.0:
|
||||
version "2.3.0"
|
||||
resolved "https://registry.npmjs.org/punycode/-/punycode-2.3.0.tgz"
|
||||
integrity sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA==
|
||||
@ -4461,13 +4400,6 @@ safe-stable-stringify@^2.3.1:
|
||||
resolved "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz"
|
||||
integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==
|
||||
|
||||
saslprep@^1.0.3:
|
||||
version "1.0.3"
|
||||
resolved "https://registry.yarnpkg.com/saslprep/-/saslprep-1.0.3.tgz#4c02f946b56cf54297e347ba1093e7acac4cf226"
|
||||
integrity sha512-/MY/PEMbk2SuY5sScONwhUDsV2p77Znkb/q3nSVstq/yQzYJOH/Azh29p9oJLsl3LnQwSvZDKagDGBsBwSooag==
|
||||
dependencies:
|
||||
sparse-bitfield "^3.0.3"
|
||||
|
||||
schema-utils@^3.1.0, schema-utils@^3.1.1:
|
||||
version "3.1.1"
|
||||
resolved "https://registry.npmjs.org/schema-utils/-/schema-utils-3.1.1.tgz"
|
||||
@ -4595,19 +4527,6 @@ slash@^3.0.0:
|
||||
resolved "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz"
|
||||
integrity sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==
|
||||
|
||||
smart-buffer@^4.2.0:
|
||||
version "4.2.0"
|
||||
resolved "https://registry.yarnpkg.com/smart-buffer/-/smart-buffer-4.2.0.tgz#6e1d71fa4f18c05f7d0ff216dd16a481d0e8d9ae"
|
||||
integrity sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==
|
||||
|
||||
socks@^2.7.1:
|
||||
version "2.7.1"
|
||||
resolved "https://registry.yarnpkg.com/socks/-/socks-2.7.1.tgz#d8e651247178fde79c0663043e07240196857d55"
|
||||
integrity sha512-7maUZy1N7uo6+WVEX6psASxtNlKaNVMlGQKkG/63nEDdLOWNbiUMoLK7X4uYoLhQstau72mLgfEWcXcwsaHbYQ==
|
||||
dependencies:
|
||||
ip "^2.0.0"
|
||||
smart-buffer "^4.2.0"
|
||||
|
||||
source-map-support@0.5.13:
|
||||
version "0.5.13"
|
||||
resolved "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.13.tgz"
|
||||
@ -4639,13 +4558,6 @@ sourcemap-codec@^1.4.8:
|
||||
resolved "https://registry.npmjs.org/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz"
|
||||
integrity sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==
|
||||
|
||||
sparse-bitfield@^3.0.3:
|
||||
version "3.0.3"
|
||||
resolved "https://registry.yarnpkg.com/sparse-bitfield/-/sparse-bitfield-3.0.3.tgz#ff4ae6e68656056ba4b3e792ab3334d38273ca11"
|
||||
integrity sha512-kvzhi7vqKTfkh0PZU+2D2PIllw2ymqJKujUcyPMd9Y75Nv4nPbGJZXNhxsgdQab2BmlDct1YnfQCguEvHr7VsQ==
|
||||
dependencies:
|
||||
memory-pager "^1.0.2"
|
||||
|
||||
sprintf-js@~1.0.2:
|
||||
version "1.0.3"
|
||||
resolved "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz"
|
||||
@ -4889,13 +4801,6 @@ toidentifier@1.0.1:
|
||||
resolved "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz"
|
||||
integrity sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==
|
||||
|
||||
tr46@^3.0.0:
|
||||
version "3.0.0"
|
||||
resolved "https://registry.yarnpkg.com/tr46/-/tr46-3.0.0.tgz#555c4e297a950617e8eeddef633c87d4d9d6cbf9"
|
||||
integrity sha512-l7FvfAHlcmulp8kr+flpQZmVwtu7nfRV7NZujtN0OqES8EL4O4e0qqzL0DC5gAvx/ZC/9lk6rhcUwYvkBnBnYA==
|
||||
dependencies:
|
||||
punycode "^2.1.1"
|
||||
|
||||
tr46@~0.0.3:
|
||||
version "0.0.3"
|
||||
resolved "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz"
|
||||
@ -5161,11 +5066,6 @@ webidl-conversions@^3.0.0:
|
||||
resolved "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz"
|
||||
integrity sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==
|
||||
|
||||
webidl-conversions@^7.0.0:
|
||||
version "7.0.0"
|
||||
resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-7.0.0.tgz#256b4e1882be7debbf01d05f0aa2039778ea080a"
|
||||
integrity sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==
|
||||
|
||||
webpack-node-externals@3.0.0:
|
||||
version "3.0.0"
|
||||
resolved "https://registry.npmjs.org/webpack-node-externals/-/webpack-node-externals-3.0.0.tgz"
|
||||
@ -5206,14 +5106,6 @@ webpack@5.76.2:
|
||||
watchpack "^2.4.0"
|
||||
webpack-sources "^3.2.3"
|
||||
|
||||
whatwg-url@^11.0.0:
|
||||
version "11.0.0"
|
||||
resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-11.0.0.tgz#0a849eebb5faf2119b901bb76fd795c2848d4018"
|
||||
integrity sha512-RKT8HExMpoYx4igMiVMY83lN6UeITKJlBQ+vR/8ZJ8OCdSiN3RwCq+9gH0+Xzj0+5IrM6i4j/6LuvzbZIQgEcQ==
|
||||
dependencies:
|
||||
tr46 "^3.0.0"
|
||||
webidl-conversions "^7.0.0"
|
||||
|
||||
whatwg-url@^5.0.0:
|
||||
version "5.0.0"
|
||||
resolved "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz"
|
||||
|
Loading…
Reference in New Issue
Block a user