From 9921869191e7de1d339c84952bab234a5547e4e0 Mon Sep 17 00:00:00 2001 From: Michelle Ndiangui <105012834+MuthoniMN@users.noreply.github.com> Date: Wed, 7 Aug 2024 22:21:37 +0000 Subject: [PATCH] fix: adding response bodies to product docs --- .../responses/create-product-response.dto.ts | 23 ++++ .../dto/responses/delete-product.dto.ts | 17 +++ .../dto/responses/error-response.dto.ts | 110 ++++++++++++++++++ .../dto/responses/get-products.dto.ts | 17 +++ .../dto/responses/product-details.dto.ts | 63 ++++++++++ .../dto/responses/search-products.dto.ts | 23 ++++ .../dto/responses/stock-response.dto.ts | 37 ++++++ src/modules/products/products.controller.ts | 53 +++++---- 8 files changed, 323 insertions(+), 20 deletions(-) create mode 100644 src/modules/products/dto/responses/create-product-response.dto.ts create mode 100644 src/modules/products/dto/responses/delete-product.dto.ts create mode 100644 src/modules/products/dto/responses/error-response.dto.ts create mode 100644 src/modules/products/dto/responses/get-products.dto.ts create mode 100644 src/modules/products/dto/responses/product-details.dto.ts create mode 100644 src/modules/products/dto/responses/search-products.dto.ts create mode 100644 src/modules/products/dto/responses/stock-response.dto.ts diff --git a/src/modules/products/dto/responses/create-product-response.dto.ts b/src/modules/products/dto/responses/create-product-response.dto.ts new file mode 100644 index 000000000..dd4cda981 --- /dev/null +++ b/src/modules/products/dto/responses/create-product-response.dto.ts @@ -0,0 +1,23 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { IsString } from 'class-validator'; +import { ProductDetailsDto } from './product-details.dto'; + +export class SuccessfulCreateResponseDto { + @ApiProperty({ + description: 'The status of the request', + example: '200', + }) + status: number; + + @ApiProperty({ + description: 'The message of the response', + example: 'Product created successfully', + }) + @IsString() + message: string; + + @ApiProperty({ + description: 'The created product details', + }) + data: ProductDetailsDto; +} diff --git a/src/modules/products/dto/responses/delete-product.dto.ts b/src/modules/products/dto/responses/delete-product.dto.ts new file mode 100644 index 000000000..c5f845c7f --- /dev/null +++ b/src/modules/products/dto/responses/delete-product.dto.ts @@ -0,0 +1,17 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { IsString } from 'class-validator'; + +export class DeleteProductDto { + @ApiProperty({ + description: 'The response message', + example: 'Product successfully deleted', + }) + @IsString() + message: string; + + @ApiProperty({ + description: 'The response data', + }) + @IsString() + data: {}; +} diff --git a/src/modules/products/dto/responses/error-response.dto.ts b/src/modules/products/dto/responses/error-response.dto.ts new file mode 100644 index 000000000..9544286e8 --- /dev/null +++ b/src/modules/products/dto/responses/error-response.dto.ts @@ -0,0 +1,110 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { IsString } from 'class-validator'; + +export class BadRequestResponseDto { + @ApiProperty({ + description: 'The status of the request', + example: 'Unprocessable entity exception', + }) + @IsString() + status: string; + + @ApiProperty({ + description: 'The response code', + example: '422', + }) + @IsString() + status_code: string; + + @ApiProperty({ + description: 'The message of the response', + example: 'Invalid organisation credentials', + }) + @IsString() + message: string; +} + +export class NotFoundResponseDto { + @ApiProperty({ + description: 'The response code', + example: '404', + }) + @IsString() + status_code: string; + + @ApiProperty({ + description: 'The message of the response', + example: 'Product not found', + }) + @IsString() + message: string; +} + +export class NoResultsResponseDto { + @ApiProperty({ + description: 'The status of the request', + example: 'No Content', + }) + @IsString() + status: string; + + @ApiProperty({ + description: 'The response code', + example: '204', + }) + @IsString() + status_code: string; + + @ApiProperty({ + description: 'The message of the response', + example: 'No products found', + }) + @IsString() + message: string; +} + +export class ForbiddenErrorResponseDto { + @ApiProperty({ + description: 'The status of the request', + example: 'fail', + }) + @IsString() + status: string; + + @ApiProperty({ + description: 'The response code', + example: '403', + }) + @IsString() + status_code: string; + + @ApiProperty({ + description: 'The message of the response', + example: 'Not allowed to perform this action', + }) + @IsString() + message: string; +} + +export class ServerErrorResponseDto { + @ApiProperty({ + description: 'The status of the request', + example: 'Internal server error', + }) + @IsString() + status: string; + + @ApiProperty({ + description: 'The response code', + example: '500', + }) + @IsString() + status_code: string; + + @ApiProperty({ + description: 'The message of the response', + example: 'An unexpected error occurred. Please try again later.', + }) + @IsString() + message: string; +} diff --git a/src/modules/products/dto/responses/get-products.dto.ts b/src/modules/products/dto/responses/get-products.dto.ts new file mode 100644 index 000000000..7945af203 --- /dev/null +++ b/src/modules/products/dto/responses/get-products.dto.ts @@ -0,0 +1,17 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { IsString, IsBoolean } from 'class-validator'; +import { ProductDetailsDto } from './product-details.dto'; + +export class ProductResponseDto { + @ApiProperty({ + description: 'The message of the response', + example: 'Product retrieved successfully', + }) + @IsString() + message: string; + + @ApiProperty({ + description: 'The product details', + }) + data: ProductDetailsDto; +} diff --git a/src/modules/products/dto/responses/product-details.dto.ts b/src/modules/products/dto/responses/product-details.dto.ts new file mode 100644 index 000000000..17aba9cd7 --- /dev/null +++ b/src/modules/products/dto/responses/product-details.dto.ts @@ -0,0 +1,63 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { IsString, IsBoolean } from 'class-validator'; + +export class ProductDetailsDto { + @ApiProperty({ + description: 'Product id', + example: 'product_1', + }) + @IsString() + id: string; + + @ApiProperty({ + description: 'Product name', + example: 'Product 1', + }) + @IsString() + name: string; + + @ApiProperty({ + description: 'Product description', + example: 'Product description 1', + }) + @IsString() + description: string; + + @ApiProperty({ + description: 'Product price', + example: '500', + }) + price: number; + + @ApiProperty({ + description: 'Product status', + example: 'Product status', + }) + @IsString() + status: string; + + @ApiProperty({ + description: 'Product deletion status', + example: 'false', + }) + @IsBoolean() + is_deleted: boolean; + + @ApiProperty({ + description: 'Product quantity', + example: 'Product quantity', + }) + quantity: number; + + @ApiProperty({ + description: 'Date when the product was created', + example: new Date(), + }) + created_at: Date; + + @ApiProperty({ + description: 'Date when the product was last updated', + example: new Date(), + }) + updated_at: Date; +} diff --git a/src/modules/products/dto/responses/search-products.dto.ts b/src/modules/products/dto/responses/search-products.dto.ts new file mode 100644 index 000000000..f4fd0674a --- /dev/null +++ b/src/modules/products/dto/responses/search-products.dto.ts @@ -0,0 +1,23 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { IsString, IsBoolean } from 'class-validator'; +import { ProductDetailsDto } from './product-details.dto'; + +export class SearchResponseDto { + @ApiProperty({ + description: 'The status of the request', + example: 'true', + }) + @IsBoolean() + success: boolean; + + @ApiProperty({ + description: 'The status of the request', + example: '200', + }) + statusCode: number; + + @ApiProperty({ + description: 'The search results', + }) + data: ProductDetailsDto[]; +} diff --git a/src/modules/products/dto/responses/stock-response.dto.ts b/src/modules/products/dto/responses/stock-response.dto.ts new file mode 100644 index 000000000..2332538da --- /dev/null +++ b/src/modules/products/dto/responses/stock-response.dto.ts @@ -0,0 +1,37 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { IsString } from 'class-validator'; + +class StockDto { + @ApiProperty({ + description: 'Product id', + example: 'product_1', + }) + @IsString() + product_id: string; + + @ApiProperty({ + description: 'Product stock', + example: '15', + }) + current_stock: number; + + @ApiProperty({ + description: 'Date when the stock was last updated', + example: new Date(), + }) + last_updated: Date; +} + +export class StockResponseDto { + @ApiProperty({ + description: 'The response message', + example: 'Product stock retrieved successfully', + }) + @IsString() + message: string; + + @ApiProperty({ + description: 'The response data', + }) + data: StockDto; +} diff --git a/src/modules/products/products.controller.ts b/src/modules/products/products.controller.ts index e5086c0f8..1618a7dd7 100644 --- a/src/modules/products/products.controller.ts +++ b/src/modules/products/products.controller.ts @@ -4,6 +4,18 @@ import { OwnershipGuard } from '../../guards/authorization.guard'; import { CreateProductRequestDto } from './dto/create-product.dto'; import { ProductsService } from './products.service'; import { UpdateProductDTO } from './dto/update-product.dto'; +import { SuccessfulCreateResponseDto } from './dto/responses/create-product-response.dto'; +import { + BadRequestResponseDto, + ServerErrorResponseDto, + NotFoundResponseDto, + NoResultsResponseDto, + ForbiddenErrorResponseDto, +} from './dto/responses/error-response.dto'; +import { StockResponseDto } from './dto/responses/stock-response.dto'; +import { DeleteProductDto } from './dto/responses/delete-product.dto'; +import { SearchResponseDto } from './dto/responses/search-products.dto'; +import { ProductResponseDto } from './dto/responses/get-products.dto'; @ApiTags('Products') @Controller('/organizations/:id/products') @@ -16,9 +28,9 @@ export class ProductsController { @ApiOperation({ summary: 'Creates a new product' }) @ApiParam({ name: 'id', description: 'organisation ID', example: '12345' }) @ApiBody({ type: CreateProductRequestDto, description: 'Details of the product to be created' }) - @ApiResponse({ status: 201, description: 'Product created successfully' }) - @ApiResponse({ status: 400, description: 'Bad request' }) - @ApiResponse({ status: 500, description: 'Internal server error' }) + @ApiResponse({ status: 201, description: 'Product created successfully', type: SuccessfulCreateResponseDto }) + @ApiResponse({ status: 422, description: 'Invalid Organisation', type: BadRequestResponseDto }) + @ApiResponse({ status: 500, description: 'Internal server error', type: ServerErrorResponseDto }) async createProduct(@Param('id') id: string, @Body() createProductDto: CreateProductRequestDto) { return this.productsService.createProduct(id, createProductDto); } @@ -26,10 +38,10 @@ export class ProductsController { @Get('search') @ApiOperation({ summary: 'Search for products' }) @ApiParam({ name: 'id', description: 'organisation ID', example: '12345' }) - @ApiResponse({ status: 200, description: 'Products found successfully' }) - @ApiResponse({ status: 204, description: 'No products found' }) - @ApiResponse({ status: 400, description: 'Bad request' }) - @ApiResponse({ status: 500, description: 'Internal server error' }) + @ApiResponse({ status: 200, description: 'Products found successfully', type: SearchResponseDto }) + @ApiResponse({ status: 204, description: 'No products found', type: NoResultsResponseDto }) + @ApiResponse({ status: 422, description: 'Invalid organisation', type: BadRequestResponseDto }) + @ApiResponse({ status: 500, description: 'Internal server error', type: ServerErrorResponseDto }) async searchProducts( @Param('id') id: string, @Query('name') name?: string, @@ -47,8 +59,8 @@ export class ProductsController { @ApiBody({ type: CreateProductRequestDto, description: 'Details of the product to be created' }) @ApiResponse({ status: 200, description: 'Product created successfully' }) @ApiResponse({ status: 400, description: 'Bad request' }) - @ApiResponse({ status: 404, description: 'Product not found' }) - @ApiResponse({ status: 500, description: 'Internal server error' }) + @ApiResponse({ status: 404, description: 'Product not found', type: NotFoundResponseDto }) + @ApiResponse({ status: 500, description: 'Internal server error', type: ServerErrorResponseDto }) async getById(@Param('orgId') id: string, @Param('id') productId: string) { return this.productsService.getProductById(productId); } @@ -58,10 +70,11 @@ export class ProductsController { @HttpCode(200) @ApiOperation({ summary: 'Update product' }) @ApiParam({ name: 'productId', type: String, description: 'Product ID' }) - @ApiResponse({ status: 200, description: 'Product updated successfully' }) - @ApiResponse({ status: 400, description: 'Bad request' }) - @ApiResponse({ status: 404, description: 'Product not found' }) - @ApiResponse({ status: 500, description: 'Internal server error' }) + @ApiResponse({ status: 200, description: 'Product updated successfully', type: SuccessfulCreateResponseDto }) + @ApiResponse({ status: 422, description: 'Invalid organisation', type: BadRequestResponseDto }) + @ApiResponse({ status: 403, description: 'Forbidden', type: ForbiddenErrorResponseDto }) + @ApiResponse({ status: 404, description: 'Product not found', type: NotFoundResponseDto }) + @ApiResponse({ status: 500, description: 'Internal server error', type: ServerErrorResponseDto }) async updateProduct( @Param('id') id: string, @Param('productId') productId: string, @@ -74,10 +87,10 @@ export class ProductsController { @Delete(':productId') @ApiOperation({ summary: 'Delete a product' }) @ApiParam({ name: 'productId', description: 'Product ID' }) - @ApiResponse({ status: 200, description: 'Product deleted successfully' }) - @ApiResponse({ status: 400, description: 'Bad request' }) - @ApiResponse({ status: 404, description: 'Product not found' }) - @ApiResponse({ status: 500, description: 'Internal server error' }) + @ApiResponse({ status: 200, description: 'Product deleted successfully', type: DeleteProductDto }) + @ApiResponse({ status: 422, description: 'Bad request', type: BadRequestResponseDto }) + @ApiResponse({ status: 404, description: 'Product not found', type: NotFoundResponseDto }) + @ApiResponse({ status: 500, description: 'Internal server error', type: ServerErrorResponseDto }) async deleteProduct(@Param('id') id: string, @Param('productId') productId: string) { return this.productsService.deleteProduct(id, productId); } @@ -87,9 +100,9 @@ export class ProductsController { @ApiOperation({ summary: 'Gets a product stock details by id' }) @ApiParam({ name: 'id', description: 'Organization ID', example: '12345' }) @ApiParam({ name: 'productId', description: 'Product ID' }) - @ApiResponse({ status: 200, description: 'Product stock retrieved successfully' }) - @ApiResponse({ status: 404, description: 'Product not found' }) - @ApiResponse({ status: 500, description: 'Internal server error' }) + @ApiResponse({ status: 200, description: 'Product stock retrieved successfully', type: StockResponseDto }) + @ApiResponse({ status: 404, description: 'Product not found', type: NotFoundResponseDto }) + @ApiResponse({ status: 500, description: 'Internal server error', type: ServerErrorResponseDto }) async getProductStock(@Param('productId') productId: string) { return this.productsService.getProductStock(productId); }