Skip to content

Commit

Permalink
feat(swagger): add examples to all api params (#280)
Browse files Browse the repository at this point in the history
  • Loading branch information
tamirGer authored Oct 5, 2023
1 parent ac31c65 commit a8df59b
Show file tree
Hide file tree
Showing 10 changed files with 156 additions and 30 deletions.
16 changes: 16 additions & 0 deletions src/app.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import {
ApiOkResponse,
ApiOperation,
ApiProduces,
ApiQuery,
ApiTags,
} from '@nestjs/swagger';
import * as dotT from 'dot';
Expand Down Expand Up @@ -73,6 +74,7 @@ export class AppController {
}

@Get('goto')
@ApiQuery({ name: 'url', example: 'https://google.com', required: true })
@ApiOperation({
description: API_DESC_REDIRECT_REQUEST,
})
Expand All @@ -85,6 +87,17 @@ export class AppController {
}

@Post('metadata')
@ApiProduces('text/plain')
@ApiConsumes('text/plain')
@ApiBody({
type: String,
examples: {
xml_doc: {
summary: 'XML doc',
value: `<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 915 585"><g stroke-width="3.45" fill="none"><path stroke="#000" d="M11.8 11.8h411v411l-411 .01v-411z"/><path stroke="#448" d="M489 11.7h415v411H489v-411z"/></g></svg>`,
},
},
})
@ApiOperation({
description: API_DESC_XML_METADATA,
})
Expand Down Expand Up @@ -120,6 +133,7 @@ export class AppController {
}

@Get('spawn')
@ApiQuery({ name: 'command', example: 'ls -la', required: true })
@ApiOperation({
description: API_DESC_LAUNCH_COMMAND,
})
Expand Down Expand Up @@ -191,6 +205,7 @@ export class AppController {
}

@Get('/v1/userinfo/:email')
@ApiQuery({ name: 'email', example: '[email protected]', required: true })
@UseInterceptors(ClassSerializerInterceptor)
@SerializeOptions({ groups: [BASIC_USER_INFO] })
@ApiOperation({
Expand Down Expand Up @@ -219,6 +234,7 @@ export class AppController {
}

@Get('/v2/userinfo/:email')
@ApiQuery({ name: 'email', example: '[email protected]', required: true })
@UseGuards(AuthGuard)
@JwtType(JwtProcessorType.RSA)
@UseInterceptors(ClassSerializerInterceptor)
Expand Down
5 changes: 3 additions & 2 deletions src/auth/api/login.request.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,15 @@ export enum FormMode {
}

export class LoginRequest {
@ApiProperty()
@ApiProperty({ example: 'john', required: true })
user: string;

@ApiProperty()
@ApiProperty({ example: 'Pa55w0rd', required: true })
password: string;

@ApiProperty({
enum: FormMode,
required: true,
})
op?: string;

Expand Down
91 changes: 77 additions & 14 deletions src/file/file.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,12 @@ import {
Res,
} from '@nestjs/common';
import {
ApiHeader,
ApiInternalServerErrorResponse,
ApiNotFoundResponse,
ApiOkResponse,
ApiOperation,
ApiQuery,
ApiTags,
} from '@nestjs/swagger';
import { W_OK } from 'constants';
Expand Down Expand Up @@ -49,17 +51,24 @@ export class FileController {
}
}

private async loadCPFile(cpBaseUrl: string, path: string){
if (!path.startsWith(cpBaseUrl)){
throw new BadRequestException(`Invalid paramater 'path' ${path}`)
private async loadCPFile(cpBaseUrl: string, path: string) {
if (!path.startsWith(cpBaseUrl)) {
throw new BadRequestException(`Invalid paramater 'path' ${path}`);
}

const file: Stream = await this.fileService.getFile(path);

return file;
}

@Get()
@ApiQuery({
name: 'path',
example: 'config/products/crystals/amethyst.jpg',
required: true,
})
@ApiQuery({ name: 'type', example: 'image/jpg', required: true })
@ApiHeader({ name: 'accept', example: 'image/jpg', required: true })
@ApiOkResponse({
description: 'File read successfully',
})
Expand All @@ -81,15 +90,21 @@ export class FileController {
@Res({ passthrough: true }) res: FastifyReply,
@Headers('accept') acceptHeader: string,
) {

const file: Stream = await this.fileService.getFile(path);
const type = this.getContentType(contentType, acceptHeader)
const type = this.getContentType(contentType, acceptHeader);
res.type(type);

return file;
}

@Get('/google')
@ApiQuery({
name: 'path',
example: 'config/products/crystals/amethyst.jpg',
required: true,
})
@ApiQuery({ name: 'type', example: 'image/jpg', required: true })
@ApiHeader({ name: 'accept', example: 'image/jpg', required: true })
@ApiOkResponse({
description: 'File read successfully',
})
Expand All @@ -111,14 +126,24 @@ export class FileController {
@Res({ passthrough: true }) res: FastifyReply,
@Headers('accept') acceptHeader: string,
) {
const file: Stream = await this.loadCPFile(CloudProvidersMetaData.GOOGLE, path);
const type = this.getContentType(contentType, acceptHeader)
const file: Stream = await this.loadCPFile(
CloudProvidersMetaData.GOOGLE,
path,
);
const type = this.getContentType(contentType, acceptHeader);
res.type(type);

return file;
}

@Get('/aws')
@ApiQuery({
name: 'path',
example: 'config/products/crystals/amethyst.jpg',
required: true,
})
@ApiQuery({ name: 'type', example: 'image/jpg', required: true })
@ApiHeader({ name: 'accept', example: 'image/jpg', required: true })
@ApiOkResponse({
description: 'File read successfully',
})
Expand All @@ -140,14 +165,24 @@ export class FileController {
@Res({ passthrough: true }) res: FastifyReply,
@Headers('accept') acceptHeader: string,
) {
const file: Stream = await this.loadCPFile(CloudProvidersMetaData.AWS, path);
const type = this.getContentType(contentType, acceptHeader)
const file: Stream = await this.loadCPFile(
CloudProvidersMetaData.AWS,
path,
);
const type = this.getContentType(contentType, acceptHeader);
res.type(type);

return file;
}

@Get('/azure')
@ApiQuery({
name: 'path',
example: 'config/products/crystals/amethyst.jpg',
required: true,
})
@ApiQuery({ name: 'type', example: 'image/jpg', required: true })
@ApiHeader({ name: 'accept', example: 'image/jpg', required: true })
@ApiOkResponse({
description: 'File read successfully',
})
Expand All @@ -169,14 +204,24 @@ export class FileController {
@Res({ passthrough: true }) res: FastifyReply,
@Headers('accept') acceptHeader: string,
) {
const file: Stream = await this.loadCPFile(CloudProvidersMetaData.AZURE, path);
const type = this.getContentType(contentType, acceptHeader)
const file: Stream = await this.loadCPFile(
CloudProvidersMetaData.AZURE,
path,
);
const type = this.getContentType(contentType, acceptHeader);
res.type(type);

return file;
}

@Get('/digital_ocean')
@ApiQuery({
name: 'path',
example: 'config/products/crystals/amethyst.jpg',
required: true,
})
@ApiQuery({ name: 'type', example: 'image/jpg', required: true })
@ApiHeader({ name: 'accept', example: 'image/jpg', required: true })
@ApiOkResponse({
description: 'File read successfully',
})
Expand All @@ -198,14 +243,22 @@ export class FileController {
@Res({ passthrough: true }) res: FastifyReply,
@Headers('accept') acceptHeader: string,
) {
const file: Stream = await this.loadCPFile(CloudProvidersMetaData.DIGITAL_OCEAN, path);
const type = this.getContentType(contentType, acceptHeader)
const file: Stream = await this.loadCPFile(
CloudProvidersMetaData.DIGITAL_OCEAN,
path,
);
const type = this.getContentType(contentType, acceptHeader);
res.type(type);

return file;
}

@Delete()
@ApiQuery({
name: 'path',
example: 'config/products/crystals/some_file.jpg',
required: true,
})
@ApiOperation({
description: SWAGGER_DESC_DELETE_FILE,
})
Expand All @@ -226,6 +279,11 @@ export class FileController {
}

@Put('raw')
@ApiQuery({
name: 'path',
example: 'some/path/to/file.png',
required: true,
})
@ApiOperation({
description: SWAGGER_DESC_SAVE_RAW_CONTENT,
})
Expand All @@ -247,6 +305,11 @@ export class FileController {
}

@Get('raw')
@ApiQuery({
name: 'path',
example: 'config/products/crystals/amethyst.jpg',
required: true,
})
@ApiOperation({
description: SWAGGER_DESC_READ_FILE_ON_SERVER,
})
Expand Down
14 changes: 9 additions & 5 deletions src/products/api/ProductDto.ts
Original file line number Diff line number Diff line change
@@ -1,19 +1,23 @@
import { ApiProperty } from '@nestjs/swagger';

export class ProductDto {
@ApiProperty()
@ApiProperty({ example: 'Amethyst', required: true })
name: string;

@ApiProperty()
@ApiProperty({ example: 'Healing', required: true })
category: string;

@ApiProperty()
@ApiProperty({
default:
'/api/file?path=config/products/crystals/amethyst.jpg&type=image/jpg',
required: true,
})
photoUrl: string;

@ApiProperty()
@ApiProperty({ example: 'a violet variety of quartz', required: true })
description: string;

@ApiProperty()
@ApiProperty({ example: 1, required: true })
viewsCount: number;

constructor(params: {
Expand Down
2 changes: 2 additions & 0 deletions src/products/products.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import {
ApiTags,
ApiForbiddenResponse,
ApiInternalServerErrorResponse,
ApiHeader,
} from '@nestjs/swagger';
import { AuthGuard } from '../auth/auth.guard';
import { JwtProcessorType } from '../auth/auth.service';
Expand Down Expand Up @@ -74,6 +75,7 @@ export class ProductsController {
}

@Get('views')
@ApiHeader({ name: 'x-product-name', example: 'Amethyst' })
@ApiOperation({
description: API_DESC_GET_VIEW_PRODUCT,
})
Expand Down
12 changes: 11 additions & 1 deletion src/subscriptions/subscriptions.controller.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
import { Controller, Logger, Post, Query } from '@nestjs/common';
import { ApiCreatedResponse, ApiOperation, ApiTags } from '@nestjs/swagger';
import {
ApiCreatedResponse,
ApiOperation,
ApiQuery,
ApiTags,
} from '@nestjs/swagger';
import { SWAGGER_DESC_CREATE_SUBSCRIPTION } from './subscriptions.controller.swagger.desc';

@Controller('/api/subscriptions')
Expand All @@ -8,6 +13,11 @@ export class SubscriptionsController {
private readonly logger = new Logger(SubscriptionsController.name);

@Post()
@ApiQuery({
name: 'email',
example: '[email protected]',
required: true,
})
@ApiOperation({
description: SWAGGER_DESC_CREATE_SUBSCRIPTION,
})
Expand Down
6 changes: 3 additions & 3 deletions src/testimonials/api/TestimonialDto.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,13 @@ import { ApiProperty } from '@nestjs/swagger';
import { Testimonial } from '../../model/testimonial.entity';

export class TestimonialDto {
@ApiProperty()
@ApiProperty({ example: 'John', required: true })
name: string;

@ApiProperty()
@ApiProperty({ example: 'Doe', required: true })
title: string;

@ApiProperty()
@ApiProperty({ example: "I've broken all the crystals", required: true })
message: string;

public static covertToApi(t: Testimonial): TestimonialDto {
Expand Down
10 changes: 10 additions & 0 deletions src/testimonials/testimonials.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,11 @@ import {
UseGuards,
} from '@nestjs/common';
import {
ApiBody,
ApiForbiddenResponse,
ApiOkResponse,
ApiOperation,
ApiQuery,
ApiTags,
} from '@nestjs/swagger';
import { AuthGuard } from '../auth/auth.guard';
Expand All @@ -34,6 +36,9 @@ export class TestimonialsController {
constructor(private readonly testimonialsService: TestimonialsService) {}

@Post()
@ApiBody({
type: TestimonialDto,
})
@UseGuards(AuthGuard)
@JwtType(JwtProcessorType.RSA)
@ApiOperation({
Expand Down Expand Up @@ -81,6 +86,11 @@ export class TestimonialsController {
}

@Get('count')
@ApiQuery({
name: 'query',
example: 'select count(*) as count from testimonial',
required: true,
})
@Header('content-type', 'text/html')
@ApiOperation({
description: API_DESC_GET_TESTIMONIALS_ON_SQL_QUERY,
Expand Down
Loading

0 comments on commit a8df59b

Please sign in to comment.