├── .gitignore ├── .gitmodules ├── .travis.yml ├── Gruntfile.js ├── LICENSE ├── README.md ├── apiproxy ├── forecastweather-grunt-plugin-api.xml ├── policies │ ├── Assign-Message-Remove-Suffix.xml │ ├── JSON-to-XML.xml │ ├── Script-External-Variables.xml │ ├── SimpleJavaCallout.xml.bak │ └── XMLtoJSON-Common.xml ├── proxies │ └── default.xml ├── resources │ └── jsc │ │ ├── js_external_vars.js │ │ ├── search-and-replace-example.js │ │ └── search-and-replace-example2.js └── targets │ ├── default.xml │ └── node.xml ├── config └── kvm │ └── testmyapi │ ├── passwords.json │ ├── passwords.json.encrypted │ ├── testmyapi-prod │ ├── passwords.json │ ├── passwords.json.encrypted │ ├── passwords2.json.encrypted │ ├── targets.json │ └── targets.json.encrypted │ └── testmyapi-test │ ├── passwords.json │ ├── passwords.json.encrypted │ ├── targets.json │ └── targets.json.encrypted ├── grunt ├── apigee-config.js ├── conf │ ├── eslint.json │ └── rules │ │ └── if-curly-formatting.js ├── lib │ └── helper-functions.js ├── search-and-replace-files.js └── tasks │ ├── apigeeGruntPluginBanner.js │ ├── compressAlias.js │ ├── deleteApiRevision.js │ ├── deployApiRevision.js │ ├── deployApiRevisionAlias.js │ ├── executeTests.js │ ├── force.js │ ├── getAllApiRevisions.js │ ├── getDeployedApiRevisions.js │ ├── installNpmRevision.js │ ├── installNpmRevisionAlias.js │ ├── saveGitRevision.js │ ├── undeployApiRevision.js │ └── updateApiRevision.js ├── java ├── lib │ ├── expressions-1.0.0.jar │ └── message-flow-1.0.0.jar └── src │ └── com │ └── example │ └── SimpleJavaCallout.java ├── node ├── .gitignore ├── app.js ├── node_modules │ └── path │ │ ├── package.json │ │ └── path.js ├── package.json ├── public │ ├── images │ │ └── tree.jpg │ └── tree.jpg └── resources │ └── forecastweather_node.js ├── package.json ├── tests ├── diego-forecastweather-grunt-plugin-api-test-data.js ├── forecastweather-grunt-plugin-api-prod-data.js ├── forecastweather-grunt-plugin-api-test-data.js └── forecastweather-grunt-plugin-api.js └── tools ├── CommonProxy └── apiproxy │ ├── policies │ ├── Script-External-Variables.xml │ └── xmltojson-1.xml │ ├── proxies │ ├── common-js-vars.flowfrag │ └── xmltojson.flowfrag │ └── resources │ └── jsc │ └── js_external_vars.js ├── forecastweather-jmeter-example ├── README.md ├── pom.xml └── tests │ ├── weather.jmx │ ├── weather_prod.csv │ └── weather_test.csv └── proxy-dependency-maven-plugin ├── CommonProxy └── apiproxy │ ├── policies │ ├── Script-External-Variables.xml │ └── xmltojson-1.xml │ ├── proxies │ ├── common-js-vars.flowfrag │ └── xmltojson.flowfrag │ └── resources │ └── jsc │ └── js_external_vars.js ├── Gruntfile.js ├── README.md ├── apiproxy ├── forecastweather-grunt-plugin-api.xml ├── policies │ ├── Assign-Message-Remove-Suffix.xml │ ├── JSON-to-XML.xml │ ├── Script-External-Variables.xml │ ├── SimpleJavaCallout.xml.bak │ └── XMLtoJSON-Common.xml ├── proxies │ └── default.xml ├── resources │ └── jsc │ │ ├── js_external_vars.js │ │ ├── search-and-replace-example.js │ │ └── search-and-replace-example2.js └── targets │ ├── default.xml │ └── node.xml └── pom.xml /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | /node_modules 3 | target/ 4 | report.xml 5 | checkstyle.xml 6 | results.html 7 | results.txt 8 | /config/kvm/**/*.decrypted 9 | /config/kvm/**/*.json -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "common"] 2 | path = common 3 | url = https://github.com/apigeecs/apigee-deploy-grunt-plugin.git 4 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - "0.10" 4 | before_install: npm install -g grunt-cli 5 | install: npm install 6 | # step pending until Apigee credentials are encrypted in Travis by following these steps: http://docs.travis-ci.com/user/encryption-keys/ 7 | before_script: grunt default -env=test -username=$ae_username -password=$ae_password -debug --upload-modules 8 | # Travis will look for scripts in the package.json, there I indicated the test step should run grunt mochaTest -env=test 9 | # based on answer from SO http://stackoverflow.com/questions/21128478/run-grunt-build-command-on-travis-ci 10 | -------------------------------------------------------------------------------- /Gruntfile.js: -------------------------------------------------------------------------------- 1 | /*jslint node: true */ 2 | 3 | /************************************************************************ 4 | The MIT License (MIT) 5 | 6 | Copyright (c) 2014 7 | 8 | Permission is hereby granted, free of charge, to any person obtaining a copy 9 | of this software and associated documentation files (the "Software"), to deal 10 | in the Software without restriction, including without limitation the rights 11 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | copies of the Software, and to permit persons to whom the Software is 13 | furnished to do so, subject to the following conditions: 14 | 15 | The above copyright notice and this permission notice shall be included in all 16 | copies or substantial portions of the Software. 17 | 18 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 24 | SOFTWARE. 25 | **************************************************************************/ 26 | 27 | module.exports = function(grunt) { 28 | "use strict"; 29 | var apigee_conf = require('./grunt/apigee-config.js') 30 | var helper = require('./grunt/lib/helper-functions.js'); 31 | var searchNReplace = require('./grunt/search-and-replace-files.js'); 32 | require('time-grunt')(grunt); 33 | // Project configuration. 34 | grunt.initConfig({ 35 | pkg: grunt.file.readJSON('package.json'), 36 | apigee_profiles : apigee_conf.profiles(grunt),//{ 37 | availabletasks: { 38 | tasks: {} 39 | }, 40 | clean: ["target"], 41 | mkdir: { 42 | all: { 43 | options: { 44 | create: ['target', 'target/apiproxy/resources/java/', 'target/java/bin'] 45 | }, 46 | }, 47 | }, 48 | copy: { 49 | "java-jar" : { 50 | src: ['java/lib/*.jar', '!java/lib/expressions-1.0.0.jar', '!java/lib/message-flow-1.0.0.jar'], 51 | dest: 'target/apiproxy/resources/java/', filter: 'isFile', flatten: true, expand : true, 52 | }, 53 | apiproxy: { 54 | src: 'apiproxy/**', 55 | dest: './target/', 56 | }, 57 | "node-target": { // copy node folder to target for search and replace 58 | src: './node/**', 59 | dest: './target/' 60 | }, 61 | "node-js-root": { //copy app.js and package.json 62 | expand : true, 63 | src: './target/node/*', 64 | dest: './target/apiproxy/resources/node/', filter: 'isFile', flatten: true 65 | }, 66 | }, 67 | // make a zipfile 68 | compress: { 69 | "node-modules": helper.setNodeResources('./target/node/node_modules/' ,{ 70 | mode : 'zip', 71 | archive: './target/apiproxy/resources/node/node_modules.zip' 72 | }, [ 73 | {expand: true, cwd: './target/node/node_modules/', src: ['**'], dest: 'node_modules/' } // makes all src relative to cwd 74 | ]), 75 | 76 | "node-public": helper.setNodeResources('./target/node/public/', { 77 | mode : 'zip', 78 | archive: './target/apiproxy/resources/node/public.zip' 79 | },[ 80 | {expand: true, cwd: './target/node/public/', src: ['**'], dest: 'public/' }, // makes all src relative to cwd 81 | ]), 82 | 83 | "node-resources": helper.setNodeResources('./target/node/resources/', { 84 | mode : 'zip', 85 | archive: './target/apiproxy/resources/node/resources.zip' 86 | },[ 87 | {expand: true, cwd: './target/node/resources/', src: ['**'], dest: 'resources/' }, // makes all src relative to cwd 88 | ]), 89 | main: { 90 | options: { 91 | mode : 'zip', 92 | archive: "target/<%= apigee_profiles[grunt.option('env')].apiproxy %>.zip" 93 | }, 94 | files: [ 95 | {expand: true, cwd: 'target/apiproxy/', src: ['**'], dest: 'apiproxy/' }, // makes all src relative to cwd 96 | ] 97 | } 98 | }, 99 | // task for configuration management: search and replace elements within XML files 100 | xmlpoke: apigee_conf.xmlconfig(grunt.option('env'), grunt), 101 | // Configure a mochaTest task 102 | mochaTest: { 103 | test: { 104 | options: { 105 | reporter: 'spec', //supported reporters: tap 106 | timeout : 5000, 107 | quiet: false // Optionally suppress output to standard out (defaults to false) 108 | }, 109 | src: ["tests/<%= apigee_profiles[grunt.option('env')].apiproxy %>**.js"] 110 | } 111 | }, 112 | jshint: { 113 | options: { //see options reference http://www.jshint.com/docs/options/ 114 | curly: true, 115 | eqeqeq: true, 116 | eqnull: true, 117 | browser: true, 118 | asi : true, 119 | debug : true, 120 | undef : true, 121 | unused : true, 122 | maxcomplexity : 5, 123 | reporter: require('jshint-stylish') 124 | }, 125 | all: ['Gruntfile.js', 'apiproxy/**/*.js', 'tests/**/*.js', 'tasks/*.js'] 126 | }, 127 | eslint: { // task 128 | options: { 129 | config: 'grunt/conf/eslint.json', // custom config 130 | rulesdir: ['grunt/conf/rules'] // custom rules 131 | }, 132 | target: ['Gruntfile.js', 'target/apiproxy/**/*.js', 'tests/**/*.js', 'tasks/*.js'] // array of files 133 | }, 134 | 'string-replace': { 135 | dist : searchNReplace.searchAndReplaceFiles(grunt.option('env'), grunt) 136 | }, 137 | shell: { 138 | options: { 139 | stderr: false, 140 | failOnError : true 141 | }, 142 | // Remove comments to enable JavaCallout Policy 143 | // javaCompile: { 144 | // command: 'javac -sourcepath ./java/src/**/*.java -d ./target/java/bin -cp java/lib/expressions-1.0.0.jar:java/lib/message-flow-1.0.0.jar:jar:java/lib/message-flow-1.0.1.jar java/src/com/example/SimpleJavaCallout.java', 145 | // }, 146 | // javaJar : { 147 | // command: 'jar cvf target/apiproxy/resources/java/javaCallouts.jar -C target/java/bin .', 148 | // }, 149 | 150 | //run jmeter tests from Grunt 151 | /* "run_jmeter_tests" : { 152 | command: 'mvn install -P <%= grunt.option("env") %>', 153 | },*/ 154 | }, 155 | notify: { 156 | task_name: { 157 | options: { 158 | // Task-specific options go here. 159 | } 160 | }, 161 | ApiDeployed: { 162 | options: { 163 | message: 'Deployment is ready!' 164 | } 165 | } 166 | }, 167 | complexity: { 168 | generic: { 169 | src: ['target/apiproxy/**/*.js', 'tests/**/*.js', 'tasks/*.js'], 170 | exclude: ['doNotTest.js'], 171 | options: { 172 | breakOnErrors: true, 173 | jsLintXML: 'report.xml', // create XML JSLint-like report 174 | checkstyleXML: 'checkstyle.xml', // create checkstyle report 175 | errorsOnly: false, // show only maintainability errors 176 | cyclomatic: [3, 7, 12], // or optionally a single value, like 3 177 | halstead: [8, 13, 20], // or optionally a single value, like 8 178 | maintainability: 100, 179 | hideComplexFunctions: false, // only display maintainability 180 | broadcast: false // broadcast data over event-bus 181 | } 182 | }, 183 | }, 184 | prompt: helper.prompts(grunt) 185 | // Configure this task to support KVM data config migrations 186 | /* "apigee_kvm": { 187 | "testmyapi-test" : { 188 | options: { 189 | type: "env" 190 | }, 191 | files: [{src: ['config/kvm/testmyapi/testmyapi-test/*.json']}, 192 | ] 193 | }, 194 | "testmyapi-prod" : { 195 | options: { 196 | type: "env" 197 | }, 198 | files: [{src: ['config/kvm/testmyapi/testmyapi-prod/*.json']}, 199 | ] 200 | }, 201 | "testmyapi" : { 202 | options: { 203 | type: "org" 204 | }, 205 | files: [{src: ['config/kvm/testmyapi/*.json']}, 206 | ] 207 | } 208 | },*/ 209 | }) 210 | 211 | require('load-grunt-tasks')(grunt); 212 | 213 | // importKVM at Organization and Environment level. See apigee_kvm task above 214 | grunt.registerTask('importKVMs', ['apigee_kvm:' + grunt.config.get("apigee_profiles")[grunt.option('env')].org + '-' + grunt.option("env"), 'apigee_kvm:' + grunt.config.get("apigee_profiles")[grunt.option('env')].org]); 215 | 216 | grunt.registerTask('buildApiBundle', 'Build zip without importing it to Edge', ['apigeeGruntPluginBanner', 'prompt', 'clean', 'saveGitRevision', 'mkdir','copy', 'xmlpoke', 'string-replace', 'jshint', 'eslint', 'complexity', /*'shell'*/ 'compressAlias']); 217 | //1. import revision bumping revision id 218 | grunt.registerTask('IMPORT_DEPLOY_BUMP_REVISION', [ 'buildApiBundle', 'getDeployedApiRevisions', 'undeployApiRevision', 219 | 'apigee_import_api_bundle', 'installNpmRevisionAlias', 'deployApiRevisionAlias', 'executeTests', /*'shell:run_jmeter_tests',*/ 'notify:ApiDeployed']); 220 | 221 | //2. update revision keeping same id 222 | grunt.registerTask('UPDATE_CURRENT_REVISION', [ 'buildApiBundle', 'getDeployedApiRevisions', 'undeployApiRevision', 223 | 'updateApiRevision', 'installNpmRevisionAlias', 'deployApiRevisionAlias', 'executeTests', 'notify:ApiDeployed']); 224 | 225 | //3. import revision and run seamless deployment 226 | grunt.registerTask('DEPLOY_IMPORT_BUMP_SEAMLESS_REVISION', [ 'buildApiBundle', 'getDeployedApiRevisions', 'apigee_import_api_bundle', 227 | 'installNpmRevisionAlias', 'deployApiRevisionAlias', 'executeTests', /*'shell:run_jmeter_tests',*/ 'notify:ApiDeployed']); 228 | 229 | //set to DEPLOY_IMPORT_BUMP_SEAMLESS_REVISION by default. This is critical for production for seamless deploymen and not lose traffic 230 | grunt.registerTask('default', [ /*'importKVMs',*/'DEPLOY_IMPORT_BUMP_SEAMLESS_REVISION' ]); 231 | 232 | grunt.loadTasks('grunt/tasks'); 233 | if(grunt.option.flags().indexOf('--help') === -1 && !grunt.option('env')) { 234 | grunt.fail.fatal('Invalid environment --env={env}.') 235 | } 236 | }; 237 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "{}" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright 2015 Apigee Corporation 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![Build Status](https://travis-ci.org/apigeecs/apigee-deploy-grunt-plugin.svg?branch=master)](https://travis-ci.org/apigeecs/apigee-deploy-grunt-plugin) 2 | 3 | ![apigee-deploy-grunt-plugin](https://www.dropbox.com/s/5tgy3fpyud5skhv/Apigee%20Deploy%20Grunt%20Plugin%20logo2.png?dl=1 "apigee-deploy-grunt-plugin-logo") 4 | 5 | ![apigee-deploy-grunt-terminal](https://www.dropbox.com/s/mkev4l0kmy3bvfw/apigee-deploy-grunt-plugin.gif?dl=1 "apigee-deploy-grunt-plugin-terminal") 6 | 7 | 8 | 9 | **Table of Contents** *generated with [DocToc](http://doctoc.herokuapp.com/)* 10 | 11 | - [Why do we need a tool to manage the API Development Lifecycle for Apigee?](#why-do-we-need-a-tool-to-manage-the-api-development-lifecycle-for-apigee) 12 | - [Getting Started](#getting-started) 13 | - [Steps to get started (deprecated)](#steps-to-get-started-deprecated) 14 | - [Supported tasks](#supported-tasks) 15 | - [Most Important Tasks](#most-important-tasks) 16 | - [Default task : DEPLOY_IMPORT_BUMP_SEAMLESS_REVISION](#default-task--deploy_import_bump_seamless_revision) 17 | - [IMPORT_DEPLOY_BUMP_REVISION Task](#import_deploy_bump_revision-task) 18 | - [UPDATE_CURRENT_REVISION Task](#update_current_revision-task) 19 | - [Test what you just deployed](#test-what-you-just-deployed) 20 | - [Use apigee gateway and with Yahoo Weather standard Target](#use-apigee-gateway-and-with-yahoo-weather-standard-target) 21 | - [Use apigee gateway calling Yahoo Weather through Apigee Node.js as Target](#use-apigee-gateway-calling-yahoo-weather-through-apigee-nodejs-as-target) 22 | - [Use apigee gateway retrieving static content through Node.js as Target](#use-apigee-gateway-retrieving-static-content-through-nodejs-as-target) 23 | - [Use apigee gateway retrieving static content through Node.js as Target (nested folder)](#use-apigee-gateway-retrieving-static-content-through-nodejs-as-target-nested-folder) 24 | - [Use apigee gateway leveraging a JavaCallout policy](#use-apigee-gateway-leveraging-a-javacallout-policy) 25 | - [Complementary Tasks](#complementary-tasks) 26 | - [get all deployed api revisions](#get-all-deployed-api-revisions) 27 | - [undeploy api revision](#undeploy-api-revision) 28 | - [import API bundle without deploying it](#import-api-bundle-without-deploying-it) 29 | - [configuration management](#configuration-management) 30 | - [builds zip bundle under target directory](#builds-zip-bundle-under-target-directory) 31 | - [check all tasks available](#check-all-tasks-available) 32 | - [Node.js Deployment](#nodejs-deployment) 33 | - [JavaCallout Policy Support](#javacallout-policy-support) 34 | - [Search and Replace Functionality](#search-and-replace-functionality) 35 | - [Continuous Integration with Jenkins](#continuous-integration-with-jenkins) 36 | - [API Static Code Analysis](#api-static-code-analysis) 37 | - [JSHint](#jshint) 38 | - [ESLint](#eslint) 39 | - [Reusability of code with Maven Plugins and shell scripts/command line tools](#reusability-of-code-with-maven-plugins-and-shell-scriptscommand-line-tools) 40 | - [Contributing](#contributing) 41 | 42 | 43 | 44 | # Why do we need a tool to manage the API Development Lifecycle for Apigee? 45 | 46 | [![Join the chat at https://gitter.im/apigeecs/apigee-deploy-grunt-plugin](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/apigeecs/apigee-deploy-grunt-plugin?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) 47 | 48 | * Pluggable environment (thousands of npm and grunt modules and plugins) 49 | * Grunt is perfect for applying continuous improvement by easily adding custom tasks. See [Tasks directory](https://github.com/apigeecs/apigee-deploy-grunt-plugin/tree/master/grunt/tasks) 50 | * It's pure JavaScript running on Node.js. Enough said, right :-) 51 | * Deploys Node.js API Proxies (node_modules, resources, and public directories) see [Gruntfile.js compress task](https://github.com/apigeecs/apigee-deploy-grunt-plugin/blob/master/Gruntfile.js). Also support of [Apigee NPM API](http://apigee.com/docs/management/apis/post/organizations/%7Borg_name%7D/apis/%7Bapi_name%7D/revisions/%7Brevision_num%7D/npm) 52 | * Supports JavaCallout Policies. Compiles, package and include Java library dependencies. 53 | * It's ready for TDD with Mocha.js and Chai. See [tests directory](https://github.com/apigeecs/apigee-deploy-grunt-plugin/tree/master/tests). 54 | * Does static code analysis with [JSHint](http://www.jshint.com/) and [ESLint](http://eslint.org/). So, out-of-the-box, you get the ability to add custom rules that promote coding best practices in JavaScript. See [ESLint custom rules](https://gist.github.com/jareware/7179093) 55 | * Reviews JavaScript file complexity by leveraging [Grunt-Complexity](https://github.com/vigetlabs/grunt-complexity#grunt-complexity) 56 | * Suppports Apigee KVM migration see Gruntfile.js for configuration task or please visit [grunt-apigee-kvm](https://github.com/grunt-apigee/grunt-apigee-kvm) 57 | * It's easier to troubleshoot. cURL command support. Just pass ```--curl=true``` 58 | * Searches and replace files content with RegEx, string patterns, or XPath. See string-replace and xmlpoke tasks in Gruntfile.js 59 | * Includes files dynamically from common git submodule to promote DRY (Don't Repeat Yourself) principle (check search-and-replace.js entries) 60 | * It's compatible with most CI tools Jenkins, Bamboo, Go, and Travis 61 | * Sends automatic desktop notifications with [Grunt Notify](https://github.com/dylang/grunt-notify#screenshots) 62 | * It's compatible with IDEs. See [Chrome Grunt Dev Tools](https://chrome.google.com/webstore/detail/grunt-devtools/fbiodiodggnlakggeeckkjccjhhjndnb?hl=en) and [Grunt Task Runner for Web Storm](http://www.jetbrains.com/webstorm/webhelp/using-grunt-task-runner.html) 63 | * It's Compatible with Maven. So you can still run JMeter tests or single Maven tasks, if Mocha doesn't suit you needs, see **shell:run_jmeter_tests** target. [tools examples](https://github.com/apigeecs/apigee-deploy-grunt-plugin/tree/master/tools/forecastweather-jmeter-example). 64 | 65 | # Getting Started 66 | The easiest way to use this plugin is by leveraging Yeoman. Follow Getting Started from [Apigee API Proxy Generator](https://github.com/dzuluaga/generator-apigee-deploy-grunt-api#getting-started). 67 | 68 | # Steps to get started (deprecated) 69 | Getting started with Yeoman is much easier than following steps above. You no longer need to folow these steps unless you want learn how to use Git Submodules. 70 | 71 | **Prerequisites: Node.js and NPM** 72 | 73 | **Optional tools: Git. You can still use this plugin without Git, however manual steps will be required to initialize common folder.** 74 | 75 | * **Step 1:** Clone this repo with Git. ```git clone https://github.com/apigeecs/apigee-deploy-grunt-plugin.git``` 76 | If you don't have Git is installed, download this repo as a zip file and expand it somewhere in the filesytem. 77 | * **Step 2:** ```cd apigee-deploy-grunt-plugin``` 78 | * **Step 3:** open apigee-deploy-grunt-plugin folder and execute the two commands: 79 | ``` 80 | git submodule init 81 | git submodule update 82 | 83 | ``` 84 | **These two commands initialize Git Submodules by downloading source to Common folder from common branch. Installation without Git requires to download common branch as a zip file and expand its content into common folder (common/apiproxy)** 85 | 86 | * **Step 4:** setup Apigee Edge credentials as system environment variables ae_username and ae_password or just pass credentials as arguments 87 | * **Step 5:** install [grunt cli](http://gruntjs.com/getting-started#installing-the-cli) ```sudo npm install grunt-cli -g``` 88 | * **Step 6:** execute ```npm install``` to install all grunt dependencies 89 | * **Step 7:** Edit and add environment to grunt/apigee-config.js file. Each environment will be referenced below as a flag e.g. --env={test, prod} 90 | * **Step 8:** Edit and add environments to grunt/search-and-replace-files.js for string replacements. 91 | * **Step 9:** run ```grunt --env=test --username={apigee_edge_email_address} --password={apigee_edge_password} --debug``` 92 | 93 | # Supported tasks 94 | 95 | ### Most Important Tasks 96 | 97 | ##### Default task : DEPLOY_IMPORT_BUMP_SEAMLESS_REVISION 98 | One of the cool features of Apigee is seamless deployment or zero downtime. By default Grunt deploys in that mode. The following command will deploy api bundles with that mode: 99 | ```bash 100 | $ grunt --env=test --username={apigee_edge_email_address} --password={apigee_edge_password} --debug --curl=true 101 | ``` 102 | **Note: Pass --debug flag to display Management API responses.** 103 | 104 | ##### IMPORT_DEPLOY_BUMP_REVISION Task 105 | The following command will undeploy is similar to the default task, however it undeploys the bundle, so there's some downtime. import, and deploy the api bundle: 106 | ```bash 107 | $ grunt IMPORT_DEPLOY_BUMP_REVISION --env=test --username={apigee_edge_email_address} --password={apigee_edge_password} --debug --curl=true 108 | ``` 109 | 110 | ##### UPDATE_CURRENT_REVISION Task 111 | The following command will update current revision. Please be aware that it undeploys the current revision, so task is more suitable for development to avoid creating new revisions: 112 | ```bash 113 | $ grunt UPDATE_CURRENT_REVISION --env=test --username={apigee_edge_email_address} --password={apigee_edge_password} --debug --curl=true 114 | ``` 115 | 116 | ### Test what you just deployed 117 | Once previous is executed, you should be able to try the following calls: 118 | 119 | ##### Use apigee gateway and with Yahoo Weather standard Target 120 | ``` 121 | https://{org-env}.apigee.net/{api-basepath}/apigee/forecastrss?w=2502265 122 | ``` 123 | 124 | Example ```https://testmyapi-test.apigee.net/weathergrunt/apigee/forecastrss?w=2502265``` 125 | 126 | ##### Use apigee gateway calling Yahoo Weather through Apigee Node.js as Target 127 | ``` 128 | https://{org-env}.apigee.net/{api-basepath}/forecastweather_node/2502265 129 | ``` 130 | 131 | Example ```https://testmyapi-test.apigee.net/weathergrunt/forecastweather_node/2502265``` 132 | 133 | ##### Use apigee gateway retrieving static content through Node.js as Target 134 | ``` 135 | https://{org-env}.apigee.net/{api-basepath}/images/tree.jpg 136 | ``` 137 | 138 | Example ```https://testmyapi-test.apigee.net/weathergrunt/images/tree.jpg``` 139 | 140 | ##### Use apigee gateway retrieving static content through Node.js as Target (nested folder) 141 | ``` 142 | https://{org-env}.apigee.net/{api-basepath}/tree.jpg 143 | ``` 144 | 145 | Example ```https://testmyapi-test.apigee.net/weathergrunt/tree.jpg``` 146 | 147 | ##### Use apigee gateway leveraging a JavaCallout policy 148 | **Disabled by default. Read section below for enabling directions.** 149 | ``` 150 | https://{org-env}.apigee.net/{api-basepath}/javacallout 151 | ``` 152 | 153 | Example ```curl https://testmyapi-test.apigee.net/weathergrunt/javacallout``` 154 | 155 | ### Complementary Tasks 156 | 157 | #### get all deployed api revisions 158 | ```grunt getDeployedApiRevisions --env=test --debug``` 159 | 160 | #### undeploy api revision 161 | ```grunt undeployApiRevision:{revision_id} --env=test --debug``` 162 | 163 | #### undeploy api revision 164 | ```grunt deployApiRevision:{revision_id} --env=test --debug``` 165 | 166 | #### get all api revisions 167 | ```grunt getAllApiRevisions --env=test --debug``` 168 | 169 | #### import API build bundle without deploying it 170 | ```grunt importApiBundle --env=test --debug --debug``` 171 | 172 | #### delete a revision 173 | ```grunt deleteApiRevision:{revision_id} --env=test --debug``` 174 | 175 | #### update a revision with build bundle 176 | ``` 177 | $ grunt updateApiRevision:{revision_id} --env=test 178 | ``` 179 | #### configuration management 180 | See grunt/apigee-config.js file. 181 | 182 | #### builds zip bundle under target directory 183 | ```grunt compress --env=test``` 184 | 185 | #### clean Target directory 186 | ```grunt clean --env=test``` 187 | 188 | #### check all tasks available 189 | ```grunt --help``` or ```grunt availabletasks --env=test``` will display a nicer colored output. 190 | 191 | #### supported arguments and flags 192 | ``` 193 | --username={apigee_edge_username} 194 | ``` 195 | 196 | ``` 197 | --password={apigee_edge_password} 198 | ``` 199 | 200 | ``` 201 | --skip-tests=true 202 | ``` 203 | 204 | ``` 205 | --curl generate curl commands to be executed from command line 206 | ``` 207 | 208 | ``` 209 | --override 210 | ``` 211 | 212 | ``` 213 | --delay={seconds} 214 | ``` 215 | 216 | Upload node.js modules (by default the plugin will try to install npm in Apigee Edge) 217 | ``` 218 | --upload-modules 219 | ``` 220 | 221 | Node.js Deployment 222 | ==== 223 | In order to compress node.js modules, it's required node directory to exist in the root folder. By default node_modules, public, and resources are generated as part of the build. Compress configuration in Gruntfile.js can be modified to include any other configuration. 224 | 225 | JavaCallout Policy Support 226 | ==== 227 | This task comes disabled by default to prevent issues from OS environments (MacOS and Windows). To enable, remove comments from shell task in Gruntfile and customize as directions below. 228 | 229 | JavaCallouts are currently supported by leveraging [grunt-shell npm package](https://www.npmjs.org/package/grunt-shell). Therefore, compilation and packaging steps are dependent on your local installation of javac and jar tools. Source code directories must be located under java/src directory and any jar dependencies under java/lib directory. See Gruntfile.js shell task for more details. 230 | 231 | Note: Since javac requires to know where .java files are located, it is required to include java callout separated by spaces in shell javaCompile target. 232 | 233 | For instance: 234 | ``` 235 | javac -sourcepath ./java/src/**/*.java -d ./target/java/bin -cp java/lib/expressions-1.0.0.jar:java/lib/message-flow-1.0.0.jar:jar:java/lib/message-flow-1.0.1.jar **java/src/com/example/SimpleJavaCallout.java** 236 | ``` 237 | 238 | Search and Replace Functionality 239 | ====== 240 | 241 | ####String 242 | Ability to search and replace strings from text files that match any pattern in Regex or string. See grunt/search-and-replace-files.js to setup per environment. This task leverages [grunt-string-replace module](https://www.npmjs.org/package/grunt-string-replace). See grunt/search-and-replace-files.js for an example. 243 | 244 | ####Include files from common Git submodule 245 | Ability to search and include content from files is also provided by string-replace task. See examples under grunt/search-and-replace-files.js that include fragments from common folder for fragments (multiple steps), policies, and JavaScript files. Note common folder leverages [Git Submodule](http://git-scm.com/book/en/Git-Tools-Submodules), which resides in a separate branch under the same repository. This allows reusing code across multiple APIs without adding more complexity. 246 | 247 | Continuous Integration with Jenkins 248 | ====== 249 | [This repo provides a guide for setting up an instance of Jenkins with Grunt](https://github.com/dzuluagaapigee/apigee-ci-jenkins-git-maven-jmeter) to deploy and configure an API bundle. 250 | 251 | API Static Code Analysis 252 | ======== 253 | This plugin is enabled to provide feedback about coding best practices for JavaScript. 254 | 255 | JSHint 256 | -------- 257 | JSHInt provides a large set of configurable (options)[http://www.jshint.com/docs/options/] that can be enabled out-of-the-box. 258 | See jshint task in Gruntfile.js 259 | 260 | ESLint 261 | -------- 262 | ESLint provides an pluggable framework to enable static code analysis. In contrast to JSHint, ESLint can be extended to write custom API specific rules. See conf/rules/if-curly-formatting.js rule and conf/eslint.json to manage alerts. 263 | See ESLint Gruntfile.js section 264 | 265 | Reusability of code with Maven Plugins and shell scripts/command line tools 266 | ===== 267 | Grunt plugin can be extended to support plugins, shell script or any other command line tools. For instance Proxy Dependency Maven Plugin includes an example of a [pom.xml](https://github.com/apigeecs/apigee-deploy-grunt-plugin/tree/master/tools/proxy-dependency-maven-plugin). 268 | 269 | Contributing 270 | ===== 271 | If you would like to contribute, simply fork the repository, push your changes to a branch and send a pull request: 272 | 273 | 1. Fork it 274 | 2. Create your feature branch (`git checkout -b my-new-feature`) 275 | 3. Commit your changes (`git commit -am 'Add some feature'`) 276 | 4. Push to the branch (`git push origin my-new-feature`) 277 | 5. Create new Pull Request 278 | 279 | Typo fixes, improvements to grammar or readability, it's all welcome. 280 | 281 | ## License 282 | 283 | Copyright (c) 2014 Diego Zuluaga (twitter: @dzuluaga) 284 | Licensed under the MIT license. 285 | 286 | -------------------------------------------------------------------------------- /apiproxy/forecastweather-grunt-plugin-api.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 1384727595303 5 | dzuluaga@apigee.com 6 | @@DESCRIPTION TO BE REPLACED BY GITHUB ID 7 | weather 8 | 1384727595303 9 | dzuluaga@apigee.com 10 | 11 | xmltojson-1 12 | 13 | 14 | default 15 | 16 | 17 | 18 | 19 | default 20 | 21 | false 22 | 23 | -------------------------------------------------------------------------------- /apiproxy/policies/Assign-Message-Remove-Suffix.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | Assign Message Remove Suffix 4 | 5 | 6 | 7 | target.copy.pathsuffix 8 | false 9 | 10 | true 11 | 12 | -------------------------------------------------------------------------------- /apiproxy/policies/JSON-to-XML.xml: -------------------------------------------------------------------------------- 1 | @@JSON-to-XML.xml -------------------------------------------------------------------------------- /apiproxy/policies/Script-External-Variables.xml: -------------------------------------------------------------------------------- 1 | @@Script-External-Variables.xml -------------------------------------------------------------------------------- /apiproxy/policies/SimpleJavaCallout.xml.bak: -------------------------------------------------------------------------------- 1 | 2 | 3 | com.example.SimpleJavaCallout 4 | java://javaCallouts.jar 5 | -------------------------------------------------------------------------------- /apiproxy/policies/XMLtoJSON-Common.xml: -------------------------------------------------------------------------------- 1 | @@XMLtoJSON-Common.xml -------------------------------------------------------------------------------- /apiproxy/proxies/default.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | @@EXAMPLE-STEP-COMMON-XML-TO-JSON-GIT-SUBMODULE-FRAGMENT#1 10 | 11 | (proxy.pathsuffix MatchesPath "/apigee/**") and (request.verb = "GET") 12 | 13 | 14 | 15 | 16 | 17 | @@EXAMPLE-STEP-COMMON-XML-TO-JSON-GIT-SUBMODULE-FRAGMENT#2 18 | 19 | (proxy.pathsuffix MatchesPath "/forecastweather_node/**") and (request.verb = "GET") 20 | 21 | 22 | 23 | 24 | 25 | @@EXAMPLE-STEP-COMMON-XML-TO-JSON-GIT-SUBMODULE-FRAGMENT#3 26 | 27 | (proxy.pathsuffix MatchesPath "/images/**") and (request.verb = "GET") 28 | 29 | 30 | 31 | 32 | 33 | 34 | 38 | 39 | (proxy.pathsuffix MatchesPath "/javacallout") and (request.verb = "GET") 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | /weathergrunt 48 | default 49 | secure 50 | 51 | 52 | (proxy.pathsuffix MatchesPath "/apigee/**") and (request.verb = "GET") 53 | default 54 | 55 | 56 | 57 | node 58 | 59 | 60 | 61 | 62 | 63 | -------------------------------------------------------------------------------- /apiproxy/resources/jsc/js_external_vars.js: -------------------------------------------------------------------------------- 1 | // jshint ignore: start 2 | /* jshint ignore:start */ 3 | 4 | @@js_external_vars.js -------------------------------------------------------------------------------- /apiproxy/resources/jsc/search-and-replace-example.js: -------------------------------------------------------------------------------- 1 | function testFunction(){ 2 | 'use strict'; 3 | var test = "VALUE TO BE REPLACED #1"; 4 | return test; 5 | } 6 | //remove comment to see jshint triggering errors 7 | //var a = "test"; 8 | /*if(1 === 1) { 9 | if(2 === 2) { 10 | if(3 === 3) { 11 | if(4 === 4) { 12 | 13 | } 14 | } 15 | } 16 | 17 | }*/ 18 | 19 | testFunction(); -------------------------------------------------------------------------------- /apiproxy/resources/jsc/search-and-replace-example2.js: -------------------------------------------------------------------------------- 1 | function testFunction2(){ 2 | 'use strict'; 3 | var test2 = "VALUE TO BE REPLACED #2"; 4 | return test2; 5 | } 6 | 7 | testFunction2(); -------------------------------------------------------------------------------- /apiproxy/targets/default.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | Assign-Message-Remove-Suffix 10 | 11 | 12 | 13 | 14 | 15 | http://weather.yahooapis.com/forecastrss 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /apiproxy/targets/node.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | Node Target 4 | 5 | 6 | 7 | node://app.js 8 | 9 | -------------------------------------------------------------------------------- /config/kvm/testmyapi/passwords.json: -------------------------------------------------------------------------------- 1 | { 2 | "entry": [ 3 | { 4 | "name": "dzuluaga+testoauth@apigee.com", 5 | "value": "b002625528157aeb315c91f3ae74cad9e5708af8:Q5ENLG2CoLQvyfSo4FQyRjQa4TEST" 6 | } 7 | ], 8 | "name": "passwords" 9 | } -------------------------------------------------------------------------------- /config/kvm/testmyapi/passwords.json.encrypted: -------------------------------------------------------------------------------- 1 | 745e184792a532e9152810e931f0604edfbf164a91cdcd7a82905b34b753aec5d453d2e7b9cbfff61ba88f7223af23a05e21f34bde37b9c91c0820ae3cd59f60c67145dc54fd2aeaeb4cb7a36bad632bead85a61d1350393da2b31d756b537525e729d801c4fe49c0d51116ba9706e150c241c5921e9ec1a078ca02080460de4c1465c52d297505f5336c2709d9647327bec2fff77271d77943b94b3cf43c82ea8a82e58fc735b88241839d78cbeb6a3b2493b479c47e04e34506ed1678e324b -------------------------------------------------------------------------------- /config/kvm/testmyapi/testmyapi-prod/passwords.json: -------------------------------------------------------------------------------- 1 | { 2 | "entry": [ 3 | { 4 | "name": "dzuluaga+testoauth@apigee.com", 5 | "value": "b002625528157aeb315c91f3ae74cad9e5708af8:Q5ENLG2CoLQvyfSo4FQyRjQa4TEST" 6 | } 7 | ], 8 | "name": "passwords" 9 | } -------------------------------------------------------------------------------- /config/kvm/testmyapi/testmyapi-prod/passwords.json.encrypted: -------------------------------------------------------------------------------- 1 | 745e184792a532e9152810e931f0604edfbf164a91cdcd7a82905b34b753aec5d453d2e7b9cbfff61ba88f7223af23a05e21f34bde37b9c91c0820ae3cd59f60c67145dc54fd2aeaeb4cb7a36bad632bead85a61d1350393da2b31d756b537525e729d801c4fe49c0d51116ba9706e150c241c5921e9ec1a078ca02080460de4c1465c52d297505f5336c2709d9647327bec2fff77271d77943b94b3cf43c82ea8a82e58fc735b88241839d78cbeb6a3b2493b479c47e04e34506ed1678e324b -------------------------------------------------------------------------------- /config/kvm/testmyapi/testmyapi-prod/passwords2.json.encrypted: -------------------------------------------------------------------------------- 1 | 745e184792a532e9152810e931f0604edfbf164a91cdcd7a82905b34b753aec5d453d2e7b9cbfff61ba88f7223af23a05e21f34bde37b9c91c0820ae3cd59f60c67145dc54fd2aeaeb4cb7a36bad632bead85a61d1350393da2b31d756b537525e729d801c4fe49c0d51116ba9706e150c241c5921e9ec1a078ca02080460de4c1465c52d297505f5336c2709d9647327bec2fff77271d77943b94b3cf43c82ea8a82e58fc735b88241839d78cbeb6a3b2493b479c47e04e34506ed1678e324b -------------------------------------------------------------------------------- /config/kvm/testmyapi/testmyapi-prod/targets.json: -------------------------------------------------------------------------------- 1 | { 2 | "entry": [ 3 | { 4 | "name": "Key1", 5 | "value": "value_one-test" 6 | }, 7 | { 8 | "name": "Key2", 9 | "value": "value_two-test" 10 | } 11 | ], 12 | "name": "targets" 13 | } -------------------------------------------------------------------------------- /config/kvm/testmyapi/testmyapi-prod/targets.json.encrypted: -------------------------------------------------------------------------------- 1 | 745e184792a532e9152810e931f0604edfbf164a91cdcd7a82905b34b753aec58767290af189d2f7dba464f1f53792b60d678f2133d91bf4ac40764cbf34f2ee5af35f3b9aafff8b317aa6f756aed50bb45a460fde3c5fc05b3e4afe7d0c56a15f0814298536320b0943c68b18a153fa8883f6c242caa172405e03d5922eb04f347cb1d23e4d37233b8caca25eab935056df2017c54ee681df20c375b4536944a0e59b379b99b4f353a1405969a17667 -------------------------------------------------------------------------------- /config/kvm/testmyapi/testmyapi-test/passwords.json: -------------------------------------------------------------------------------- 1 | { 2 | "entry": [ 3 | { 4 | "name": "dzuluaga+testoauth@apigee.com", 5 | "value": "b002625528157aeb315c91f3ae74cad9e5708af8:Q5ENLG2CoLQvyfSo4FQyRjQa4TEST" 6 | } 7 | ], 8 | "name": "passwords" 9 | } -------------------------------------------------------------------------------- /config/kvm/testmyapi/testmyapi-test/passwords.json.encrypted: -------------------------------------------------------------------------------- 1 | 745e184792a532e9152810e931f0604edfbf164a91cdcd7a82905b34b753aec5d453d2e7b9cbfff61ba88f7223af23a05e21f34bde37b9c91c0820ae3cd59f60c67145dc54fd2aeaeb4cb7a36bad632bead85a61d1350393da2b31d756b537525e729d801c4fe49c0d51116ba9706e150c241c5921e9ec1a078ca02080460de4c1465c52d297505f5336c2709d9647327bec2fff77271d77943b94b3cf43c82ea8a82e58fc735b88241839d78cbeb6a3b2493b479c47e04e34506ed1678e324b -------------------------------------------------------------------------------- /config/kvm/testmyapi/testmyapi-test/targets.json: -------------------------------------------------------------------------------- 1 | { 2 | "entry": [ 3 | { 4 | "name": "Key1", 5 | "value": "value_one-test" 6 | }, 7 | { 8 | "name": "Key2", 9 | "value": "value_two-test" 10 | } 11 | ], 12 | "name": "targets4" 13 | } -------------------------------------------------------------------------------- /config/kvm/testmyapi/testmyapi-test/targets.json.encrypted: -------------------------------------------------------------------------------- 1 | 745e184792a532e9152810e931f0604edfbf164a91cdcd7a82905b34b753aec58767290af189d2f7dba464f1f53792b60d678f2133d91bf4ac40764cbf34f2ee5af35f3b9aafff8b317aa6f756aed50bb45a460fde3c5fc05b3e4afe7d0c56a15f0814298536320b0943c68b18a153fa8883f6c242caa172405e03d5922eb04f347cb1d23e4d37233b8caca25eab935056df2017c54ee681df20c375b4536944eb132568474852b5b3499b3e10c973fc -------------------------------------------------------------------------------- /grunt/apigee-config.js: -------------------------------------------------------------------------------- 1 | exports.profiles = function(grunt){ 2 | return { 3 | env : grunt.option('env'), // replace with environment 4 | 'test' : { 5 | apiproxy : 'forecastweather-grunt-plugin-api', 6 | org : 'testmyapi', // replace with organization 7 | env : 'test', // replace with environment 8 | url_mgmt : 'https://api.enterprise.apigee.com', // for cloud environments, leave as is 9 | username : grunt.option('username'), //|| process.env.ae_username, // pass credentials as arguments as grunt task --username=$ae_username --password=$ae_password 10 | password : grunt.option('password'), //|| process.env.ae_password, // use ae_username and ae_password are defined as environment variables and no arguments are passed 11 | revision : grunt.option('revision'), // provide revision to be undeployed by passing argument as --revision=X 12 | override : grunt.option('override') || true, 13 | delay : grunt.option('delay') || 10 14 | }, 15 | 'prod' : { 16 | apiproxy : 'forecastweather-grunt-plugin-api', 17 | org : 'testmyapi', // replace with organization 18 | env : 'prod', // replace with environment 19 | url_mgmt : 'https://api.enterprise.apigee.com', // for cloud environments, leave as is 20 | username : grunt.option('username'), //|| process.env.ae_username, // pass credentials as arguments as grunt task --username=$ae_username --password=$ae_password 21 | password : grunt.option('password'), //|| process.env.ae_password, // use ae_username and ae_password are defined as environment variables and no arguments are passed 22 | revision : grunt.option('revision'), // provide revision to be undeployed by passing argument as --revision=X 23 | override : grunt.option('override') || true, 24 | delay : grunt.option('delay') || 10 25 | } 26 | } 27 | } 28 | 29 | exports.xmlconfig = function(env, grunt){ 30 | config = { "test" : [ 31 | {//sets description within API proxy for tracking purposes with this format 'git commit: 8974b5a by dzuluaga on Diegos-MacBook-Pro-2.local' 32 | //see grunt/tasks/saveGitRevision.js for further customization 33 | "options": { 34 | "xpath": "//APIProxy/Description", 35 | "value": "<%= grunt.option('gitRevision') %>" 36 | }, 37 | "files": { 38 | "target/apiproxy/<%= apigee_profiles[grunt.option('env')].apiproxy %>.xml": "apiproxy/*.xml" 39 | } 40 | }, 41 | { 42 | "options": { 43 | "xpath": "//TargetEndpoint/HTTPTargetConnection/URL", 44 | "value": "https://weather.yahooapis.com/forecastrss" 45 | }, 46 | "files": { 47 | "target/apiproxy/targets/default.xml": "apiproxy/targets/default.xml" 48 | } 49 | }, 50 | { 51 | "options": { 52 | "xpath": "//ProxyEndpoint/HTTPProxyConnection/BasePath", 53 | "value": "weathergrunt" 54 | }, 55 | "files": { 56 | "target/apiproxy/proxies/default.xml": "apiproxy/proxies/default.xml" 57 | } 58 | } 59 | ], 60 | "prod" : [ 61 | {//sets description within API proxy for tracking purposes with this format 'git commit: 8974b5a by dzuluaga on Diegos-MacBook-Pro-2.local' 62 | //see grunt/tasks/saveGitRevision.js for further customization 63 | "options": { 64 | "xpath": "//APIProxy/Description", 65 | "value": "<%= grunt.option('gitRevision') %>" 66 | }, 67 | "files": { 68 | "target/apiproxy/<%= apigee_profiles[grunt.option('env')].apiproxy %>.xml": "apiproxy/*.xml" 69 | } 70 | }, 71 | { 72 | "options": { 73 | "xpath": "//TargetEndpoint/HTTPTargetConnection/URL", 74 | "value": "https://weather.yahooapis.com/forecastrss" 75 | }, 76 | "files": { 77 | "target/apiproxy/targets/default.xml": "apiproxy/targets/default.xml" 78 | } 79 | }, 80 | { 81 | "options": { 82 | "xpath": "//ProxyEndpoint/HTTPProxyConnection/BasePath", 83 | "value": "weathergrunt" 84 | }, 85 | "files": { 86 | "target/apiproxy/proxies/default.xml": "apiproxy/proxies/default.xml" 87 | } 88 | } 89 | ]} 90 | if(!config[env])grunt.fail.fatal('Environment '+ env +' does not exist under grunt/apigee-config.js') 91 | return config[env]; 92 | } -------------------------------------------------------------------------------- /grunt/conf/eslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "rules": { 3 | "if-curly-formatting": 2, 4 | "no-undef": 0, 5 | "no-mixed-spaces-and-tabs" : 0, 6 | "quotes" : 0, 7 | "eol-last" : 0, 8 | "semi" : 0, 9 | "no-comma-dangle" : 0, 10 | "camelcase" : 0, 11 | "no-global-strict" : 0, 12 | "no-use-before-define" : 0 13 | 14 | } 15 | } -------------------------------------------------------------------------------- /grunt/conf/rules/if-curly-formatting.js: -------------------------------------------------------------------------------- 1 | module.exports = function(context) { 2 | return { 3 | IfStatement: function(node) { 4 | var source = context.getSource(node.test, 0, 3); 5 | if (!source.match(/ {$/)) { 6 | context.report(node, "Found improperly formatted if-statement"); 7 | } 8 | } 9 | }; 10 | }; 11 | -------------------------------------------------------------------------------- /grunt/lib/helper-functions.js: -------------------------------------------------------------------------------- 1 | exports.generatecURL = function(options){ 2 | //echo curl -H "Authorization:Basic $credentials" "$url/v1/organizations/$org/apis/$application/revisions" -X GET 3 | var optionsl = JSON.parse(JSON.stringify(options)); 4 | var curl = require('curl-cmd'); 5 | optionsl.auth = optionsl.auth.user + ":" + optionsl.auth.password 6 | optionsl.hostname = options.uri.replace('https://', '') 7 | optionsl.path = optionsl.hostname.substring(optionsl.hostname.indexOf('/')); 8 | optionsl.hostname = optionsl.hostname.replace(optionsl.path, '') 9 | console.log(curl.cmd(optionsl, {ssl: true, verbose: true})); 10 | } 11 | 12 | exports.setNodeResources = function(dir, options, files){ 13 | var fs = require('fs'); 14 | var task = {}; 15 | if (fs.existsSync('./node')) { 16 | task.options = options; 17 | task.files = files 18 | } 19 | return task; 20 | } 21 | 22 | exports.prompts = function(grunt) { 23 | return { 24 | target: { 25 | options: { 26 | questions: [ 27 | { 28 | config: 'username', 29 | type: 'input', 30 | message: 'Apigee Edge EMail', 31 | when: function(){ 32 | return !grunt.option('username'); 33 | } 34 | }, 35 | { 36 | config: 'password', // arbitrary name or config for any other grunt task 37 | type: 'password', // list, checkbox, confirm, input, password 38 | message: 'Apigee Edge Password', // Question to ask the user, function needs to return a string, 39 | when: function(){ 40 | return !grunt.option('password'); 41 | } 42 | } 43 | ], 44 | then : function(results, done){ 45 | if(results.username) {grunt.config("apigee_profiles." + grunt.option('env') + ".username", results.username);} 46 | if(results.password) {grunt.config("apigee_profiles." + grunt.option('env') + ".password", results.password);} 47 | done(); 48 | return true; 49 | } 50 | } 51 | } 52 | } 53 | } -------------------------------------------------------------------------------- /grunt/search-and-replace-files.js: -------------------------------------------------------------------------------- 1 | exports.searchAndReplaceFiles = function(env, grunt){ 2 | var config = { 3 | test: { 4 | files: [{ 5 | cwd: 'target/', 6 | src: ['**/*.js','**/*.xml','!node/node_modules/**/*.js'], 7 | dest: 'target/', 8 | expand: true, 9 | }], 10 | options: { 11 | replacements: [ 12 | { 13 | pattern: 'VALUE TO BE REPLACED #1', 14 | replacement: '<%= grunt.template.today() %>_test' 15 | }, 16 | { 17 | pattern: 'VALUE TO BE REPLACED #2', 18 | replacement: 'REPLACE VALUE #2_TEST' 19 | }, 20 | {//fragment from common folder (Git submodule) 21 | pattern: '@@EXAMPLE-STEP-COMMON-XML-TO-JSON-GIT-SUBMODULE-FRAGMENT#1', 22 | replacement: "<%= grunt.file.read('common/apiproxy/proxies/xmltojson-js-steps-common-frag.xml') %>" 23 | }, 24 | {//fragment from common folder (Git submodule) 25 | pattern: '@@EXAMPLE-STEP-COMMON-XML-TO-JSON-GIT-SUBMODULE-FRAGMENT#2', 26 | replacement: "<%= grunt.file.read('common/apiproxy/proxies/xmltojson-js-steps-common-frag.xml') %>" 27 | }, 28 | {//fragment from common folder (Git submodule) 29 | pattern: '@@EXAMPLE-STEP-COMMON-XML-TO-JSON-GIT-SUBMODULE-FRAGMENT#3', 30 | replacement: "<%= grunt.file.read('common/apiproxy/proxies/xmltojson-js-steps-common-frag.xml') %>" 31 | }, 32 | { 33 | pattern: '@@XMLtoJSON-Common.xml', 34 | replacement: "<%= grunt.file.read('common/apiproxy/policies/XMLtoJSON-Common.xml') %>" 35 | }, 36 | {//fragment from common folder (Git submodule) 37 | pattern: '@@Script-External-Variables.xml', 38 | replacement: "<%= grunt.file.read('common/apiproxy/policies/Script-External-Variables.xml') %>" 39 | }, 40 | {//fragment from common folder (Git submodule) 41 | pattern: '@@js_external_vars.js', 42 | replacement: "<%= grunt.file.read('common/apiproxy/resources/jsc/js_external_vars.js') %>" 43 | }, 44 | { 45 | pattern: '@@JSON-to-XML.xml', 46 | replacement: "<%= grunt.file.read('common/apiproxy/policies/JSON-to-XML.xml') %>" 47 | }, 48 | ] 49 | } 50 | }, 51 | prod: { 52 | files: [{ 53 | cwd: 'target/', 54 | src: ['**/*.js','**/*.xml','!node/node_modules/**/*.js'], 55 | dest: 'target/', 56 | expand: true, 57 | }], 58 | options: { 59 | replacements: [{ 60 | pattern: 'VALUE TO BE REPLACED #1', 61 | replacement: '<%= grunt.template.today() %>_test' 62 | }, 63 | { 64 | pattern: 'VALUE TO BE REPLACED #2', 65 | replacement: 'REPLACE VALUE #2_TEST' 66 | }, 67 | {//fragment from common folder (Git submodule) 68 | pattern: '@@EXAMPLE-STEP-COMMON-XML-TO-JSON-GIT-SUBMODULE-FRAGMENT#1', 69 | replacement: "<%= grunt.file.read('common/apiproxy/proxies/xmltojson-js-steps-common-frag.xml') %>" 70 | }, 71 | {//fragment from common folder (Git submodule) 72 | pattern: '@@EXAMPLE-STEP-COMMON-XML-TO-JSON-GIT-SUBMODULE-FRAGMENT#2', 73 | replacement: "<%= grunt.file.read('common/apiproxy/proxies/xmltojson-js-steps-common-frag.xml') %>" 74 | }, 75 | {//fragment from common folder (Git submodule) 76 | pattern: '@@EXAMPLE-STEP-COMMON-XML-TO-JSON-GIT-SUBMODULE-FRAGMENT#3', 77 | replacement: "<%= grunt.file.read('common/apiproxy/proxies/xmltojson-js-steps-common-frag.xml') %>" 78 | }, 79 | { 80 | pattern: '@@XMLtoJSON-Common.xml', 81 | replacement: "<%= grunt.file.read('common/apiproxy/policies/XMLtoJSON-Common.xml') %>" 82 | }, 83 | {//fragment from common folder (Git submodule) 84 | pattern: '@@Script-External-Variables.xml', 85 | replacement: "<%= grunt.file.read('common/apiproxy/policies/Script-External-Variables.xml') %>" 86 | }, 87 | {//fragment from common folder (Git submodule) 88 | pattern: '@@js_external_vars.js', 89 | replacement: "<%= grunt.file.read('common/apiproxy/resources/jsc/js_external_vars.js') %>" 90 | }, 91 | { 92 | pattern: '@@JSON-to-XML.xml', 93 | replacement: "<%= grunt.file.read('common/apiproxy/policies/JSON-to-XML.xml') %>" 94 | }, 95 | ] 96 | } 97 | }, 98 | } 99 | if(!config[env])grunt.fail.fatal('Environment '+ env +' does not exist under grunt/search-and-replace-files.js') 100 | return(config[env]) 101 | } -------------------------------------------------------------------------------- /grunt/tasks/apigeeGruntPluginBanner.js: -------------------------------------------------------------------------------- 1 | /*jslint node: true */ 2 | var FONTS = require('cfonts'); 3 | 4 | 5 | module.exports = function(grunt) { 6 | 'use strict'; 7 | grunt.registerTask("apigeeGruntPluginBanner", 'Display Apigee Grunt Plugin Banner', function(set){ 8 | var fonts = new FONTS({ 9 | 'text': 'Apigee Grunt Plugin', //text to be converted 10 | 'font': 'block', //define the font face 11 | 'colors': ['red','blue'], //define all colors 12 | 'background': 'black', //define the background color 13 | 'letterSpacing': 1, //define letter spacing 14 | 'space': true, //define if the output text should have empty lines on top and on the bottom 15 | 'maxLength': '12' //define how many character can be on one line 16 | }); 17 | new FONTS({ 18 | 'text': 'Welcome to Apigee Grunt Plugin!', //text to be converted 19 | 'font': 'console', //define the font face 20 | 'colors': ['red'], //define all colors 21 | 'background': 'blue', //define the background color 22 | 'letterSpacing': 10, //define letter spacing 23 | 'space': true, //define if the output text should have empty lines on top and on the bottom 24 | 'maxLength': '12' //define how many character can be on one line 25 | }); 26 | }); 27 | } 28 | -------------------------------------------------------------------------------- /grunt/tasks/compressAlias.js: -------------------------------------------------------------------------------- 1 | /*jslint node: true */ 2 | 3 | module.exports = function(grunt) { 4 | 'use strict'; 5 | grunt.registerTask('compressAlias', 'compress alias task that conditionally runs compress task.', function() { 6 | grunt.log.debug('Node.js modules not include in API bundle.'); 7 | var targetsFiltered = []; 8 | Object.keys(grunt.config('compress')).forEach(function (i){ 9 | if(!grunt.option('upload-modules') && i == 'node-modules'){ 10 | grunt.log.debug('skipped node-modules compress file'); 11 | return; 12 | }else{ 13 | targetsFiltered.push('compress:' + i); 14 | } 15 | }) 16 | grunt.task.run(targetsFiltered || 'compress'); 17 | }); 18 | }; -------------------------------------------------------------------------------- /grunt/tasks/deleteApiRevision.js: -------------------------------------------------------------------------------- 1 | /*jslint node: true */ 2 | 3 | var grunt_common = require('apigee-sdk-mgmt-api'); 4 | 5 | module.exports = function(grunt) { 6 | 'use strict'; 7 | grunt.registerTask('deleteApiRevision', 'Delete an API revision. e.g. grunt deleteApiRevision:{revision_id}', function(revision) { 8 | if(grunt.option.flags().indexOf('--keep-last-revision') === -1) { //delete revision when --keep-last-revision flag is not passed 9 | var deleteRevision = function(error, response, body){ 10 | // if (!error && response.statusCode === 200) { 11 | // //var deletionResult = JSON.parse(body); 12 | // } 13 | grunt.log.debug(response.statusCode) 14 | grunt.log.debug(body); 15 | done(); 16 | } 17 | var revisionl = revision || (grunt.option('revisions_undeployed') && grunt.option('revisions_undeployed').revision); 18 | if(!revisionl) { 19 | grunt.log.warn('invalid revision. e.g. grunt deleteApiRevision:{revision_id}'); 20 | }else{ 21 | var done = this.async(); 22 | grunt_common.deleteApiRevision(grunt.config.get('apigee_profiles'), revisionl, deleteRevision, grunt.option.flags().indexOf('--curl') !== -1) 23 | } 24 | } 25 | else{ 26 | grunt.log.ok('task skipped. Remove --keep-last-revision flag to delete undeployed revision.') 27 | } 28 | }); 29 | }; 30 | -------------------------------------------------------------------------------- /grunt/tasks/deployApiRevision.js: -------------------------------------------------------------------------------- 1 | /*jslint node: true */ 2 | 3 | var grunt_common = require('apigee-sdk-mgmt-api'); 4 | 5 | module.exports = function(grunt) { 6 | 'use strict'; 7 | grunt.registerTask('deployApiRevision', 'Deploy an API revision. deployApiRevision:{revision_id}', function(revision) { 8 | var deployedRevision = function(error, response, body) { 9 | /*eslint no-empty:0 */ 10 | if (!error && response.statusCode === 200) { 11 | //var undeployResult = JSON.parse(body); 12 | } 13 | else{ 14 | done(false) 15 | } 16 | grunt.log.debug(response.statusCode) 17 | grunt.log.debug(body); 18 | done(error); 19 | } 20 | //core logic 21 | if(!revision) { 22 | grunt.fail.fatal('invalid revision id. provide either argument as deployApiRevision:{revision_id}'); 23 | }else{ 24 | var done = this.async(); 25 | grunt_common.deployApiRevision(grunt.config.get('apigee_profiles'), revision, deployedRevision, grunt.option.flags().indexOf('--curl') !== -1) 26 | } 27 | }); 28 | }; 29 | -------------------------------------------------------------------------------- /grunt/tasks/deployApiRevisionAlias.js: -------------------------------------------------------------------------------- 1 | /*jslint node: true */ 2 | 3 | module.exports = function(grunt) { 4 | 'use strict'; 5 | grunt.registerTask('deployApiRevisionAlias', 'Deploy an API revision alias', function() { 6 | switch (grunt.cli.tasks[0]){ 7 | case 'UPDATE_CURRENT_REVISION' : 8 | grunt.task.run('deployApiRevision:' + grunt.option('revisions_undeployed').name); 9 | break; 10 | case 'IMPORT_DEPLOY_BUMP_REVISION' : 11 | grunt.task.run('deployApiRevision:' + grunt.option('revision')); 12 | break; 13 | case 'DEPLOY_IMPORT_BUMP_SEAMLESS_REVISION' : 14 | grunt.task.run('deployApiRevision:' + grunt.option('revision')); 15 | break; 16 | default : 17 | grunt.task.run('deployApiRevision:' + grunt.option('revision')); 18 | break; 19 | } 20 | }); 21 | }; 22 | -------------------------------------------------------------------------------- /grunt/tasks/executeTests.js: -------------------------------------------------------------------------------- 1 | /*jslint node: true */ 2 | 3 | module.exports = function(grunt) { 4 | 'use strict'; 5 | grunt.registerTask('executeTests', 'execute tests when flag --skip-tests is absent.', function() { 6 | /*eslint if-curly-formatting:0 */ 7 | if(grunt.option.flags().indexOf('--skip-tests') === -1){ 8 | grunt.task.run('mochaTest'); 9 | } 10 | else{ 11 | grunt.fail.warn('tests skipped. Remove --skip-tests to execute tests.'); 12 | } 13 | }); 14 | }; 15 | -------------------------------------------------------------------------------- /grunt/tasks/force.js: -------------------------------------------------------------------------------- 1 | /*jslint node: true */ 2 | 3 | module.exports = function(grunt) { 4 | 'use strict'; 5 | var previous_force_state = grunt.option("force"); 6 | grunt.registerTask("force", 'Enable --force via its targets "on", "off", "restore"', function(set){ 7 | if (set === "on") { 8 | grunt.option("force",true); 9 | } 10 | else if (set === "off") { 11 | grunt.option("force",false); 12 | } 13 | else if (set === "restore") { 14 | grunt.option("force",previous_force_state); 15 | } 16 | }); 17 | } 18 | -------------------------------------------------------------------------------- /grunt/tasks/getAllApiRevisions.js: -------------------------------------------------------------------------------- 1 | /*jslint node: true */ 2 | 3 | var grunt_common = require('apigee-sdk-mgmt-api'); 4 | 5 | module.exports = function(grunt) { 6 | 'use strict'; 7 | grunt.registerTask('getAllApiRevisions', 'Retrieve all API revisions', function() { 8 | var apiRevisions = function(error, response, body) { 9 | grunt.log.writeln(response.statusCode) 10 | grunt.log.writeln(body); 11 | done(); 12 | } 13 | var done = this.async(); 14 | grunt_common.getAllApiRevisions(grunt.config.get('apigee_profiles'), apiRevisions, grunt.option.flags().indexOf('--curl') !== -1) 15 | }); 16 | }; 17 | -------------------------------------------------------------------------------- /grunt/tasks/getDeployedApiRevisions.js: -------------------------------------------------------------------------------- 1 | /*jslint node: true */ 2 | 3 | var grunt_common = require('apigee-sdk-mgmt-api'); 4 | 5 | module.exports = function(grunt) { 6 | 'use strict'; 7 | grunt.registerTask('getDeployedApiRevisions', 'Retrieve Last API revision deployed', function() { 8 | var apiRevisions = function(error, response, body) { 9 | if (!error && (response.statusCode === 200 || response.statusCode === 400)) { 10 | var apiDeployedrevisions = JSON.parse(body); 11 | grunt.option('revisions_deployed', apiDeployedrevisions); 12 | } 13 | grunt.log.debug(response.statusCode) 14 | grunt.log.debug(JSON.stringify(response.headers)) 15 | grunt.log.debug(body); 16 | done(); 17 | } 18 | var done = this.async(); 19 | grunt_common.getDeployedApiRevisions(grunt.config.get('apigee_profiles'), apiRevisions, grunt.option.flags().indexOf('--curl') !== -1) 20 | }); 21 | }; 22 | -------------------------------------------------------------------------------- /grunt/tasks/installNpmRevision.js: -------------------------------------------------------------------------------- 1 | /*jslint node: true */ 2 | 3 | var grunt_common = require('apigee-sdk-mgmt-api'); 4 | 5 | module.exports = function(grunt) { 6 | 'use strict'; 7 | grunt.registerTask('installNpmRevision', 'install npm API revision. e.g. grunt installNpmRevision:{revision_id}', function(revision) { 8 | var done = this.async(); 9 | if(grunt.option('upload-modules')){ 10 | grunt.log.debug('skipped installNpmRevision due to upload-modules option'); 11 | done(); 12 | return false; 13 | } 14 | var installNpmRevision = function(error, response, body) { 15 | /*eslint no-empty:0 */ 16 | if (!error && response.statusCode === 200) { 17 | //var undeployResult = JSON.parse(body); 18 | } 19 | else{ 20 | done(false) 21 | } 22 | grunt.log.debug(response.statusCode) 23 | grunt.log.debug(body); 24 | done(error); 25 | } 26 | 27 | var revisionl = revision || grunt.option('revision'); 28 | //core logic 29 | if(!revisionl) { 30 | grunt.fail.fatal('invalid revision id. provide either argument as installNpmRevision:{revision_id}'); 31 | }else{ 32 | grunt_common.npmInstallRevision(grunt.config.get('apigee_profiles'), revisionl, installNpmRevision, grunt.option.flags().indexOf('--curl') !== -1) 33 | } 34 | }); 35 | }; 36 | -------------------------------------------------------------------------------- /grunt/tasks/installNpmRevisionAlias.js: -------------------------------------------------------------------------------- 1 | /*jslint node: true */ 2 | 3 | module.exports = function(grunt) { 4 | 'use strict'; 5 | grunt.registerTask('installNpmRevisionAlias', 'install npm API revision alias.', function() { 6 | //grunt.log.debug('Node.js modules not include in API bundle.'); 7 | switch (grunt.cli.tasks[0]){ 8 | case 'UPDATE_CURRENT_REVISION' : 9 | grunt.task.run('installNpmRevision:' + grunt.option('revisions_undeployed').name); 10 | break; 11 | case 'IMPORT_DEPLOY_BUMP_REVISION' : 12 | grunt.task.run('installNpmRevision:' + grunt.option('revision')); 13 | break; 14 | case 'DEPLOY_IMPORT_BUMP_SEAMLESS_REVISION' : 15 | grunt.task.run('installNpmRevision:' + grunt.option('revision')); 16 | break; 17 | default : 18 | grunt.task.run('installNpmRevision:' + grunt.option('revision')); 19 | break; 20 | } 21 | }); 22 | }; 23 | -------------------------------------------------------------------------------- /grunt/tasks/saveGitRevision.js: -------------------------------------------------------------------------------- 1 | module.exports = function(grunt) { 2 | 'use strict'; 3 | grunt.registerTask('saveGitRevision', function() { 4 | var git = require('gift'); 5 | var repo = git(".") 6 | var done = this.async(); 7 | var os = require("os"); 8 | repo.current_commit( function(err, commit){ 9 | var gitRevision = ""; 10 | if(commit) { 11 | gitRevision = "git commit: " + commit.id.substring(0,7) + " by " + commit.author.name + " on " + os.hostname(); 12 | grunt.option('gitRevision', gitRevision); 13 | } 14 | done(); 15 | } 16 | )}); 17 | }; -------------------------------------------------------------------------------- /grunt/tasks/undeployApiRevision.js: -------------------------------------------------------------------------------- 1 | /*jslint node: true */ 2 | 3 | var grunt_common = require('apigee-sdk-mgmt-api'); 4 | 5 | module.exports = function(grunt) { 6 | 'use strict'; 7 | grunt.registerTask('undeployApiRevision', 'Undeploy an API revision. e.g. grunt undeployApiRevision:{revision_id}', function(revision) { 8 | var undeployedRevision = function(error, response, body) { 9 | /*eslint if-curly-formatting:0 */ 10 | if (!error && response.statusCode === 200){ 11 | var undeployResult = JSON.parse(body); 12 | grunt.option('revisions_undeployed', undeployResult) 13 | } 14 | grunt.log.debug(response.statusCode) 15 | grunt.log.debug(body); 16 | done(); 17 | } 18 | var revisionl = revision || (grunt.option('revisions_deployed') && grunt.option('revisions_deployed').revision && grunt.option('revisions_deployed').revision[0].name); 19 | //revisions_deployed are only set when grunt is run in sequence, otherwise it'll be null 20 | if(!revisionl) { 21 | grunt.log.warn('Invalid revision id. e.g. grunt undeployApiRevision:{revision_id}'); 22 | }else{ 23 | var done = this.async(); 24 | grunt_common.undeployApiRevision(grunt.config.get('apigee_profiles'), revisionl, undeployedRevision, grunt.option.flags().indexOf('--curl') !== -1); //send cURL switch to log curl commands 25 | } 26 | }); 27 | }; 28 | -------------------------------------------------------------------------------- /grunt/tasks/updateApiRevision.js: -------------------------------------------------------------------------------- 1 | /*jslint node: true */ 2 | 3 | var grunt_common = require('apigee-sdk-mgmt-api'); 4 | 5 | module.exports = function(grunt) { 6 | 'use strict'; 7 | grunt.registerTask('updateApiRevision', 'Update an API revision. e.g. grunt updateApiRevision:{revision_id}', function(revision) { 8 | var updateRevision = function(error, response, body){ 9 | grunt.log.debug(response.statusCode) 10 | done(); 11 | } 12 | var revisionl = revision || (grunt.option('revisions_undeployed') && grunt.option('revisions_undeployed').revision); 13 | if(!revisionl) { 14 | grunt.log.warn('invalid revision. e.g. grunt updateApiRevision:{revision_id}'); 15 | }else{ 16 | var done = this.async(); 17 | grunt_common.updateApiRevision(grunt.config.get('apigee_profiles'), revisionl, updateRevision, grunt.option.flags().indexOf('--curl') !== -1) 18 | } 19 | }); 20 | }; 21 | -------------------------------------------------------------------------------- /java/lib/expressions-1.0.0.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/apigeecs/apigee-deploy-grunt-plugin/8abc54f010d128a12f92b8ddc26e5b088f6370d0/java/lib/expressions-1.0.0.jar -------------------------------------------------------------------------------- /java/lib/message-flow-1.0.0.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/apigeecs/apigee-deploy-grunt-plugin/8abc54f010d128a12f92b8ddc26e5b088f6370d0/java/lib/message-flow-1.0.0.jar -------------------------------------------------------------------------------- /java/src/com/example/SimpleJavaCallout.java: -------------------------------------------------------------------------------- 1 | package com.example; 2 | 3 | import java.io.PrintWriter; 4 | import java.io.StringWriter; 5 | import com.apigee.flow.execution.ExecutionContext; 6 | import com.apigee.flow.execution.ExecutionResult; 7 | import com.apigee.flow.execution.spi.Execution; 8 | import com.apigee.flow.message.MessageContext; 9 | 10 | public class SimpleJavaCallout implements Execution{ 11 | public ExecutionResult execute(MessageContext messageContext, ExecutionContext executionContext) { 12 | try 13 | { 14 | messageContext.setVariable("response.content", "Payload set by a Java Callout"); 15 | return ExecutionResult.SUCCESS; 16 | 17 | } catch (Exception e) { 18 | StringWriter sw = new StringWriter(); 19 | e.printStackTrace(new PrintWriter(sw)); 20 | String exceptionAsString = sw.toString(); 21 | messageContext.setVariable("ERROR_MESSAGE", exceptionAsString); 22 | return ExecutionResult.ABORT; 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /node/.gitignore: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/apigeecs/apigee-deploy-grunt-plugin/8abc54f010d128a12f92b8ddc26e5b088f6370d0/node/.gitignore -------------------------------------------------------------------------------- /node/app.js: -------------------------------------------------------------------------------- 1 | var express = require('express'), 2 | forecastweather_node = require('./resources/forecastweather_node'), 3 | app = express(); 4 | //lessMiddleware = require('less-middleware'), 5 | path = require('path'); 6 | app.use(express.static(path.join(__dirname, 'public'))); 7 | app.get('/forecastweather_node/:woeid', forecastweather_node.get) 8 | app.listen(9000); -------------------------------------------------------------------------------- /node/node_modules/path/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "author": { 3 | "name": "Joyent", 4 | "url": "http://www.joyent.com" 5 | }, 6 | "name": "path", 7 | "description": "Node.JS path module", 8 | "keywords": [ 9 | "ender", 10 | "path" 11 | ], 12 | "version": "0.4.9", 13 | "homepage": "http://nodejs.org/docs/v0.4.9/api/path.html", 14 | "repository": { 15 | "type": "git", 16 | "url": "git://github.com/coolaj86/nodejs-libs-4-browser.git" 17 | }, 18 | "main": "./path.js", 19 | "directories": { 20 | "lib": "." 21 | }, 22 | "engines": { 23 | "node": ">= 0.2.0", 24 | "ender": ">= 0.5.0" 25 | }, 26 | "dependencies": {}, 27 | "devDependencies": {}, 28 | "_npmJsonOpts": { 29 | "file": "/Users/coolaj86/.npm/path/0.4.9/package/package.json", 30 | "wscript": false, 31 | "contributors": false, 32 | "serverjs": false 33 | }, 34 | "_id": "path@0.4.9", 35 | "_engineSupported": true, 36 | "_npmVersion": "1.0.15", 37 | "_nodeVersion": "v0.4.8", 38 | "_defaultsLoaded": true, 39 | "dist": { 40 | "shasum": "380c68d01273e43f9368d7ad50fee5e3e8d477f0", 41 | "tarball": "http://registry.npmjs.org/path/-/path-0.4.9.tgz" 42 | }, 43 | "scripts": {}, 44 | "_shasum": "380c68d01273e43f9368d7ad50fee5e3e8d477f0", 45 | "_from": "path@^0.4.9", 46 | "_resolved": "https://registry.npmjs.org/path/-/path-0.4.9.tgz", 47 | "bugs": { 48 | "url": "https://github.com/coolaj86/nodejs-libs-4-browser/issues" 49 | }, 50 | "readme": "ERROR: No README data found!" 51 | } 52 | -------------------------------------------------------------------------------- /node/node_modules/path/path.js: -------------------------------------------------------------------------------- 1 | var process = process || {}; 2 | (function () { 3 | "use strict"; 4 | 5 | // Copyright Joyent, Inc. and other Node contributors. 6 | // 7 | // Permission is hereby granted, free of charge, to any person obtaining a 8 | // copy of this software and associated documentation files (the 9 | // "Software"), to deal in the Software without restriction, including 10 | // without limitation the rights to use, copy, modify, merge, publish, 11 | // distribute, sublicense, and/or sell copies of the Software, and to permit 12 | // persons to whom the Software is furnished to do so, subject to the 13 | // following conditions: 14 | // 15 | // The above copyright notice and this permission notice shall be included 16 | // in all copies or substantial portions of the Software. 17 | // 18 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 19 | // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 20 | // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN 21 | // NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 22 | // DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 23 | // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE 24 | // USE OR OTHER DEALINGS IN THE SOFTWARE. 25 | 26 | 27 | var isWindows = process.platform === 'win32'; 28 | 29 | 30 | // resolves . and .. elements in a path array with directory names there 31 | // must be no slashes, empty elements, or device names (c:\) in the array 32 | // (so also no leading and trailing slashes - it does not distinguish 33 | // relative and absolute paths) 34 | function normalizeArray(parts, allowAboveRoot) { 35 | // if the path tries to go above the root, `up` ends up > 0 36 | var up = 0; 37 | for (var i = parts.length; i >= 0; i--) { 38 | var last = parts[i]; 39 | if (last == '.') { 40 | parts.splice(i, 1); 41 | } else if (last === '..') { 42 | parts.splice(i, 1); 43 | up++; 44 | } else if (up) { 45 | parts.splice(i, 1); 46 | up--; 47 | } 48 | } 49 | 50 | // if the path is allowed to go above the root, restore leading ..s 51 | if (allowAboveRoot) { 52 | for (; up--; up) { 53 | parts.unshift('..'); 54 | } 55 | } 56 | 57 | return parts; 58 | } 59 | 60 | 61 | if (isWindows) { 62 | 63 | // Regex to split a filename into [*, dir, basename, ext] 64 | // windows version 65 | var splitPathRe = /^(.+(?:[\\\/](?!$)|:)|[\\\/])?((?:.+?)?(\.[^.]*)?)$/; 66 | 67 | // Regex to split a windows path into three parts: [*, device, slash, 68 | // tail] windows-only 69 | var splitDeviceRe = 70 | /^([a-zA-Z]:|[\\\/]{2}[^\\\/]+[\\\/][^\\\/]+)?([\\\/])?(.*?)$/; 71 | 72 | // path.resolve([from ...], to) 73 | // windows version 74 | exports.resolve = function() { 75 | var resolvedDevice = '', 76 | resolvedTail = '', 77 | resolvedAbsolute = false; 78 | 79 | for (var i = arguments.length; i >= -1; i--) { 80 | var path = (i >= 0) 81 | ? arguments[i] 82 | : process.cwd(); 83 | 84 | // Skip empty and invalid entries 85 | if (typeof path !== 'string' || !path) { 86 | continue; 87 | } 88 | 89 | var result = splitDeviceRe.exec(path), 90 | device = result[1] || '', 91 | isUnc = device && device.charAt(1) !== ':', 92 | isAbsolute = !!result[2] || isUnc, // UNC paths are always absolute 93 | tail = result[3]; 94 | 95 | if (device && 96 | resolvedDevice && 97 | device.toLowerCase() !== resolvedDevice.toLowerCase()) { 98 | // This path points to another device so it is not applicable 99 | continue; 100 | } 101 | 102 | if (!resolvedDevice) { 103 | resolvedDevice = device; 104 | } 105 | if (!resolvedAbsolute) { 106 | resolvedTail = tail + '\\' + resolvedTail; 107 | resolvedAbsolute = isAbsolute; 108 | } 109 | 110 | if (resolvedDevice && resolvedAbsolute) { 111 | break; 112 | } 113 | } 114 | 115 | if (!resolvedAbsolute && resolvedDevice) { 116 | // If we still don't have an absolute path, 117 | // prepend the current path for the device found. 118 | 119 | // TODO 120 | // Windows stores the current directories for 'other' drives 121 | // as hidden environment variables like =C:=c:\windows (literally) 122 | // var deviceCwd = os.getCwdForDrive(resolvedDevice); 123 | var deviceCwd = ''; 124 | 125 | // If there is no cwd set for the drive, it is at root 126 | resolvedTail = deviceCwd + '\\' + resolvedTail; 127 | resolvedAbsolute = true; 128 | } 129 | 130 | // Replace slashes (in UNC share name) by backslashes 131 | resolvedDevice = resolvedDevice.replace(/\//g, '\\'); 132 | 133 | // At this point the path should be resolved to a full absolute path, 134 | // but handle relative paths to be safe (might happen when process.cwd() 135 | // fails) 136 | 137 | // Normalize the tail path 138 | 139 | function f(p) { 140 | return !!p; 141 | } 142 | 143 | resolvedTail = normalizeArray(resolvedTail.split(/[\\\/]+/).filter(f), 144 | !resolvedAbsolute).join('\\'); 145 | 146 | return (resolvedDevice + (resolvedAbsolute ? '\\' : '') + resolvedTail) || 147 | '.'; 148 | }; 149 | 150 | // windows version 151 | exports.normalize = function(path) { 152 | var result = splitDeviceRe.exec(path), 153 | device = result[1] || '', 154 | isUnc = device && device.charAt(1) !== ':', 155 | isAbsolute = !!result[2] || isUnc, // UNC paths are always absolute 156 | tail = result[3], 157 | trailingSlash = /[\\\/]$/.test(tail); 158 | 159 | // Normalize the tail path 160 | tail = normalizeArray(tail.split(/[\\\/]+/).filter(function(p) { 161 | return !!p; 162 | }), !isAbsolute).join('\\'); 163 | 164 | if (!tail && !isAbsolute) { 165 | tail = '.'; 166 | } 167 | if (tail && trailingSlash) { 168 | tail += '\\'; 169 | } 170 | 171 | return device + (isAbsolute ? '\\' : '') + tail; 172 | }; 173 | 174 | // windows version 175 | exports.join = function() { 176 | function f(p) { 177 | return p && typeof p === 'string'; 178 | } 179 | 180 | var paths = Array.prototype.slice.call(arguments, 0).filter(f); 181 | var joined = paths.join('\\'); 182 | 183 | // Make sure that the joined path doesn't start with two slashes 184 | // - it will be mistaken for an unc path by normalize() - 185 | // unless the paths[0] also starts with two slashes 186 | if (/^[\\\/]{2}/.test(joined) && !/^[\\\/]{2}/.test(paths[0])) { 187 | joined = joined.slice(1); 188 | } 189 | 190 | return exports.normalize(joined); 191 | }; 192 | 193 | 194 | } else /* posix */ { 195 | 196 | // Regex to split a filename into [*, dir, basename, ext] 197 | // posix version 198 | var splitPathRe = /^(.+\/(?!$)|\/)?((?:.+?)?(\.[^.]*)?)$/; 199 | 200 | // path.resolve([from ...], to) 201 | // posix version 202 | exports.resolve = function() { 203 | var resolvedPath = '', 204 | resolvedAbsolute = false; 205 | 206 | for (var i = arguments.length; i >= -1 && !resolvedAbsolute; i--) { 207 | var path = (i >= 0) 208 | ? arguments[i] 209 | : process.cwd(); 210 | 211 | // Skip empty and invalid entries 212 | if (typeof path !== 'string' || !path) { 213 | continue; 214 | } 215 | 216 | resolvedPath = path + '/' + resolvedPath; 217 | resolvedAbsolute = path.charAt(0) === '/'; 218 | } 219 | 220 | // At this point the path should be resolved to a full absolute path, but 221 | // handle relative paths to be safe (might happen when process.cwd() fails) 222 | 223 | // Normalize the path 224 | resolvedPath = normalizeArray(resolvedPath.split('/').filter(function(p) { 225 | return !!p; 226 | }), !resolvedAbsolute).join('/'); 227 | 228 | return ((resolvedAbsolute ? '/' : '') + resolvedPath) || '.'; 229 | }; 230 | 231 | // path.normalize(path) 232 | // posix version 233 | exports.normalize = function(path) { 234 | var isAbsolute = path.charAt(0) === '/', 235 | trailingSlash = path.slice(-1) === '/'; 236 | 237 | // Normalize the path 238 | path = normalizeArray(path.split('/').filter(function(p) { 239 | return !!p; 240 | }), !isAbsolute).join('/'); 241 | 242 | if (!path && !isAbsolute) { 243 | path = '.'; 244 | } 245 | if (path && trailingSlash) { 246 | path += '/'; 247 | } 248 | 249 | return (isAbsolute ? '/' : '') + path; 250 | }; 251 | 252 | 253 | // posix version 254 | exports.join = function() { 255 | var paths = Array.prototype.slice.call(arguments, 0); 256 | return exports.normalize(paths.filter(function(p, index) { 257 | return p && typeof p === 'string'; 258 | }).join('/')); 259 | }; 260 | } 261 | 262 | 263 | exports.dirname = function(path) { 264 | var dir = splitPathRe.exec(path)[1] || ''; 265 | if (!dir) { 266 | // No dirname 267 | return '.'; 268 | } else if (dir.length === 1 || 269 | (isWindows && dir.length <= 3 && dir.charAt(1) === ':')) { 270 | // It is just a slash or a drive letter with a slash 271 | return dir; 272 | } else { 273 | // It is a full dirname, strip trailing slash 274 | return dir.substring(0, dir.length - 1); 275 | } 276 | }; 277 | 278 | 279 | exports.basename = function(path, ext) { 280 | var f = splitPathRe.exec(path)[2] || ''; 281 | // TODO: make this comparison case-insensitive on windows? 282 | if (ext && f.substr(-1 * ext.length) === ext) { 283 | f = f.substr(0, f.length - ext.length); 284 | } 285 | return f; 286 | }; 287 | 288 | 289 | exports.extname = function(path) { 290 | return splitPathRe.exec(path)[3] || ''; 291 | }; 292 | 293 | 294 | exports.exists = function(path, callback) { 295 | process.binding('fs').stat(path, function(err, stats) { 296 | if (callback) callback(err ? false : true); 297 | }); 298 | }; 299 | 300 | 301 | exports.existsSync = function(path) { 302 | try { 303 | process.binding('fs').stat(path); 304 | return true; 305 | } catch (e) { 306 | return false; 307 | } 308 | }; 309 | 310 | }()); 311 | -------------------------------------------------------------------------------- /node/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "express-test", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "author": "", 10 | "license": "ISC", 11 | "devDependencies": { 12 | "path": "^0.4.9" 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /node/public/images/tree.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/apigeecs/apigee-deploy-grunt-plugin/8abc54f010d128a12f92b8ddc26e5b088f6370d0/node/public/images/tree.jpg -------------------------------------------------------------------------------- /node/public/tree.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/apigeecs/apigee-deploy-grunt-plugin/8abc54f010d128a12f92b8ddc26e5b088f6370d0/node/public/tree.jpg -------------------------------------------------------------------------------- /node/resources/forecastweather_node.js: -------------------------------------------------------------------------------- 1 | exports.get = function(req, res){ 2 | var woeid = req.params.woeid; 3 | //res.end('Hello, World!'); 4 | var request = require('request'); 5 | 6 | request('http://weather.yahooapis.com/forecastrss?w=' + woeid, function (error, response, body) { 7 | if (!error && response.statusCode == 200) { 8 | res.writeHead(200, {'Content-Type':'application/json;charset=utf-8'}); 9 | res.end(JSON.stringify(response)); 10 | } 11 | }) 12 | } -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "apigee-deploy-grunt-plugin", 3 | "version": "0.0.4", 4 | "description": "Out of the box I include scaffolding apiproxy, Node.js, and Gruntfile to build your app.", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "grunt mochaTest -env=test" 8 | }, 9 | "repository": { 10 | "type": "git", 11 | "url": "https://github.com/{org}/<%= apiname %>" 12 | }, 13 | "keywords": [ 14 | "apigee", 15 | "deploy", 16 | "lifecycle", 17 | "API" 18 | ], 19 | "author": "{YOUR_NAME_HERE}", 20 | "license": "ISC", 21 | "dependencies": { 22 | "apigee-sdk-mgmt-api": "^1.1.0", 23 | "async": "^0.9.0", 24 | "blanket": "^1.1.6", 25 | "chai": "^1.9.1", 26 | "chai-http": "^1.0.0", 27 | "curl-cmd": "0.0.0", 28 | "gift": "^0.4.3-1", 29 | "grunt": "^0.4.5", 30 | "grunt-available-tasks": "^0.5.0", 31 | "grunt-complexity": "^0.2.0", 32 | "grunt-contrib-clean": "^0.5.0", 33 | "grunt-contrib-compress": "^0.12.0", 34 | "grunt-contrib-concat": "^0.5.0", 35 | "grunt-contrib-copy": "^0.5.0", 36 | "grunt-contrib-jshint": "^0.10.0", 37 | "grunt-eslint": "^1.0.0", 38 | "grunt-mkdir": "^0.1.2", 39 | "grunt-mocha-test": "^0.11.0", 40 | "grunt-notify": "^0.3.1", 41 | "grunt-shell": "^1.1.1", 42 | "grunt-string-replace": "^1.0.0", 43 | "grunt-update-submodules": "^0.4.1", 44 | "grunt-apigee-kvm": "*", 45 | "grunt-xmlpoke": "^0.6.0", 46 | "jshint-stylish": "^0.4.0", 47 | "load-grunt-config": "^0.12.0", 48 | "load-grunt-tasks": "^0.6.0", 49 | "request": "^2.37.0", 50 | "time-grunt": "^1.0.0", 51 | "cfonts": "0.0.13", 52 | "grunt-prompt": "^1.3.0", 53 | "grunt-apigee-import-api-bundle": "^0.0.1" 54 | }, 55 | "devDependencies": {} 56 | } 57 | -------------------------------------------------------------------------------- /tests/diego-forecastweather-grunt-plugin-api-test-data.js: -------------------------------------------------------------------------------- 1 | /*jslint node: true */ 2 | 3 | var common = { 4 | url : "https://testmyapi-test.apigee.net/weathergrunt/apigee/forecastrss", 5 | url_node : "https://testmyapi-test.apigee.net/weathergrunt/forecastweather_node" 6 | } 7 | 8 | exports.simpleWeatherArray = function(){ 9 | "use strict"; 10 | var weatherArray = [ 11 | { 12 | "url" : common.url + "?w=2502265", 13 | "name" : "Sunnyvale", 14 | "responseCode" : 200 15 | }, 16 | { 17 | "url" : common.url + "?w=766273", 18 | "name" : "Madrid", 19 | "responseCode" : 200 20 | }, 21 | ]; 22 | return weatherArray; 23 | } 24 | 25 | exports.simpleFormatArray = function(){ 26 | "use strict"; 27 | var weatherArray = [ 28 | { 29 | "url" : common.url + "?w=2502265", 30 | "name" : "Sunnyvale", 31 | "contentType" : "text/xml;charset=UTF-8", 32 | "responseCode" : 200 33 | }, 34 | { 35 | "url" : common.url + "?w=766273", 36 | "name" : "Madrid", 37 | "contentType" : "application/json;charset=UTF-8", 38 | "responseCode" : 200 39 | }, 40 | ]; 41 | return weatherArray; 42 | } 43 | 44 | exports.nodeArray = function(){ 45 | "use strict"; 46 | var weatherArray = [ 47 | { 48 | "url" : common.url_node + "/368149", 49 | "name" : "Cali-Colombia", 50 | "contentAssertion" : "Weather for Cali", 51 | "responseCode" : 200 52 | }, 53 | ]; 54 | return weatherArray; 55 | } -------------------------------------------------------------------------------- /tests/forecastweather-grunt-plugin-api-prod-data.js: -------------------------------------------------------------------------------- 1 | /*jslint node: true */ 2 | 3 | var common = { 4 | url : "https://testmyapi-test.apigee.net/weathergrunt/apigee/forecastrss", 5 | url_node : "https://testmyapi-test.apigee.net/weathergrunt/forecastweather_node" 6 | } 7 | 8 | exports.simpleWeatherArray = function(){ 9 | "use strict"; 10 | var weatherArray = [ 11 | { 12 | "url" : common.url + "?w=2502265", 13 | "name" : "Sunnyvale", 14 | "responseCode" : 200 15 | }, 16 | { 17 | "url" : common.url + "?w=766273", 18 | "name" : "Madrid", 19 | "responseCode" : 200 20 | }, 21 | ]; 22 | return weatherArray; 23 | } 24 | 25 | exports.simpleFormatArray = function(){ 26 | "use strict"; 27 | var weatherArray = [ 28 | { 29 | "url" : common.url + "?w=2502265", 30 | "name" : "Sunnyvale", 31 | "contentType" : "text/xml;charset=UTF-8", 32 | "responseCode" : 200 33 | }, 34 | { 35 | "url" : common.url + "?w=766273", 36 | "name" : "Madrid", 37 | "contentType" : "application/json;charset=UTF-8", 38 | "responseCode" : 200 39 | }, 40 | ]; 41 | return weatherArray; 42 | } 43 | 44 | exports.nodeArray = function(){ 45 | "use strict"; 46 | var weatherArray = [ 47 | { 48 | "url" : common.url_node + "/368149", 49 | "name" : "Cali-Colombia", 50 | "contentAssertion" : "Weather for Cali", 51 | "responseCode" : 200 52 | }, 53 | ]; 54 | return weatherArray; 55 | } -------------------------------------------------------------------------------- /tests/forecastweather-grunt-plugin-api-test-data.js: -------------------------------------------------------------------------------- 1 | /*jslint node: true */ 2 | 3 | var common = { 4 | url : "https://testmyapi-test.apigee.net/weathergrunt/apigee/forecastrss", 5 | url_node : "https://testmyapi-test.apigee.net/weathergrunt/forecastweather_node" 6 | } 7 | 8 | exports.simpleWeatherArray = function(){ 9 | "use strict"; 10 | var weatherArray = [ 11 | { 12 | "url" : common.url + "?w=2502265", 13 | "name" : "Sunnyvale" , 14 | "responseCode" : 200 15 | }, 16 | { 17 | "url" : common.url + "?w=766273", 18 | "name" : "Madrid", 19 | "responseCode" : 200 20 | }, 21 | ]; 22 | return weatherArray; 23 | } 24 | 25 | exports.simpleFormatArray = function(){ 26 | "use strict"; 27 | var weatherArray = [ 28 | { 29 | "url" : common.url + "?w=2502265", 30 | "name" : "Sunnyvale", 31 | "contentType" : "text/xml;charset=UTF-8", 32 | "responseCode" : 200 33 | }, 34 | { 35 | "url" : common.url + "?w=766273", 36 | "name" : "Madrid", 37 | "contentType" : "application/json;charset=UTF-8", 38 | "responseCode" : 200 39 | }, 40 | ]; 41 | return weatherArray; 42 | } 43 | 44 | exports.nodeArray = function(){ 45 | "use strict"; 46 | var weatherArray = [ 47 | { 48 | "url" : common.url_node + "/368149", 49 | "name" : "Cali-Colombia", 50 | "contentAssertion" : "Weather for Cali", 51 | "responseCode" : 200 52 | }, 53 | ]; 54 | return weatherArray; 55 | } 56 | -------------------------------------------------------------------------------- /tests/forecastweather-grunt-plugin-api.js: -------------------------------------------------------------------------------- 1 | /*globals describe:true, it:true, expect:true, before:true, beforeEach:true, after:true, afterEach:true*/ 2 | /*jslint node: true */ 3 | 4 | var grunt = require('grunt'); 5 | var testDataFilename = grunt.template.process("<%= apigee_profiles[grunt.option('env')].apiproxy + '-' + grunt.option('env') + '-data' %>"); 6 | var async = require('async') 7 | var chai = require('chai') 8 | var assert = require('chai').assert 9 | var expect = require('chai').expect 10 | var request = require('request'), chaiHttp = require('chai-http'); 11 | var weatherData = require('./' + testDataFilename); 12 | 13 | chai.use(chaiHttp); 14 | 15 | describe('ForecastWeather Public API Test',function() { 16 | "use strict"; 17 | before(function () { 18 | // get OAuth 2.0 token 19 | // console.log('initialize'); 20 | }); 21 | beforeEach(function () { 22 | //console.log('initialize each test'); 23 | }); 24 | 25 | describe('Check weather in cities', function() { 26 | async.each(weatherData.simpleWeatherArray() , function(cityData, callback) { 27 | it('you should be able to get forecast weather for ' + cityData.name + ' from this API Proxy.', function(done) { 28 | var options = { 29 | url: cityData.url, //'https://testmyapi-test.apigee.net/weathergrunt/apigee/forecastrss?w=2502265', 30 | headers: { 31 | 'User-Agent': 'request' 32 | } 33 | } 34 | request(options, function (error, response, body) { 35 | expect(body).to.contain(cityData.name) //Sunnyvale, Madrid 36 | assert.equal(cityData.responseCode, response.statusCode) 37 | done() 38 | }) 39 | }) 40 | callback(); 41 | }); 42 | }); 43 | 44 | describe('ForecastWeather Public API Test', function() { 45 | async.each(weatherData.simpleFormatArray(), function(cityData, callback){ 46 | it('you should be able to get forecast weather for ' + cityData.name + ' from this API Proxy in ' + cityData.contentType, function(done) { 47 | var options = { 48 | url: cityData.url,//'https://testmyapi-test.apigee.net/weathergrunt/apigee/forecastrss?w=2502265', 49 | headers: { 50 | 'User-Agent': 'request', 51 | 'Accept' : cityData.contentType//'text/xml;charset=UTF-8' 52 | } 53 | } 54 | request(options, function (error, response, body) { 55 | expect(body).to.contain(cityData.name) 56 | assert.equal(cityData.responseCode, response.statusCode) 57 | expect(response).to.have.header('content-type', cityData.contentType); 58 | done() 59 | }) 60 | }) 61 | callback(); 62 | }); 63 | 64 | async.each(weatherData.nodeArray(),function(cityData, callback){ 65 | it('you should be able to get forecast weather for ' + cityData.name + ' from this API Proxy via a Node.js Target', function(done) { 66 | var options = { 67 | url: cityData.url, 68 | headers: { 69 | 'User-Agent': 'request' 70 | } 71 | } 72 | request(options, function (error, response, body) { 73 | expect(body).to.contain(cityData.contentAssertion) 74 | assert.equal(cityData.responseCode, response.statusCode) 75 | done() 76 | }) 77 | }) 78 | callback(); 79 | }) 80 | 81 | it('you should be able to retrieve image with content-type header image/jpg', function(done) { 82 | chai.request('https://testmyapi-test.apigee.net/weathergrunt/images') 83 | .get('/tree.jpg') 84 | .set('test', '123') 85 | .end(function (err, res) { 86 | expect(res).to.have.status(200); 87 | expect(res).to.have.header('content-type','image/jpeg'); 88 | done(); 89 | }); 90 | }) 91 | 92 | it('you should be able use oauth', function(done) { 93 | done() 94 | }) 95 | //Disabled by default (JavaCallout Policy) 96 | /* describe('you should be able to make a call to resource which executes a JavaCallout', function() { 97 | it('with a response "Payload set by a Java Callout"', function(done) { 98 | chai.request('https://testmyapi-test.apigee.net/weathergrunt') 99 | .get('/javacallout') 100 | .res(function (res) { 101 | //expect(res.content).to.contain('Weather for Cali') 102 | expect(res.text).to.contain('Payload set by a Java Callout') 103 | done(); 104 | }); 105 | }) 106 | })*/ 107 | 108 | }); 109 | after(function () { 110 | // teardown test 111 | //console.log('destroy'); 112 | }); 113 | afterEach(function () { 114 | //console.log('finalize each test'); 115 | }); 116 | }); 117 | 118 | //pending test 119 | describe('Array - pending test', function(){ 120 | "use strict"; 121 | describe('#indexOf()', function(){ 122 | it('should return -1 when the value is not present') 123 | }) 124 | }) 125 | -------------------------------------------------------------------------------- /tools/CommonProxy/apiproxy/policies/Script-External-Variables.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | Script-External-Variables 4 | 5 | 6 | jsc://js_external_vars.js 7 | -------------------------------------------------------------------------------- /tools/CommonProxy/apiproxy/policies/xmltojson-1.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | XMLtoJSON-1 5 | 6 | 7 | yahoo 8 | response 9 | response 10 | 11 | -------------------------------------------------------------------------------- /tools/CommonProxy/apiproxy/proxies/common-js-vars.flowfrag: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Script-External-Variables 5 | -------------------------------------------------------------------------------- /tools/CommonProxy/apiproxy/proxies/xmltojson.flowfrag: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | xmltojson-1 5 | -------------------------------------------------------------------------------- /tools/CommonProxy/apiproxy/resources/jsc/js_external_vars.js: -------------------------------------------------------------------------------- 1 | var client_start_time = context.getVariable('client.received.start.timestamp'); 2 | var target_start_time = context.getVariable('target.sent.start.timestamp'); 3 | var client_end_time = context.getVariable('system.timestamp'); 4 | var target_end_time = context.getVariable('target.received.end.timestamp'); 5 | 6 | context.setVariable("total_request_time",(client_end_time-client_start_time)+''); 7 | context.setVariable("total_target_time", (target_end_time-target_start_time)+''); 8 | 9 | response.headers['total_request_time'] =(client_end_time-client_start_time)+''; 10 | response.headers['total_target_time'] = (target_end_time-target_start_time)+''; -------------------------------------------------------------------------------- /tools/forecastweather-jmeter-example/README.md: -------------------------------------------------------------------------------- 1 | README.md 2 | ==== 3 | 4 | Switching to JMeter framework to run tests is as easy as following these steps: 5 | 6 | #### **Step 1:** 7 | Copy pom.xml file to root folder (where Gruntfile.js file resides) 8 | #### **Step 2:** 9 | Copy tests jmx and data files to tests root folder 10 | #### **Step 3:** 11 | Enable shell task to run run_jmeter_tests in Gruntfile.js 12 | 13 | That's it! You can now run JMeter tests from Grunt through Maven. ```grunt shell:run_jmeter_tests```. -------------------------------------------------------------------------------- /tools/forecastweather-jmeter-example/pom.xml: -------------------------------------------------------------------------------- 1 | 16 | 18 | 4.0.0 19 | apigee 20 | forecastweatherapi-grunt 21 | 1.0 22 | forecastweatherapi-grunt 23 | pom 24 | 25 | 26 | test 27 | 28 | 29 | 30 | com.lazerycode.jmeter 31 | jmeter-maven-plugin 32 | 1.8.1 33 | 34 | 35 | jmeter-tests 36 | test 37 | 38 | jmeter 39 | 40 | 41 | ${skipTests} 42 | true 43 | false 44 | 45 | weather_test.csv 46 | 5 47 | 5 48 | 2 49 | 50 | 51 | 52 | 53 | 54 | tests 55 | false 56 | 57 | 58 | 59 | 60 | 61 | 62 | prod 63 | 64 | 65 | 66 | com.lazerycode.jmeter 67 | jmeter-maven-plugin 68 | 1.8.1 69 | 70 | 71 | jmeter-tests 72 | test 73 | 74 | jmeter 75 | 76 | 77 | ${skipTests} 78 | true 79 | false 80 | 81 | weather_prod.csv 82 | 5 83 | 5 84 | 2 85 | 86 | 87 | 88 | 89 | 90 | tests 91 | false 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | -------------------------------------------------------------------------------- /tools/forecastweather-jmeter-example/tests/weather.jmx: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | false 7 | false 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | continue 16 | 17 | false 18 | ${__P(loopCount,1)} 19 | 20 | ${__P(threadNum,10)} 21 | ${__P(rampUpPeriodSecs,3)} 22 | 1384727829000 23 | 1384727829000 24 | false 25 | 26 | 27 | 28 | 29 | 30 | , 31 | 32 | ${__P(testData,weather_dev.csv)} 33 | false 34 | true 35 | shareMode.all 36 | true 37 | 38 | 39 | 40 | 41 | "${execute}" == "Y" 42 | false 43 | 44 | 45 | 46 | 47 | 48 | 49 | false 50 | ${woeid} 51 | = 52 | true 53 | w 54 | 55 | 56 | false 57 | ${units} 58 | = 59 | true 60 | u 61 | 62 | 63 | 64 | ${servername} 65 | 66 | 67 | 68 | 69 | 70 | ${path} 71 | GET 72 | true 73 | false 74 | true 75 | false 76 | HttpClient4 77 | false 78 | 79 | 80 | 81 | 82 | 83 | ${assertheader} 84 | 85 | Assertion.response_headers 86 | true 87 | 2 88 | dddd 89 | 90 | 91 | 92 | false 93 | city 94 | "city": "(.+?)" 95 | $1$ 96 | NOT_FOUND 97 | 98 | 99 | 100 | 101 | 102 | ${assertcontent} 103 | 104 | Assertion.response_data 105 | false 106 | 2 107 | 108 | 109 | 110 | 111 | ${assertcontentnegative} 112 | 113 | Assertion.response_data 114 | false 115 | 6 116 | 117 | 118 | 119 | 120 | false 121 | 122 | saveConfig 123 | 124 | 125 | true 126 | true 127 | true 128 | 129 | true 130 | false 131 | true 132 | true 133 | true 134 | true 135 | true 136 | false 137 | true 138 | true 139 | false 140 | false 141 | false 142 | false 143 | true 144 | 0 145 | true 146 | true 147 | true 148 | true 149 | true 150 | true 151 | 152 | 153 | ../target/jmeter/results/weather.jtl 154 | 155 | 156 | 157 | 158 | 159 | 160 | -------------------------------------------------------------------------------- /tools/forecastweather-jmeter-example/tests/weather_prod.csv: -------------------------------------------------------------------------------- 1 | testcaseid,userstory,testcasedescription,servername,path,woeid,units,execute,assertheader,assertcontent,env,assertcontentnegative 2 | WEATHER-1-1,WEATHER-1,valid location (woeid),testmyapi-prod.apigee.net,/weather/forecastrss,2502265,c,Y,HTTP/1.1 200 OK,city,DEV,NON-EXISTENT-WORD 3 | WEATHER-1-2,WEATHER-1,valid location (woeid),testmyapi-prod.apigee.net,/weather/forecastrss,56465629,c,Y,HTTP/1.1 200 OK,city,DEV,NON-EXISTENT-WORD 4 | WEATHER-1-3,WEATHER-1,weather at maine,testmyapi-prod.apigee.net,/weather/forecastrss,2524575,c,Y,HTTP/1.1 200 OK,city,DEV,NON-EXISTENT-WORD 5 | WEATHER-1-4,WEATHER-1,invalid location (woed),testmyapi-prod.apigee.net,/weather/forecastrss,00000000,c,Y,HTTP/1.1 200 OK,,DEV,city,NON-EXISTENT-WORD 6 | WEATHER-1-5,WEATHER-1,invalid location (woed) test that channel word does not exist,testmyapi-prod.apigee.net,/weather/forecastrss,00000000,c,N,HTTP/1.1 200 OK,,DEV,channel -------------------------------------------------------------------------------- /tools/forecastweather-jmeter-example/tests/weather_test.csv: -------------------------------------------------------------------------------- 1 | testcaseid,userstory,testcasedescription,servername,path,woeid,units,execute,assertheader,assertcontent,env,assertcontentnegative 2 | WEATHER-1-1,WEATHER-1,valid location (woeid),testmyapi-test.apigee.net,/weather/forecastrss,2502265,c,Y,HTTP/1.1 200 OK,city,DEV,NON-EXISTENT-WORD 3 | WEATHER-1-2,WEATHER-1,valid location (woeid),testmyapi-test.apigee.net,/weather/forecastrss,56465629,c,Y,HTTP/1.1 200 OK,city,DEV,NON-EXISTENT-WORD 4 | WEATHER-1-3,WEATHER-1,weather at maine,testmyapi-test.apigee.net,/weather/forecastrss,2524575,c,Y,HTTP/1.1 200 OK,city,DEV,NON-EXISTENT-WORD 5 | WEATHER-1-4,WEATHER-1,invalid location (woed),testmyapi-test.apigee.net,/weather/forecastrss,00000000,c,Y,HTTP/1.1 200 OK,,DEV,city,NON-EXISTENT-WORD 6 | WEATHER-1-5,WEATHER-1,invalid location (woed) test that channel word does not exist,testmyapi-test.apigee.net,/weather/forecastrss,00000000,c,N,HTTP/1.1 200 OK,,DEV,channel 7 | -------------------------------------------------------------------------------- /tools/proxy-dependency-maven-plugin/CommonProxy/apiproxy/policies/Script-External-Variables.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | Script-External-Variables 4 | 5 | 6 | jsc://js_external_vars.js 7 | -------------------------------------------------------------------------------- /tools/proxy-dependency-maven-plugin/CommonProxy/apiproxy/policies/xmltojson-1.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | XMLtoJSON-1 5 | 6 | 7 | yahoo 8 | response 9 | response 10 | 11 | -------------------------------------------------------------------------------- /tools/proxy-dependency-maven-plugin/CommonProxy/apiproxy/proxies/common-js-vars.flowfrag: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Script-External-Variables 5 | -------------------------------------------------------------------------------- /tools/proxy-dependency-maven-plugin/CommonProxy/apiproxy/proxies/xmltojson.flowfrag: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | xmltojson-1 5 | -------------------------------------------------------------------------------- /tools/proxy-dependency-maven-plugin/CommonProxy/apiproxy/resources/jsc/js_external_vars.js: -------------------------------------------------------------------------------- 1 | var client_start_time = context.getVariable('client.received.start.timestamp'); 2 | var target_start_time = context.getVariable('target.sent.start.timestamp'); 3 | var client_end_time = context.getVariable('system.timestamp'); 4 | var target_end_time = context.getVariable('target.received.end.timestamp'); 5 | 6 | context.setVariable("total_request_time",(client_end_time-client_start_time)+''); 7 | context.setVariable("total_target_time", (target_end_time-target_start_time)+''); 8 | 9 | response.headers['total_request_time'] =(client_end_time-client_start_time)+''; 10 | response.headers['total_target_time'] = (target_end_time-target_start_time)+''; -------------------------------------------------------------------------------- /tools/proxy-dependency-maven-plugin/Gruntfile.js: -------------------------------------------------------------------------------- 1 | /*jslint node: true */ 2 | 3 | module.exports = function(grunt) { 4 | var apigee_conf = require('./apigee-config.js') 5 | require('load-grunt-tasks')(grunt); 6 | // Project configuration. 7 | grunt.initConfig({ 8 | pkg: grunt.file.readJSON('package.json'), 9 | apigee_profiles : apigee_conf.profiles(grunt),//{ 10 | clean: ["target"], 11 | mkdir: { 12 | all: { 13 | options: { 14 | create: ['target'] 15 | }, 16 | }, 17 | }, 18 | copy: { 19 | main: { 20 | src: 'apiproxy/**', 21 | dest: 'target/', 22 | }, 23 | }, 24 | // make a zipfile 25 | compress: { 26 | main: { 27 | options: { 28 | mode : 'zip', 29 | archive: function(){ 30 | var ap = grunt.config.get("apigee_profiles") 31 | return 'target/' + ap[ap.env].apiproxy + ".zip" 32 | } 33 | }, 34 | files: [ 35 | {expand: true, cwd: 'target/apiproxy/', src: ['**'], dest: 'apiproxy/' }, // makes all src relative to cwd 36 | ] 37 | } 38 | }, 39 | // task for configuration management: search and replace elements within XML files 40 | xmlpoke: apigee_conf.config(apigee_conf.profiles(grunt).env), 41 | // Configure a mochaTest task 42 | mochaTest: { 43 | test: { 44 | options: { 45 | reporter: 'spec', 46 | timeout : 5000 47 | }, 48 | src: ['tests/**.js'] 49 | } 50 | }, 51 | jshint: { 52 | options: { //see options reference http://www.jshint.com/docs/options/ 53 | curly: true, 54 | eqeqeq: true, 55 | eqnull: true, 56 | browser: true, 57 | asi : true, 58 | debug : true, 59 | undef : true, 60 | unused : true, 61 | maxcomplexity : 5, 62 | reporter: require('jshint-stylish') 63 | }, 64 | all: ['Gruntfile.js', 'apiproxy/**/*.js', 'tests/**/*.js', 'tasks/*.js'] 65 | }, 66 | eslint: { // task 67 | options: { 68 | config: 'conf/eslint.json', // custom config 69 | rulesdir: ['conf/rules'] // custom rules 70 | }, 71 | target: ['Gruntfile.js', 'apiproxy/**/*.js', 'tests/**/*.js', 'tasks/*.js'] // array of files 72 | }, 73 | shell: { // Task 74 | callMaven: { // Target 75 | command: 'mvn install -Ptest -Dusername=$ae_username -Dpassword=$ae_password' 76 | } 77 | } 78 | }) 79 | 80 | grunt.registerTask('buildApiBundle', 'Build zip without importing it to Edge', ['jshint', 'eslint', 'clean', 'mkdir','copy', 'shell:callMaven','xmlpoke','compress']); 81 | 82 | // Default task(s). 83 | //delete and then import revision keeping same id 84 | grunt.registerTask('default', [ 'buildApiBundle',/*'force:on',*/ 'getDeployedApiRevisions', 'undeployApiRevision', 85 | 'deleteApiRevision', /*'force:restore',*/ 'importApiBundle', 'deployApiRevision', 'executeTests']); 86 | 87 | grunt.loadTasks('tasks'); 88 | if(grunt.option.flags().indexOf('--help') === -1 && !apigee_conf.profiles(grunt).env) { 89 | grunt.fail.fatal('Invalid environment flag --env={env}. Provide environment as argument, see apigee_profiles in Grunfile.js.') 90 | } 91 | 92 | }; -------------------------------------------------------------------------------- /tools/proxy-dependency-maven-plugin/README.md: -------------------------------------------------------------------------------- 1 | ## Support for Maven Plugins 2 | Grunt Plugin can be enabled to support Maven Plugins. For instance Proxy Dependency Plugin can be incorporated by following these steps: 3 | 4 | #### Step 1: Include pom.xml under Gruntfile.js file 5 | #### Step 2: Include CommonProxy folder where fragments are extracted from 6 | #### Step 3: Incorporate shell task into Gruntfile.js. This task execute a shell task that calls a Maven target. Then after that all other tasks will continue to be executed by Grunt 7 | See example included 8 | ``` 9 | shell: { // Task 10 | callMaven: { // Target 11 | command: 'mvn install -Ptest -Dusername=$ae_username -Dpassword=$ae_password' 12 | } 13 | } 14 | ``` 15 | #### Step 4: Execute this shell task after copy target. Note that this task will replace target folder. 16 | 17 | ``` 18 | grunt.registerTask('buildApiBundle', 'Build zip without importing it to Edge', ['jshint', 'eslint', 'clean', 'mkdir','copy', 'shell:callMaven','xmlpoke','compress']); 19 | 20 | ``` 21 | 22 | #### Step 5: This plugin requires that apiproxy folder is under another folder to work properly. So, for instance, proxySrcDir will point to apiproxyroot, then apiproxy will exist be under it. 23 | ``` 24 | ./apiproxyroot 25 | ``` 26 | -------------------------------------------------------------------------------- /tools/proxy-dependency-maven-plugin/apiproxy/forecastweather-grunt-plugin-api.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 1384727595303 5 | dzuluaga@apigee.com 6 | @@DESCRIPTION TO BE REPLACED BY GITHUB ID 7 | weather 8 | 1384727595303 9 | dzuluaga@apigee.com 10 | 11 | xmltojson-1 12 | 13 | 14 | default 15 | 16 | 17 | 18 | 19 | default 20 | 21 | false 22 | 23 | -------------------------------------------------------------------------------- /tools/proxy-dependency-maven-plugin/apiproxy/policies/Assign-Message-Remove-Suffix.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | Assign Message Remove Suffix 4 | 5 | 6 | 7 | target.copy.pathsuffix 8 | false 9 | 10 | true 11 | 12 | -------------------------------------------------------------------------------- /tools/proxy-dependency-maven-plugin/apiproxy/policies/JSON-to-XML.xml: -------------------------------------------------------------------------------- 1 | @@JSON-to-XML.xml -------------------------------------------------------------------------------- /tools/proxy-dependency-maven-plugin/apiproxy/policies/Script-External-Variables.xml: -------------------------------------------------------------------------------- 1 | @@Script-External-Variables.xml -------------------------------------------------------------------------------- /tools/proxy-dependency-maven-plugin/apiproxy/policies/SimpleJavaCallout.xml.bak: -------------------------------------------------------------------------------- 1 | 2 | 3 | com.example.SimpleJavaCallout 4 | java://javaCallouts.jar 5 | -------------------------------------------------------------------------------- /tools/proxy-dependency-maven-plugin/apiproxy/policies/XMLtoJSON-Common.xml: -------------------------------------------------------------------------------- 1 | @@XMLtoJSON-Common.xml -------------------------------------------------------------------------------- /tools/proxy-dependency-maven-plugin/apiproxy/proxies/default.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | @@EXAMPLE-STEP-COMMON-XML-TO-JSON-GIT-SUBMODULE-FRAGMENT#1 10 | 11 | (proxy.pathsuffix MatchesPath "/apigee/**") and (request.verb = "GET") 12 | 13 | 14 | 15 | 16 | 17 | @@EXAMPLE-STEP-COMMON-XML-TO-JSON-GIT-SUBMODULE-FRAGMENT#2 18 | 19 | (proxy.pathsuffix MatchesPath "/forecastweather_node/**") and (request.verb = "GET") 20 | 21 | 22 | 23 | 24 | 25 | @@EXAMPLE-STEP-COMMON-XML-TO-JSON-GIT-SUBMODULE-FRAGMENT#3 26 | 27 | (proxy.pathsuffix MatchesPath "/images/**") and (request.verb = "GET") 28 | 29 | 30 | 31 | 32 | 33 | 34 | 38 | 39 | (proxy.pathsuffix MatchesPath "/javacallout") and (request.verb = "GET") 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | /weathergrunt 48 | default 49 | secure 50 | 51 | 52 | (proxy.pathsuffix MatchesPath "/apigee/**") and (request.verb = "GET") 53 | default 54 | 55 | 56 | 57 | node 58 | 59 | 60 | 61 | 62 | 63 | -------------------------------------------------------------------------------- /tools/proxy-dependency-maven-plugin/apiproxy/resources/jsc/js_external_vars.js: -------------------------------------------------------------------------------- 1 | // jshint ignore: start 2 | /* jshint ignore:start */ 3 | 4 | @@js_external_vars.js -------------------------------------------------------------------------------- /tools/proxy-dependency-maven-plugin/apiproxy/resources/jsc/search-and-replace-example.js: -------------------------------------------------------------------------------- 1 | function testFunction(){ 2 | 'use strict'; 3 | var test = "VALUE TO BE REPLACED #1"; 4 | return test; 5 | } 6 | //remove comment to see jshint triggering errors 7 | //var a = "test"; 8 | /*if(1 === 1) { 9 | if(2 === 2) { 10 | if(3 === 3) { 11 | if(4 === 4) { 12 | 13 | } 14 | } 15 | } 16 | 17 | }*/ 18 | 19 | testFunction(); -------------------------------------------------------------------------------- /tools/proxy-dependency-maven-plugin/apiproxy/resources/jsc/search-and-replace-example2.js: -------------------------------------------------------------------------------- 1 | function testFunction2(){ 2 | 'use strict'; 3 | var test2 = "VALUE TO BE REPLACED #2"; 4 | return test2; 5 | } 6 | 7 | testFunction2(); -------------------------------------------------------------------------------- /tools/proxy-dependency-maven-plugin/apiproxy/targets/default.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | Assign-Message-Remove-Suffix 10 | 11 | 12 | 13 | 14 | 15 | http://weather.yahooapis.com/forecastrss 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /tools/proxy-dependency-maven-plugin/apiproxy/targets/node.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | Node Target 4 | 5 | 6 | 7 | node://app.js 8 | 9 | -------------------------------------------------------------------------------- /tools/proxy-dependency-maven-plugin/pom.xml: -------------------------------------------------------------------------------- 1 | 18 | 20 | 26 | 27 | 4.0.0 28 | apigee 29 | apigee-deploy-grunt-plugin-maven 30 | 1.0 31 | apigee-deploy-grunt-plugin-maven 32 | pom 33 | 34 | 35 | 36 | test 37 | 38 | testmyapi 39 | validate,update 40 | test 41 | test 42 | https://api.enterprise.apigee.com 43 | v1 44 | ${org} 45 | ${username} 46 | ${password} 47 | ${options} 48 | 49 | 50 | 51 | 52 | 53 | 54 | io.apigee.build-tools.enterprise4g 55 | proxy-dependency-maven-plugin 56 | 2.0.0 57 | 58 | 59 | prepare-package 60 | 61 | resolve 62 | 63 | 64 | ./apiproxyroot 65 | 66 | ../CommonProxy 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | prod 77 | 78 | testmyapi 79 | validate,update 80 | test 81 | prod 82 | https://api.enterprise.apigee.com 83 | v1 84 | ${org} 85 | ${username} 86 | ${password} 87 | ${options} 88 | 89 | 90 | 91 | 92 | 93 | 94 | io.apigee.build-tools.enterprise4g 95 | proxy-dependency-maven-plugin 96 | 2.0.0 97 | 98 | 99 | prepare-package 100 | 101 | resolve 102 | 103 | 104 | ./apiproxyroot 105 | 106 | ../CommonProxy 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | --------------------------------------------------------------------------------