speedrun-cli 2.6.9 → 2.6.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +8 -0
- package/package.json +1 -1
- package/src/generator.js +4 -0
- package/src/moduleGenerator.js +2 -1
- package/templates/base-crud/src/modules/products/dto/create-product.dto.ts +34 -0
- package/templates/base-crud/src/modules/products/dto/product.dto.ts +36 -0
- package/templates/base-crud/src/modules/products/dto/update-product.dto.ts +4 -0
- package/templates/base-crud/src/modules/products/products.controller.ts +78 -0
- package/templates/base-crud-drizzle/src/modules/products/dto/create-product.dto.ts +34 -0
- package/templates/base-crud-drizzle/src/modules/products/dto/product.dto.ts +36 -0
- package/templates/base-crud-drizzle/src/modules/products/dto/update-product.dto.ts +4 -0
- package/templates/base-crud-drizzle/src/modules/products/products.controller.ts +0 -1
- package/templates/base-crud-mongoose/src/modules/products/dto/create-product.dto.ts +34 -0
- package/templates/base-crud-mongoose/src/modules/products/dto/product.dto.ts +36 -0
- package/templates/base-crud-mongoose/src/modules/products/dto/update-product.dto.ts +4 -0
- package/templates/base-crud-mongoose/src/modules/products/products.controller.ts +0 -1
- package/templates/base-crud-prisma/src/modules/products/dto/create-product.dto.ts +34 -0
- package/templates/base-crud-prisma/src/modules/products/dto/product.dto.ts +36 -0
- package/templates/base-crud-prisma/src/modules/products/dto/update-product.dto.ts +4 -0
- package/templates/base-crud-prisma/src/modules/products/products.controller.ts +78 -0
- package/templates/base-crud-typeorm/src/modules/products/dto/create-product.dto.ts +34 -0
- package/templates/base-crud-typeorm/src/modules/products/dto/product.dto.ts +36 -0
- package/templates/base-crud-typeorm/src/modules/products/dto/update-product.dto.ts +4 -0
- package/templates/base-crud-typeorm/src/modules/products/products.controller.ts +0 -1
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,14 @@ All notable changes to create-nestjs-auth will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [2.6.10] - 2026-08-21
|
|
9
|
+
|
|
10
|
+
### Fixed
|
|
11
|
+
- **Restored Products controller and DTO files** — Restored `products.controller.ts`, `create-product.dto.ts`, `update-product.dto.ts`, and `product.dto.ts` into the `base-crud` and `base-crud-*` templates to eliminate TypeScript compilation errors (`TS2307: Cannot find module './products.controller'` / `Cannot find module './dto/create-product.dto'`).
|
|
12
|
+
- **Automated `app.module.ts` registration** — `ProductsModule` (and any module generated via `speedrun-cli generate`) is now automatically imported and registered inside `src/app.module.ts`'s `imports` array upon generation.
|
|
13
|
+
|
|
14
|
+
---
|
|
15
|
+
|
|
8
16
|
## [2.6.9] - 2026-08-21
|
|
9
17
|
|
|
10
18
|
### Fixed
|
package/package.json
CHANGED
package/src/generator.js
CHANGED
|
@@ -121,6 +121,10 @@ async function generateFromModularTemplates(targetDir, { baseDir, ormDir, dbDir,
|
|
|
121
121
|
filter: createCopyFilter(baseCrudOrmDir),
|
|
122
122
|
});
|
|
123
123
|
}
|
|
124
|
+
|
|
125
|
+
// 6c. Automatically register ProductsModule in src/app.module.ts
|
|
126
|
+
const { registerInAppModule } = require('./moduleGenerator');
|
|
127
|
+
await registerInAppModule(targetDir, 'Products', 'products');
|
|
124
128
|
}
|
|
125
129
|
}
|
|
126
130
|
|
package/src/moduleGenerator.js
CHANGED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger';
|
|
2
|
+
import { IsString, IsNotEmpty, IsNumber, IsOptional, Min } from 'class-validator';
|
|
3
|
+
|
|
4
|
+
export class CreateProductDto {
|
|
5
|
+
@ApiProperty({ example: 'Wireless Mouse' })
|
|
6
|
+
@IsString()
|
|
7
|
+
@IsNotEmpty()
|
|
8
|
+
name: string;
|
|
9
|
+
|
|
10
|
+
@ApiPropertyOptional({ example: 'Ergonomic wireless mouse' })
|
|
11
|
+
@IsString()
|
|
12
|
+
@IsOptional()
|
|
13
|
+
description?: string;
|
|
14
|
+
|
|
15
|
+
@ApiProperty({ example: 'PROD-001' })
|
|
16
|
+
@IsString()
|
|
17
|
+
@IsNotEmpty()
|
|
18
|
+
sku: string;
|
|
19
|
+
|
|
20
|
+
@ApiProperty({ example: 29.99 })
|
|
21
|
+
@IsNumber()
|
|
22
|
+
@Min(0)
|
|
23
|
+
price: number;
|
|
24
|
+
|
|
25
|
+
@ApiProperty({ example: 100 })
|
|
26
|
+
@IsNumber()
|
|
27
|
+
@Min(0)
|
|
28
|
+
stock: number;
|
|
29
|
+
|
|
30
|
+
@ApiPropertyOptional({ example: 'Electronics' })
|
|
31
|
+
@IsString()
|
|
32
|
+
@IsOptional()
|
|
33
|
+
category?: string;
|
|
34
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger';
|
|
2
|
+
|
|
3
|
+
export class ProductDto {
|
|
4
|
+
@ApiProperty({ example: '123e4567-e89b-12d3-a456-426614174000', format: 'uuid' })
|
|
5
|
+
id: string;
|
|
6
|
+
|
|
7
|
+
@ApiProperty({ example: 'Wireless Mouse' })
|
|
8
|
+
name: string;
|
|
9
|
+
|
|
10
|
+
@ApiPropertyOptional({ example: 'Ergonomic wireless mouse' })
|
|
11
|
+
description?: string;
|
|
12
|
+
|
|
13
|
+
@ApiProperty({ example: 'PROD-001' })
|
|
14
|
+
sku: string;
|
|
15
|
+
|
|
16
|
+
@ApiProperty({ example: 29.99 })
|
|
17
|
+
price: number;
|
|
18
|
+
|
|
19
|
+
@ApiProperty({ example: 100 })
|
|
20
|
+
stock: number;
|
|
21
|
+
|
|
22
|
+
@ApiPropertyOptional({ example: 'Electronics' })
|
|
23
|
+
category?: string;
|
|
24
|
+
|
|
25
|
+
@ApiProperty({ example: 'ACTIVE' })
|
|
26
|
+
status: string;
|
|
27
|
+
|
|
28
|
+
@ApiProperty({ example: '2025-01-01T00:00:00.000Z' })
|
|
29
|
+
createdAt: Date;
|
|
30
|
+
|
|
31
|
+
@ApiProperty({ example: '2025-01-01T00:00:00.000Z' })
|
|
32
|
+
updatedAt: Date;
|
|
33
|
+
|
|
34
|
+
@ApiPropertyOptional({ nullable: true, example: null })
|
|
35
|
+
deletedAt?: Date | null;
|
|
36
|
+
}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import {
|
|
2
|
+
Controller,
|
|
3
|
+
Get,
|
|
4
|
+
Param,
|
|
5
|
+
Query,
|
|
6
|
+
ParseUUIDPipe,
|
|
7
|
+
HttpStatus,
|
|
8
|
+
Type,
|
|
9
|
+
} from '@nestjs/common';
|
|
10
|
+
import {
|
|
11
|
+
ApiTags,
|
|
12
|
+
ApiOperation,
|
|
13
|
+
ApiResponse,
|
|
14
|
+
ApiBearerAuth,
|
|
15
|
+
ApiExtraModels,
|
|
16
|
+
ApiParam,
|
|
17
|
+
} from '@nestjs/swagger';
|
|
18
|
+
import { BaseController } from '../../common/base/base.controller';
|
|
19
|
+
import {
|
|
20
|
+
ApiResponseDto,
|
|
21
|
+
ApiResponseSchema,
|
|
22
|
+
PaginatedResponseDto,
|
|
23
|
+
PaginatedResponseSchema,
|
|
24
|
+
PaginationQueryDto,
|
|
25
|
+
} from '../../common/base';
|
|
26
|
+
import { ProductsService, ProductEntity } from './products.service';
|
|
27
|
+
import { CreateProductDto } from './dto/create-product.dto';
|
|
28
|
+
import { UpdateProductDto } from './dto/update-product.dto';
|
|
29
|
+
import { ProductDto } from './dto/product.dto';
|
|
30
|
+
|
|
31
|
+
@ApiTags('Products')
|
|
32
|
+
@ApiBearerAuth('bearer')
|
|
33
|
+
@ApiExtraModels(ApiResponseDto, PaginatedResponseDto, ProductDto)
|
|
34
|
+
@Controller('products')
|
|
35
|
+
export class ProductsController extends BaseController<
|
|
36
|
+
ProductEntity,
|
|
37
|
+
CreateProductDto,
|
|
38
|
+
UpdateProductDto
|
|
39
|
+
> {
|
|
40
|
+
constructor(private readonly productsService: ProductsService) {
|
|
41
|
+
super(productsService);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
protected getDtoClass(): Type<ProductEntity> {
|
|
45
|
+
return ProductDto as unknown as Type<ProductEntity>;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
@Get()
|
|
49
|
+
@ApiOperation({ summary: 'Get all products (paginated)' })
|
|
50
|
+
@ApiResponse({ status: HttpStatus.OK, schema: PaginatedResponseSchema(ProductDto) })
|
|
51
|
+
@ApiResponse({ status: HttpStatus.UNAUTHORIZED, description: 'Unauthorized' })
|
|
52
|
+
override async findAll(@Query() pagination: PaginationQueryDto): Promise<PaginatedResponseDto<ProductEntity>> {
|
|
53
|
+
return super.findAll(pagination);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
@Get('by-sku/:sku')
|
|
57
|
+
@ApiOperation({ summary: 'Get a product by SKU' })
|
|
58
|
+
@ApiParam({ name: 'sku', example: 'KB-WL-MEC-001' })
|
|
59
|
+
@ApiResponse({ status: HttpStatus.OK, schema: ApiResponseSchema(ProductDto) })
|
|
60
|
+
@ApiResponse({ status: HttpStatus.NOT_FOUND, description: 'Product not found' })
|
|
61
|
+
async findBySku(@Param('sku') sku: string): Promise<ApiResponseDto<ProductEntity>> {
|
|
62
|
+
const data = await this.productsService.findBySku(sku);
|
|
63
|
+
return { success: true, data, meta: { correlationId: '', timestamp: new Date().toISOString() } };
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
@Get(':id')
|
|
67
|
+
@ApiOperation({ summary: 'Get a product by ID' })
|
|
68
|
+
@ApiParam({ name: 'id', format: 'uuid', example: '123e4567-e89b-12d3-a456-426614174000' })
|
|
69
|
+
@ApiResponse({ status: HttpStatus.OK, schema: ApiResponseSchema(ProductDto) })
|
|
70
|
+
@ApiResponse({ status: HttpStatus.NOT_FOUND, description: 'Product not found' })
|
|
71
|
+
@ApiResponse({ status: HttpStatus.BAD_REQUEST, description: 'Invalid UUID' })
|
|
72
|
+
override async findOne(
|
|
73
|
+
@Param('id', new ParseUUIDPipe({ version: '4', errorHttpStatusCode: HttpStatus.BAD_REQUEST }))
|
|
74
|
+
id: string,
|
|
75
|
+
): Promise<ApiResponseDto<ProductEntity>> {
|
|
76
|
+
return super.findOne(id);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger';
|
|
2
|
+
import { IsString, IsNotEmpty, IsNumber, IsOptional, Min } from 'class-validator';
|
|
3
|
+
|
|
4
|
+
export class CreateProductDto {
|
|
5
|
+
@ApiProperty({ example: 'Wireless Mouse' })
|
|
6
|
+
@IsString()
|
|
7
|
+
@IsNotEmpty()
|
|
8
|
+
name: string;
|
|
9
|
+
|
|
10
|
+
@ApiPropertyOptional({ example: 'Ergonomic wireless mouse' })
|
|
11
|
+
@IsString()
|
|
12
|
+
@IsOptional()
|
|
13
|
+
description?: string;
|
|
14
|
+
|
|
15
|
+
@ApiProperty({ example: 'PROD-001' })
|
|
16
|
+
@IsString()
|
|
17
|
+
@IsNotEmpty()
|
|
18
|
+
sku: string;
|
|
19
|
+
|
|
20
|
+
@ApiProperty({ example: 29.99 })
|
|
21
|
+
@IsNumber()
|
|
22
|
+
@Min(0)
|
|
23
|
+
price: number;
|
|
24
|
+
|
|
25
|
+
@ApiProperty({ example: 100 })
|
|
26
|
+
@IsNumber()
|
|
27
|
+
@Min(0)
|
|
28
|
+
stock: number;
|
|
29
|
+
|
|
30
|
+
@ApiPropertyOptional({ example: 'Electronics' })
|
|
31
|
+
@IsString()
|
|
32
|
+
@IsOptional()
|
|
33
|
+
category?: string;
|
|
34
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger';
|
|
2
|
+
|
|
3
|
+
export class ProductDto {
|
|
4
|
+
@ApiProperty({ example: '123e4567-e89b-12d3-a456-426614174000', format: 'uuid' })
|
|
5
|
+
id: string;
|
|
6
|
+
|
|
7
|
+
@ApiProperty({ example: 'Wireless Mouse' })
|
|
8
|
+
name: string;
|
|
9
|
+
|
|
10
|
+
@ApiPropertyOptional({ example: 'Ergonomic wireless mouse' })
|
|
11
|
+
description?: string;
|
|
12
|
+
|
|
13
|
+
@ApiProperty({ example: 'PROD-001' })
|
|
14
|
+
sku: string;
|
|
15
|
+
|
|
16
|
+
@ApiProperty({ example: 29.99 })
|
|
17
|
+
price: number;
|
|
18
|
+
|
|
19
|
+
@ApiProperty({ example: 100 })
|
|
20
|
+
stock: number;
|
|
21
|
+
|
|
22
|
+
@ApiPropertyOptional({ example: 'Electronics' })
|
|
23
|
+
category?: string;
|
|
24
|
+
|
|
25
|
+
@ApiProperty({ example: 'ACTIVE' })
|
|
26
|
+
status: string;
|
|
27
|
+
|
|
28
|
+
@ApiProperty({ example: '2025-01-01T00:00:00.000Z' })
|
|
29
|
+
createdAt: Date;
|
|
30
|
+
|
|
31
|
+
@ApiProperty({ example: '2025-01-01T00:00:00.000Z' })
|
|
32
|
+
updatedAt: Date;
|
|
33
|
+
|
|
34
|
+
@ApiPropertyOptional({ nullable: true, example: null })
|
|
35
|
+
deletedAt?: Date | null;
|
|
36
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger';
|
|
2
|
+
import { IsString, IsNotEmpty, IsNumber, IsOptional, Min } from 'class-validator';
|
|
3
|
+
|
|
4
|
+
export class CreateProductDto {
|
|
5
|
+
@ApiProperty({ example: 'Wireless Mouse' })
|
|
6
|
+
@IsString()
|
|
7
|
+
@IsNotEmpty()
|
|
8
|
+
name: string;
|
|
9
|
+
|
|
10
|
+
@ApiPropertyOptional({ example: 'Ergonomic wireless mouse' })
|
|
11
|
+
@IsString()
|
|
12
|
+
@IsOptional()
|
|
13
|
+
description?: string;
|
|
14
|
+
|
|
15
|
+
@ApiProperty({ example: 'PROD-001' })
|
|
16
|
+
@IsString()
|
|
17
|
+
@IsNotEmpty()
|
|
18
|
+
sku: string;
|
|
19
|
+
|
|
20
|
+
@ApiProperty({ example: 29.99 })
|
|
21
|
+
@IsNumber()
|
|
22
|
+
@Min(0)
|
|
23
|
+
price: number;
|
|
24
|
+
|
|
25
|
+
@ApiProperty({ example: 100 })
|
|
26
|
+
@IsNumber()
|
|
27
|
+
@Min(0)
|
|
28
|
+
stock: number;
|
|
29
|
+
|
|
30
|
+
@ApiPropertyOptional({ example: 'Electronics' })
|
|
31
|
+
@IsString()
|
|
32
|
+
@IsOptional()
|
|
33
|
+
category?: string;
|
|
34
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger';
|
|
2
|
+
|
|
3
|
+
export class ProductDto {
|
|
4
|
+
@ApiProperty({ example: '123e4567-e89b-12d3-a456-426614174000', format: 'uuid' })
|
|
5
|
+
id: string;
|
|
6
|
+
|
|
7
|
+
@ApiProperty({ example: 'Wireless Mouse' })
|
|
8
|
+
name: string;
|
|
9
|
+
|
|
10
|
+
@ApiPropertyOptional({ example: 'Ergonomic wireless mouse' })
|
|
11
|
+
description?: string;
|
|
12
|
+
|
|
13
|
+
@ApiProperty({ example: 'PROD-001' })
|
|
14
|
+
sku: string;
|
|
15
|
+
|
|
16
|
+
@ApiProperty({ example: 29.99 })
|
|
17
|
+
price: number;
|
|
18
|
+
|
|
19
|
+
@ApiProperty({ example: 100 })
|
|
20
|
+
stock: number;
|
|
21
|
+
|
|
22
|
+
@ApiPropertyOptional({ example: 'Electronics' })
|
|
23
|
+
category?: string;
|
|
24
|
+
|
|
25
|
+
@ApiProperty({ example: 'ACTIVE' })
|
|
26
|
+
status: string;
|
|
27
|
+
|
|
28
|
+
@ApiProperty({ example: '2025-01-01T00:00:00.000Z' })
|
|
29
|
+
createdAt: Date;
|
|
30
|
+
|
|
31
|
+
@ApiProperty({ example: '2025-01-01T00:00:00.000Z' })
|
|
32
|
+
updatedAt: Date;
|
|
33
|
+
|
|
34
|
+
@ApiPropertyOptional({ nullable: true, example: null })
|
|
35
|
+
deletedAt?: Date | null;
|
|
36
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger';
|
|
2
|
+
import { IsString, IsNotEmpty, IsNumber, IsOptional, Min } from 'class-validator';
|
|
3
|
+
|
|
4
|
+
export class CreateProductDto {
|
|
5
|
+
@ApiProperty({ example: 'Wireless Mouse' })
|
|
6
|
+
@IsString()
|
|
7
|
+
@IsNotEmpty()
|
|
8
|
+
name: string;
|
|
9
|
+
|
|
10
|
+
@ApiPropertyOptional({ example: 'Ergonomic wireless mouse' })
|
|
11
|
+
@IsString()
|
|
12
|
+
@IsOptional()
|
|
13
|
+
description?: string;
|
|
14
|
+
|
|
15
|
+
@ApiProperty({ example: 'PROD-001' })
|
|
16
|
+
@IsString()
|
|
17
|
+
@IsNotEmpty()
|
|
18
|
+
sku: string;
|
|
19
|
+
|
|
20
|
+
@ApiProperty({ example: 29.99 })
|
|
21
|
+
@IsNumber()
|
|
22
|
+
@Min(0)
|
|
23
|
+
price: number;
|
|
24
|
+
|
|
25
|
+
@ApiProperty({ example: 100 })
|
|
26
|
+
@IsNumber()
|
|
27
|
+
@Min(0)
|
|
28
|
+
stock: number;
|
|
29
|
+
|
|
30
|
+
@ApiPropertyOptional({ example: 'Electronics' })
|
|
31
|
+
@IsString()
|
|
32
|
+
@IsOptional()
|
|
33
|
+
category?: string;
|
|
34
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger';
|
|
2
|
+
|
|
3
|
+
export class ProductDto {
|
|
4
|
+
@ApiProperty({ example: '123e4567-e89b-12d3-a456-426614174000', format: 'uuid' })
|
|
5
|
+
id: string;
|
|
6
|
+
|
|
7
|
+
@ApiProperty({ example: 'Wireless Mouse' })
|
|
8
|
+
name: string;
|
|
9
|
+
|
|
10
|
+
@ApiPropertyOptional({ example: 'Ergonomic wireless mouse' })
|
|
11
|
+
description?: string;
|
|
12
|
+
|
|
13
|
+
@ApiProperty({ example: 'PROD-001' })
|
|
14
|
+
sku: string;
|
|
15
|
+
|
|
16
|
+
@ApiProperty({ example: 29.99 })
|
|
17
|
+
price: number;
|
|
18
|
+
|
|
19
|
+
@ApiProperty({ example: 100 })
|
|
20
|
+
stock: number;
|
|
21
|
+
|
|
22
|
+
@ApiPropertyOptional({ example: 'Electronics' })
|
|
23
|
+
category?: string;
|
|
24
|
+
|
|
25
|
+
@ApiProperty({ example: 'ACTIVE' })
|
|
26
|
+
status: string;
|
|
27
|
+
|
|
28
|
+
@ApiProperty({ example: '2025-01-01T00:00:00.000Z' })
|
|
29
|
+
createdAt: Date;
|
|
30
|
+
|
|
31
|
+
@ApiProperty({ example: '2025-01-01T00:00:00.000Z' })
|
|
32
|
+
updatedAt: Date;
|
|
33
|
+
|
|
34
|
+
@ApiPropertyOptional({ nullable: true, example: null })
|
|
35
|
+
deletedAt?: Date | null;
|
|
36
|
+
}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import {
|
|
2
|
+
Controller,
|
|
3
|
+
Get,
|
|
4
|
+
Param,
|
|
5
|
+
Query,
|
|
6
|
+
ParseUUIDPipe,
|
|
7
|
+
HttpStatus,
|
|
8
|
+
Type,
|
|
9
|
+
} from '@nestjs/common';
|
|
10
|
+
import {
|
|
11
|
+
ApiTags,
|
|
12
|
+
ApiOperation,
|
|
13
|
+
ApiResponse,
|
|
14
|
+
ApiBearerAuth,
|
|
15
|
+
ApiExtraModels,
|
|
16
|
+
ApiParam,
|
|
17
|
+
} from '@nestjs/swagger';
|
|
18
|
+
import { BaseController } from '../../common/base/base.controller';
|
|
19
|
+
import {
|
|
20
|
+
ApiResponseDto,
|
|
21
|
+
ApiResponseSchema,
|
|
22
|
+
PaginatedResponseDto,
|
|
23
|
+
PaginatedResponseSchema,
|
|
24
|
+
PaginationQueryDto,
|
|
25
|
+
} from '../../common/base';
|
|
26
|
+
import { ProductsService, ProductEntity } from './products.service';
|
|
27
|
+
import { CreateProductDto } from './dto/create-product.dto';
|
|
28
|
+
import { UpdateProductDto } from './dto/update-product.dto';
|
|
29
|
+
import { ProductDto } from './dto/product.dto';
|
|
30
|
+
|
|
31
|
+
@ApiTags('Products')
|
|
32
|
+
@ApiBearerAuth('bearer')
|
|
33
|
+
@ApiExtraModels(ApiResponseDto, PaginatedResponseDto, ProductDto)
|
|
34
|
+
@Controller('products')
|
|
35
|
+
export class ProductsController extends BaseController<
|
|
36
|
+
ProductEntity,
|
|
37
|
+
CreateProductDto,
|
|
38
|
+
UpdateProductDto
|
|
39
|
+
> {
|
|
40
|
+
constructor(private readonly productsService: ProductsService) {
|
|
41
|
+
super(productsService);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
protected getDtoClass(): Type<ProductEntity> {
|
|
45
|
+
return ProductDto as unknown as Type<ProductEntity>;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
@Get()
|
|
49
|
+
@ApiOperation({ summary: 'Get all products (paginated)' })
|
|
50
|
+
@ApiResponse({ status: HttpStatus.OK, schema: PaginatedResponseSchema(ProductDto) })
|
|
51
|
+
@ApiResponse({ status: HttpStatus.UNAUTHORIZED, description: 'Unauthorized' })
|
|
52
|
+
override async findAll(@Query() pagination: PaginationQueryDto): Promise<PaginatedResponseDto<ProductEntity>> {
|
|
53
|
+
return super.findAll(pagination);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
@Get('by-sku/:sku')
|
|
57
|
+
@ApiOperation({ summary: 'Get a product by SKU' })
|
|
58
|
+
@ApiParam({ name: 'sku', example: 'KB-WL-MEC-001' })
|
|
59
|
+
@ApiResponse({ status: HttpStatus.OK, schema: ApiResponseSchema(ProductDto) })
|
|
60
|
+
@ApiResponse({ status: HttpStatus.NOT_FOUND, description: 'Product not found' })
|
|
61
|
+
async findBySku(@Param('sku') sku: string): Promise<ApiResponseDto<ProductEntity>> {
|
|
62
|
+
const data = await this.productsService.findBySku(sku);
|
|
63
|
+
return { success: true, data, meta: { correlationId: '', timestamp: new Date().toISOString() } };
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
@Get(':id')
|
|
67
|
+
@ApiOperation({ summary: 'Get a product by ID' })
|
|
68
|
+
@ApiParam({ name: 'id', format: 'uuid', example: '123e4567-e89b-12d3-a456-426614174000' })
|
|
69
|
+
@ApiResponse({ status: HttpStatus.OK, schema: ApiResponseSchema(ProductDto) })
|
|
70
|
+
@ApiResponse({ status: HttpStatus.NOT_FOUND, description: 'Product not found' })
|
|
71
|
+
@ApiResponse({ status: HttpStatus.BAD_REQUEST, description: 'Invalid UUID' })
|
|
72
|
+
override async findOne(
|
|
73
|
+
@Param('id', new ParseUUIDPipe({ version: '4', errorHttpStatusCode: HttpStatus.BAD_REQUEST }))
|
|
74
|
+
id: string,
|
|
75
|
+
): Promise<ApiResponseDto<ProductEntity>> {
|
|
76
|
+
return super.findOne(id);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger';
|
|
2
|
+
import { IsString, IsNotEmpty, IsNumber, IsOptional, Min } from 'class-validator';
|
|
3
|
+
|
|
4
|
+
export class CreateProductDto {
|
|
5
|
+
@ApiProperty({ example: 'Wireless Mouse' })
|
|
6
|
+
@IsString()
|
|
7
|
+
@IsNotEmpty()
|
|
8
|
+
name: string;
|
|
9
|
+
|
|
10
|
+
@ApiPropertyOptional({ example: 'Ergonomic wireless mouse' })
|
|
11
|
+
@IsString()
|
|
12
|
+
@IsOptional()
|
|
13
|
+
description?: string;
|
|
14
|
+
|
|
15
|
+
@ApiProperty({ example: 'PROD-001' })
|
|
16
|
+
@IsString()
|
|
17
|
+
@IsNotEmpty()
|
|
18
|
+
sku: string;
|
|
19
|
+
|
|
20
|
+
@ApiProperty({ example: 29.99 })
|
|
21
|
+
@IsNumber()
|
|
22
|
+
@Min(0)
|
|
23
|
+
price: number;
|
|
24
|
+
|
|
25
|
+
@ApiProperty({ example: 100 })
|
|
26
|
+
@IsNumber()
|
|
27
|
+
@Min(0)
|
|
28
|
+
stock: number;
|
|
29
|
+
|
|
30
|
+
@ApiPropertyOptional({ example: 'Electronics' })
|
|
31
|
+
@IsString()
|
|
32
|
+
@IsOptional()
|
|
33
|
+
category?: string;
|
|
34
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger';
|
|
2
|
+
|
|
3
|
+
export class ProductDto {
|
|
4
|
+
@ApiProperty({ example: '123e4567-e89b-12d3-a456-426614174000', format: 'uuid' })
|
|
5
|
+
id: string;
|
|
6
|
+
|
|
7
|
+
@ApiProperty({ example: 'Wireless Mouse' })
|
|
8
|
+
name: string;
|
|
9
|
+
|
|
10
|
+
@ApiPropertyOptional({ example: 'Ergonomic wireless mouse' })
|
|
11
|
+
description?: string;
|
|
12
|
+
|
|
13
|
+
@ApiProperty({ example: 'PROD-001' })
|
|
14
|
+
sku: string;
|
|
15
|
+
|
|
16
|
+
@ApiProperty({ example: 29.99 })
|
|
17
|
+
price: number;
|
|
18
|
+
|
|
19
|
+
@ApiProperty({ example: 100 })
|
|
20
|
+
stock: number;
|
|
21
|
+
|
|
22
|
+
@ApiPropertyOptional({ example: 'Electronics' })
|
|
23
|
+
category?: string;
|
|
24
|
+
|
|
25
|
+
@ApiProperty({ example: 'ACTIVE' })
|
|
26
|
+
status: string;
|
|
27
|
+
|
|
28
|
+
@ApiProperty({ example: '2025-01-01T00:00:00.000Z' })
|
|
29
|
+
createdAt: Date;
|
|
30
|
+
|
|
31
|
+
@ApiProperty({ example: '2025-01-01T00:00:00.000Z' })
|
|
32
|
+
updatedAt: Date;
|
|
33
|
+
|
|
34
|
+
@ApiPropertyOptional({ nullable: true, example: null })
|
|
35
|
+
deletedAt?: Date | null;
|
|
36
|
+
}
|