├── .gitignore ├── .gitlab-ci.yml ├── CHANGELOG.md ├── LICENSE.txt ├── README.md ├── api-docs.json ├── axenapi_generator_Rabbit_producer_generation.patch ├── build.gradle ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── settings.gradle └── src ├── main ├── java │ └── pro │ │ └── axenix_innovation │ │ └── axenapi │ │ └── codegen │ │ ├── KafkaCodegenGenerator.java │ │ ├── MessageBrokerCodegen.java │ │ ├── MyCodegen.java │ │ └── helper │ │ ├── EmptyHelper.java │ │ ├── JmsHelper.java │ │ ├── KafkaHelper.java │ │ ├── LibHelper.java │ │ └── RabbitHelper.java └── resources │ ├── META-INF │ └── services │ │ └── org.openapitools.codegen.CodegenConfig │ ├── gradle │ └── gradle-wrapper.jar │ └── templates │ ├── application.mustache │ ├── axenapi_properties.mustache │ ├── client.mustache │ ├── gradle_build_spring_boot_2.mustache │ ├── gradle_properties.mustache │ ├── gradle_wrapper_properties.mustache │ ├── gradlew.mustache │ ├── gradlew_bat.mustache │ ├── jms │ ├── clientImpl.mustache │ ├── listener.mustache │ ├── senderService.mustache │ ├── senderServiceConfig.mustache │ ├── senderServiceImpl.mustache │ ├── spring_2_autoconfig.mustache │ └── spring_3_autoconfig.mustache │ ├── kafka │ ├── KafkaConsumerConfig.mustache │ ├── clientImpl.mustache │ ├── listener.mustache │ ├── producerConfig.mustache │ ├── senderService.mustache │ ├── senderServiceConfig.mustache │ ├── senderServiceImpl.mustache │ ├── spring_2_autoconfig.mustache │ └── spring_3_autoconfig.mustache │ ├── listenerService.mustache │ ├── listenerServiceImpl.mustache │ ├── model.mustache │ ├── pojo.mustache │ ├── pom.mustache │ ├── rabbit │ ├── clientImpl.mustache │ ├── listener.mustache │ ├── senderService.mustache │ ├── senderServiceConfig.mustache │ ├── senderServiceImpl.mustache │ ├── spring_2_autoconfig.mustache │ └── spring_3_autoconfig.mustache │ └── settings_gradle.mustache └── test └── java ├── com └── kafka │ └── company │ └── codegen │ ├── KafkaClientServerGenerationTest.java │ ├── KafkaCodegenGeneratorTest.java │ ├── MessageBrokerCodegenGeneratorTest.java │ └── MessageBrokerCodegenGeneratorTestGradle.java └── resources └── json ├── emptySpec.json ├── test-jms.json ├── test-kafka.json └── test-rabbit.json /.gitignore: -------------------------------------------------------------------------------- 1 | ### IntelliJ IDEA ### 2 | .idea 3 | *.iws 4 | *.iml 5 | *.ipr 6 | out/ 7 | !**/src/main/**/out/ 8 | !**/src/test/**/out/ 9 | 10 | ### NetBeans ### 11 | /nbproject/private/ 12 | /nbbuild/ 13 | /dist/ 14 | /nbdist/ 15 | /.nb-gradle/ 16 | 17 | ### VS Code ### 18 | .vscode/ 19 | /.gradle/ 20 | /build/ 21 | -------------------------------------------------------------------------------- /.gitlab-ci.yml: -------------------------------------------------------------------------------- 1 | include: 2 | - project: 'axenapi/devops' 3 | ref: main 4 | file: 'component_build_ci.yml' 5 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ### version 2.0.1 2 | 1) Generator code for all application [MessageBrokerCodegen](src/main/java/pro/axenix_innovation/axenapi/codegen/MessageBrokerCodegen.java) 3 | 2) Use generator in openapi-generarator cli (more info in readme) 4 | 3) 5 | ### version 1.0.2 6 | 1) Use generator in classpath with "org.openapi.generator" gradle plugin 7 | ### version 1.0.1 8 | 1) GPG signing 9 | ### version 1.0.0 10 | 1) create generation server and client side for application - kafka handlers, kafka producers 11 | 2) add support parameters in generator: 12 | 1) openApiPath 13 | 2) outDir 14 | 3) srcDir 15 | 4) listenerPackage 16 | 5) modelPackage 17 | 6) useSpring3 18 | 7) kafkaClient 19 | 8) interfaceOnly 20 | 9) resultWrapper 21 | 10) securityAnnotation 22 | 11) sendBytes 23 | 12) useAutoconfig 24 | 13) generateMessageId 25 | 14) generateCorrelationId 26 | 15) messageIdName 27 | 16) correlationIdName 28 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | APPENDIX: How to apply the Apache License to your work. 180 | 181 | To apply the Apache License to your work, attach the following 182 | boilerplate notice, with the fields enclosed by brackets "[]" 183 | replaced with your own identifying information. (Don't include 184 | the brackets!) The text should be enclosed in the appropriate 185 | comment syntax for the file format. We also recommend that a 186 | file or class name and description of purpose be included on the 187 | same "printed page" as the copyright notice for easier 188 | identification within third-party archives. 189 | 190 | Copyright (C) 2023 Axenix Innovations LLC 191 | 192 | Licensed under the Apache License, Version 2.0 (the "License"); 193 | you may not use this file except in compliance with the License. 194 | You may obtain a copy of the License at 195 | 196 | http://www.apache.org/licenses/LICENSE-2.0 197 | 198 | Unless required by applicable law or agreed to in writing, software 199 | distributed under the License is distributed on an "AS IS" BASIS, 200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 201 | See the License for the specific language governing permissions and 202 | limitations under the License. 203 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Add Axenapi-generator as a classpath. First steps: 2 | 1) Add Axenapi-generator in classpath like this 3 | ```groovy 4 | buildscript { 5 | dependencies { 6 | classpath "pro.axenix-innovation:axenapi-generator:1.0.2-SNAPSHOT" 7 | } 8 | } 9 | ``` 10 | 2) Add openapi-generator (you can use another version of generator) 11 | ```groovy 12 | plugins { 13 | id "org.openapi.generator" version '7.0.0' 14 | } 15 | ``` 16 | 3) Add configuration for openapiGenerate. **Use "kafka-codegen" as "generatorName".** 17 | ```groovy 18 | openApiGenerate { 19 | generatorName = "kafka-codegen" 20 | inputSpec = getProjectDir().getAbsolutePath() + '/src/main/resources/joker.json' 21 | outputDir = getProjectDir().getAbsolutePath() + '/build' 22 | globalProperties = [ 23 | apis : "", 24 | models : "", 25 | supportingFiles: 'ApiUtil.java' 26 | ] 27 | skipOverwrite = true 28 | configOptions = [ 29 | useSpringBoot3 : "true", 30 | listenerPackage: 'axenapi.generated', 31 | modelPackage : 'axenapi.generated.model', 32 | kafkaClient : "false", 33 | interfaceOnly : "false", 34 | useSpring3 : "false", 35 | resultWrapper : "java.util.concurrent.CompletableFuture" 36 | ] 37 | } 38 | ``` 39 | 40 | 41 | To use generator by openapi-generarator cli use command line: 42 | ``` 43 | $ java -cp "axenapi_generator/build/libs/axenapi-generator-2.0.0.jar;openapi-generator-cli.jar" org.openapitools.codegen.OpenAPIGenerator generate -g messageBroker -o out/ -i api-docs.json --additional-properties=kafkaBootstrap=localhost:29092 44 | ``` 45 | -------------------------------------------------------------------------------- /api-docs.json: -------------------------------------------------------------------------------- 1 | { 2 | "openapi" : "3.0.1", 3 | "info" : { 4 | "title" : "New_Service_1", 5 | "description" : "axenapi Specification for New_Service_1", 6 | "version" : "1.0.0" 7 | }, 8 | "paths" : { 9 | "/kafka/group_1/New_Topic_1/Event" : { 10 | "description" : "Operation for Event", 11 | "post" : { 12 | "requestBody" : { 13 | "content" : { 14 | "application/json" : { 15 | "schema" : { 16 | "$ref" : "#/components/schemas/Event" 17 | } 18 | } 19 | }, 20 | "required" : true 21 | }, 22 | "responses" : { 23 | "200" : { 24 | "description" : "Success. No content." 25 | } 26 | } 27 | } 28 | } 29 | }, 30 | "components" : { 31 | "schemas" : { 32 | "Event" : { 33 | "required" : [ "countryName", "locality", "region" ], 34 | "type" : "object", 35 | "properties" : { 36 | "postOfficeBox" : { 37 | "type" : "string" 38 | }, 39 | "extendedAddress" : { 40 | "type" : "string" 41 | }, 42 | "streetAddress" : { 43 | "type" : "string" 44 | }, 45 | "locality" : { 46 | "type" : "string" 47 | }, 48 | "region" : { 49 | "type" : "string" 50 | }, 51 | "postalCode" : { 52 | "type" : "string" 53 | }, 54 | "countryName" : { 55 | "type" : "string" 56 | } 57 | }, 58 | "description" : "An address similar to http://microformats.org/wiki/h-card" 59 | } 60 | } 61 | } 62 | } -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'java' 3 | id 'maven-publish' 4 | id 'signing' 5 | } 6 | 7 | group = 'pro.axenix-innovation' 8 | description = 'axenapi-codegen-generator' 9 | sourceCompatibility = '11' 10 | 11 | String repoURL = "" 12 | String sonatypeRepoURL = "" 13 | 14 | if (version.endsWith('-SNAPSHOT')) { 15 | repoURL = System.getenv('NEXUS_SERVER_URL_SNAPSHOT').toString() 16 | sonatypeRepoURL = "https://s01.oss.sonatype.org/content/repositories/snapshots/" 17 | } else { 18 | repoURL = System.getenv('NEXUS_SERVER_URL_RELEASE').toString() 19 | sonatypeRepoURL = "https://s01.oss.sonatype.org/service/local/staging/deploy/maven2/" 20 | } 21 | 22 | String nexus_user = System.getenv('NEXUS_USER') 23 | String nexus_password = System.getenv('NEXUS_PASSWORD') 24 | String sonatype_nexus_user = System.getenv('SONATYPE_NEXUS_USER') 25 | String sonatype_nexus_password = System.getenv('SONATYPE_NEXUS_PASSWORD') 26 | 27 | repositories { 28 | mavenLocal() 29 | mavenCentral() 30 | } 31 | 32 | java { 33 | withJavadocJar() 34 | withSourcesJar() 35 | } 36 | 37 | ext.isReleaseVersion = !version.endsWith("SNAPSHOT") 38 | signing { 39 | required { isReleaseVersion && gradle.taskGraph.hasTask("publish") } 40 | sign publishing.publications 41 | } 42 | 43 | publishing { 44 | publications { 45 | mavenJava(MavenPublication) { 46 | artifactId project.name 47 | groupId project.group 48 | version version 49 | from components.java 50 | pom { 51 | name = 'AxenAPI' 52 | description = 'Library for generation Spring conrollers by consumers (kafka consumers and other).' 53 | url = 'https://github.com/AxenAPI/axenapi-generator' 54 | licenses { 55 | license { 56 | name = 'The Apache License, Version 2.0' 57 | url = 'http://www.apache.org/licenses/LICENSE-2.0.txt' 58 | } 59 | } 60 | developers { 61 | developer { 62 | id = "axenix-innovation" 63 | name = "AxenAPI-Generator Contributors" 64 | email = "axenapi-info@axenix.pro" 65 | } 66 | } 67 | scm { 68 | url = "https://github.com/AxenAPI/axenapi-generator" 69 | connection = "scm:git:git://github.com/AxenAPI/axenapi-generator.git" 70 | developerConnection = "scm:git:ssh://git@github.com:AxenAPI/axenapi-generator.git" 71 | } 72 | issueManagement { 73 | system = "GitHub" 74 | url = "https://github.com/AxenAPI/axenapi-generator/issues" 75 | } 76 | } 77 | } 78 | } 79 | repositories { 80 | maven { 81 | allowInsecureProtocol = true 82 | url repoURL 83 | credentials.setUsername(nexus_user) 84 | credentials.setPassword(nexus_password) 85 | } 86 | maven { 87 | allowInsecureProtocol = true 88 | url sonatypeRepoURL 89 | credentials.setUsername(sonatype_nexus_user) 90 | credentials.setPassword(sonatype_nexus_password) 91 | } 92 | } 93 | } 94 | 95 | dependencies { 96 | implementation 'junit:junit:4.13.2' 97 | implementation 'org.openapitools:openapi-generator:6.4.0' 98 | } 99 | 100 | tasks.withType(JavaCompile) { 101 | options.encoding = 'UTF-8' 102 | } -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | version=2.0.1 2 | #gpr.user= 3 | #gpr.key= 4 | signing.keyId=069FD068 5 | signing.secretKeyRingFile=./gpg-secret-dir/secring.gpg -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AxenAPI/axenapi-generator/c9a436e734e80b8a9d3eb199a62c87eadf291e63/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.2.1-bin.zip 4 | networkTimeout=10000 5 | validateDistributionUrl=true 6 | zipStoreBase=GRADLE_USER_HOME 7 | zipStorePath=wrapper/dists 8 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # 4 | # Copyright © 2015-2021 the original authors. 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # https://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # 18 | 19 | ############################################################################## 20 | # 21 | # Gradle start up script for POSIX generated by Gradle. 22 | # 23 | # Important for running: 24 | # 25 | # (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is 26 | # noncompliant, but you have some other compliant shell such as ksh or 27 | # bash, then to run this script, type that shell name before the whole 28 | # command line, like: 29 | # 30 | # ksh Gradle 31 | # 32 | # Busybox and similar reduced shells will NOT work, because this script 33 | # requires all of these POSIX shell features: 34 | # * functions; 35 | # * expansions «$var», «${var}», «${var:-default}», «${var+SET}», 36 | # «${var#prefix}», «${var%suffix}», and «$( cmd )»; 37 | # * compound commands having a testable exit status, especially «case»; 38 | # * various built-in commands including «command», «set», and «ulimit». 39 | # 40 | # Important for patching: 41 | # 42 | # (2) This script targets any POSIX shell, so it avoids extensions provided 43 | # by Bash, Ksh, etc; in particular arrays are avoided. 44 | # 45 | # The "traditional" practice of packing multiple parameters into a 46 | # space-separated string is a well documented source of bugs and security 47 | # problems, so this is (mostly) avoided, by progressively accumulating 48 | # options in "$@", and eventually passing that to Java. 49 | # 50 | # Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, 51 | # and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; 52 | # see the in-line comments for details. 53 | # 54 | # There are tweaks for specific operating systems such as AIX, CygWin, 55 | # Darwin, MinGW, and NonStop. 56 | # 57 | # (3) This script is generated from the Groovy template 58 | # https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt 59 | # within the Gradle project. 60 | # 61 | # You can find Gradle at https://github.com/gradle/gradle/. 62 | # 63 | ############################################################################## 64 | 65 | # Attempt to set APP_HOME 66 | 67 | # Resolve links: $0 may be a link 68 | app_path=$0 69 | 70 | # Need this for daisy-chained symlinks. 71 | while 72 | APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path 73 | [ -h "$app_path" ] 74 | do 75 | ls=$( ls -ld "$app_path" ) 76 | link=${ls#*' -> '} 77 | case $link in #( 78 | /*) app_path=$link ;; #( 79 | *) app_path=$APP_HOME$link ;; 80 | esac 81 | done 82 | 83 | # This is normally unused 84 | # shellcheck disable=SC2034 85 | APP_BASE_NAME=${0##*/} 86 | APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit 87 | 88 | # Use the maximum available, or set MAX_FD != -1 to use that value. 89 | MAX_FD=maximum 90 | 91 | warn () { 92 | echo "$*" 93 | } >&2 94 | 95 | die () { 96 | echo 97 | echo "$*" 98 | echo 99 | exit 1 100 | } >&2 101 | 102 | # OS specific support (must be 'true' or 'false'). 103 | cygwin=false 104 | msys=false 105 | darwin=false 106 | nonstop=false 107 | case "$( uname )" in #( 108 | CYGWIN* ) cygwin=true ;; #( 109 | Darwin* ) darwin=true ;; #( 110 | MSYS* | MINGW* ) msys=true ;; #( 111 | NONSTOP* ) nonstop=true ;; 112 | esac 113 | 114 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 115 | 116 | 117 | # Determine the Java command to use to start the JVM. 118 | if [ -n "$JAVA_HOME" ] ; then 119 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 120 | # IBM's JDK on AIX uses strange locations for the executables 121 | JAVACMD=$JAVA_HOME/jre/sh/java 122 | else 123 | JAVACMD=$JAVA_HOME/bin/java 124 | fi 125 | if [ ! -x "$JAVACMD" ] ; then 126 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 127 | 128 | Please set the JAVA_HOME variable in your environment to match the 129 | location of your Java installation." 130 | fi 131 | else 132 | JAVACMD=java 133 | if ! command -v java >/dev/null 2>&1 134 | then 135 | die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 136 | 137 | Please set the JAVA_HOME variable in your environment to match the 138 | location of your Java installation." 139 | fi 140 | fi 141 | 142 | # Increase the maximum file descriptors if we can. 143 | if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then 144 | case $MAX_FD in #( 145 | max*) 146 | # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. 147 | # shellcheck disable=SC3045 148 | MAX_FD=$( ulimit -H -n ) || 149 | warn "Could not query maximum file descriptor limit" 150 | esac 151 | case $MAX_FD in #( 152 | '' | soft) :;; #( 153 | *) 154 | # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. 155 | # shellcheck disable=SC3045 156 | ulimit -n "$MAX_FD" || 157 | warn "Could not set maximum file descriptor limit to $MAX_FD" 158 | esac 159 | fi 160 | 161 | # Collect all arguments for the java command, stacking in reverse order: 162 | # * args from the command line 163 | # * the main class name 164 | # * -classpath 165 | # * -D...appname settings 166 | # * --module-path (only if needed) 167 | # * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. 168 | 169 | # For Cygwin or MSYS, switch paths to Windows format before running java 170 | if "$cygwin" || "$msys" ; then 171 | APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) 172 | CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) 173 | 174 | JAVACMD=$( cygpath --unix "$JAVACMD" ) 175 | 176 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 177 | for arg do 178 | if 179 | case $arg in #( 180 | -*) false ;; # don't mess with options #( 181 | /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath 182 | [ -e "$t" ] ;; #( 183 | *) false ;; 184 | esac 185 | then 186 | arg=$( cygpath --path --ignore --mixed "$arg" ) 187 | fi 188 | # Roll the args list around exactly as many times as the number of 189 | # args, so each arg winds up back in the position where it started, but 190 | # possibly modified. 191 | # 192 | # NB: a `for` loop captures its iteration list before it begins, so 193 | # changing the positional parameters here affects neither the number of 194 | # iterations, nor the values presented in `arg`. 195 | shift # remove old arg 196 | set -- "$@" "$arg" # push replacement arg 197 | done 198 | fi 199 | 200 | 201 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 202 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' 203 | 204 | # Collect all arguments for the java command; 205 | # * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of 206 | # shell script including quotes and variable substitutions, so put them in 207 | # double quotes to make sure that they get re-expanded; and 208 | # * put everything else in single quotes, so that it's not re-expanded. 209 | 210 | set -- \ 211 | "-Dorg.gradle.appname=$APP_BASE_NAME" \ 212 | -classpath "$CLASSPATH" \ 213 | org.gradle.wrapper.GradleWrapperMain \ 214 | "$@" 215 | 216 | # Stop when "xargs" is not available. 217 | if ! command -v xargs >/dev/null 2>&1 218 | then 219 | die "xargs is not available" 220 | fi 221 | 222 | # Use "xargs" to parse quoted args. 223 | # 224 | # With -n1 it outputs one arg per line, with the quotes and backslashes removed. 225 | # 226 | # In Bash we could simply go: 227 | # 228 | # readarray ARGS < <( xargs -n1 <<<"$var" ) && 229 | # set -- "${ARGS[@]}" "$@" 230 | # 231 | # but POSIX shell has neither arrays nor command substitution, so instead we 232 | # post-process each arg (as a line of input to sed) to backslash-escape any 233 | # character that might be a shell metacharacter, then use eval to reverse 234 | # that process (while maintaining the separation between arguments), and wrap 235 | # the whole thing up as a single "set" statement. 236 | # 237 | # This will of course break if any of these variables contains a newline or 238 | # an unmatched quote. 239 | # 240 | 241 | eval "set -- $( 242 | printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | 243 | xargs -n1 | 244 | sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | 245 | tr '\n' ' ' 246 | )" '"$@"' 247 | 248 | exec "$JAVACMD" "$@" 249 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @rem 2 | @rem Copyright 2015 the original author or authors. 3 | @rem 4 | @rem Licensed under the Apache License, Version 2.0 (the "License"); 5 | @rem you may not use this file except in compliance with the License. 6 | @rem You may obtain a copy of the License at 7 | @rem 8 | @rem https://www.apache.org/licenses/LICENSE-2.0 9 | @rem 10 | @rem Unless required by applicable law or agreed to in writing, software 11 | @rem distributed under the License is distributed on an "AS IS" BASIS, 12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | @rem See the License for the specific language governing permissions and 14 | @rem limitations under the License. 15 | @rem 16 | 17 | @if "%DEBUG%"=="" @echo off 18 | @rem ########################################################################## 19 | @rem 20 | @rem Gradle startup script for Windows 21 | @rem 22 | @rem ########################################################################## 23 | 24 | @rem Set local scope for the variables with windows NT shell 25 | if "%OS%"=="Windows_NT" setlocal 26 | 27 | set DIRNAME=%~dp0 28 | if "%DIRNAME%"=="" set DIRNAME=. 29 | @rem This is normally unused 30 | set APP_BASE_NAME=%~n0 31 | set APP_HOME=%DIRNAME% 32 | 33 | @rem Resolve any "." and ".." in APP_HOME to make it shorter. 34 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 35 | 36 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 37 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 38 | 39 | @rem Find java.exe 40 | if defined JAVA_HOME goto findJavaFromJavaHome 41 | 42 | set JAVA_EXE=java.exe 43 | %JAVA_EXE% -version >NUL 2>&1 44 | if %ERRORLEVEL% equ 0 goto execute 45 | 46 | echo. 47 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 48 | echo. 49 | echo Please set the JAVA_HOME variable in your environment to match the 50 | echo location of your Java installation. 51 | 52 | goto fail 53 | 54 | :findJavaFromJavaHome 55 | set JAVA_HOME=%JAVA_HOME:"=% 56 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 57 | 58 | if exist "%JAVA_EXE%" goto execute 59 | 60 | echo. 61 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 62 | echo. 63 | echo Please set the JAVA_HOME variable in your environment to match the 64 | echo location of your Java installation. 65 | 66 | goto fail 67 | 68 | :execute 69 | @rem Setup the command line 70 | 71 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 72 | 73 | 74 | @rem Execute Gradle 75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* 76 | 77 | :end 78 | @rem End local scope for the variables with windows NT shell 79 | if %ERRORLEVEL% equ 0 goto mainEnd 80 | 81 | :fail 82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 83 | rem the _cmd.exe /c_ return code! 84 | set EXIT_CODE=%ERRORLEVEL% 85 | if %EXIT_CODE% equ 0 set EXIT_CODE=1 86 | if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% 87 | exit /b %EXIT_CODE% 88 | 89 | :mainEnd 90 | if "%OS%"=="Windows_NT" endlocal 91 | 92 | :omega 93 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | rootProject.name = 'axenapi-generator' 2 | -------------------------------------------------------------------------------- /src/main/java/pro/axenix_innovation/axenapi/codegen/KafkaCodegenGenerator.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2023 Axenix Innovations LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package pro.axenix_innovation.axenapi.codegen; 18 | 19 | import io.swagger.v3.oas.models.Operation; 20 | import org.openapitools.codegen.*; 21 | import org.openapitools.codegen.languages.SpringCodegen; 22 | import org.openapitools.codegen.meta.GeneratorMetadata; 23 | import org.openapitools.codegen.meta.Stability; 24 | import org.slf4j.Logger; 25 | import org.slf4j.LoggerFactory; 26 | import pro.axenix_innovation.axenapi.codegen.helper.JmsHelper; 27 | import pro.axenix_innovation.axenapi.codegen.helper.KafkaHelper; 28 | import pro.axenix_innovation.axenapi.codegen.helper.LibHelper; 29 | import pro.axenix_innovation.axenapi.codegen.helper.RabbitHelper; 30 | 31 | import java.util.*; 32 | import java.util.stream.Collectors; 33 | 34 | import static org.openapitools.codegen.utils.StringUtils.camelize; 35 | 36 | public class KafkaCodegenGenerator extends SpringCodegen implements MyCodegen { 37 | private static final String MODEL_TEMPLATE_NAME = "model.mustache"; 38 | public static final String CAMEL_REST_COMPONENT = "camelRestComponent"; 39 | public static final String CAMEL_REST_BINDING_MODE = "camelRestBindingMode"; 40 | public static final String CAMEL_REST_CLIENT_REQUEST_VALIDATION = "camelRestClientRequestValidation"; 41 | public static final String CAMEL_USE_DEFAULT_VALIDATION_ERROR_PROCESSOR = "camelUseDefaultValidationErrorProcessor"; 42 | public static final String CAMEL_VALIDATION_ERROR_PROCESSOR = "camelValidationErrorProcessor"; 43 | public static final String CAMEL_SECURITY_DEFINITIONS = "camelSecurityDefinitions"; 44 | public static final String CAMEL_DATAFORMAT_PROPERTIES = "camelDataformatProperties"; 45 | public static final String RESULT_WRAPPER = "resultWrapper"; 46 | public static final String SECURITY_ANNOTATION = "securityAnnotation"; 47 | public static final String SEND_BYTES = "sendBytes"; 48 | public static final String MESSAGE_ID_NAME = "messageIdName"; 49 | public static final String CORRELATION_ID_NAME = "correlationIdName"; 50 | public static final String GENERATE_MESSAGE_ID = "generateMessageId"; 51 | public static final String GENERATE_CORRELATION_ID = "generateCorrelationId"; 52 | 53 | private static final String IS_KAFKA_CLIENT = "kafkaClient"; 54 | 55 | private String camelRestComponent = "servlet"; 56 | private String camelRestBindingMode = "auto"; 57 | private boolean camelRestClientRequestValidation = false; 58 | private boolean camelUseDefaultValidationErrorProcessor = true; 59 | private String camelValidationErrorProcessor = "validationErrorProcessor"; 60 | private boolean camelSecurityDefinitions = true; 61 | private String camelDataformatProperties = ""; 62 | 63 | private String resultWrapper = ""; 64 | 65 | private String securityAnnotation = ""; 66 | 67 | private boolean sendBytes = false; 68 | 69 | private boolean fromAxenAPIPlugin = false; 70 | private String messageIdName = "kafka_messageId"; 71 | private String correlationIdName = "kafka_correlationId"; 72 | private Boolean generateMessageId = false; 73 | private Boolean generateCorrelationId = false; 74 | 75 | private LibHelper libHelper; 76 | 77 | private boolean useAutoConfig = true; 78 | 79 | 80 | public boolean isKafkaClient() { 81 | return isKafkaClient; 82 | } 83 | 84 | @Override 85 | public boolean isUseGradle() { 86 | return false; 87 | } 88 | 89 | public void setKafkaClient(boolean kafkaClient) { 90 | isKafkaClient = kafkaClient; 91 | } 92 | 93 | private boolean isKafkaClient = false; 94 | 95 | public String getResultWrapper() { 96 | return resultWrapper; 97 | } 98 | 99 | public void setResultWrapper(String resultWrapper) { 100 | this.resultWrapper = resultWrapper; 101 | } 102 | 103 | private static final Logger LOGGER = LoggerFactory.getLogger(KafkaCodegenGenerator.class); 104 | 105 | @Override 106 | public CodegenType getTag() { 107 | return CodegenType.SERVER; 108 | } 109 | 110 | @Override 111 | public String getName() { 112 | return "kafka-codegen"; 113 | } 114 | 115 | @Override 116 | public String getHelp() { 117 | return "Generates message communication (e.g. Kafka) participants (listeners/producers)"; 118 | } 119 | 120 | public KafkaCodegenGenerator() { 121 | super(); 122 | templateDir = "templates"; 123 | addCliOptions(); 124 | artifactId = "kafka-codegen"; 125 | super.library = ""; 126 | fromAxenAPIPlugin = false; 127 | } 128 | 129 | @Override 130 | public String toApiName(String name) { 131 | if (name.isEmpty()) { 132 | return "DefaultListener"; 133 | } 134 | name = sanitizeName(name); 135 | 136 | if (isKafkaClient) return camelize(name) + "Producer"; 137 | 138 | return camelize(name); 139 | } 140 | 141 | private void addCliOptions() { 142 | cliOptions.add(new CliOption(CAMEL_REST_COMPONENT, "name of the Camel component to use as the REST consumer").defaultValue(camelRestComponent)); 143 | cliOptions.add(new CliOption(CAMEL_REST_BINDING_MODE, "binding mode to be used by the REST consumer").defaultValue(camelRestBindingMode)); 144 | cliOptions.add(CliOption.newBoolean(CAMEL_REST_CLIENT_REQUEST_VALIDATION, "enable validation of the client request to check whether the Content-Type and Accept headers from the client is supported by the Rest-DSL configuration", camelRestClientRequestValidation)); 145 | cliOptions.add(CliOption.newBoolean(CAMEL_USE_DEFAULT_VALIDATION_ERROR_PROCESSOR, "generate default validation error processor", camelUseDefaultValidationErrorProcessor)); 146 | cliOptions.add(new CliOption(CAMEL_VALIDATION_ERROR_PROCESSOR, "validation error processor bean name").defaultValue(camelValidationErrorProcessor)); 147 | cliOptions.add(CliOption.newBoolean(CAMEL_SECURITY_DEFINITIONS, "generate camel security definitions", camelSecurityDefinitions)); 148 | cliOptions.add(new CliOption(CAMEL_DATAFORMAT_PROPERTIES, "list of dataformat properties separated by comma (propertyName1=propertyValue2,...").defaultValue(camelDataformatProperties)); 149 | 150 | cliOptions.add(new CliOption("listenerPackage", "Yes\tNo default value\tPackage, in which client/listeners will be generated.")); 151 | cliOptions.add(new CliOption("modelPackage", "Package, in wich models will be generated (Data Transfer Object).")); 152 | cliOptions.add(CliOption.newBoolean("useSpring3", "If true, then code will be generated for springboot 3.1. If false, then code will be generated for spring boot 2.7.", false)); 153 | cliOptions.add(CliOption.newBoolean(IS_KAFKA_CLIENT, "If true, client code(producer) will be generated, if false - server code(consumer).", false)); 154 | cliOptions.add(CliOption.newBoolean("interfaceOnly", "Affects only client generation. If true - Kafka consumer implemenation classes will be generated, if false - only iterfaces.", true)); 155 | cliOptions.add(CliOption.newString(RESULT_WRAPPER, "Class, in which return value will be wrapped. Full path to that class must be specified.").defaultValue("")); 156 | cliOptions.add(CliOption.newString(SECURITY_ANNOTATION, "Annotation class which will be used in consumer code generation if consumer authorization is implemented. If this parameter is not specified, security annotations will not be generated.").defaultValue("")); 157 | cliOptions.add(CliOption.newBoolean("generateSupportingFiles", "generate camel security definitions", camelSecurityDefinitions)); 158 | cliOptions.add(CliOption.newBoolean(SEND_BYTES, "If true, then headers with types mapped by header names will not be used. If false, then types will be mapped.", false)); 159 | cliOptions.add(CliOption.newBoolean( "useAutoconfig", "If true, then autoconfiguation files will be generated alongside clients.", true)); 160 | cliOptions.add(CliOption.newString(MESSAGE_ID_NAME, "Name of the header, in which messageId value will be stored. If generateMessageId = true").defaultValue("kafka_messageId")); 161 | cliOptions.add(CliOption.newString(CORRELATION_ID_NAME, "Name of the header, in which correlationId value will be stored. If generateCorrelationId = true").defaultValue("kafka_correlationId")); 162 | cliOptions.add(CliOption.newBoolean(GENERATE_MESSAGE_ID, "If true, then generated clients will use header kafka_messageId by default. Header value will be random UUID.", true)); 163 | cliOptions.add(CliOption.newBoolean(GENERATE_CORRELATION_ID, "If true, then generated clients will use header kafka_correlationId by default. Header value will be random UUID.", true)); 164 | } 165 | 166 | @Override 167 | public void processOpts() { 168 | determineLib(); 169 | 170 | generatorMetadata = GeneratorMetadata.newBuilder(generatorMetadata) 171 | .stability(Stability.BETA) 172 | .build(); 173 | 174 | if (!additionalProperties.containsKey(DATE_LIBRARY)) { 175 | additionalProperties.put(DATE_LIBRARY, "legacy"); 176 | } 177 | super.processOpts(); 178 | if(!fromAxenAPIPlugin) { 179 | manageAdditionalProperties(); 180 | } 181 | //super.apiTemplateFiles.remove("apiController.mustache"); 182 | LOGGER.info("***** Additional properties after manageAdditionalProperties(); *****"); 183 | logAdditionalProperties(); 184 | 185 | setTemplates(); 186 | 187 | Map dataFormatProperties = new HashMap<>(); 188 | if (!"off".equals(camelRestBindingMode)) { 189 | Arrays.stream(camelDataformatProperties.split(",")).forEach(property -> { 190 | String[] dataFormatProperty = property.split("="); 191 | if (dataFormatProperty.length == 2) { 192 | dataFormatProperties.put(dataFormatProperty[0].trim(), dataFormatProperty[1].trim()); 193 | } 194 | }); 195 | } 196 | additionalProperties.put(CAMEL_DATAFORMAT_PROPERTIES, dataFormatProperties.entrySet()); 197 | } 198 | 199 | private void determineLib() { 200 | String libPrefix = null; 201 | String path = null; 202 | var pathEntryOpt = openAPI.getPaths().entrySet().stream().findFirst(); 203 | if (pathEntryOpt.isPresent()) { 204 | path = pathEntryOpt.get().getKey(); 205 | if (path.startsWith("/")) { 206 | path = path.substring(1); 207 | } 208 | 209 | var pathElements = Arrays.asList(path.split("/")); 210 | if (!pathElements.isEmpty()) { 211 | libPrefix = pathElements.get(0); 212 | } 213 | } 214 | LOGGER.info("prefix = " + libPrefix); 215 | if (KafkaHelper.PREFIX.equals(libPrefix)) { 216 | libHelper = KafkaHelper.getInstance(); 217 | } else if (RabbitHelper.PREFIX.equals(libPrefix)) { 218 | libHelper = RabbitHelper.getInstance(); 219 | } else if (JmsHelper.PREFIX.equals(libPrefix)) { 220 | libHelper = JmsHelper.getInstance(); 221 | } 222 | 223 | if (path != null && libHelper == null) { 224 | var exc = new RuntimeException(String.format("Path does not conform to the requirements. (%s)", path)); 225 | LOGGER.error(exc.getMessage()); 226 | throw exc; 227 | } 228 | } 229 | 230 | private void logAdditionalProperties() { 231 | LOGGER.info("Additional properties:"); 232 | additionalProperties.forEach( 233 | (key, value) -> LOGGER.info("{}: {}", key, value) 234 | ); 235 | } 236 | 237 | private void manageAdditionalProperties() { 238 | camelRestComponent = manageAdditionalProperty(CAMEL_REST_COMPONENT, camelRestComponent); 239 | camelRestBindingMode = manageAdditionalProperty(CAMEL_REST_BINDING_MODE, camelRestBindingMode); 240 | camelRestClientRequestValidation = manageAdditionalProperty(CAMEL_REST_CLIENT_REQUEST_VALIDATION, camelRestClientRequestValidation); 241 | camelUseDefaultValidationErrorProcessor = manageAdditionalProperty(CAMEL_USE_DEFAULT_VALIDATION_ERROR_PROCESSOR, camelUseDefaultValidationErrorProcessor); 242 | camelValidationErrorProcessor = manageAdditionalProperty(CAMEL_VALIDATION_ERROR_PROCESSOR, camelValidationErrorProcessor); 243 | camelSecurityDefinitions = manageAdditionalProperty(CAMEL_SECURITY_DEFINITIONS, camelSecurityDefinitions); 244 | // camelDataformatProperties = manageAdditionalProperty(CAMEL_DATAFORMAT_PROPERTIES, camelDataformatProperties); 245 | 246 | isKafkaClient = manageAdditionalProperty(IS_KAFKA_CLIENT, isKafkaClient); 247 | resultWrapper = manageAdditionalProperty(RESULT_WRAPPER, resultWrapper); 248 | securityAnnotation = manageAdditionalProperty(SECURITY_ANNOTATION, securityAnnotation); 249 | sendBytes = manageAdditionalProperty(SEND_BYTES, sendBytes); 250 | generateMessageId = manageAdditionalProperty(GENERATE_MESSAGE_ID, generateMessageId); 251 | generateCorrelationId = manageAdditionalProperty(GENERATE_CORRELATION_ID, generateCorrelationId); 252 | messageIdName = manageAdditionalProperty(MESSAGE_ID_NAME, messageIdName); 253 | correlationIdName = manageAdditionalProperty(CORRELATION_ID_NAME, correlationIdName); 254 | apiPackage = manageAdditionalProperty("apiPackage", "pro.axenix_innovation.axenapi.listener"); 255 | modelPackage = manageAdditionalProperty("modelPackage", "pro.axenix_innovation.axenapi.model"); 256 | } 257 | 258 | private T manageAdditionalProperty(String propertyName, T defaultValue) { 259 | if (additionalProperties.containsKey(propertyName)) { 260 | Object propertyValue = additionalProperties.get(propertyName); 261 | if (defaultValue instanceof Boolean && !(propertyValue instanceof Boolean)) { 262 | return (T) manageBooleanAdditionalProperty((String) propertyValue); 263 | } 264 | return (T) additionalProperties.get(propertyName); 265 | } 266 | additionalProperties.put(propertyName, defaultValue); 267 | return defaultValue; 268 | } 269 | 270 | private Boolean manageBooleanAdditionalProperty(String propertyValue) { 271 | return Boolean.parseBoolean(propertyValue); 272 | } 273 | 274 | private void setTemplates() { 275 | supportingFiles.clear(); 276 | apiTemplateFiles.clear(); 277 | 278 | modelTemplateFiles.put(MODEL_TEMPLATE_NAME, ".java"); 279 | 280 | libHelper.setTemplates(this, interfaceOnly); 281 | } 282 | 283 | @Override 284 | public void addOperationToGroup(String tag, String resourcePath, Operation operation, CodegenOperation co, Map> operations) { 285 | String operationId = null; 286 | String basePath = resourcePath; 287 | if (basePath.startsWith("/")) { 288 | basePath = basePath.substring(1); 289 | } 290 | 291 | ArrayList> xTags = (ArrayList>) operation.getExtensions().get("x-tags"); 292 | 293 | String tags = xTags.stream().map(m -> 294 | m.entrySet().stream() 295 | .filter(e -> e.getKey().equals("tag")) 296 | .map(Map.Entry::getValue) 297 | .collect(Collectors.joining("\", \"", "\"", "\"")) 298 | ).collect(Collectors.joining(", ")); 299 | 300 | co.vendorExtensions.put("tags", tags); 301 | 302 | operationId = libHelper.addOperationInfo(tag, basePath, operation, co, operations); 303 | 304 | operations.computeIfAbsent(operationId, k -> new ArrayList<>()); 305 | operations.get(operationId).add(co); 306 | } 307 | 308 | @Override 309 | public String apiFilename(String templateName, String tag) { 310 | return libHelper.apiFilename(templateName, tag, this); 311 | } 312 | 313 | public String getSecurityAnnotation() { 314 | return securityAnnotation; 315 | } 316 | 317 | public void setSecurityAnnotation(String securityAnnotation) { 318 | this.securityAnnotation = securityAnnotation; 319 | } 320 | 321 | public boolean isSendBytes() { 322 | return sendBytes; 323 | } 324 | 325 | public void setSendBytes(boolean sendBytes) { 326 | this.sendBytes = sendBytes; 327 | } 328 | 329 | public void setUseAutoConfig(boolean useAutoConfig) { 330 | this.useAutoConfig = useAutoConfig; 331 | } 332 | 333 | public String getMessageIdName() { 334 | return messageIdName; 335 | } 336 | 337 | public void setMessageIdName(String messageIdName) { 338 | this.messageIdName = messageIdName; 339 | } 340 | 341 | public String getCorrelationIdName() { 342 | return correlationIdName; 343 | } 344 | 345 | public void setCorrelationIdName(String correlationIdName) { 346 | this.correlationIdName = correlationIdName; 347 | } 348 | 349 | public Boolean getGenerateMessageId() { 350 | return generateMessageId; 351 | } 352 | 353 | public void setGenerateMessageId(Boolean generateMessageId) { 354 | this.generateMessageId = generateMessageId; 355 | } 356 | 357 | public Boolean getGenerateCorrelationId() { 358 | return generateCorrelationId; 359 | } 360 | 361 | public void setGenerateCorrelationId(Boolean generateCorrelationId) { 362 | this.generateCorrelationId = generateCorrelationId; 363 | } 364 | 365 | public void setFromAxenAPIPlugin( boolean fromAxenAPIPlugin) { 366 | this.fromAxenAPIPlugin = fromAxenAPIPlugin; 367 | } 368 | } 369 | -------------------------------------------------------------------------------- /src/main/java/pro/axenix_innovation/axenapi/codegen/MessageBrokerCodegen.java: -------------------------------------------------------------------------------- 1 | package pro.axenix_innovation.axenapi.codegen; 2 | 3 | import io.swagger.v3.oas.models.Operation; 4 | import org.openapitools.codegen.CliOption; 5 | import org.openapitools.codegen.CodegenOperation; 6 | import org.openapitools.codegen.SupportingFile; 7 | import org.openapitools.codegen.languages.SpringCodegen; 8 | import org.slf4j.Logger; 9 | import org.slf4j.LoggerFactory; 10 | import pro.axenix_innovation.axenapi.codegen.helper.*; 11 | 12 | import java.util.*; 13 | import java.util.stream.Collectors; 14 | 15 | import static org.openapitools.codegen.utils.StringUtils.camelize; 16 | 17 | public class MessageBrokerCodegen extends SpringCodegen implements MyCodegen { 18 | private static final Logger LOGGER = LoggerFactory.getLogger(MessageBrokerCodegen.class); 19 | 20 | public static final String RESULT_WRAPPER = "resultWrapper"; 21 | public static final String SECURITY_ANNOTATION = "securityAnnotation"; 22 | public static final String SEND_BYTES = "sendBytes"; 23 | public static final String MESSAGE_ID_NAME = "messageIdName"; 24 | public static final String CORRELATION_ID_NAME = "correlationIdName"; 25 | public static final String GENERATE_MESSAGE_ID = "generateMessageId"; 26 | public static final String GENERATE_CORRELATION_ID = "generateCorrelationId"; 27 | 28 | private static final String IS_KAFKA_CLIENT = "kafkaClient"; 29 | 30 | 31 | protected boolean useKafka = false; 32 | protected boolean useRabbit = false; 33 | protected boolean useJms = false; 34 | protected boolean useAxenAPI = true; 35 | protected String axenAPIVersion = "1.0.1"; 36 | protected boolean interfaceOnly = false; 37 | 38 | private LibHelper libHelper; 39 | private boolean useGradle = false; 40 | 41 | @Override 42 | public void processOpts() { 43 | super.processOpts(); 44 | determineLib(); 45 | System.out.println("apiTemplateFiles: " + apiTemplateFiles); 46 | System.out.println("modelTemplateFiles: " + modelTemplateFiles); 47 | System.out.println("supportingFiles: " + supportingFiles); 48 | apiTemplateFiles.clear(); 49 | // find ApiUtil.Java in supportingFiles and remove it 50 | supportingFiles.removeIf(f -> f.getDestinationFilename().equals("ApiUtil.java")); 51 | if (this.additionalProperties.containsKey("useGradle")) { 52 | useGradle = this.convertPropertyToBoolean("useGradle"); 53 | } 54 | libHelper.setTemplates(this, interfaceOnly); 55 | setBuilderTemplates(); 56 | System.out.println("----------- supportingFiles: " + supportingFiles); 57 | if (this.additionalProperties.containsKey("useAxenAPI")) { 58 | useAxenAPI = this.convertPropertyToBoolean("useAxenAPI"); 59 | } 60 | 61 | } 62 | 63 | private void setBuilderTemplates() { 64 | 65 | } 66 | 67 | @Override 68 | public String toApiName(String name) { 69 | if (name.isEmpty()) { 70 | return "DefaultListener"; 71 | } 72 | name = sanitizeName(name); 73 | // TODO 74 | // if (isKafkaClient) { 75 | // return camelize(name) + "Producer"; 76 | // } 77 | 78 | return camelize(name); 79 | } 80 | 81 | @Override 82 | public void addOperationToGroup(String tag, String resourcePath, Operation operation, CodegenOperation co, Map> operations) { 83 | String operationId = null; 84 | String basePath = resourcePath; 85 | if (basePath.startsWith("/")) { 86 | basePath = basePath.substring(1); 87 | } 88 | 89 | // ArrayList> xTags = (ArrayList>) operation.getExtensions().get("x-tags"); 90 | 91 | // if(xTags != null) { 92 | // String tags = xTags.stream().map(m -> 93 | // m.entrySet().stream() 94 | // .filter(e -> e.getKey().equals("tag")) 95 | // .map(Map.Entry::getValue) 96 | // .collect(Collectors.joining("\", \"", "\"", "\"")) 97 | // ).collect(Collectors.joining(", ")); 98 | // 99 | // co.vendorExtensions.put("tags", tags); 100 | // } 101 | 102 | operationId = libHelper.addOperationInfo(tag, basePath, operation, co, operations); 103 | 104 | operations.computeIfAbsent(operationId, k -> new ArrayList<>()); 105 | operations.get(operationId).add(co); 106 | } 107 | 108 | @Override 109 | public String apiFilename(String templateName, String tag) { 110 | return libHelper.apiFilename(templateName, tag, this); 111 | } 112 | 113 | @Override 114 | public String getName() { 115 | return "messageBroker"; 116 | } 117 | 118 | public MessageBrokerCodegen() { 119 | super(); 120 | templateDir = "templates"; 121 | addCliOptions(); 122 | } 123 | 124 | private void addCliOptions() { 125 | cliOptions.add(CliOption.newBoolean("useGradle", "If true, then Gradle will be used. If false, then Maven will not be used.", useGradle)); 126 | cliOptions.add(CliOption.newBoolean("useAxenAPI", "If true, then AxenApi will be used. If false, then AxenApi will not be used.")); 127 | cliOptions.add(CliOption.newString("axenAPIVersion", "AxenApi version. If not specified, then latest version will be used.").defaultValue(axenAPIVersion)); 128 | cliOptions.add(CliOption.newString("kafkaBootstrap", "List of kafka bootstrap servers (comma separated).")); 129 | cliOptions.add(CliOption.newString("listenerPackage", "Yes\tNo default value\tPackage, in which client/listeners will be generated.")); 130 | cliOptions.add(CliOption.newString("modelPackage", "Package, in which models will be generated (Data Transfer Object).")); 131 | cliOptions.add(CliOption.newBoolean("useSpring3", "If true, then code will be generated for springboot 3.1. If false, then code will be generated for spring boot 2.7.", true)); 132 | cliOptions.add(CliOption.newBoolean(IS_KAFKA_CLIENT, "If true, client code(producer) will be generated, if false - server code(consumer).", false)); 133 | cliOptions.add(CliOption.newBoolean("interfaceOnly", "Affects only client generation. If true - Kafka consumer implemenation classes will be generated, if false - only iterfaces.", true)); 134 | cliOptions.add(CliOption.newString(RESULT_WRAPPER, "Class, in which return value will be wrapped. Full path to that class must be specified.").defaultValue("")); 135 | cliOptions.add(CliOption.newString(SECURITY_ANNOTATION, "Annotation class which will be used in consumer code generation if consumer authorization is implemented. If this parameter is not specified, security annotations will not be generated.").defaultValue("")); 136 | cliOptions.add(CliOption.newBoolean(SEND_BYTES, "If true, then headers with types mapped by header names will not be used. If false, then types will be mapped.", false)); 137 | cliOptions.add(CliOption.newBoolean( "useAutoconfig", "If true, then autoconfiguation files will be generated alongside clients.", true)); 138 | cliOptions.add(CliOption.newString(MESSAGE_ID_NAME, "Name of the header, in which messageId value will be stored. If generateMessageId = true").defaultValue("kafka_messageId")); 139 | cliOptions.add(CliOption.newString(CORRELATION_ID_NAME, "Name of the header, in which correlationId value will be stored. If generateCorrelationId = true").defaultValue("kafka_correlationId")); 140 | cliOptions.add(CliOption.newBoolean(GENERATE_MESSAGE_ID, "If true, then generated clients will use header kafka_messageId by default. Header value will be random UUID.", true)); 141 | cliOptions.add(CliOption.newBoolean(GENERATE_CORRELATION_ID, "If true, then generated clients will use header kafka_correlationId by default. Header value will be random UUID.", true)); 142 | } 143 | 144 | private void determineLib() { 145 | String libPrefix = null; 146 | String path = null; 147 | var pathEntryOpt = openAPI.getPaths().entrySet().stream().findFirst(); 148 | if (pathEntryOpt.isPresent()) { 149 | path = pathEntryOpt.get().getKey(); 150 | if (path.startsWith("/")) { 151 | path = path.substring(1); 152 | } 153 | 154 | var pathElements = Arrays.asList(path.split("/")); 155 | if (!pathElements.isEmpty()) { 156 | libPrefix = pathElements.get(0); 157 | } 158 | } 159 | 160 | LOGGER.info("prefix = " + libPrefix); 161 | if (KafkaHelper.PREFIX.equals(libPrefix)) { 162 | useKafka = true; 163 | libHelper = KafkaHelper.getInstance(); 164 | } else if (RabbitHelper.PREFIX.equals(libPrefix)) { 165 | useRabbit = true; 166 | libHelper = RabbitHelper.getInstance(); 167 | } else if (JmsHelper.PREFIX.equals(libPrefix)) { 168 | useJms = true; 169 | libHelper = JmsHelper.getInstance(); 170 | } else { 171 | useJms = false; 172 | useKafka = false; 173 | useRabbit = false; 174 | libHelper = EmptyHelper.getInstance(); 175 | } 176 | 177 | if (path != null && libHelper == null) { 178 | var exc = new RuntimeException(String.format("Path does not conform to the requirements. (%s)", path)); 179 | LOGGER.error(exc.getMessage()); 180 | throw exc; 181 | } 182 | } 183 | 184 | @Override 185 | public boolean isKafkaClient() { 186 | return false; 187 | } 188 | 189 | @Override 190 | public boolean isUseGradle() { 191 | return useGradle; 192 | } 193 | 194 | public boolean isUseAxenAPI() { 195 | return useAxenAPI; 196 | } 197 | 198 | // public String axenAPIVersion() { 199 | // return axenAPIVersion; 200 | // } 201 | 202 | public boolean isInterfaceOnly() { 203 | return interfaceOnly; 204 | } 205 | 206 | public void setUseGradle(boolean useGradle) { 207 | this.useGradle = useGradle; 208 | } 209 | } 210 | -------------------------------------------------------------------------------- /src/main/java/pro/axenix_innovation/axenapi/codegen/MyCodegen.java: -------------------------------------------------------------------------------- 1 | package pro.axenix_innovation.axenapi.codegen; 2 | 3 | import org.openapitools.codegen.CodegenConfig; 4 | import org.openapitools.codegen.SupportingFile; 5 | 6 | import java.util.Collection; 7 | import java.util.Dictionary; 8 | 9 | public interface MyCodegen extends CodegenConfig { 10 | boolean isKafkaClient(); 11 | 12 | String getSourceFolder(); 13 | 14 | boolean isUseSpringBoot3(); 15 | 16 | String getConfigPackage(); 17 | 18 | boolean isUseGradle(); 19 | } 20 | -------------------------------------------------------------------------------- /src/main/java/pro/axenix_innovation/axenapi/codegen/helper/EmptyHelper.java: -------------------------------------------------------------------------------- 1 | package pro.axenix_innovation.axenapi.codegen.helper; 2 | 3 | import io.swagger.v3.oas.models.Operation; 4 | import org.openapitools.codegen.CodegenOperation; 5 | import pro.axenix_innovation.axenapi.codegen.MyCodegen; 6 | 7 | import java.util.List; 8 | import java.util.Map; 9 | 10 | public class EmptyHelper implements LibHelper { 11 | private static EmptyHelper instance; 12 | 13 | public static EmptyHelper getInstance() { 14 | if (instance == null) { 15 | instance = new EmptyHelper(); 16 | } 17 | return instance; 18 | } 19 | 20 | 21 | @Override 22 | public void setTemplates(MyCodegen gen, boolean isInterfaceOnly) { 23 | 24 | } 25 | 26 | @Override 27 | public String addOperationInfo(String tag, String path, Operation operation, CodegenOperation co, Map> operations) { 28 | return ""; 29 | } 30 | 31 | @Override 32 | public String apiFilename(String templateName, String tag, MyCodegen gen) { 33 | return ""; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/main/java/pro/axenix_innovation/axenapi/codegen/helper/JmsHelper.java: -------------------------------------------------------------------------------- 1 | package pro.axenix_innovation.axenapi.codegen.helper; 2 | 3 | import io.swagger.v3.oas.models.Operation; 4 | import org.apache.commons.text.CaseUtils; 5 | import org.openapitools.codegen.CodegenOperation; 6 | import org.openapitools.codegen.SupportingFile; 7 | import org.openapitools.codegen.utils.CamelizeOption; 8 | import org.slf4j.Logger; 9 | import org.slf4j.LoggerFactory; 10 | import pro.axenix_innovation.axenapi.codegen.KafkaCodegenGenerator; 11 | import pro.axenix_innovation.axenapi.codegen.MyCodegen; 12 | 13 | import java.io.File; 14 | import java.util.Arrays; 15 | import java.util.List; 16 | import java.util.Map; 17 | 18 | import static org.openapitools.codegen.utils.StringUtils.camelize; 19 | 20 | public class JmsHelper implements LibHelper { 21 | public static final String PREFIX = "jms"; 22 | 23 | private static final String CLIENT_IMPL_TEMPLATE_NAME = PREFIX + File.separator + "clientImpl.mustache"; 24 | private static final String SENDER_SERVICE_TEMPLATE_NAME = PREFIX + File.separator + "senderService.mustache"; 25 | private static final String SENDER_SERVICE_FILENAME = "JmsSenderService.java"; 26 | private static final String SENDER_SERVICE_IMPL_TEMPLATE_NAME = PREFIX + File.separator + "senderServiceImpl.mustache"; 27 | private static final String SENDER_SERVICE_IMPL_FILENAME = "JmsSenderServiceImpl.java"; 28 | private static final String SENDER_SERVICE_CONFIG_TEMPLATE_NAME = PREFIX + File.separator + "senderServiceConfig.mustache"; 29 | private static final String SENDER_SERVICE_CONFIG_FILENAME = "JmsSenderServiceConfig.java"; 30 | private static final String SPRING_2_AUTOCONFIG_TEMPLATE_NAME = PREFIX + File.separator + "spring_2_autoconfig.mustache"; 31 | private static final String SPRING_3_AUTOCONFIG_TEMPLATE_NAME = PREFIX + File.separator + "spring_3_autoconfig.mustache"; 32 | 33 | private static final String LISTENER_TEMPLATE_NAME = PREFIX + File.separator + "listener.mustache"; 34 | 35 | private static final String QUEUE = "queue"; 36 | 37 | private static final Logger LOGGER = LoggerFactory.getLogger(JmsHelper.class); 38 | 39 | private static LibHelper instance; 40 | 41 | public static LibHelper getInstance() { 42 | if (instance == null) { 43 | instance = new JmsHelper(); 44 | } 45 | return instance; 46 | } 47 | 48 | @Override 49 | public void setTemplates(MyCodegen gen, boolean isInterfaceOnly) { 50 | if (gen.isKafkaClient()) { 51 | gen.apiTemplateFiles().put(CLIENT_TEMPLATE_NAME, ".java"); 52 | if (!isInterfaceOnly) { 53 | gen.apiTemplateFiles().put(CLIENT_IMPL_TEMPLATE_NAME, ".java"); 54 | gen.supportingFiles().add(new SupportingFile(SENDER_SERVICE_TEMPLATE_NAME, 55 | gen.getSourceFolder() + File.separator + "service", SENDER_SERVICE_FILENAME)); 56 | gen.supportingFiles().add(new SupportingFile(SENDER_SERVICE_IMPL_TEMPLATE_NAME, 57 | gen.getSourceFolder() + File.separator + "service" + File.separator + "impl", SENDER_SERVICE_IMPL_FILENAME)); 58 | 59 | gen.supportingFiles().add(new SupportingFile(SENDER_SERVICE_CONFIG_TEMPLATE_NAME, 60 | gen.getSourceFolder() + File.separator + "config", SENDER_SERVICE_CONFIG_FILENAME)); 61 | if (gen.isUseSpringBoot3()) { 62 | gen.supportingFiles().add(new SupportingFile(SPRING_3_AUTOCONFIG_TEMPLATE_NAME, // /../resources/META-INF/spring 63 | gen.getSourceFolder() + File.separator + ".." + File.separator + "resources" + 64 | File.separator + "META-INF" + File.separator + "spring", SPRING_3_AUTOCONFIG_FILENAME)); 65 | } else { 66 | gen.supportingFiles().add(new SupportingFile(SPRING_2_AUTOCONFIG_TEMPLATE_NAME, // /../resources/META-INF 67 | gen.getSourceFolder() + File.separator + ".." + File.separator + 68 | "resources" + File.separator + "META-INF", SPRING_2_AUTOCONFIG_FILENAME)); 69 | } 70 | } 71 | } else { 72 | gen.apiTemplateFiles().put(LISTENER_TEMPLATE_NAME, ".java"); 73 | gen.apiTemplateFiles().put(LISTENER_SERVICE_TEMPLATE_NAME, ".java"); 74 | } 75 | } 76 | 77 | @Override 78 | public String addOperationInfo(String tag, String path, Operation operation, CodegenOperation co, Map> operations) { 79 | List pathElements = Arrays.asList(path.split("/")); 80 | String queue = ""; 81 | 82 | if (pathElements.size() != 3) { 83 | var exc = new RuntimeException(String.format("Path does not conform to the pattern /jms//. (%s)", path)); 84 | LOGGER.error(exc.getMessage()); 85 | throw exc; 86 | } 87 | 88 | queue = pathElements.get(1); 89 | co.vendorExtensions.put(QUEUE, queue); 90 | co.vendorExtensions.put(MODEL_NAME, pathElements.get(2)); 91 | co.vendorExtensions.put(MODEL_NAME_CAMEL, camelize(pathElements.get(2), CamelizeOption.LOWERCASE_FIRST_CHAR)); 92 | 93 | queue = CaseUtils.toCamelCase(queue, true, '-','_','.',' ').replaceAll("[^a-zA-Zа-яёА-ЯЁ\\d]", "").replace("-", ""); 94 | 95 | // TODO: temporarily solution - grouping by QUEUE + MODEL_NAME 96 | // as the library currently works properly only with one JmsListener per class 97 | return queue + camelize((String) co.vendorExtensions.get(MODEL_NAME_CAMEL), CamelizeOption.UPPERCASE_FIRST_CHAR); 98 | } 99 | 100 | @Override 101 | public String apiFilename(String templateName, String tag, MyCodegen gen) { 102 | String suffix = gen.apiTemplateFiles().get(templateName); 103 | if (templateName.equals(CLIENT_IMPL_TEMPLATE_NAME)) { 104 | return gen.apiFileFolder() + File.separator + "impl" + File.separator + 105 | gen.toApiFilename(tag) + "Impl" + suffix; 106 | } 107 | 108 | String listenerQualifier = ""; 109 | String listenerInnerPackage = ""; 110 | if (templateName.equals(LISTENER_TEMPLATE_NAME)) { 111 | listenerQualifier = "Listener"; 112 | } else if (templateName.equals(LISTENER_SERVICE_TEMPLATE_NAME)) { 113 | listenerQualifier = "Service"; 114 | listenerInnerPackage = "service" + File.separator; 115 | } 116 | if (!listenerQualifier.isEmpty()) { 117 | return gen.apiFileFolder() + File.separator + listenerInnerPackage + 118 | gen.toApiFilename(tag) + listenerQualifier + suffix; 119 | } 120 | 121 | return gen.apiFileFolder() + File.separator + gen.toApiFilename(tag) + suffix; 122 | } 123 | } 124 | -------------------------------------------------------------------------------- /src/main/java/pro/axenix_innovation/axenapi/codegen/helper/KafkaHelper.java: -------------------------------------------------------------------------------- 1 | package pro.axenix_innovation.axenapi.codegen.helper; 2 | 3 | import io.swagger.v3.oas.models.Operation; 4 | import org.apache.commons.text.CaseUtils; 5 | import org.openapitools.codegen.CodegenOperation; 6 | import org.openapitools.codegen.SupportingFile; 7 | import org.openapitools.codegen.utils.CamelizeOption; 8 | import org.slf4j.Logger; 9 | import org.slf4j.LoggerFactory; 10 | import pro.axenix_innovation.axenapi.codegen.KafkaCodegenGenerator; 11 | import pro.axenix_innovation.axenapi.codegen.MyCodegen; 12 | 13 | import java.io.File; 14 | import java.util.*; 15 | 16 | import static org.openapitools.codegen.utils.StringUtils.camelize; 17 | 18 | public class KafkaHelper implements LibHelper { 19 | public static final String PREFIX = "kafka"; 20 | 21 | private static final String CLIENT_IMPL_TEMPLATE_NAME = PREFIX + File.separator + "clientImpl.mustache"; 22 | private static final String SENDER_SERVICE_TEMPLATE_NAME = PREFIX + File.separator + "senderService.mustache"; 23 | private static final String SENDER_SERVICE_FILENAME = "KafkaSenderService.java"; 24 | private static final String SENDER_SERVICE_IMPL_TEMPLATE_NAME = PREFIX + File.separator + "senderServiceImpl.mustache"; 25 | private static final String SENDER_SERVICE_IMPL_FILENAME = "KafkaSenderServiceImpl.java"; 26 | private static final String SENDER_SERVICE_CONFIG_TEMPLATE_NAME = PREFIX + File.separator + "senderServiceConfig.mustache"; 27 | private static final String SENDER_SERVICE_CONFIG_FILENAME = "KafkaSenderServiceConfig.java"; 28 | private static final String SPRING_2_AUTOCONFIG_TEMPLATE_NAME = PREFIX + File.separator + "spring_2_autoconfig.mustache"; 29 | private static final String SPRING_3_AUTOCONFIG_TEMPLATE_NAME = PREFIX + File.separator + "spring_3_autoconfig.mustache"; 30 | 31 | private static final String PRODUCER_CONFIG_TEMPLATE_NAME = PREFIX + File.separator + "producerConfig.mustache"; 32 | private static final String PRODUCER_CONFIG_FILENAME = "KafkaProducerConfig.java"; 33 | 34 | private static final String LISTENER_TEMPLATE_NAME = PREFIX + File.separator + "listener.mustache"; 35 | 36 | private static final String GROUP_ID = "groupId"; 37 | private static final String TOPIC = "topic"; 38 | 39 | private static final Logger LOGGER = LoggerFactory.getLogger(KafkaHelper.class); 40 | private static final String KAFKA_CONSUMER_CONFIG_TEMPLATE_NAME = PREFIX + File.separator + "KafkaConsumerConfig.mustache"; 41 | private static final String KAFKA_CONSUMER_CONFIG_FILENAME = PREFIX + File.separator + "KafkaConsumerConfig.java"; 42 | 43 | private static LibHelper instance; 44 | 45 | public static LibHelper getInstance() { 46 | if (instance == null) { 47 | instance = new KafkaHelper(); 48 | } 49 | return instance; 50 | } 51 | 52 | @Override 53 | public void setTemplates(MyCodegen gen, boolean isInterfaceOnly) { 54 | System.out.println("gen.isUseGradle(): " + gen.isUseGradle()); 55 | if (gen.isUseGradle()) { 56 | gen.supportingFiles().add(new SupportingFile("gradle_build_spring_boot_2.mustache", "", "build.gradle")); 57 | gen.supportingFiles().add(new SupportingFile("gradle_wrapper_properties.mustache", "gradle" + File.separator + "wrapper", "gradle-wrapper.properties")); 58 | gen.supportingFiles().add(new SupportingFile("gradlew.mustache", "", "gradlew")); 59 | gen.supportingFiles().add(new SupportingFile("gradlew_bat.mustache", "", "gradlew.bat")); 60 | gen.supportingFiles().add(new SupportingFile("settings_gradle.mustache", "", "settings.gradle")); 61 | gen.supportingFiles().add(new SupportingFile("axenapi_properties.mustache", "", "axenapi.properties")); 62 | // remove pom.xml from supporting files 63 | boolean b = gen.supportingFiles().removeIf(f -> f.getDestinationFilename().equals("pom.xml")); 64 | System.out.println(b); 65 | System.out.println("----------- from helper supportingFiles: " + gen.supportingFiles()); 66 | } 67 | if (gen.isKafkaClient()) { 68 | gen.apiTemplateFiles().put(CLIENT_TEMPLATE_NAME, ".java"); 69 | if (!isInterfaceOnly) { 70 | gen.apiTemplateFiles().put(CLIENT_IMPL_TEMPLATE_NAME, ".java"); 71 | gen.supportingFiles().add(new SupportingFile(SENDER_SERVICE_TEMPLATE_NAME, // TODO: create method for filename 72 | gen.getSourceFolder() + File.separator + "service", SENDER_SERVICE_FILENAME)); 73 | gen.supportingFiles().add(new SupportingFile(SENDER_SERVICE_IMPL_TEMPLATE_NAME, 74 | gen.getSourceFolder() + File.separator + "service" + File.separator + "impl", SENDER_SERVICE_IMPL_FILENAME)); 75 | gen.supportingFiles().add(new SupportingFile(PRODUCER_CONFIG_TEMPLATE_NAME, 76 | gen.getSourceFolder() + File.separator + "config", PRODUCER_CONFIG_FILENAME)); 77 | 78 | gen.supportingFiles().add(new SupportingFile(SENDER_SERVICE_CONFIG_TEMPLATE_NAME, 79 | gen.getSourceFolder() + File.separator + "config", SENDER_SERVICE_CONFIG_FILENAME)); 80 | 81 | if (gen.isUseSpringBoot3()) { 82 | gen.supportingFiles().add(new SupportingFile(SPRING_3_AUTOCONFIG_TEMPLATE_NAME, // /../resources/META-INF/spring 83 | gen.getSourceFolder() + File.separator + ".." + File.separator + "resources" + 84 | File.separator + "META-INF" + File.separator + "spring", SPRING_3_AUTOCONFIG_FILENAME)); 85 | } else { 86 | gen.supportingFiles().add(new SupportingFile(SPRING_2_AUTOCONFIG_TEMPLATE_NAME, // /../resources/META-INF 87 | gen.getSourceFolder() + File.separator + ".." + File.separator + 88 | "resources" + File.separator + "META-INF", SPRING_2_AUTOCONFIG_FILENAME)); 89 | } 90 | } 91 | } else { 92 | if(!isInterfaceOnly) { 93 | gen.apiTemplateFiles().put(LISTENER_SERVICE_IMPL_TEMPLATE_NAME, ".java"); 94 | } 95 | gen.supportingFiles().add(new SupportingFile(KAFKA_CONSUMER_CONFIG_TEMPLATE_NAME, 96 | (gen.getSourceFolder() + File.separator + gen.getConfigPackage()).replace(".", java.io.File.separator), KAFKA_CONSUMER_CONFIG_FILENAME)); 97 | gen.apiTemplateFiles().put(LISTENER_TEMPLATE_NAME, ".java"); 98 | gen.apiTemplateFiles().put(LISTENER_SERVICE_TEMPLATE_NAME, ".java"); 99 | } 100 | } 101 | 102 | @Override 103 | public String addOperationInfo(String tag, String path, Operation operation, CodegenOperation co, 104 | Map> operations) { 105 | String topic = ""; 106 | String groupId = ""; 107 | String topicGroupId = ""; 108 | 109 | List pathElements = Arrays.asList(path.split("/")); 110 | 111 | if (pathElements.size() < 3 || pathElements.size() > 4) { 112 | var exc = new RuntimeException(String.format("Path does not conform to either patterns /kafka///" + 113 | " and /kafka//. (%s)", path)); 114 | LOGGER.error(exc.getMessage()); 115 | throw exc; 116 | } 117 | 118 | if (pathElements.size() == 4) { 119 | groupId = pathElements.get(1); 120 | topic = pathElements.get(2); 121 | co.vendorExtensions.put(GROUP_ID, groupId); 122 | co.vendorExtensions.put(TOPIC, topic); 123 | co.vendorExtensions.put(MODEL_NAME, pathElements.get(3)); 124 | co.vendorExtensions.put(MODEL_NAME_CAMEL, camelize(pathElements.get(3), CamelizeOption.LOWERCASE_FIRST_CHAR)); 125 | } 126 | 127 | if (pathElements.size() == 3) { 128 | topic = pathElements.get(1); 129 | co.vendorExtensions.put(TOPIC, topic); 130 | co.vendorExtensions.put(MODEL_NAME, pathElements.get(2)); 131 | co.vendorExtensions.put(MODEL_NAME_CAMEL, camelize(pathElements.get(2), CamelizeOption.LOWERCASE_FIRST_CHAR)); 132 | } 133 | 134 | if (!topic.isEmpty()) { 135 | topicGroupId = topic; 136 | } 137 | 138 | if (!groupId.isEmpty()) { 139 | topicGroupId = topicGroupId + ' ' + groupId; 140 | } 141 | 142 | topicGroupId = CaseUtils.toCamelCase(topicGroupId, true, '-','_','.',' ').replaceAll("[^a-zA-Zа-яёА-ЯЁ\\d]", "").replace("-", ""); 143 | return topicGroupId; 144 | } 145 | 146 | @Override 147 | public String apiFilename(String templateName, String tag, MyCodegen gen) { // TODO: consider default implementation in interface 148 | String suffix = gen.apiTemplateFiles().get(templateName); 149 | if (templateName.equals(CLIENT_IMPL_TEMPLATE_NAME)) { 150 | return gen.apiFileFolder() + File.separator + "impl" + File.separator + 151 | gen.toApiFilename(tag) + "Impl" + suffix; 152 | } 153 | 154 | String listenerQualifier = ""; 155 | String listenerInnerPackage = ""; 156 | if (templateName.equals(LISTENER_TEMPLATE_NAME)) { 157 | listenerQualifier = "Listener"; 158 | } else if (templateName.equals(LISTENER_SERVICE_TEMPLATE_NAME)) { 159 | listenerQualifier = "Service"; 160 | listenerInnerPackage = "service" + File.separator; 161 | } else if (LISTENER_SERVICE_IMPL_TEMPLATE_NAME.equals(templateName)) { 162 | listenerQualifier = "ServiceImpl"; 163 | listenerInnerPackage = "service" + File.separator; 164 | } 165 | if (!listenerQualifier.isEmpty()) { 166 | return gen.apiFileFolder() + File.separator + listenerInnerPackage + 167 | gen.toApiFilename(tag) + listenerQualifier + suffix; 168 | } 169 | 170 | return gen.apiFileFolder() + File.separator + gen.toApiFilename(tag) + suffix; 171 | } 172 | } 173 | -------------------------------------------------------------------------------- /src/main/java/pro/axenix_innovation/axenapi/codegen/helper/LibHelper.java: -------------------------------------------------------------------------------- 1 | package pro.axenix_innovation.axenapi.codegen.helper; 2 | 3 | import io.swagger.v3.oas.models.Operation; 4 | import org.openapitools.codegen.CodegenOperation; 5 | import pro.axenix_innovation.axenapi.codegen.KafkaCodegenGenerator; 6 | import pro.axenix_innovation.axenapi.codegen.MyCodegen; 7 | 8 | import java.util.List; 9 | import java.util.Map; 10 | 11 | public interface LibHelper { 12 | String CLIENT_TEMPLATE_NAME = "client.mustache"; 13 | String LISTENER_SERVICE_TEMPLATE_NAME = "listenerService.mustache"; 14 | String SPRING_2_AUTOCONFIG_FILENAME = "spring.factories"; 15 | String SPRING_3_AUTOCONFIG_FILENAME = "org.springframework.boot.autoconfigure.AutoConfiguration.imports"; 16 | String LISTENER_SERVICE_IMPL_TEMPLATE_NAME = "listenerServiceImpl.mustache"; 17 | 18 | String MODEL_NAME = "modelName"; 19 | String MODEL_NAME_CAMEL = "modelNameCamel"; 20 | 21 | void setTemplates(MyCodegen gen, boolean isInterfaceOnly); 22 | 23 | String addOperationInfo(String tag, String path, Operation operation, CodegenOperation co, 24 | Map> operations); 25 | 26 | String apiFilename(String templateName, String tag, MyCodegen gen); 27 | } 28 | -------------------------------------------------------------------------------- /src/main/java/pro/axenix_innovation/axenapi/codegen/helper/RabbitHelper.java: -------------------------------------------------------------------------------- 1 | package pro.axenix_innovation.axenapi.codegen.helper; 2 | 3 | import io.swagger.v3.oas.models.Operation; 4 | import org.apache.commons.text.CaseUtils; 5 | import org.openapitools.codegen.CodegenOperation; 6 | import org.openapitools.codegen.SupportingFile; 7 | import org.openapitools.codegen.utils.CamelizeOption; 8 | import org.slf4j.Logger; 9 | import org.slf4j.LoggerFactory; 10 | import pro.axenix_innovation.axenapi.codegen.KafkaCodegenGenerator; 11 | import pro.axenix_innovation.axenapi.codegen.MyCodegen; 12 | 13 | import java.io.File; 14 | import java.util.Arrays; 15 | import java.util.List; 16 | import java.util.Map; 17 | 18 | import static org.openapitools.codegen.utils.StringUtils.camelize; 19 | 20 | public class RabbitHelper implements LibHelper { 21 | public static final String PREFIX = "rabbit"; 22 | 23 | private static final String CLIENT_IMPL_TEMPLATE_NAME = PREFIX + File.separator + "clientImpl.mustache"; 24 | private static final String SENDER_SERVICE_TEMPLATE_NAME = PREFIX + File.separator + "senderService.mustache"; 25 | private static final String SENDER_SERVICE_FILENAME = "RabbitSenderService.java"; 26 | private static final String SENDER_SERVICE_IMPL_TEMPLATE_NAME = PREFIX + File.separator + "senderServiceImpl.mustache"; 27 | private static final String SENDER_SERVICE_IMPL_FILENAME = "RabbitSenderServiceImpl.java"; 28 | private static final String SENDER_SERVICE_CONFIG_TEMPLATE_NAME = PREFIX + File.separator + "senderServiceConfig.mustache"; 29 | private static final String SENDER_SERVICE_CONFIG_FILENAME = "RabbitSenderServiceConfig.java"; 30 | private static final String SPRING_2_AUTOCONFIG_TEMPLATE_NAME = PREFIX + File.separator + "spring_2_autoconfig.mustache"; 31 | private static final String SPRING_3_AUTOCONFIG_TEMPLATE_NAME = PREFIX + File.separator + "spring_3_autoconfig.mustache"; 32 | 33 | private static final String LISTENER_TEMPLATE_NAME = PREFIX + File.separator + "listener.mustache"; 34 | 35 | private static final String QUEUE = "queue"; 36 | 37 | private static final Logger LOGGER = LoggerFactory.getLogger(RabbitHelper.class); 38 | 39 | private static LibHelper instance; 40 | 41 | public static LibHelper getInstance() { 42 | if (instance == null) { 43 | instance = new RabbitHelper(); 44 | } 45 | return instance; 46 | } 47 | 48 | @Override 49 | public void setTemplates(MyCodegen gen, boolean isInterfaceOnly) { 50 | if (gen.isKafkaClient()) { 51 | gen.apiTemplateFiles().put(CLIENT_TEMPLATE_NAME, ".java"); 52 | if (!isInterfaceOnly) { 53 | gen.apiTemplateFiles().put(CLIENT_IMPL_TEMPLATE_NAME, ".java"); 54 | gen.supportingFiles().add(new SupportingFile(SENDER_SERVICE_TEMPLATE_NAME, 55 | gen.getSourceFolder() + File.separator + "service", SENDER_SERVICE_FILENAME)); 56 | gen.supportingFiles().add(new SupportingFile(SENDER_SERVICE_IMPL_TEMPLATE_NAME, 57 | gen.getSourceFolder() + File.separator + "service" + File.separator + "impl", SENDER_SERVICE_IMPL_FILENAME)); 58 | 59 | gen.supportingFiles().add(new SupportingFile(SENDER_SERVICE_CONFIG_TEMPLATE_NAME, 60 | gen.getSourceFolder() + File.separator + "config", SENDER_SERVICE_CONFIG_FILENAME)); 61 | if (gen.isUseSpringBoot3()) { 62 | gen.supportingFiles().add(new SupportingFile(SPRING_3_AUTOCONFIG_TEMPLATE_NAME, // /../resources/META-INF/spring 63 | gen.getSourceFolder() + File.separator + ".." + File.separator + "resources" + 64 | File.separator + "META-INF" + File.separator + "spring", SPRING_3_AUTOCONFIG_FILENAME)); 65 | } else { 66 | gen.supportingFiles().add(new SupportingFile(SPRING_2_AUTOCONFIG_TEMPLATE_NAME, // /../resources/META-INF 67 | gen.getSourceFolder() + File.separator + ".." + File.separator + 68 | "resources" + File.separator + "META-INF", SPRING_2_AUTOCONFIG_FILENAME)); 69 | } 70 | } 71 | } else { 72 | gen.apiTemplateFiles().put(LISTENER_TEMPLATE_NAME, ".java"); 73 | gen.apiTemplateFiles().put(LISTENER_SERVICE_TEMPLATE_NAME, ".java"); 74 | } 75 | } 76 | 77 | @Override 78 | public String addOperationInfo(String tag, String path, Operation operation, CodegenOperation co, Map> operations) { 79 | List pathElements = Arrays.asList(path.split("/")); 80 | String queue = ""; 81 | 82 | if (pathElements.size() != 3) { 83 | var exc = new RuntimeException(String.format("Path does not conform to the pattern /rabbit//. (%s)", path)); 84 | LOGGER.error(exc.getMessage()); 85 | throw exc; 86 | } 87 | 88 | queue = pathElements.get(1); 89 | co.vendorExtensions.put(QUEUE, queue); 90 | co.vendorExtensions.put(MODEL_NAME, pathElements.get(2)); 91 | co.vendorExtensions.put(MODEL_NAME_CAMEL, camelize(pathElements.get(2), CamelizeOption.LOWERCASE_FIRST_CHAR)); 92 | 93 | queue = CaseUtils.toCamelCase(queue, true, '-','_','.',' ').replaceAll("[^a-zA-Zа-яёА-ЯЁ\\d]", "").replace("-", ""); 94 | return queue; 95 | } 96 | 97 | @Override 98 | public String apiFilename(String templateName, String tag, MyCodegen gen) { 99 | String suffix = gen.apiTemplateFiles().get(templateName); 100 | if (templateName.equals(CLIENT_IMPL_TEMPLATE_NAME)) { 101 | return gen.apiFileFolder() + File.separator + "impl" + File.separator + 102 | gen.toApiFilename(tag) + "Impl" + suffix; 103 | } 104 | 105 | String listenerQualifier = ""; 106 | String listenerInnerPackage = ""; 107 | if (templateName.equals(LISTENER_TEMPLATE_NAME)) { 108 | listenerQualifier = "Listener"; 109 | } else if (templateName.equals(LISTENER_SERVICE_TEMPLATE_NAME)) { 110 | listenerQualifier = "Service"; 111 | listenerInnerPackage = "service" + File.separator; 112 | } 113 | if (!listenerQualifier.isEmpty()) { 114 | return gen.apiFileFolder() + File.separator + listenerInnerPackage + 115 | gen.toApiFilename(tag) + listenerQualifier + suffix; 116 | } 117 | 118 | return gen.apiFileFolder() + File.separator + gen.toApiFilename(tag) + suffix; 119 | } 120 | } 121 | -------------------------------------------------------------------------------- /src/main/resources/META-INF/services/org.openapitools.codegen.CodegenConfig: -------------------------------------------------------------------------------- 1 | pro.axenix_innovation.axenapi.codegen.KafkaCodegenGenerator 2 | pro.axenix_innovation.axenapi.codegen.MessageBrokerCodegen -------------------------------------------------------------------------------- /src/main/resources/gradle/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AxenAPI/axenapi-generator/c9a436e734e80b8a9d3eb199a62c87eadf291e63/src/main/resources/gradle/gradle-wrapper.jar -------------------------------------------------------------------------------- /src/main/resources/templates/application.mustache: -------------------------------------------------------------------------------- 1 | # Application Configuration 2 | server.port={{serverPort}} 3 | spring.jackson.date-format={{basePackage}}.RFC3339DateFormat 4 | spring.jackson.serialization.WRITE_DATES_AS_TIMESTAMPS=false 5 | 6 | {{#useKafka}} 7 | {{#kafkaBootstrap}} 8 | # kafka configuration 9 | spring.kafka.bootstrap-servers={{kafkaBootstrap}} 10 | {{/kafkaBootstrap}} 11 | {{^kafkaBootstrap}} 12 | spring.kafka.bootstrap-servers=localhost:29092 13 | {{/kafkaBootstrap}} 14 | {{/useKafka}} 15 | 16 | {{#useRabbit}} 17 | # rabbitmq configuration 18 | spring.rabbitmq.host={{rabbitHost}} 19 | spring.rabbitmq.port={{rabbitPort}} 20 | spring.rabbitmq.username={{rabbitUsername}} 21 | spring.rabbitmq.password={{rabbitPassword}} 22 | {{/useRabbit}} 23 | 24 | {{#useJms}} 25 | # Jms configuration 26 | 27 | {{/useJms}} 28 | 29 | {{#virtualService}} 30 | virtualan.datasource.driver-class-name=org.hsqldb.jdbcDriver 31 | virtualan.datasource.jdbcurl=jdbc:hsqldb:mem:dataSource 32 | virtualan.datasource.username=sa 33 | virtualan.datasource.password= 34 | {{/virtualService}} 35 | 36 | axenapi.kafka.swagger.enabled=true 37 | -------------------------------------------------------------------------------- /src/main/resources/templates/axenapi_properties.mustache: -------------------------------------------------------------------------------- 1 | #if no property annotation processor scans all project 2 | package = {{apiPackage}} 3 | 4 | # default value org.springframework.kafka.annotation.KafkaHandler 5 | #kafka.handler.annotaion = com.example.demo.annotation.MyKafkaHandler 6 | # if false use only custom annotation. If true use custom annotation and spring annotation 7 | use.standart.kafkahandler.annotation = true 8 | #kafka.access.token.header = SERVICE_ACCESS_TOKEN 9 | language = eng -------------------------------------------------------------------------------- /src/main/resources/templates/client.mustache: -------------------------------------------------------------------------------- 1 | /** 2 | * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech) ({{{generatorVersion}}}). 3 | * https://openapi-generator.tech 4 | * Do not edit the class manually. 5 | */ 6 | package {{apiPackage}}; 7 | 8 | {{#imports}}import {{import}}; 9 | {{/imports}} 10 | 11 | import java.util.Map; 12 | 13 | public interface {{baseName}}Producer { 14 | 15 | {{#operations}} 16 | {{#operation}} 17 | {{#vendorExtensions}} 18 | void send{{modelName}}({{modelName}} {{modelNameCamel}}, Map params); 19 | 20 | {{/vendorExtensions}} 21 | {{/operation}} 22 | {{/operations}} 23 | } 24 | -------------------------------------------------------------------------------- /src/main/resources/templates/gradle_build_spring_boot_2.mustache: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'java' 3 | id 'org.springframework.boot' version '2.7.5' 4 | id 'io.spring.dependency-management' version '1.0.15.RELEASE' 5 | id "org.openapi.generator" version "6.1.0" 6 | } 7 | 8 | repositories { 9 | mavenLocal() 10 | mavenCentral() 11 | } 12 | 13 | 14 | group = 'com.example' 15 | version = '0.0.1-SNAPSHOT' 16 | 17 | java { 18 | sourceCompatibility = JavaVersion.VERSION_11 19 | } 20 | 21 | configurations { 22 | compileOnly { 23 | extendsFrom annotationProcessor 24 | } 25 | } 26 | 27 | dependencies { 28 | implementation 'org.springframework.boot:spring-boot-starter' 29 | implementation 'org.springframework.boot:spring-boot-starter-web' 30 | implementation 'com.fasterxml.jackson.core:jackson-databind' 31 | implementation 'org.springframework.kafka:spring-kafka' 32 | compileOnly 'org.projectlombok:lombok' 33 | annotationProcessor 'org.projectlombok:lombok' 34 | // axenapi 35 | annotationProcessor "pro.axenix-innovation:axenapi:1.0.1" 36 | implementation("pro.axenix-innovation:axenapi:1.0.1") { 37 | exclude group: 'org.springdoc' 38 | } 39 | // https://mvnrepository.com/artifact/org.springdoc/springdoc-openapi-ui 40 | implementation 'org.springdoc:springdoc-openapi-ui:1.6.13' 41 | implementation group: 'org.openapitools', name: 'jackson-databind-nullable', version: '0.2.6' 42 | testImplementation 'org.springframework.boot:spring-boot-starter-test' 43 | } 44 | 45 | tasks.named('test') { 46 | useJUnitPlatform() 47 | } 48 | 49 | 50 | 51 | -------------------------------------------------------------------------------- /src/main/resources/templates/gradle_properties.mustache: -------------------------------------------------------------------------------- 1 | version=1.0.0 -------------------------------------------------------------------------------- /src/main/resources/templates/gradle_wrapper_properties.mustache: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.2.1-bin.zip 4 | networkTimeout=10000 5 | validateDistributionUrl=true 6 | zipStoreBase=GRADLE_USER_HOME 7 | zipStorePath=wrapper/dists 8 | -------------------------------------------------------------------------------- /src/main/resources/templates/gradlew.mustache: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # 4 | # Copyright © 2015-2021 the original authors. 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # https://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # 18 | 19 | ############################################################################## 20 | # 21 | # Gradle start up script for POSIX generated by Gradle. 22 | # 23 | # Important for running: 24 | # 25 | # (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is 26 | # noncompliant, but you have some other compliant shell such as ksh or 27 | # bash, then to run this script, type that shell name before the whole 28 | # command line, like: 29 | # 30 | # ksh Gradle 31 | # 32 | # Busybox and similar reduced shells will NOT work, because this script 33 | # requires all of these POSIX shell features: 34 | # * functions; 35 | # * expansions «$var», «${var}», «${var:-default}», «${var+SET}», 36 | # «${var#prefix}», «${var%suffix}», and «$( cmd )»; 37 | # * compound commands having a testable exit status, especially «case»; 38 | # * various built-in commands including «command», «set», and «ulimit». 39 | # 40 | # Important for patching: 41 | # 42 | # (2) This script targets any POSIX shell, so it avoids extensions provided 43 | # by Bash, Ksh, etc; in particular arrays are avoided. 44 | # 45 | # The "traditional" practice of packing multiple parameters into a 46 | # space-separated string is a well documented source of bugs and security 47 | # problems, so this is (mostly) avoided, by progressively accumulating 48 | # options in "$@", and eventually passing that to Java. 49 | # 50 | # Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, 51 | # and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; 52 | # see the in-line comments for details. 53 | # 54 | # There are tweaks for specific operating systems such as AIX, CygWin, 55 | # Darwin, MinGW, and NonStop. 56 | # 57 | # (3) This script is generated from the Groovy template 58 | # https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt 59 | # within the Gradle project. 60 | # 61 | # You can find Gradle at https://github.com/gradle/gradle/. 62 | # 63 | ############################################################################## 64 | 65 | # Attempt to set APP_HOME 66 | 67 | # Resolve links: $0 may be a link 68 | app_path=$0 69 | 70 | # Need this for daisy-chained symlinks. 71 | while 72 | APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path 73 | [ -h "$app_path" ] 74 | do 75 | ls=$( ls -ld "$app_path" ) 76 | link=${ls#*' -> '} 77 | case $link in #( 78 | /*) app_path=$link ;; #( 79 | *) app_path=$APP_HOME$link ;; 80 | esac 81 | done 82 | 83 | # This is normally unused 84 | # shellcheck disable=SC2034 85 | APP_BASE_NAME=${0##*/} 86 | APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit 87 | 88 | # Use the maximum available, or set MAX_FD != -1 to use that value. 89 | MAX_FD=maximum 90 | 91 | warn () { 92 | echo "$*" 93 | } >&2 94 | 95 | die () { 96 | echo 97 | echo "$*" 98 | echo 99 | exit 1 100 | } >&2 101 | 102 | # OS specific support (must be 'true' or 'false'). 103 | cygwin=false 104 | msys=false 105 | darwin=false 106 | nonstop=false 107 | case "$( uname )" in #( 108 | CYGWIN* ) cygwin=true ;; #( 109 | Darwin* ) darwin=true ;; #( 110 | MSYS* | MINGW* ) msys=true ;; #( 111 | NONSTOP* ) nonstop=true ;; 112 | esac 113 | 114 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 115 | 116 | 117 | # Determine the Java command to use to start the JVM. 118 | if [ -n "$JAVA_HOME" ] ; then 119 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 120 | # IBM's JDK on AIX uses strange locations for the executables 121 | JAVACMD=$JAVA_HOME/jre/sh/java 122 | else 123 | JAVACMD=$JAVA_HOME/bin/java 124 | fi 125 | if [ ! -x "$JAVACMD" ] ; then 126 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 127 | 128 | Please set the JAVA_HOME variable in your environment to match the 129 | location of your Java installation." 130 | fi 131 | else 132 | JAVACMD=java 133 | if ! command -v java >/dev/null 2>&1 134 | then 135 | die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 136 | 137 | Please set the JAVA_HOME variable in your environment to match the 138 | location of your Java installation." 139 | fi 140 | fi 141 | 142 | # Increase the maximum file descriptors if we can. 143 | if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then 144 | case $MAX_FD in #( 145 | max*) 146 | # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. 147 | # shellcheck disable=SC3045 148 | MAX_FD=$( ulimit -H -n ) || 149 | warn "Could not query maximum file descriptor limit" 150 | esac 151 | case $MAX_FD in #( 152 | '' | soft) :;; #( 153 | *) 154 | # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. 155 | # shellcheck disable=SC3045 156 | ulimit -n "$MAX_FD" || 157 | warn "Could not set maximum file descriptor limit to $MAX_FD" 158 | esac 159 | fi 160 | 161 | # Collect all arguments for the java command, stacking in reverse order: 162 | # * args from the command line 163 | # * the main class name 164 | # * -classpath 165 | # * -D...appname settings 166 | # * --module-path (only if needed) 167 | # * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. 168 | 169 | # For Cygwin or MSYS, switch paths to Windows format before running java 170 | if "$cygwin" || "$msys" ; then 171 | APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) 172 | CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) 173 | 174 | JAVACMD=$( cygpath --unix "$JAVACMD" ) 175 | 176 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 177 | for arg do 178 | if 179 | case $arg in #( 180 | -*) false ;; # don't mess with options #( 181 | /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath 182 | [ -e "$t" ] ;; #( 183 | *) false ;; 184 | esac 185 | then 186 | arg=$( cygpath --path --ignore --mixed "$arg" ) 187 | fi 188 | # Roll the args list around exactly as many times as the number of 189 | # args, so each arg winds up back in the position where it started, but 190 | # possibly modified. 191 | # 192 | # NB: a `for` loop captures its iteration list before it begins, so 193 | # changing the positional parameters here affects neither the number of 194 | # iterations, nor the values presented in `arg`. 195 | shift # remove old arg 196 | set -- "$@" "$arg" # push replacement arg 197 | done 198 | fi 199 | 200 | 201 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 202 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' 203 | 204 | # Collect all arguments for the java command; 205 | # * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of 206 | # shell script including quotes and variable substitutions, so put them in 207 | # double quotes to make sure that they get re-expanded; and 208 | # * put everything else in single quotes, so that it's not re-expanded. 209 | 210 | set -- \ 211 | "-Dorg.gradle.appname=$APP_BASE_NAME" \ 212 | -classpath "$CLASSPATH" \ 213 | org.gradle.wrapper.GradleWrapperMain \ 214 | "$@" 215 | 216 | # Stop when "xargs" is not available. 217 | if ! command -v xargs >/dev/null 2>&1 218 | then 219 | die "xargs is not available" 220 | fi 221 | 222 | # Use "xargs" to parse quoted args. 223 | # 224 | # With -n1 it outputs one arg per line, with the quotes and backslashes removed. 225 | # 226 | # In Bash we could simply go: 227 | # 228 | # readarray ARGS < <( xargs -n1 <<<"$var" ) && 229 | # set -- "${ARGS[@]}" "$@" 230 | # 231 | # but POSIX shell has neither arrays nor command substitution, so instead we 232 | # post-process each arg (as a line of input to sed) to backslash-escape any 233 | # character that might be a shell metacharacter, then use eval to reverse 234 | # that process (while maintaining the separation between arguments), and wrap 235 | # the whole thing up as a single "set" statement. 236 | # 237 | # This will of course break if any of these variables contains a newline or 238 | # an unmatched quote. 239 | # 240 | 241 | eval "set -- $( 242 | printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | 243 | xargs -n1 | 244 | sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | 245 | tr '\n' ' ' 246 | )" '"$@"' 247 | 248 | exec "$JAVACMD" "$@" 249 | -------------------------------------------------------------------------------- /src/main/resources/templates/gradlew_bat.mustache: -------------------------------------------------------------------------------- 1 | @rem 2 | @rem Copyright 2015 the original author or authors. 3 | @rem 4 | @rem Licensed under the Apache License, Version 2.0 (the "License"); 5 | @rem you may not use this file except in compliance with the License. 6 | @rem You may obtain a copy of the License at 7 | @rem 8 | @rem https://www.apache.org/licenses/LICENSE-2.0 9 | @rem 10 | @rem Unless required by applicable law or agreed to in writing, software 11 | @rem distributed under the License is distributed on an "AS IS" BASIS, 12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | @rem See the License for the specific language governing permissions and 14 | @rem limitations under the License. 15 | @rem 16 | 17 | @if "%DEBUG%"=="" @echo off 18 | @rem ########################################################################## 19 | @rem 20 | @rem Gradle startup script for Windows 21 | @rem 22 | @rem ########################################################################## 23 | 24 | @rem Set local scope for the variables with windows NT shell 25 | if "%OS%"=="Windows_NT" setlocal 26 | 27 | set DIRNAME=%~dp0 28 | if "%DIRNAME%"=="" set DIRNAME=. 29 | @rem This is normally unused 30 | set APP_BASE_NAME=%~n0 31 | set APP_HOME=%DIRNAME% 32 | 33 | @rem Resolve any "." and ".." in APP_HOME to make it shorter. 34 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 35 | 36 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 37 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 38 | 39 | @rem Find java.exe 40 | if defined JAVA_HOME goto findJavaFromJavaHome 41 | 42 | set JAVA_EXE=java.exe 43 | %JAVA_EXE% -version >NUL 2>&1 44 | if %ERRORLEVEL% equ 0 goto execute 45 | 46 | echo. 47 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 48 | echo. 49 | echo Please set the JAVA_HOME variable in your environment to match the 50 | echo location of your Java installation. 51 | 52 | goto fail 53 | 54 | :findJavaFromJavaHome 55 | set JAVA_HOME=%JAVA_HOME:"=% 56 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 57 | 58 | if exist "%JAVA_EXE%" goto execute 59 | 60 | echo. 61 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 62 | echo. 63 | echo Please set the JAVA_HOME variable in your environment to match the 64 | echo location of your Java installation. 65 | 66 | goto fail 67 | 68 | :execute 69 | @rem Setup the command line 70 | 71 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 72 | 73 | 74 | @rem Execute Gradle 75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* 76 | 77 | :end 78 | @rem End local scope for the variables with windows NT shell 79 | if %ERRORLEVEL% equ 0 goto mainEnd 80 | 81 | :fail 82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 83 | rem the _cmd.exe /c_ return code! 84 | set EXIT_CODE=%ERRORLEVEL% 85 | if %EXIT_CODE% equ 0 set EXIT_CODE=1 86 | if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% 87 | exit /b %EXIT_CODE% 88 | 89 | :mainEnd 90 | if "%OS%"=="Windows_NT" endlocal 91 | 92 | :omega 93 | -------------------------------------------------------------------------------- /src/main/resources/templates/jms/clientImpl.mustache: -------------------------------------------------------------------------------- 1 | /** 2 | * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech) ({{{generatorVersion}}}). 3 | * https://openapi-generator.tech 4 | * Do not edit the class manually. 5 | */ 6 | package {{apiPackage}}.impl; 7 | 8 | import service.JmsSenderService; 9 | import org.springframework.stereotype.Component; 10 | import {{apiPackage}}.{{baseName}}Producer; 11 | 12 | import java.util.Map; 13 | 14 | {{#imports}}import {{import}}; 15 | {{/imports}} 16 | 17 | @Component 18 | public class {{baseName}}ProducerImpl implements {{baseName}}Producer { 19 | 20 | private final JmsSenderService jmsSenderService; 21 | 22 | public {{baseName}}ProducerImpl(JmsSenderService jmsSenderService) { 23 | this.jmsSenderService = jmsSenderService; 24 | } 25 | 26 | {{#operations}} 27 | {{#operation}} 28 | {{#vendorExtensions}} 29 | @Override 30 | public void send{{modelName}}({{modelName}} {{modelNameCamel}}, Map params) { 31 | jmsSenderService.send("{{queue}}", {{modelNameCamel}}); 32 | } 33 | {{/vendorExtensions}} 34 | {{/operation}} 35 | {{/operations}} 36 | } 37 | -------------------------------------------------------------------------------- /src/main/resources/templates/jms/listener.mustache: -------------------------------------------------------------------------------- 1 | /** 2 | * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech) ({{{generatorVersion}}}). 3 | * https://openapi-generator.tech 4 | * Do not edit the class manually. 5 | */ 6 | package {{apiPackage}}; 7 | 8 | import {{apiPackage}}.service.{{baseName}}Service; 9 | import org.springframework.beans.factory.annotation.Autowired; 10 | import org.springframework.jms.annotation.JmsListener; 11 | import org.springframework.messaging.handler.annotation.Headers; 12 | import org.springframework.messaging.handler.annotation.Payload; 13 | import pro.axenix_innovation.axenapi.annotation.KafkaHandlerDescription; 14 | import pro.axenix_innovation.axenapi.annotation.KafkaHandlerHeader; 15 | import pro.axenix_innovation.axenapi.annotation.KafkaHandlerHeaders; 16 | import pro.axenix_innovation.axenapi.annotation.KafkaHandlerTags; 17 | import pro.axenix_innovation.axenapi.annotation.KafkaSecured; 18 | import pro.axenix_innovation.axenapi.jms.JmsHandler; 19 | {{#imports}}import {{import}}; 20 | {{/imports}} 21 | 22 | import java.util.Map; 23 | 24 | {{#operations}} 25 | {{#operation}} 26 | {{#vendorExtensions}} 27 | @JmsHandler(jmsTemplateName = "default", destination = "{{queue}}", payload = {{modelName}}.class, 28 | {{#notes}} 29 | description = "{{notes}}") 30 | {{/notes}} 31 | {{/vendorExtensions}} 32 | {{/operation}} 33 | {{/operations}} 34 | public final class {{baseName}}Listener { 35 | private final {{baseName}}Service service; 36 | 37 | @Autowired 38 | public {{baseName}}Listener({{baseName}}Service service) { 39 | this.service = service; 40 | } 41 | 42 | {{#operations}} 43 | {{#operation}} 44 | {{#vendorExtensions}} 45 | @JmsListener(destination = "{{queue}}") 46 | {{#notes}} 47 | @KafkaHandlerDescription("{{notes}}") 48 | {{/notes}} 49 | {{#tags}} 50 | @KafkaHandlerTags(tags = { {{{tags}}} }) 51 | {{/tags}} 52 | {{#hasOptionalParams}} 53 | @KafkaHandlerHeaders(headers = { 54 | {{#queryParams}} 55 | @KafkaHandlerHeader(header = "{{paramName}}"{{#required}}, required = true{{/required}}){{^-last}},{{/-last}} 56 | {{/queryParams}} 57 | }) 58 | {{/hasOptionalParams}} 59 | {{#securityAnnotation}} 60 | {{#hasAuthMethods}} 61 | @{{securityAnnotation}} 62 | {{/hasAuthMethods}} 63 | {{/securityAnnotation}} 64 | {{#resultWrapper}} 65 | {{resultWrapper}}{{#returnSimpleType}}<{{returnType}}>{{/returnSimpleType}}{{^returnSimpleType}}{{/returnSimpleType}} handle{{modelName}}(@Payload {{modelName}} {{modelNameCamel}}{{#hasParams}}, @Headers Map headers{{/hasParams}}) { 66 | {{/resultWrapper}} 67 | {{^resultWrapper}} 68 | {{#returnSimpleType}}{{returnType}}{{/returnSimpleType}}{{^returnSimpleType}}void{{/returnSimpleType}} handle{{modelName}}(@Payload {{modelName}} {{modelNameCamel}}{{#hasParams}}, @Headers Map headers{{/hasParams}}) { 69 | {{/resultWrapper}} 70 | {{#resultWrapper}}return{{/resultWrapper}}{{^resultWrapper}}{{#returnSimpleType}}return{{/returnSimpleType}}{{/resultWrapper}} service.handle{{modelName}}({{modelNameCamel}}{{#hasParams}}, headers{{/hasParams}}); 71 | } 72 | {{/vendorExtensions}} 73 | {{/operation}} 74 | {{/operations}} 75 | } 76 | -------------------------------------------------------------------------------- /src/main/resources/templates/jms/senderService.mustache: -------------------------------------------------------------------------------- 1 | /** 2 | * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech) ({{{generatorVersion}}}). 3 | * https://openapi-generator.tech 4 | * Do not edit the class manually. 5 | */ 6 | package service; 7 | 8 | public interface JmsSenderService { 9 | void send(String queue, Object message); 10 | } 11 | -------------------------------------------------------------------------------- /src/main/resources/templates/jms/senderServiceConfig.mustache: -------------------------------------------------------------------------------- 1 | /** 2 | * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech) (6.3.0). 3 | * https://openapi-generator.tech 4 | * Do not edit the class manually. 5 | */ 6 | package config; 7 | 8 | import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; 9 | import org.springframework.context.annotation.Bean; 10 | import org.springframework.context.annotation.ComponentScan; 11 | import org.springframework.jms.core.JmsTemplate; 12 | import org.springframework.jms.support.converter.MappingJackson2MessageConverter; 13 | import org.springframework.jms.support.converter.MessageConverter; 14 | import service.JmsSenderService; 15 | import service.impl.JmsSenderServiceImpl; 16 | 17 | @ComponentScan("axenapi") 18 | public class JmsSenderServiceConfig { 19 | 20 | @Bean 21 | @ConditionalOnMissingBean 22 | public MessageConverter jmsMessageConverter() { 23 | MappingJackson2MessageConverter converter = new MappingJackson2MessageConverter(); 24 | converter.setTypeIdPropertyName("_type"); 25 | return converter; 26 | } 27 | 28 | @Bean 29 | @ConditionalOnMissingBean 30 | public JmsSenderService jmsSenderService(JmsTemplate template) { 31 | return new JmsSenderServiceImpl(template); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/main/resources/templates/jms/senderServiceImpl.mustache: -------------------------------------------------------------------------------- 1 | /** 2 | * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech) ({{{generatorVersion}}}). 3 | * https://openapi-generator.tech 4 | * Do not edit the class manually. 5 | */ 6 | package service.impl; 7 | 8 | import org.springframework.jms.core.JmsTemplate; 9 | import service.JmsSenderService; 10 | 11 | public class JmsSenderServiceImpl implements JmsSenderService { 12 | 13 | private final JmsTemplate template; 14 | 15 | public JmsSenderServiceImpl(JmsTemplate template) { 16 | this.template = template; 17 | } 18 | 19 | @Override 20 | public void send(String queue, Object message) { 21 | template.convertAndSend(queue, message); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/main/resources/templates/jms/spring_2_autoconfig.mustache: -------------------------------------------------------------------------------- 1 | # Auto Configure 2 | org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ 3 | config.JmsSenderServiceConfig -------------------------------------------------------------------------------- /src/main/resources/templates/jms/spring_3_autoconfig.mustache: -------------------------------------------------------------------------------- 1 | config.JmsSenderServiceConfig -------------------------------------------------------------------------------- /src/main/resources/templates/kafka/KafkaConsumerConfig.mustache: -------------------------------------------------------------------------------- 1 | package {{configPackage}}.kafka; 2 | 3 | import org.apache.kafka.clients.consumer.ConsumerConfig; 4 | import org.apache.kafka.clients.producer.ProducerConfig; 5 | import org.apache.kafka.common.serialization.StringDeserializer; 6 | import org.apache.kafka.common.serialization.StringSerializer; 7 | import org.springframework.context.annotation.Bean; 8 | import org.springframework.context.annotation.Configuration; 9 | import org.springframework.kafka.annotation.EnableKafka; 10 | import org.springframework.kafka.config.ConcurrentKafkaListenerContainerFactory; 11 | import org.springframework.kafka.core.*; 12 | import org.springframework.kafka.support.serializer.JsonDeserializer; 13 | import org.springframework.kafka.support.serializer.JsonSerializer; 14 | 15 | 16 | import java.util.HashMap; 17 | import java.util.Map; 18 | 19 | @EnableKafka 20 | @Configuration 21 | public class KafkaConsumerConfig { 22 | 23 | @Bean 24 | public ConsumerFactory 25 | consumerFactory() { 26 | Map props = new HashMap<>(); 27 | props.put( 28 | ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, 29 | "localhost:29092"); 30 | props.put(ConsumerConfig.GROUP_ID_CONFIG,"example_group"); 31 | props.put(ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class); 32 | props.put(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG,JsonDeserializer.class); 33 | props.put(JsonDeserializer.TRUSTED_PACKAGES, "{{modelPackage}}"); 34 | return new DefaultKafkaConsumerFactory<>(props); 35 | } 36 | 37 | @Bean 38 | public ConcurrentKafkaListenerContainerFactory kafkaListenerContainerFactory() { 39 | ConcurrentKafkaListenerContainerFactory 40 | factory = new ConcurrentKafkaListenerContainerFactory<>(); 41 | factory.setConsumerFactory(consumerFactory()); 42 | return factory; 43 | } 44 | 45 | @Bean 46 | public ProducerFactory producerFactory() { 47 | Map configProps = new HashMap<>(); 48 | configProps.put( 49 | ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, 50 | "localhost:9092"); 51 | configProps.put( 52 | ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG, 53 | StringSerializer.class); 54 | configProps.put( 55 | ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG, 56 | JsonSerializer.class); 57 | return new DefaultKafkaProducerFactory(configProps); 58 | } 59 | 60 | @Bean 61 | public KafkaTemplate kafkaTemplate() { 62 | return new KafkaTemplate<>(producerFactory()); 63 | } 64 | } -------------------------------------------------------------------------------- /src/main/resources/templates/kafka/clientImpl.mustache: -------------------------------------------------------------------------------- 1 | /** 2 | * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech) ({{{generatorVersion}}}). 3 | * https://openapi-generator.tech 4 | * Do not edit the class manually. 5 | */ 6 | package {{apiPackage}}.impl; 7 | 8 | import service.KafkaSenderService; 9 | import org.springframework.stereotype.Component; 10 | import {{apiPackage}}.{{baseName}}Producer; 11 | import java.util.Map; 12 | 13 | {{#imports}}import {{import}}; 14 | {{/imports}} 15 | 16 | @Component 17 | public class {{baseName}}ProducerImpl implements {{baseName}}Producer { 18 | 19 | private final KafkaSenderService kafkaSenderService; 20 | 21 | public {{baseName}}ProducerImpl(KafkaSenderService kafkaSenderService) { 22 | this.kafkaSenderService = kafkaSenderService; 23 | } 24 | 25 | {{#operations}} 26 | {{#operation}} 27 | {{#vendorExtensions}} 28 | @Override 29 | public void send{{modelName}}({{modelName}} {{modelNameCamel}}, Map params) { 30 | kafkaSenderService.send("{{topic}}", {{modelNameCamel}}, params); 31 | } 32 | 33 | {{/vendorExtensions}} 34 | {{/operation}} 35 | {{/operations}} 36 | } 37 | -------------------------------------------------------------------------------- /src/main/resources/templates/kafka/listener.mustache: -------------------------------------------------------------------------------- 1 | /** 2 | * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech) ({{{generatorVersion}}}). 3 | * https://openapi-generator.tech 4 | * Do not edit the class manually. 5 | */ 6 | package {{apiPackage}}; 7 | 8 | import {{apiPackage}}.service.{{baseName}}Service; 9 | import org.springframework.beans.factory.annotation.Autowired; 10 | {{#isUseAxenAPI}} 11 | import pro.axenix_innovation.axenapi.annotation.KafkaHandlerDescription; 12 | import pro.axenix_innovation.axenapi.annotation.KafkaHandlerTags; 13 | import pro.axenix_innovation.axenapi.annotation.KafkaSecured; 14 | import pro.axenix_innovation.axenapi.annotation.KafkaHandlerHeader; 15 | import pro.axenix_innovation.axenapi.annotation.KafkaHandlerHeaders; 16 | {{/isUseAxenAPI}} 17 | import org.springframework.kafka.annotation.KafkaHandler; 18 | import org.springframework.kafka.annotation.KafkaListener; 19 | {{#imports}}import {{import}}; 20 | {{/imports}} 21 | import org.springframework.messaging.handler.annotation.Payload; 22 | import org.springframework.messaging.handler.annotation.Headers; 23 | import java.util.Map; 24 | 25 | {{#operations}} 26 | {{#operation.0.vendorExtensions}} 27 | @KafkaListener(topics = "{{topic}}", groupId = "{{groupId}}") 28 | {{/operation.0.vendorExtensions}} 29 | {{/operations}} 30 | {{^isInterfaceOnly}} 31 | @org.springframework.stereotype.Component 32 | {{/isInterfaceOnly}} 33 | public final class {{baseName}}Listener { 34 | private final {{baseName}}Service service; 35 | 36 | @Autowired 37 | public {{baseName}}Listener({{baseName}}Service service) { 38 | this.service = service; 39 | } 40 | 41 | {{#operations}} 42 | {{#operation}} 43 | {{#vendorExtensions}} 44 | @KafkaHandler 45 | {{#notes}} 46 | {{#isUseAxenAPI}} 47 | @KafkaHandlerDescription("{{notes}}") 48 | {{/isUseAxenAPI}} 49 | {{/notes}} 50 | {{#tags}} 51 | {{#isUseAxenAPI}} 52 | /* @KafkaHandlerTags(tags = { {{{tags}}} }) */ 53 | {{/isUseAxenAPI}} 54 | {{/tags}} 55 | {{#hasOptionalParams}} 56 | {{#isUseAxenAPI}} 57 | @KafkaHandlerHeaders(headers = { 58 | {{#queryParams}} 59 | @KafkaHandlerHeader(header = "{{paramName}}"{{#required}}, required = true{{/required}}){{^-last}},{{/-last}} 60 | {{/queryParams}} 61 | }) 62 | {{/isUseAxenAPI}} 63 | {{/hasOptionalParams}} 64 | {{#securityAnnotation}} 65 | {{#hasAuthMethods}} 66 | @{{securityAnnotation}} 67 | {{/hasAuthMethods}} 68 | {{/securityAnnotation}} 69 | {{#resultWrapper}} 70 | {{resultWrapper}}{{#returnSimpleType}}<{{returnType}}>{{/returnSimpleType}}{{^returnSimpleType}}{{/returnSimpleType}} handle{{modelName}}(@Payload {{modelName}} {{modelNameCamel}}{{#hasParams}}, @Headers Map headers{{/hasParams}}) { 71 | {{/resultWrapper}} 72 | {{^resultWrapper}} 73 | {{#returnSimpleType}}{{returnType}}{{/returnSimpleType}}{{^returnSimpleType}}void{{/returnSimpleType}} handle{{modelName}}(@Payload {{modelName}} {{modelNameCamel}}{{#hasParams}}, @Headers Map headers{{/hasParams}}) { 74 | {{/resultWrapper}} 75 | {{#resultWrapper}}return{{/resultWrapper}}{{^resultWrapper}}{{#returnSimpleType}}return{{/returnSimpleType}}{{/resultWrapper}} service.handle{{modelName}}({{modelNameCamel}}{{#hasParams}}, headers{{/hasParams}}); 76 | } 77 | {{/vendorExtensions}} 78 | {{/operation}} 79 | {{/operations}} 80 | } 81 | -------------------------------------------------------------------------------- /src/main/resources/templates/kafka/producerConfig.mustache: -------------------------------------------------------------------------------- 1 | package config; 2 | 3 | import org.apache.kafka.clients.producer.ProducerConfig; 4 | import org.apache.kafka.common.serialization.StringSerializer; 5 | import org.springframework.beans.factory.annotation.Value; 6 | import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; 7 | import org.springframework.context.annotation.Bean; 8 | import org.springframework.kafka.core.DefaultKafkaProducerFactory; 9 | import org.springframework.kafka.core.KafkaTemplate; 10 | import org.springframework.kafka.core.ProducerFactory; 11 | import org.springframework.kafka.support.serializer.JsonSerializer; 12 | import org.springframework.kafka.transaction.KafkaTransactionManager; 13 | 14 | import java.util.HashMap; 15 | import java.util.Map; 16 | 17 | public class KafkaProducerConfig { 18 | 19 | @Value("${spring.kafka.bootstrap-servers}") 20 | private String kafkaBootstrap; 21 | 22 | @Bean("producerFactory") 23 | @ConditionalOnMissingBean 24 | public ProducerFactory producerFactory() { 25 | Map configProps = new HashMap<>(); 26 | configProps.put( 27 | ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, 28 | kafkaBootstrap); 29 | configProps.put( 30 | ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG, 31 | StringSerializer.class); 32 | configProps.put( 33 | ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG, 34 | JsonSerializer.class); 35 | configProps.put(ProducerConfig.ENABLE_IDEMPOTENCE_CONFIG, "true"); 36 | configProps.put(ProducerConfig.TRANSACTIONAL_ID_CONFIG, "prod-1"); 37 | DefaultKafkaProducerFactory factory = new DefaultKafkaProducerFactory<>(configProps); 38 | //factory.setTransactionIdPrefix("prod"); 39 | return factory; 40 | } 41 | 42 | @Bean 43 | public KafkaTransactionManager kafkaTransactionManager(ProducerFactory producerFactory) { 44 | return new KafkaTransactionManager(producerFactory); 45 | } 46 | 47 | @Bean 48 | @ConditionalOnMissingBean 49 | public KafkaTemplate kafkaTemplate(ProducerFactory producerFactory) { 50 | return new KafkaTemplate<>(producerFactory); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/main/resources/templates/kafka/senderService.mustache: -------------------------------------------------------------------------------- 1 | /** 2 | * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech) ({{{generatorVersion}}}). 3 | * https://openapi-generator.tech 4 | * Do not edit the class manually. 5 | */ 6 | package service; 7 | 8 | import java.util.Map; 9 | 10 | public interface KafkaSenderService { 11 | void send(String topicName, Object message, Map params); 12 | } 13 | -------------------------------------------------------------------------------- /src/main/resources/templates/kafka/senderServiceConfig.mustache: -------------------------------------------------------------------------------- 1 | /** 2 | * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech) (6.3.0). 3 | * https://openapi-generator.tech 4 | * Do not edit the class manually. 5 | */ 6 | package config; 7 | 8 | import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; 9 | import org.springframework.context.annotation.Bean; 10 | import org.springframework.kafka.core.KafkaTemplate; 11 | import org.springframework.kafka.support.converter.MessagingMessageConverter; 12 | import service.KafkaSenderService; 13 | import service.impl.KafkaSenderServiceImpl; 14 | import org.springframework.context.annotation.ComponentScan; 15 | 16 | @ComponentScan("axenapi") 17 | public class KafkaSenderServiceConfig { 18 | 19 | private final KafkaTemplate kafkaTemplate; 20 | 21 | public KafkaSenderServiceConfig(KafkaTemplate kafkaTemplate) { 22 | this.kafkaTemplate = kafkaTemplate; 23 | } 24 | 25 | @Bean 26 | @ConditionalOnMissingBean 27 | public MessagingMessageConverter converter() { 28 | MessagingMessageConverter converter = new MessagingMessageConverter(); 29 | converter.setGenerateMessageId(true); 30 | converter.setGenerateTimestamp(true); 31 | return converter; 32 | } 33 | 34 | @Bean 35 | @ConditionalOnMissingBean 36 | public KafkaSenderService kafkaSenderService(MessagingMessageConverter converter) { 37 | return new KafkaSenderServiceImpl(kafkaTemplate, converter); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/main/resources/templates/kafka/senderServiceImpl.mustache: -------------------------------------------------------------------------------- 1 | /** 2 | * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech) ({{{generatorVersion}}}). 3 | * https://openapi-generator.tech 4 | * Do not edit the class manually. 5 | */ 6 | package service.impl; 7 | 8 | import org.springframework.kafka.support.KafkaHeaders; 9 | import org.springframework.messaging.support.MessageHeaderAccessor; 10 | import org.springframework.stereotype.Service; 11 | import service.KafkaSenderService; 12 | import org.apache.kafka.clients.producer.ProducerRecord; 13 | import org.springframework.kafka.core.KafkaTemplate; 14 | 15 | import java.util.Map; 16 | 17 | import org.springframework.kafka.support.converter.MessagingMessageConverter; 18 | import org.springframework.messaging.Message; 19 | import org.springframework.messaging.support.MessageBuilder; 20 | 21 | import java.nio.charset.StandardCharsets; 22 | import java.util.UUID; 23 | 24 | @Service 25 | public class KafkaSenderServiceImpl implements KafkaSenderService { 26 | 27 | private String messageIdName = "{{messageIdName}}"; 28 | private String correlationIdName = "{{correlationIdName}}"; 29 | private Boolean sendBytes = {{sendBytes}}; 30 | private Boolean generateMessageId = {{generateMessageId}}; 31 | private Boolean generateCorrelationId = {{generateCorrelationId}}; 32 | 33 | private final KafkaTemplate kafkaTemplate; 34 | private final MessagingMessageConverter converter; 35 | 36 | public KafkaSenderServiceImpl(KafkaTemplate kafkaTemplate, 37 | MessagingMessageConverter converter) { 38 | this.kafkaTemplate = kafkaTemplate; 39 | this.converter = converter; 40 | } 41 | 42 | public KafkaTemplate getKafkaTemplate() { 43 | return kafkaTemplate; 44 | } 45 | 46 | public void send(String topicName, Object message, Map params) { 47 | 48 | MessageHeaderAccessor headerAccessor = new MessageHeaderAccessor(); 49 | if (generateMessageId) { 50 | if (sendBytes) { 51 | headerAccessor.setHeader(messageIdName, UUID.randomUUID().toString().getBytes(StandardCharsets.UTF_8)); 52 | } else { 53 | headerAccessor.setHeader(messageIdName, UUID.randomUUID()); 54 | } 55 | } 56 | if (generateCorrelationId) { 57 | if (sendBytes) { 58 | headerAccessor.setHeader(correlationIdName, UUID.randomUUID().toString().getBytes(StandardCharsets.UTF_8)); 59 | } else { 60 | headerAccessor.setHeader(correlationIdName, UUID.randomUUID()); 61 | } 62 | } 63 | if (params != null && params.size() > 0) { 64 | for (var entry : params.entrySet()) { 65 | if (sendBytes) { 66 | headerAccessor.setHeader(entry.getKey(), entry.getValue().getBytes(StandardCharsets.UTF_8)); 67 | } else { 68 | headerAccessor.setHeader(entry.getKey(), entry.getValue()); 69 | } 70 | } 71 | } 72 | Message msg = MessageBuilder.createMessage(message, headerAccessor.getMessageHeaders()); 73 | ProducerRecord producerRecord = converter.fromMessage(msg, topicName); 74 | kafkaTemplate.send(producerRecord); 75 | } 76 | } 77 | 78 | -------------------------------------------------------------------------------- /src/main/resources/templates/kafka/spring_2_autoconfig.mustache: -------------------------------------------------------------------------------- 1 | # Auto Configure 2 | org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ 3 | config.KafkaSenderServiceConfig,\ 4 | config.KafkaProducerConfig -------------------------------------------------------------------------------- /src/main/resources/templates/kafka/spring_3_autoconfig.mustache: -------------------------------------------------------------------------------- 1 | config.KafkaSenderServiceConfig 2 | config.KafkaProducerConfig -------------------------------------------------------------------------------- /src/main/resources/templates/listenerService.mustache: -------------------------------------------------------------------------------- 1 | /** 2 | * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech) ({{{generatorVersion}}}). 3 | * https://openapi-generator.tech 4 | * Do not edit the class manually. 5 | */ 6 | package {{apiPackage}}.service; 7 | 8 | import {{apiPackage}}.{{baseName}}Listener; 9 | {{#imports}}import {{import}}; 10 | {{/imports}} 11 | 12 | import java.util.Map; 13 | 14 | /** 15 | * The service is used by {@link {{baseName}}Listener}. Implement to provide your business logic. 16 | */ 17 | public interface {{baseName}}Service { 18 | {{#operations}} 19 | {{#operation}} 20 | {{#vendorExtensions}} 21 | {{#resultWrapper}} 22 | {{resultWrapper}}{{#returnSimpleType}}<{{returnType}}>{{/returnSimpleType}}{{^returnSimpleType}}{{/returnSimpleType}} handle{{modelName}}({{modelName}} {{modelNameCamel}}{{#hasParams}}, Map headers{{/hasParams}}); 23 | {{/resultWrapper}} 24 | {{^resultWrapper}} 25 | {{#returnSimpleType}}{{returnType}}{{/returnSimpleType}}{{^returnSimpleType}}void{{/returnSimpleType}} handle{{modelName}}({{modelName}} {{modelNameCamel}}{{#hasParams}}, Map headers{{/hasParams}}); 26 | {{/resultWrapper}} 27 | {{/vendorExtensions}} 28 | {{/operation}} 29 | {{/operations}} 30 | } 31 | -------------------------------------------------------------------------------- /src/main/resources/templates/listenerServiceImpl.mustache: -------------------------------------------------------------------------------- 1 | /** 2 | * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech) ({{{generatorVersion}}}). 3 | * https://openapi-generator.tech 4 | * Do not edit the class manually. 5 | */ 6 | package {{apiPackage}}.service; 7 | 8 | import {{apiPackage}}.{{baseName}}Listener; 9 | {{#imports}}import {{import}}; 10 | {{/imports}} 11 | 12 | import java.util.stream.Collectors; 13 | import java.util.Map; 14 | import org.springframework.stereotype.Service; 15 | 16 | /** 17 | * The service is used by {@link {{baseName}}Listener}. Implement to provide your business logic. 18 | */ 19 | @Service 20 | public class {{baseName}}ServiceImpl implements {{baseName}}Service { 21 | {{#operations}} 22 | {{#operation}} 23 | {{#vendorExtensions}} 24 | {{#resultWrapper}} 25 | public {{resultWrapper}}{{#returnSimpleType}}<{{returnType}}>{{/returnSimpleType}}{{^returnSimpleType}}{{/returnSimpleType}} handle{{modelName}}({{modelName}} {{modelNameCamel}}{{#hasParams}}, Map headers{{/hasParams}}) { 26 | {{/resultWrapper}} 27 | {{^resultWrapper}} 28 | public {{#returnSimpleType}}{{returnType}}{{/returnSimpleType}}{{^returnSimpleType}}void{{/returnSimpleType}} handle{{modelName}}({{modelName}} {{modelNameCamel}}{{#hasParams}}, Map headers{{/hasParams}}) { 29 | {{/resultWrapper}} 30 | System.out.println("handle message" + {{modelNameCamel}}.toString()); 31 | {{#returnSimpleType}} 32 | return null; 33 | {{/returnSimpleType}} 34 | } 35 | {{/vendorExtensions}} 36 | {{/operation}} 37 | {{/operations}} 38 | } 39 | -------------------------------------------------------------------------------- /src/main/resources/templates/model.mustache: -------------------------------------------------------------------------------- 1 | {{! Adapted copy of openapi-generator-6.4.0.jar!\JavaSpring\model.mustache }} 2 | package {{package}}; 3 | 4 | import java.net.URI; 5 | import java.util.Objects; 6 | {{#imports}}import {{import}}; 7 | {{/imports}} 8 | {{#openApiNullable}} 9 | import org.openapitools.jackson.nullable.JsonNullable; 10 | {{/openApiNullable}} 11 | {{#serializableModel}} 12 | import java.io.Serializable; 13 | {{/serializableModel}} 14 | import java.time.OffsetDateTime; 15 | {{#useBeanValidation}} 16 | import {{javaxPackage}}.validation.Valid; 17 | import {{javaxPackage}}.validation.constraints.*; 18 | {{/useBeanValidation}} 19 | {{^useBeanValidation}} 20 | import {{javaxPackage}}.validation.constraints.NotNull; 21 | {{/useBeanValidation}} 22 | {{#performBeanValidation}} 23 | import org.hibernate.validator.constraints.*; 24 | {{/performBeanValidation}} 25 | {{#jackson}} 26 | {{#withXml}} 27 | import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlRootElement; 28 | import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty; 29 | {{/withXml}} 30 | {{/jackson}} 31 | {{#swagger2AnnotationLibrary}} 32 | import io.swagger.v3.oas.annotations.media.Schema; 33 | {{/swagger2AnnotationLibrary}} 34 | 35 | {{#withXml}} 36 | import {{javaxPackage}}.xml.bind.annotation.*; 37 | {{/withXml}} 38 | {{^parent}} 39 | {{#hateoas}} 40 | import org.springframework.hateoas.RepresentationModel; 41 | {{/hateoas}} 42 | {{/parent}} 43 | 44 | import java.util.*; 45 | import {{javaxPackage}}.annotation.Generated; 46 | {{#models}}{{#model}} 47 | {{#isUseAxenAPI}} 48 | {{#vendorExtensions.x-outgoing}} 49 | import pro.axenix_innovation.axenapi.annotation.Outgoing; 50 | {{/vendorExtensions.x-outgoing}} 51 | {{/isUseAxenAPI}} 52 | {{/model}}{{/models}} 53 | 54 | {{#models}} 55 | {{#model}} 56 | {{#isEnum}} 57 | {{>enumOuterClass}} 58 | {{/isEnum}} 59 | {{^isEnum}} 60 | {{#vendorExtensions.x-is-one-of-interface}}{{>oneof_interface}}{{/vendorExtensions.x-is-one-of-interface}}{{^vendorExtensions.x-is-one-of-interface}}{{>pojo}}{{/vendorExtensions.x-is-one-of-interface}} 61 | {{/isEnum}} 62 | {{/model}} 63 | {{/models}} 64 | -------------------------------------------------------------------------------- /src/main/resources/templates/pojo.mustache: -------------------------------------------------------------------------------- 1 | {{! Adapted copy of openapi-generator-6.4.0.jar!\JavaSpring\pojo.mustache }} 2 | /** 3 | * {{description}}{{^description}}{{classname}}{{/description}} 4 | */ 5 | {{>additionalModelTypeAnnotations}} 6 | {{#description}} 7 | {{#swagger1AnnotationLibrary}} 8 | @ApiModel(description = "{{{description}}}") 9 | {{/swagger1AnnotationLibrary}} 10 | {{#swagger2AnnotationLibrary}} 11 | @Schema({{#name}}name = "{{name}}", {{/name}}description = "{{{description}}}") 12 | {{/swagger2AnnotationLibrary}} 13 | {{/description}} 14 | {{#isUseAxenAPI}} 15 | {{#vendorExtensions.x-outgoing}} 16 | @Outgoing(topics = { {{#topics}}"{{.}}"{{^-last}}, {{/-last}}{{/topics}} }, type = Outgoing.Type.{{#lambda.uppercase}}{{type}}{{/lambda.uppercase}}{{^tags}}){{/tags}}{{#tags}}{{#-first}}, 17 | tags = { {{/-first}}"{{.}}"{{^-last}}, {{/-last}}{{#-last}} }){{/-last}}{{/tags}} 18 | {{/vendorExtensions.x-outgoing}} 19 | {{/isUseAxenAPI}} 20 | {{#discriminator}} 21 | {{>typeInfoAnnotation}} 22 | {{/discriminator}} 23 | {{#jackson}} 24 | {{#isClassnameSanitized}} 25 | {{^hasDiscriminatorWithNonEmptyMapping}} 26 | @JsonTypeName("{{name}}") 27 | {{/hasDiscriminatorWithNonEmptyMapping}} 28 | {{/isClassnameSanitized}} 29 | {{/jackson}} 30 | {{#withXml}} 31 | {{>xmlAnnotation}} 32 | {{/withXml}} 33 | {{>generatedAnnotation}} 34 | {{#vendorExtensions.x-class-extra-annotation}} 35 | {{{vendorExtensions.x-class-extra-annotation}}} 36 | {{/vendorExtensions.x-class-extra-annotation}} 37 | public class {{classname}}{{#parent}} extends {{{parent}}}{{/parent}}{{^parent}}{{#hateoas}} extends RepresentationModel<{{classname}}> {{/hateoas}}{{/parent}}{{#vendorExtensions.x-implements}}{{#-first}} implements {{{.}}}{{/-first}}{{^-first}}, {{{.}}}{{/-first}}{{/vendorExtensions.x-implements}} { 38 | {{#serializableModel}} 39 | 40 | private static final long serialVersionUID = 1L; 41 | {{/serializableModel}} 42 | {{#vars}} 43 | 44 | {{#isEnum}} 45 | {{^isContainer}} 46 | {{>enumClass}} 47 | {{/isContainer}} 48 | {{#isContainer}} 49 | {{#mostInnerItems}} 50 | {{>enumClass}} 51 | {{/mostInnerItems}} 52 | {{/isContainer}} 53 | {{/isEnum}} 54 | {{#jackson}} 55 | @JsonProperty("{{baseName}}") 56 | {{#withXml}} 57 | @JacksonXmlProperty({{#isXmlAttribute}}isAttribute = true, {{/isXmlAttribute}}{{#xmlNamespace}}namespace="{{.}}", {{/xmlNamespace}}localName = "{{xmlName}}{{^xmlName}}{{baseName}}{{/xmlName}}") 58 | {{/withXml}} 59 | {{/jackson}} 60 | {{#gson}} 61 | @SerializedName("{{baseName}}") 62 | {{/gson}} 63 | {{#vendorExtensions.x-field-extra-annotation}} 64 | {{{vendorExtensions.x-field-extra-annotation}}} 65 | {{/vendorExtensions.x-field-extra-annotation}} 66 | {{#isContainer}} 67 | {{#useBeanValidation}}@Valid{{/useBeanValidation}} 68 | {{#openApiNullable}} 69 | private {{>nullableDataType}} {{name}} = {{#isNullable}}JsonNullable.undefined(){{/isNullable}}{{^isNullable}}{{#required}}{{{defaultValue}}}{{/required}}{{^required}}null{{/required}}{{/isNullable}}; 70 | {{/openApiNullable}} 71 | {{^openApiNullable}} 72 | private {{>nullableDataType}} {{name}} = {{#required}}{{{defaultValue}}}{{/required}}{{^required}}null{{/required}}; 73 | {{/openApiNullable}} 74 | {{/isContainer}} 75 | {{^isContainer}} 76 | {{#isDate}} 77 | @DateTimeFormat(iso = DateTimeFormat.ISO.DATE) 78 | {{/isDate}} 79 | {{#isDateTime}} 80 | @DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) 81 | {{/isDateTime}} 82 | {{#openApiNullable}} 83 | private {{>nullableDataType}} {{name}}{{#isNullable}} = JsonNullable.undefined(){{/isNullable}}{{^isNullable}}{{#defaultValue}} = {{{.}}}{{/defaultValue}}{{/isNullable}}; 84 | {{/openApiNullable}} 85 | {{^openApiNullable}} 86 | private {{>nullableDataType}} {{name}}{{#isNullable}} = null{{/isNullable}}{{^isNullable}}{{#defaultValue}} = {{{.}}}{{/defaultValue}}{{/isNullable}}; 87 | {{/openApiNullable}} 88 | {{/isContainer}} 89 | {{/vars}} 90 | {{#vars}} 91 | 92 | {{! begin feature: fluent setter methods }} 93 | public {{classname}} {{name}}({{{datatypeWithEnum}}} {{name}}) { 94 | {{#openApiNullable}} 95 | this.{{name}} = {{#isNullable}}JsonNullable.of({{name}}){{/isNullable}}{{^isNullable}}{{name}}{{/isNullable}}; 96 | {{/openApiNullable}} 97 | {{^openApiNullable}} 98 | this.{{name}} = {{name}}; 99 | {{/openApiNullable}} 100 | return this; 101 | } 102 | {{#isArray}} 103 | 104 | public {{classname}} add{{nameInCamelCase}}Item({{{items.datatypeWithEnum}}} {{name}}Item) { 105 | {{#openApiNullable}} 106 | {{^required}} 107 | if (this.{{name}} == null{{#isNullable}} || !this.{{name}}.isPresent(){{/isNullable}}) { 108 | this.{{name}} = {{#isNullable}}JsonNullable.of({{/isNullable}}{{{defaultValue}}}{{^defaultValue}}new {{#uniqueItems}}LinkedHashSet{{/uniqueItems}}{{^uniqueItems}}ArrayList{{/uniqueItems}}<>(){{/defaultValue}}{{#isNullable}}){{/isNullable}}; 109 | } 110 | {{/required}} 111 | this.{{name}}{{#isNullable}}.get(){{/isNullable}}.add({{name}}Item); 112 | {{/openApiNullable}} 113 | {{^openApiNullable}} 114 | if (this.{{name}} == null) { 115 | this.{{name}} = {{{defaultValue}}}; 116 | } 117 | this.{{name}}.add({{name}}Item); 118 | {{/openApiNullable}} 119 | return this; 120 | } 121 | {{/isArray}} 122 | {{#isMap}} 123 | 124 | public {{classname}} put{{nameInCamelCase}}Item(String key, {{{items.datatypeWithEnum}}} {{name}}Item) { 125 | {{^required}} 126 | if (this.{{name}} == null) { 127 | this.{{name}} = {{{defaultValue}}}{{^defaultValue}}new HashMap<>(){{/defaultValue}}; 128 | } 129 | {{/required}} 130 | this.{{name}}.put(key, {{name}}Item); 131 | return this; 132 | } 133 | {{/isMap}} 134 | {{! end feature: fluent setter methods }} 135 | {{! begin feature: getter and setter }} 136 | 137 | /** 138 | {{#description}} 139 | * {{{.}}} 140 | {{/description}} 141 | {{^description}} 142 | * Get {{name}} 143 | {{/description}} 144 | {{#minimum}} 145 | * minimum: {{.}} 146 | {{/minimum}} 147 | {{#maximum}} 148 | * maximum: {{.}} 149 | {{/maximum}} 150 | * @return {{name}} 151 | */ 152 | {{#vendorExtensions.x-extra-annotation}} 153 | {{{vendorExtensions.x-extra-annotation}}} 154 | {{/vendorExtensions.x-extra-annotation}} 155 | {{#useBeanValidation}} 156 | {{>beanValidation}} 157 | {{/useBeanValidation}} 158 | {{^useBeanValidation}} 159 | {{#required}}@NotNull{{/required}} 160 | {{/useBeanValidation}} 161 | {{#swagger2AnnotationLibrary}} 162 | @Schema(name = "{{{baseName}}}"{{#isReadOnly}}, accessMode = Schema.AccessMode.READ_ONLY{{/isReadOnly}}{{#example}}, example = "{{{.}}}"{{/example}}{{#description}}, description = "{{{.}}}"{{/description}}, requiredMode = {{#required}}Schema.RequiredMode.REQUIRED{{/required}}{{^required}}Schema.RequiredMode.NOT_REQUIRED{{/required}}) 163 | {{/swagger2AnnotationLibrary}} 164 | {{#swagger1AnnotationLibrary}} 165 | @ApiModelProperty({{#example}}example = "{{{.}}}", {{/example}}{{#required}}required = {{required}}, {{/required}}{{#isReadOnly}}readOnly = {{{isReadOnly}}}, {{/isReadOnly}}value = "{{{description}}}") 166 | {{/swagger1AnnotationLibrary}} 167 | public {{>nullableDataType}} {{getter}}() { 168 | return {{name}}; 169 | } 170 | 171 | {{#vendorExtensions.x-setter-extra-annotation}} 172 | {{{vendorExtensions.x-setter-extra-annotation}}} 173 | {{/vendorExtensions.x-setter-extra-annotation}} 174 | public void {{setter}}({{>nullableDataType}} {{name}}) { 175 | this.{{name}} = {{name}}; 176 | } 177 | {{! end feature: getter and setter }} 178 | {{/vars}} 179 | {{#parentVars}} 180 | 181 | {{! begin feature: fluent setter methods for inherited properties }} 182 | public {{classname}} {{name}}({{{datatypeWithEnum}}} {{name}}) { 183 | super.{{setter}}({{name}}); 184 | return this; 185 | } 186 | {{#isArray}} 187 | 188 | public {{classname}} add{{nameInCamelCase}}Item({{{items.datatypeWithEnum}}} {{name}}Item) { 189 | super.add{{nameInCamelCase}}Item({{name}}Item); 190 | return this; 191 | } 192 | {{/isArray}} 193 | {{#isMap}} 194 | 195 | public {{classname}} put{{nameInCamelCase}}Item(String key, {{{items.datatypeWithEnum}}} {{name}}Item) { 196 | super.put{{nameInCamelCase}}Item(key, {{name}}Item); 197 | return this; 198 | } 199 | {{/isMap}} 200 | {{! end feature: fluent setter methods for inherited properties }} 201 | {{/parentVars}} 202 | 203 | @Override 204 | public boolean equals(Object o) { 205 | if (this == o) { 206 | return true; 207 | } 208 | if (o == null || getClass() != o.getClass()) { 209 | return false; 210 | }{{#hasVars}} 211 | {{classname}} {{classVarName}} = ({{classname}}) o; 212 | return {{#vars}}{{#vendorExtensions.x-is-jackson-optional-nullable}}equalsNullable(this.{{name}}, {{classVarName}}.{{name}}){{/vendorExtensions.x-is-jackson-optional-nullable}}{{^vendorExtensions.x-is-jackson-optional-nullable}}{{#isByteArray}}Arrays{{/isByteArray}}{{^isByteArray}}Objects{{/isByteArray}}.equals(this.{{name}}, {{classVarName}}.{{name}}){{/vendorExtensions.x-is-jackson-optional-nullable}}{{^-last}} && 213 | {{/-last}}{{/vars}}{{#parent}} && 214 | super.equals(o){{/parent}};{{/hasVars}}{{^hasVars}} 215 | return true;{{/hasVars}} 216 | } 217 | {{#vendorExtensions.x-jackson-optional-nullable-helpers}} 218 | 219 | private static boolean equalsNullable(JsonNullable a, JsonNullable b) { 220 | return a == b || (a != null && b != null && a.isPresent() && b.isPresent() && Objects.deepEquals(a.get(), b.get())); 221 | } 222 | {{/vendorExtensions.x-jackson-optional-nullable-helpers}} 223 | 224 | @Override 225 | public int hashCode() { 226 | return Objects.hash({{#vars}}{{#vendorExtensions.x-is-jackson-optional-nullable}}hashCodeNullable({{name}}){{/vendorExtensions.x-is-jackson-optional-nullable}}{{^vendorExtensions.x-is-jackson-optional-nullable}}{{^isByteArray}}{{name}}{{/isByteArray}}{{#isByteArray}}Arrays.hashCode({{name}}){{/isByteArray}}{{/vendorExtensions.x-is-jackson-optional-nullable}}{{^-last}}, {{/-last}}{{/vars}}{{#parent}}{{#hasVars}}, {{/hasVars}}super.hashCode(){{/parent}}); 227 | } 228 | {{#vendorExtensions.x-jackson-optional-nullable-helpers}} 229 | 230 | private static int hashCodeNullable(JsonNullable a) { 231 | if (a == null) { 232 | return 1; 233 | } 234 | return a.isPresent() ? Arrays.deepHashCode(new Object[]{a.get()}) : 31; 235 | } 236 | {{/vendorExtensions.x-jackson-optional-nullable-helpers}} 237 | 238 | @Override 239 | public String toString() { 240 | StringBuilder sb = new StringBuilder(); 241 | sb.append("class {{classname}} {\n"); 242 | {{#parent}} 243 | sb.append(" ").append(toIndentedString(super.toString())).append("\n"); 244 | {{/parent}} 245 | {{#vars}}sb.append(" {{name}}: ").append(toIndentedString({{name}})).append("\n"); 246 | {{/vars}}sb.append("}"); 247 | return sb.toString(); 248 | } 249 | 250 | /** 251 | * Convert the given object to string with each line indented by 4 spaces 252 | * (except the first line). 253 | */ 254 | private String toIndentedString(Object o) { 255 | if (o == null) { 256 | return "null"; 257 | } 258 | return o.toString().replace("\n", "\n "); 259 | } 260 | } 261 | -------------------------------------------------------------------------------- /src/main/resources/templates/pom.mustache: -------------------------------------------------------------------------------- 1 | 2 | 4.0.0 3 | {{groupId}} 4 | {{artifactId}} 5 | jar 6 | {{artifactId}} 7 | {{artifactVersion}} 8 | 9 | 1.8 10 | ${java.version} 11 | ${java.version} 12 | UTF-8 13 | {{#springFoxDocumentationProvider}} 14 | 2.9.2 15 | {{/springFoxDocumentationProvider}} 16 | {{#springDocDocumentationProvider}} 17 | 1.6.14 18 | {{/springDocDocumentationProvider}} 19 | {{^springFoxDocumentationProvider}} 20 | {{^springDocDocumentationProvider}} 21 | {{#swagger1AnnotationLibrary}} 22 | 1.6.6 23 | {{/swagger1AnnotationLibrary}} 24 | {{#swagger2AnnotationLibrary}} 25 | 2.2.7 26 | {{/swagger2AnnotationLibrary}} 27 | {{/springDocDocumentationProvider}} 28 | {{/springFoxDocumentationProvider}} 29 | {{#virtualService}} 30 | 2.5.2 31 | {{/virtualService}} 32 | {{#useSwaggerUI}} 33 | 5.3.1 34 | {{/useSwaggerUI}} 35 | 36 | {{#parentOverridden}} 37 | 38 | {{{parentGroupId}}} 39 | {{{parentArtifactId}}} 40 | {{{parentVersion}}} 41 | 42 | {{/parentOverridden}} 43 | {{^parentOverridden}} 44 | 45 | org.springframework.boot 46 | spring-boot-starter-parent 47 | {{#springFoxDocumentationProvider}}2.5.14{{/springFoxDocumentationProvider}}{{^springFoxDocumentationProvider}}2.7.15{{/springFoxDocumentationProvider}} 48 | 49 | 50 | {{/parentOverridden}} 51 | 52 | src/main/java 53 | {{^interfaceOnly}} 54 | 55 | 56 | org.springframework.boot 57 | spring-boot-maven-plugin 58 | 59 | {{#classifier}} 60 | {{{classifier}}} 61 | {{/classifier}} 62 | {{#lombok}} 63 | 64 | 65 | org.projectlombok 66 | lombok 67 | 68 | 69 | {{/lombok}} 70 | 71 | 72 | {{#apiFirst}} 73 | 74 | org.openapitools 75 | openapi-generator-maven-plugin 76 | {{{generatorVersion}}} 77 | 78 | 79 | 80 | generate 81 | 82 | 83 | src/main/resources/openapi.yaml 84 | spring 85 | {{{apiPackage}}} 86 | {{{modelPackage}}} 87 | false 88 | {{#modelNamePrefix}} 89 | {{{.}}} 90 | {{/modelNamePrefix}} 91 | {{#modelNameSuffix}} 92 | {{{.}}} 93 | {{/modelNameSuffix}} 94 | 95 | {{#configOptions}} 96 | <{{left}}>{{right}} 97 | {{/configOptions}} 98 | 99 | 100 | 101 | 102 | 103 | {{/apiFirst}} 104 | 105 | {{/interfaceOnly}} 106 | 107 | 108 | 109 | 110 | com.squareup 111 | javapoet 112 | 1.13.0 113 | 114 | 115 | org.springframework.boot 116 | spring-boot-starter-web{{#reactive}}flux{{/reactive}} 117 | 118 | 119 | org.springframework.data 120 | spring-data-commons 121 | 122 | {{#useKafka}} 123 | 124 | org.springframework.kafka 125 | spring-kafka 126 | 127 | {{/useKafka}} 128 | {{#useRabbit}} 129 | 130 | org.springframework.amqp 131 | spring-rabbit 132 | 133 | {{/useRabbit}} 134 | {{#useJms}} 135 | 136 | org.springframework.jms 137 | spring-jms 138 | 139 | {{/useJms}} 140 | {{#isUseAxenAPI}} 141 | 142 | pro.axenix-innovation 143 | axenapi 144 | {{axenAPIVersion}} 145 | 146 | {{/isUseAxenAPI}} 147 | {{#springDocDocumentationProvider}} 148 | 149 | {{#useSwaggerUI}} 150 | 151 | org.springdoc 152 | springdoc-openapi-{{#reactive}}webflux-{{/reactive}}ui 153 | ${springdoc.version} 154 | 155 | {{/useSwaggerUI}} 156 | {{^useSwaggerUI}} 157 | 158 | org.springdoc 159 | springdoc-openapi-{{#reactive}}webflux{{/reactive}}{{^reactive}}webmvc{{/reactive}}-core 160 | ${springdoc.version} 161 | 162 | {{/useSwaggerUI}} 163 | {{/springDocDocumentationProvider}} 164 | {{#springFoxDocumentationProvider}} 165 | 166 | 167 | io.springfox 168 | springfox-swagger2 169 | ${springfox.version} 170 | 171 | {{/springFoxDocumentationProvider}} 172 | {{#useSwaggerUI}} 173 | {{^springDocDocumentationProvider}} 174 | 175 | org.webjars 176 | swagger-ui 177 | ${swagger-ui.version} 178 | 179 | 180 | org.webjars 181 | webjars-locator-core 182 | 183 | {{/springDocDocumentationProvider}} 184 | {{/useSwaggerUI}} 185 | {{^springFoxDocumentationProvider}} 186 | {{^springDocDocumentationProvider}} 187 | {{#swagger1AnnotationLibrary}} 188 | 189 | io.swagger 190 | swagger-annotations 191 | ${swagger-annotations.version} 192 | 193 | {{/swagger1AnnotationLibrary}} 194 | {{#swagger2AnnotationLibrary}} 195 | 196 | io.swagger.core.v3 197 | swagger-annotations 198 | ${swagger-annotations.version} 199 | 200 | {{/swagger2AnnotationLibrary}} 201 | {{/springDocDocumentationProvider}} 202 | {{/springFoxDocumentationProvider}} 203 | 204 | 205 | com.google.code.findbugs 206 | jsr305 207 | 3.0.2 208 | 209 | 210 | com.fasterxml.jackson.dataformat 211 | jackson-dataformat-yaml 212 | 213 | {{#withXml}} 214 | 215 | 216 | jakarta.xml.bind 217 | jakarta.xml.bind-api 218 | 219 | 220 | com.fasterxml.jackson.dataformat 221 | jackson-dataformat-xml 222 | 223 | {{/withXml}} 224 | 225 | com.fasterxml.jackson.datatype 226 | jackson-datatype-jsr310 227 | 228 | {{#joda}} 229 | 230 | com.fasterxml.jackson.datatype 231 | jackson-datatype-joda 232 | 233 | {{/joda}} 234 | {{#openApiNullable}} 235 | 236 | org.openapitools 237 | jackson-databind-nullable 238 | 0.2.6 239 | 240 | {{/openApiNullable}} 241 | {{#useBeanValidation}} 242 | 243 | 244 | org.springframework.boot 245 | spring-boot-starter-validation 246 | 247 | {{/useBeanValidation}} 248 | {{#virtualService}} 249 | 250 | 251 | io.virtualan 252 | virtualan-plugin 253 | ${virtualan.version} 254 | 255 | 256 | 257 | org.hsqldb 258 | hsqldb 259 | 260 | 261 | {{/virtualService}} 262 | {{#hateoas}} 263 | 264 | 265 | org.springframework.boot 266 | spring-boot-starter-hateoas 267 | 268 | {{/hateoas}} 269 | {{#lombok}} 270 | 271 | org.projectlombok 272 | lombok 273 | true 274 | 275 | {{/lombok}} 276 | 277 | com.fasterxml.jackson.core 278 | jackson-databind 279 | 280 | 281 | org.springframework.boot 282 | spring-boot-starter-test 283 | test 284 | 285 | 286 | 287 | -------------------------------------------------------------------------------- /src/main/resources/templates/rabbit/clientImpl.mustache: -------------------------------------------------------------------------------- 1 | /** 2 | * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech) ({{{generatorVersion}}}). 3 | * https://openapi-generator.tech 4 | * Do not edit the class manually. 5 | */ 6 | package {{apiPackage}}.impl; 7 | 8 | import service.RabbitSenderService; 9 | import org.springframework.stereotype.Component; 10 | import {{apiPackage}}.{{baseName}}Producer; 11 | 12 | import java.util.Map; 13 | 14 | {{#imports}}import {{import}}; 15 | {{/imports}} 16 | 17 | @Component 18 | public class {{baseName}}ProducerImpl implements {{baseName}}Producer { 19 | 20 | private final RabbitSenderService rabbitSenderService; 21 | 22 | public {{baseName}}ProducerImpl(RabbitSenderService rabbitSenderService) { 23 | this.rabbitSenderService = rabbitSenderService; 24 | } 25 | 26 | {{#operations}} 27 | {{#operation}} 28 | {{#vendorExtensions}} 29 | @Override 30 | public void send{{modelName}}({{modelName}} {{modelNameCamel}}, Map params) { 31 | rabbitSenderService.send("{{queue}}", {{modelNameCamel}}); 32 | } 33 | {{/vendorExtensions}} 34 | {{/operation}} 35 | {{/operations}} 36 | } 37 | -------------------------------------------------------------------------------- /src/main/resources/templates/rabbit/listener.mustache: -------------------------------------------------------------------------------- 1 | /** 2 | * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech) ({{{generatorVersion}}}). 3 | * https://openapi-generator.tech 4 | * Do not edit the class manually. 5 | */ 6 | package {{apiPackage}}; 7 | 8 | import {{apiPackage}}.service.{{baseName}}Service; 9 | import org.springframework.beans.factory.annotation.Autowired; 10 | import pro.axenix_innovation.axenapi.annotation.KafkaHandlerDescription; 11 | import pro.axenix_innovation.axenapi.annotation.KafkaHandlerTags; 12 | import pro.axenix_innovation.axenapi.annotation.KafkaSecured; 13 | import org.springframework.amqp.rabbit.annotation.RabbitHandler; 14 | import org.springframework.amqp.rabbit.annotation.RabbitListener; 15 | import pro.axenix_innovation.axenapi.annotation.KafkaHandlerHeader; 16 | import pro.axenix_innovation.axenapi.annotation.KafkaHandlerHeaders; 17 | {{#imports}}import {{import}}; 18 | {{/imports}} 19 | import org.springframework.messaging.handler.annotation.Payload; 20 | import org.springframework.messaging.handler.annotation.Headers; 21 | import java.util.Map; 22 | 23 | {{#operations}} 24 | {{#operation.0.vendorExtensions}} 25 | @RabbitListener(queues = "{{queue}}") 26 | {{/operation.0.vendorExtensions}} 27 | {{/operations}} 28 | public final class {{baseName}}Listener { 29 | private final {{baseName}}Service service; 30 | 31 | @Autowired 32 | public {{baseName}}Listener({{baseName}}Service service) { 33 | this.service = service; 34 | } 35 | 36 | {{#operations}} 37 | {{#operation}} 38 | {{#vendorExtensions}} 39 | @RabbitHandler 40 | {{#notes}} 41 | @KafkaHandlerDescription("{{notes}}") 42 | {{/notes}} 43 | {{#tags}} 44 | @KafkaHandlerTags(tags = { {{{tags}}} }) 45 | {{/tags}} 46 | {{#hasOptionalParams}} 47 | @KafkaHandlerHeaders(headers = { 48 | {{#queryParams}} 49 | @KafkaHandlerHeader(header = "{{paramName}}"{{#required}}, required = true{{/required}}){{^-last}},{{/-last}} 50 | {{/queryParams}} 51 | }) 52 | {{/hasOptionalParams}} 53 | {{#securityAnnotation}} 54 | {{#hasAuthMethods}} 55 | @{{securityAnnotation}} 56 | {{/hasAuthMethods}} 57 | {{/securityAnnotation}} 58 | {{#resultWrapper}} 59 | {{resultWrapper}}{{#returnSimpleType}}<{{returnType}}>{{/returnSimpleType}}{{^returnSimpleType}}{{/returnSimpleType}} handle{{modelName}}(@Payload {{modelName}} {{modelNameCamel}}{{#hasParams}}, @Headers Map headers{{/hasParams}}) { 60 | {{/resultWrapper}} 61 | {{^resultWrapper}} 62 | {{#returnSimpleType}}{{returnType}}{{/returnSimpleType}}{{^returnSimpleType}}void{{/returnSimpleType}} handle{{modelName}}(@Payload {{modelName}} {{modelNameCamel}}{{#hasParams}}, @Headers Map headers{{/hasParams}}) { 63 | {{/resultWrapper}} 64 | {{#resultWrapper}}return{{/resultWrapper}}{{^resultWrapper}}{{#returnSimpleType}}return{{/returnSimpleType}}{{/resultWrapper}} service.handle{{modelName}}({{modelNameCamel}}{{#hasParams}}, headers{{/hasParams}}); 65 | } 66 | {{/vendorExtensions}} 67 | {{/operation}} 68 | {{/operations}} 69 | } 70 | -------------------------------------------------------------------------------- /src/main/resources/templates/rabbit/senderService.mustache: -------------------------------------------------------------------------------- 1 | /** 2 | * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech) ({{{generatorVersion}}}). 3 | * https://openapi-generator.tech 4 | * Do not edit the class manually. 5 | */ 6 | package service; 7 | 8 | public interface RabbitSenderService { 9 | void send(String queue, Object message); 10 | } 11 | -------------------------------------------------------------------------------- /src/main/resources/templates/rabbit/senderServiceConfig.mustache: -------------------------------------------------------------------------------- 1 | /** 2 | * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech) (6.3.0). 3 | * https://openapi-generator.tech 4 | * Do not edit the class manually. 5 | */ 6 | package config; 7 | 8 | import org.springframework.amqp.rabbit.connection.ConnectionFactory; 9 | import org.springframework.amqp.rabbit.core.RabbitTemplate; 10 | import org.springframework.amqp.support.converter.Jackson2JsonMessageConverter; 11 | import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; 12 | import org.springframework.context.annotation.Bean; 13 | import org.springframework.context.annotation.ComponentScan; 14 | import service.RabbitSenderService; 15 | import service.impl.RabbitSenderServiceImpl; 16 | 17 | @ComponentScan("axenapi") 18 | public class RabbitSenderServiceConfig { 19 | 20 | @Bean 21 | @ConditionalOnMissingBean 22 | public RabbitTemplate rabbitTemplate(ConnectionFactory connectionFactory) { 23 | RabbitTemplate template = new RabbitTemplate(connectionFactory); 24 | template.setMessageConverter(new Jackson2JsonMessageConverter()); 25 | return template; 26 | } 27 | 28 | @Bean 29 | @ConditionalOnMissingBean 30 | public RabbitSenderService rabbitSenderService(RabbitTemplate template) { 31 | return new RabbitSenderServiceImpl(template); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/main/resources/templates/rabbit/senderServiceImpl.mustache: -------------------------------------------------------------------------------- 1 | /** 2 | * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech) ({{{generatorVersion}}}). 3 | * https://openapi-generator.tech 4 | * Do not edit the class manually. 5 | */ 6 | package service.impl; 7 | 8 | import org.springframework.amqp.rabbit.core.RabbitTemplate; 9 | import service.RabbitSenderService; 10 | 11 | public class RabbitSenderServiceImpl implements RabbitSenderService { 12 | 13 | private final RabbitTemplate template; 14 | 15 | public RabbitSenderServiceImpl(RabbitTemplate template) { 16 | this.template = template; 17 | } 18 | 19 | @Override 20 | public void send(String queue, Object message) { 21 | template.convertAndSend(queue, message); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/main/resources/templates/rabbit/spring_2_autoconfig.mustache: -------------------------------------------------------------------------------- 1 | # Auto Configure 2 | org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ 3 | config.RabbitSenderServiceConfig -------------------------------------------------------------------------------- /src/main/resources/templates/rabbit/spring_3_autoconfig.mustache: -------------------------------------------------------------------------------- 1 | config.RabbitSenderServiceConfig -------------------------------------------------------------------------------- /src/main/resources/templates/settings_gradle.mustache: -------------------------------------------------------------------------------- 1 | rootProject.name = '{{artifactId}}' 2 | -------------------------------------------------------------------------------- /src/test/java/com/kafka/company/codegen/MessageBrokerCodegenGeneratorTest.java: -------------------------------------------------------------------------------- 1 | package com.kafka.company.codegen; 2 | 3 | import io.swagger.parser.OpenAPIParser; 4 | import io.swagger.v3.oas.models.OpenAPI; 5 | import io.swagger.v3.parser.core.models.ParseOptions; 6 | import org.apache.commons.io.FileUtils; 7 | import org.apache.commons.lang3.StringUtils; 8 | import org.apache.commons.text.CaseUtils; 9 | import org.junit.Assert; 10 | import org.junit.Test; 11 | import org.openapitools.codegen.ClientOptInput; 12 | import org.openapitools.codegen.CodegenConstants; 13 | import org.openapitools.codegen.DefaultGenerator; 14 | import org.openapitools.codegen.languages.features.CXFServerFeatures; 15 | import pro.axenix_innovation.axenapi.codegen.KafkaCodegenGenerator; 16 | import pro.axenix_innovation.axenapi.codegen.MessageBrokerCodegen; 17 | 18 | import java.io.File; 19 | import java.io.IOException; 20 | import java.nio.file.Files; 21 | import java.nio.file.Path; 22 | import java.nio.file.Paths; 23 | import java.util.Arrays; 24 | import java.util.HashSet; 25 | import java.util.List; 26 | import java.util.Set; 27 | import java.util.stream.Collectors; 28 | import java.util.stream.Stream; 29 | 30 | /*** 31 | * This test allows you to easily launch your code generation software under a debugger. 32 | * Then run this test under debug mode. You will be able to step through your java code 33 | * and then see the results in the out directory. 34 | * 35 | * To experiment with debugging your code generator: 36 | * 1) Set a break point in KafkaCodegenGenerator.java in the postProcessOperationsWithModels() method. 37 | * 2) To launch this test in Eclipse: right-click | Debug As | JUnit Test 38 | * 39 | */ 40 | public class MessageBrokerCodegenGeneratorTest { 41 | 42 | /** 43 | * Полный тест генерации с учетом некоторых параметров. 44 | * @throws IOException 45 | */ 46 | @Test 47 | public void testKafkaClientFullGeneration() throws IOException { 48 | boolean isKafkaClient = true; 49 | boolean isInterfaceOnly = false; 50 | boolean setTags = false; 51 | boolean sendBytes = false; 52 | boolean generateMessageId = false; 53 | boolean generateCorrelationId = false; 54 | boolean useSpringBoot3 = false; 55 | String messageIdName = "kafka_messageId"; 56 | String correlationIdName = "kafka_correlationId"; 57 | String modelPackage = "swagger4kafka.model"; 58 | String apiPackage = "swagger4kafka.client"; 59 | String outputDir = Paths.get("build", "generated", "kafka-client").toAbsolutePath().toString(); 60 | String pathToOpenApi = Paths.get("src","test", "java", "resources", "json", "emptySpec.json") 61 | .toFile() 62 | .getAbsolutePath(); 63 | 64 | OpenAPI openAPI = generateClient(pathToOpenApi, 65 | outputDir, 66 | apiPackage, 67 | modelPackage, 68 | setTags, 69 | isInterfaceOnly, 70 | isKafkaClient, 71 | sendBytes, 72 | messageIdName, 73 | correlationIdName, 74 | useSpringBoot3); 75 | 76 | // String kafkaClientPath = Paths.get("build", "generated", "kafka-client").toAbsolutePath().toString(); 77 | // 78 | // Assert.assertTrue(new File(kafkaClientPath).exists()); 79 | // Set fileNames = new HashSet<>(10); 80 | // 81 | // try (Stream stream = Files.walk(Paths.get(kafkaClientPath))) { 82 | // Set finalFileNames1 = fileNames; 83 | // stream.filter(Files::isRegularFile) 84 | // .forEach(file -> { 85 | // finalFileNames1.add(file.getFileName().toString());}); 86 | // } 87 | // 88 | // Set schemas = openAPI.getComponents().getSchemas().keySet().stream().collect(Collectors.toSet()); 89 | // 90 | // //check that all schemas was generated 91 | // for (String schema: schemas) { 92 | // Assert.assertTrue(fileNames.contains(schema.concat(".java"))); 93 | // } 94 | // 95 | // //convert PathItem To ClassName 96 | // Set classes = new HashSet<>(openAPI.getPaths().size()); 97 | // 98 | // for (String path: openAPI.getPaths().keySet()) { 99 | // classes.add(convertPathItemToClassName(path)); 100 | // } 101 | // 102 | // Set lowerCaseFileNames = fileNames.stream().map(String::toLowerCase).collect(Collectors.toSet()); 103 | // 104 | // //check class (interface and impl) exists 105 | // for (String className: classes) { 106 | // Assert.assertTrue(lowerCaseFileNames.contains(className.concat("Producer.java").toLowerCase())); 107 | // Assert.assertTrue(lowerCaseFileNames.contains(className.concat("ProducerImpl.java").toLowerCase())); 108 | // } 109 | // 110 | // String pathToKafkaSenderServiceImpl = Paths.get("build", 111 | // "generated", 112 | // "kafka-client", 113 | // "src", 114 | // "main", 115 | // "java", 116 | // "service", 117 | // "impl", 118 | // "KafkaSenderServiceImpl.java").toAbsolutePath().toString(); 119 | // 120 | // File file = new File(pathToKafkaSenderServiceImpl); 121 | // String data = FileUtils.readFileToString(file, "UTF-8"); 122 | // Assert.assertTrue(data.contains("sendBytes = " + sendBytes)); 123 | // Assert.assertTrue(data.contains("generateMessageId = " + generateMessageId)); 124 | // Assert.assertTrue(data.contains("generateCorrelationId = " + generateCorrelationId)); 125 | // Assert.assertTrue(data.contains("messageIdName = \"" + messageIdName + "\"")); 126 | // Assert.assertTrue(data.contains("correlationIdName = \"" + correlationIdName + "\"")); 127 | // 128 | // messageIdName = "kafka_messageId1"; 129 | // correlationIdName = "kafka_correlationId1"; 130 | // sendBytes = true; 131 | // 132 | // generateClient(pathToOpenApi, 133 | // outputDir, 134 | // apiPackage, 135 | // modelPackage, 136 | // setTags, 137 | // isInterfaceOnly, 138 | // isKafkaClient, 139 | // sendBytes, 140 | // messageIdName, 141 | // correlationIdName, 142 | // useSpringBoot3); 143 | // 144 | // data = FileUtils.readFileToString(file, "UTF-8"); 145 | // Assert.assertTrue(data.contains("messageIdName = \"" + messageIdName + "\"")); 146 | // Assert.assertTrue(data.contains("correlationIdName = \"" + correlationIdName + "\"")); 147 | // Assert.assertTrue(data.contains("sendBytes = " + sendBytes)); 148 | // Assert.assertTrue(data.contains("generateMessageId = " + generateMessageId)); 149 | // Assert.assertTrue(data.contains("generateCorrelationId = " + generateCorrelationId)); 150 | // 151 | // isKafkaClient = false; 152 | // 153 | // generateClient(pathToOpenApi, 154 | // outputDir, 155 | // apiPackage, 156 | // modelPackage, 157 | // setTags, 158 | // isInterfaceOnly, 159 | // isKafkaClient, 160 | // sendBytes, 161 | // messageIdName, 162 | // correlationIdName, 163 | // useSpringBoot3); 164 | // 165 | // Assert.assertFalse(new File(pathToKafkaSenderServiceImpl).exists()); 166 | // 167 | // isKafkaClient = true; 168 | // 169 | // isInterfaceOnly = true; 170 | // 171 | // generateClient(pathToOpenApi, 172 | // outputDir, 173 | // apiPackage, 174 | // modelPackage, 175 | // setTags, 176 | // isInterfaceOnly, 177 | // isKafkaClient, 178 | // sendBytes, 179 | // messageIdName, 180 | // correlationIdName, 181 | // useSpringBoot3); 182 | // 183 | // fileNames = new HashSet<>(10); 184 | // 185 | // try (Stream stream = Files.walk(Paths.get(kafkaClientPath))) { 186 | // Set finalFileNames = fileNames; 187 | // stream.filter(Files::isRegularFile) 188 | // .forEach(fileName -> { 189 | // finalFileNames.add(fileName.getFileName().toString());}); 190 | // } 191 | // 192 | // lowerCaseFileNames = fileNames.stream().map(String::toLowerCase).collect(Collectors.toSet()); 193 | // 194 | // //check class (interface) exists without impl 195 | // for (String className: classes) { 196 | // Assert.assertTrue(lowerCaseFileNames.contains(className.concat("Producer.java").toLowerCase())); 197 | // Assert.assertFalse(lowerCaseFileNames.contains(className.concat("ProducerImpl.java").toLowerCase())); 198 | // } 199 | // 200 | // isKafkaClient = true; 201 | // 202 | // isInterfaceOnly = false; 203 | // 204 | // generateClient(pathToOpenApi, 205 | // outputDir, 206 | // apiPackage, 207 | // modelPackage, 208 | // setTags, 209 | // isInterfaceOnly, 210 | // isKafkaClient, 211 | // sendBytes, 212 | // messageIdName, 213 | // correlationIdName, 214 | // useSpringBoot3); 215 | // 216 | // String pathToChiefModelFile = Paths.get("build", 217 | // "generated", 218 | // "kafka-client", 219 | // "src", 220 | // "main", 221 | // "java", 222 | // "swagger4kafka", 223 | // "model", 224 | // "Chief.java").toAbsolutePath().toString(); 225 | // 226 | // 227 | // String chief = FileUtils.readFileToString(new File(pathToChiefModelFile), "UTF-8"); 228 | // 229 | // Assert.assertTrue(chief.contains("import javax.validation.Valid")); 230 | // Assert.assertFalse(chief.contains("jakarta.validation.Valid")); 231 | // 232 | // useSpringBoot3 = true; 233 | // 234 | // generateClient(pathToOpenApi, 235 | // outputDir, 236 | // apiPackage, 237 | // modelPackage, 238 | // setTags, 239 | // isInterfaceOnly, 240 | // isKafkaClient, 241 | // sendBytes, 242 | // messageIdName, 243 | // correlationIdName, 244 | // useSpringBoot3); 245 | // 246 | // chief = FileUtils.readFileToString(new File(pathToChiefModelFile), "UTF-8"); 247 | // 248 | // Assert.assertFalse(chief.contains("import javax.validation.Valid")); 249 | // Assert.assertTrue(chief.contains("import jakarta.validation.Valid")); 250 | 251 | } 252 | 253 | private OpenAPI generateClient(String pathToOpenApi, 254 | String outputDir, 255 | String apiPackage, 256 | String modelPackage, 257 | boolean setTags, 258 | boolean isInterfaceOnly, 259 | boolean isKafkaClient, 260 | boolean sendBytes, 261 | String messageIdName, 262 | String correlationIdName, 263 | boolean useSpringBoot3) throws IOException { 264 | OpenAPI openAPI = new OpenAPIParser() 265 | .readLocation(pathToOpenApi, null, new ParseOptions()).getOpenAPI(); 266 | 267 | FileUtils.deleteDirectory(new File(outputDir)); 268 | 269 | MessageBrokerCodegen messageBrokerCodegen = new MessageBrokerCodegen(); 270 | messageBrokerCodegen.additionalProperties().put(CXFServerFeatures.LOAD_TEST_DATA_FROM_FILE, "true"); 271 | messageBrokerCodegen.additionalProperties().put("enablePostProcessFile", "true"); 272 | messageBrokerCodegen.setUseOneOfInterfaces(false); 273 | messageBrokerCodegen.setLegacyDiscriminatorBehavior(false); 274 | messageBrokerCodegen.setUseTags(setTags); 275 | messageBrokerCodegen.setInterfaceOnly(isInterfaceOnly); 276 | messageBrokerCodegen.setModelPackage(modelPackage); 277 | messageBrokerCodegen.setApiPackage(apiPackage); 278 | // messageBrokerCodegen.setSourceFolder(Paths.get("build", "kafka-client", "src", "main", "java").toAbsolutePath().toString()); 279 | messageBrokerCodegen.setOutputDir(outputDir); 280 | // messageBrokerCodegen.setKafkaClient(isKafkaClient); 281 | // messageBrokerCodegen.setSendBytes(sendBytes); 282 | // messageBrokerCodegen.setMessageIdName(messageIdName); 283 | // messageBrokerCodegen.setCorrelationIdName(correlationIdName); 284 | messageBrokerCodegen.setUseSpringBoot3(useSpringBoot3); 285 | 286 | ClientOptInput input = new ClientOptInput(); 287 | input.openAPI(openAPI); 288 | 289 | input.setConfig(messageBrokerCodegen); 290 | messageBrokerCodegen.processOpts(); 291 | 292 | DefaultGenerator generator = new DefaultGenerator(); 293 | // codegen.setHateoas(true); 294 | generator.setGeneratorPropertyDefault(CodegenConstants.MODELS, "true"); 295 | //generator.setGeneratorPropertyDefault(CodegenConstants.USE_ONEOF_DISCRIMINATOR_LOOKUP, "true"); 296 | generator.setGeneratorPropertyDefault(CodegenConstants.LEGACY_DISCRIMINATOR_BEHAVIOR, "false"); 297 | generator.setGeneratorPropertyDefault(CodegenConstants.MODEL_TESTS, "false"); 298 | generator.setGeneratorPropertyDefault(CodegenConstants.MODEL_DOCS, "false"); 299 | generator.setGeneratorPropertyDefault(CodegenConstants.APIS, "true"); 300 | generator.setGeneratorPropertyDefault(CodegenConstants.SUPPORTING_FILES, "true"); 301 | generator.setGenerateMetadata(false); 302 | generator.opts(input).generate(); 303 | 304 | return openAPI; 305 | } 306 | 307 | private String convertPathItemToClassName(String path) { 308 | List values = Arrays.stream(StringUtils.split(path, "/")).collect(Collectors.toList()); 309 | 310 | String className = ""; 311 | for (int i = values.size() - 2; i > 0; i--) { 312 | className = className.concat(values.get(i)).concat(" "); 313 | } 314 | className = className.replaceAll("-"," ") 315 | .replaceAll("_"," "); 316 | 317 | className = CaseUtils.toCamelCase(className, true, ' '); 318 | return className; 319 | } 320 | } -------------------------------------------------------------------------------- /src/test/java/com/kafka/company/codegen/MessageBrokerCodegenGeneratorTestGradle.java: -------------------------------------------------------------------------------- 1 | package com.kafka.company.codegen; 2 | 3 | import io.swagger.parser.OpenAPIParser; 4 | import io.swagger.v3.oas.models.OpenAPI; 5 | import io.swagger.v3.parser.core.models.ParseOptions; 6 | import org.apache.commons.io.FileUtils; 7 | import org.apache.commons.lang3.StringUtils; 8 | import org.apache.commons.text.CaseUtils; 9 | import org.junit.Test; 10 | import org.openapitools.codegen.ClientOptInput; 11 | import org.openapitools.codegen.CodegenConstants; 12 | import org.openapitools.codegen.DefaultGenerator; 13 | import org.openapitools.codegen.languages.features.CXFServerFeatures; 14 | import pro.axenix_innovation.axenapi.codegen.MessageBrokerCodegen; 15 | 16 | import java.io.File; 17 | import java.io.IOException; 18 | import java.nio.file.Paths; 19 | import java.util.Arrays; 20 | import java.util.List; 21 | import java.util.stream.Collectors; 22 | 23 | /*** 24 | * This test allows you to easily launch your code generation software under a debugger. 25 | * Then run this test under debug mode. You will be able to step through your java code 26 | * and then see the results in the out directory. 27 | * 28 | * To experiment with debugging your code generator: 29 | * 1) Set a break point in KafkaCodegenGenerator.java in the postProcessOperationsWithModels() method. 30 | * 2) To launch this test in Eclipse: right-click | Debug As | JUnit Test 31 | * 32 | */ 33 | public class MessageBrokerCodegenGeneratorTestGradle { 34 | 35 | /** 36 | * Полный тест генерации с учетом некоторых параметров. 37 | * @throws IOException 38 | */ 39 | @Test 40 | public void testKafkaClientFullGeneration() throws IOException { 41 | boolean isKafkaClient = true; 42 | boolean isInterfaceOnly = false; 43 | boolean setTags = false; 44 | boolean sendBytes = false; 45 | boolean generateMessageId = false; 46 | boolean generateCorrelationId = false; 47 | boolean useSpringBoot3 = false; 48 | boolean useGradle = true; 49 | String messageIdName = "kafka_messageId"; 50 | String correlationIdName = "kafka_correlationId"; 51 | String modelPackage = "swagger4kafka.model"; 52 | String apiPackage = "swagger4kafka.client"; 53 | String outputDir = Paths.get("build", "generated", "kafka-client").toAbsolutePath().toString(); 54 | String pathToOpenApi = Paths.get("src","test", "java", "resources", "json", "emptySpec.json") 55 | .toFile() 56 | .getAbsolutePath(); 57 | 58 | OpenAPI openAPI = generateClient(pathToOpenApi, 59 | outputDir, 60 | apiPackage, 61 | modelPackage, 62 | setTags, 63 | isInterfaceOnly, 64 | isKafkaClient, 65 | sendBytes, 66 | messageIdName, 67 | correlationIdName, 68 | useGradle, 69 | useSpringBoot3); 70 | 71 | // String kafkaClientPath = Paths.get("build", "generated", "kafka-client").toAbsolutePath().toString(); 72 | // 73 | // Assert.assertTrue(new File(kafkaClientPath).exists()); 74 | // Set fileNames = new HashSet<>(10); 75 | // 76 | // try (Stream stream = Files.walk(Paths.get(kafkaClientPath))) { 77 | // Set finalFileNames1 = fileNames; 78 | // stream.filter(Files::isRegularFile) 79 | // .forEach(file -> { 80 | // finalFileNames1.add(file.getFileName().toString());}); 81 | // } 82 | // 83 | // Set schemas = openAPI.getComponents().getSchemas().keySet().stream().collect(Collectors.toSet()); 84 | // 85 | // //check that all schemas was generated 86 | // for (String schema: schemas) { 87 | // Assert.assertTrue(fileNames.contains(schema.concat(".java"))); 88 | // } 89 | // 90 | // //convert PathItem To ClassName 91 | // Set classes = new HashSet<>(openAPI.getPaths().size()); 92 | // 93 | // for (String path: openAPI.getPaths().keySet()) { 94 | // classes.add(convertPathItemToClassName(path)); 95 | // } 96 | // 97 | // Set lowerCaseFileNames = fileNames.stream().map(String::toLowerCase).collect(Collectors.toSet()); 98 | // 99 | // //check class (interface and impl) exists 100 | // for (String className: classes) { 101 | // Assert.assertTrue(lowerCaseFileNames.contains(className.concat("Producer.java").toLowerCase())); 102 | // Assert.assertTrue(lowerCaseFileNames.contains(className.concat("ProducerImpl.java").toLowerCase())); 103 | // } 104 | // 105 | // String pathToKafkaSenderServiceImpl = Paths.get("build", 106 | // "generated", 107 | // "kafka-client", 108 | // "src", 109 | // "main", 110 | // "java", 111 | // "service", 112 | // "impl", 113 | // "KafkaSenderServiceImpl.java").toAbsolutePath().toString(); 114 | // 115 | // File file = new File(pathToKafkaSenderServiceImpl); 116 | // String data = FileUtils.readFileToString(file, "UTF-8"); 117 | // Assert.assertTrue(data.contains("sendBytes = " + sendBytes)); 118 | // Assert.assertTrue(data.contains("generateMessageId = " + generateMessageId)); 119 | // Assert.assertTrue(data.contains("generateCorrelationId = " + generateCorrelationId)); 120 | // Assert.assertTrue(data.contains("messageIdName = \"" + messageIdName + "\"")); 121 | // Assert.assertTrue(data.contains("correlationIdName = \"" + correlationIdName + "\"")); 122 | // 123 | // messageIdName = "kafka_messageId1"; 124 | // correlationIdName = "kafka_correlationId1"; 125 | // sendBytes = true; 126 | // 127 | // generateClient(pathToOpenApi, 128 | // outputDir, 129 | // apiPackage, 130 | // modelPackage, 131 | // setTags, 132 | // isInterfaceOnly, 133 | // isKafkaClient, 134 | // sendBytes, 135 | // messageIdName, 136 | // correlationIdName, 137 | // useSpringBoot3); 138 | // 139 | // data = FileUtils.readFileToString(file, "UTF-8"); 140 | // Assert.assertTrue(data.contains("messageIdName = \"" + messageIdName + "\"")); 141 | // Assert.assertTrue(data.contains("correlationIdName = \"" + correlationIdName + "\"")); 142 | // Assert.assertTrue(data.contains("sendBytes = " + sendBytes)); 143 | // Assert.assertTrue(data.contains("generateMessageId = " + generateMessageId)); 144 | // Assert.assertTrue(data.contains("generateCorrelationId = " + generateCorrelationId)); 145 | // 146 | // isKafkaClient = false; 147 | // 148 | // generateClient(pathToOpenApi, 149 | // outputDir, 150 | // apiPackage, 151 | // modelPackage, 152 | // setTags, 153 | // isInterfaceOnly, 154 | // isKafkaClient, 155 | // sendBytes, 156 | // messageIdName, 157 | // correlationIdName, 158 | // useSpringBoot3); 159 | // 160 | // Assert.assertFalse(new File(pathToKafkaSenderServiceImpl).exists()); 161 | // 162 | // isKafkaClient = true; 163 | // 164 | // isInterfaceOnly = true; 165 | // 166 | // generateClient(pathToOpenApi, 167 | // outputDir, 168 | // apiPackage, 169 | // modelPackage, 170 | // setTags, 171 | // isInterfaceOnly, 172 | // isKafkaClient, 173 | // sendBytes, 174 | // messageIdName, 175 | // correlationIdName, 176 | // useSpringBoot3); 177 | // 178 | // fileNames = new HashSet<>(10); 179 | // 180 | // try (Stream stream = Files.walk(Paths.get(kafkaClientPath))) { 181 | // Set finalFileNames = fileNames; 182 | // stream.filter(Files::isRegularFile) 183 | // .forEach(fileName -> { 184 | // finalFileNames.add(fileName.getFileName().toString());}); 185 | // } 186 | // 187 | // lowerCaseFileNames = fileNames.stream().map(String::toLowerCase).collect(Collectors.toSet()); 188 | // 189 | // //check class (interface) exists without impl 190 | // for (String className: classes) { 191 | // Assert.assertTrue(lowerCaseFileNames.contains(className.concat("Producer.java").toLowerCase())); 192 | // Assert.assertFalse(lowerCaseFileNames.contains(className.concat("ProducerImpl.java").toLowerCase())); 193 | // } 194 | // 195 | // isKafkaClient = true; 196 | // 197 | // isInterfaceOnly = false; 198 | // 199 | // generateClient(pathToOpenApi, 200 | // outputDir, 201 | // apiPackage, 202 | // modelPackage, 203 | // setTags, 204 | // isInterfaceOnly, 205 | // isKafkaClient, 206 | // sendBytes, 207 | // messageIdName, 208 | // correlationIdName, 209 | // useSpringBoot3); 210 | // 211 | // String pathToChiefModelFile = Paths.get("build", 212 | // "generated", 213 | // "kafka-client", 214 | // "src", 215 | // "main", 216 | // "java", 217 | // "swagger4kafka", 218 | // "model", 219 | // "Chief.java").toAbsolutePath().toString(); 220 | // 221 | // 222 | // String chief = FileUtils.readFileToString(new File(pathToChiefModelFile), "UTF-8"); 223 | // 224 | // Assert.assertTrue(chief.contains("import javax.validation.Valid")); 225 | // Assert.assertFalse(chief.contains("jakarta.validation.Valid")); 226 | // 227 | // useSpringBoot3 = true; 228 | // 229 | // generateClient(pathToOpenApi, 230 | // outputDir, 231 | // apiPackage, 232 | // modelPackage, 233 | // setTags, 234 | // isInterfaceOnly, 235 | // isKafkaClient, 236 | // sendBytes, 237 | // messageIdName, 238 | // correlationIdName, 239 | // useSpringBoot3); 240 | // 241 | // chief = FileUtils.readFileToString(new File(pathToChiefModelFile), "UTF-8"); 242 | // 243 | // Assert.assertFalse(chief.contains("import javax.validation.Valid")); 244 | // Assert.assertTrue(chief.contains("import jakarta.validation.Valid")); 245 | 246 | } 247 | 248 | private OpenAPI generateClient(String pathToOpenApi, 249 | String outputDir, 250 | String apiPackage, 251 | String modelPackage, 252 | boolean setTags, 253 | boolean isInterfaceOnly, 254 | boolean isKafkaClient, 255 | boolean sendBytes, 256 | String messageIdName, 257 | String correlationIdName, boolean useGradle, 258 | boolean useSpringBoot3) throws IOException { 259 | OpenAPI openAPI = new OpenAPIParser() 260 | .readLocation(pathToOpenApi, null, new ParseOptions()).getOpenAPI(); 261 | 262 | FileUtils.deleteDirectory(new File(outputDir)); 263 | 264 | MessageBrokerCodegen messageBrokerCodegen = new MessageBrokerCodegen(); 265 | messageBrokerCodegen.additionalProperties().put(CXFServerFeatures.LOAD_TEST_DATA_FROM_FILE, "true"); 266 | messageBrokerCodegen.additionalProperties().put("enablePostProcessFile", "true"); 267 | messageBrokerCodegen.setUseOneOfInterfaces(false); 268 | messageBrokerCodegen.setLegacyDiscriminatorBehavior(false); 269 | messageBrokerCodegen.setUseTags(setTags); 270 | messageBrokerCodegen.setInterfaceOnly(isInterfaceOnly); 271 | messageBrokerCodegen.setModelPackage(modelPackage); 272 | messageBrokerCodegen.setApiPackage(apiPackage); 273 | // messageBrokerCodegen.setSourceFolder(Paths.get("build", "kafka-client", "src", "main", "java").toAbsolutePath().toString()); 274 | messageBrokerCodegen.setOutputDir(outputDir); 275 | // messageBrokerCodegen.setKafkaClient(isKafkaClient); 276 | // messageBrokerCodegen.setSendBytes(sendBytes); 277 | // messageBrokerCodegen.setMessageIdName(messageIdName); 278 | // messageBrokerCodegen.setCorrelationIdName(correlationIdName); 279 | messageBrokerCodegen.setUseSpringBoot3(useSpringBoot3); 280 | messageBrokerCodegen.setUseGradle(useGradle); 281 | 282 | ClientOptInput input = new ClientOptInput(); 283 | input.openAPI(openAPI); 284 | 285 | input.setConfig(messageBrokerCodegen); 286 | messageBrokerCodegen.processOpts(); 287 | 288 | DefaultGenerator generator = new DefaultGenerator(); 289 | // codegen.setHateoas(true); 290 | generator.setGeneratorPropertyDefault(CodegenConstants.MODELS, "true"); 291 | //generator.setGeneratorPropertyDefault(CodegenConstants.USE_ONEOF_DISCRIMINATOR_LOOKUP, "true"); 292 | generator.setGeneratorPropertyDefault(CodegenConstants.LEGACY_DISCRIMINATOR_BEHAVIOR, "false"); 293 | generator.setGeneratorPropertyDefault(CodegenConstants.MODEL_TESTS, "false"); 294 | generator.setGeneratorPropertyDefault(CodegenConstants.MODEL_DOCS, "false"); 295 | generator.setGeneratorPropertyDefault(CodegenConstants.APIS, "true"); 296 | generator.setGeneratorPropertyDefault(CodegenConstants.SUPPORTING_FILES, "true"); 297 | generator.setGenerateMetadata(false); 298 | generator.opts(input).generate(); 299 | 300 | return openAPI; 301 | } 302 | 303 | private String convertPathItemToClassName(String path) { 304 | List values = Arrays.stream(StringUtils.split(path, "/")).collect(Collectors.toList()); 305 | 306 | String className = ""; 307 | for (int i = values.size() - 2; i > 0; i--) { 308 | className = className.concat(values.get(i)).concat(" "); 309 | } 310 | className = className.replaceAll("-"," ") 311 | .replaceAll("_"," "); 312 | 313 | className = CaseUtils.toCamelCase(className, true, ' '); 314 | return className; 315 | } 316 | } -------------------------------------------------------------------------------- /src/test/java/resources/json/emptySpec.json: -------------------------------------------------------------------------------- 1 | { 2 | "openapi" : "3.0.1", 3 | "info" : { 4 | "title" : "New_Service_1", 5 | "description" : "axenapi Specification for New_Service_1", 6 | "version" : "1.0.0" 7 | }, 8 | "paths" : { } 9 | } -------------------------------------------------------------------------------- /src/test/java/resources/json/test-jms.json: -------------------------------------------------------------------------------- 1 | { 2 | "openapi": "3.0.1", 3 | "info": { 4 | "title": "App API", 5 | "version": "snapshot" 6 | }, 7 | "servers": [ 8 | { 9 | "url": "http://axenapi.demo", 10 | "description": "Generated server url" 11 | } 12 | ], 13 | "paths": { 14 | "/jms/example_queue/ExampleIn": { 15 | "post": { 16 | "description": "example handler", 17 | "operationId": "ExampleHandler", 18 | "tags": [ 19 | "example" 20 | ], 21 | "security": [ 22 | { 23 | "Internal-Token": [] 24 | } 25 | ], 26 | "requestBody": { 27 | "description": "Example in", 28 | "content": { 29 | "*/*": { 30 | "schema": { 31 | "$ref": "#/components/schemas/ExampleIn" 32 | } 33 | } 34 | } 35 | }, 36 | "responses": { 37 | "200": { 38 | "description": "Example out", 39 | "content": { 40 | "*/*": { 41 | "schema": { 42 | "$ref": "#/components/schemas/ExampleOut" 43 | } 44 | } 45 | } 46 | } 47 | } 48 | } 49 | }, 50 | "/jms/example_queue/ExampleOut": { 51 | "post": { 52 | "description": "Queue: example_queue, Payload: ExampleOut", 53 | "operationId": "example_queueExampleOutHandler", 54 | "tags": [ 55 | "example" 56 | ], 57 | "security": [ 58 | { 59 | "Internal-Token": [] 60 | } 61 | ], 62 | "requestBody": { 63 | "description": "Example out", 64 | "content": { 65 | "*/*": { 66 | "schema": { 67 | "$ref": "#/components/schemas/ExampleOut" 68 | } 69 | } 70 | } 71 | } 72 | } 73 | }, 74 | "/jms/new_queue/ExampleOut": { 75 | "post": { 76 | "description": "New queue handler", 77 | "operationId": "NewQueueHandler", 78 | "tags": [ 79 | "newQueue" 80 | ], 81 | "security": [ 82 | { 83 | "Internal-Token": [] 84 | } 85 | ], 86 | "requestBody": { 87 | "description": "Example in", 88 | "content": { 89 | "*/*": { 90 | "schema": { 91 | "$ref": "#/components/schemas/ExampleOut" 92 | } 93 | } 94 | } 95 | }, 96 | "responses": { 97 | "200": {} 98 | } 99 | } 100 | } 101 | }, 102 | "components": { 103 | "schemas": { 104 | "ExampleIn": { 105 | "type": "object", 106 | "properties": { 107 | "message": { 108 | "type": "string", 109 | "description": "example field" 110 | } 111 | }, 112 | "description": "example message" 113 | }, 114 | "ExampleOut": { 115 | "type": "object", 116 | "properties": { 117 | "message": { 118 | "type": "string", 119 | "description": "example field" 120 | } 121 | }, 122 | "description": "example message" 123 | }, 124 | "OutgPojo": { 125 | "type": "object", 126 | "properties": { 127 | "subject": { 128 | "type": "string", 129 | "description": "subj" 130 | } 131 | }, 132 | "description": "subject descr", 133 | "x-outgoing": { 134 | "topics": [ 135 | "Outg topic" 136 | ], 137 | "type": "Message", 138 | "tags": [ 139 | "abcde", 140 | "outg" 141 | ] 142 | } 143 | } 144 | }, 145 | "securitySchemes": { 146 | "Internal-Token": { 147 | "type": "apiKey", 148 | "name": "SERVICE_ACCESS_TOKEN", 149 | "in": "header" 150 | } 151 | } 152 | } 153 | } -------------------------------------------------------------------------------- /src/test/java/resources/json/test-kafka.json: -------------------------------------------------------------------------------- 1 | { 2 | "openapi": "3.0.1", 3 | "info": { 4 | "title": "App API", 5 | "version": "snapshot" 6 | }, 7 | "servers": [ 8 | { 9 | "url": "http://example.example", 10 | "description": "Generated server url" 11 | } 12 | ], 13 | "paths": { 14 | "/kafka/my_annotation/my_annotation/ExampleMessage": { 15 | "post": { 16 | "tags": [ 17 | "my-kafka-handler-annotaion-controller" 18 | ], 19 | "operationId": "executeExampleMessage", 20 | "requestBody": { 21 | "content": { 22 | "application/json": { 23 | "schema": { 24 | "$ref": "#/components/schemas/ExampleMessage" 25 | } 26 | } 27 | }, 28 | "required": true 29 | }, 30 | "responses": { 31 | "200": { 32 | "description": "???????????? ???????? ???????????" 33 | } 34 | } 35 | } 36 | }, 37 | "/kafka/group-2/multiType/Subordinate": { 38 | "post": { 39 | "tags": [ 40 | "group-2", 41 | "subordinate" 42 | ], 43 | "description": "Message listener with Subordinate payload type", 44 | "operationId": "executeSubordinate", 45 | "parameters": [ 46 | { 47 | "name": "req", 48 | "in": "query", 49 | "schema": { 50 | "type": "string" 51 | } 52 | } 53 | ], 54 | "requestBody": { 55 | "content": { 56 | "application/json": { 57 | "schema": { 58 | "$ref": "#/components/schemas/Subordinate" 59 | } 60 | } 61 | }, 62 | "required": true 63 | }, 64 | "responses": { 65 | "200": { 66 | "description": "?????????? ????? Subordinate ? ?????, ???????????? ????? ????? replyTopic. ???????????? ???????? ?? ???????????????", 67 | "content": { 68 | "*/*": { 69 | "schema": { 70 | "$ref": "#/components/schemas/Subordinate" 71 | } 72 | } 73 | } 74 | } 75 | } 76 | } 77 | }, 78 | "/kafka/group-2/multiType/Object": { 79 | "post": { 80 | "tags": [ 81 | "group-2", 82 | "default handler" 83 | ], 84 | "description": "Message listener with payload type other then Chief or Subordinate", 85 | "operationId": "executeObject", 86 | "parameters": [ 87 | { 88 | "name": "req1", 89 | "in": "query", 90 | "required": true, 91 | "schema": { 92 | "type": "string" 93 | } 94 | 95 | }, 96 | { 97 | "name": "req2", 98 | "in": "query", 99 | "schema": { 100 | "type": "string" 101 | } 102 | 103 | } 104 | ], 105 | "requestBody": { 106 | "content": { 107 | "application/json": { 108 | "schema": { 109 | "type": "object" 110 | } 111 | } 112 | }, 113 | "required": true 114 | }, 115 | "responses": { 116 | "200": { 117 | "description": "???????????? ???????? ???????????" 118 | } 119 | } 120 | } 121 | }, 122 | "/kafka/group-2/multiType/Chief": { 123 | "post": { 124 | "tags": [ 125 | "group-2", 126 | "chief" 127 | ], 128 | "description": "Message listener with Chief payload type", 129 | "operationId": "executeChief", 130 | "requestBody": { 131 | "content": { 132 | "application/json": { 133 | "schema": { 134 | "$ref": "#/components/schemas/Chief" 135 | } 136 | } 137 | }, 138 | "required": true 139 | }, 140 | "responses": { 141 | "200": { 142 | "description": "???????????? ???????? ???????????" 143 | } 144 | } 145 | } 146 | }, 147 | "/kafka/group-1/multiType/Subordinate": { 148 | "post": { 149 | "tags": [ 150 | "subordinate" 151 | ], 152 | "description": "Message listener with Subordinate payload type", 153 | "operationId": "executeSubordinate_1", 154 | "requestBody": { 155 | "content": { 156 | "application/json": { 157 | "schema": { 158 | "$ref": "#/components/schemas/Subordinate" 159 | } 160 | } 161 | }, 162 | "required": true 163 | }, 164 | "responses": { 165 | "200": { 166 | "description": "???????????? ???????? ???????????" 167 | } 168 | } 169 | } 170 | }, 171 | "/kafka/group-1/multiType/Object": { 172 | "post": { 173 | "tags": [ 174 | "default handler" 175 | ], 176 | "description": "Message listener with payload type other then Chief or Subordinate", 177 | "operationId": "executeObject_1", 178 | "requestBody": { 179 | "content": { 180 | "application/json": { 181 | "schema": { 182 | "type": "object" 183 | } 184 | } 185 | }, 186 | "required": true 187 | }, 188 | "responses": { 189 | "200": { 190 | "description": "???????????? ???????? ???????????" 191 | } 192 | } 193 | } 194 | }, 195 | "/kafka/group-1/multiType/Chief": { 196 | "post": { 197 | "tags": [ 198 | "chief" 199 | ], 200 | "description": "Message listener with Chief payload type", 201 | "operationId": "executeChief_1", 202 | "requestBody": { 203 | "content": { 204 | "application/json": { 205 | "schema": { 206 | "$ref": "#/components/schemas/Chief" 207 | } 208 | } 209 | }, 210 | "required": true 211 | }, 212 | "responses": { 213 | "200": { 214 | "description": "???????????? ???????? ???????????" 215 | } 216 | }, 217 | "security": [ 218 | { 219 | "Internal-Token": [] 220 | } 221 | ] 222 | } 223 | }, 224 | "/kafka/foo/my_topic/Object": { 225 | "post": { 226 | "tags": [ 227 | "example-listener-controller" 228 | ], 229 | "operationId": "executeObject_2", 230 | "requestBody": { 231 | "content": { 232 | "application/json": { 233 | "schema": { 234 | "type": "object" 235 | } 236 | } 237 | }, 238 | "required": true 239 | }, 240 | "responses": { 241 | "200": { 242 | "description": "???????????? ???????? ???????????" 243 | } 244 | } 245 | } 246 | }, 247 | "/kafka/foo/my_topic/ExampleMessage": { 248 | "post": { 249 | "tags": [ 250 | "example-listener-controller" 251 | ], 252 | "operationId": "executeExampleMessage_1", 253 | "requestBody": { 254 | "content": { 255 | "application/json": { 256 | "schema": { 257 | "$ref": "#/components/schemas/ExampleMessage" 258 | } 259 | } 260 | }, 261 | "required": true 262 | }, 263 | "responses": { 264 | "200": { 265 | "description": "???????????? ???????? ???????????" 266 | } 267 | } 268 | } 269 | }, 270 | "/kafka/foo/my_compitable_future/Chief": { 271 | "post": { 272 | "tags": [ 273 | "competable-future-kafka-listener-controller" 274 | ], 275 | "operationId": "executeChief_2", 276 | "requestBody": { 277 | "content": { 278 | "application/json": { 279 | "schema": { 280 | "$ref": "#/components/schemas/Chief" 281 | } 282 | } 283 | }, 284 | "required": true 285 | }, 286 | "responses": { 287 | "200": { 288 | "description": "?????????? ????? ExampleMessage ? ?????, ???????????? ????? ????? replyTopic. ???????????? ???????? ?? ???????????????", 289 | "content": { 290 | "*/*": { 291 | "schema": { 292 | "$ref": "#/components/schemas/ExampleMessage" 293 | } 294 | } 295 | } 296 | } 297 | } 298 | } 299 | } 300 | }, 301 | "components": { 302 | "schemas": { 303 | "ExampleMessage": { 304 | "type": "object", 305 | "properties": { 306 | "message": { 307 | "type": "string", 308 | "description": "example field" 309 | } 310 | }, 311 | "description": "example message" 312 | }, 313 | "Subordinate": { 314 | "required": [ 315 | "chiefName", 316 | "name" 317 | ], 318 | "type": "object", 319 | "properties": { 320 | "name": { 321 | "maxLength": 20, 322 | "minLength": 3, 323 | "type": "string", 324 | "description": "Subordinate name" 325 | }, 326 | "chiefName": { 327 | "maxLength": 20, 328 | "minLength": 3, 329 | "type": "string", 330 | "description": "chief name of Subordinate" 331 | } 332 | }, 333 | "description": "Subordinate DTO" 334 | }, 335 | "Chief": { 336 | "required": [ 337 | "name" 338 | ], 339 | "type": "object", 340 | "properties": { 341 | "name": { 342 | "maxLength": 20, 343 | "minLength": 3, 344 | "type": "string", 345 | "description": "chief name" 346 | } 347 | }, 348 | "description": "DTO for chief creating" 349 | } 350 | }, 351 | "securitySchemes": { 352 | "Public-Bearer-Jwt": { 353 | "type": "http", 354 | "scheme": "bearer", 355 | "bearerFormat": "JWT" 356 | }, 357 | "Internal-Token": { 358 | "type": "apiKey", 359 | "name": "SERVICE_ACCESS_TOKEN", 360 | "in": "header" 361 | } 362 | } 363 | } 364 | } -------------------------------------------------------------------------------- /src/test/java/resources/json/test-rabbit.json: -------------------------------------------------------------------------------- 1 | { 2 | "openapi": "3.0.1", 3 | "info": { 4 | "title": "App API", 5 | "version": "snapshot" 6 | }, 7 | "servers": [ 8 | { 9 | "url": "http://axenapi.demo", 10 | "description": "Generated server url" 11 | } 12 | ], 13 | "paths": { 14 | "/rabbit/new_queue/ExampleMessage": { 15 | "post": { 16 | "description": "example handler", 17 | "operationId": "ExampleHandler", 18 | "tags": [ 19 | "example" 20 | ], 21 | "security": [ 22 | { 23 | "Internal-Token": [] 24 | } 25 | ], 26 | "requestBody": { 27 | "description": "Example message", 28 | "content": { 29 | "*/*": { 30 | "schema": { 31 | "$ref": "#/components/schemas/ExampleMessage" 32 | } 33 | } 34 | } 35 | }, 36 | "responses": { 37 | "200": { 38 | "description": "Example out", 39 | "content": { 40 | "*/*": { 41 | } 42 | } 43 | } 44 | } 45 | } 46 | } 47 | }, 48 | "components": { 49 | "schemas": { 50 | "ExampleMessage": { 51 | "type": "object", 52 | "properties": { 53 | "message": { 54 | "type": "string", 55 | "description": "example field" 56 | } 57 | }, 58 | "description": "example message" 59 | } 60 | }, 61 | "securitySchemes": { 62 | "Internal-Token": { 63 | "type": "apiKey", 64 | "name": "SERVICE_ACCESS_TOKEN", 65 | "in": "header" 66 | } 67 | } 68 | } 69 | } --------------------------------------------------------------------------------