diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/CodegenOperation.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/CodegenOperation.java index ef348bbb266..83413a9ee11 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/CodegenOperation.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/CodegenOperation.java @@ -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); } } diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultCodegen.java index f1736f0e532..e97a6caf557 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultCodegen.java @@ -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) { diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/ElixirClientCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/ElixirClientCodegen.java index 9903908d4e0..62c9d6dcc5f 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/ElixirClientCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/ElixirClientCodegen.java @@ -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")) { diff --git a/modules/openapi-generator/src/main/resources/asciidoc-documentation/index.mustache b/modules/openapi-generator/src/main/resources/asciidoc-documentation/index.mustache index 48d810d038a..483ee5870f4 100644 --- a/modules/openapi-generator/src/main/resources/asciidoc-documentation/index.mustache +++ b/modules/openapi-generator/src/main/resources/asciidoc-documentation/index.mustache @@ -104,7 +104,7 @@ Operation Id:: {{nickname}} | {{code}} | {{message}} -| {{^simpleType}}{{dataType}}[<<{{baseType}}>>]{{/simpleType}} {{#simpleType}}<<{{dataType}}>>{{/simpleType}} +| {{#containerType}}{{dataType}}[<<{{baseType}}>>]{{/containerType}} {{^containerType}}<<{{dataType}}>>{{/containerType}} {{/responses}} |=== diff --git a/modules/openapi-generator/src/main/resources/htmlDocs/index.mustache b/modules/openapi-generator/src/main/resources/htmlDocs/index.mustache index a5ad68cc9c8..2f3f276c476 100644 --- a/modules/openapi-generator/src/main/resources/htmlDocs/index.mustache +++ b/modules/openapi-generator/src/main/resources/htmlDocs/index.mustache @@ -134,7 +134,7 @@ {{#responses}}

{{code}}

{{message}} - {{#simpleType}}{{dataType}}{{/simpleType}} + {{^containerType}}{{dataType}}{{/containerType}} {{#examples}}

Example data

Content-Type: {{{contentType}}}
diff --git a/modules/openapi-generator/src/test/java/org/openapitools/codegen/DefaultCodegenTest.java b/modules/openapi-generator/src/test/java/org/openapitools/codegen/DefaultCodegenTest.java index 87486da4c36..949bc1a9243 100644 --- a/modules/openapi-generator/src/test/java/org/openapitools/codegen/DefaultCodegenTest.java +++ b/modules/openapi-generator/src/test/java/org/openapitools/codegen/DefaultCodegenTest.java @@ -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"); diff --git a/modules/openapi-generator/src/test/resources/3_0/response-tests.yaml b/modules/openapi-generator/src/test/resources/3_0/response-tests.yaml new file mode 100644 index 00000000000..e17881606db --- /dev/null +++ b/modules/openapi-generator/src/test/resources/3_0/response-tests.yaml @@ -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