From 4ad3e8757567db6d1fb9ad4b92c7dec21e7bf12c Mon Sep 17 00:00:00 2001 From: Sergii Mykyteiek Date: Thu, 7 Oct 2021 15:43:55 +0300 Subject: [PATCH] LT-17: Add swagger definition to stack controllers --- README.md | 3 ++ src/config/app.ts | 2 ++ src/main.ts | 32 ++++++++++++-------- src/modules/stack/stack.controller.spec.ts | 8 ++--- src/modules/stack/stack.controller.ts | 35 +++++++++++++++++++++- src/modules/stack/stack.dto.ts | 12 ++++++++ src/modules/ttl/ttl.controller.ts | 19 ++++++------ src/shared/logger/logger.service.ts | 1 + 8 files changed, 85 insertions(+), 27 deletions(-) diff --git a/README.md b/README.md index 221d29b..59395c7 100644 --- a/README.md +++ b/README.md @@ -10,6 +10,8 @@ Clone this repo. * PORT=3000 * NODE_ENV=dev * TTL_EXPIRATION=30 + * API_SWAGGER_ENABLED=1 + * API_SWAGGER_URL=api-docs ### npm i For installing packages @@ -24,6 +26,7 @@ Run server Running tests ## Available endpoints +You can use all endpoints via Swagger http://localhost:3000/api-docs * Stack data * POST localhost:3000/stack - *add data to stack* * body { "data": any } diff --git a/src/config/app.ts b/src/config/app.ts index 676a38c..dcfc912 100644 --- a/src/config/app.ts +++ b/src/config/app.ts @@ -1,3 +1,5 @@ export const appConfig = { TTL_EXPIRATION: 30, + API_SWAGGER_ENABLED: 1, + API_SWAGGER_URL: 'api-docs', }; diff --git a/src/main.ts b/src/main.ts index 03fb07d..3783d99 100644 --- a/src/main.ts +++ b/src/main.ts @@ -20,24 +20,32 @@ function start() { app.use('/stack', stackRouter); app.use('/ttl', ttlRouter); - app.use('/api-docs', express.static('src/swagger')); - app.use('/api-docs/assets', express.static('node_modules/swagger-ui-dist')); - app.use( swagger.express( - { - definition : { - info : { - title : "TTL-LIFO API" , - version : "1.0" - } , - } - } - ) ); + const apiDocsEnabled = !!Number(config.API_SWAGGER_ENABLED); + if (apiDocsEnabled) { + app.use(`/${config.API_SWAGGER_URL}`, express.static('src/swagger')); + app.use('/api-docs/assets', express.static('node_modules/swagger-ui-dist')); + app.use( + swagger.express({ + definition: { + info: { + title: 'TTL-LIFO API', + version: '1.0', + }, + }, + }), + ); + } app.use(errorMiddleware); app.listen(config.PORT, () => { // eslint-disable-next-line no-console console.log(`Application started on port ${config.PORT}!`); + apiDocsEnabled && + // eslint-disable-next-line no-console + console.log( + `Swagger is listening on http://localhost:${config.PORT}/${config.API_SWAGGER_URL}`, + ); }); } diff --git a/src/modules/stack/stack.controller.spec.ts b/src/modules/stack/stack.controller.spec.ts index 8302611..7a5d7b1 100644 --- a/src/modules/stack/stack.controller.spec.ts +++ b/src/modules/stack/stack.controller.spec.ts @@ -65,7 +65,7 @@ describe('StackController', () => { }); describe('get item from stack and remove it', () => { - it('When stack is empty -> should return 204', async () => { + it('When stack is empty -> should return 204', () => { const body = {}; const mockRequest = { body, @@ -73,7 +73,7 @@ describe('StackController', () => { jest.spyOn(stackService, 'isEmpty').mockImplementation(() => true); jest.spyOn(stackService, 'get').mockImplementation(() => {}); - await stackController.get(mockRequest, mockResponse, mockNextFunction); + stackController.get(mockRequest, mockResponse, mockNextFunction); expect(mockResponse.status).toBeCalledWith(StatusCodes.NO_CONTENT); expect(mockResponse.send).toBeCalledTimes(1); @@ -81,7 +81,7 @@ describe('StackController', () => { expect(stackService.get).not.toBeCalled(); }); - it('When stack is not empty -> should return 200', async () => { + it('When stack is not empty -> should return 200', () => { const body = {}; const mockRequest = { body, @@ -90,7 +90,7 @@ describe('StackController', () => { jest.spyOn(stackService, 'isEmpty').mockImplementation(() => false); jest.spyOn(stackService, 'get').mockImplementation(() => response); - await stackController.get(mockRequest, mockResponse, mockNextFunction); + stackController.get(mockRequest, mockResponse, mockNextFunction); expect(mockResponse.status).toBeCalledWith(StatusCodes.OK_RESPONSE); expect(mockResponse.send).not.toBeCalled(); diff --git a/src/modules/stack/stack.controller.ts b/src/modules/stack/stack.controller.ts index caa0304..b2d423c 100644 --- a/src/modules/stack/stack.controller.ts +++ b/src/modules/stack/stack.controller.ts @@ -1,11 +1,16 @@ import { NextFunction, Request, Response } from 'express'; import { Service } from 'typedi'; +import { ApiOperationGet, ApiOperationPost, ApiPath } from 'swagger-express-ts'; import { StackService } from './stack.service'; import { StackResponse } from './stack.dto'; import { StackValidationService } from './validation'; import { LoggerService } from '../../shared/logger'; import { StatusCodes } from '../../common/constants'; +@ApiPath({ + name: 'stack', + path: '/stack', +}) @Service() export class StackController { constructor( @@ -14,6 +19,26 @@ export class StackController { private readonly logger: LoggerService, ) {} + @ApiOperationPost({ + description: 'Add value to stack', + summary: 'Add value', + parameters: { + body: { + description: 'Item dto', + required: true, + model: 'StackDto', + type: 'object', + }, + }, + responses: { + 200: { + description: 'Value added successfully', + type: 'object', + model: 'StackResponse', + }, + 400: { description: 'Bad request' }, + }, + }) async add(req: Request, res: Response, next: NextFunction): Promise> { try { const errors = await this.validationService.validateBody(req.body); @@ -35,7 +60,15 @@ export class StackController { } } - async get(req: Request, res: Response, next: NextFunction): Promise> { + @ApiOperationGet({ + description: 'Get item the last item', + summary: 'Get item', + responses: { + 200: { description: 'Successfully returned', type: 'object', model: 'StackResponse' }, + 204: { description: 'No content, stack is empty' }, + }, + }) + get(req: Request, res: Response, next: NextFunction): Response { try { if (this.stackService.isEmpty()) { return res.status(StatusCodes.NO_CONTENT).send(); diff --git a/src/modules/stack/stack.dto.ts b/src/modules/stack/stack.dto.ts index ac51c56..4d2d8df 100644 --- a/src/modules/stack/stack.dto.ts +++ b/src/modules/stack/stack.dto.ts @@ -1,8 +1,20 @@ import { IsNotEmpty } from 'class-validator'; +import { ApiModel, ApiModelProperty } from 'swagger-express-ts'; +@ApiModel({ + description: 'StackDto description', +}) export class StackDto { @IsNotEmpty() + @ApiModelProperty({ + description: 'Value of the item', + required: true, + type: 'any', + }) data: any; } +@ApiModel({ + description: 'StackResponse description', +}) export class StackResponse extends StackDto {} diff --git a/src/modules/ttl/ttl.controller.ts b/src/modules/ttl/ttl.controller.ts index c447194..61908aa 100644 --- a/src/modules/ttl/ttl.controller.ts +++ b/src/modules/ttl/ttl.controller.ts @@ -2,7 +2,7 @@ import { NextFunction, Request, Response } from 'express'; import { Service } from 'typedi'; import { ApiPath, ApiOperationPost, ApiOperationGet, ApiOperationDelete } from 'swagger-express-ts'; import { TtlService } from './ttl.service'; -import { TtlResponse, TtlDto } from './ttl.dto'; +import { TtlResponse } from './ttl.dto'; import { TtlValidationService } from './validation'; import { LoggerService } from '../../shared/logger'; import { StatusCodes } from '../../common/constants'; @@ -28,12 +28,12 @@ export class TtlController { required: true, model: 'TtlDto', type: 'object', - } + }, }, responses: { 200: { description: 'Value added successfully', type: 'object', model: 'TtlResponse' }, 400: { description: 'Bad request' }, - 409: { description: 'Conflict exception. Key already exists' } + 409: { description: 'Conflict exception. Key already exists' }, }, }) async add(req: Request, res: Response, next: NextFunction): Promise> { @@ -71,9 +71,9 @@ export class TtlController { key: { required: true, type: 'string', - name: 'key' - } - } + name: 'key', + }, + }, }, responses: { 200: { description: 'Successfully returned', type: 'object', model: 'TtlResponse' }, @@ -89,7 +89,6 @@ export class TtlController { return res.status(StatusCodes.OK_RESPONSE).json(result); } - @ApiOperationDelete({ path: '/{key}', description: 'Remove item by key', @@ -99,9 +98,9 @@ export class TtlController { key: { required: true, type: 'string', - name: 'key' - } - } + name: 'key', + }, + }, }, responses: { 204: { description: 'Success. No content' }, diff --git a/src/shared/logger/logger.service.ts b/src/shared/logger/logger.service.ts index 3e70d11..5d2a486 100644 --- a/src/shared/logger/logger.service.ts +++ b/src/shared/logger/logger.service.ts @@ -4,6 +4,7 @@ import { ILoggerError, LoggerMessage } from './logger.interfaces'; @Service() export class LoggerService implements ILoggerError { error(message: LoggerMessage) { + // eslint-disable-next-line no-console console.log(message); } } -- GitLab