import { Post, Param, Delete, Put, Get, Res, Body, HttpStatus } from "@nestjs/common";
import { ApiResponse, ApiOperation } from "@nestjs/swagger";
import { Response } from 'express';
import { CRUDService } from "./crud.service";
import { Document } from 'mongoose';
export abstract class CRUDController<M extends Document, C, U, T extends CRUDService<M, C, U>> {
constructor ( protected readonly service: T ) { }
@Post()
@ApiOperation( { title: 'Create' } )
@ApiResponse( { status: 201, description: 'Object created.' } )
@ApiResponse( { status: 400, description: 'Validation failed.' } )
async create ( @Body() post: C, @Res() res: Response ): Promise<void> {
const result = await this.service.create( post );
res.status( result ? HttpStatus.OK : HttpStatus.NOT_FOUND ).send( result );
}
@Get()
@ApiOperation( { title: 'Find All' } )
@ApiResponse( { status: 200, description: 'Objects returned.' } )
@ApiResponse( { status: 400, description: 'Validation failed.' } )
async findAll ( @Res() res: Response ): Promise<void> {
const result = await this.service.findAll();
res.status( result ? HttpStatus.OK : HttpStatus.NOT_FOUND ).send( result );
}
@Get( ':id' )
@ApiOperation( { title: 'Find by Id' } )
@ApiResponse( { status: 200, description: 'Object found.' } )
@ApiResponse( { status: 400, description: 'Validation failed.' } )
@ApiResponse( { status: 404, description: 'Object not found.' } )
async findById ( @Param( 'id' ) id: string, @Res() res: Response ): Promise<void> {
const result = await this.service.findById( id );
res.status( result ? HttpStatus.OK : HttpStatus.NOT_FOUND ).send( result );
}
@Put( ':id' )
@ApiOperation( { title: 'Update' } )
@ApiResponse( { status: 200, description: 'Post updated.' } )
@ApiResponse( { status: 400, description: 'Validation failed.' } )
@ApiResponse( { status: 404, description: 'Post not found.' } )
async update ( @Param( 'id' ) id: string, @Body() updatePostDto: U, @Res() res: Response ): Promise<void> {
const result = await this.service.update( id, updatePostDto );
res.status( result ? HttpStatus.OK : HttpStatus.NOT_FOUND ).send( result );
}
@Delete( ':id' )
@ApiOperation( { title: 'Remove' } )
@ApiResponse( { status: 204, description: 'Object removed.' } )
@ApiResponse( { status: 400, description: 'Validation failed.' } )
@ApiResponse( { status: 404, description: 'Object not found.' } )
async remove ( @Param( 'id' ) id: string, @Res() res: Response ): Promise<void> {
const result = await this.service.remove( id );
res.status( result ? HttpStatus.NO_CONTENT : HttpStatus.NOT_FOUND ).send( result );
}
}
This will produce the following error in the console of the browser.
swagger-ui-init.js:30 Uncaught SyntaxError: Unexpected identifier
Here is swagger-ui-init.js:
window.onload = function() {
// Build a system
var url = window.location.search.match(/url=([^&]+)/);
if (url && url.length > 1) {
url = decodeURIComponent(url[1]);
} else {
url = window.location.origin;
}
var options = {
"swaggerDoc": {
"swagger": "2.0",
"info": {
"description": "Bitea API",
"version": "1.0",
"title": "Bitea API"
},
"basePath": "/",
"tags": [],
"schemes": [
"http"
],
"securityDefinitions": {},
"paths": {
"/posts": {
"post": {
"summary": "Create",
"parameters": [
{
"type": function Object() { [native code] },
"name": "Object",
"required": true,
"in": "body",
"schema": {
"$ref": "#/definitions/Object"
}
}
],
"responses": {
"201": {
"description": "Object created."
},
"400": {
"description": "Validation failed."
}
},
"tags": [
"Posts"
],
"produces": [
"application/json"
],
"consumes": [
"application/json"
]
},
"get": {
"summary": "Find All",
"responses": {
"200": {
"description": "Objects returned."
},
"400": {
"description": "Validation failed."
}
},
"tags": [
"Posts"
],
"produces": [
"application/json"
],
"consumes": [
"application/json"
]
}
},
"/posts/{id}": {
"get": {
"summary": "Find by Id",
"parameters": [
{
"type": "string",
"name": "id",
"required": true,
"in": "path"
}
],
"responses": {
"200": {
"description": "Object found."
},
"400": {
"description": "Validation failed."
},
"404": {
"description": "Object not found."
}
},
"tags": [
"Posts"
],
"produces": [
"application/json"
],
"consumes": [
"application/json"
]
},
"put": {
"summary": "Update",
"parameters": [
{
"type": function Object() { [native code] },
"name": "Object",
"required": true,
"in": "body",
"schema": {
"$ref": "#/definitions/Object"
}
},
{
"type": "string",
"name": "id",
"required": true,
"in": "path"
}
],
"responses": {
"200": {
"description": "Post updated."
},
"400": {
"description": "Validation failed."
},
"404": {
"description": "Post not found."
}
},
"tags": [
"Posts"
],
"produces": [
"application/json"
],
"consumes": [
"application/json"
]
},
"delete": {
"summary": "Remove",
"parameters": [
{
"type": "string",
"name": "id",
"required": true,
"in": "path"
}
],
"responses": {
"204": {
"description": "Object removed."
},
"400": {
"description": "Validation failed."
},
"404": {
"description": "Object not found."
}
},
"tags": [
"Posts"
],
"produces": [
"application/json"
],
"consumes": [
"application/json"
]
}
}
},
"definitions": {
"Object": {
"type": "object",
"properties": {}
}
}
},
"customOptions": {}
};
url = options.swaggerUrl || url
var customOptions = options.customOptions
var spec1 = options.swaggerDoc
var swaggerOptions = {
spec: spec1,
url: url,
dom_id: '#swagger-ui',
deepLinking: true,
presets: [
SwaggerUIBundle.presets.apis,
SwaggerUIStandalonePreset
],
plugins: [
SwaggerUIBundle.plugins.DownloadUrl
],
layout: "StandaloneLayout"
}
for (var attrname in customOptions) {
swaggerOptions[attrname] = customOptions[attrname];
}
var ui = SwaggerUIBundle(swaggerOptions)
if (customOptions.oauth) {
ui.initOAuth(customOptions.oauth)
}
if (customOptions.authAction) {
ui.authActions.authorize(customOptions.authAction)
}
window.ui = ui
}
Have the same issue.
What version of swagger/nest do you use?
@kamilmysliwiec Does not work for
"@nestjs/common": "^5.0.0",
"@nestjs/core": "^5.0.0",
"@nestjs/microservices": "^5.0.0",
"@nestjs/swagger": "^2.0.0",
"@nestjs/testing": "^5.0.0",
"@nestjs/websockets": "^5.0.0",
Worked for nest/-5.0.0-rc.2 with nestjs/swagger-1.2.2
Keep in mind that swagger module won't work well with generics - TypeScript doesn't emit any metadata regarding generic types, and thus we cannot reflect those types nor correctly put them into the swagger definition. It's not a Nest issue, but rather a language insufficiency.
swagger-ui-init.js:30 Uncaught SyntaxError: Unexpected identifier
This issue is fixed in 2.0.1. Please, update your package
@kamilmysliwiec so maybe we'll add some additional decorator for class definition that will cover generic output, something like:
@ApiInputOutput(Class)
fe Im using TypeORM, and using generic RestController with methods like:
@Post()
public async update(partial: DataPartial<T>): Promise<T> {
return this.service.update(partial);
}
This produce and empty POST params in swagger, by this, swagger is useless.
Can it be implemented in the form of callbacks, similar to that in class-transformer?
export class SuperCollection<T> {
@Exclude()
private type: Function;
@Type(options => {
return (options.newObject as SuperCollection<T>).type;
})
items: T[];
count: number;
constructor(type: Function) {
this.type = type;
}
}
@kamilmysliwiec
Actually, all this stuff is available already https://docs.nestjs.com/recipes/swagger
@ApiImplicit[...] decorator for input values while @ApiResponse is used for output values
I now have a generic type, and the type attribute of metadata in @ApiModelProperty does not know how to define it.
export class ResultEntity<T> {
@Exclude()
private type: Function;
@ApiModelProperty({ description: "status code" })
readonly code: number = 0;
@ApiModelProperty({ description: "return value" })
@Type(options => {
return (options.newObject as ResultEntity<T>).type;
})
data: T;
constructor(data: T, type: Function) {
this.data = data;
this.type = type;
}
}
@iyangsheng @kamilmysliwiec Same issue here. I've managed to get validation working but not way I can get the generic to print out it's schema into swagger. As a result I don't even get the model registered into swagger. Any workaround ?
Same here, any workaround?
Solution using Mixin:
export type ClassType<T = any> = new (...args: any[]) => T;
interface IPaginated {
limit?: number;
offset?: number;
}
export function PaginatedRequestDto<T extends ClassType>(ResourceCls: T) {
class Paginated extends ResourceCls implements IPaginated {
@ApiModelPropertyOptional({ default: 100 })
public limit?: number;
@ApiModelPropertyOptional({ default: 0 })
public offset?: number;
}
return Paginated;
}
and then use it like this:
class PaginatedSearchRequestDto extends PaginatedRequestDto(SearchRequestDto) {}
I've also verified this with swagger docs shows both definitions from SearchRequestDto and Paginated.
This thread has been automatically locked since there has not been any recent activity after it was closed. Please open a new issue for related bugs.
Most helpful comment
Solution using Mixin:
and then use it like this:
I've also verified this with swagger docs shows both definitions from SearchRequestDto and Paginated.