├── .gitignore ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── pom.xml ├── samples ├── Drupal10 │ ├── DevPortal │ │ ├── apicatalog-config.json │ │ ├── pom.xml │ │ ├── shared-pom.xml │ │ └── specs │ │ │ ├── Petstore.yaml │ │ │ └── pets.jpeg │ ├── README.md │ └── media │ │ ├── screenshot1.png │ │ └── screenshot2.png ├── Drupal7 │ ├── DevPortal │ │ ├── pom.xml │ │ ├── shared-pom.xml │ │ └── specs │ │ │ └── mock.yaml │ ├── README.md │ └── images │ │ ├── Fake-Company.png │ │ ├── Field-Company.png │ │ ├── Service-Edit.png │ │ ├── Service-Endpoints-Advanced.png │ │ ├── Service-Endpoints-User.png │ │ ├── Service-Endpoints.png │ │ └── Service-Listing.png └── README.md └── src └── main ├── java └── com │ └── apigee │ └── smartdocs │ └── config │ ├── mavenplugin │ ├── APIDocsMojo.java │ └── PortalAbstractMojo.java │ ├── rest │ ├── FakeHostnameVerifier.java │ ├── PortalRestUtil.java │ └── XTrustProvider.java │ └── utils │ ├── PortalField.java │ ├── PrintUtil.java │ └── ServerProfile.java └── resources └── log4j2.properties /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | target/ 3 | bin/ 4 | .settings 5 | .classpath 6 | .project 7 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # How to Contribute 2 | 3 | We'd love to accept your patches and contributions to this project. There are 4 | just a few small guidelines you need to follow. 5 | 6 | ## Contributor License Agreement 7 | 8 | Contributions to this project must be accompanied by a Contributor License 9 | Agreement. You (or your employer) retain the copyright to your contribution, 10 | this simply gives us permission to use and redistribute your contributions as 11 | part of the project. Head over to to see 12 | your current agreements on file or to sign a new one. 13 | 14 | You generally only need to submit a CLA once, so if you've already submitted one 15 | (even if it was for a different project), you probably don't need to do it 16 | again. 17 | 18 | ## Code reviews 19 | 20 | All submissions, including submissions by project members, require review. We 21 | use GitHub pull requests for this purpose. Consult 22 | [GitHub Help](https://help.github.com/articles/about-pull-requests/) for more 23 | information on using pull requests. -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 5 | 1. Definitions. 6 | "License" shall mean the terms and conditions for use, reproduction, 7 | and distribution as defined by Sections 1 through 9 of this document. 8 | "Licensor" shall mean the copyright owner or entity authorized by 9 | the copyright owner that is granting the License. 10 | "Legal Entity" shall mean the union of the acting entity and all 11 | other entities that control, are controlled by, or are under common 12 | control with that entity. For the purposes of this definition, 13 | "control" means (i) the power, direct or indirect, to cause the 14 | direction or management of such entity, whether by contract or 15 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 16 | outstanding shares, or (iii) beneficial ownership of such entity. 17 | "You" (or "Your") shall mean an individual or Legal Entity 18 | exercising permissions granted by this License. 19 | "Source" form shall mean the preferred form for making modifications, 20 | including but not limited to software source code, documentation 21 | source, and configuration files. 22 | "Object" form shall mean any form resulting from mechanical 23 | transformation or translation of a Source form, including but 24 | not limited to compiled object code, generated documentation, 25 | and conversions to other media types. 26 | "Work" shall mean the work of authorship, whether in Source or 27 | Object form, made available under the License, as indicated by a 28 | copyright notice that is included in or attached to the work 29 | (an example is provided in the Appendix below). 30 | "Derivative Works" shall mean any work, whether in Source or Object 31 | form, that is based on (or derived from) the Work and for which the 32 | editorial revisions, annotations, elaborations, or other modifications 33 | represent, as a whole, an original work of authorship. For the purposes 34 | of this License, Derivative Works shall not include works that remain 35 | separable from, or merely link (or bind by name) to the interfaces of, 36 | the Work and Derivative Works thereof. 37 | "Contribution" shall mean any work of authorship, including 38 | the original version of the Work and any modifications or additions 39 | to that Work or Derivative Works thereof, that is intentionally 40 | submitted to Licensor for inclusion in the Work by the copyright owner 41 | or by an individual or Legal Entity authorized to submit on behalf of 42 | the copyright owner. For the purposes of this definition, "submitted" 43 | means any form of electronic, verbal, or written communication sent 44 | to the Licensor or its representatives, including but not limited to 45 | communication on electronic mailing lists, source code control systems, 46 | and issue tracking systems that are managed by, or on behalf of, the 47 | Licensor for the purpose of discussing and improving the Work, but 48 | excluding communication that is conspicuously marked or otherwise 49 | designated in writing by the copyright owner as "Not a Contribution." 50 | "Contributor" shall mean Licensor and any individual or Legal Entity 51 | on behalf of whom a Contribution has been received by Licensor and 52 | subsequently incorporated within the Work. 53 | 2. Grant of Copyright License. Subject to the terms and conditions of 54 | this License, each Contributor hereby grants to You a perpetual, 55 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 56 | copyright license to reproduce, prepare Derivative Works of, 57 | publicly display, publicly perform, sublicense, and distribute the 58 | Work and such Derivative Works in Source or Object form. 59 | 3. Grant of Patent License. Subject to the terms and conditions of 60 | this License, each Contributor hereby grants to You a perpetual, 61 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 62 | (except as stated in this section) patent license to make, have made, 63 | use, offer to sell, sell, import, and otherwise transfer the Work, 64 | where such license applies only to those patent claims licensable 65 | by such Contributor that are necessarily infringed by their 66 | Contribution(s) alone or by combination of their Contribution(s) 67 | with the Work to which such Contribution(s) was submitted. If You 68 | institute patent litigation against any entity (including a 69 | cross-claim or counterclaim in a lawsuit) alleging that the Work 70 | or a Contribution incorporated within the Work constitutes direct 71 | or contributory patent infringement, then any patent licenses 72 | granted to You under this License for that Work shall terminate 73 | as of the date such litigation is filed. 74 | 4. Redistribution. You may reproduce and distribute copies of the 75 | Work or Derivative Works thereof in any medium, with or without 76 | modifications, and in Source or Object form, provided that You 77 | meet the following conditions: 78 | (a) You must give any other recipients of the Work or 79 | Derivative Works a copy of this License; and 80 | (b) You must cause any modified files to carry prominent notices 81 | stating that You changed the files; and 82 | (c) You must retain, in the Source form of any Derivative Works 83 | that You distribute, all copyright, patent, trademark, and 84 | attribution notices from the Source form of the Work, 85 | excluding those notices that do not pertain to any part of 86 | the Derivative Works; and 87 | (d) If the Work includes a "NOTICE" text file as part of its 88 | distribution, then any Derivative Works that You distribute must 89 | include a readable copy of the attribution notices contained 90 | within such NOTICE file, excluding those notices that do not 91 | pertain to any part of the Derivative Works, in at least one 92 | of the following places: within a NOTICE text file distributed 93 | as part of the Derivative Works; within the Source form or 94 | documentation, if provided along with the Derivative Works; or, 95 | within a display generated by the Derivative Works, if and 96 | wherever such third-party notices normally appear. The contents 97 | of the NOTICE file are for informational purposes only and 98 | do not modify the License. You may add Your own attribution 99 | notices within Derivative Works that You distribute, alongside 100 | or as an addendum to the NOTICE text from the Work, provided 101 | that such additional attribution notices cannot be construed 102 | as modifying the License. 103 | You may add Your own copyright statement to Your modifications and 104 | may provide additional or different license terms and conditions 105 | for use, reproduction, or distribution of Your modifications, or 106 | for any such Derivative Works as a whole, provided Your use, 107 | reproduction, and distribution of the Work otherwise complies with 108 | the conditions stated in this License. 109 | 5. Submission of Contributions. Unless You explicitly state otherwise, 110 | any Contribution intentionally submitted for inclusion in the Work 111 | by You to the Licensor shall be under the terms and conditions of 112 | this License, without any additional terms or conditions. 113 | Notwithstanding the above, nothing herein shall supersede or modify 114 | the terms of any separate license agreement you may have executed 115 | with Licensor regarding such Contributions. 116 | 6. Trademarks. This License does not grant permission to use the trade 117 | names, trademarks, service marks, or product names of the Licensor, 118 | except as required for reasonable and customary use in describing the 119 | origin of the Work and reproducing the content of the NOTICE file. 120 | 7. Disclaimer of Warranty. Unless required by applicable law or 121 | agreed to in writing, Licensor provides the Work (and each 122 | Contributor provides its Contributions) on an "AS IS" BASIS, 123 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 124 | implied, including, without limitation, any warranties or conditions 125 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 126 | PARTICULAR PURPOSE. You are solely responsible for determining the 127 | appropriateness of using or redistributing the Work and assume any 128 | risks associated with Your exercise of permissions under this License. 129 | 8. Limitation of Liability. In no event and under no legal theory, 130 | whether in tort (including negligence), contract, or otherwise, 131 | unless required by applicable law (such as deliberate and grossly 132 | negligent acts) or agreed to in writing, shall any Contributor be 133 | liable to You for damages, including any direct, indirect, special, 134 | incidental, or consequential damages of any character arising as a 135 | result of this License or out of the use or inability to use the 136 | Work (including but not limited to damages for loss of goodwill, 137 | work stoppage, computer failure or malfunction, or any and all 138 | other commercial damages or losses), even if such Contributor 139 | has been advised of the possibility of such damages. 140 | 9. Accepting Warranty or Additional Liability. While redistributing 141 | the Work or Derivative Works thereof, You may choose to offer, 142 | and charge a fee for, acceptance of support, warranty, indemnity, 143 | or other liability obligations and/or rights consistent with this 144 | License. However, in accepting such obligations, You may act only 145 | on Your own behalf and on Your sole responsibility, not on behalf 146 | of any other Contributor, and only if You agree to indemnify, 147 | defend, and hold each Contributor harmless for any liability 148 | incurred by, or claims asserted against, such Contributor by reason 149 | of your accepting any such warranty or additional liability. 150 | END OF TERMS AND CONDITIONS 151 | APPENDIX: How to apply the Apache License to your work. 152 | To apply the Apache License to your work, attach the following 153 | boilerplate notice, with the fields enclosed by brackets "[]" 154 | replaced with your own identifying information. (Don't include 155 | the brackets!) The text should be enclosed in the appropriate 156 | comment syntax for the file format. We also recommend that a 157 | file or class name and description of purpose be included on the 158 | same "printed page" as the copyright notice for easier 159 | identification within third-party archives. 160 | Copyright [yyyy] [name of copyright owner] 161 | Licensed under the Apache License, Version 2.0 (the "License"); 162 | you may not use this file except in compliance with the License. 163 | You may obtain a copy of the License at 164 | http://www.apache.org/licenses/LICENSE-2.0 165 | Unless required by applicable law or agreed to in writing, software 166 | distributed under the License is distributed on an "AS IS" BASIS, 167 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 168 | See the License for the specific language governing permissions and 169 | limitations under the License. 170 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # apigee-smartdocs-maven-plugin 2 | 3 | ---------------- 4 | About the Plugin 5 | ---------------- 6 | 7 | apigee-smartdocs-maven-plugin is a utility for creating API models and pushing an OpenAPI spec document into the API Catalog module for a Drupal 10-based developer portal, where it can then be rendered into documentation using SmartDocs or another renderer. 8 | 9 | The code is distributed under the Apache License 2.0. 10 | 11 | **NOTE:** Log4J libraries are upgraded to v2.17.1 in v2.2.2 12 | 13 | ------------ 14 | TL;DR 15 | ------------ 16 | 17 | - Version **1.x** of this plugin should be used for **Drupal 7** version of the Developer portal and the goal is `apimodel`. For example in your pom.xml 18 | ```xml 19 | 20 | com.apigee.smartdocs.config 21 | apigee-smartdocs-maven-plugin 22 | 1.0.8 23 | 24 | 25 | smartdocs-deploy 26 | install 27 | 28 | apimodel 29 | 30 | 31 | 32 | 33 | ``` 34 | - Version **2.x** of this plugin should be used for **Drupal 10** version of the Developer portal and the goal is `apidoc`. For example in your pom.xml 35 | ```xml 36 | 37 | com.apigee.smartdocs.config 38 | apigee-smartdocs-maven-plugin 39 | 2.1.0 40 | 41 | 42 | smartdocs-deploy 43 | install 44 | 45 | apidoc 46 | 47 | 48 | 49 | 50 | ``` 51 | 52 | The [samples folder](https://github.com/apigee/apigee-smartdocs-maven-plugin/tree/master/samples) provides a Readme with Getting Started steps and commands to hit the ground quickly. Contains samples for Drupal 7 and Drupal 10 version of Developer portal 53 | 54 | 55 | ## Support 56 | * Please send feature requests using [issues](https://github.com/apigee/apigee-smartdocs-maven-plugin/issues) 57 | * Post a question in [Apigee community](https://community.apigee.com/index.html) 58 | * Create an [issue](https://github.com/apigee/apigee-smartdocs-maven-plugin/issues/new) 59 | 60 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4.0.0 3 | 4 | 5 | org.sonatype.oss 6 | oss-parent 7 | 7 8 | 9 | 10 | com.apigee.smartdocs.config 11 | apigee-smartdocs-maven-plugin 12 | 2.2.5-SNAPSHOT 13 | maven-plugin 14 | apigee-smartdocs-maven-plugin 15 | Plugin to manage configuration for Smartdocs in Apigee Drupal developer portal 16 | https://github.com/apigee/apigee-smartdocs-maven-plugin 17 | 18 | 19 | 20 | The Apache Software License, Version 2.0 21 | http://www.apache.org/licenses/LICENSE-2.0.txt 22 | 23 | 24 | 25 | 26 | 27 | Sai Saran Vaidyanathan 28 | ssvaidyanathan@google.com 29 | Google 30 | www.google.com 31 | 32 | 33 | Gitesh Koli 34 | gkoli@google.com 35 | Google 36 | www.google.com 37 | 38 | 39 | 40 | 41 | git@github.com:apigee/apigee-smartdocs-maven-plugin.git 42 | scm:git:https://github.com/apigee/apigee-smartdocs-maven-plugin.git 43 | scm:git:ssh://git@github.com/apigee/apigee-smartdocs-maven-plugin.git 44 | 45 | 46 | 47 | 1.8 48 | 1.8 49 | UTF-8 50 | 5.3.26 51 | 2.17.1 52 | 53 | 54 | 55 | 56 | org.apache.maven 57 | maven-plugin-api 58 | 2.0 59 | 60 | 61 | org.apache.maven.plugin-tools 62 | maven-plugin-annotations 63 | 3.2 64 | provided 65 | 66 | 67 | org.codehaus.plexus 68 | plexus-utils 69 | 3.0.24 70 | 71 | 72 | junit 73 | junit 74 | 4.13.1 75 | test 76 | 77 | 78 | org.apache.axis2 79 | axis2-kernel 80 | 1.3 81 | jar 82 | compile 83 | 84 | 85 | jakarta-httpcore-niossl 86 | httpcomponents-httpcore 87 | 88 | 89 | httpcore 90 | org.apache.httpcomponents 91 | 92 | 93 | org.apache.httpcomponents 94 | httpcore-niossl 95 | 96 | 97 | org.apache.httpcomponents 98 | httpcore-nio 99 | 100 | 101 | woden 102 | org.apache.woden 103 | 104 | 105 | log4j 106 | log4j 107 | 108 | 109 | 110 | 111 | org.apache.axis2 112 | axis2-metadata 113 | 1.3 114 | jar 115 | compile 116 | 117 | 118 | woden 119 | org.apache.woden 120 | 121 | 122 | log4j 123 | log4j 124 | 125 | 126 | 127 | 128 | com.google.http-client 129 | google-http-client 130 | 1.23.0 131 | 132 | 133 | com.google.http-client 134 | google-http-client-jackson 135 | 1.23.0 136 | 137 | 138 | com.google.code.gson 139 | gson 140 | 2.8.9 141 | 142 | 143 | org.apache.logging.log4j 144 | log4j-api 145 | ${log4j.version} 146 | 147 | 148 | org.apache.logging.log4j 149 | log4j-core 150 | ${log4j.version} 151 | 152 | 153 | org.eclipse.jgit 154 | org.eclipse.jgit 155 | 3.5.3.201412180710-r 156 | 157 | 158 | 159 | com.googlecode.json-simple 160 | json-simple 161 | 1.1.1 162 | 163 | 164 | 165 | org.springframework 166 | spring-core 167 | ${spring.version} 168 | 169 | 170 | org.springframework 171 | spring-context 172 | ${spring.version} 173 | 174 | 175 | org.springframework 176 | spring-web 177 | ${spring.version} 178 | 179 | 180 | cglib 181 | cglib 182 | 2.2.2 183 | 184 | 185 | org.yaml 186 | snakeyaml 187 | 2.0 188 | 189 | 190 | com.auth0 191 | java-jwt 192 | 3.1.0 193 | 194 | 195 | 196 | org.apache.commons 197 | commons-text 198 | 1.2 199 | 200 | 201 | com.fasterxml.jackson.core 202 | jackson-databind 203 | 2.13.4.2 204 | 205 | 206 | 207 | 208 | 209 | 210 | org.apache.maven.plugins 211 | maven-javadoc-plugin 212 | 2.10.2 213 | 214 | 215 | attach-javadocs 216 | 217 | jar 218 | 219 | 220 | -Xdoclint:none 221 | 222 | 223 | 224 | 225 | 226 | org.apache.maven.plugins 227 | maven-plugin-plugin 228 | 3.5 229 | 230 | apigee-smartdocs 231 | true 232 | 233 | 234 | 235 | mojo-descriptor 236 | 237 | descriptor 238 | 239 | 240 | 246 | 247 | 248 | 249 | 250 | 251 | 252 | 253 | run-its 254 | 255 | 256 | 257 | org.apache.maven.plugins 258 | maven-invoker-plugin 259 | 1.7 260 | 261 | true 262 | ${project.build.directory}/it 263 | 264 | */pom.xml 265 | 266 | verify 267 | ${project.build.directory}/local-repo 268 | src/it/settings.xml 269 | 270 | clean 271 | test-compile 272 | 273 | 274 | 275 | 276 | integration-test 277 | 278 | install 279 | integration-test 280 | verify 281 | 282 | 283 | 284 | 285 | 286 | 287 | 288 | 289 | default 290 | 291 | true 292 | 293 | 294 | ${org} 295 | ${env} 296 | ${username} 297 | ${password} 298 | true 299 | 300 | 301 | 302 | release-sign-artifacts 303 | 304 | 305 | performRelease 306 | true 307 | 308 | 309 | 310 | ${org} 311 | ${env} 312 | ${username} 313 | ${password} 314 | true 315 | 316 | 317 | 318 | 319 | org.apache.maven.plugins 320 | maven-source-plugin 321 | 322 | 323 | attach-sources 324 | 325 | jar 326 | 327 | 328 | 329 | 330 | 331 | org.apache.maven.plugins 332 | maven-javadoc-plugin 333 | 334 | 335 | attach-javadocs 336 | 337 | jar 338 | 339 | 340 | 341 | 342 | 343 | org.apache.maven.plugins 344 | maven-gpg-plugin 345 | 1.4 346 | 347 | 348 | sign-artifacts 349 | verify 350 | 351 | sign 352 | 353 | 354 | 355 | 356 | 357 | 358 | 359 | 360 | 361 | 362 | -------------------------------------------------------------------------------- /samples/Drupal10/DevPortal/apicatalog-config.json: -------------------------------------------------------------------------------- 1 | { 2 | "fields":{ 3 | "field_image": "pets.jpeg", 4 | "field_api_product":[ 5 | "demo" 6 | ] 7 | } 8 | } -------------------------------------------------------------------------------- /samples/Drupal10/DevPortal/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | apigee 5 | parent-pom 6 | 1.0 7 | shared-pom.xml 8 | 9 | 4.0.0 10 | Apigee 11 | DeveloperPortal 12 | 1.0 13 | DeveloperPortal 14 | pom 15 | 16 | 17 | dev 18 | 19 | ${purl} 20 | ${pusername} 21 | ${ppassword} 22 | yaml 23 | basic_html 24 | ./specs 25 | ./apicatalog-config.json 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /samples/Drupal10/DevPortal/shared-pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4.0.0 4 | apigee 5 | parent-pom 6 | pom 7 | 1.0 8 | 9 | 10 | central 11 | Maven Plugin Repository 12 | https://repo1.maven.org/maven2 13 | default 14 | 15 | false 16 | 17 | 18 | never 19 | 20 | 21 | 22 | 24 | 25 | 26 | 27 | com.apigee.smartdocs.config 28 | apigee-smartdocs-maven-plugin 29 | 2.2.4 30 | 31 | 32 | smartdocs-deploy 33 | install 34 | 35 | apidoc 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | junit 45 | junit 46 | 4.8.2 47 | test 48 | 49 | 50 | 51 | -------------------------------------------------------------------------------- /samples/Drupal10/DevPortal/specs/Petstore.yaml: -------------------------------------------------------------------------------- 1 | openapi: "3.0.0" 2 | info: 3 | version: 1.0.0 4 | title: Swagger Petstore 5 | description: Swagger Petstore Description 6 | license: 7 | name: MIT 8 | servers: 9 | - url: http://petstore.swagger.io/v1 10 | paths: 11 | /pets: 12 | get: 13 | summary: List all pets 14 | operationId: listPets 15 | tags: 16 | - pets 17 | parameters: 18 | - name: limit 19 | in: query 20 | description: How many items to return at one time (max 100) 21 | required: false 22 | schema: 23 | type: integer 24 | format: int32 25 | responses: 26 | '200': 27 | description: A paged array of pets 28 | headers: 29 | x-next: 30 | description: A link to the next page of responses 31 | schema: 32 | type: string 33 | content: 34 | application/json: 35 | schema: 36 | $ref: "#/components/schemas/Pets" 37 | default: 38 | description: unexpected error 39 | content: 40 | application/json: 41 | schema: 42 | $ref: "#/components/schemas/Error" 43 | post: 44 | summary: Create a pet 45 | operationId: createPets 46 | tags: 47 | - pets 48 | responses: 49 | '201': 50 | description: Null response 51 | default: 52 | description: unexpected error 53 | content: 54 | application/json: 55 | schema: 56 | $ref: "#/components/schemas/Error" 57 | /pets/{petId}: 58 | get: 59 | summary: Info for a specific pet 60 | operationId: showPetById 61 | tags: 62 | - pets 63 | parameters: 64 | - name: petId 65 | in: path 66 | required: true 67 | description: The id of the pet to retrieve 68 | schema: 69 | type: string 70 | responses: 71 | '200': 72 | description: Expected response to a valid request 73 | content: 74 | application/json: 75 | schema: 76 | $ref: "#/components/schemas/Pet" 77 | default: 78 | description: unexpected error 79 | content: 80 | application/json: 81 | schema: 82 | $ref: "#/components/schemas/Error" 83 | components: 84 | schemas: 85 | Pet: 86 | type: object 87 | required: 88 | - id 89 | - name 90 | properties: 91 | id: 92 | type: integer 93 | format: int64 94 | name: 95 | type: string 96 | tag: 97 | type: string 98 | Pets: 99 | type: array 100 | items: 101 | $ref: "#/components/schemas/Pet" 102 | Error: 103 | type: object 104 | required: 105 | - code 106 | - message 107 | properties: 108 | code: 109 | type: integer 110 | format: int32 111 | message: 112 | type: string -------------------------------------------------------------------------------- /samples/Drupal10/DevPortal/specs/pets.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/apigee/apigee-smartdocs-maven-plugin/2d14d6b171419062a8d6880c53d54321a73ffec7/samples/Drupal10/DevPortal/specs/pets.jpeg -------------------------------------------------------------------------------- /samples/Drupal10/README.md: -------------------------------------------------------------------------------- 1 | ------------ 2 | Plugin Usage 3 | ------------ 4 | ``` 5 | mvn install -Pdev -Dapigee.smartdocs.config.options=create 6 | 7 | # Options 8 | 9 | -P 10 | Pick a profile in the parent pom.xml (shared-pom.xml in the example). 11 | Apigee org and env information comes from the profile. 12 | 13 | -Dapigee.smartdocs.config.options 14 | none - No action (default) 15 | create - Creates the doc found in the OpenAPI Spec directory 16 | update - Updates the doc found in the OpenAPI Spec directory 17 | delete - Deletes the doc found in the OpenAPI Spec directory 18 | sync - executes delete and update options mentioned above 19 | 20 | ``` 21 | 22 | 23 | # Samples 24 | 25 | ## Prerequisites (Developer Portal setup) 26 | - This sample is for **Drupal 10 version of Developer portal**. The version of the plugin used in the pom **should be 2.x** 27 | - If you are using Drupal 7 version of Developer portal, please follow the instructions [here](https://github.com/apigee/apigee-smartdocs-maven-plugin/tree/master/samples/Drupal7) 28 | - To utilize this example, you will need a working developer portal instance with the [API Docs](https://www.drupal.org/docs/8/modules/apigee-api-catalog/expose-rest-apis-to-interact-with-api-docs#s-prerequisites) installed and enabled. That module will expose endpoints for use by the SmartDocs Maven Plugin. 29 | - For Apigee API Catalog 1.x module, use version 2.0.1 (Apigee API Catalog 1.x which is already deprecated) 30 | ```xml 31 | 32 | com.apigee.smartdocs.config 33 | apigee-smartdocs-maven-plugin 34 | 2.0.1 35 | 36 | ``` 37 | - For Apigee API Catalog 2.x module, use version 2.1.0 or later 38 | ```xml 39 | 40 | com.apigee.smartdocs.config 41 | apigee-smartdocs-maven-plugin 42 | 2.1.0 43 | 44 | ``` 45 | 46 | ## DevPortal 47 | 48 | ### Basic Implementation 49 | 50 | **Please ensure all prerequisites have been followed prior to continuing.** 51 | 52 | Goal: Import OpenAPI specs and create API Docs in the developer portal instance. 53 | 54 | ``` 55 | /samples/DevPortal 56 | ``` 57 | 58 | This project demonstrates use of apigee-smartdocs-maven-plugin to create API Docs using OpenAPI specs to a developer portal. 59 | 60 | To use, edit samples/DevPortal/pom.xml and update portal values as specified. 61 | 62 | ${purl} 63 | ${pusername} 64 | ${ppassword} 65 | yaml 66 | basic_html 67 | ${pdirectory} 68 | 69 | **NOTE:** Please provide the url of the developer portal without the trailing "/" 70 | 71 | To run, jump to the sample project `cd /samples/DevPortal` and run 72 | 73 | `mvn install -Pdev -Dapigee.smartdocs.config.options=create` 74 | 75 | ### Advanced Implementation (available on v2.1.0 or later) 76 | 77 | **To use these features make sure you have the Apigee API Catalog module version apigee_api_catalog 3.0.4 or higher. Please read through the [release notes](https://www.drupal.org/project/apigee_api_catalog/releases/3.0.4).** 78 | 79 | If you want to configure/manage fields and taxonomy, you can create a json file and pass that as `apigee.smartdocs.config.file` argument. A simple example below: 80 | 81 | **NOTE: The fields should be pre-configured on the API Doc content type in the portal** 82 | 83 | #### Sample 84 | 85 | ![](./media/screenshot1.png) 86 | 87 | Types of fields supported : 88 | - Text (single or multivalued textfields or dropdowns) 89 | - Entity Reference (Taxonomy) 90 | 91 | To use Taxonomy reference fields make sure that you are using a term already defined in the Vocabulary. In the config file, we will need to specify the vocabulary name for every taxonomy field that is being used. Additionally we have to ensure that the JSONAPIs for the related taxonomy terms are enabled. 92 | 93 | ![](./media/screenshot2.png) 94 | 95 | For example, the default Categories field on API Doc is associated with the "API Category" Vocabulary and has Data Append, Identity, Mobile, Payments , etc as acceptable term values. 96 | 97 | Here is a sample metadata file (apicatalog-config.json) : 98 | 99 | **NOTE: Support for API spec image is available from v2.1.2. Make sure the image is in the same folder as the spec and include the name of the file in the config file (see below)** 100 | 101 | ``` 102 | { 103 | "fields":{ 104 | "field_image": "pets.jpeg", 105 | "field_api_product":[ 106 | "demo" 107 | ], 108 | "field_business_unit": "ABC", 109 | "field_multi_value":[ 110 | "item1", 111 | "item2" 112 | ] 113 | }, 114 | "taxonomy_terms":[ 115 | { 116 | "vocabulary":"capability", 117 | "field":"field_capability", 118 | "data":[ 119 | "Capability1", 120 | "Capability2" 121 | ] 122 | }, 123 | { 124 | "vocabulary":"api_category", 125 | "field":"field_categories", 126 | "data":[ 127 | "Mobile" 128 | ] 129 | } 130 | ] 131 | } 132 | 133 | ``` 134 | 135 | NOTE: The fields and taxonomy should be pre-configured in the portal. Please provide the correct field, vocabulary names. 136 | 137 | `mvn install -Pdev -Dapigee.smartdocs.config.options=create -Dapigee.smartdocs.config.file=./apicatalog-config.json` 138 | 139 | 140 | ### Troubleshooting 141 | 142 | #### 404 Error 143 | Validate that the module is installed and enabled as described in the prerequisites. If you are still encountering errors, try downloading an app such as [Postman](https://www.getpostman.com/) and making a direct request to endpoints defined [here](https://www.drupal.org/docs/8/modules/apigee-api-catalog/expose-rest-apis-to-interact-with-api-docs#s-interacting-with-the-rest-api). 144 | 145 | #### Handshake_failure 146 | If you receive a handshake failure, it is likely an issue with TLS mismatch between your machine and the server. Add the following to the end of the `mvn install ...` line: `-Dhttps.protocols=TLSv1.2` 147 | 148 | e.g. `mvn install -Pdev -Dapigee.smartdocs.config.options=create -Dhttps.protocols=TLSv1.2` 149 | -------------------------------------------------------------------------------- /samples/Drupal10/media/screenshot1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/apigee/apigee-smartdocs-maven-plugin/2d14d6b171419062a8d6880c53d54321a73ffec7/samples/Drupal10/media/screenshot1.png -------------------------------------------------------------------------------- /samples/Drupal10/media/screenshot2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/apigee/apigee-smartdocs-maven-plugin/2d14d6b171419062a8d6880c53d54321a73ffec7/samples/Drupal10/media/screenshot2.png -------------------------------------------------------------------------------- /samples/Drupal7/DevPortal/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | apigee 5 | parent-pom 6 | 1.0 7 | shared-pom.xml 8 | 9 | 4.0.0 10 | Apigee 11 | DeveloperPortal 12 | 1.0 13 | DeveloperPortal 14 | pom 15 | 16 | 17 | dev 18 | 19 | ${pusername} 20 | ${ppassword} 21 | ./specs 22 | ${purl} 23 | ${ppath} 24 | ${pcronkey} 25 | yaml 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /samples/Drupal7/DevPortal/shared-pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4.0.0 4 | apigee 5 | parent-pom 6 | pom 7 | 1.0 8 | 9 | 10 | central 11 | Maven Plugin Repository 12 | https://repo1.maven.org/maven2 13 | default 14 | 15 | false 16 | 17 | 18 | never 19 | 20 | 21 | 22 | 24 | 25 | 26 | 27 | com.apigee.smartdocs.config 28 | apigee-smartdocs-maven-plugin 29 | 1.0.5 30 | 45 | 61 | 62 | 63 | smartdocs-deploy 64 | install 65 | 66 | apimodel 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | junit 76 | junit 77 | 4.8.2 78 | test 79 | 80 | 81 | 82 | -------------------------------------------------------------------------------- /samples/Drupal7/DevPortal/specs/mock.yaml: -------------------------------------------------------------------------------- 1 | swagger: '2.0' 2 | info: 3 | description: 'OpenAPI Specification for the Apigee mock target service endpoint. This is a "test" description' 4 | version: 1.0.0 5 | title: Mock Target API 6 | host: mocktarget.apigee.net 7 | schemes: 8 | - http 9 | - https 10 | securityDefinitions: 11 | basicAuth: 12 | type: basic 13 | description: HTTP Basic Authentication. 14 | paths: 15 | /: 16 | get: 17 | summary: View personalized greeting 18 | operationId: View a personalized greeting 19 | description: View a personalized greeting for the specified or guest user. 20 | produces: 21 | - text/plain 22 | parameters: 23 | - name: user 24 | in: query 25 | description: Your user name. 26 | required: false 27 | type: string 28 | responses: 29 | '200': 30 | description: Success 31 | /help: 32 | get: 33 | summary: Get help 34 | operationId: Get help 35 | description: View help information about available resources in HTML format. 36 | produces: 37 | - text/html 38 | responses: 39 | '200': 40 | description: Success 41 | /user: 42 | get: 43 | summary: View personalized greeting 44 | operationId: View personalized greeting 45 | description: View a personalized greeting for the specified or guest user. 46 | produces: 47 | - text/plain 48 | parameters: 49 | - name: user 50 | in: query 51 | description: Your user name. 52 | required: false 53 | type: string 54 | responses: 55 | '200': 56 | description: Success 57 | /iloveapis: 58 | get: 59 | summary: View API affirmation 60 | operationId: View API affirmation 61 | description: View API affirmation in HTML format. 62 | produces: 63 | - text/html 64 | responses: 65 | '200': 66 | description: Success 67 | /ip: 68 | get: 69 | summary: View IP address 70 | operationId: View IP address 71 | description: View the IP address of the client in JSON format. 72 | produces: 73 | - application/json 74 | responses: 75 | '200': 76 | description: Success 77 | /xml: 78 | get: 79 | summary: View XML response 80 | operationId: View XML response 81 | description: View a sample response in XML format. 82 | produces: 83 | - application/xml 84 | responses: 85 | '200': 86 | description: Success 87 | /json: 88 | get: 89 | summary: View JSON response 90 | operationId: View JSON response 91 | description: View a sample response in JSON format. 92 | produces: 93 | - application/json 94 | responses: 95 | '200': 96 | description: Success 97 | /echo: 98 | get: 99 | summary: View request headers and body 100 | operationId: View request headers and body 101 | description: View the request headers and body in JSON format. 102 | produces: 103 | - application/json 104 | responses: 105 | '200': 106 | description: Success 107 | post: 108 | summary: Send request and view request headers and body 109 | produces: 110 | - application/json 111 | - application/xml 112 | - application/x-www-form-urlencoded 113 | operationId: Send request and view request headers and body 114 | description: 'Send a request and view the resulting request headers and body in JSON format.

