diff --git a/packages/swagger/src/swaggerExplorer.ts b/packages/swagger/src/swaggerExplorer.ts index 530988a58e36..82328ff6e8c6 100644 --- a/packages/swagger/src/swaggerExplorer.ts +++ b/packages/swagger/src/swaggerExplorer.ts @@ -378,7 +378,9 @@ export class SwaggerExplorer { operMeta?.metadata?.description, webRouter.description ), - operationId: this.getOperationId(target.name, webRouter), + operationId: + operMeta?.metadata?.operationId || + this.getOperationId(target.name, webRouter), tags: operMeta?.metadata?.tags || [], }; if (operMeta?.metadata?.deprecated != null) { @@ -402,14 +404,83 @@ export class SwaggerExplorer { item.propertyName === webRouter.method ); + // set params information from @ApiQuery() to parameters + for (const param of params) { + // rebuild query param to swagger format + if (!param.metadata.schema !== undefined) { + param.metadata.schema = {}; + if (param.metadata.type) { + param.metadata.schema['type'] = param.metadata.type; + delete param.metadata.type; + } + if (param.metadata.isArray !== undefined) { + param.metadata.schema['items'] = { + type: param.metadata.schema['type'], + }; + param.metadata.schema['type'] = 'array'; + delete param.metadata.isArray; + } + if (param.metadata.enum !== undefined) { + param.metadata.schema.enum = param.metadata.enum; + delete param.metadata.enum; + } + } else { + // if schema is defined, then type is not needed + delete param.metadata.type; + delete param.metadata.isArray; + delete param.metadata.enum; + } + + const p = param.metadata; + p.schema = this.formatType(param.metadata.schema); + + if (p.in === 'query' || p.in === 'path' || p.in === 'header') { + parameters.push(p); + } else if (p.in === 'body') { + p.content = p.content ?? {}; + if (Object.keys(p.content).length === 0) { + p.content[p.contentType || 'application/json'] = p.content[ + p.contentType || 'application/json' + ] ?? { + schema: p.schema, + }; + } + + // format schema + for (const key in p.content) { + p.content[key].schema = this.formatType(p.content[key].schema); + } + + // if requestBody is already set, skip + opts[webRouter.requestMethod].requestBody = + opts[webRouter.requestMethod].requestBody ?? {}; + opts[webRouter.requestMethod].requestBody.description = + opts[webRouter.requestMethod].requestBody.description ?? + p.description; + opts[webRouter.requestMethod].requestBody.content = + opts[webRouter.requestMethod].requestBody.content ?? p.content; + opts[webRouter.requestMethod].requestBody.required = + opts[webRouter.requestMethod].requestBody.required ?? p.required; + } + } + for (const arg of args) { const currentType = types[arg.parameterIndex]; const p: any = { - name: arg?.metadata?.propertyData ?? '', + name: arg?.metadata?.propertyData, in: convertTypeToString(arg.metadata?.type), required: false, }; + const existsParam = parameters.find(item => { + return item.name === arg?.metadata?.propertyData && item.in === p.in; + }); + + // if exists same param from @ApiQuery and other decorator, just skip + if (existsParam) { + continue; + } + if (p.in === 'path') { p.required = true; @@ -425,12 +496,11 @@ export class SwaggerExplorer { // 如果@Query()装饰的 是一个对象,则把该对象的子属性作为多个@Query参数 const schema = this.documentBuilder.getSchema(currentType.name); Object.keys(schema.properties).forEach(pName => { - const ppt: any = schema.properties[pName]; const pp = { name: pName, in: p.in, + schema: schema.properties[pName], }; - this.parseFromParamsToP({ metadata: ppt }, pp); parameters.push(pp); }); continue; @@ -440,20 +510,22 @@ export class SwaggerExplorer { }; } } else { + if (!p.name) { + continue; + } p.schema = { type: convertSchemaType(currentType?.name ?? currentType), }; } - this.parseFromParamsToP( - params[params.length - 1 - arg.parameterIndex], - p - ); - if (p.in === 'body') { if (webRouter.requestMethod === RequestMethod.GET) { continue; } + // if requestBody is already set, skip + if (opts[webRouter.requestMethod].requestBody) { + continue; + } // 这里兼容一下 @File()、@Files()、@Fields() 装饰器 if (arg.metadata?.type === RouteParamTypes.FILESSTREAM) { p.schema = { @@ -631,7 +703,7 @@ export class SwaggerExplorer { * @param params * @param p */ - private parseFromParamsToP(paramMeta: any, p: any) { + protected parseFromParamsToP(paramMeta: any, p: any) { if (paramMeta) { const param = paramMeta.metadata; @@ -1157,7 +1229,7 @@ function convertSchemaType(value) { case 'String': return 'string'; default: - return value; + return 'object'; } } diff --git a/packages/swagger/test/__snapshots__/parser.test.ts.snap b/packages/swagger/test/__snapshots__/parser.test.ts.snap index af18d084fa3c..957b2fa832da 100644 --- a/packages/swagger/test/__snapshots__/parser.test.ts.snap +++ b/packages/swagger/test/__snapshots__/parser.test.ts.snap @@ -11,12 +11,12 @@ exports[`/test/parser.test.ts should fix issue#2286 with array example 1`] = ` "items": { "$ref": "#/components/schemas/Catd", }, + "required": false, "type": "array", }, "name": { "description": "The name of the Cat", "example": "Kitty", - "format": undefined, "type": "string", }, }, @@ -27,19 +27,16 @@ exports[`/test/parser.test.ts should fix issue#2286 with array example 1`] = ` "aged": { "description": "The age of the Cat", "example": 1, - "format": undefined, "type": "number", }, "breedd": { "description": "The breed of the Cat", "example": "Maine Coon", - "format": undefined, "type": "string", }, "named": { "description": "The name of the Cat", "example": "Kitty", - "format": undefined, "type": "string", }, }, @@ -164,6 +161,20 @@ exports[`test @ApiBody should test ApiBody 1`] = ` "description": undefined, "operationId": "apicontroller_updateuser", "parameters": [], + "requestBody": { + "content": { + "application/json": { + "schema": { + "items": { + "$ref": "#/components/schemas/Cat", + }, + "type": "array", + }, + }, + }, + "description": "hello fields", + "required": true, + }, "responses": { "200": { "description": "OK", @@ -221,6 +232,256 @@ exports[`test @ApiBody should test ApiBody with array 1`] = ` "description": undefined, "operationId": "apicontroller_updateuser", "parameters": [], + "requestBody": { + "content": { + "application/json": { + "schema": { + "items": { + "$ref": "#/components/schemas/Cat", + }, + "type": "array", + }, + }, + }, + "description": undefined, + "required": true, + }, + "responses": { + "200": { + "description": "OK", + }, + }, + "summary": undefined, + "tags": [ + "api", + ], + }, + }, + }, + "servers": [], + "tags": [ + { + "description": "api", + "externalDocs": undefined, + "name": "api", + }, + ], +} +`; + +exports[`test @ApiBody should test ApiBody with example 1`] = ` +{ + "components": { + "schemas": { + "Cat": { + "properties": { + "age": { + "description": "The age of the Cat", + "example": 1, + "type": "number", + }, + "name": { + "description": "The name of the Cat", + "example": "Kitty", + "type": "string", + }, + }, + "type": "object", + }, + }, + }, + "info": { + "contact": {}, + "description": "", + "title": "", + "version": "1.0.0", + }, + "openapi": "3.0.1", + "paths": { + "/api/update_user": { + "post": { + "description": undefined, + "operationId": "apicontroller_updateuser", + "parameters": [], + "requestBody": { + "content": { + "application/json": { + "example": "Fluffy", + "schema": { + "items": { + "$ref": "#/components/schemas/Cat", + }, + "type": "array", + }, + }, + }, + "description": undefined, + "required": true, + }, + "responses": { + "200": { + "description": "OK", + }, + }, + "summary": undefined, + "tags": [ + "api", + ], + }, + }, + }, + "servers": [], + "tags": [ + { + "description": "api", + "externalDocs": undefined, + "name": "api", + }, + ], +} +`; + +exports[`test @ApiBody should test ApiBody with examples 1`] = ` +{ + "components": { + "schemas": { + "Cat": { + "properties": { + "age": { + "description": "The age of the Cat", + "example": 1, + "type": "number", + }, + "name": { + "description": "The name of the Cat", + "example": "Kitty", + "type": "string", + }, + }, + "type": "object", + }, + }, + }, + "info": { + "contact": {}, + "description": "", + "title": "", + "version": "1.0.0", + }, + "openapi": "3.0.1", + "paths": { + "/api/update_user": { + "post": { + "description": undefined, + "operationId": "apicontroller_updateuser", + "parameters": [], + "requestBody": { + "content": { + "application/json": { + "examples": { + "example1": { + "summary": "An example of a cat", + }, + "example2": { + "$ref": "#/components/examples/hamster", + }, + }, + "schema": { + "items": { + "$ref": "#/components/schemas/Cat", + }, + "type": "array", + }, + }, + }, + "description": undefined, + "required": true, + }, + "responses": { + "200": { + "description": "OK", + }, + }, + "summary": undefined, + "tags": [ + "api", + ], + }, + }, + }, + "servers": [], + "tags": [ + { + "description": "api", + "externalDocs": undefined, + "name": "api", + }, + ], +} +`; + +exports[`test @ApiBody should test ApiBody with formData 1`] = ` +{ + "components": { + "schemas": { + "Cat": { + "properties": { + "age": { + "description": "The age of the Cat", + "example": 1, + "type": "number", + }, + "name": { + "description": "The name of the Cat", + "example": "Kitty", + "type": "string", + }, + }, + "type": "object", + }, + }, + }, + "info": { + "contact": {}, + "description": "", + "title": "", + "version": "1.0.0", + }, + "openapi": "3.0.1", + "paths": { + "/api/update_user": { + "post": { + "description": undefined, + "operationId": "apicontroller_updateuser", + "parameters": [], + "requestBody": { + "content": { + "application/x-www-form-urlencoded:": { + "encoding": { + "color": { + "explode": false, + "style": "form", + }, + }, + "schema": { + "properties": { + "fav_number": { + "type": "integer", + }, + "name": { + "type": "string", + }, + }, + "required": [ + "name", + ], + "type": "object", + }, + }, + }, + "description": undefined, + "required": true, + }, "responses": { "200": { "description": "OK", @@ -504,32 +765,190 @@ exports[`test @ApiHeaders and @ApiHeader should test ApiHeaders in class and met } `; -exports[`test @ApiProperty should parse base type 1`] = ` +exports[`test @ApiOperation should test custom operationId 1`] = ` { - "properties": { - "breeds": { - "description": "The name of the Catage", - "example": [ - "1", - ], - "items": { - "type": "string", + "components": {}, + "info": { + "contact": {}, + "description": "", + "title": "", + "version": "1.0.0", + }, + "openapi": "3.0.1", + "paths": { + "/api/update_user": { + "post": { + "description": undefined, + "operationId": "updateUser", + "parameters": [], + "responses": { + "200": { + "description": "OK", + }, + }, + "summary": undefined, + "tags": [ + "api", + ], }, - "nullable": true, - "type": "array", - "uniqueItems": true, }, }, - "type": "object", + "servers": [], + "tags": [ + { + "description": "api", + "externalDocs": undefined, + "name": "api", + }, + ], } `; -exports[`test @ApiProperty should parse extends base 1`] = ` +exports[`test @ApiOperation should test deprecated 1`] = ` { - "properties": { - "a": { - "type": "number", - }, + "components": {}, + "info": { + "contact": {}, + "description": "", + "title": "", + "version": "1.0.0", + }, + "openapi": "3.0.1", + "paths": { + "/api/update_user": { + "post": { + "deprecated": true, + "description": undefined, + "operationId": "apicontroller_updateuser", + "parameters": [], + "responses": { + "200": { + "description": "OK", + }, + }, + "summary": undefined, + "tags": [ + "api", + ], + }, + }, + }, + "servers": [], + "tags": [ + { + "description": "api", + "externalDocs": undefined, + "name": "api", + }, + ], +} +`; + +exports[`test @ApiOperation should test summary and description 1`] = ` +{ + "components": {}, + "info": { + "contact": {}, + "description": "", + "title": "", + "version": "1.0.0", + }, + "openapi": "3.0.1", + "paths": { + "/api/update_user": { + "post": { + "description": "update user description", + "operationId": "apicontroller_updateuser", + "parameters": [], + "responses": { + "200": { + "description": "OK", + }, + }, + "summary": "update user", + "tags": [ + "api", + ], + }, + }, + }, + "servers": [], + "tags": [ + { + "description": "api", + "externalDocs": undefined, + "name": "api", + }, + ], +} +`; + +exports[`test @ApiOperation should test tags 1`] = ` +{ + "components": {}, + "info": { + "contact": {}, + "description": "", + "title": "", + "version": "1.0.0", + }, + "openapi": "3.0.1", + "paths": { + "/api/update_user": { + "post": { + "description": undefined, + "operationId": "apicontroller_updateuser", + "parameters": [], + "responses": { + "200": { + "description": "OK", + }, + }, + "summary": undefined, + "tags": [ + "tag1", + "tag2", + ], + }, + }, + }, + "servers": [], + "tags": [ + { + "description": "api", + "externalDocs": undefined, + "name": "api", + }, + ], +} +`; + +exports[`test @ApiProperty should parse base type 1`] = ` +{ + "properties": { + "breeds": { + "description": "The name of the Catage", + "example": [ + "1", + ], + "items": { + "type": "string", + }, + "nullable": true, + "type": "array", + "uniqueItems": true, + }, + }, + "type": "object", +} +`; + +exports[`test @ApiProperty should parse extends base 1`] = ` +{ + "properties": { + "a": { + "type": "number", + }, "created_at": { "format": "date-time", "type": "string", @@ -878,6 +1297,470 @@ exports[`test @ApiProperty should test specify type 1`] = ` } `; +exports[`test @ApiQuery should get array type from DTO 1`] = ` +{ + "components": { + "schemas": { + "UserDTO": { + "properties": { + "id": { + "description": "The uid of the user", + "type": "number", + }, + "name": { + "description": "The name of the user", + "type": "string", + }, + }, + "type": "object", + }, + }, + }, + "info": { + "contact": {}, + "description": "", + "title": "", + "version": "1.0.0", + }, + "openapi": "3.0.1", + "paths": { + "/api/get_user": { + "get": { + "description": undefined, + "operationId": "apicontroller_getuser", + "parameters": [ + { + "in": "query", + "name": "user", + "required": true, + "schema": { + "items": { + "$ref": "#/components/schemas/UserDTO", + }, + "type": "array", + }, + }, + ], + "responses": { + "200": { + "description": "OK", + }, + }, + "summary": undefined, + "tags": [ + "api", + ], + }, + }, + }, + "servers": [], + "tags": [ + { + "description": "api", + "externalDocs": undefined, + "name": "api", + }, + ], +} +`; + +exports[`test @ApiQuery should get base type from @query 1`] = ` +{ + "components": {}, + "info": { + "contact": {}, + "description": "", + "title": "", + "version": "1.0.0", + }, + "openapi": "3.0.1", + "paths": { + "/api/get_user": { + "get": { + "description": undefined, + "operationId": "apicontroller_getuser", + "parameters": [ + { + "in": "query", + "name": "list", + "required": false, + "schema": { + "type": "object", + }, + }, + { + "in": "query", + "name": "flag", + "required": false, + "schema": { + "type": "boolean", + }, + }, + { + "in": "query", + "name": "id", + "required": false, + "schema": { + "type": "number", + }, + }, + { + "in": "query", + "name": "data", + "required": false, + "schema": { + "type": "string", + }, + }, + ], + "responses": { + "200": { + "description": "OK", + }, + }, + "summary": undefined, + "tags": [ + "api", + ], + }, + }, + }, + "servers": [], + "tags": [ + { + "description": "api", + "externalDocs": undefined, + "name": "api", + }, + ], +} +`; + +exports[`test @ApiQuery should get class type from DTO 1`] = ` +{ + "components": { + "schemas": { + "UserDTO": { + "properties": { + "id": { + "description": "The uid of the user", + "type": "number", + }, + "name": { + "description": "The name of the user", + "type": "string", + }, + }, + "type": "object", + }, + }, + }, + "info": { + "contact": {}, + "description": "", + "title": "", + "version": "1.0.0", + }, + "openapi": "3.0.1", + "paths": { + "/api/get_user": { + "get": { + "description": undefined, + "operationId": "apicontroller_getuser", + "parameters": [ + { + "in": "query", + "name": "name", + "schema": { + "description": "The name of the user", + "type": "string", + }, + }, + { + "in": "query", + "name": "id", + "schema": { + "description": "The uid of the user", + "type": "number", + }, + }, + ], + "responses": { + "200": { + "description": "OK", + }, + }, + "summary": undefined, + "tags": [ + "api", + ], + }, + }, + }, + "servers": [], + "tags": [ + { + "description": "api", + "externalDocs": undefined, + "name": "api", + }, + ], +} +`; + +exports[`test @ApiQuery should get enum type 1`] = ` +{ + "components": {}, + "info": { + "contact": {}, + "description": "", + "title": "", + "version": "1.0.0", + }, + "openapi": "3.0.1", + "paths": { + "/api/get_user": { + "get": { + "description": undefined, + "operationId": "apicontroller_getuser", + "parameters": [ + { + "in": "query", + "name": "status", + "required": true, + "schema": { + "enum": [ + "active", + "inactive", + ], + "type": "string", + }, + }, + ], + "responses": { + "200": { + "description": "OK", + }, + }, + "summary": undefined, + "tags": [ + "api", + ], + }, + }, + }, + "servers": [], + "tags": [ + { + "description": "api", + "externalDocs": undefined, + "name": "api", + }, + ], +} +`; + +exports[`test @ApiQuery should test merge @query and @ApiQuery 1`] = ` +{ + "components": {}, + "info": { + "contact": {}, + "description": "", + "title": "", + "version": "1.0.0", + }, + "openapi": "3.0.1", + "paths": { + "/api/get_user": { + "get": { + "description": undefined, + "operationId": "apicontroller_getuser", + "parameters": [ + { + "description": "The name of the user", + "in": "query", + "name": "data", + "required": false, + "schema": { + "type": "string", + }, + }, + { + "in": "query", + "name": "id", + "required": false, + "schema": { + "type": "object", + }, + }, + ], + "responses": { + "200": { + "description": "OK", + }, + }, + "summary": undefined, + "tags": [ + "api", + ], + }, + }, + }, + "servers": [], + "tags": [ + { + "description": "api", + "externalDocs": undefined, + "name": "api", + }, + ], +} +`; + +exports[`test @ApiQuery should test with @query decorator and @ApiQuery 1`] = ` +{ + "components": {}, + "info": { + "contact": {}, + "description": "", + "title": "", + "version": "1.0.0", + }, + "openapi": "3.0.1", + "paths": { + "/api/get_user": { + "get": { + "description": undefined, + "operationId": "apicontroller_getuser", + "parameters": [ + { + "in": "query", + "name": "id", + "required": true, + "schema": { + "type": "number", + }, + }, + { + "description": "The name of the user", + "in": "query", + "name": "data", + "required": true, + "schema": { + "type": "string", + }, + }, + ], + "responses": { + "200": { + "description": "OK", + }, + }, + "summary": undefined, + "tags": [ + "api", + ], + }, + }, + }, + "servers": [], + "tags": [ + { + "description": "api", + "externalDocs": undefined, + "name": "api", + }, + ], +} +`; + +exports[`test @ApiQuery should test with @query decorator and any type 1`] = ` +{ + "components": {}, + "info": { + "contact": {}, + "description": "", + "title": "", + "version": "1.0.0", + }, + "openapi": "3.0.1", + "paths": { + "/api/get_user": { + "get": { + "description": undefined, + "operationId": "apicontroller_getuser", + "parameters": [], + "responses": { + "200": { + "description": "OK", + }, + }, + "summary": undefined, + "tags": [ + "api", + ], + }, + }, + }, + "servers": [], + "tags": [ + { + "description": "api", + "externalDocs": undefined, + "name": "api", + }, + ], +} +`; + +exports[`test @ApiQuery should test with @query decorator and fixed name 1`] = ` +{ + "components": {}, + "info": { + "contact": {}, + "description": "", + "title": "", + "version": "1.0.0", + }, + "openapi": "3.0.1", + "paths": { + "/api/get_user": { + "get": { + "description": undefined, + "operationId": "apicontroller_getuser", + "parameters": [ + { + "in": "query", + "name": "data", + "required": false, + "schema": { + "type": "object", + }, + }, + ], + "responses": { + "200": { + "description": "OK", + }, + }, + "summary": undefined, + "tags": [ + "api", + ], + }, + }, + }, + "servers": [], + "tags": [ + { + "description": "api", + "externalDocs": undefined, + "name": "api", + }, + ], +} +`; + exports[`test @ApiResponse should test @ApiCreatedResponse with type 1`] = ` { "components": { diff --git a/packages/swagger/test/parser.test.ts b/packages/swagger/test/parser.test.ts index b2cdd635704c..2361a587d77b 100644 --- a/packages/swagger/test/parser.test.ts +++ b/packages/swagger/test/parser.test.ts @@ -28,7 +28,7 @@ import { ApiOperation, ApiPayloadTooLargeResponse, ApiPreconditionFailedResponse, - ApiProperty, + ApiProperty, ApiQuery, ApiRequestTimeoutResponse, ApiResponse, ApiSecurity, @@ -406,6 +406,135 @@ describe('test @ApiBody', () => { explorer.generatePath(APIController); expect(explorer.getData()).toMatchSnapshot(); }); + + it('should test ApiBody with example', () => { + class Cat { + @ApiProperty({ example: 'Kitty', description: 'The name of the Cat' }) + name: string; + + @ApiProperty({ example: 1, description: 'The age of the Cat' }) + age: number; + } + + @Controller('/api') + @ApiExtraModel(Cat) + class APIController { + @Post('/update_user') + @ApiBody({ + content: { + 'application/json': { + schema: { + type: 'array', + items: { + $ref: getSchemaPath(Cat), + }, + }, + example: 'Fluffy', + }, + }, + }) + async updateUser() { + // ... + } + } + + const explorer = new CustomSwaggerExplorer(); + explorer.generatePath(APIController); + expect(explorer.getData()).toMatchSnapshot(); + }); + + it('should test ApiBody with examples', () => { + class Cat { + @ApiProperty({ example: 'Kitty', description: 'The name of the Cat' }) + name: string; + + @ApiProperty({ example: 1, description: 'The age of the Cat' }) + age: number; + } + + @Controller('/api') + @ApiExtraModel(Cat) + class APIController { + @Post('/update_user') + @ApiBody({ + content: { + 'application/json': { + schema: { + type: 'array', + items: { + $ref: getSchemaPath(Cat), + }, + }, + examples: { + example1: { + summary: 'An example of a cat', + }, + example2: { + $ref: '#/components/examples/hamster' + }, + } + }, + }, + }) + async updateUser() { + // ... + } + } + + const explorer = new CustomSwaggerExplorer(); + explorer.generatePath(APIController); + expect(explorer.getData()).toMatchSnapshot(); + }); + + it('should test ApiBody with formData', () => { + class Cat { + @ApiProperty({ example: 'Kitty', description: 'The name of the Cat' }) + name: string; + + @ApiProperty({ example: 1, description: 'The age of the Cat' }) + age: number; + } + + @Controller('/api') + @ApiExtraModel(Cat) + class APIController { + @Post('/update_user') + @ApiBody({ + required: true, + content: { + 'application/x-www-form-urlencoded:': { + schema: { + type: 'object', + properties: { + name: { + type: 'string', + }, + fav_number: { + type: 'integer', + } + }, + required: [ + 'name', + ] + }, + encoding: { + color: { + style: 'form', + explode: false, + } + } + }, + }, + }) + async updateUser() { + // ... + } + } + + const explorer = new CustomSwaggerExplorer(); + explorer.generatePath(APIController); + expect(explorer.getData()).toMatchSnapshot(); + }); }); describe('test @ApiExtension', () => { @@ -1472,3 +1601,225 @@ describe('test property metadata parse', () => { }); }); }); + +describe('test @ApiOperation', () => { + it('should test deprecated', () => { + @Controller('/api') + class APIController { + @ApiOperation({ + deprecated: true, + }) + @Post('/update_user') + async updateUser() { + // ... + } + } + + const explorer = new CustomSwaggerExplorer(); + explorer.generatePath(APIController); + expect(explorer.getData()).toMatchSnapshot(); + }); + + it('should test tags', () => { + @Controller('/api') + class APIController { + @ApiOperation({ + tags: ['tag1', 'tag2'], + }) + @Post('/update_user') + async updateUser() { + // ... + } + } + + const explorer = new CustomSwaggerExplorer(); + explorer.generatePath(APIController); + expect(explorer.getData()).toMatchSnapshot(); + }); + + it('should test summary and description', () => { + @Controller('/api') + class APIController { + @ApiOperation({ + summary: 'update user', + description: 'update user description', + }) + @Post('/update_user') + async updateUser() { + // ... + } + } + const explorer = new CustomSwaggerExplorer(); + explorer.generatePath(APIController); + expect(explorer.getData()).toMatchSnapshot(); + }); + + it('should test custom operationId', () => { + @Controller('/api') + class APIController { + @ApiOperation({ + operationId: 'updateUser', + }) + @Post('/update_user') + async updateUser() { + // ... + } + } + const explorer = new CustomSwaggerExplorer(); + explorer.generatePath(APIController); + expect(explorer.getData()).toMatchSnapshot(); + }); +}); + +describe('test @ApiQuery', () => { + it('should test with @query decorator and any type', () => { + @Controller('/api') + class APIController { + @Get('/get_user') + async getUser(@Query() data: any) { + // ... + } + } + + const explorer = new CustomSwaggerExplorer(); + explorer.generatePath(APIController); + expect(explorer.getData()).toMatchSnapshot(); + }); + + it('should test with @query decorator and fixed name', () => { + @Controller('/api') + class APIController { + @Get('/get_user') + async getUser(@Query('data') data: any) { + // ... + } + } + + const explorer = new CustomSwaggerExplorer(); + explorer.generatePath(APIController); + expect(explorer.getData()).toMatchSnapshot(); + }); + + it('should test with @query decorator and @ApiQuery', () => { + @Controller('/api') + class APIController { + @Get('/get_user') + @ApiQuery({ name: 'data', description: 'The name of the user', type: 'string'}) + @ApiQuery({ name: 'id', schema: {type: 'number'} }) + async getUser(@Query('data') data: any, @Query('id') id: any) { + // ... + } + } + + const explorer = new CustomSwaggerExplorer(); + explorer.generatePath(APIController); + expect(explorer.getData()).toMatchSnapshot(); + }); + + it('should test merge @query and @ApiQuery', () => { + @Controller('/api') + class APIController { + @Get('/get_user') + @ApiQuery({ name: 'data', description: 'The name of the user', type: 'string', required: false}) + async getUser(@Query('data') data: any, @Query('id') id: any) { + // ... + } + } + + const explorer = new CustomSwaggerExplorer(); + explorer.generatePath(APIController); + expect(explorer.getData()).toMatchSnapshot(); + }); + + it('should get base type from @query', () => { + @Controller('/api') + class APIController { + @Get('/get_user') + async getUser( + @Query('data') data: string, + @Query('id') id: number, + @Query('flag') flag: boolean, + @Query('list') list: string[], + ) { + // ... + } + } + + const explorer = new CustomSwaggerExplorer(); + explorer.generatePath(APIController); + expect(explorer.getData()).toMatchSnapshot(); + }); + + it('should get class type from DTO', () => { + class UserDTO { + @ApiProperty({ + description: 'The name of the user', + }) + name: string; + + @ApiProperty({ + description: 'The uid of the user', + }) + id: number; + } + + @Controller('/api') + class APIController { + @Get('/get_user') + async getUser(@Query() user: UserDTO) { + // ... + } + } + + const explorer = new CustomSwaggerExplorer(); + explorer.generatePath(APIController); + expect(explorer.getData()).toMatchSnapshot(); + }); + + it('should get array type from DTO', () => { + class UserDTO { + @ApiProperty({ + description: 'The name of the user', + }) + name: string; + + @ApiProperty({ + description: 'The uid of the user', + }) + id: number; + } + + @Controller('/api') + class APIController { + @Get('/get_user') + @ApiQuery({ name: 'user', type: UserDTO, isArray: true }) + async getUser(@Query() user: UserDTO[]) { + // ... + } + } + + const explorer = new CustomSwaggerExplorer(); + explorer.generatePath(APIController); + expect(explorer.getData()).toMatchSnapshot(); + }); + + it('should get enum type', () => { + enum Status { + ACTIVE = 'active', + INACTIVE = 'inactive', + } + + @Controller('/api') + class APIController { + @Get('/get_user') + @ApiQuery({ name: 'status', enum: Status }) + async getUser(@Query('status') status: Status) { + // ... + } + } + + const explorer = new CustomSwaggerExplorer(); + explorer.generatePath(APIController); + expect(explorer.getData()).toMatchSnapshot(); + }); +});