From 667a6097b50944ced9ab40502344e7bee279282a Mon Sep 17 00:00:00 2001 From: Jim Schubert Date: Mon, 16 Sep 2019 10:46:47 -0400 Subject: [PATCH] [spring] Resolve regression on RequestParam for non-objects (#3855) * [spring] Resolve regression on RequestParam for non-objects * Regenerate Go client samples * Include testcase for issue 3248 * Set isModel appropriately for referenced schemas --- .../openapitools/codegen/DefaultCodegen.java | 3 +- .../codegen/DefaultCodegenTest.java | 36 ++++++ .../java/spring/SpringCodegenTest.java | 104 +++++++++++++-- .../src/test/resources/2_0/arrayRefBody.yaml | 52 ++++++++ .../resources/3_0/3248-regression-dates.yaml | 27 ++++ .../test/resources/3_0/3248-regression.yaml | 47 +++++++ .../src/test/resources/3_0/arrayRefBody.yaml | 35 +++++ .../src/test/resources/3_0/issue_3248.yaml | 121 ++++++++++++++++++ .../go/go-petstore-withXml/api_fake.go | 6 +- .../petstore/go/go-petstore/api_fake.go | 6 +- 10 files changed, 417 insertions(+), 20 deletions(-) create mode 100644 modules/openapi-generator/src/test/resources/2_0/arrayRefBody.yaml create mode 100644 modules/openapi-generator/src/test/resources/3_0/3248-regression-dates.yaml create mode 100644 modules/openapi-generator/src/test/resources/3_0/3248-regression.yaml create mode 100644 modules/openapi-generator/src/test/resources/3_0/arrayRefBody.yaml create mode 100644 modules/openapi-generator/src/test/resources/3_0/issue_3248.yaml 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 bfc8dbbb63c2..682ca581355a 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 @@ -2303,8 +2303,9 @@ public class DefaultCodegen implements CodegenConfig { // property.baseType = getSimpleRef(p.get$ref()); //} // --END of revision - property.isModel = ModelUtils.isModel(p); setNonArrayMapProperty(property, type); + Schema refOrCurrent = ModelUtils.getReferencedSchema(this.openAPI, p); + property.isModel = (ModelUtils.isComposedSchema(refOrCurrent) || ModelUtils.isObjectSchema(refOrCurrent)) && ModelUtils.isModel(refOrCurrent); } LOGGER.debug("debugging from property return: " + property); 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 9dbb5bb5c134..26ae76afc1bc 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 @@ -27,6 +27,7 @@ import io.swagger.v3.oas.models.Operation; import io.swagger.v3.oas.models.PathItem; import io.swagger.v3.oas.models.headers.Header; import io.swagger.v3.oas.models.media.*; +import io.swagger.v3.oas.models.parameters.Parameter; import io.swagger.v3.oas.models.parameters.QueryParameter; import io.swagger.v3.oas.models.parameters.RequestBody; import io.swagger.v3.oas.models.responses.ApiResponse; @@ -911,6 +912,41 @@ public class DefaultCodegenTest { Assert.assertEquals(codegenModel.vars.size(), 1); } + @Test + public void arrayInnerReferencedSchemaMarkedAsModel_20() { + final OpenAPI openAPI = TestUtils.parseSpec("src/test/resources/2_0/arrayRefBody.yaml"); + final DefaultCodegen codegen = new DefaultCodegen(); + codegen.setOpenAPI(openAPI); + + Set imports = new HashSet<>(); + + RequestBody body = openAPI.getPaths().get("/examples").getPost().getRequestBody(); + + CodegenParameter codegenParameter = codegen.fromRequestBody(body, imports, ""); + + Assert.assertTrue(codegenParameter.isContainer); + Assert.assertTrue(codegenParameter.items.isModel); + Assert.assertFalse(codegenParameter.items.isContainer); + } + + @Test + public void arrayInnerReferencedSchemaMarkedAsModel_30() { + final OpenAPI openAPI = TestUtils.parseSpec("src/test/resources/3_0/arrayRefBody.yaml"); + new InlineModelResolver().flatten(openAPI); + final DefaultCodegen codegen = new DefaultCodegen(); + codegen.setOpenAPI(openAPI); + + Set imports = new HashSet<>(); + + RequestBody body = openAPI.getPaths().get("/examples").getPost().getRequestBody(); + + CodegenParameter codegenParameter = codegen.fromRequestBody(body, imports, ""); + + Assert.assertTrue(codegenParameter.isContainer); + Assert.assertTrue(codegenParameter.items.isModel); + Assert.assertFalse(codegenParameter.items.isContainer); + } + @Test @SuppressWarnings("unchecked") public void commonLambdasRegistrationTest() { diff --git a/modules/openapi-generator/src/test/java/org/openapitools/codegen/java/spring/SpringCodegenTest.java b/modules/openapi-generator/src/test/java/org/openapitools/codegen/java/spring/SpringCodegenTest.java index 68c0e86f0d21..378f7e5d8e07 100644 --- a/modules/openapi-generator/src/test/java/org/openapitools/codegen/java/spring/SpringCodegenTest.java +++ b/modules/openapi-generator/src/test/java/org/openapitools/codegen/java/spring/SpringCodegenTest.java @@ -174,7 +174,7 @@ public class SpringCodegenTest { String outputPath = output.getAbsolutePath().replace('\\', '/'); OpenAPI openAPI = new OpenAPIParser() - .readLocation("src/test/resources/3_0/issue_2053.yaml", null, new ParseOptions()).getOpenAPI(); + .readLocation("src/test/resources/3_0/issue_2053.yaml", null, new ParseOptions()).getOpenAPI(); SpringCodegen codegen = new SpringCodegen(); codegen.setOutputDir(output.getAbsolutePath()); @@ -188,17 +188,97 @@ public class SpringCodegenTest { generator.opts(input).generate(); checkFileContains( - generator, - outputPath + "/src/main/java/org/openapitools/api/ElephantsApi.java", - "@org.springframework.format.annotation.DateTimeFormat(iso = org.springframework.format.annotation.DateTimeFormat.ISO.DATE)" + generator, + outputPath + "/src/main/java/org/openapitools/api/ElephantsApi.java", + "@org.springframework.format.annotation.DateTimeFormat(iso = org.springframework.format.annotation.DateTimeFormat.ISO.DATE)" ); checkFileContains( - generator, - outputPath + "/src/main/java/org/openapitools/api/ZebrasApi.java", - "@org.springframework.format.annotation.DateTimeFormat(iso = org.springframework.format.annotation.DateTimeFormat.ISO.DATE_TIME)" + generator, + outputPath + "/src/main/java/org/openapitools/api/ZebrasApi.java", + "@org.springframework.format.annotation.DateTimeFormat(iso = org.springframework.format.annotation.DateTimeFormat.ISO.DATE_TIME)" ); } + @Test + public void shouldGenerateRequestParamForRefParams_3248_Regression() throws IOException { + File output = Files.createTempDirectory("test").toFile().getCanonicalFile(); + output.deleteOnExit(); + String outputPath = output.getAbsolutePath().replace('\\', '/'); + + OpenAPI openAPI = new OpenAPIParser() + .readLocation("src/test/resources/3_0/3248-regression.yaml", null, new ParseOptions()).getOpenAPI(); + + SpringCodegen codegen = new SpringCodegen(); + codegen.setOutputDir(output.getAbsolutePath()); + codegen.additionalProperties().put(CXFServerFeatures.LOAD_TEST_DATA_FROM_FILE, "true"); + + ClientOptInput input = new ClientOptInput(); + input.setOpenAPI(openAPI); + input.setConfig(codegen); + + MockDefaultGenerator generator = new MockDefaultGenerator(); + generator.opts(input).generate(); + + checkFileContains(generator, outputPath + "/src/main/java/org/openapitools/api/ExampleApi.java", + "@RequestParam(value = \"format\"", + "@RequestParam(value = \"query\""); + } + + @Test + public void shouldGenerateRequestParamForRefParams_3248_RegressionDates() throws IOException { + File output = Files.createTempDirectory("test").toFile().getCanonicalFile(); + output.deleteOnExit(); + String outputPath = output.getAbsolutePath().replace('\\', '/'); + + OpenAPI openAPI = new OpenAPIParser() + .readLocation("src/test/resources/3_0/3248-regression-dates.yaml", null, new ParseOptions()).getOpenAPI(); + + SpringCodegen codegen = new SpringCodegen(); + codegen.setOutputDir(output.getAbsolutePath()); + codegen.additionalProperties().put(CXFServerFeatures.LOAD_TEST_DATA_FROM_FILE, "true"); + + ClientOptInput input = new ClientOptInput(); + input.setOpenAPI(openAPI); + input.setConfig(codegen); + + MockDefaultGenerator generator = new MockDefaultGenerator(); + generator.opts(input).generate(); + + checkFileContains(generator, outputPath + "/src/main/java/org/openapitools/api/ExampleApi.java", + "@RequestParam(value = \"start\""); + } + + @Test + public void doGenerateRequestParamForSimpleParam() throws IOException { + File output = Files.createTempDirectory("test").toFile().getCanonicalFile(); + output.deleteOnExit(); + String outputPath = output.getAbsolutePath().replace('\\', '/'); + + OpenAPI openAPI = new OpenAPIParser() + .readLocation("src/test/resources/3_0/issue_3248.yaml", null, new ParseOptions()).getOpenAPI(); + + SpringCodegen codegen = new SpringCodegen(); + codegen.setOutputDir(output.getAbsolutePath()); + codegen.additionalProperties().put(CXFServerFeatures.LOAD_TEST_DATA_FROM_FILE, "true"); + + ClientOptInput input = new ClientOptInput(); + input.openAPI(openAPI); + input.config(codegen); + + MockDefaultGenerator generator = new MockDefaultGenerator(); + generator.opts(input).generate(); + + checkFileContains(generator, outputPath + "/src/main/java/org/openapitools/api/MonkeysApi.java", "@RequestParam"); + checkFileContains(generator, outputPath + "/src/main/java/org/openapitools/api/ElephantsApi.java", "@RequestParam"); + checkFileContains(generator, outputPath + "/src/main/java/org/openapitools/api/ZebrasApi.java", "@RequestParam"); + checkFileContains(generator, outputPath + "/src/main/java/org/openapitools/api/BearsApi.java", "@RequestParam"); + checkFileContains(generator, outputPath + "/src/main/java/org/openapitools/api/CamelsApi.java", "@RequestParam"); + checkFileContains(generator, outputPath + "/src/main/java/org/openapitools/api/PandasApi.java", "@RequestParam"); + checkFileContains(generator, outputPath + "/src/main/java/org/openapitools/api/CrocodilesApi.java", "@RequestParam"); + checkFileContains(generator, outputPath + "/src/main/java/org/openapitools/api/PolarBearsApi.java", "@RequestParam"); + + } + private void checkFileNotContains(MockDefaultGenerator generator, String path, String... lines) { String file = generator.getFiles().get(path); assertNotNull(file); @@ -209,8 +289,14 @@ public class SpringCodegenTest { private void checkFileContains(MockDefaultGenerator generator, String path, String... lines) { String file = generator.getFiles().get(path); assertNotNull(file); - for (String line : lines) - assertTrue(file.contains(line)); + int expectedCount = lines.length; + int actualCount = 0; + for (String line : lines) { + if (file.contains(line)) { + actualCount++; + } + } + assertEquals(actualCount, expectedCount, "File is missing " + (expectedCount - actualCount) + " expected lines."); } @Test diff --git a/modules/openapi-generator/src/test/resources/2_0/arrayRefBody.yaml b/modules/openapi-generator/src/test/resources/2_0/arrayRefBody.yaml new file mode 100644 index 000000000000..b4f176b0b657 --- /dev/null +++ b/modules/openapi-generator/src/test/resources/2_0/arrayRefBody.yaml @@ -0,0 +1,52 @@ +swagger: '2.0' +info: + version: '' + title: arrayRefBody + contact: {} +host: www.example.com +basePath: / +schemes: + - https +consumes: + - application/json +produces: + - application/json +paths: + /examples: + post: + description: Create an array of inputs + summary: '' + tags: + - Examples + operationId: createExamples + produces: + - application/json + parameters: + - name: body + in: body + required: true + description: inputs description + schema: + type: array + items: + $ref: '#/definitions/Input' + responses: + 200: + description: successful operation + headers: {} +definitions: + Input: + title: Input + type: object + properties: + id: + type: string + age: + type: integer + format: int32 + dt: + type: string + format: date-time +tags: + - name: Examples + description: 'Example inputs' diff --git a/modules/openapi-generator/src/test/resources/3_0/3248-regression-dates.yaml b/modules/openapi-generator/src/test/resources/3_0/3248-regression-dates.yaml new file mode 100644 index 000000000000..19a6b0e4ddc8 --- /dev/null +++ b/modules/openapi-generator/src/test/resources/3_0/3248-regression-dates.yaml @@ -0,0 +1,27 @@ +openapi: 3.0.2 +info: + title: info + description: info + version: 0.1.0 + +paths: + /example/api: + get: + summary: summary + description: description + parameters: + - name: start + in: query + schema: + type: string + format: date-time + required: true + description: The start time. + responses: + 200: + description: response + content: + application/json: + schema: + type: string + diff --git a/modules/openapi-generator/src/test/resources/3_0/3248-regression.yaml b/modules/openapi-generator/src/test/resources/3_0/3248-regression.yaml new file mode 100644 index 000000000000..623cfa03e27a --- /dev/null +++ b/modules/openapi-generator/src/test/resources/3_0/3248-regression.yaml @@ -0,0 +1,47 @@ +openapi: 3.0.2 +info: + title: info + description: info + version: 0.1.0 + +paths: + /example/api: + get: + summary: summary + description: description + parameters: + - $ref: '#/components/parameters/requiredQueryParam' + - $ref: '#/components/parameters/formatParam' + responses: + 200: + description: response + content: + application/json: + schema: + type: string + +components: + parameters: + requiredQueryParam: + description: set query + in: query + name: query + required: true + schema: + type: string + formatParam: + description: set format + in: query + name: format + required: false + schema: + $ref: '#/components/schemas/format' + + schemas: + format: + default: json + description: response format + enum: + - json + - csv + type: string diff --git a/modules/openapi-generator/src/test/resources/3_0/arrayRefBody.yaml b/modules/openapi-generator/src/test/resources/3_0/arrayRefBody.yaml new file mode 100644 index 000000000000..a77e4c308091 --- /dev/null +++ b/modules/openapi-generator/src/test/resources/3_0/arrayRefBody.yaml @@ -0,0 +1,35 @@ +openapi: 3.0.0 +info: + title: '' + version: '' +paths: + /examples: + post: + tags: + - Examples + summary: Get a list of transactions + operationId: getFilteredTransactions + responses: + default: + description: successful operation + requestBody: + description: subscription payload + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/Input' +components: + schemas: + Input: + type: object + properties: + id: + type: string + age: + type: integer + format: int32 + dt: + type: string + format: date-time diff --git a/modules/openapi-generator/src/test/resources/3_0/issue_3248.yaml b/modules/openapi-generator/src/test/resources/3_0/issue_3248.yaml new file mode 100644 index 000000000000..14a9d61659f3 --- /dev/null +++ b/modules/openapi-generator/src/test/resources/3_0/issue_3248.yaml @@ -0,0 +1,121 @@ +openapi: 3.0.0 +servers: + - url: 'localhost:8080' +info: + version: 1.0.0 + title: OpenAPI Zoo + license: + name: Apache-2.0 + url: 'http://www.apache.org/licenses/LICENSE-2.0.html' +paths: + /monkeys: + get: + operationId: getMonkeys + parameters: + - $ref: '#/components/parameters/refDate' + /elephants: + get: + operationId: getElephants + parameters: + - in: query + name: someDate + required: true + schema: + type: string + format: date + /girafes: + get: + operationId: getGirafes + parameters: + - $ref: '#/components/parameters/refStatus' + /zebras: + get: + operationId: getZebras + parameters: + - in: query + name: status + required: true + schema: + type: integer + enum: [0,1] + default: 0 + /bears: + get: + operationId: getBears + parameters: + - $ref: '#/components/parameters/refCondition' + /camels: + get: + operationId: getCamels + parameters: + - in: query + name: condition + required: true + schema: + type: string + enum: + - sleeping + - awake + /pandas: + get: + operationId: getPandas + parameters: + - $ref: '#/components/parameters/refName' + /crocodiles: + get: + operationId: getCrocodiles + parameters: + - in: query + name: name + required: true + schema: + type: string + /polarBears: + get: + operationId: getPolarBears + parameters: + - $ref: '#/components/parameters/refAge' + /birds: + get: + operationId: getBirds + parameters: + - in: query + name: age + required: true + schema: + type: integer +components: + parameters: + refDate: + in: query + name: refDate + required: true + schema: + type: string + format: date + refStatus: + in: query + name: refStatus + required: true + schema: + type: integer + enum: [0,1] + default: 0 + refCondition: + in: query + name: refCondition + schema: + type: string + enum: + - sleeping + - awake + refName: + in: query + name: refName + schema: + type: string + refAge: + in: query + name: refAge + schema: + type: integer diff --git a/samples/client/petstore/go/go-petstore-withXml/api_fake.go b/samples/client/petstore/go/go-petstore-withXml/api_fake.go index 08859ad9f74c..c08e9e995e90 100644 --- a/samples/client/petstore/go/go-petstore-withXml/api_fake.go +++ b/samples/client/petstore/go/go-petstore-withXml/api_fake.go @@ -835,11 +835,7 @@ func (a *FakeApiService) TestEndpointParameters(ctx _context.Context, number flo localVarFormParams.Add("date", parameterToString(localVarOptionals.Date.Value(), "")) } if localVarOptionals != nil && localVarOptionals.DateTime.IsSet() { - paramJson, err := parameterToJson(localVarOptionals.DateTime.Value()) - if err != nil { - return nil, err - } - localVarFormParams.Add("dateTime", paramJson) + localVarFormParams.Add("dateTime", parameterToString(localVarOptionals.DateTime.Value(), "")) } if localVarOptionals != nil && localVarOptionals.Password.IsSet() { localVarFormParams.Add("password", parameterToString(localVarOptionals.Password.Value(), "")) diff --git a/samples/client/petstore/go/go-petstore/api_fake.go b/samples/client/petstore/go/go-petstore/api_fake.go index 392ab5c46efb..731d5c00f421 100644 --- a/samples/client/petstore/go/go-petstore/api_fake.go +++ b/samples/client/petstore/go/go-petstore/api_fake.go @@ -834,11 +834,7 @@ func (a *FakeApiService) TestEndpointParameters(ctx _context.Context, number flo localVarFormParams.Add("date", parameterToString(localVarOptionals.Date.Value(), "")) } if localVarOptionals != nil && localVarOptionals.DateTime.IsSet() { - paramJson, err := parameterToJson(localVarOptionals.DateTime.Value()) - if err != nil { - return nil, err - } - localVarFormParams.Add("dateTime", paramJson) + localVarFormParams.Add("dateTime", parameterToString(localVarOptionals.DateTime.Value(), "")) } if localVarOptionals != nil && localVarOptionals.Password.IsSet() { localVarFormParams.Add("password", parameterToString(localVarOptionals.Password.Value(), ""))