diff --git a/docs/generators/java-camel.md b/docs/generators/java-camel.md index c6e2cfcd7ea..84a85a253a2 100644 --- a/docs/generators/java-camel.md +++ b/docs/generators/java-camel.md @@ -119,6 +119,7 @@ These options may be applied as additional-properties (cli) or configOptions (pl |x-content-type|Specify custom value for 'Content-Type' header for operation|OPERATION|null |x-class-extra-annotation|List of custom annotations to be added to model|MODEL|null |x-field-extra-annotation|List of custom annotations to be added to property|FIELD|null +|x-operation-extra-annotation|List of custom annotations to be added to operation|OPERATION|null |x-spring-paginated|Add org.springframework.data.domain.Pageable to controller method. Can be used to handle page & size query parameters|OPERATION|false |x-version-param|Marker property that tells that this parameter would be used for endpoint versioning. Applicable for headers & query params. true/false|OPERATION_PARAMETER|null |x-pattern-message|Add this property whenever you need to customize the invalidation error message for the regex pattern of a variable|FIELD|null diff --git a/docs/generators/spring.md b/docs/generators/spring.md index 60b8f3a79d2..3b5f5f5f4d0 100644 --- a/docs/generators/spring.md +++ b/docs/generators/spring.md @@ -112,6 +112,7 @@ These options may be applied as additional-properties (cli) or configOptions (pl |x-content-type|Specify custom value for 'Content-Type' header for operation|OPERATION|null |x-class-extra-annotation|List of custom annotations to be added to model|MODEL|null |x-field-extra-annotation|List of custom annotations to be added to property|FIELD|null +|x-operation-extra-annotation|List of custom annotations to be added to operation|OPERATION|null |x-spring-paginated|Add org.springframework.data.domain.Pageable to controller method. Can be used to handle page & size query parameters|OPERATION|false |x-version-param|Marker property that tells that this parameter would be used for endpoint versioning. Applicable for headers & query params. true/false|OPERATION_PARAMETER|null |x-pattern-message|Add this property whenever you need to customize the invalidation error message for the regex pattern of a variable|FIELD|null diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/VendorExtension.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/VendorExtension.java index 77b1b834166..8d2361d0a06 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/VendorExtension.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/VendorExtension.java @@ -15,6 +15,7 @@ public enum VendorExtension { X_CONTENT_TYPE("x-content-type", ExtensionLevel.OPERATION, "Specify custom value for 'Content-Type' header for operation", null), X_CLASS_EXTRA_ANNOTATION("x-class-extra-annotation", ExtensionLevel.MODEL, "List of custom annotations to be added to model", null), X_FIELD_EXTRA_ANNOTATION("x-field-extra-annotation", ExtensionLevel.FIELD, "List of custom annotations to be added to property", null), + X_OPERATION_EXTRA_ANNOTATION("x-operation-extra-annotation", ExtensionLevel.OPERATION, "List of custom annotations to be added to operation", null), X_VERSION_PARAM("x-version-param", ExtensionLevel.OPERATION_PARAMETER, "Marker property that tells that this parameter would be used for endpoint versioning. Applicable for headers & query params. true/false", null), X_PATTERN_MESSAGE("x-pattern-message", ExtensionLevel.FIELD, "Add this property whenever you need to customize the invalidation error message for the regex pattern of a variable", null), ; diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/SpringCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/SpringCodegen.java index 6db6ad2903e..b2ae0ecd991 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/SpringCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/SpringCodegen.java @@ -1304,6 +1304,7 @@ public class SpringCodegen extends AbstractJavaCodegen @Override public List getSupportedVendorExtensions() { List extensions = super.getSupportedVendorExtensions(); + extensions.add(VendorExtension.X_OPERATION_EXTRA_ANNOTATION); extensions.add(VendorExtension.X_SPRING_PAGINATED); extensions.add(VendorExtension.X_VERSION_PARAM); extensions.add(VendorExtension.X_PATTERN_MESSAGE); diff --git a/modules/openapi-generator/src/main/resources/JavaSpring/api.mustache b/modules/openapi-generator/src/main/resources/JavaSpring/api.mustache index e02b4ba6b4b..6dea8c3ccd4 100644 --- a/modules/openapi-generator/src/main/resources/JavaSpring/api.mustache +++ b/modules/openapi-generator/src/main/resources/JavaSpring/api.mustache @@ -240,6 +240,9 @@ public interface {{classname}} { {{^useResponseEntity}} @ResponseStatus({{#springHttpStatus}}{{#responses.0}}{{{code}}}{{/responses.0}}{{/springHttpStatus}}) {{/useResponseEntity}} + {{#vendorExtensions.x-operation-extra-annotation}} + {{{.}}} + {{/vendorExtensions.x-operation-extra-annotation}} {{#jdk8-default-interface}}default {{/jdk8-default-interface}}{{#responseWrapper}}{{.}}<{{/responseWrapper}}{{#useResponseEntity}}ResponseEntity<{{/useResponseEntity}}{{>returnTypes}}{{#useResponseEntity}}>{{/useResponseEntity}}{{#responseWrapper}}>{{/responseWrapper}} {{#delegate-method}}_{{/delegate-method}}{{operationId}}( {{#allParams}}{{>queryParams}}{{>pathParams}}{{>headerParams}}{{>bodyParams}}{{>formParams}}{{>cookieParams}}{{^-last}}, {{/-last}}{{/allParams}}{{#reactive}}{{#hasParams}}, 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 1c3313381ea..5e819f5fd92 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 @@ -2899,6 +2899,109 @@ public class SpringCodegenTest { } + @Test + public void testHasOperationExtraAnnotation_issue15822() throws IOException { + File output = Files.createTempDirectory("test").toFile().getCanonicalFile(); + output.deleteOnExit(); + + final OpenAPI openAPI = TestUtils.parseFlattenSpec("src/test/resources/2_0/issue15822.yaml"); + final SpringCodegen codegen = new SpringCodegen(); + codegen.setOpenAPI(openAPI); + codegen.setOutputDir(output.getAbsolutePath()); + + codegen.additionalProperties().put(SpringCodegen.DATE_LIBRARY, "java8-localdatetime"); + codegen.additionalProperties().put(INTERFACE_ONLY, "true"); + codegen.additionalProperties().put(USE_RESPONSE_ENTITY, "false"); + codegen.additionalProperties().put(DELEGATE_PATTERN, "true"); + codegen.additionalProperties().put(REQUEST_MAPPING_OPTION, "api_interface"); + codegen.additionalProperties().put(SPRING_CONTROLLER, "true"); + + ClientOptInput input = new ClientOptInput(); + input.openAPI(openAPI); + input.config(codegen); + + DefaultGenerator generator = new DefaultGenerator(); + + Map files = generator.opts(input).generate().stream() + .collect(Collectors.toMap(File::getName, Function.identity())); + + JavaFileAssert javaFileAssert = JavaFileAssert.assertThat(files.get("TestApi.java")); + javaFileAssert + .assertMethod("_postToTest") + .assertMethodAnnotations() + .containsWithName("javax.annotation.security.RolesAllowed"); + } + + @Test + public void testHasOperationExtraAnnotation_issue12219() throws IOException { + File output = Files.createTempDirectory("test").toFile().getCanonicalFile(); + output.deleteOnExit(); + + final OpenAPI openAPI = TestUtils.parseFlattenSpec("src/test/resources/2_0/issue12219.yaml"); + final SpringCodegen codegen = new SpringCodegen(); + codegen.setOpenAPI(openAPI); + codegen.setOutputDir(output.getAbsolutePath()); + + codegen.additionalProperties().put(SpringCodegen.DATE_LIBRARY, "java8-localdatetime"); + codegen.additionalProperties().put(INTERFACE_ONLY, "true"); + codegen.additionalProperties().put(USE_RESPONSE_ENTITY, "false"); + codegen.additionalProperties().put(DELEGATE_PATTERN, "true"); + codegen.additionalProperties().put(REQUEST_MAPPING_OPTION, "api_interface"); + codegen.additionalProperties().put(SPRING_CONTROLLER, "true"); + + ClientOptInput input = new ClientOptInput(); + input.openAPI(openAPI); + input.config(codegen); + + DefaultGenerator generator = new DefaultGenerator(); + + Map files = generator.opts(input).generate().stream() + .collect(Collectors.toMap(File::getName, Function.identity())); + + JavaFileAssert javaFileAssert = JavaFileAssert.assertThat(files.get("TestApi.java")); + javaFileAssert + .assertMethod("_postToTest") + .assertMethodAnnotations() + .containsWithName("javax.annotation.security.RolesAllowed") + .containsWithName("org.springframework.security.access.annotation.Secured") + .containsWithName("org.springframework.security.access.prepost.PreAuthorize"); + } + + @Test + public void testHasOperationExtraAnnotation_issue12219_array() throws IOException { + File output = Files.createTempDirectory("test").toFile().getCanonicalFile(); + output.deleteOnExit(); + + final OpenAPI openAPI = TestUtils.parseFlattenSpec("src/test/resources/2_0/issue12219_array.yaml"); + final SpringCodegen codegen = new SpringCodegen(); + codegen.setOpenAPI(openAPI); + codegen.setOutputDir(output.getAbsolutePath()); + + codegen.additionalProperties().put(SpringCodegen.DATE_LIBRARY, "java8-localdatetime"); + codegen.additionalProperties().put(INTERFACE_ONLY, "true"); + codegen.additionalProperties().put(USE_RESPONSE_ENTITY, "false"); + codegen.additionalProperties().put(DELEGATE_PATTERN, "true"); + codegen.additionalProperties().put(REQUEST_MAPPING_OPTION, "api_interface"); + codegen.additionalProperties().put(SPRING_CONTROLLER, "true"); + + ClientOptInput input = new ClientOptInput(); + input.openAPI(openAPI); + input.config(codegen); + + DefaultGenerator generator = new DefaultGenerator(); + + Map files = generator.opts(input).generate().stream() + .collect(Collectors.toMap(File::getName, Function.identity())); + + JavaFileAssert javaFileAssert = JavaFileAssert.assertThat(files.get("TestApi.java")); + javaFileAssert + .assertMethod("_postToTest") + .assertMethodAnnotations() + .containsWithName("javax.annotation.security.RolesAllowed") + .containsWithName("org.springframework.security.access.annotation.Secured") + .containsWithName("org.springframework.security.access.prepost.PreAuthorize"); + } + @Test public void multiLineOperationDescription() throws IOException { diff --git a/modules/openapi-generator/src/test/resources/2_0/issue12219.yaml b/modules/openapi-generator/src/test/resources/2_0/issue12219.yaml new file mode 100644 index 00000000000..5d261286ca8 --- /dev/null +++ b/modules/openapi-generator/src/test/resources/2_0/issue12219.yaml @@ -0,0 +1,53 @@ +swagger: '2.0' +info: + description: 'blah' + version: 1.0.0 + title: sample spec +host: fake.site.com +tags: + - name: Test +schemes: + - https +paths: + /test: + post: + summary: Post to test + description: '' + operationId: postToTest + consumes: + - application/json + produces: + - application/json + parameters: + - in: body + name: Obj to test + schema: + $ref: '#/definitions/ObjTest' + responses: + '201': + description: successful operation + schema: + $ref: '#/definitions/ObjTest' + x-operation-extra-annotation: '@javax.annotation.security.RolesAllowed({"ROLE_TEST"}) @org.springframework.security.access.annotation.Secured({"ROLE_TEST"}) @org.springframework.security.access.prepost.PreAuthorize("hasRole(''ROLE_TEST'') and hasRole(''ROLE_TEST_TWO'')")' +definitions: + ObjTest: + description: A model to return + type: object + properties: + field1: + type: integer + format: int64 + field2: + type: string + pattern: "\\w" + x-pattern-message: "Only letters, numbers and underscore" + field3: + type: string + pattern: "\\w" + EnumTest: + title: An enum to test + type: string + enum: + - ONE + - Two + - three \ No newline at end of file diff --git a/modules/openapi-generator/src/test/resources/2_0/issue12219_array.yaml b/modules/openapi-generator/src/test/resources/2_0/issue12219_array.yaml new file mode 100644 index 00000000000..393f6a25812 --- /dev/null +++ b/modules/openapi-generator/src/test/resources/2_0/issue12219_array.yaml @@ -0,0 +1,56 @@ +swagger: '2.0' +info: + description: 'blah' + version: 1.0.0 + title: sample spec +host: fake.site.com +tags: + - name: Test +schemes: + - https +paths: + /test: + post: + summary: Post to test + description: '' + operationId: postToTest + consumes: + - application/json + produces: + - application/json + parameters: + - in: body + name: Obj to test + schema: + $ref: '#/definitions/ObjTest' + responses: + '201': + description: successful operation + schema: + $ref: '#/definitions/ObjTest' + x-operation-extra-annotation: + - '@javax.annotation.security.RolesAllowed({"ROLE_TEST"})' + - '@org.springframework.security.access.annotation.Secured({"ROLE_TEST"})' + - '@org.springframework.security.access.prepost.PreAuthorize("hasRole(''ROLE_TEST'') and hasRole(''ROLE_TEST_TWO'')")' +definitions: + ObjTest: + description: A model to return + type: object + properties: + field1: + type: integer + format: int64 + field2: + type: string + pattern: "\\w" + x-pattern-message: "Only letters, numbers and underscore" + field3: + type: string + pattern: "\\w" + EnumTest: + title: An enum to test + type: string + enum: + - ONE + - Two + - three \ No newline at end of file diff --git a/modules/openapi-generator/src/test/resources/2_0/issue15822.yaml b/modules/openapi-generator/src/test/resources/2_0/issue15822.yaml new file mode 100644 index 00000000000..408ee5665c9 --- /dev/null +++ b/modules/openapi-generator/src/test/resources/2_0/issue15822.yaml @@ -0,0 +1,53 @@ +swagger: '2.0' +info: + description: 'blah' + version: 1.0.0 + title: sample spec +host: fake.site.com +tags: + - name: Test +schemes: + - https +paths: + /test: + post: + summary: Post to test + description: '' + operationId: postToTest + consumes: + - application/json + produces: + - application/json + parameters: + - in: body + name: Obj to test + schema: + $ref: '#/definitions/ObjTest' + responses: + '201': + description: successful operation + schema: + $ref: '#/definitions/ObjTest' + x-operation-extra-annotation: '@javax.annotation.security.RolesAllowed({"ROLE_TEST"})' +definitions: + ObjTest: + description: A model to return + type: object + properties: + field1: + type: integer + format: int64 + field2: + type: string + pattern: "\\w" + x-pattern-message: "Only letters, numbers and underscore" + field3: + type: string + pattern: "\\w" + EnumTest: + title: An enum to test + type: string + enum: + - ONE + - Two + - three \ No newline at end of file