[CodegenResponse] various enhancements and bug fixes (#10984)

* add more response tests, fix simpleType

* samples update

* replace simpleType with containerType

* update doc template

* fix typo in mustache tag
This commit is contained in:
William Cheng 2021-12-05 17:43:17 +08:00 committed by GitHub
parent 186842ea19
commit 5451b77d42
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 643 additions and 12 deletions

View File

@ -29,7 +29,8 @@ public class CodegenOperation {
isArray, isMultipart,
isResponseBinary = false, isResponseFile = false, hasReference = false,
isRestfulIndex, isRestfulShow, isRestfulCreate, isRestfulUpdate, isRestfulDestroy,
isRestful, isDeprecated, isCallbackRequest, uniqueItems, hasDefaultResponse = false;
isRestful, isDeprecated, isCallbackRequest, uniqueItems, hasDefaultResponse = false,
hasErrorResponseObject; // if 4xx, 5xx repsonses have at least one error object defined
public String path, operationId, returnType, returnFormat, httpMethod, returnBaseType,
returnContainer, summary, unescapedNotes, notes, baseName, defaultResponse;
public CodegenDiscriminator discriminator;
@ -297,6 +298,7 @@ public class CodegenOperation {
sb.append(", isResponseFile=").append(isResponseFile);
sb.append(", hasReference=").append(hasReference);
sb.append(", hasDefaultResponse=").append(hasDefaultResponse);
sb.append(", hasErrorResponseObject=").append(hasErrorResponseObject);
sb.append(", isRestfulIndex=").append(isRestfulIndex);
sb.append(", isRestfulShow=").append(isRestfulShow);
sb.append(", isRestfulCreate=").append(isRestfulCreate);
@ -371,6 +373,7 @@ public class CodegenOperation {
isResponseFile == that.isResponseFile &&
hasReference == that.hasReference &&
hasDefaultResponse == that.hasDefaultResponse &&
hasErrorResponseObject == that.hasErrorResponseObject &&
isRestfulIndex == that.isRestfulIndex &&
isRestfulShow == that.isRestfulShow &&
isRestfulCreate == that.isRestfulCreate &&
@ -435,6 +438,7 @@ public class CodegenOperation {
produces, prioritizedContentTypes, servers, bodyParam, allParams, bodyParams, pathParams, queryParams,
headerParams, formParams, cookieParams, requiredParams, optionalParams, authMethods, tags,
responses, callbacks, imports, examples, requestBodyExamples, externalDocs, vendorExtensions,
nickname, operationIdOriginal, operationIdLowerCase, operationIdCamelCase, operationIdSnakeCase);
nickname, operationIdOriginal, operationIdLowerCase, operationIdCamelCase, operationIdSnakeCase,
hasErrorResponseObject);
}
}

View File

@ -3976,6 +3976,12 @@ public class DefaultCodegen implements CodegenConfig {
if (Boolean.TRUE.equals(r.isFile) && Boolean.TRUE.equals(r.is2xx) && Boolean.FALSE.equals(op.isResponseFile)) {
op.isResponseFile = Boolean.TRUE;
}
// check if any 4xx or 5xx reponse has an error response object defined
if ((Boolean.TRUE.equals(r.is4xx) || Boolean.TRUE.equals(r.is5xx)) &&
Boolean.FALSE.equals(r.primitiveType) && Boolean.FALSE.equals(r.simpleType)) {
op.hasErrorResponseObject = Boolean.TRUE;
}
}
op.responses.sort((a, b) -> {
int aScore = a.isWildcard() ? 2 : a.isRange() ? 1 : 0;
@ -4269,6 +4275,7 @@ public class DefaultCodegen implements CodegenConfig {
r.setComposedSchemas(getComposedSchemas(responseSchema));
if (ModelUtils.isArraySchema(responseSchema)) {
r.simpleType = false;
r.isArray = true;
r.containerType = cp.containerType;
ArraySchema as = (ArraySchema) responseSchema;
CodegenProperty items = fromProperty("response", getSchemaItems(as));
@ -4324,6 +4331,8 @@ public class DefaultCodegen implements CodegenConfig {
} else if (ModelUtils.isTypeObjectSchema(responseSchema)) {
if (ModelUtils.isFreeFormObject(openAPI, responseSchema)) {
r.isFreeFormObject = true;
} else {
r.isModel = true;
}
r.simpleType = false;
r.containerType = cp.containerType;
@ -4335,9 +4344,6 @@ public class DefaultCodegen implements CodegenConfig {
LOGGER.debug("Property type is not primitive: {}", cp.dataType);
}
if (!r.isMap && !r.isArray) {
r.simpleType = true;
}
r.primitiveType = (r.baseType == null || languageSpecificPrimitives().contains(r.baseType));
if (r.baseType == null) {

View File

@ -613,7 +613,7 @@ public class ElixirClientCodegen extends DefaultCodegen implements CodegenConfig
return "%{}";
}
// Primitive return type, don't even try to decode
if (baseType == null || (simpleType && primitiveType)) {
if (baseType == null || (containerType == null && primitiveType)) {
return "false";
} else if (isArray && languageSpecificPrimitives().contains(baseType)) {
return "[]";
@ -733,16 +733,13 @@ public class ElixirClientCodegen extends DefaultCodegen implements CodegenConfig
StringBuilder returnEntry = new StringBuilder();
if (exResponse.baseType == null) {
returnEntry.append("nil");
} else if (exResponse.simpleType) {
} else if (exResponse.containerType == null) { // not container (array, map, set)
if (!exResponse.primitiveType) {
returnEntry.append(moduleName);
returnEntry.append(".Model.");
}
returnEntry.append(exResponse.baseType);
returnEntry.append(".t");
} else if (exResponse.containerType == null) {
returnEntry.append(returnBaseType);
returnEntry.append(".t");
} else {
if (exResponse.containerType.equals("array") ||
exResponse.containerType.equals("set")) {

View File

@ -104,7 +104,7 @@ Operation Id:: {{nickname}}
| {{code}}
| {{message}}
| {{^simpleType}}{{dataType}}[<<{{baseType}}>>]{{/simpleType}} {{#simpleType}}<<{{dataType}}>>{{/simpleType}}
| {{#containerType}}{{dataType}}[<<{{baseType}}>>]{{/containerType}} {{^containerType}}<<{{dataType}}>>{{/containerType}}
{{/responses}}
|===

View File

@ -134,7 +134,7 @@
{{#responses}}
<h4 class="field-label">{{code}}</h4>
{{message}}
{{#simpleType}}<a href="#{{dataType}}">{{dataType}}</a>{{/simpleType}}
{{^containerType}}<a href="#{{dataType}}">{{dataType}}</a>{{/containerType}}
{{#examples}}
<h3 class="field-label">Example data</h3>
<div class="example-data-content-type">Content-Type: {{{contentType}}}</div>

View File

@ -3877,6 +3877,59 @@ public class DefaultCodegenTest {
}
@Test
public void testResponses() {
final OpenAPI openAPI = TestUtils.parseFlattenSpec("src/test/resources/3_0/response-tests.yaml");
final DefaultCodegen codegen = new DefaultCodegen();
codegen.setOpenAPI(openAPI);
codegen.setDisallowAdditionalPropertiesIfNotPresent(false);
String path;
Operation operation;
CodegenOperation co;
CodegenParameter cpa;
CodegenResponse cr;
path = "/pet/{petId}";
operation = openAPI.getPaths().get(path).getGet();
co = codegen.fromOperation(path, "GET", operation, null);
//assertTrue(co.hasErrorResponseObject);
cr = co.responses.get(0);
assertTrue(cr.is2xx);
assertFalse(cr.simpleType);
assertFalse(cr.primitiveType);
cr = co.responses.get(3);
assertTrue(cr.is5xx);
assertFalse(cr.simpleType);
assertFalse(cr.primitiveType);
path = "/pet";
operation = openAPI.getPaths().get(path).getPut();
co = codegen.fromOperation(path, "PUT", operation, null);
assertTrue(co.hasErrorResponseObject);
// 200 response
cr = co.responses.get(0);
assertTrue(cr.is2xx);
assertFalse(cr.simpleType);
assertFalse(cr.primitiveType);
// 400 response
cr = co.responses.get(1);
assertTrue(cr.is4xx);
assertEquals(cr.code, "400");
assertFalse(cr.simpleType);
assertFalse(cr.primitiveType);
path = "/pet/findByTags";
operation = openAPI.getPaths().get(path).getGet();
co = codegen.fromOperation(path, "GET", operation, null);
assertFalse(co.hasErrorResponseObject);
cr = co.responses.get(0);
assertTrue(cr.is2xx);
assertFalse(cr.simpleType);
assertFalse(cr.primitiveType);
}
public void testParameterContent() {
DefaultCodegen codegen = new DefaultCodegen();
final OpenAPI openAPI = TestUtils.parseFlattenSpec("src/test/resources/3_0/content-data.yaml");

View File

@ -0,0 +1,571 @@
openapi: 3.0.0
servers:
- url: 'http://petstore.swagger.io/v2'
info:
description: >-
This is a sample server Petstore server. For this sample, you can use the api key
`special-key` to test the authorization filters.
version: 1.0.0
title: OpenAPI Petstore
license:
name: Apache-2.0
url: 'https://www.apache.org/licenses/LICENSE-2.0.html'
tags:
- name: pet
description: Everything about your Pets
- name: store
description: Access to Petstore orders
- name: user
description: Operations about user
paths:
/pet:
post:
tags:
- pet
summary: Add a new pet to the store
description: ''
operationId: addPet
responses:
'200':
description: successful operation
content:
application/xml:
schema:
$ref: '#/components/schemas/Pet'
application/json:
schema:
$ref: '#/components/schemas/Pet'
'405':
description: Invalid input
security:
- petstore_auth:
- 'write:pets'
- 'read:pets'
requestBody:
$ref: '#/components/requestBodies/Pet'
put:
tags:
- pet
summary: Update an existing pet
description: ''
operationId: updatePet
responses:
'200':
description: successful operation
content:
application/xml:
schema:
$ref: '#/components/schemas/Pet'
application/json:
schema:
$ref: '#/components/schemas/Pet'
'400':
description: Invalid ID supplied
content:
application/json:
schema:
$ref: '#/components/schemas/ApiResponse'
'404':
description: Pet not found
'405':
description: Validation exception
security:
- petstore_auth:
- 'write:pets'
- 'read:pets'
requestBody:
$ref: '#/components/requestBodies/Pet'
/pet/findByStatus:
get:
tags:
- pet
summary: Finds Pets by status
description: Multiple status values can be provided with comma separated strings
operationId: findPetsByStatus
parameters:
- name: status
in: query
description: Status values that need to be considered for filter
required: true
style: form
explode: false
deprecated: true
schema:
type: array
items:
type: string
enum:
- available
- pending
- sold
default: available
responses:
'200':
description: successful operation
content:
application/xml:
schema:
type: array
items:
$ref: '#/components/schemas/Pet'
application/json:
schema:
type: array
items:
$ref: '#/components/schemas/Pet'
'400':
description: Invalid status value
security:
- petstore_auth:
- 'read:pets'
/pet/findByTags:
get:
tags:
- pet
summary: Finds Pets by tags
description: >-
Multiple tags can be provided with comma separated strings. Use tag1,
tag2, tag3 for testing.
operationId: findPetsByTags
parameters:
- name: tags
in: query
description: Tags to filter by
required: true
style: form
explode: false
schema:
type: array
items:
type: string
responses:
'200':
description: successful operation
content:
application/xml:
schema:
type: array
items:
$ref: '#/components/schemas/Pet'
application/json:
schema:
type: array
items:
$ref: '#/components/schemas/Pet'
'400':
description: Invalid tag value
security:
- petstore_auth:
- 'read:pets'
deprecated: true
'/pet/{petId}':
get:
tags:
- pet
summary: Find pet by ID
description: Returns a single pet
operationId: getPetById
parameters:
- name: petId
in: path
description: ID of pet to return
required: true
schema:
type: integer
format: int64
responses:
'200':
description: successful operation
content:
application/xml:
schema:
$ref: '#/components/schemas/Pet'
application/json:
schema:
$ref: '#/components/schemas/Pet'
'400':
description: Invalid ID supplied
'404':
description: Pet not found
'500':
description: Server error
content:
application/application:
schema:
$ref: '#/components/schemas/ApiResponse'
security:
- api_key: []
post:
tags:
- pet
summary: Updates a pet in the store with form data
description: ''
operationId: updatePetWithForm
parameters:
- name: petId
in: path
description: ID of pet that needs to be updated
required: true
schema:
type: integer
format: int64
responses:
'405':
description: Invalid input
security:
- petstore_auth:
- 'write:pets'
- 'read:pets'
requestBody:
content:
application/x-www-form-urlencoded:
schema:
type: object
properties:
name:
description: Updated name of the pet
type: string
status:
description: Updated status of the pet
type: string
delete:
tags:
- pet
summary: Deletes a pet
description: ''
operationId: deletePet
parameters:
- name: api_key
in: header
required: false
schema:
type: string
- name: petId
in: path
description: Pet id to delete
required: true
schema:
type: integer
format: int64
responses:
'400':
description: Invalid pet value
security:
- petstore_auth:
- 'write:pets'
- 'read:pets'
'/pet/{petId}/uploadImage':
post:
tags:
- pet
summary: uploads an image
description: ''
operationId: uploadFile
parameters:
- name: petId
in: path
description: ID of pet to update
required: true
schema:
type: integer
format: int64
responses:
'200':
description: successful operation
content:
application/json:
schema:
$ref: '#/components/schemas/ApiResponse'
security:
- petstore_auth:
- 'write:pets'
- 'read:pets'
requestBody:
content:
multipart/form-data:
schema:
type: object
properties:
additionalMetadata:
description: Additional data to pass to server
type: string
file:
description: file to upload
type: string
format: binary
'/store/order/{orderId}':
get:
tags:
- store
summary: Find purchase order by ID
description: >-
For valid response try integer IDs with value <= 5 or > 10. Other values
will generated exceptions
operationId: getOrderById
parameters:
- name: orderId
in: path
description: ID of pet that needs to be fetched
required: true
schema:
type: integer
format: int64
minimum: 1
maximum: 5
responses:
'200':
description: successful operation
content:
application/xml:
schema:
$ref: '#/components/schemas/Order'
application/json:
schema:
$ref: '#/components/schemas/Order'
'400':
description: Invalid ID supplied
'404':
description: Order not found
'500':
description: Server error
delete:
tags:
- store
summary: Delete purchase order by ID
description: >-
For valid response try integer IDs with value < 1000. Anything above
1000 or nonintegers will generate API errors
operationId: deleteOrder
parameters:
- name: orderId
in: path
description: ID of the order that needs to be deleted
required: true
schema:
type: string
responses:
'400':
description: Invalid ID supplied
'404':
description: Order not found
/user/login:
get:
tags:
- user
summary: Logs user into the system
description: ''
operationId: loginUser
parameters:
- name: username
in: query
description: The user name for login
required: true
schema:
type: string
pattern: '^[a-zA-Z0-9]+[a-zA-Z0-9\.\-_]*[a-zA-Z0-9]+$'
- name: password
in: query
description: The password for login in clear text
required: true
schema:
type: string
responses:
'200':
description: successful operation
headers:
Set-Cookie:
description: >-
Cookie authentication key for use with the `api_key`
apiKey authentication.
schema:
type: string
example: AUTH_KEY=abcde12345; Path=/; HttpOnly
X-Rate-Limit:
description: calls per hour allowed by the user
schema:
type: integer
format: int32
X-Expires-After:
description: date in UTC when token expires
schema:
type: string
format: date-time
content:
application/xml:
schema:
type: string
application/json:
schema:
type: string
'400':
description: Invalid username/password supplied
externalDocs:
description: Find out more about Swagger
url: 'http://swagger.io'
components:
requestBodies:
UserArray:
content:
application/json:
schema:
type: array
items:
$ref: '#/components/schemas/User'
description: List of user object
required: true
Pet:
content:
application/json:
schema:
$ref: '#/components/schemas/Pet'
application/xml:
schema:
$ref: '#/components/schemas/Pet'
description: Pet object that needs to be added to the store
required: true
securitySchemes:
petstore_auth:
type: oauth2
flows:
implicit:
authorizationUrl: 'http://petstore.swagger.io/api/oauth/dialog'
scopes:
'write:pets': modify pets in your account
'read:pets': read your pets
api_key:
type: apiKey
name: api_key
in: header
schemas:
Order:
title: Pet Order
description: An order for a pets from the pet store
type: object
properties:
id:
type: integer
format: int64
petId:
type: integer
format: int64
quantity:
type: integer
format: int32
shipDate:
type: string
format: date-time
status:
type: string
description: Order Status
enum:
- placed
- approved
- delivered
complete:
type: boolean
default: false
xml:
name: Order
Category:
title: Pet category
description: A category for a pet
type: object
properties:
id:
type: integer
format: int64
name:
type: string
pattern: '^[a-zA-Z0-9]+[a-zA-Z0-9\.\-_]*[a-zA-Z0-9]+$'
xml:
name: Category
User:
title: a User
description: A User who is purchasing from the pet store
type: object
properties:
id:
type: integer
format: int64
username:
type: string
firstName:
type: string
lastName:
type: string
email:
type: string
password:
type: string
phone:
type: string
userStatus:
type: integer
format: int32
description: User Status
xml:
name: User
Tag:
title: Pet Tag
description: A tag for a pet
type: object
properties:
id:
type: integer
format: int64
name:
type: string
xml:
name: Tag
Pet:
title: a Pet
description: A pet for sale in the pet store
type: object
required:
- name
- photoUrls
properties:
id:
type: integer
format: int64
category:
$ref: '#/components/schemas/Category'
name:
type: string
example: doggie
photoUrls:
type: array
xml:
name: photoUrl
wrapped: true
items:
type: string
tags:
type: array
xml:
name: tag
wrapped: true
items:
$ref: '#/components/schemas/Tag'
status:
type: string
description: pet status in the store
deprecated: true
enum:
- available
- pending
- sold
xml:
name: Pet
ApiResponse:
title: An uploaded response
description: Describes the result of uploading an image resource
type: object
properties:
code:
type: integer
format: int32
type:
type: string
message:
type: string