From bfabff741d4d7d2373fd130904419950516236b7 Mon Sep 17 00:00:00 2001 From: Inga Date: Sat, 9 Jul 2022 23:04:51 +0200 Subject: [PATCH] configured openapi docs --- nest-cli.json | 5 +- package-lock.json | 121 ++++++++++++++++++++-- package.json | 2 + src/main.ts | 7 ++ src/screenshots/screenshots.controller.ts | 24 +++-- src/screenshots/screenshots.dto.ts | 24 +++++ 6 files changed, 165 insertions(+), 18 deletions(-) create mode 100644 src/screenshots/screenshots.dto.ts diff --git a/nest-cli.json b/nest-cli.json index 2566481..fb0d691 100644 --- a/nest-cli.json +++ b/nest-cli.json @@ -1,5 +1,8 @@ { "$schema": "https://json.schemastore.org/nest-cli", "collection": "@nestjs/schematics", - "sourceRoot": "src" + "sourceRoot": "src", + "compilerOptions": { + "plugins": ["@nestjs/swagger"] + } } diff --git a/package-lock.json b/package-lock.json index aa9112c..5974934 100644 --- a/package-lock.json +++ b/package-lock.json @@ -14,6 +14,8 @@ "@nestjs/config": "^2.2.0", "@nestjs/core": "^9.0.0", "@nestjs/platform-express": "^9.0.0", + "@nestjs/swagger": "^6.0.1", + "class-validator": "^0.13.2", "puppeteer": "^15.3.2", "reflect-metadata": "^0.1.13", "rimraf": "^3.0.2", @@ -1609,6 +1611,25 @@ } } }, + "node_modules/@nestjs/mapped-types": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@nestjs/mapped-types/-/mapped-types-1.1.0.tgz", + "integrity": "sha512-+2kSly4P1QI+9eGt+/uGyPdEG1hVz7nbpqPHWZVYgoqz8eOHljpXPag+UCVRw9zo2XCu4sgNUIGe8Uk0+OvUQg==", + "peerDependencies": { + "@nestjs/common": "^7.0.8 || ^8.0.0 || ^9.0.0", + "class-transformer": "^0.2.0 || ^0.3.0 || ^0.4.0 || ^0.5.0", + "class-validator": "^0.11.1 || ^0.12.0 || ^0.13.0", + "reflect-metadata": "^0.1.12" + }, + "peerDependenciesMeta": { + "class-transformer": { + "optional": true + }, + "class-validator": { + "optional": true + } + } + }, "node_modules/@nestjs/platform-express": { "version": "9.0.1", "resolved": "https://registry.npmjs.org/@nestjs/platform-express/-/platform-express-9.0.1.tgz", @@ -1645,6 +1666,29 @@ "typescript": "^4.3.5" } }, + "node_modules/@nestjs/swagger": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/@nestjs/swagger/-/swagger-6.0.1.tgz", + "integrity": "sha512-rNpncETn+twpf3OPUtGO0MssEOd0cEqiklj2TZv+kAU9gmIWtC/p7Hf1hWbSp9C/l8vF1TnquK80oOaS2G0bAA==", + "dependencies": { + "@nestjs/mapped-types": "1.1.0", + "js-yaml": "4.1.0", + "lodash": "4.17.21", + "path-to-regexp": "3.2.0", + "swagger-ui-dist": "4.12.0" + }, + "peerDependencies": { + "@fastify/static": "^6.0.0", + "@nestjs/common": "^9.0.0", + "@nestjs/core": "^9.0.0", + "reflect-metadata": "^0.1.12" + }, + "peerDependenciesMeta": { + "@fastify/static": { + "optional": true + } + } + }, "node_modules/@nestjs/testing": { "version": "9.0.1", "resolved": "https://registry.npmjs.org/@nestjs/testing/-/testing-9.0.1.tgz", @@ -2623,8 +2667,7 @@ "node_modules/argparse": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "dev": true + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==" }, "node_modules/array-flatten": { "version": "1.1.1", @@ -3123,6 +3166,15 @@ "integrity": "sha512-cOU9usZw8/dXIXKtwa8pM0OTJQuJkxMN6w30csNRUerHfeQ5R6U3kkU/FtJeIf3M202OHfY2U8ccInBG7/xogA==", "dev": true }, + "node_modules/class-validator": { + "version": "0.13.2", + "resolved": "https://registry.npmjs.org/class-validator/-/class-validator-0.13.2.tgz", + "integrity": "sha512-yBUcQy07FPlGzUjoLuUfIOXzgynnQPPruyK1Ge2B74k9ROwnle1E+NxLWnUv5OLU8hA/qL5leAE9XnXq3byaBw==", + "dependencies": { + "libphonenumber-js": "^1.9.43", + "validator": "^13.7.0" + } + }, "node_modules/cli-cursor": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", @@ -6063,7 +6115,6 @@ "version": "4.1.0", "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", - "dev": true, "dependencies": { "argparse": "^2.0.1" }, @@ -6162,6 +6213,11 @@ "node": ">= 0.8.0" } }, + "node_modules/libphonenumber-js": { + "version": "1.10.7", + "resolved": "https://registry.npmjs.org/libphonenumber-js/-/libphonenumber-js-1.10.7.tgz", + "integrity": "sha512-jZXLCCWMe1b/HXkjiLeYt2JsytZMcqH26jLFIdzFDFF0xvSUWrYKyvPlyPG+XJzEyKUFbcZxLdWGMwQsWaHDxQ==" + }, "node_modules/lines-and-columns": { "version": "1.2.4", "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", @@ -7938,6 +7994,11 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/swagger-ui-dist": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/swagger-ui-dist/-/swagger-ui-dist-4.12.0.tgz", + "integrity": "sha512-B0Iy2ueXtbByE6OOyHTi3lFQkpPi/L7kFOKFeKTr44za7dJIELa9kzaca6GkndCgpK1QTjArnoXG+aUy0XQp1w==" + }, "node_modules/symbol-observable": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-4.0.0.tgz", @@ -8584,6 +8645,14 @@ "node": ">=10.12.0" } }, + "node_modules/validator": { + "version": "13.7.0", + "resolved": "https://registry.npmjs.org/validator/-/validator-13.7.0.tgz", + "integrity": "sha512-nYXQLCBkpJ8X6ltALua9dRrZDHVYxjJ1wgskNt1lH9fzGjs3tgojGSCBjmEPwkWS1y29+DrizMTW19Pr9uB2nw==", + "engines": { + "node": ">= 0.10" + } + }, "node_modules/vary": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", @@ -10095,6 +10164,12 @@ "uuid": "8.3.2" } }, + "@nestjs/mapped-types": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@nestjs/mapped-types/-/mapped-types-1.1.0.tgz", + "integrity": "sha512-+2kSly4P1QI+9eGt+/uGyPdEG1hVz7nbpqPHWZVYgoqz8eOHljpXPag+UCVRw9zo2XCu4sgNUIGe8Uk0+OvUQg==", + "requires": {} + }, "@nestjs/platform-express": { "version": "9.0.1", "resolved": "https://registry.npmjs.org/@nestjs/platform-express/-/platform-express-9.0.1.tgz", @@ -10120,6 +10195,18 @@ "pluralize": "8.0.0" } }, + "@nestjs/swagger": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/@nestjs/swagger/-/swagger-6.0.1.tgz", + "integrity": "sha512-rNpncETn+twpf3OPUtGO0MssEOd0cEqiklj2TZv+kAU9gmIWtC/p7Hf1hWbSp9C/l8vF1TnquK80oOaS2G0bAA==", + "requires": { + "@nestjs/mapped-types": "1.1.0", + "js-yaml": "4.1.0", + "lodash": "4.17.21", + "path-to-regexp": "3.2.0", + "swagger-ui-dist": "4.12.0" + } + }, "@nestjs/testing": { "version": "9.0.1", "resolved": "https://registry.npmjs.org/@nestjs/testing/-/testing-9.0.1.tgz", @@ -10915,8 +11002,7 @@ "argparse": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "dev": true + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==" }, "array-flatten": { "version": "1.1.1", @@ -11280,6 +11366,15 @@ "integrity": "sha512-cOU9usZw8/dXIXKtwa8pM0OTJQuJkxMN6w30csNRUerHfeQ5R6U3kkU/FtJeIf3M202OHfY2U8ccInBG7/xogA==", "dev": true }, + "class-validator": { + "version": "0.13.2", + "resolved": "https://registry.npmjs.org/class-validator/-/class-validator-0.13.2.tgz", + "integrity": "sha512-yBUcQy07FPlGzUjoLuUfIOXzgynnQPPruyK1Ge2B74k9ROwnle1E+NxLWnUv5OLU8hA/qL5leAE9XnXq3byaBw==", + "requires": { + "libphonenumber-js": "^1.9.43", + "validator": "^13.7.0" + } + }, "cli-cursor": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", @@ -13501,7 +13596,6 @@ "version": "4.1.0", "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", - "dev": true, "requires": { "argparse": "^2.0.1" } @@ -13574,6 +13668,11 @@ "type-check": "~0.4.0" } }, + "libphonenumber-js": { + "version": "1.10.7", + "resolved": "https://registry.npmjs.org/libphonenumber-js/-/libphonenumber-js-1.10.7.tgz", + "integrity": "sha512-jZXLCCWMe1b/HXkjiLeYt2JsytZMcqH26jLFIdzFDFF0xvSUWrYKyvPlyPG+XJzEyKUFbcZxLdWGMwQsWaHDxQ==" + }, "lines-and-columns": { "version": "1.2.4", "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", @@ -14897,6 +14996,11 @@ "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", "dev": true }, + "swagger-ui-dist": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/swagger-ui-dist/-/swagger-ui-dist-4.12.0.tgz", + "integrity": "sha512-B0Iy2ueXtbByE6OOyHTi3lFQkpPi/L7kFOKFeKTr44za7dJIELa9kzaca6GkndCgpK1QTjArnoXG+aUy0XQp1w==" + }, "symbol-observable": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-4.0.0.tgz", @@ -15353,6 +15457,11 @@ "convert-source-map": "^1.6.0" } }, + "validator": { + "version": "13.7.0", + "resolved": "https://registry.npmjs.org/validator/-/validator-13.7.0.tgz", + "integrity": "sha512-nYXQLCBkpJ8X6ltALua9dRrZDHVYxjJ1wgskNt1lH9fzGjs3tgojGSCBjmEPwkWS1y29+DrizMTW19Pr9uB2nw==" + }, "vary": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", diff --git a/package.json b/package.json index f2a6d68..65fe43d 100644 --- a/package.json +++ b/package.json @@ -26,6 +26,8 @@ "@nestjs/config": "^2.2.0", "@nestjs/core": "^9.0.0", "@nestjs/platform-express": "^9.0.0", + "@nestjs/swagger": "^6.0.1", + "class-validator": "^0.13.2", "puppeteer": "^15.3.2", "reflect-metadata": "^0.1.13", "rimraf": "^3.0.2", diff --git a/src/main.ts b/src/main.ts index 13cad38..f39fb80 100644 --- a/src/main.ts +++ b/src/main.ts @@ -1,8 +1,15 @@ import { NestFactory } from '@nestjs/core'; +import { SwaggerModule, DocumentBuilder } from '@nestjs/swagger'; import { AppModule } from './app.module'; async function bootstrap() { const app = await NestFactory.create(AppModule); + + const config = new DocumentBuilder().build(); + const document = SwaggerModule.createDocument(app, config); + SwaggerModule.setup('api', app, document); + await app.listen(3000); + console.log(`Application is running on: ${await app.getUrl()}`); } bootstrap(); diff --git a/src/screenshots/screenshots.controller.ts b/src/screenshots/screenshots.controller.ts index 886b867..c2acffd 100644 --- a/src/screenshots/screenshots.controller.ts +++ b/src/screenshots/screenshots.controller.ts @@ -1,26 +1,27 @@ import { InjectQueue } from '@nestjs/bull'; import { Body, Controller, Get, NotFoundException, Param, Post } from '@nestjs/common'; +import { ApiResponse } from '@nestjs/swagger'; +import { CreateJobRequestDto, GetJobResponseDto, JobStatusDto } from './screenshots.dto' import { QUEUE_NAME, ScreenshotsQueue } from './shared'; -type CreateScreenshotJobDto = { - pageUrl: URL, - imageType: 'jpeg' | 'png' -} - @Controller('screenshots') export class ScreenshotsController { constructor(@InjectQueue(QUEUE_NAME) private readonly screenshotsQueue: ScreenshotsQueue) {} @Post() - async createScreenshotJob(@Body() createScreenshotJobDto: CreateScreenshotJobDto) { - const result = await this.screenshotsQueue.add(createScreenshotJobDto) + async createScreenshotJob(@Body() createScreenshotJobDto: CreateJobRequestDto): Promise<{ jobId: string }> { + const result = await this.screenshotsQueue.add({ + pageUrl: new URL(createScreenshotJobDto.pageUrl), + imageType: createScreenshotJobDto.imageType, + }) return { jobId: result.id.toString() } } @Get(':id') - async getJob(@Param('id') id: string) { + @ApiResponse({ status: 404 }) + async getJob(@Param('id') id: string): Promise { const jobInfo = await this.screenshotsQueue.getJob(id) if (!jobInfo) { @@ -29,15 +30,16 @@ export class ScreenshotsController { switch (await jobInfo.getState()) { case 'completed': - return { status: 'completed' } + return { status: JobStatusDto.Completed } case 'failed': - return { status: 'failed' } + return { status: JobStatusDto.Failed } default: - return { status: 'queued' } + return { status: JobStatusDto.Queued } } } @Get(':id/result') + @ApiResponse({ status: 404 }) async getScreenshot(@Param('id') id: string) { const jobInfo = await this.screenshotsQueue.getJob(id) diff --git a/src/screenshots/screenshots.dto.ts b/src/screenshots/screenshots.dto.ts new file mode 100644 index 0000000..f8fbb1e --- /dev/null +++ b/src/screenshots/screenshots.dto.ts @@ -0,0 +1,24 @@ +import { IsEnum, IsString, IsUrl } from "class-validator"; + +enum ImageTypeDto { + Jpeg = 'jpeg', + Png = 'png', +} + +export enum JobStatusDto { + Completed = 'completed', + Failed = 'failed', + Queued = 'queued', +} + +export class CreateJobRequestDto { + @IsUrl() + pageUrl!: string; + + @IsEnum(ImageTypeDto) + imageType!: ImageTypeDto +} + +export class GetJobResponseDto { + status!: JobStatusDto +} \ No newline at end of file