The request payload can be specified using one of the following formats: application/json, application/x-www-form-urlencoded, or application/xml.' 115 | parameters: 116 | - name: body 117 | in: body 118 | description: 'Request payload in application/json, application/x-www-form-urlencoded, or application/xml format.' 119 | required: true 120 | schema: 121 | $ref: '#/definitions/request-body' 122 | responses: 123 | '200': 124 | description: Success 125 | /statuscode/{code}: 126 | get: 127 | summary: View status code and message 128 | operationId: View status code and message 129 | description: View status code and message for the specified value. 130 | produces: 131 | - application/json 132 | parameters: 133 | - name: code 134 | in: path 135 | description: HTTP status code. 136 | required: true 137 | type: string 138 | responses: 139 | '200': 140 | description: Success 141 | /auth: 142 | get: 143 | security: 144 | - basicAuth: [] 145 | summary: Validate access using basic authentication 146 | operationId: Validate access using basic authentication 147 | description: Validate access using basic authentication. 148 | produces: 149 | - application/json 150 | responses: 151 | '200': 152 | description: Success 153 | definitions: 154 | request-body: 155 | properties: 156 | replace-me: 157 | type: object 158 | description: 'Replace with request payload in application/json, application/x-www-form-urlencoded, or application/xml format.' -------------------------------------------------------------------------------- /samples/Drupal7/README.md: -------------------------------------------------------------------------------- 1 | ------------ 2 | Plugin Usage 3 | ------------ 4 | ``` 5 | mvn install -Pdev -Dapigee.smartdocs.config.options=create 6 | 7 | # Options 8 | 9 | -P 10 | Pick a profile in the parent pom.xml (shared-pom.xml in the example). 11 | Apigee org and env information comes from the profile. 12 | 13 | -Dapigee.smartdocs.config.options 14 | none - No action (default) 15 | create - Creates the model found in the OpenAPI Spec directory 16 | update - Updates the model found in the OpenAPI Spec directory 17 | delete - Deletes all models not found in the OpenAPI Spec directory 18 | deleteAPIModel - Deletes all models from dev portal found in the OpenAPI Spec directory (available on v1.0.4 or later) 19 | render - Renders the smart docs 20 | sync - executes the delete option (mentioned above) and recreates the models found in the OpenAPI Spec directory. This also renders the smart docs as well 21 | 22 | If you use create or update option, you will need to run the command again with option as render 23 | ``` 24 | 25 | 26 | # Samples 27 | 28 | ## Prerequisites (Developer Portal setup) 29 | 30 | - This sample is for **Drupal 7 version of Developer portal**. The version of the plugin used in the pom **should be 1.x** 31 | - If you are using Drupal 10 version of Developer portal, please follow the instructions [here](https://github.com/apigee/apigee-smartdocs-maven-plugin/tree/master/samples/Drupal10) 32 | - To utilize this example, you will need a working developer portal instance with the [smartdocs_service module](https://github.com/apigeecs/smartdocs_service) installed and enabled. That module will expose endpoints for use by the SmartDocs Maven Plugin. For advanced configuration/setup please consult an expert. See [here](https://www.drupal.org/docs/7/extending-drupal-7/installing-drupal-7-contributed-modules#import_mod--manual) for more information on installing modules into the Developer Portal. 33 | 34 | When the module has been installed in the codebase, go to http://[yoursite.com]/admin/modules, select the smartdocs_service module and click "Save configuration". This will enable your module. 35 | 36 | When smartdocs_service module is installed and enabled, you should see 1 available service labeled smartdocs_service on the page located at http://[yoursite.com]/admin/structure/services. 37 | 38 | ### Service Listing 39 | ![](images/Service-Listing.png) 40 | 41 | From this page, first, click the down arrow by "Edit Resources" and choose "Edit". 42 | 43 | ### Service Edit 44 | 45 | ![](images/Service-Edit.png) 46 | 47 | Note the "Path to endpoint" field. The value set here is what we will set in the pom.xml as the "portal.path" value. You can change this value, so long as you note it for use in the pom.xml file 48 | 49 | You will need to change Authentication from "HTTP basic authentication" to "Session authentication as depicted in the image above. 50 | 51 | ### Resource Edit 52 | 53 | ![](images/Service-Endpoints.png) 54 | 55 | Leave all smartdocs endpoints enabled. The Smartdocs Maven Plugin makes use of all but queue_status and index at present. DELETE and DO NOT ADD any aliases -- utilizing an alias will cause the system not to function. 56 | 57 | ![](images/Service-Endpoints-User.png) 58 | 59 | Enable the user login endpoint. This will us to authenticate against the system and perform updates. 60 | 61 | Note that it is recommended that you create a special user for the integration and limit the permissions of the user accordingly. 62 | 63 | ### Resource Edit (Advanced implementation) 64 | 65 | If you plan to followed the advanced implementation model and import metadata with your models, you will need to enable the taxonomy_term update endpoint and the retrieveMachineName and getTree endpoints as depicted in the image below. 66 | 67 | ![](images/Service-Endpoints-Advanced.png) 68 | 69 | 70 | ## DevPortal 71 | 72 | ### Basic Implementation (Model import) 73 | 74 | **Please ensure all prerequisites have been followed prior to continuing.** 75 | 76 | Goal: Create models and import OpenAPI specs to a developer portal instance. 77 | 78 | ``` 79 | /samples/DevPortal 80 | ``` 81 | 82 | This project demonstrates use of apigee-smartdocs-maven-plugin to create models and import OpenAPI specs to a developer portal. The example project performs a data import defined in pom.xml 83 | 84 | To use, edit samples/DevPortal/pom.xml and update portal values as specified. 85 | 86 | ${pusername} 87 | ${ppassword} 88 | ${pdirectory} 89 | ${purl} 90 | ${ppath} 91 | json 92 | 93 | To run, jump to the sample project `cd /samples/DevPortal` and run 94 | 95 | `mvn install -Pdev -Dapigee.smartdocs.config.options=create` 96 | 97 | 98 | ### Advanced Implementation (Model metadata import) 99 | 100 | **Please ensure the prerequisites and basic implementation have been followed prior to continuing.** 101 | 102 | Goal: Add field data to models in a developer portal instance. 103 | 104 | ``` 105 | /samples/DevPortal 106 | ``` 107 | 108 | This project demonstrates use of apigee-smartdocs-maven-plugin to add field data to a model in a developer portal. The example project performs a data import defined in shared-pom.xml 109 | 110 | To use, edit samples/DevPortal/shared-pom.xml, uncomment, and update portal values as needed. 111 | 112 | 113 | ${model_vocabulary} 114 | 115 | 116 | contact|company 117 | field_company 118 | 119 | 120 | 121 | 122 | The path value signifies the path to data stored within the info object. 123 | The pipe character can be used to traverse into sub-items. For instance, with a OpenAPI document containing { "info": { "contact": { "company": "Google" } } }, you could be access the data by setting the path to contact|company. 124 | In the example above, "Google" would be mapped to the field_company field within the model related to the OpenAPI document definition. 125 | 126 | Example of a field_company value set within the OpenApi specification: 127 | ![](images/Fake-Company.png) 128 | 129 | Before using, you will need to create fields on the smartdocs_model taxonomy page within the developer portal. field_company would relate to a field your create on the page located at http://[yoursite.com]/admin/structure/taxonomy/smartdocs_models/fields. Note that the current sytem currently only supports the Field type of "Text". 130 | ![](images/Field-Company.png) 131 | 132 | To run jump to samples project `cd /samples/DevPortal` and run 133 | 134 | `mvn install -Pdev -Dapigee.smartdocs.config.options=create` 135 | 136 | #### Configuring the model name on the Developer Portal (Optional) 137 | 138 | The default model name without this configuration is the title field from the info object. 139 | 140 | If you would like to configure the model name of the SmartDoc on the developer portal, you can use additional fields from the OpenAPI specification. The default model name is based on the title. 141 | 142 | In order to configure it to generate a unique model name, you will need to specify the format in the shared-pom.xml 143 | 144 | 145 | ... 146 | contact|x-country^title 147 | ... 148 | 149 | 150 | The caret symbol (^) is used to separate which fields you want extracted from the info object and it will be replaced with dash (-) in the name 151 | 152 | Additionally, spaces and periods are converted to dashes 153 | 154 | The example config above would generate a model name based on the info object OpenAPI document. For instance, an OpenAPI document containing: 155 | ```json 156 | { 157 | "info": { 158 | "title": "Hello World API", 159 | "contact": { 160 | "x-country": "Canada" 161 | } 162 | } 163 | } 164 | ``` 165 | The model name would be `Canada-Hello-World-API` 166 | 167 | ### Troubleshooting 168 | 169 | #### 404 Error 170 | Validate that the module is installed and enabled and that resource endpoints are set as described in the prerequisites. If you are still encountering errors, try downloading an app such as [Postman](https://www.getpostman.com/) and making a direct request to endpoints defined on the service endpoint page. 171 | 172 | #### Handshake_failure 173 | If you receive a handshake failure, it is likely an issue with TLS mismatch between your machine and the server. Add the following to the end of the `mvn install ...` line: `-Dhttps.protocols=TLSv1.2` 174 | 175 | e.g. `mvn install -Pdev -Dapigee.smartdocs.config.options=create -Dhttps.protocols=TLSv1.2` 176 | 177 | #### Render error 178 | While the system is capable of telling the Developer Portal to "render" the content - making them visible to end users - the Developer Portal will not render the content until after a "cron" run has occurred. If you believe the content should be rendered and it has not, please validate that cron has run 179 | -------------------------------------------------------------------------------- /samples/Drupal7/images/Fake-Company.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/apigee/apigee-smartdocs-maven-plugin/2d14d6b171419062a8d6880c53d54321a73ffec7/samples/Drupal7/images/Fake-Company.png -------------------------------------------------------------------------------- /samples/Drupal7/images/Field-Company.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/apigee/apigee-smartdocs-maven-plugin/2d14d6b171419062a8d6880c53d54321a73ffec7/samples/Drupal7/images/Field-Company.png -------------------------------------------------------------------------------- /samples/Drupal7/images/Service-Edit.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/apigee/apigee-smartdocs-maven-plugin/2d14d6b171419062a8d6880c53d54321a73ffec7/samples/Drupal7/images/Service-Edit.png -------------------------------------------------------------------------------- /samples/Drupal7/images/Service-Endpoints-Advanced.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/apigee/apigee-smartdocs-maven-plugin/2d14d6b171419062a8d6880c53d54321a73ffec7/samples/Drupal7/images/Service-Endpoints-Advanced.png -------------------------------------------------------------------------------- /samples/Drupal7/images/Service-Endpoints-User.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/apigee/apigee-smartdocs-maven-plugin/2d14d6b171419062a8d6880c53d54321a73ffec7/samples/Drupal7/images/Service-Endpoints-User.png -------------------------------------------------------------------------------- /samples/Drupal7/images/Service-Endpoints.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/apigee/apigee-smartdocs-maven-plugin/2d14d6b171419062a8d6880c53d54321a73ffec7/samples/Drupal7/images/Service-Endpoints.png -------------------------------------------------------------------------------- /samples/Drupal7/images/Service-Listing.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/apigee/apigee-smartdocs-maven-plugin/2d14d6b171419062a8d6880c53d54321a73ffec7/samples/Drupal7/images/Service-Listing.png -------------------------------------------------------------------------------- /samples/README.md: -------------------------------------------------------------------------------- 1 | # Samples 2 | 3 | - [Drupal 7](https://github.com/apigee/apigee-smartdocs-maven-plugin/tree/master/samples/Drupal7) 4 | - [Drupal 10](https://github.com/apigee/apigee-smartdocs-maven-plugin/tree/master/samples/Drupal10) -------------------------------------------------------------------------------- /src/main/java/com/apigee/smartdocs/config/mavenplugin/APIDocsMojo.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2019 Google Inc. 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 com.apigee.smartdocs.config.mavenplugin; 18 | 19 | import java.io.File; 20 | import java.io.FilenameFilter; 21 | import java.io.IOException; 22 | 23 | import org.apache.maven.plugin.MojoExecutionException; 24 | import org.apache.maven.plugin.MojoFailureException; 25 | import org.apache.logging.log4j.LogManager; 26 | import org.apache.logging.log4j.Logger; 27 | import com.apigee.smartdocs.config.rest.PortalRestUtil; 28 | import com.apigee.smartdocs.config.utils.ServerProfile; 29 | 30 | /** ¡¡ 31 | * Goal to create API Docs in Apigee Developer Portal 32 | * scope: org 33 | * 34 | * @author ssvaidyanathan 35 | * @goal apidoc 36 | * @phase install 37 | */ 38 | 39 | public class APIDocsMojo extends PortalAbstractMojo { 40 | static Logger logger = LogManager.getLogger(APIDocsMojo.class); 41 | private static File[] files = null; 42 | 43 | public static final String ____ATTENTION_MARKER____ = 44 | "************************************************************************"; 45 | 46 | enum OPTIONS { 47 | none, create, update, delete, sync 48 | } 49 | 50 | OPTIONS buildOption = OPTIONS.none; 51 | 52 | private ServerProfile serverProfile; 53 | 54 | /** 55 | * Constructor. 56 | */ 57 | public APIDocsMojo() { 58 | super(); 59 | } 60 | 61 | public void init() throws MojoExecutionException, MojoFailureException { 62 | try { 63 | logger.info(____ATTENTION_MARKER____); 64 | logger.info("API Docs"); 65 | logger.info(____ATTENTION_MARKER____); 66 | 67 | String options=""; 68 | serverProfile = super.getProfile(); 69 | 70 | options = super.getOptions(); 71 | if (options != null) { 72 | buildOption = OPTIONS.valueOf(options); 73 | } 74 | if (buildOption == OPTIONS.none) { 75 | logger.info("Skipping APIDoc (default action)"); 76 | return; 77 | } 78 | 79 | logger.debug("Build option " + buildOption.name()); 80 | 81 | if (serverProfile.getPortalURL() == null) { 82 | throw new MojoExecutionException( 83 | "Developer portal URL not found in profile"); 84 | } 85 | if (serverProfile.getPortalURL() != null && serverProfile.getPortalURL().endsWith("/")) { 86 | throw new MojoExecutionException( 87 | "Please provide the url of the developer portal without the trailing \"/\""); 88 | } 89 | if (serverProfile.getPortalUserName() == null) { 90 | throw new MojoExecutionException( 91 | "Developer portal username not found in profile"); 92 | } 93 | if (serverProfile.getPortalPassword() == null) { 94 | throw new MojoExecutionException( 95 | "Developer portal password not found in profile"); 96 | } 97 | if (serverProfile.getPortalFormat() == null) { 98 | throw new MojoExecutionException( 99 | "Developer portal file format not found in profile"); 100 | } 101 | if (serverProfile.getPortalAPIDocFormat() == null) { 102 | throw new MojoExecutionException( 103 | "Developer portal API Doc format not found in profile"); 104 | } 105 | 106 | // Scan to make sure there are swagger files to send. 107 | getOpenAPISpecs(); 108 | 109 | } catch (IllegalArgumentException e) { 110 | throw new RuntimeException("Invalid apigee.option provided"); 111 | } catch (RuntimeException e) { 112 | throw e; 113 | } catch (MojoExecutionException e) { 114 | throw e; 115 | } 116 | 117 | } 118 | 119 | /** 120 | * Entry point for the mojo. 121 | */ 122 | public void execute() throws MojoExecutionException, MojoFailureException { 123 | if (super.isSkip()) { 124 | getLog().info("Skipping"); 125 | return; 126 | } 127 | 128 | try { 129 | init(); 130 | if (buildOption == OPTIONS.none) { 131 | return; 132 | } 133 | 134 | if (buildOption == OPTIONS.create){ 135 | doUpdate(true); 136 | } 137 | 138 | if (buildOption == OPTIONS.update) { 139 | doUpdate(false); 140 | } 141 | 142 | if (buildOption == OPTIONS.delete) { 143 | doDelete(); 144 | } 145 | 146 | if (buildOption == OPTIONS.sync) { 147 | doDelete(); 148 | doUpdate(true); 149 | } 150 | 151 | } catch (MojoFailureException e) { 152 | throw e; 153 | } catch (RuntimeException e) { 154 | throw e; 155 | } 156 | } 157 | 158 | /** 159 | * Posts an update based on available OpenAPI specs. Will update base 160 | * information, then upload the current OpenAPI spec. 161 | * @throws MojoExecutionException 162 | */ 163 | public void doUpdate(boolean isCreate) throws MojoExecutionException { 164 | try { 165 | for (File file : files) { 166 | PortalRestUtil.postAPIDoc(serverProfile, file, isCreate); 167 | } 168 | } 169 | catch (IOException e) { 170 | throw new RuntimeException("Update failure: " + e.getMessage()); 171 | } 172 | } 173 | 174 | /** 175 | * Deletes API Doc that exist in the API and not in the file system. 176 | * @throws MojoExecutionException 177 | */ 178 | public void doDelete() throws MojoExecutionException { 179 | try { 180 | for (File file : files) { 181 | PortalRestUtil.deleteAPIDoc(serverProfile, file); 182 | } 183 | } 184 | catch (IOException e) { 185 | throw new RuntimeException("Delete failure: " + e.getMessage()); 186 | } 187 | } 188 | 189 | /** 190 | * Pulls a list of OpenAPI specs frpm a directory to be sent 191 | * to a Developer Portal instance. 192 | * 193 | * @throws MojoExecutionException 194 | */ 195 | public void getOpenAPISpecs() throws MojoExecutionException { 196 | // Ensure we have a directory to read. 197 | if (serverProfile.getPortalDirectory() == null) { 198 | throw new MojoExecutionException( 199 | "Developer portal directory not found in profile"); 200 | } 201 | 202 | // Scan the directory for files. 203 | String directory = serverProfile.getPortalDirectory(); 204 | logger.info("Get OpenAPI Specs from " + directory); 205 | files = new File(directory).listFiles(new FilenameFilter() { 206 | @Override 207 | public boolean accept(File dir, String name) { 208 | return name.endsWith(".yaml") || name.endsWith(".yml") || name.endsWith(".json"); 209 | } 210 | }); 211 | } 212 | } 213 | 214 | 215 | 216 | 217 | -------------------------------------------------------------------------------- /src/main/java/com/apigee/smartdocs/config/mavenplugin/PortalAbstractMojo.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2017 Google Inc. 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 com.apigee.smartdocs.config.mavenplugin; 18 | 19 | import java.io.File; 20 | import java.util.Map; 21 | import org.apache.maven.plugin.AbstractMojo; 22 | import com.apigee.smartdocs.config.utils.ServerProfile; 23 | import com.apigee.smartdocs.config.utils.PortalField; 24 | 25 | public abstract class PortalAbstractMojo extends AbstractMojo { 26 | 27 | /** 28 | * Directory containing the build files. 29 | * 30 | * @parameter property="project.build.directory" 31 | */ 32 | private File buildDirectory; 33 | 34 | /** 35 | * Base directory of the project. 36 | * 37 | * @parameter property="basedir" 38 | */ 39 | private File baseDirectory; 40 | 41 | /** 42 | * Project Name 43 | * 44 | * @parameter property="project.name" 45 | */ 46 | private String projectName; 47 | 48 | /** 49 | * Project version 50 | * 51 | * @parameter property="project.version" 52 | */ 53 | private String projectVersion; 54 | 55 | /** 56 | * Project artifact id 57 | * 58 | * @parameter property="project.artifactId" 59 | */ 60 | private String artifactId; 61 | 62 | /** 63 | * Profile id 64 | * 65 | * @parameter property="apigee.profile" 66 | */ 67 | private String id; 68 | 69 | /** 70 | * Build option 71 | * 72 | * @parameter property="build.option" 73 | */ 74 | private String buildOption; 75 | 76 | /** 77 | * Gateway options 78 | * 79 | * @parameter property="apigee.smartdocs.config.options" 80 | */ 81 | private String options; 82 | 83 | /** 84 | * Config File 85 | * 86 | * @parameter property="apigee.smartdocs.config.file" 87 | */ 88 | private String configFile; 89 | 90 | /** 91 | * Portal User Name 92 | * 93 | * @parameter property="portal.username" 94 | */ 95 | private String portalUserName; 96 | 97 | /** 98 | * Portal Password 99 | * 100 | * @parameter property="portal.password" 101 | */ 102 | private String portalPassword; 103 | 104 | /** 105 | * OpenAPI Spec Directory 106 | * 107 | * @parameter property="portal.directory" 108 | */ 109 | private String portalDirectory; 110 | 111 | /** 112 | * Portal URL 113 | * 114 | * @parameter property="portal.url" 115 | */ 116 | private String portalURL; 117 | 118 | /** 119 | * Portal Path 120 | * 121 | * @parameter property="portal.path" 122 | */ 123 | private String portalPath; 124 | 125 | /** 126 | * Portal Format 127 | * 128 | * @parameter property="portal.format" 129 | */ 130 | private String portalFormat; 131 | 132 | /** 133 | * Portal Format 134 | * 135 | * @parameter alias="portal.model.vocabulary" 136 | */ 137 | private String portalModelVocabulary; 138 | 139 | /** 140 | * Portal Model Fields 141 | * 142 | * @parameter alias="portal.model.fields" 143 | */ 144 | private Map portalModelFields; 145 | 146 | /** 147 | * Portal Cron Key 148 | * 149 | * @parameter property="portal.cronkey" 150 | */ 151 | private String portalCronKey; 152 | 153 | /** 154 | * Portal Model Name Config 155 | * 156 | * @parameter alias="portal.model.config.name" 157 | */ 158 | private String portalModelNameConfig; 159 | 160 | /** 161 | * Portal API Doc Format 162 | * 163 | * @parameter property="portal.api.doc.format" 164 | */ 165 | private String portalAPIDocFormat; 166 | 167 | /** 168 | * Skip running this plugin. Default is false. 169 | * 170 | * @parameter default-value="false" 171 | */ 172 | private boolean skip = false; 173 | 174 | public ServerProfile buildProfile; 175 | 176 | public PortalAbstractMojo() { 177 | super(); 178 | } 179 | 180 | public ServerProfile getProfile() { 181 | this.buildProfile = new ServerProfile(); 182 | this.buildProfile.setOptions(this.options); 183 | this.buildProfile.setConfigFile(this.configFile); 184 | this.buildProfile.setPortalUserName(this.portalUserName); 185 | this.buildProfile.setPortalPassword(this.portalPassword); 186 | this.buildProfile.setPortalDirectory(this.portalDirectory); 187 | this.buildProfile.setPortalURL(this.portalURL); 188 | this.buildProfile.setPortalPath(this.portalPath); 189 | this.buildProfile.setPortalFormat(this.portalFormat); 190 | this.buildProfile.setPortalModelFields(this.portalModelFields); 191 | this.buildProfile.setPortalModelVocabulary(this.portalModelVocabulary); 192 | this.buildProfile.setPortalCronKey(this.portalCronKey); 193 | this.buildProfile.setPortalModelNameConfig(this.portalModelNameConfig); 194 | this.buildProfile.setPortalAPIDocFormat(this.portalAPIDocFormat); 195 | return buildProfile; 196 | } 197 | 198 | public void setProfile(ServerProfile profile) { 199 | this.buildProfile = profile; 200 | } 201 | 202 | public void setBaseDirectory(File baseDirectory) { 203 | this.baseDirectory = baseDirectory; 204 | } 205 | 206 | public String getBuildDirectory() { 207 | return this.buildDirectory.getAbsolutePath(); 208 | } 209 | 210 | public String getBaseDirectoryPath() { 211 | return this.baseDirectory.getAbsolutePath(); 212 | } 213 | 214 | public String getBuildOption() { 215 | return buildOption; 216 | } 217 | 218 | public void setBuildOption(String buildOption) { 219 | this.buildOption = buildOption; 220 | } 221 | 222 | public String getOptions() { 223 | return options; 224 | } 225 | 226 | public void setOptions(String options) { 227 | this.options = options; 228 | } 229 | 230 | public void setConfigFile(String configFile) { 231 | this.configFile = configFile; 232 | } 233 | 234 | public String getConfigFile() { 235 | return configFile; 236 | } 237 | 238 | /** 239 | * @return the id 240 | */ 241 | public String getId() { 242 | return id; 243 | } 244 | 245 | /** 246 | * @param id the id to set 247 | */ 248 | public void setId(String id) { 249 | this.id = id; 250 | } 251 | 252 | public boolean isSkip() { 253 | return skip; 254 | } 255 | 256 | public void setSkip(boolean skip) { 257 | this.skip = skip; 258 | } 259 | 260 | } 261 | -------------------------------------------------------------------------------- /src/main/java/com/apigee/smartdocs/config/rest/FakeHostnameVerifier.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2017 Google Inc. 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 com.apigee.smartdocs.config.rest; 18 | 19 | import java.io.IOException; 20 | import java.security.cert.X509Certificate; 21 | 22 | import javax.net.ssl.SSLException; 23 | import javax.net.ssl.SSLSession; 24 | import javax.net.ssl.SSLSocket; 25 | 26 | import org.apache.axis2.transport.nhttp.HostnameVerifier; 27 | 28 | public class FakeHostnameVerifier implements HostnameVerifier { 29 | 30 | public void check(String arg0, SSLSocket arg1) throws IOException { 31 | // TODO Auto-generated method stub 32 | 33 | } 34 | 35 | public void check(String arg0, X509Certificate arg1) throws SSLException { 36 | // TODO Auto-generated method stub 37 | 38 | } 39 | 40 | public void check(String[] arg0, SSLSocket arg1) throws IOException { 41 | // TODO Auto-generated method stub 42 | 43 | } 44 | 45 | public void check(String[] arg0, X509Certificate arg1) throws SSLException { 46 | // TODO Auto-generated method stub 47 | 48 | } 49 | 50 | public void check(String arg0, String[] arg1, String[] arg2) 51 | throws SSLException { 52 | // TODO Auto-generated method stub 53 | 54 | } 55 | 56 | public void check(String[] arg0, String[] arg1, String[] arg2) 57 | throws SSLException { 58 | // TODO Auto-generated method stub 59 | 60 | } 61 | 62 | public boolean verify(String arg0, SSLSession arg1) { 63 | // TODO Auto-generated method stub 64 | return true; 65 | } 66 | 67 | } 68 | -------------------------------------------------------------------------------- /src/main/java/com/apigee/smartdocs/config/rest/PortalRestUtil.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2017 Google Inc. 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 com.apigee.smartdocs.config.rest; 18 | 19 | import java.io.File; 20 | import java.io.FileNotFoundException; 21 | import java.io.IOException; 22 | import java.io.InputStream; 23 | import java.io.InputStreamReader; 24 | import java.io.Reader; 25 | import java.io.StringReader; 26 | import java.nio.file.Files; 27 | import java.util.ArrayList; 28 | import java.util.HashMap; 29 | import java.util.Iterator; 30 | import java.util.List; 31 | import java.util.Map; 32 | 33 | import javax.net.ssl.HttpsURLConnection; 34 | 35 | import org.apache.commons.text.StringEscapeUtils; 36 | import org.apache.logging.log4j.LogManager; 37 | import org.apache.logging.log4j.Logger; 38 | import org.json.simple.JSONValue; 39 | import org.yaml.snakeyaml.Yaml; 40 | 41 | import com.apigee.smartdocs.config.utils.ServerProfile; 42 | import com.fasterxml.jackson.databind.ObjectMapper; 43 | import com.google.api.client.http.ByteArrayContent; 44 | import com.google.api.client.http.FileContent; 45 | import com.google.api.client.http.GenericUrl; 46 | import com.google.api.client.http.HttpHeaders; 47 | import com.google.api.client.http.HttpMethods; 48 | import com.google.api.client.http.HttpRequest; 49 | import com.google.api.client.http.HttpRequestFactory; 50 | import com.google.api.client.http.HttpRequestInitializer; 51 | import com.google.api.client.http.HttpResponse; 52 | import com.google.api.client.http.HttpResponseException; 53 | import com.google.api.client.http.HttpTransport; 54 | import com.google.api.client.http.apache.ApacheHttpTransport; 55 | import com.google.api.client.http.javanet.NetHttpTransport; 56 | import com.google.api.client.json.JsonFactory; 57 | import com.google.api.client.json.jackson.JacksonFactory; 58 | import com.google.api.client.util.Key; 59 | import com.google.gson.Gson; 60 | import com.google.gson.JsonArray; 61 | import com.google.gson.JsonObject; 62 | import com.google.gson.internal.LinkedTreeMap; 63 | 64 | public class PortalRestUtil { 65 | 66 | static final HttpTransport HTTP_TRANSPORT = new NetHttpTransport(); 67 | static final HttpTransport APACHE_HTTP_TRANSPORT = new ApacheHttpTransport(); 68 | static final JsonFactory JSON_FACTORY = new JacksonFactory(); 69 | static String versionRevision; 70 | static Logger logger = LogManager.getLogger(PortalRestUtil.class); 71 | static String accessToken = null; 72 | 73 | static HttpRequestFactory REQUEST_FACTORY = HTTP_TRANSPORT 74 | .createRequestFactory(new HttpRequestInitializer() { 75 | // @Override 76 | public void initialize(HttpRequest request) { 77 | request.setParser(JSON_FACTORY.createJsonObjectParser()); 78 | XTrustProvider.install(); 79 | FakeHostnameVerifier _hostnameVerifier = new FakeHostnameVerifier(); 80 | // Install the all-trusting host name verifier: 81 | HttpsURLConnection.setDefaultHostnameVerifier(_hostnameVerifier); 82 | } 83 | }); 84 | 85 | static HttpRequestFactory APACHE_REQUEST_FACTORY = APACHE_HTTP_TRANSPORT 86 | .createRequestFactory(new HttpRequestInitializer() { 87 | // @Override 88 | public void initialize(HttpRequest request) { 89 | request.setParser(JSON_FACTORY.createJsonObjectParser()); 90 | XTrustProvider.install(); 91 | FakeHostnameVerifier _hostnameVerifier = new FakeHostnameVerifier(); 92 | // Install the all-trusting host name verifier: 93 | HttpsURLConnection.setDefaultHostnameVerifier(_hostnameVerifier); 94 | } 95 | }); 96 | 97 | 98 | // Header values to ensure headers are set and then stored. 99 | public static Boolean headersSet = false; 100 | public static HttpHeaders headers = null; 101 | 102 | // Class to handle our authentication response. 103 | public static class AuthObject { 104 | 105 | @Key 106 | public String token; 107 | public String sessid; 108 | public String session_name; 109 | } 110 | 111 | public static class SpecObject { 112 | 113 | public LinkedTreeMap info; 114 | 115 | public String getName(String portalModelNameConfig) { 116 | if(portalModelNameConfig == null) { 117 | return info.get("title").toString().replace(" ", "-"); 118 | } else { 119 | return getConfigModelName(portalModelNameConfig); 120 | } 121 | } 122 | 123 | private String getConfigModelName(String portalModelNameConfig) { 124 | //default return 125 | String returnString = getName(null); 126 | if(portalModelNameConfig == null) { 127 | return returnString; 128 | } 129 | //Generate Model Name From Config 130 | ArrayList al = new ArrayList(); 131 | String[] nameParts = portalModelNameConfig.split("\\^"); 132 | LinkedTreeMap jo = info; 133 | for (String namePart : nameParts) { 134 | //Reset head of tree to info 135 | jo = info; 136 | //Split each config name into model information 137 | String[] namePartPaths = namePart.split("\\|"); 138 | for (String namePartPath : namePartPaths) { 139 | // Only traverse down if the key exists. 140 | if (jo.containsKey(namePartPath)) { 141 | Object o = jo.get(namePartPath); 142 | // If we still need to go deeper, we have a tree. 143 | if (o instanceof LinkedTreeMap) { 144 | jo = (LinkedTreeMap) o; 145 | } else { 146 | // Otherwise, store the value in our ArrayList 147 | al.add(o.toString().replace(".", "-").replace(" ","-")); 148 | } 149 | } 150 | } 151 | } 152 | if(al.isEmpty()) { 153 | return returnString; 154 | } 155 | returnString = ""; 156 | Iterator it = al.iterator(); 157 | while (it.hasNext()) { 158 | String namePart = (String) it.next(); 159 | returnString += namePart; 160 | //Separate fields with dash 161 | if(it.hasNext()) { 162 | returnString += "-"; 163 | } 164 | } 165 | //Clean the return string to only include Alphanumeric characters and the dash character 166 | returnString = returnString.replaceAll("[^A-Za-z0-9-]",""); 167 | //set Max length to be 255 168 | if(returnString.length() > 255) { 169 | returnString = returnString.substring(0,255); 170 | } 171 | logger.debug("API Model Name: " + returnString); 172 | return returnString; 173 | } 174 | 175 | public String getTitle() { 176 | return info.get("title").toString(); 177 | } 178 | 179 | public String getDescription() { 180 | if (info.get("description") != null) { 181 | return info.get("description").toString(); 182 | } 183 | return ""; 184 | } 185 | 186 | @Override 187 | public String toString() { 188 | return "(SpecObject) Name: " + getName(null) + "; Title: " + getTitle(); 189 | } 190 | } 191 | 192 | 193 | public static class APIDocResponseObject { 194 | 195 | public JSONAPI jsonapi; 196 | public List data; 197 | public Object links; 198 | } 199 | 200 | public static class APIDocObject { 201 | public JSONAPI jsonapi; 202 | public Data data; 203 | public Object links; 204 | } 205 | 206 | public static class ImageDocObject { 207 | public JSONAPI jsonapi; 208 | public Data data; 209 | public Object links; 210 | } 211 | 212 | public static class APIErrorObject { 213 | public JSONAPI jsonapi; 214 | public List errors; 215 | } 216 | 217 | public static class JSONAPI { 218 | public String version; 219 | public Object meta; 220 | } 221 | 222 | public static class Error{ 223 | public String title; 224 | public String status; 225 | public String detail; 226 | } 227 | 228 | public static class Data { 229 | public String type; 230 | public String id; 231 | public Attributes attributes; 232 | public Relationships relationships; 233 | public Object links; 234 | } 235 | 236 | public static class Attributes { 237 | public boolean status; 238 | public String title; 239 | public String name; 240 | public Body body; 241 | public String field_apidoc_spec_file_source; 242 | public Object relationships; 243 | public FileLink file_link; 244 | public Object links; 245 | } 246 | 247 | public static class Body{ 248 | public String value; 249 | public String format; 250 | } 251 | 252 | public static class Relationships{ 253 | public Relationships_Spec field_apidoc_spec; 254 | public Relationships_Spec field_image; 255 | } 256 | 257 | public static class Relationships_Data{ 258 | public String type; 259 | public String id; 260 | } 261 | 262 | public static class Relationships_Spec{ 263 | public Relationships_Data data; 264 | } 265 | 266 | public static class FileLink{ 267 | public String uri; 268 | } 269 | 270 | // Get headers that have been set. 271 | public static HttpHeaders getHeaders() { 272 | return headers; 273 | } 274 | 275 | // Store/update headers. 276 | public static HttpHeaders setHeaders(HttpHeaders tempHeaders) { 277 | headers = tempHeaders; 278 | return headers; 279 | } 280 | 281 | /** 282 | * Authenticate against the Developer portal and set necessary headers to 283 | * facilitate additional transactions. 284 | */ 285 | public static HttpResponse authenticate(ServerProfile profile) throws IOException { 286 | HttpResponse response = null; 287 | if (headersSet == false) { 288 | String payload = "{\"username\": \"" + profile.getPortalUserName() 289 | + "\", \"password\":\"" + profile.getPortalPassword() + "\"}"; 290 | ByteArrayContent content = new ByteArrayContent("application/json", 291 | payload.getBytes()); 292 | 293 | HttpRequest restRequest = REQUEST_FACTORY 294 | .buildPostRequest(new GenericUrl( 295 | profile.getPortalURL() + "/" + profile.getPortalPath() 296 | + "/user/login.json"), content); 297 | restRequest.setReadTimeout(0); 298 | 299 | try { 300 | // Call execute directly since we don't have headers yet. 301 | response = restRequest.execute(); 302 | 303 | InputStream source = response.getContent(); //Get the data in the entity 304 | Reader reader = new InputStreamReader(source); 305 | 306 | Gson gson = new Gson(); 307 | AuthObject auth = gson.fromJson(reader, AuthObject.class); 308 | headersSet = true; 309 | 310 | HttpHeaders tempHeaders = new HttpHeaders(); 311 | tempHeaders.setCookie(auth.session_name + "=" + auth.sessid); 312 | tempHeaders.set("X-CSRF-Token", auth.token); 313 | setHeaders(tempHeaders); 314 | 315 | } catch (HttpResponseException e) { 316 | logger.error(e.getMessage()); 317 | // Throw an error as there is no point in continuing. 318 | throw e; 319 | } 320 | } 321 | 322 | return response; 323 | } 324 | 325 | 326 | /** 327 | * Run Cron 328 | */ 329 | public static void runCron(ServerProfile profile) throws IOException { 330 | try { 331 | // First authenticate. 332 | authenticate(profile); 333 | 334 | HttpRequest restRequest = REQUEST_FACTORY 335 | .buildGetRequest(new GenericUrl(profile.getPortalURL() + "/cron.php?cron_key="+ profile.getPortalCronKey())); 336 | restRequest.setReadTimeout(0); 337 | logger.info("Running Cron"); 338 | 339 | PortalRestUtil.executeRequest(restRequest); 340 | } catch (HttpResponseException e) { 341 | throw e; 342 | } 343 | } 344 | 345 | 346 | /** 347 | * Helper function to build the body for API Doc creations and updates. 348 | */ 349 | private static ByteArrayContent constructAPIDocRequestBody(ServerProfile profile, SpecObject spec, String uuid, String docId, String imageId, boolean isUpdate) throws IOException { 350 | boolean hasImage = false; 351 | boolean hasAPIProducts = false; 352 | File imageFile = null; 353 | List apiProducts = null; 354 | Gson gson = new Gson(); 355 | JsonObject body = new JsonObject(); 356 | if (spec.getDescription() != null) { 357 | body.addProperty("value", StringEscapeUtils.escapeJava(spec.getDescription())); 358 | } 359 | body.addProperty("format", profile.getPortalAPIDocFormat()); 360 | 361 | JsonObject attributes = new JsonObject(); 362 | attributes.addProperty("status", true); 363 | attributes.addProperty("title", spec.getTitle()); 364 | attributes.add("body", body); 365 | attributes.addProperty("field_apidoc_spec_file_source", "file"); 366 | 367 | JsonObject relationships = new JsonObject(); 368 | 369 | //config 370 | if(profile.getConfigFile()!=null && !profile.getConfigFile().equalsIgnoreCase("")) { 371 | FileContent tempFileContent = new FileContent("application/json", new File(profile.getConfigFile()).getAbsoluteFile()); 372 | Reader reader = new InputStreamReader(tempFileContent.getInputStream()); 373 | Map result = new ObjectMapper().readValue(reader, HashMap.class); 374 | if(result!=null && result.size()>0) { 375 | //For Custom Fields 376 | Map fieldsMap = (Map) result.get("fields"); 377 | if(fieldsMap!=null && fieldsMap.size()>0) { 378 | for (String key : fieldsMap.keySet()) { 379 | if (key!=null && key.equals("field_api_product")){ 380 | hasAPIProducts = true; 381 | apiProducts = new ArrayList(); 382 | apiProducts = (List)fieldsMap.get(key); 383 | } 384 | //no need to add to attributes for "field_image" 385 | else if (key!=null && key.equals("field_image")){ 386 | hasImage = true; 387 | imageFile = new File(profile.getPortalDirectory()+"/"+(String)fieldsMap.get(key)); 388 | } 389 | else if(fieldsMap.get(key) instanceof List){ 390 | attributes.add(key, gson.toJsonTree(fieldsMap.get(key))); 391 | } 392 | else 393 | attributes.addProperty(key, (String)fieldsMap.get(key)); 394 | } 395 | } 396 | 397 | //For Custom taxonomy_terms 398 | List> taxonomyTerm = (List>) result.get("taxonomy_terms"); 399 | if(taxonomyTerm!=null && taxonomyTerm.size()>0) { 400 | for (Map map : taxonomyTerm) { 401 | Map> taxonomyTermsIdMap = getTaxonomyTermId(profile, map); 402 | if(taxonomyTermsIdMap!=null && taxonomyTermsIdMap.size()>0) { 403 | JsonArray taxonomyData = new JsonArray(); 404 | for (String key : taxonomyTermsIdMap.keySet()) { 405 | List list = taxonomyTermsIdMap.get(key); 406 | if(list!=null && list.size()>0) { 407 | for (String id : taxonomyTermsIdMap.get(key)) { 408 | JsonObject taxonomyId = new JsonObject(); 409 | taxonomyId.addProperty("type", "taxonomy_term--"+(String)map.get("vocabulary")); 410 | taxonomyId.addProperty("id", id); 411 | taxonomyData.add(taxonomyId); 412 | } 413 | } 414 | JsonObject taxonomyDataObj = new JsonObject(); 415 | taxonomyDataObj.add("data", taxonomyData); 416 | relationships.add(key, taxonomyDataObj); 417 | } 418 | } 419 | } 420 | } 421 | } 422 | } 423 | JsonObject field_apidoc_spec_data = new JsonObject(); 424 | field_apidoc_spec_data.addProperty("type", "file--file"); 425 | if(!isUpdate) 426 | field_apidoc_spec_data.addProperty("id", uuid); 427 | else 428 | field_apidoc_spec_data.addProperty("id", docId); 429 | JsonObject field_apidoc_spec = new JsonObject(); 430 | field_apidoc_spec.add("data", field_apidoc_spec_data); 431 | 432 | relationships.add("field_apidoc_spec", field_apidoc_spec); 433 | 434 | //field_image 435 | if(hasImage) { 436 | JsonObject field_image_data = new JsonObject(); 437 | field_image_data.addProperty("type", "media--image"); 438 | if(!isUpdate) { 439 | ImageDocObject imageDoc = importImage(profile, imageFile); 440 | String mediaImageId = importMediaImage(profile, imageFile.getName(), imageDoc.data.id); 441 | field_image_data.addProperty("id", mediaImageId); 442 | } 443 | else { 444 | field_image_data.addProperty("id", imageId); 445 | } 446 | JsonObject field_image = new JsonObject(); 447 | field_image.add("data", field_image_data); 448 | 449 | relationships.add("field_image", field_image); 450 | } 451 | //field_api_product 452 | if(hasAPIProducts) { 453 | JsonArray productArray = new JsonArray(); 454 | for (int i = 0; i < apiProducts.size(); i++) { 455 | JsonObject product = new JsonObject(); 456 | product.addProperty("type", "api_product--api_product"); 457 | product.addProperty("id", apiProducts.get(i)); 458 | productArray.add(product); 459 | } 460 | JsonObject field_api_product = new JsonObject(); 461 | field_api_product.add("data", productArray); 462 | relationships.add("field_api_product", field_api_product); 463 | } 464 | 465 | JsonObject data = new JsonObject(); 466 | data.addProperty("type", "node--apidoc"); 467 | if(isUpdate) 468 | data.addProperty("id", uuid); //set this only for update call 469 | data.add("attributes", attributes); 470 | data.add("relationships", relationships); 471 | 472 | JsonObject payloadData = new JsonObject(); 473 | payloadData.add("data", data); 474 | 475 | String payload = gson.toJson(payloadData); 476 | 477 | logger.debug("Request payload: \n" + payload); 478 | ByteArrayContent content = new ByteArrayContent("application/vnd.api+json", payload.getBytes()); 479 | return content; 480 | } 481 | 482 | /** 483 | * Posts the OpenAPI Spec to a APIDoc in Developer Portal. 484 | */ 485 | public static void postAPIDoc(ServerProfile profile, File file, boolean isCreate) throws IOException { 486 | try { 487 | APIDocResponseObject respObj = getAPIDoc(profile, file); 488 | if (respObj == null) { 489 | APIDocObject obj = importAPIDoc(profile, file); 490 | if(obj == null) 491 | throw new IOException("Error occured while importing spec"); 492 | else 493 | createAPIDoc(profile, file, obj); 494 | } else { 495 | if(isCreate) { 496 | logger.info("Skipping as the spec already exist. Please use \"sync\" or \"update\" options"); 497 | return; 498 | } 499 | updateAPIDoc(profile, file, respObj); 500 | } 501 | } catch (HttpResponseException e) { 502 | logger.error(e.getMessage()); 503 | throw e; 504 | } 505 | } 506 | 507 | /** 508 | * Get the Taxonomy Id 509 | * @param profile 510 | * @param taxonomyTermsMap 511 | * @return 512 | * @throws IOException 513 | */ 514 | public static Map> getTaxonomyTermId(ServerProfile profile, Map taxonomyTermsMap) throws IOException{ 515 | Map> taxonomyTermIdMap = new HashMap>(); 516 | try{ 517 | String vocabulary = (String) taxonomyTermsMap.get("vocabulary"); 518 | String field = (String) taxonomyTermsMap.get("field"); 519 | List dataList = (List) taxonomyTermsMap.get("data"); 520 | if(vocabulary!=null && !vocabulary.equals("")) { 521 | logger.info("Retrieving taxonomy_term for " + vocabulary); 522 | HttpRequest restRequest = REQUEST_FACTORY 523 | .buildGetRequest(new GenericUrl(profile.getPortalURL() + "/jsonapi/taxonomy_term/"+vocabulary)); 524 | HttpHeaders headers = restRequest.getHeaders(); 525 | headers.setAccept("application/vnd.api+json"); 526 | headers.setBasicAuthentication(profile.getPortalUserName(), profile.getPortalPassword()); 527 | restRequest.setReadTimeout(0); 528 | HttpResponse response = restRequest.execute(); 529 | Gson gson = new Gson(); 530 | Reader reader = new InputStreamReader(response.getContent()); 531 | APIDocResponseObject model = gson.fromJson(reader, APIDocResponseObject.class); 532 | List idList = new ArrayList(); 533 | List termList = new ArrayList(); 534 | for (Data data : model.data) { 535 | termList.add(data.attributes.name); 536 | if(dataList.contains(data.attributes.name)) { 537 | idList.add(data.id); 538 | } 539 | } 540 | dataList.removeAll(termList); 541 | //Throw exception if the taxonomy terms from the config file does not exist 542 | if(dataList!=null && dataList.size()>0) { 543 | throw new IOException("Terms "+ dataList +" does not exist"); 544 | } 545 | taxonomyTermIdMap.put(field, idList); 546 | } 547 | }catch (HttpResponseException e) { 548 | throw new IOException(e.getStatusMessage()); 549 | } 550 | return taxonomyTermIdMap; 551 | } 552 | 553 | /** 554 | * Retrieve an existing API Doc. Returns null if the doc does not exist 555 | */ 556 | public static APIDocResponseObject getAPIDoc(ServerProfile profile, File file) throws IOException { 557 | HttpResponse response = null; 558 | try { 559 | SpecObject spec = parseSpec(profile, file); 560 | logger.info("Getting API doc for "+ spec.getTitle()); 561 | HttpRequest restRequest = REQUEST_FACTORY 562 | .buildGetRequest(new GenericUrl(profile.getPortalURL() + "/jsonapi/node/apidoc?filter[title]=" + spec.getTitle())); 563 | HttpHeaders headers = restRequest.getHeaders(); 564 | headers.setAccept("application/vnd.api+json"); 565 | headers.setBasicAuthentication(profile.getPortalUserName(), profile.getPortalPassword()); 566 | logger.info("Retrieving " + spec.getTitle() + " doc."); 567 | restRequest.setReadTimeout(0); 568 | response = restRequest.execute(); 569 | Gson gson = new Gson(); 570 | Reader reader = new InputStreamReader(response.getContent()); 571 | APIDocResponseObject model = gson.fromJson(reader, APIDocResponseObject.class); 572 | if(model != null && model.data!=null && model.data.size()>0) { 573 | logger.info("API Doc uuid:" + model.data.get(0).id); 574 | return model; 575 | } else { 576 | logger.info("API Doc: "+ spec.getTitle()+" does not exist"); 577 | return null; 578 | } 579 | 580 | } catch (HttpResponseException e) { 581 | throw e; 582 | } 583 | } 584 | 585 | /** 586 | * Import an API Doc 587 | */ 588 | public static APIDocObject importAPIDoc(ServerProfile profile, File file) throws IOException { 589 | HttpResponse response = null; 590 | try { 591 | SpecObject spec = parseSpec(profile, file); 592 | 593 | logger.info("Importing spec.."); 594 | byte[] fileBytes = Files.readAllBytes(file.toPath()); 595 | ByteArrayContent fileContent = new ByteArrayContent("application/octet-stream", fileBytes); 596 | HttpRequest restRequest = REQUEST_FACTORY 597 | .buildPostRequest(new GenericUrl(profile.getPortalURL() + "/jsonapi/node/apidoc/field_apidoc_spec"), fileContent); 598 | HttpHeaders headers = restRequest.getHeaders(); 599 | headers.setAccept("application/vnd.api+json"); 600 | headers.set("Content-Disposition", "file; filename=\""+spec.getTitle()+"."+profile.getPortalFormat()+"\""); 601 | headers.setBasicAuthentication(profile.getPortalUserName(), profile.getPortalPassword()); 602 | restRequest.setReadTimeout(0); 603 | response = restRequest.execute(); 604 | logger.info("Spec import complete.."); 605 | Gson gson = new Gson(); 606 | Reader reader = new InputStreamReader(response.getContent()); 607 | APIDocObject model = gson.fromJson(reader, APIDocObject.class); 608 | if(model != null && model.data!=null) { 609 | logger.info("File uuid:" + model.data.id); 610 | return model; 611 | } 612 | return null; 613 | 614 | } catch (HttpResponseException e) { 615 | throw new IOException(exceptionHandler(e)); 616 | } 617 | } 618 | 619 | /** 620 | * Import an image 621 | */ 622 | public static ImageDocObject importImage(ServerProfile profile, File imageFile) throws IOException { 623 | HttpResponse response = null; 624 | try { 625 | logger.info("Importing image.."); 626 | byte[] fileBytes = Files.readAllBytes(imageFile.toPath()); 627 | ByteArrayContent fileContent = new ByteArrayContent("application/octet-stream", fileBytes); 628 | HttpRequest restRequest = REQUEST_FACTORY 629 | .buildPostRequest(new GenericUrl(profile.getPortalURL() + "/jsonapi/media/image/field_media_image"), fileContent); 630 | HttpHeaders headers = restRequest.getHeaders(); 631 | headers.setAccept("application/vnd.api+json"); 632 | headers.set("Content-Disposition", "file; filename=\""+imageFile.getName()+"\""); 633 | headers.setBasicAuthentication(profile.getPortalUserName(), profile.getPortalPassword()); 634 | restRequest.setReadTimeout(0); 635 | response = restRequest.execute(); 636 | logger.info("Image import complete.."); 637 | Gson gson = new Gson(); 638 | Reader reader = new InputStreamReader(response.getContent()); 639 | 640 | ImageDocObject model = gson.fromJson(reader, ImageDocObject.class); 641 | if(model != null && model.data!=null) { 642 | logger.info("Image uuid:" + model.data.id); 643 | return model; 644 | } 645 | return null; 646 | 647 | } catch (HttpResponseException e) { 648 | throw new IOException(exceptionHandler(e)); 649 | } 650 | } 651 | 652 | /** 653 | * Configure media-image 654 | */ 655 | 656 | public static String importMediaImage(ServerProfile profile, String fileName, String imageId) throws IOException { 657 | HttpResponse response = null; 658 | try { 659 | String payload = "{\n" 660 | + " \"data\": {\n" 661 | + " \"type\": \"media--image\",\n" 662 | + " \"attributes\": {\n" 663 | + " \"name\": \""+fileName+"\"\n" 664 | + " },\n" 665 | + " \"relationships\": {\n" 666 | + " \"field_media_image\": {\n" 667 | + " \"data\": {\n" 668 | + " \"type\": \"file--file\",\n" 669 | + " \"id\": \""+imageId+"\"\n" 670 | + " }\n" 671 | + " }\n" 672 | + " }\n" 673 | + " }\n" 674 | + "}"; 675 | ByteArrayContent content = new ByteArrayContent("application/vnd.api+json", payload.getBytes()); 676 | HttpRequest restRequest = REQUEST_FACTORY 677 | .buildPostRequest(new GenericUrl(profile.getPortalURL() + "/jsonapi/media/image"), content); 678 | HttpHeaders headers = restRequest.getHeaders(); 679 | headers.setAccept("application/vnd.api+json"); 680 | headers.setBasicAuthentication(profile.getPortalUserName(), profile.getPortalPassword()); 681 | restRequest.setReadTimeout(0); 682 | response = restRequest.execute(); 683 | Gson gson = new Gson(); 684 | Reader reader = new InputStreamReader(response.getContent()); 685 | ImageDocObject model = gson.fromJson(reader, ImageDocObject.class); 686 | if(model != null && model.data!=null) { 687 | logger.info("media--image uuid:" + model.data.id); 688 | return model.data.id; 689 | } 690 | } 691 | catch (HttpResponseException e) { 692 | throw new IOException(exceptionHandler(e)); 693 | } 694 | return null; 695 | } 696 | 697 | /** 698 | * Import an API Doc 699 | */ 700 | public static void updateAPIDoc(ServerProfile profile, File file, APIDocResponseObject doc) throws IOException { 701 | try { 702 | SpecObject spec = parseSpec(profile, file); 703 | 704 | logger.info("Update API catalog"); 705 | String uuid = (doc.data.get(0).id != null)?doc.data.get(0).id:null; 706 | String docId = (doc.data.get(0).relationships.field_apidoc_spec!=null && doc.data.get(0).relationships.field_apidoc_spec.data!=null && doc.data.get(0).relationships.field_apidoc_spec.data.id!=null)?doc.data.get(0).relationships.field_apidoc_spec.data.id:null; 707 | String imageId = (doc.data.get(0).relationships.field_image!=null && doc.data.get(0).relationships.field_image.data!=null && doc.data.get(0).relationships.field_image.data.id!=null)?doc.data.get(0).relationships.field_image.data.id:null; 708 | ByteArrayContent content = constructAPIDocRequestBody(profile, spec, uuid, docId, imageId, true); 709 | HttpRequest restPatchRequest = APACHE_REQUEST_FACTORY.buildRequest(HttpMethods.PATCH, new GenericUrl(profile.getPortalURL() + "/jsonapi/node/apidoc/"+doc.data.get(0).id), 710 | content); 711 | HttpHeaders patchHeaders = restPatchRequest.getHeaders(); 712 | patchHeaders.setAccept("application/vnd.api+json"); 713 | patchHeaders.setBasicAuthentication(profile.getPortalUserName(), profile.getPortalPassword()); 714 | restPatchRequest.setReadTimeout(0); 715 | restPatchRequest.execute(); 716 | 717 | logger.info("Update API Doc.."); 718 | byte[] fileBytes = Files.readAllBytes(file.toPath()); 719 | ByteArrayContent fileContent = new ByteArrayContent("application/octet-stream", fileBytes); 720 | HttpRequest restRequest = REQUEST_FACTORY 721 | .buildPostRequest(new GenericUrl(profile.getPortalURL() + "/jsonapi/node/apidoc/"+doc.data.get(0).id+"/field_apidoc_spec"), fileContent); 722 | HttpHeaders headers = restRequest.getHeaders(); 723 | headers.setAccept("application/vnd.api+json"); 724 | headers.set("Content-Disposition", "file; filename=\""+spec.getTitle()+"."+profile.getPortalFormat()+"\""); 725 | headers.setBasicAuthentication(profile.getPortalUserName(), profile.getPortalPassword()); 726 | restRequest.setReadTimeout(0); 727 | restRequest.execute(); 728 | logger.info("Update API Doc complete.."); 729 | } catch (HttpResponseException e) { 730 | throw new IOException(exceptionHandler(e)); 731 | } 732 | } 733 | 734 | /** 735 | * Create an API Doc 736 | */ 737 | public static String createAPIDoc(ServerProfile profile, File file, APIDocObject doc) throws IOException { 738 | HttpResponse response = null; 739 | try { 740 | SpecObject spec = parseSpec(profile, file); 741 | 742 | logger.info("Creating spec.." + doc.data.id); 743 | ByteArrayContent content = constructAPIDocRequestBody(profile, spec, doc.data.id, null, null, false); 744 | HttpRequest restRequest = REQUEST_FACTORY 745 | .buildPostRequest(new GenericUrl(profile.getPortalURL() + "/jsonapi/node/apidoc"), content); 746 | HttpHeaders headers = restRequest.getHeaders(); 747 | headers.setAccept("application/vnd.api+json"); 748 | headers.setBasicAuthentication(profile.getPortalUserName(), profile.getPortalPassword()); 749 | restRequest.setReadTimeout(0); 750 | response = restRequest.execute(); 751 | Gson gson = new Gson(); 752 | Reader reader = new InputStreamReader(response.getContent()); 753 | APIDocObject model = gson.fromJson(reader, APIDocObject.class); 754 | logger.info("API Doc: " + spec.getTitle()+ " created"); 755 | if(model != null && model.data!=null) { 756 | logger.info("API Doc uuid:" + model.data.id); 757 | return model.data.id; 758 | } 759 | return null; 760 | 761 | } catch (HttpResponseException e) { 762 | throw new IOException(exceptionHandler(e)); 763 | } 764 | } 765 | 766 | /** 767 | * Delete an API Doc 768 | */ 769 | public static void deleteAPIDoc(ServerProfile profile, File file) throws IOException { 770 | try { 771 | APIDocResponseObject respObj = getAPIDoc(profile, file); 772 | if (respObj != null) { 773 | logger.info("Delete API Doc.."); 774 | HttpRequest restRequest = REQUEST_FACTORY 775 | .buildDeleteRequest(new GenericUrl(profile.getPortalURL() + "/jsonapi/node/apidoc/"+respObj.data.get(0).id)); 776 | HttpHeaders headers = restRequest.getHeaders(); 777 | headers.setAccept("application/vnd.api+json"); 778 | headers.setBasicAuthentication(profile.getPortalUserName(), profile.getPortalPassword()); 779 | restRequest.setReadTimeout(0); 780 | HttpResponse response = restRequest.execute(); 781 | logger.info("Delete API Doc complete.."); 782 | } 783 | } catch (HttpResponseException e) { 784 | throw new IOException(exceptionHandler(e)); 785 | } 786 | } 787 | 788 | 789 | /** 790 | * Helper function to parse a json formatted OpenAPI spec and turn it into an 791 | * object containing the properties we will reuse. 792 | */ 793 | public static SpecObject parseSpec(ServerProfile profile, File file) throws FileNotFoundException { 794 | 795 | SpecObject spec = new SpecObject(); 796 | try { 797 | Reader reader = null; 798 | if (profile.getPortalFormat().equals("yaml")) { 799 | 800 | // Read in the YAML file. 801 | FileContent tempFileContent = new FileContent("application/x-yaml", file); 802 | Reader yamlReader = new InputStreamReader(tempFileContent.getInputStream()); 803 | Yaml yaml = new Yaml(); 804 | Object obj = yaml.load(yamlReader); 805 | 806 | // Convert it to a JSON file for consistent handling. 807 | String YAMLString = JSONValue.toJSONString(obj); 808 | reader = new StringReader(YAMLString); 809 | } 810 | else { 811 | FileContent tempFileContent = new FileContent("application/json", file); 812 | reader = new InputStreamReader(tempFileContent.getInputStream()); 813 | } 814 | Gson gson = new Gson(); 815 | spec = gson.fromJson(reader, SpecObject.class); 816 | } 817 | catch (IOException e) { 818 | logger.error(e.getMessage()); 819 | } 820 | 821 | return spec; 822 | } 823 | 824 | public static HttpResponse executeRequest(HttpRequest restRequest) throws IOException { 825 | HttpResponse response = null; 826 | try { 827 | restRequest.setHeaders(getHeaders()); 828 | restRequest.setReadTimeout(0); 829 | response = restRequest.execute(); 830 | logger.info(response.getStatusMessage()); 831 | } catch (HttpResponseException e) { 832 | throw e; 833 | } 834 | return response; 835 | } 836 | 837 | public static String exceptionHandler(HttpResponseException hre) { 838 | Gson gson = new Gson(); 839 | APIErrorObject errorObj = gson.fromJson(hre.getContent(), APIErrorObject.class); 840 | if(errorObj!=null && errorObj.errors!=null && errorObj.errors.size()>0){ 841 | String errorMsg = "\nStatus code: "+ errorObj.errors.get(0).status +"\n"; 842 | errorMsg = errorMsg + "Status Message: " + errorObj.errors.get(0).title +"\n"; 843 | errorMsg = errorMsg + "Detailed Message: " + errorObj.errors.get(0).detail +"\n"; 844 | return errorMsg; 845 | } 846 | return ""; 847 | } 848 | 849 | } 850 | -------------------------------------------------------------------------------- /src/main/java/com/apigee/smartdocs/config/rest/XTrustProvider.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2017 Google Inc. 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 com.apigee.smartdocs.config.rest; 18 | 19 | import java.security.AccessController; 20 | import java.security.InvalidAlgorithmParameterException; 21 | import java.security.KeyStore; 22 | import java.security.KeyStoreException; 23 | import java.security.PrivilegedAction; 24 | import java.security.Security; 25 | import java.security.cert.X509Certificate; 26 | 27 | import javax.net.ssl.ManagerFactoryParameters; 28 | import javax.net.ssl.SSLSession; 29 | import javax.net.ssl.TrustManager; 30 | import javax.net.ssl.TrustManagerFactorySpi; 31 | import javax.net.ssl.X509TrustManager; 32 | 33 | public final class XTrustProvider extends java.security.Provider 34 | { 35 | private final static String NAME = "XTrustJSSE"; 36 | private final static String INFO = 37 | "XTrust JSSE Provider (implements trust factory with truststore validation disabled)"; 38 | private final static double VERSION = 1.0D; 39 | 40 | public XTrustProvider() 41 | { 42 | super(NAME, VERSION, INFO); 43 | 44 | AccessController.doPrivileged(new PrivilegedAction() 45 | { 46 | public Object run() 47 | { 48 | put("TrustManagerFactory." + TrustManagerFactoryImpl.getAlgorithm(), 49 | TrustManagerFactoryImpl.class.getName()); 50 | return null; 51 | } 52 | }); 53 | } 54 | 55 | public static void install() 56 | { 57 | if(Security.getProvider(NAME) == null) 58 | { 59 | Security.insertProviderAt(new XTrustProvider(), 2); 60 | Security.setProperty("ssl.TrustManagerFactory.algorithm",TrustManagerFactoryImpl.getAlgorithm()); 61 | } 62 | } 63 | 64 | public final static class TrustManagerFactoryImpl extends TrustManagerFactorySpi 65 | { 66 | public TrustManagerFactoryImpl() { } 67 | public static String getAlgorithm() { return "XTrust509"; } 68 | protected void engineInit(KeyStore keystore) throws KeyStoreException { } 69 | protected void engineInit(ManagerFactoryParameters mgrparams) 70 | throws InvalidAlgorithmParameterException 71 | { 72 | throw new InvalidAlgorithmParameterException( 73 | XTrustProvider.NAME + " does not use ManagerFactoryParameters"); 74 | } 75 | 76 | protected TrustManager[] engineGetTrustManagers() 77 | { 78 | return new TrustManager[] { new X509TrustManager() 79 | { 80 | public X509Certificate[] getAcceptedIssuers() { return null; } 81 | public boolean verify(String arg0, SSLSession arg1) { return true; } 82 | public void checkClientTrusted(X509Certificate[] certs, String authType) { } 83 | public void checkServerTrusted(X509Certificate[] certs, String authType) { } 84 | }}; 85 | } 86 | } 87 | } -------------------------------------------------------------------------------- /src/main/java/com/apigee/smartdocs/config/utils/PortalField.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2017 Google Inc. 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 com.apigee.smartdocs.config.utils; 18 | 19 | /** 20 | * A portal field represents a config element with path and field mapping. 21 | * 22 | * @author william.oconnor 23 | */ 24 | public class PortalField { 25 | 26 | private String path; 27 | private String field; 28 | 29 | public void setPath(String pathName) { 30 | this.path = pathName; 31 | } 32 | 33 | public String getPath() { 34 | return this.path; 35 | } 36 | 37 | public void setField(String fieldName) { 38 | this.field = fieldName; 39 | } 40 | 41 | public String getField() { 42 | return this.field; 43 | } 44 | 45 | @Override 46 | public String toString() { 47 | return "PortalField: " + path + " --> " + field; 48 | } // to test 49 | } 50 | -------------------------------------------------------------------------------- /src/main/java/com/apigee/smartdocs/config/utils/PrintUtil.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2017 Google Inc. 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 com.apigee.smartdocs.config.utils; 18 | 19 | import com.google.api.client.http.HttpHeaders; 20 | import com.google.api.client.http.HttpMethods; 21 | import com.google.api.client.http.HttpRequest; 22 | import com.google.api.client.http.HttpResponse; 23 | 24 | import java.io.ByteArrayOutputStream; 25 | import java.util.Iterator; 26 | import java.util.Set; 27 | 28 | 29 | public class PrintUtil { 30 | 31 | public static String formatRequest(HttpRequest request) { 32 | 33 | String prettyRequest = "\n\n\nRequest prepared for the server \n **************************\n"; 34 | 35 | // Print all headers except auth 36 | 37 | prettyRequest = prettyRequest + request.getRequestMethod() + " " + request.getUrl(); 38 | 39 | HttpHeaders headers = request.getHeaders(); 40 | 41 | Set tempheadersmap = headers.keySet(); 42 | 43 | for (Iterator iter = tempheadersmap.iterator(); iter.hasNext(); ) { 44 | 45 | try { 46 | String headerkey = iter.next(); 47 | if (!headerkey.trim().equalsIgnoreCase("Authorization")) { 48 | String headervalue = ""+headers.get(headerkey); 49 | prettyRequest = prettyRequest + "\n" + headerkey + ": " + headervalue; 50 | }else { 51 | String headervalue = ""+headers.get(headerkey); 52 | String arr[] = headervalue.split(" ", 2); 53 | String prefix = arr[0]; // Basic, Bearer 54 | prettyRequest = prettyRequest + "\n" + 55 | "authorization" + ": " + prefix + " [Not shown in log]"; 56 | } 57 | } catch (Exception e) { 58 | e.printStackTrace(); 59 | } 60 | 61 | } 62 | 63 | try { 64 | if (request.getRequestMethod().compareTo(HttpMethods.POST) == 0 ){ 65 | 66 | if (request.getContent()!=null && request.getContent().getType() !=null) 67 | { 68 | prettyRequest = prettyRequest + "\n" + "content-type" + ": " + request.getContent().getType(); 69 | 70 | if (!request.getContent().getType().contains("octet")) 71 | { 72 | ByteArrayOutputStream out = new ByteArrayOutputStream(); 73 | request.getContent().writeTo(out); 74 | prettyRequest = prettyRequest + "\n [Request body]\n" + out.toString(); 75 | } else { 76 | prettyRequest = prettyRequest + "\n [Request body contains data, not shown] \n"; 77 | } 78 | } 79 | 80 | } 81 | }catch (Exception e){ 82 | e.printStackTrace(); 83 | } 84 | 85 | return prettyRequest; 86 | } 87 | 88 | 89 | public static String formatResponse(HttpResponse response, String body) { 90 | 91 | String prettyString = "\n\n\nResponse returned by the server \n **************************\n"; 92 | 93 | // Print all headers except auth 94 | 95 | prettyString = prettyString + response.getStatusCode() + " " + response.getStatusMessage(); 96 | 97 | HttpHeaders headers = response.getHeaders(); 98 | 99 | Set tempheadersmap = headers.keySet(); 100 | 101 | for (Iterator iter = tempheadersmap.iterator(); iter.hasNext(); ) { 102 | 103 | try { 104 | String headerkey = iter.next(); 105 | if (!headerkey.trim().equalsIgnoreCase("Authorization")) { 106 | String headervalue = ""+headers.get(headerkey); 107 | prettyString = prettyString + "\n" + headerkey + ": " + headervalue; 108 | } 109 | 110 | } catch (Exception e) { 111 | e.printStackTrace(); 112 | } 113 | 114 | } 115 | 116 | // print response body 117 | prettyString = prettyString + "\n" + body; 118 | 119 | return prettyString; 120 | } 121 | } -------------------------------------------------------------------------------- /src/main/java/com/apigee/smartdocs/config/utils/ServerProfile.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2017 Google Inc. 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 com.apigee.smartdocs.config.utils; 18 | 19 | import java.util.Map; 20 | 21 | public class ServerProfile { 22 | 23 | private String options; 24 | private String configFile; 25 | 26 | // Portal Parameters 27 | private String portalUserName; // Developer Portal Username 28 | private String portalPassword; // Developer Portal Password 29 | private String portalDirectory; // Directory holding OpenAPI specs 30 | private String portalURL; // Developer Portal URL 31 | private String portalPath; // Developer Portal REST base path 32 | private String portalFormat; // OpenAPI spec format 33 | private String portalModelVocabulary; // Model Vocabulary 34 | private String portalCronKey; // Dev portal Cron Key 35 | private String portalModelNameConfig; // OPTIONAL configuration for Model Name 36 | private String portalAPIDocFormat; // Dev Portal API Doc Format 37 | private Map portalModelFields; // OpenAPI spec format 38 | 39 | /** 40 | * @return the portalUser 41 | */ 42 | public String getPortalUserName() { 43 | return portalUserName; 44 | } 45 | 46 | /** 47 | * @param portalUserName the portalUserName to set 48 | */ 49 | public void setPortalUserName(String portalUserName) { 50 | this.portalUserName = portalUserName; 51 | } 52 | 53 | /** 54 | * @return the portalPassword 55 | */ 56 | public String getPortalPassword() { 57 | return portalPassword; 58 | } 59 | 60 | /** 61 | * @param portalPassword the portalPassword to set 62 | */ 63 | public void setPortalPassword(String portalPassword) { 64 | this.portalPassword = portalPassword; 65 | } 66 | 67 | /** 68 | * @return the portalDirectory 69 | */ 70 | public String getPortalDirectory() { 71 | return portalDirectory; 72 | } 73 | 74 | /** 75 | * @param portalDirectory the portalDirectory to set 76 | */ 77 | public void setPortalDirectory(String portalDirectory) { 78 | this.portalDirectory = portalDirectory; 79 | } 80 | 81 | /** 82 | * @return the portalURL 83 | */ 84 | public String getPortalURL() { 85 | return portalURL; 86 | } 87 | 88 | /** 89 | * @param portalURL the portalURL to set 90 | */ 91 | public void setPortalURL(String portalURL) { 92 | this.portalURL = portalURL; 93 | } 94 | 95 | /** 96 | * @return the portalPath 97 | */ 98 | public String getPortalPath() { 99 | return portalPath; 100 | } 101 | 102 | /** 103 | * @param portalPath the portalPath to set 104 | */ 105 | public void setPortalPath(String portalPath) { 106 | this.portalPath = portalPath; 107 | } 108 | 109 | /** 110 | * @return the portalFormat 111 | */ 112 | public String getPortalFormat() { 113 | return portalFormat; 114 | } 115 | 116 | /** 117 | * @param portalFormat the portalFormat to set 118 | */ 119 | public void setPortalFormat(String portalFormat) { 120 | this.portalFormat = portalFormat; 121 | } 122 | 123 | /** 124 | * @param portalModelVocabulary the portalModelVocabulary to set 125 | */ 126 | public void setPortalModelVocabulary(String portalModelVocabulary) { 127 | this.portalModelVocabulary = portalModelVocabulary; 128 | } 129 | 130 | /** 131 | * @return the portalModelVocabulary 132 | */ 133 | public String getPortalModelVocabulary() { 134 | return portalModelVocabulary; 135 | } 136 | 137 | /** 138 | * @param portalCronKey the portalCronKey to set 139 | */ 140 | public void setPortalCronKey(String portalCronKey) { 141 | this.portalCronKey = portalCronKey; 142 | } 143 | 144 | /** 145 | * @return the portalCronKey 146 | */ 147 | public String getPortalCronKey() { 148 | return portalCronKey; 149 | } 150 | 151 | /** 152 | * @return the portalModelFields 153 | */ 154 | public Map getPortalModelFields() { 155 | return portalModelFields; 156 | } 157 | 158 | /** 159 | * @param portalModelFields the portalModelFields to set 160 | */ 161 | public void setPortalModelFields(Map portalModelFields) { 162 | this.portalModelFields = portalModelFields; 163 | } 164 | 165 | /** 166 | * @param configFile the options to set 167 | */ 168 | public String getConfigFile() { 169 | return configFile; 170 | } 171 | 172 | public void setConfigFile(String configFile) { 173 | this.configFile = configFile; 174 | } 175 | 176 | /** 177 | * @param options the options to set 178 | */ 179 | public String getOptions() { 180 | return options; 181 | } 182 | 183 | public void setOptions(String options) { 184 | this.options = options; 185 | } 186 | 187 | /** 188 | * @return the PortalModelNameConfig 189 | */ 190 | public String getPortalModelNameConfig() { 191 | return portalModelNameConfig; 192 | } 193 | 194 | /** 195 | * @param portalModelNameConfig the portalModelNameConfig to set 196 | */ 197 | public void setPortalModelNameConfig(String portalModelNameConfig) { 198 | this.portalModelNameConfig = portalModelNameConfig; 199 | } 200 | 201 | /** 202 | * @return the portalAPIDocFormat 203 | */ 204 | public String getPortalAPIDocFormat() { 205 | return portalAPIDocFormat; 206 | } 207 | 208 | /** 209 | * @param portalAPIDocFormat the portalAPIDocFormat to set 210 | */ 211 | public void setPortalAPIDocFormat(String portalAPIDocFormat) { 212 | this.portalAPIDocFormat = portalAPIDocFormat; 213 | } 214 | } 215 | -------------------------------------------------------------------------------- /src/main/resources/log4j2.properties: -------------------------------------------------------------------------------- 1 | status = INFO 2 | name = ConsoleExample 3 | #packages = com.apigee 4 | 5 | appender.console.type = Console 6 | appender.console.name = STDOUT 7 | appender.console.layout.type = PatternLayout 8 | #appender.console.layout.pattern = %d{yyyy-MM-dd HH:mm:ss} %-5p %c{1}:%L - %m%n 9 | 10 | rootLogger.level = info 11 | rootLogger.appenderRef.stdout.ref = STDOUT --------------------------------------------------------------------------------