├── .eslintignore ├── .eslintrc.json ├── .github └── workflows │ └── pr-validation.yml ├── .gitignore ├── .npmrc ├── LICENSE ├── README.md ├── package.json └── packages ├── grunt-nwabap-ui5uploader ├── .npmrc ├── CHANGELOG.md ├── README.md ├── package.json └── tasks │ ├── grunt-nwabap-ui5uploader.js │ └── lib │ └── Logger.js ├── ui5-app ├── .npmrc ├── .ui5deployrc-tmp ├── .ui5deployrc-tmp-create-tp ├── .ui5deployrc-tmp-tp ├── Gruntfile.js ├── README.md ├── package.json ├── test-ui5-deployer-cli-configfile.cmd ├── test-ui5-deployer-cli-tmp-empty.cmd ├── test-ui5-deployer-cli-tmp.cmd ├── test-ui5-webapp-create-tp.cmd ├── test-ui5-webapp-tp.cmd ├── test-ui5-webapp.cmd ├── ui5-webapp-create-tp.yaml ├── ui5-webapp-tp.yaml ├── ui5-webapp.yaml ├── webapp │ ├── Component.js │ ├── controller │ │ ├── MainView.controller.js │ │ └── test │ │ │ └── test.txt │ ├── css │ │ └── style.css │ ├── i18n │ │ └── i18n.properties │ ├── img │ │ └── ui5logo.jpg │ ├── index.html │ ├── manifest.json │ ├── model │ │ └── models.js │ └── view │ │ └── MainView.view.xml └── webapp_tp │ ├── Component.js │ ├── controller │ ├── MainView.controller.js │ └── test │ │ └── test.txt │ ├── css │ └── style.css │ ├── i18n │ └── i18n.properties │ ├── img │ └── ui5logo.jpg │ ├── index.html │ ├── manifest.json │ ├── model │ └── models.js │ └── view │ └── MainView.view.xml ├── ui5-nwabap-deployer-cli ├── .npmrc ├── CHANGELOG.md ├── README.md ├── bin │ └── cli.js ├── lib │ ├── commands │ │ ├── deploy.js │ │ └── undeploy.js │ ├── config │ │ └── configHandler.js │ └── log │ │ └── Logger.js └── package.json ├── ui5-nwabap-deployer-core ├── .npmrc ├── CHANGELOG.md ├── README.md ├── lib │ ├── AdtClient.js │ ├── TransportManager.js │ ├── UI5ABAPRepoClient.js │ ├── Util.js │ └── ui5-nwabap-deployer-core.js └── package.json └── ui5-task-nwabap-deployer ├── .npmrc ├── CHANGELOG.md ├── README.md ├── lib ├── Logger.js └── ui5-task-nwabap-deployer.js ├── package.json └── ui5.yaml /.eslintignore: -------------------------------------------------------------------------------- 1 | # default 2 | dist/ 3 | node_modules/ 4 | 5 | # test 6 | packages/ui5-app/ -------------------------------------------------------------------------------- /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "node": true, 4 | "es6": true 5 | }, 6 | "parserOptions": { 7 | "ecmaVersion": 2017 8 | }, 9 | "extends": ["eslint:recommended", "google"], 10 | "rules": { 11 | "quotes": [ 12 | "error", 13 | "double", 14 | { "allowTemplateLiterals": true } 15 | ], 16 | "semi": [ 17 | "error", 18 | "always" 19 | ], 20 | "no-negated-condition": "off", 21 | "require-jsdoc": "off", 22 | "no-mixed-requires": "off", 23 | "max-len": "off", 24 | "no-implicit-coercion": [ 25 | 2, 26 | { "allow": ["!!"] } 27 | ], 28 | "comma-dangle": "off", 29 | "no-tabs": "off", 30 | "no-console": "off", 31 | "valid-jsdoc": "off", 32 | "linebreak-style": "off", 33 | "indent": "off", 34 | "no-async-promise-executor": "off", 35 | "no-prototype-builtins": "off", 36 | "object-curly-spacing": "off" 37 | }, 38 | "root": true 39 | } -------------------------------------------------------------------------------- /.github/workflows/pr-validation.yml: -------------------------------------------------------------------------------- 1 | name: pr-validation 2 | 3 | on: 4 | pull_request: 5 | branches: [ master ] 6 | jobs: 7 | validation: 8 | runs-on: ubuntu-latest 9 | steps: 10 | - uses: actions/checkout@v2 11 | - uses: actions/setup-node@v2 12 | with: 13 | node-version: '18' 14 | - run: npm install 15 | - run: npm run lint 16 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | 8 | # Runtime data 9 | pids 10 | *.pid 11 | *.seed 12 | *.pid.lock 13 | 14 | # Directory for instrumented libs generated by jscoverage/JSCover 15 | lib-cov 16 | 17 | # Coverage directory used by tools like istanbul 18 | coverage 19 | 20 | # nyc test coverage 21 | .nyc_output 22 | 23 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 24 | .grunt 25 | 26 | # Bower dependency directory (https://bower.io/) 27 | bower_components 28 | 29 | # node-waf configuration 30 | .lock-wscript 31 | 32 | # Compiled binary addons (https://nodejs.org/api/addons.html) 33 | build/Release 34 | 35 | # Dependency directories 36 | node_modules/ 37 | jspm_packages/ 38 | 39 | # TypeScript v1 declaration files 40 | typings/ 41 | 42 | # Optional npm cache directory 43 | .npm 44 | 45 | # Optional eslint cache 46 | .eslintcache 47 | 48 | # Optional REPL history 49 | .node_repl_history 50 | 51 | # Output of 'npm pack' 52 | *.tgz 53 | 54 | # Yarn Integrity file 55 | .yarn-integrity 56 | 57 | # dotenv environment variables file 58 | .env 59 | 60 | # next.js build output 61 | .next 62 | 63 | # build directories 64 | dist/ -------------------------------------------------------------------------------- /.npmrc: -------------------------------------------------------------------------------- 1 | package-lock=false -------------------------------------------------------------------------------- /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 [yyyy] [name of copyright owner] 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 | # ui5-nwabap-deployer 2 | UI5 Deployer to SAP NetWeaver ABAP application server 3 | 4 | This monorepo manages following packages which are used for deploying UI5 sources to a SAP NetWeaver ABAP application server. 5 | - [grunt-nwabap-ui5uploader](./packages/grunt-nwabap-ui5uploader/README.md): Grunt task to deploy UI5 sources. 6 | - [ui5-task-nwabap-deployer](./packages/ui5-task-nwabap-deployer/README.md): UI5 Tooling custom task to deploy UI5 sources. 7 | - [ui5-nwabap-deployer-cli](./packages/ui5-nwabap-deployer-cli/README.md): CLI to deploy UI5 sources. 8 | 9 | All tools use as base the [ui5-nwabap-deployer-core](./packages/ui5-nwabap-deployer-core/README.md) package which provides the core functionality for the UI5 source deployment. 10 | 11 | Starting from version 2.0.0 the deployer functionalities use the OData Service [/UI5/ABAP_RESPOSITORY_SRV](https://ui5.sap.com/#/topic/a883327a82ef4cc792f3c1e7b7a48de8) for deploying UI5 sources. Please make sure that the service is activated on your system (for details you can check SAP note [2999557](https://launchpad.support.sap.com/#/notes/2999557)). The new service does some sanity checks like e.g. virus scans. If you have not configured virus scan profiles or want to disable virus scanning please have a look to SAP note [2437892](https://launchpad.support.sap.com/#/notes/2437892). 12 |
Current deployer versions starting from version 2.0.0 can be used with SAP systems on which component SAP_UI 753 is installed. On systems with a lower version of component SAP_UI, you have to use version 1.x.x of this deployer. -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ui5-nwabap-deployer", 3 | "description": "UI5 Deployer to SAP NetWeaver ABAP", 4 | "version": "2.1.0", 5 | "author": { 6 | "name": "Florian Pfeffer", 7 | "email": "florian.pfeffer@outlook.com" 8 | }, 9 | "repository": { 10 | "type": "git", 11 | "url": "git+https://github.com/pfefferf/ui5-nwabap-deployer.git" 12 | }, 13 | "bugs": { 14 | "url": "https://github.com/pfefferf/ui5-nwabap-deployer/issues" 15 | }, 16 | "license": "Apache-2.0", 17 | "licenses": [ 18 | { 19 | "type": "Apache-2.0", 20 | "url": "https://github.com/pfefferf/ui5-nwabap-deployer/blob/master/LICENSE" 21 | } 22 | ], 23 | "keywords": [ 24 | "SAP NetWeaver ABAP", 25 | "UI5 Repository", 26 | "SAPUI5", 27 | "OpenUI5" 28 | ], 29 | "devDependencies": { 30 | "eslint": "^8.34.0", 31 | "eslint-config-google": "^0.14.0" 32 | }, 33 | "scripts": { 34 | "lint": "eslint ./", 35 | "lint:fix": "eslint ./ --fix" 36 | }, 37 | "workspaces": [ 38 | "./packages/*" 39 | ] 40 | } 41 | -------------------------------------------------------------------------------- /packages/grunt-nwabap-ui5uploader/.npmrc: -------------------------------------------------------------------------------- 1 | package-lock=false -------------------------------------------------------------------------------- /packages/grunt-nwabap-ui5uploader/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## 2.2.0 (2023-11-16) 2 | 3 | ### Fixes 4 | - Do grunt.fail.warn to raise exit code. 5 | 6 | ## 2.1.0 (2021-07-21) 7 | 8 | ### Features 9 | - New option `testMode` for deployment execution in test mode. 10 | 11 | ## 2.0.0 (2021-06-18) 12 | 13 | ### Features 14 | - Usage of /UI5/ABAP_REPOSITORY_SRV service for deployment instead of Team Provider API. 15 |
Team Provider API was set to deprecated. As a consequence starting from version 2.0.0 this deployer supports only systems with at least SAP_UI 753 component installed. 16 |
For all previous versions, version 1.x.x of this deployer needs to be used. 17 | 18 | ## 1.0.7 (2020-09-25) 19 | 20 | ### Feature 21 | - Support of `options.conn.customQueryParams` configuration option to be able to transfer custom parameters to the backend (for instance to bypass SAML2 or SPNEGO authentication). 22 | 23 | ## 1.0.6 (2020-04-27) 24 | 25 | ### General 26 | - Update dependency for ui5-nwabap-deployer-core package to v1.0.6 + setting for automatic update of ui5-nwabap-deployer-core package. 27 | 28 | ## 1.0.5 (2020-04-15) 29 | 30 | ### General 31 | - Update dependency for ui5-nwabap-deployer-core package to v1.0.5. 32 | 33 | ## 1.0.4 (2020-03-17) 34 | 35 | ### General 36 | - Update dependency for ui5-nwabap-deployer-core package to v1.0.4. 37 | 38 | ## 1.0.3 (2020-03-11) 39 | 40 | ### General 41 | - Update dependency for ui5-nwabap-deployer-core package to v1.0.3. 42 | 43 | ## 1.0.2 (2020-01-24) 44 | 45 | ### Fixes 46 | - Update depedency to ui5-nwabap-deployer-core to fix an issue regarding upload of files with special characters in their name. 47 | 48 | ## 1.0.1 (2019-12-20) 49 | 50 | ### General 51 | - Update dependency for ui5-nwabap-deployer-core package to v1.0.2. 52 | 53 | ## 1.0.0 (2019-11-24) 54 | 55 | ### General 56 | - Usage of ui5-nwabap-deployer-core package. 57 | 58 | ## 0.3.4 (2019-05-16) 59 | 60 | ### Features 61 | - In case the creation of a transport request is required via option `options.ui5.create_transport`, the number of the created transport request is logged in form "Creation of transport request required. Number of created transport request: \". 62 | 63 | ## 0.3.3 (2019-01-14) 64 | 65 | ### Fixes 66 | - Fixed issue regarding response status code check (thanks to [BurnerPat](https://github.com/BurnerPat)). 67 | 68 | ## 0.3.2 (2019-01-03) 69 | 70 | ### General 71 | - More detailed information in case an ADT API call fails (expected HTTP Status Code, actual HTTP Status Code, response body). 72 | 73 | ## 0.3.1 (2018-11-26) 74 | 75 | ### Fixes 76 | - Fixed issue in check if a transport for the user exists. 77 | 78 | ## 0.3.0 (2018-11-19) 79 | 80 | ### General 81 | - Replaced `unirest` library by `request` library, because of a better maintenance support. 82 | - Removal of duplicated code. 83 | - Support for Node versions < 5.0.0 skipped. 84 | 85 | ## 0.2.10 (2018-09-07) 86 | 87 | ### Features 88 | - Option `options.conn.proxy` added to specify a proxy for SAP NetWeaver ABAP server communcation (thanks to [anomistu](https://github.com/anomistu)). 89 | 90 | ## 0.2.9 (2018-06-01) 91 | 92 | ### Fixes 93 | - Removal of ISO-8859-1 to avoid side issues when project is edited in different editors. 94 | 95 | ## 0.2.8 (2018-05-02) 96 | 97 | ### Fixes 98 | - Fixed issue that "properties" files were uploaded with UTF-8 charset instead of ISO-8859-1 charset (thanks to [stockbal](https://github.com/stockbal)). 99 | 100 | ## 0.2.7 (2018-04-06) 101 | 102 | ### Fixes 103 | - Fixed issues for `transport_use_user_match' option (thanks to [stockbal](https://github.com/stockbal)). 104 | 105 | ## 0.2.6 (2018-04-05) 106 | 107 | ### Fixes 108 | - Fixed issues for `create_transport` option (thanks to [stockbal](https://github.com/stockbal)). 109 | 110 | ## 0.2.5 (2018-03-22) 111 | 112 | ### Fixes 113 | - Increased the backoff reconnect time interval. 114 | 115 | ## 0.2.4 (2018-03-22) 116 | 117 | ### Features 118 | - Reconnect if request was aborted due to a syscall error. 119 | 120 | ## 0.2.3 (2018-03-13) 121 | 122 | ### Fixes 123 | - In case of using the parameter to use a locked transport, an error occurred if no response body was available when the check was done for a locked transport. 124 | 125 | ## 0.2.2 (2018-02-27) 126 | 127 | ### General 128 | - NPM package dependencies updated. 129 | 130 | ### Features 131 | - Options `options.ui5.transport_use_user_match` and `options.ui5.transport_use_locked` added for specific cases. These options allow to reuse an existing not released transport for a user or the usage of a not released transport by which the UI5 application BSP container is locked. Thanks to [@mxschmitt](https://github.com/mxschmitt) for adding these options. 132 | 133 | ## 0.2.1 (2018-01-23) 134 | 135 | ### Features 136 | - Option `options.ui5.create_transport` and `options.ui5.transport_text` added to be able to create a new transport for each upload. Thanks to [@themasch](https://github.com/themasch) for adding that feature. 137 | 138 | ## 0.2.0 (2017-05-29) 139 | 140 | ### General 141 | - Grunt dependency updated to 1.0.1, to avoid security vulnerabilities of 0.4.5. 142 | 143 | ## 0.1.19 (2017-02-10) 144 | 145 | ### General 146 | - Adjustment of copyright information (year). 147 | - Now also tested with NW ABAP 7.51. 148 | 149 | ## 0.1.18 (2016-12-14) 150 | 151 | ### Fixes 152 | - Application index was recalculated independent of option setting. 153 | 154 | ## 0.1.17 (2016-12-12) 155 | 156 | ### Fixes 157 | - Error forwarding in case CSRF Token determination fails. 158 | 159 | ## 0.1.16 (2016-11-18) 160 | 161 | ### General 162 | - Switched from JSHint to ESLint linting. 163 | - Support for Node versions < 4.0.0 skipped. 164 | - Logging changed to an "immediate" logging. Folder/File actions are displayed immediately instead of collecting them. 165 | - Slashes at the end of a defined server URL are ignored. 166 | 167 | ## 0.1.15 (2016-10-25) 168 | 169 | ### General 170 | - Code simplification regarding sap-language parameter. 171 | 172 | ## 0.1.14 (2016-10-21) 173 | 174 | ### Fixes 175 | - BSP container length check excludes customer specific namespaces. 176 | - Deletion requests are fired with sap-language parameter. 177 | 178 | ## 0.1.13 (2016-09-30) 179 | 180 | ### General 181 | - Readme update. 182 | 183 | ## 0.1.12 (2016-09-28) 184 | 185 | ### General 186 | - Update dependency to Unirest 0.5.1 187 | 188 | ### Fixes 189 | - Client parameter handling 190 | 191 | ## 0.1.11 (2016-09-27) 192 | 193 | ### General 194 | - Added Travis CI support. 195 | 196 | ## 0.1.10 (2016-08-04) 197 | 198 | ### Fixes 199 | - Crash caused by empty files fixed. 200 | 201 | ## 0.1.9 (2016-08-01) 202 | 203 | ### Features 204 | - Option `options.conn.client` added to be able to specify a SAP client (in case no default client is maintained in system). 205 | 206 | ## 0.1.8 (2016-07-25) 207 | 208 | ### Features 209 | - Option `options.ui5.calc_appindex` steers if the SAPUI5 application index is recalculated (program /UI5/APP_INDEX_CALCULATE). Thanks to [@olirogers](https://github.com/olirogers) for adding this feature. 210 | 211 | ## 0.1.7 (2016-06-17) 212 | 213 | ### Fixes 214 | - Ensure ES 5.1 compatibility. 215 | 216 | ## 0.1.6 (2016-06-13) 217 | 218 | ### Fixes 219 | - Namespace handling for file comparison. 220 | 221 | ## 0.1.5 (2016-06-13) 222 | 223 | ### Features 224 | - Option `options.conn.useStrictSSL` steers if a strict SSL mode check has to be executed. In case of self signed certificates it can be set to `false` to allow an upload of files. 225 | 226 | ### Fixes 227 | - Correct encoding of namespaces for file upload. 228 | 229 | ## 0.1.4 (2016-06-08) 230 | 231 | ### Features 232 | - Option `options.ui5.language` introduced to be able to specify objects original language (e.g. for BSP Container). 233 | 234 | ## 0.1.3 (2016-04-01) 235 | 236 | ### General 237 | - Readme update. 238 | 239 | ## 0.1.2 (2016-03-30) 240 | 241 | ### General 242 | - Minor issues. 243 | 244 | ## 0.1.1 (2016-03-25) 245 | 246 | ### General 247 | - Readme update. 248 | 249 | ## 0.1.0 (2016-03-25) 250 | 251 | ### General 252 | - Initial release. -------------------------------------------------------------------------------- /packages/grunt-nwabap-ui5uploader/README.md: -------------------------------------------------------------------------------- 1 | [![npm version](https://badge.fury.io/js/grunt-nwabap-ui5uploader.svg)](https://badge.fury.io/js/grunt-nwabap-ui5uploader) 2 | 3 | # grunt-nwabap-ui5uploader 4 | 5 | 'grunt-nwabap-ui5uploader' is a Grunt Plugin which allows a developer to deploy SAPUI5/OpenUI5 sources to a SAP NetWeaver ABAP system as part of the Grunt task chain. 6 | 7 | Starting from version 2.0.0 this deployer uses the OData Service [/UI5/ABAP_RESPOSITORY_SRV](https://ui5.sap.com/#/topic/a883327a82ef4cc792f3c1e7b7a48de8) for deploying UI5 sources. Please make sure that the service is activated on your system (for details you can check SAP note [2999557](https://launchpad.support.sap.com/#/notes/2999557)). The new service does some sanity checks like e.g. virus scans. If you have not configured virus scan profiles or want to disable virus scanning please have a look to SAP note [2437892](https://launchpad.support.sap.com/#/notes/2437892). 8 |
Current deployer versions starting from version 2.0.0 can be used with SAP systems on which component SAP_UI 753 is installed. On systems with a lower version of component SAP_UI, you have to use version 1.x.x of this deployer. 9 | 10 | Some further information can be found in the [SAP Community](https://blogs.sap.com/2016/03/25/grunt-plugin-to-upload-ui5-sources-to-netweaver-abap/). 11 | 12 | 13 | ## Getting Started 14 | 15 | ## Installation and Pre-Conditions 16 | 17 | ### Grunt 18 | This plugin requires Grunt `1.0.1` 19 | 20 | If you haven't used [Grunt](http://gruntjs.com/) before, be sure to check out the [Getting Started](http://gruntjs.com/getting-started) guide, as it explains how to create a [Gruntfile](http://gruntjs.com/sample-gruntfile) as well as install and use Grunt plugins. Once you are familiar with that process, you may install this plugin with this command: 21 | 22 | ```shell 23 | npm install grunt-nwabap-ui5uploader --save-dev 24 | ``` 25 | 26 | Once the plugin has been installed, it may be enabled inside your Gruntfile with this line of JavaScript: 27 | 28 | ```js 29 | grunt.loadNpmTasks('grunt-nwabap-ui5uploader'); 30 | ``` 31 | 32 | 33 | ### ABAP Development Tool Services 34 | The ABAP Development Tool Services have to be activated on the SAP NetWeaver ABAP System (transaction SICF, path /sap/bc/adt). 35 | The user used for uploading the sources needs to have the authorization to use the ADT Services and to create/modify BSP applications. 36 | The plugin is tested with NW 7.40 and NW 7.5x systems. 37 | 38 | ## Task "nwabap_ui5uploader" 39 | 40 | ### Overview 41 | In your project's Gruntfile, add a section named `nwabap_ui5uploader` to the data object passed into `grunt.initConfig()`. 42 | 43 | ```js 44 | grunt.initConfig({ 45 | nwabap_ui5uploader: { 46 | options: { 47 | // Task-specific options go here. 48 | }, 49 | your_target: { 50 | // Target-specific file lists and/or options go here. 51 | }, 52 | } 53 | }); 54 | ``` 55 | 56 | ### Options 57 | 58 | #### options.conn.server 59 | Type: `String` 60 | 61 | Defines SAP NetWeaver ABAP server (for instance 'http://myserver:8000'). 62 | 63 | #### options.conn.client 64 | Type: `String` 65 | 66 | Optional parameter to specify the client (transferred as sap-client URL parameter). In case the option is not specified the default client is used if specified. 67 | 68 | #### options.conn.useStrictSSL 69 | Type: `Boolean` 70 | Default: `true` 71 | 72 | SSL mode handling. In case of self signed certificates the useStrictSSL mode option can be set to `false` to allow an upload of files. 73 | 74 | #### options.conn.proxy 75 | Type: `String` 76 | 77 | Optional parameter to specify proxy used for communication with SAP NetWeaver ABAP server (for instance 'http://myproxyhost:3128'). 78 | 79 | #### options.conn.testMode 80 | Type: `Boolean` 81 | Default: `false` 82 | 83 | Optional parameter to define if upload is done in test mode or not. 84 | 85 | #### options.conn.customQueryParams 86 | Type: `Object` 87 | 88 | Optional parameter with key/value pairs of custom parameters which are added to the call to the SAP NetWeaver ABAP server. For instance: 89 | ```js 90 | customQueryParams: { 91 | myCustomParameter1: 'myCustomValue1', 92 | myCustomParameter2: 'myCustomValue2' 93 | } 94 | ``` 95 | 96 | #### options.auth.user 97 | Type: `String` 98 | 99 | Defines the user which is used for access to the SAP NetWeaver ABAP server. It is not recommended to store the user in the Grunt file. It should be passed as argument. 100 | 101 | #### options.auth.pwd 102 | Type: `String` 103 | 104 | Defines the users password for access to the SAP NetWeaver ABAP server. It is not recommended to store the password in the Grunt file. It should be passed as argument. Do also not store the password as not masked value in a CI server environment. Use plugins to create masked variables (for instance the 'Mask Passwords Plugin' for Jenkins). 105 | 106 | #### options.ui5.language 107 | Type: `String` 108 | Default: `EN` 109 | 110 | Defines the objects original language. 111 | 112 | #### options.ui5.package 113 | Type: `String` 114 | 115 | Defines the development package in which the BSP container for the UI5 sources is available or should be created. 116 | 117 | #### options.ui5.bspcontainer 118 | Type: `String` 119 | 120 | Defines the name of the BSP container used for the storage of the UI5 sources. Length is restricted to 15 characters (exclusive customer specific namespaces, e.g. /YYY/). 121 | 122 | #### options.ui5.bspcontainer_text 123 | Type: `String` 124 | 125 | Defines the description of the BSP container. 126 | 127 | #### options.ui5.transportno 128 | Type: `String` 129 | Optional in case options.ui5.package is set to '$TMP'. 130 | 131 | Defines the transport number which logs the changes. For the transport number it would also make sense to pass it via an argument. 132 | 133 | #### options.ui5.create_transport 134 | Type: `Boolean` 135 | Default: `false` 136 | 137 | Set this option to true in case a new transport should be created each time the application is uploaded. 138 | 139 | #### options.ui5.transport_text 140 | Type: `String` 141 | 142 | Optional in case options.ui5.create_transport is set to false. 143 | 144 | Text for the new transport to be created. 145 | 146 | #### options.ui5.transport_use_user_match 147 | Type: `Boolean` 148 | Default: `false` 149 | 150 | Optional, if set to true, it will be tried to find a transport request of the given user. If no transport is found and `create_transport` is enabled a new one will be created and used for further file uploads. 151 | 152 | #### options.ui5.transport_use_locked 153 | Type: `Boolean` 154 | Default: `false` 155 | 156 | Optional, if set to true and a file upload failed due the BSP application is locked in another transport, the old (original one) one will be used to upload the files. 157 | 158 | #### options.resources.cwd 159 | Type: `String` 160 | 161 | Defines the base folder which contains the sources (for instance 'build'). It should be avoided to use everything from the ``webapp`` folder, because some directories in it should not be packaged and uploaded into a BSP application. To create a build, use another grunt task to copy the relevant files to the ``build`` folder. In addition for instance you can use the [openui5_preload](https://github.com/SAP/grunt-openui5#openui5_preload) task from the ``grunt-openui5`` plugin to create a component preload file. 162 | 163 | #### options.resources.src 164 | Type: `String` or `array of String` 165 | 166 | Defines files for upload. 167 | 168 | ### Usage Examples 169 | 170 | #### Upload to '$TMP' package 171 | 172 | ```js 173 | var sUser = grunt.option('user'); 174 | var sPwd = grunt.option('pwd'); 175 | 176 | grunt.initConfig({ 177 | nwabap_ui5uploader: { 178 | options: { 179 | conn: { 180 | server: 'http://myserver:8000', 181 | }, 182 | auth: { 183 | user: sUser, 184 | pwd: sPwd 185 | } 186 | }, 187 | upload_build: { 188 | options: { 189 | ui5: { 190 | package: '$TMP', 191 | bspcontainer: 'ZZ_UI5_LOCAL', 192 | bspcontainer_text: 'UI5 upload local objects' 193 | }, 194 | resources: { 195 | cwd: 'build-folder', 196 | src: '**/*.*' 197 | } 198 | } 199 | } 200 | } 201 | }); 202 | ``` 203 | 204 | #### Upload to a transport tracked package 205 | 206 | ```js 207 | var sUser = grunt.option('user'); 208 | var sPwd = grunt.option('pwd'); 209 | 210 | grunt.initConfig({ 211 | nwabap_ui5uploader: { 212 | options: { 213 | conn: { 214 | server: 'http://myserver:8000', 215 | }, 216 | auth: { 217 | user: sUser, 218 | pwd: sPwd 219 | } 220 | }, 221 | upload_build: { 222 | options: { 223 | ui5: { 224 | package: 'ZZ_UI5_REPO', 225 | bspcontainer: 'ZZ_UI5_TRACKED', 226 | bspcontainer_text: 'UI5 upload', 227 | transportno: 'DEVK900000' 228 | }, 229 | resources: { 230 | cwd: 'build-folder', 231 | src: '**/*.*' 232 | } 233 | } 234 | } 235 | } 236 | }); 237 | ``` 238 | 239 | #### Upload to a transport tracked package, creating a new transport for each upload 240 | 241 | ```js 242 | var sUser = grunt.option('user'); 243 | var sPwd = grunt.option('pwd'); 244 | 245 | grunt.initConfig({ 246 | nwabap_ui5uploader: { 247 | options: { 248 | conn: { 249 | server: 'http://myserver:8000', 250 | }, 251 | auth: { 252 | user: sUser, 253 | pwd: sPwd 254 | } 255 | }, 256 | upload_build: { 257 | options: { 258 | ui5: { 259 | package: 'ZZ_UI5_REPO', 260 | bspcontainer: 'ZZ_UI5_TRACKED', 261 | bspcontainer_text: 'UI5 upload', 262 | create_transport: true, 263 | transport_text: 'Transport for ZZ_UI5_TRACKED container' 264 | }, 265 | resources: { 266 | cwd: 'build-folder', 267 | src: '**/*.*' 268 | } 269 | } 270 | } 271 | } 272 | }); 273 | ``` 274 | 275 | #### Upload to different servers 276 | 277 | ```js 278 | var sUser = grunt.option('user'); 279 | var sPwd = grunt.option('pwd'); 280 | 281 | grunt.initConfig({ 282 | nwabap_ui5uploader: { 283 | upload_build_740: { 284 | options: { 285 | conn: { 286 | server: 'http://myserver740:8000', 287 | }, 288 | auth: { 289 | user: sUser, 290 | pwd: sPwd 291 | }, 292 | ui5: { 293 | package: 'ZZ_UI5_REPO', 294 | bspcontainer: 'ZZ_UI5_TRACKED', 295 | bspcontainer_text: 'UI5 upload', 296 | transportno: 'DEVK900000' 297 | }, 298 | resources: { 299 | cwd: 'build-folder', 300 | src: '**/*.*' 301 | } 302 | } 303 | }, 304 | upload_build_750: { 305 | options: { 306 | conn: { 307 | server: 'http://myserver750:8000', 308 | }, 309 | auth: { 310 | user: sUser, 311 | pwd: sPwd 312 | }, 313 | ui5: { 314 | package: 'ZZ_UI5_REPO', 315 | bspcontainer: 'ZZ_UI5_TRACKED', 316 | bspcontainer_text: 'UI5 upload', 317 | transportno: 'DEVK900000' 318 | }, 319 | resources: { 320 | cwd: 'build-folder', 321 | src: '**/*.*' 322 | } 323 | } 324 | } 325 | } 326 | }); 327 | ``` 328 | 329 | #### Create and reuse a transport request 330 | 331 | ```js 332 | var sUser = grunt.option('user'); 333 | var sPwd = grunt.option('pwd'); 334 | 335 | grunt.initConfig({ 336 | nwabap_ui5uploader: { 337 | options: { 338 | conn: { 339 | server: 'http://myserver:8000', 340 | }, 341 | auth: { 342 | user: sUser, 343 | pwd: sPwd 344 | } 345 | }, 346 | upload_build: { 347 | options: { 348 | ui5: { 349 | package: 'ZZ_UI5_REPO', 350 | bspcontainer: 'ZZ_UI5_TRACKED', 351 | bspcontainer_text: 'UI5 upload', 352 | create_transport: true, 353 | transport_use_user_match: true, 354 | transport_text: 'Transport for ZZ_UI5_TRACKED container' 355 | }, 356 | resources: { 357 | cwd: 'build-folder', 358 | src: '**/*.*' 359 | } 360 | } 361 | } 362 | } 363 | }); 364 | ``` 365 | 366 | ## Release History 367 | 368 | [CHANGELOG.md](https://github.com/pfefferf/ui5-nwabap-deployer/blob/master/packages/grunt-nwabap-ui5uploader/CHANGELOG.md) 369 | 370 | ## License 371 | 372 | [Apache License 2.0](http://www.apache.org/licenses/LICENSE-2.0) 373 | -------------------------------------------------------------------------------- /packages/grunt-nwabap-ui5uploader/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "grunt-nwabap-ui5uploader", 3 | "version": "2.2.0", 4 | "description": "UI5 source upload to SAP NetWeaver ABAP", 5 | "author": { 6 | "name": "Florian Pfeffer", 7 | "email": "florian.pfeffer@outlook.com" 8 | }, 9 | "repository": { 10 | "type": "git", 11 | "url": "git+https://github.com/pfefferf/ui5-nwabap-deployer.git" 12 | }, 13 | "bugs": { 14 | "url": "https://github.com/pfefferf/ui5-nwabap-deployer/issues" 15 | }, 16 | "license": "Apache-2.0", 17 | "licenses": [ 18 | { 19 | "type": "Apache-2.0", 20 | "url": "https://github.com/pfefferf/ui5-nwabap-deployer/blob/master/LICENSE" 21 | } 22 | ], 23 | "engines": { 24 | "node": ">= 12", 25 | "npm": ">= 7" 26 | }, 27 | "keywords": [ 28 | "gruntplugin", 29 | "SAP NetWeaver ABAP", 30 | "UI5 Repository", 31 | "SAPUI5", 32 | "OpenUI5" 33 | ], 34 | "devDependencies": { 35 | "grunt": "^1.0.1" 36 | }, 37 | "dependencies": { 38 | "ui5-nwabap-deployer-core": "^2.2.0" 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /packages/grunt-nwabap-ui5uploader/tasks/grunt-nwabap-ui5uploader.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | const Logger = require("./lib/Logger"); 4 | const path = require("path"); 5 | const ui5Deployercore = require("ui5-nwabap-deployer-core"); 6 | 7 | module.exports = function(grunt) { 8 | grunt.registerMultiTask("nwabap_ui5uploader", "UI5 source upload to SAP NetWeaver ABAP", async function() { 9 | const oLogger = new Logger(grunt); 10 | // eslint-disable-next-line no-invalid-this 11 | const done = this.async(); 12 | 13 | oLogger.log("Start deploying UI5 sources."); 14 | 15 | // options 16 | // eslint-disable-next-line no-invalid-this 17 | const oOptions = this.options({ 18 | resources: {} 19 | }); 20 | 21 | // get file names 22 | if (!oOptions.resources || !oOptions.resources.cwd || !oOptions.resources.src) { 23 | grunt.fail.warn("Resources configuration not (fully) specified."); 24 | done(); 25 | return; 26 | } 27 | 28 | const aFiles = []; 29 | 30 | grunt.file.expand({ 31 | cwd: oOptions.resources.cwd, 32 | filter: "isFile", 33 | dot: true 34 | }, oOptions.resources.src).forEach(function(sFilePath) { 35 | const sCompleteFilePath = path.join(oOptions.resources.cwd, sFilePath); 36 | aFiles.push({ 37 | path: sFilePath, 38 | content: grunt.file.read(sCompleteFilePath, { encoding: null}) 39 | }); 40 | }); 41 | 42 | const oDeployOptions = { 43 | conn: { 44 | server: oOptions.conn.server, 45 | client: oOptions.conn.client, 46 | useStrictSSL: oOptions.conn.useStrictSSL, 47 | proxy: oOptions.conn.proxy, 48 | testMode: !!oOptions.conn.testMode, 49 | customQueryParams: oOptions.conn.customQueryParams ? oOptions.conn.customQueryParams : {} 50 | }, 51 | auth: { 52 | user: oOptions.auth.user, 53 | pwd: oOptions.auth.pwd 54 | }, 55 | ui5: { 56 | language: oOptions.ui5.language, 57 | transportno: oOptions.ui5.transportno, 58 | package: oOptions.ui5.package, 59 | bspcontainer: oOptions.ui5.bspcontainer, 60 | bspcontainer_text: oOptions.ui5.bspcontainer_text, 61 | create_transport: !!oOptions.ui5.create_transport, 62 | transport_text: oOptions.ui5.transport_text, 63 | transport_use_user_match: !!oOptions.ui5.transport_use_user_match, 64 | transport_use_locked: !!oOptions.ui5.transport_use_locked 65 | } 66 | }; 67 | 68 | try { 69 | await ui5Deployercore.deployUI5toNWABAP(oDeployOptions, aFiles, oLogger); 70 | oLogger.log("UI5 sources successfully deployed."); 71 | } catch (oError) { 72 | oLogger.error(oError); 73 | grunt.fail.warn("Error occurred while deploying UI5 sources."); 74 | } 75 | 76 | done(); 77 | }); 78 | }; 79 | -------------------------------------------------------------------------------- /packages/grunt-nwabap-ui5uploader/tasks/lib/Logger.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | const PREFIX = "NWABAP UI5UPLOADER: "; 4 | 5 | class Logger { 6 | constructor(oGrunt) { 7 | this._oGrunt = oGrunt; 8 | } 9 | 10 | log(message) { 11 | this._oGrunt.log.writeln(PREFIX + message); 12 | } 13 | 14 | error(message) { 15 | this._oGrunt.log.writeln(PREFIX + message); 16 | } 17 | 18 | logVerbose(message) { 19 | this._oGrunt.verbose.writeln(PREFIX + message); 20 | } 21 | } 22 | 23 | module.exports = Logger; 24 | -------------------------------------------------------------------------------- /packages/ui5-app/.npmrc: -------------------------------------------------------------------------------- 1 | package-lock=false -------------------------------------------------------------------------------- /packages/ui5-app/.ui5deployrc-tmp: -------------------------------------------------------------------------------- 1 | { 2 | "cwd": "./webapp", 3 | "files": "**/*.*", 4 | "server": "http://vhcala4hci:50000", 5 | "client": "001", 6 | "useStrictSSL": false, 7 | "language": "EN", 8 | "package": "$TMP", 9 | "bspContainer": "ZZ_GUI5UP_TMP01", 10 | "bspContainerText": "Test UI5 Upload", 11 | "testMode": false 12 | } 13 | -------------------------------------------------------------------------------- /packages/ui5-app/.ui5deployrc-tmp-create-tp: -------------------------------------------------------------------------------- 1 | { 2 | "cwd": "./webapp_tp", 3 | "files": "**/*.*", 4 | "server": "http://vhcala4hci:50000", 5 | "client": "001", 6 | "useStrictSSL": false, 7 | "language": "EN", 8 | "package": "ZZ_UI5_REPOSITORY", 9 | "bspContainer": "ZZ_GUI5UP_TMP02", 10 | "bspContainerText": "Test UI5 Upload", 11 | "createTransport": true, 12 | "transportText": "Test UI5 Deployment", 13 | "transportUseLocked": true 14 | } 15 | -------------------------------------------------------------------------------- /packages/ui5-app/.ui5deployrc-tmp-tp: -------------------------------------------------------------------------------- 1 | { 2 | "cwd": "./webapp_tp", 3 | "files": "**/*.*", 4 | "server": "http://vhcala4hci:50000", 5 | "client": "001", 6 | "useStrictSSL": false, 7 | "language": "EN", 8 | "package": "ZZ_UI5_REPOSITORY", 9 | "bspContainer": "ZZ_GUI5UP_TMP02", 10 | "bspContainerText": "Test UI5 Upload", 11 | "transportNo": "A4HK900098" 12 | } 13 | -------------------------------------------------------------------------------- /packages/ui5-app/Gruntfile.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | module.exports = function (grunt) { 4 | 5 | grunt.loadNpmTasks("grunt-nwabap-ui5uploader"); 6 | 7 | const sUser = grunt.option("user"); 8 | const sPwd = grunt.option("pwd"); 9 | const sServer = grunt.option("server"); 10 | const bTestMode = !!grunt.option("testMode"); 11 | 12 | grunt.initConfig({ 13 | 14 | // test configurations 15 | nwabap_ui5uploader: { 16 | options: { 17 | conn: { 18 | server: sServer, 19 | customQueryParams: { 20 | spnego: "disabled/test1/test2", 21 | test2: "test2Value" 22 | }, 23 | testMode: bTestMode 24 | }, 25 | auth: { 26 | user: sUser, 27 | pwd: sPwd 28 | } 29 | }, 30 | upload_webapp: { 31 | options: { 32 | ui5: { 33 | package: "$TMP", 34 | bspcontainer: "ZZ_GUI5UP_TMP01", 35 | bspcontainer_text: "Test Grunt UI5 upload" 36 | }, 37 | resources: { 38 | cwd: "webapp", 39 | src: "**/*.*" 40 | } 41 | } 42 | }, 43 | upload_webapp_with_transport: { 44 | options: { 45 | ui5: { 46 | package: "ZZ_FP_UI5_REPOSITORY", 47 | bspcontainer: "ZZ_GUI5UP_TMP02", 48 | bspcontainer_text: "Test Grunt UI5 upload", 49 | create_transport: true, 50 | transport_use_locked: true, 51 | transport_text: "Test Transport" 52 | }, 53 | resources: { 54 | cwd: "webapp_tp", 55 | src: "**/*.*" 56 | } 57 | } 58 | } 59 | } 60 | }); 61 | 62 | grunt.registerTask("test", ["nwabap_ui5uploader:upload_webapp"]); 63 | grunt.registerTask("test_tp", ["nwabap_ui5uploader:upload_webapp_with_transport"]); 64 | }; 65 | -------------------------------------------------------------------------------- /packages/ui5-app/README.md: -------------------------------------------------------------------------------- 1 | # UI5 application for testing 2 | 3 | This test application is used to test the `grunt-nwabap-ui5uploader`, the `ui5-nwabap-deployer` and the `ui5-nwabap-deployer-cli` tooling. -------------------------------------------------------------------------------- /packages/ui5-app/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ui5-app", 3 | "version": "2.2.0", 4 | "description": "Test", 5 | "author": { 6 | "name": "Florian Pfeffer", 7 | "email": "florian.pfeffer@outlook.com" 8 | }, 9 | "repository": { 10 | "type": "git", 11 | "url": "git+https://github.com/pfefferf/ui5-nwabap-deployer.git" 12 | }, 13 | "bugs": { 14 | "url": "https://github.com/pfefferf/ui5-nwabap-deployer/issues" 15 | }, 16 | "license": "Apache-2.0", 17 | "licenses": [ 18 | { 19 | "type": "Apache-2.0", 20 | "url": "https://github.com/pfefferf/ui5-nwabap-deployer/blob/master/LICENSE" 21 | } 22 | ], 23 | "engines": { 24 | "node": ">= 12", 25 | "npm": ">= 7" 26 | }, 27 | "devDependencies": { 28 | "@ui5/cli": "^3.0.0", 29 | "grunt": "^1.0.1", 30 | "grunt-nwabap-ui5uploader": "2.1.0", 31 | "ui5-nwabap-deployer-cli": "2.2.0", 32 | "ui5-task-nwabap-deployer": "2.1.0" 33 | }, 34 | "ui5": { 35 | "dependencies": [ 36 | "ui5-task-nwabap-deployer" 37 | ] 38 | }, 39 | "scripts": { 40 | "deploy": "ui5 build --config ui5-webapp.yaml", 41 | "deploy:only": "ui5 build --config ui5-webapp.yaml --exclude-task * --include-task ui5-task-nwabap-deployer", 42 | "deploy:tp": "ui5 build --config ui5-webapp-tp.yaml", 43 | "deploy:create-tp": "ui5 build --config ui5-webapp-create-tp.yaml", 44 | "deploy:cli": "ui5-deployer deploy", 45 | "deploy:cli:conf": "ui5-deployer deploy --config .ui5deployrc-tmp", 46 | "deploy:cli:conf:tp": "ui5-deployer deploy --config .ui5deployrc-tmp-tp", 47 | "deploy:cli:conf:create-tp": "ui5-deployer deploy --config .ui5deployrc-tmp-create-tp", 48 | "undeploy:cli": "ui5-deployer undeploy", 49 | "undeploy:cli:conf": "ui5-deployer undeploy --config .ui5deployrc-tmp", 50 | "undeploy:cli:conf:tp": "ui5-deployer undeploy --config .ui5deployrc-tmp-tp", 51 | "undeploy:cli:conf:create-tp": "ui5-deployer undeploy --config .ui5deployrc-tmp-create-tp", 52 | "deploy:grunt:testmode": "grunt nwabap_ui5uploader:upload_webapp --testMode=true" 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /packages/ui5-app/test-ui5-deployer-cli-configfile.cmd: -------------------------------------------------------------------------------- 1 | ../../node_modules/.bin/ui5-deployer deploy --config ./.ui5deployrc-tmp --user developer --pwd notMyRealPwd -------------------------------------------------------------------------------- /packages/ui5-app/test-ui5-deployer-cli-tmp-empty.cmd: -------------------------------------------------------------------------------- 1 | ../../node_modules/.bin/ui5-deployer deploy --cwd ./webapp_empty --server http://vhcala4hci:50000 --client 001 --language EN --package $TMP --bspContainer ZZ_GUI5UP_TMP01 --bspContainerText "Test UI5 Upload" --user developer --pwd notMyRealPwd -------------------------------------------------------------------------------- /packages/ui5-app/test-ui5-deployer-cli-tmp.cmd: -------------------------------------------------------------------------------- 1 | ../../node_modules/.bin/ui5-deployer deploy --cwd ./webapp --server http://vhcala4hci:50000 --client 001 --language EN --package $TMP --bspContainer ZZ_GUI5UP_TMP01 --bspContainerText "Test UI5 Upload" --user developer --pwd notMyRealPwd -------------------------------------------------------------------------------- /packages/ui5-app/test-ui5-webapp-create-tp.cmd: -------------------------------------------------------------------------------- 1 | ui5 build --config ./ui5-webapp-create-tp.yaml -------------------------------------------------------------------------------- /packages/ui5-app/test-ui5-webapp-tp.cmd: -------------------------------------------------------------------------------- 1 | ui5 build --config ./ui5-webapp-tp.yaml -------------------------------------------------------------------------------- /packages/ui5-app/test-ui5-webapp.cmd: -------------------------------------------------------------------------------- 1 | ui5 build --config ./ui5-webapp.yaml -------------------------------------------------------------------------------- /packages/ui5-app/ui5-webapp-create-tp.yaml: -------------------------------------------------------------------------------- 1 | specVersion: "2.0" 2 | type: application 3 | metadata: 4 | name: ui5-app-webapp 5 | resources: 6 | configuration: 7 | paths: 8 | webapp: webapp_tp 9 | builder: 10 | customTasks: 11 | - name: ui5-task-nwabap-deployer 12 | afterTask: generateVersionInfo 13 | configuration: 14 | resources: 15 | pattern: "**/*.*" 16 | ui5: 17 | language: EN 18 | createTransport: true 19 | transportUseLocked: true 20 | transportUseUserMatch: false 21 | transportText: Test UIW-NWABAP-DEPLOYER Create Transport 22 | package: ZZ_FP_UI5_REPOSITORY 23 | bspContainer: ZZ_GUI5UP_TMP02 24 | bspContainerText: Test UI5 Upload -------------------------------------------------------------------------------- /packages/ui5-app/ui5-webapp-tp.yaml: -------------------------------------------------------------------------------- 1 | specVersion: "2.0" 2 | type: application 3 | metadata: 4 | name: ui5-app-webapp 5 | resources: 6 | configuration: 7 | paths: 8 | webapp: webapp_tp 9 | builder: 10 | customTasks: 11 | - name: ui5-task-nwabap-deployer 12 | afterTask: generateVersionInfo 13 | configuration: 14 | resources: 15 | pattern: "**/*.*" 16 | ui5: 17 | language: EN 18 | transportNo: A4HK900098 19 | package: ZZ_FP_UI5_REPOSITORY 20 | bspContainer: ZZ_GUI5UP_TMP02 21 | bspContainerText: Test UI5 Upload -------------------------------------------------------------------------------- /packages/ui5-app/ui5-webapp.yaml: -------------------------------------------------------------------------------- 1 | specVersion: "2.0" 2 | type: application 3 | metadata: 4 | name: ui5-app-webapp 5 | resources: 6 | configuration: 7 | paths: 8 | webapp: webapp 9 | builder: 10 | customTasks: 11 | - name: ui5-task-nwabap-deployer 12 | afterTask: generateVersionInfo 13 | configuration: 14 | connection: 15 | customQueryParams: 16 | spnego: disabled/sap/bc/adt/cts/transports 17 | test2: 2 18 | resources: 19 | pattern: "**/*.*" 20 | ui5: 21 | language: EN 22 | package: $TMP 23 | bspContainer: ZZ_GUI5UP_TMP01 24 | bspContainerText: Test UI5 Upload -------------------------------------------------------------------------------- /packages/ui5-app/webapp/Component.js: -------------------------------------------------------------------------------- 1 | sap.ui.define([ 2 | "sap/ui/core/UIComponent", 3 | "sap/ui/Device", 4 | "de/fpf/test/model/models" 5 | ], function(UIComponent, Device, models) { 6 | "use strict"; 7 | 8 | return UIComponent.extend("de.fpf.test.Component", { 9 | 10 | metadata: { 11 | "version": "1.0.0", 12 | "rootView": { 13 | viewName: "de.fpf.test.view.MainView", 14 | type: sap.ui.core.mvc.ViewType.XML 15 | }, 16 | "dependencies": { 17 | "libs": ["sap.ui.core", "sap.m", "sap.ui.layout"] 18 | }, 19 | "config": { 20 | "i18nBundle": "de.fpf.test.i18n.i18n", 21 | "icon": "", 22 | "favIcon": "", 23 | "phone": "", 24 | "phone@2": "", 25 | "tablet": "", 26 | "tablet@2": "" 27 | } 28 | }, 29 | 30 | /** 31 | * The component is initialized by UI5 automatically during the startup of the app and calls the init method once. 32 | * In this method, the resource and application models are set. 33 | * @public 34 | * @override 35 | */ 36 | init: function() { 37 | var mConfig = this.getMetadata().getConfig(); 38 | 39 | // set the i18n model 40 | this.setModel(models.createResourceModel(mConfig.i18nBundle), "i18n"); 41 | 42 | // call the base component's init function 43 | UIComponent.prototype.init.apply(this, arguments); 44 | } 45 | }); 46 | 47 | }); -------------------------------------------------------------------------------- /packages/ui5-app/webapp/controller/MainView.controller.js: -------------------------------------------------------------------------------- 1 | sap.ui.define([ 2 | "sap/ui/core/mvc/Controller", 3 | "sap/m/MessageToast" 4 | ], function(Controller, MessageToast) { 5 | "use strict"; 6 | 7 | return Controller.extend("de.fpf.test.controller.MainView", { 8 | onPress: function(){ 9 | MessageToast.show('Button pressed', {duration: 2000}); 10 | } 11 | }); 12 | 13 | }); -------------------------------------------------------------------------------- /packages/ui5-app/webapp/controller/test/test.txt: -------------------------------------------------------------------------------- 1 | Test file for upload test. -------------------------------------------------------------------------------- /packages/ui5-app/webapp/css/style.css: -------------------------------------------------------------------------------- 1 | /* Enter your custom styles here */ -------------------------------------------------------------------------------- /packages/ui5-app/webapp/i18n/i18n.properties: -------------------------------------------------------------------------------- 1 | title=Test grunt-nwapap-ui5uploader 2 | btnTxt=Press Me 3 | txtValue=Test for uploading UI5 sources to NW ABAP via grunt plugin. -------------------------------------------------------------------------------- /packages/ui5-app/webapp/img/ui5logo.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pfefferf/ui5-nwabap-deployer/1b25a623a32afbc99cb3c414be61bdc1ea8db2d7/packages/ui5-app/webapp/img/ui5logo.jpg -------------------------------------------------------------------------------- /packages/ui5-app/webapp/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | Test 9 | 10 | 17 | 18 | 19 | 20 | 30 | 31 | 32 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /packages/ui5-app/webapp/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "_version": "1.12.0", 3 | "sap.app": { 4 | "id": "de.fpf.test.UploadTest01", 5 | "type": "application", 6 | "i18n": "i18n/i18n.properties", 7 | "applicationVersion": { 8 | "version": "1.0.0" 9 | }, 10 | "title": "{{appTitle}}", 11 | "description": "{{appDescription}}", 12 | "ach": "ach" 13 | }, 14 | "sap.ui": { 15 | "technology": "UI5", 16 | "icons": { 17 | "icon": "", 18 | "favIcon": "", 19 | "phone": "", 20 | "phone@2": "", 21 | "tablet": "", 22 | "tablet@2": "" 23 | }, 24 | "deviceTypes": { 25 | "desktop": true, 26 | "tablet": true, 27 | "phone": true 28 | } 29 | }, 30 | "sap.ui5": { 31 | "dependencies": { 32 | "minUI5Version": "1.60.1", 33 | "libs": { 34 | "sap.ui.core": {}, 35 | "sap.ui.layout": {}, 36 | "sap.m": {} 37 | } 38 | }, 39 | "contentDensities": { 40 | "compact": true, 41 | "cozy": true 42 | } 43 | } 44 | } -------------------------------------------------------------------------------- /packages/ui5-app/webapp/model/models.js: -------------------------------------------------------------------------------- 1 | sap.ui.define([ 2 | "sap/ui/model/resource/ResourceModel" 3 | ], function(ResourceModel) { 4 | "use strict"; 5 | 6 | return { 7 | createResourceModel: function(sBundleName) { 8 | var oResourceModel = new ResourceModel({ 9 | "bundleName": sBundleName 10 | }); 11 | return oResourceModel; 12 | } 13 | 14 | }; 15 | 16 | }); -------------------------------------------------------------------------------- /packages/ui5-app/webapp/view/MainView.view.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /packages/ui5-app/webapp_tp/Component.js: -------------------------------------------------------------------------------- 1 | sap.ui.define([ 2 | "sap/ui/core/UIComponent", 3 | "sap/ui/Device", 4 | "de/fpf/test/model/models" 5 | ], function(UIComponent, Device, models) { 6 | "use strict"; 7 | 8 | return UIComponent.extend("de.fpf.test.Component", { 9 | 10 | metadata: { 11 | "version": "1.0.0", 12 | "rootView": { 13 | viewName: "de.fpf.test.view.MainView", 14 | type: sap.ui.core.mvc.ViewType.XML 15 | }, 16 | "dependencies": { 17 | "libs": ["sap.ui.core", "sap.m", "sap.ui.layout"] 18 | }, 19 | "config": { 20 | "i18nBundle": "de.fpf.test.i18n.i18n", 21 | "icon": "", 22 | "favIcon": "", 23 | "phone": "", 24 | "phone@2": "", 25 | "tablet": "", 26 | "tablet@2": "" 27 | } 28 | }, 29 | 30 | /** 31 | * The component is initialized by UI5 automatically during the startup of the app and calls the init method once. 32 | * In this method, the resource and application models are set. 33 | * @public 34 | * @override 35 | */ 36 | init: function() { 37 | var mConfig = this.getMetadata().getConfig(); 38 | 39 | // set the i18n model 40 | this.setModel(models.createResourceModel(mConfig.i18nBundle), "i18n"); 41 | 42 | // call the base component's init function 43 | UIComponent.prototype.init.apply(this, arguments); 44 | } 45 | }); 46 | 47 | }); -------------------------------------------------------------------------------- /packages/ui5-app/webapp_tp/controller/MainView.controller.js: -------------------------------------------------------------------------------- 1 | sap.ui.define([ 2 | "sap/ui/core/mvc/Controller", 3 | "sap/m/MessageToast" 4 | ], function(Controller, MessageToast) { 5 | "use strict"; 6 | 7 | return Controller.extend("de.fpf.test.controller.MainView", { 8 | onPress: function(){ 9 | MessageToast.show('Button pressed', {duration: 2000}); 10 | } 11 | }); 12 | 13 | }); -------------------------------------------------------------------------------- /packages/ui5-app/webapp_tp/controller/test/test.txt: -------------------------------------------------------------------------------- 1 | Test file for upload test. -------------------------------------------------------------------------------- /packages/ui5-app/webapp_tp/css/style.css: -------------------------------------------------------------------------------- 1 | /* Enter your custom styles here */ -------------------------------------------------------------------------------- /packages/ui5-app/webapp_tp/i18n/i18n.properties: -------------------------------------------------------------------------------- 1 | title=Test grunt-nwapap-ui5uploader 2 | btnTxt=Press Me 3 | txtValue=Test for uploading UI5 sources to NW ABAP via grunt plugin. -------------------------------------------------------------------------------- /packages/ui5-app/webapp_tp/img/ui5logo.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pfefferf/ui5-nwabap-deployer/1b25a623a32afbc99cb3c414be61bdc1ea8db2d7/packages/ui5-app/webapp_tp/img/ui5logo.jpg -------------------------------------------------------------------------------- /packages/ui5-app/webapp_tp/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | Test 9 | 10 | 17 | 18 | 19 | 20 | 30 | 31 | 32 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /packages/ui5-app/webapp_tp/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "_version": "1.12.0", 3 | "sap.app": { 4 | "id": "de.fpf.test.UploadTest02_TP", 5 | "type": "application", 6 | "i18n": "i18n/i18n.properties", 7 | "applicationVersion": { 8 | "version": "1.0.0" 9 | }, 10 | "title": "{{appTitle}}", 11 | "description": "{{appDescription}}", 12 | "ach": "ach" 13 | }, 14 | "sap.ui": { 15 | "technology": "UI5", 16 | "icons": { 17 | "icon": "", 18 | "favIcon": "", 19 | "phone": "", 20 | "phone@2": "", 21 | "tablet": "", 22 | "tablet@2": "" 23 | }, 24 | "deviceTypes": { 25 | "desktop": true, 26 | "tablet": true, 27 | "phone": true 28 | } 29 | }, 30 | "sap.ui5": { 31 | "dependencies": { 32 | "minUI5Version": "1.60.1", 33 | "libs": { 34 | "sap.ui.core": {}, 35 | "sap.ui.layout": {}, 36 | "sap.m": {} 37 | } 38 | }, 39 | "contentDensities": { 40 | "compact": true, 41 | "cozy": true 42 | } 43 | } 44 | } -------------------------------------------------------------------------------- /packages/ui5-app/webapp_tp/model/models.js: -------------------------------------------------------------------------------- 1 | sap.ui.define([ 2 | "sap/ui/model/resource/ResourceModel" 3 | ], function(ResourceModel) { 4 | "use strict"; 5 | 6 | return { 7 | createResourceModel: function(sBundleName) { 8 | var oResourceModel = new ResourceModel({ 9 | "bundleName": sBundleName 10 | }); 11 | return oResourceModel; 12 | } 13 | 14 | }; 15 | 16 | }); -------------------------------------------------------------------------------- /packages/ui5-app/webapp_tp/view/MainView.view.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /packages/ui5-nwabap-deployer-cli/.npmrc: -------------------------------------------------------------------------------- 1 | package-lock=false -------------------------------------------------------------------------------- /packages/ui5-nwabap-deployer-cli/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## 2.2.0 (2021-07-21) 2 | 3 | ### Features 4 | - New option `testMode` for deployment execution in test mode. 5 | 6 | ## 2.1.0 (2021-06-21) 7 | 8 | ### Features 9 | - `undeploy` command supports undeploying a UI5 application. 10 | 11 | ## 2.0.0 (2021-06-18) 12 | 13 | ### Features 14 | - Usage of /UI5/ABAP_REPOSITORY_SRV service for deployment instead of Team Provider API. 15 |
Team Provider API was set to deprecated. As a consequence starting from version 2.0.0 this deployer supports only systems with at least SAP_UI 753 component installed. 16 |
For all previous versions, version 1.x.x of this deployer needs to be used. 17 | 18 | ## 1.0.4 (2021-05-02) 19 | 20 | ### Fixes 21 | - Exit process; use exitCode instead of hard process.exit call. 22 | 23 | ## 1.0.3 (2021-05-02) 24 | 25 | ### Fixes 26 | - Exit process with status 1 in case of errors. 27 | 28 | ## 1.0.2 (2021-04-30) 29 | 30 | ### Features 31 | - Support for bearer token authorization. 32 | 33 | ## 1.0.1 (2021-04-10) 34 | 35 | ### Fixes 36 | - Correction of typos in demandCommand message. 37 | 38 | ## 1.0.0 (2021-04-10) 39 | 40 | ### General 41 | - Initial release. -------------------------------------------------------------------------------- /packages/ui5-nwabap-deployer-cli/README.md: -------------------------------------------------------------------------------- 1 | [![npm version](https://badge.fury.io/js/ui5-nwabap-deployer-cli.svg)](https://badge.fury.io/js/ui5-nwabap-deployer-cli) 2 | 3 | # ui5-nwabap-deployer-cli 4 | 5 | `ui5-nwabap-deployer-cli` is a CLI tooling which allows to deploy UI5 sources to a SAP NetWeaver ABAP application server. 6 | 7 | Starting from version 2.0.0 this deployer uses the OData Service [/UI5/ABAP_RESPOSITORY_SRV](https://ui5.sap.com/#/topic/a883327a82ef4cc792f3c1e7b7a48de8) for deploying UI5 sources. Please make sure that the service is activated on your system (for details you can check SAP note [2999557](https://launchpad.support.sap.com/#/notes/2999557)). The new service does some sanity checks like e.g. virus scans. If you have not configured virus scan profiles or want to disable virus scanning please have a look to SAP note [2437892](https://launchpad.support.sap.com/#/notes/2437892). 8 |
Current deployer versions starting from version 2.0.0 can be used with SAP systems on which component SAP_UI 753 is installed. On systems with a lower version of component SAP_UI, you have to use version 1.x.x of this deployer. 9 | 10 | ## Install 11 | 12 | ### Global Installation 13 | ```bash 14 | npm install -g ui5-nwabap-deployer-cli 15 | ``` 16 | 17 | Global installation makes the command `ui5-deployer` globally available. 18 | 19 | ### Local Installation 20 | ```bash 21 | npm install ui5-nwabap-deployer-cli --save-dev 22 | ``` 23 | Local installation requires the execution of the tool like `./node_modules/.bin/ui5-deployer`. 24 | 25 | ## CLI Options for `ui5-deployer` 26 | 27 | Following base CLI options are available: 28 | - `help`: shows the CLI help, execute `ui5-deployer --help` 29 | - `version`: shows the CLI version, execute `ui5-deployer --version` 30 | 31 | ## CLI Commands for `ui5-deployer` 32 | 33 | ### deploy 34 | 35 | The `deploy` command deploys UI5 sources to an ABAP system. It provides following arguments. 36 | 37 | |Option|Description|Mandatory|Default Value| 38 | |:-|:-|:-|:-| 39 | |config |Configuration file containing options for. By default for a file './.ui5deployrc' is searched. If no file is found, it is ignored. Options defined in the configuration file are always overwritten in case they are applied on the command line. Consider to never store the user and password in the config file if the file is shared, provide them as command line arguments.|-|-| 40 | |cwd |Directory in which files for deployment are available.|X|./dist| 41 | |files |Glob pattern to match files for deployment.|X|**/\*.\*| 42 | |server |SAP NetWeaver ABAP application server information in form protocol://host:port|X|-| 43 | |client |Client of SAP NetWeaver ABAP application server; if not set default client of server is used.|-|-| 44 | |testMode |Deployment to be done in test mode.|-|false| 45 | |user |User used for logon to SAP NetWeaver ABAP application server.|X (in case no bearer token is used)|-| 46 | |pwd |Password used for logon to SAP NetWeaver ABAP application server.|X (in case no bearer token is used)|-| 47 | |bearerToken |Bearer token used for authorization.|X (in user/pwd is not used)|-| 48 | |useStrictSSL |SSL mode handling. In case of self signed certificates the useStrictSSL mode option can be set to false to allow a deployment of files.|-|true| 49 | |proxy |Proxy to be used for communication to SAP NetWeaver ABAP application server, form protocol://host:port|-|-| 50 | |customQueryParams |Additional query parameters to be appended to the server calls. To be provided in form `parameterName=parameterValue`|-|-| 51 | |language |Language for deployment.|-|EN| 52 | |package |Defines the development package in which the BSP container for the UI5 sources is available or should be created.|X|-| 53 | |bspContainer |Defines the name of the BSP container used for the storage of the UI5 sources. Length is restricted to 15 characters (exclusive customer specific namespaces, e.g. /YYY/).|X|-| 54 | |bspContainerText |Defines the description of the BSP container.|X|-| 55 | |transportNo |Defines the transport number which logs the changes|X (in case sources are not deployed as local objects)|-| 56 | |createTransport |Set this option to true in case a new transport should be created each time the application is deployed.|-|false| 57 | |transportText |Text for transport to be created.|X (in case a transport has to be created)|-| 58 | |transportUseLocked |If a deployment failed due to the BSP application is locked in another transport, the old (original one) transport will be used to deploy the files.|-|false| 59 | |transportUseUserMatch |It will be tried to find a transport request of the given user. If no transport is found and createTransport is enabled a new one will be created and used for further file deployments.|-|false| 60 | 61 | Providing the options for the `deploy` command can be done by a configuration file. By default the command searches for a file `./ui5deployrc`. Using the option `--config` an alternative file name can be provided. In the configuration file all options can be provided which are available as command line arguments. The configuration must be provided as JSON object. 62 | 63 | Configuration file example with dummy data. Consider: Do not configure the user/password and bearer token in the file if shared; provide them as command line arguments. 64 | ```json 65 | { 66 | "cwd": "./dist", 67 | "files": "**/*.*", 68 | "server": "http://localhost:8000", 69 | "client": "100", 70 | "testMode": false, 71 | "user": "testuser", 72 | "pwd": "abcd1234", 73 | "bearerToken": "eadfadfdsf...", 74 | "useStrictSSL": false, 75 | "proxy": "http://proxy:3000", 76 | "customQueryParams": { 77 | "parameter1": "Test", 78 | "parameter2": 1234 79 | }, 80 | "language": "EN", 81 | "package": "ZZ_UI5_REPOSITORY", 82 | "bspContainer": "ZZ_UI5_TEST", 83 | "bspContainerText": "Test UI5 Upload", 84 | "transportNo": "A4HK900000", 85 | "createTransport": false, 86 | "transportText": "Test Transport", 87 | "transportUseLocked": false, 88 | "transportUseUserMatch": false 89 | } 90 | ``` 91 | 92 | In a configuration file not all options must be maintained. It is possible to maintain standard options in the configuration file and provide other ones as command line arguments (like the user and password or the transport number). If an option is defined in the configuration file and provided as command line argument, always the value from the command line argument is taken. 93 | 94 | ### undeploy 95 | 96 | The `undeploy` command undeploys UI5 sources from an ABAP system. It provides following arguments. 97 | 98 | |Option|Description|Mandatory|Default Value| 99 | |:-|:-|:-|:-| 100 | |config |Configuration file containing options for. By default for a file './.ui5deployrc' is searched. If no file is found, it is ignored. Options defined in the configuration file are always overwritten in case they are applied on the command line. Consider to never store the user and password in the config file if the file is shared, provide them as command line arguments.|-|-| 101 | |server |SAP NetWeaver ABAP application server information in form protocol://host:port|X|-| 102 | |client |Client of SAP NetWeaver ABAP application server; if not set default client of server is used.|-|-| 103 | |user |User used for logon to SAP NetWeaver ABAP application server.|X (in case no bearer token is used)|-| 104 | |pwd |Password used for logon to SAP NetWeaver ABAP application server.|X (in case no bearer token is used)|-| 105 | |bearerToken |Bearer token used for authorization.|X (in user/pwd is not used)|-| 106 | |useStrictSSL |SSL mode handling. In case of self signed certificates the useStrictSSL mode option can be set to false to allow a deployment of files.|-|true| 107 | |proxy |Proxy to be used for communication to SAP NetWeaver ABAP application server, form protocol://host:port|-|-| 108 | |customQueryParams |Additional query parameters to be appended to the server calls. To be provided in form `parameterName=parameterValue`|-|-| 109 | |language |Language for deployment.|-|EN| 110 | |package |Defines the development package in which the BSP container for the UI5 sources is available.|X|-| 111 | |bspContainer |Defines the name of the BSP container used for the storage of the UI5 sources. Length is restricted to 15 characters (exclusive customer specific namespaces, e.g. /YYY/).|X|-| 112 | |transportNo |Defines the transport number which logs the changes|X (in case sources are not deployed as local objects)|-| 113 | |createTransport |Set this option to true in case a new transport should be created each time the application is undeployed.|-|false| 114 | |transportText |Text for transport to be created.|X (in case a transport has to be created)|-| 115 | |transportUseLocked |If an undeployment failed due to the BSP application is locked in another transport, the old (original one) transport will be used to undeploy the files.|-|false| 116 | 117 | Providing the options for the `undeploy` command can be done by a configuration file. By default the command searches for a file `./ui5deployrc`. Using the option `--config` an alternative file name can be provided. In the configuration file all options can be provided which are available as command line arguments. The configuration must be provided as JSON object. The same configuration file as for the `deploy` command can be used. Not relevant settings are ignored. 118 | 119 | ## Examples for `deploy` command 120 | 121 | ### Deploy an UI5 app with creation/reusage of transport - command line arguments only 122 | 123 | ```bash 124 | ui5-deployer deploy --server http://localhost:8000 --client "001" --user DEVELOPER --pwd myDeveloperPwd --package ZZ_UI5_REPOSITORY --bspContainer ZZ_UI5_TEST --bspContainerText "Crazy UI5 App" --createTransport true --transportText "UI5 App Development" --transportUseLocked true 125 | ``` 126 | 127 | ### Deploy an UI5 app - specific configuration file + user/password as command line arguments 128 | ```bash 129 | ui5-deployer deploy --config ./.myspecificui5deployconfig --user DEVELOPER --pwd myDeveloperPwd 130 | ``` 131 | 132 | ## Examples for `undeploy` command 133 | 134 | ### Undeploy an UI5 app with creation/reusage of transport -- command line arguments only 135 | 136 | ```bash 137 | ui5-deployer undeploy --server http://localhost:8000 --client "001" --user DEVELOPER --pwd myDeveloperPwd --package ZZ_UI5_REPOSITORY --bspContainer ZZ_UI5_TEST --createTransport true --transportText "UI5 App Development" --transportUseLocked true 138 | ``` 139 | 140 | ### Undeploy an UI5 app - specific configuration file + user/password as command line arguments 141 | ```bash 142 | ui5-deployer undeploy --config ./.myspecificui5deployconfig --user DEVELOPER --pwd myDeveloperPwd 143 | ``` 144 | 145 | ## Release History 146 | 147 | [CHANGELOG.md](https://github.com/pfefferf/ui5-nwabap-deployer/blob/master/packages/ui5-nwabap-deployer-cli/CHANGELOG.md) 148 | 149 | ## License 150 | 151 | [Apache License 2.0](http://www.apache.org/licenses/LICENSE-2.0) 152 | -------------------------------------------------------------------------------- /packages/ui5-nwabap-deployer-cli/bin/cli.js: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env node 2 | const scriptName = "ui5-deployer"; 3 | const yargs = require("yargs"); 4 | const deployCommand = require("../lib/commands/deploy"); 5 | const undeployCommand = require("../lib/commands/undeploy"); 6 | 7 | yargs.usage("\nUI5 Deployer to deploy UI5 sources to a SAP ABAP system.") 8 | .scriptName(scriptName) 9 | .command(deployCommand) 10 | .command(undeployCommand) 11 | .demandCommand(1, "Command required. Please have a look at the help documentation above.") 12 | .strictCommands(true) 13 | .strictOptions(true) 14 | .help(true) 15 | .locale("en") 16 | .argv; 17 | -------------------------------------------------------------------------------- /packages/ui5-nwabap-deployer-cli/lib/commands/deploy.js: -------------------------------------------------------------------------------- 1 | const Logger = require("../log/Logger"); 2 | const glob = require("glob"); 3 | const fs = require("fs"); 4 | const path = require("path"); 5 | const configHandler = require("../config/configHandler"); 6 | const ui5DeployerCore = require("ui5-nwabap-deployer-core"); 7 | 8 | const builder = (yargs) => { 9 | return yargs 10 | .option("config", { description: "Path to configuration file, default: './ui5deployrc'", string: true }) 11 | .option("cwd", { description: "Directory in which files are located, default: './dist'", string: true }) 12 | .option("files", { description: "Files (Glob Pattern), default: '**/*.*'", string: true }) 13 | .option("server", { description: "ABAP Server (form: protocol://host:port)", string: true }) 14 | .option("client", { description: "ABAP Client", string: true }) 15 | .option("useStrictSSL", { description: "Use Strict SSL, default: true", boolean: true }) 16 | .option("proxy", { description: "Proxy (form: protocol://host:port)", string: true }) 17 | .option("testMode", { description: "Test Mode deployment, default: false", boolean: true }) 18 | .option("customQueryParams", { description: "Custom Query Parameters", array: true }) 19 | .option("user", { description: "ABAP User", string: true }) 20 | .option("pwd", { description: "ABAP User Password", string: true }) 21 | .option("bearerToken", { description: "Bearer token for authorization", string: true }) 22 | .option("language", { description: "Language for deployment, default: EN", string: true }) 23 | .option("package", { description: "ABAP Package", string: true }) 24 | .option("bspContainer", { description: "BSP container", string: true }) 25 | .option("bspContainerText", { description: "BSP container text", string: true }) 26 | .option("transportNo", { description: "Transport Number", string: true }) 27 | .option("createTransport", { description: "Create a new transport, default: false", boolean: true }) 28 | .option("transportText", { description: "Text for new created transport", string: true }) 29 | .option("transportUseUserMatch", { description: "Try to find an existing transport for the user, default: false", boolean: true }) 30 | .option("transportUseLocked", { description: "Use an existing transport in which the BSP container is locked, default: false", boolean: true }); 31 | }; 32 | 33 | const handler = async (argv) => { 34 | const logger = new Logger(); 35 | 36 | logger.log("UI5 Deployer: Start deploying UI5 sources."); 37 | 38 | let configData = undefined; 39 | try { 40 | configData = configHandler.readConfigData(argv.config || "./.ui5deployrc"); 41 | } catch (error) { 42 | logger.error(`Error reading configuration file. Error: ${error.message}`); 43 | return; 44 | } 45 | 46 | if (argv.config && !configData) { 47 | logger.error(`No configuration found in config file '${argv.config}'. Please check the file and the content.`); 48 | return; 49 | } 50 | 51 | let argCwd = "./dist"; 52 | if (configData && configData.cwd) { 53 | argCwd = configData.cwd; 54 | } 55 | if (argv.cwd) { 56 | argCwd = argv.cwd; 57 | } 58 | 59 | let argFiles = "**/*.*"; 60 | if (configData && configData.files) { 61 | argFiles = configData.files; 62 | } 63 | if (argv.files) { 64 | argFiles = argv.files; 65 | } 66 | 67 | let deployOptions = configHandler.mapConfigToOptions(configData, initDeployOptions()); 68 | deployOptions = configHandler.mapArgumentsToOptions(argv, deployOptions); 69 | 70 | const files = glob.sync(argFiles, { 71 | cwd: argCwd, 72 | dot: true, 73 | nodir: true 74 | }); 75 | 76 | const fileContents = []; 77 | 78 | files.forEach((file) => { 79 | fileContents.push({ 80 | path: file, 81 | content: fs.readFileSync(path.join(argCwd, file)) 82 | }); 83 | }); 84 | 85 | try { 86 | await ui5DeployerCore.deployUI5toNWABAP(deployOptions, fileContents, logger); 87 | logger.log("UI5 Deployer: UI5 sources successfully deployed."); 88 | } catch (error) { 89 | if (error) { 90 | logger.error(error.message); 91 | process.exitCode = 1; 92 | } 93 | } 94 | }; 95 | 96 | const initDeployOptions = () => { 97 | return { 98 | conn: { 99 | server: undefined, 100 | client: undefined, 101 | useStrictSSL: true, 102 | proxy: undefined, 103 | testMode: false, 104 | customQueryParams: {} 105 | }, 106 | auth: { 107 | user: undefined, 108 | pwd: undefined, 109 | bearerToken: undefined 110 | }, 111 | ui5: { 112 | language: "EN", 113 | transportno: undefined, 114 | package: undefined, 115 | bspcontainer: undefined, 116 | bspcontainer_text: undefined, 117 | create_transport: false, 118 | transport_text: undefined, 119 | transport_use_user_match: false, 120 | transport_use_locked: false 121 | } 122 | }; 123 | }; 124 | 125 | module.exports = { 126 | command: "deploy", 127 | desc: "Deploy UI5 sources to a SAP ABAP system.", 128 | builder: builder, 129 | handler: handler 130 | }; 131 | -------------------------------------------------------------------------------- /packages/ui5-nwabap-deployer-cli/lib/commands/undeploy.js: -------------------------------------------------------------------------------- 1 | const Logger = require("../log/Logger"); 2 | const configHandler = require("../config/configHandler"); 3 | const ui5DeployerCore = require("ui5-nwabap-deployer-core"); 4 | 5 | const builder = (yargs) => { 6 | return yargs 7 | .option("config", { description: "Path to configuration file, default: './ui5deployrc'", string: true }) 8 | .option("server", { description: "ABAP Server (form: protocol://host:port)", string: true }) 9 | .option("client", { description: "ABAP Client", string: true }) 10 | .option("useStrictSSL", { description: "Use Strict SSL, default: true", boolean: true }) 11 | .option("proxy", { description: "Proxy (form: protocol://host:port)", string: true }) 12 | .option("customQueryParams", { description: "Custom Query Parameters", array: true }) 13 | .option("user", { description: "ABAP User", string: true }) 14 | .option("pwd", { description: "ABAP User Password", string: true }) 15 | .option("bearerToken", { description: "Bearer token for authorization", string: true }) 16 | .option("language", { description: "Language for deployment, default: EN", string: true }) 17 | .option("package", { description: "ABAP Package", string: true }) 18 | .option("bspContainer", { description: "BSP container", string: true }) 19 | .option("transportNo", { description: "Transport Number", string: true }) 20 | .option("createTransport", { description: "Create a new transport, default: false", boolean: true }) 21 | .option("transportText", { description: "Text for new created transport", string: true }) 22 | .option("transportUseLocked", { description: "Use an existing transport in which the BSP container is locked, default: false", boolean: true }); 23 | }; 24 | 25 | const handler = async (argv) => { 26 | const logger = new Logger(); 27 | 28 | logger.log("UI5 Deployer: Start undeploying UI5 sources."); 29 | 30 | let configData = undefined; 31 | try { 32 | configData = configHandler.readConfigData(argv.config || "./.ui5deployrc"); 33 | } catch (error) { 34 | logger.error(`Error reading configuration file. Error: ${error.message}`); 35 | return; 36 | } 37 | 38 | if (argv.config && !configData) { 39 | logger.error(`No configuration found in config file '${argv.config}'. Please check the file and the content.`); 40 | return; 41 | } 42 | 43 | let undeployOptions = configHandler.mapConfigToOptions(configData, initUndeployOptions()); 44 | undeployOptions = configHandler.mapArgumentsToOptions(argv, undeployOptions); 45 | 46 | try { 47 | await ui5DeployerCore.undeployUI5fromNWABAP(undeployOptions, logger); 48 | logger.log("UI5 Deployer: UI5 sources successfully undeployed."); 49 | } catch (error) { 50 | if (error) { 51 | logger.error(error.message); 52 | process.exitCode = 1; 53 | } 54 | } 55 | }; 56 | 57 | const initUndeployOptions = () => { 58 | return { 59 | conn: { 60 | server: undefined, 61 | client: undefined, 62 | useStrictSSL: true, 63 | proxy: undefined, 64 | customQueryParams: {} 65 | }, 66 | auth: { 67 | user: undefined, 68 | pwd: undefined, 69 | bearerToken: undefined 70 | }, 71 | ui5: { 72 | language: "EN", 73 | transportno: undefined, 74 | bspcontainer: undefined, 75 | create_transport: false, 76 | transport_text: undefined, 77 | transport_use_locked: false 78 | } 79 | }; 80 | }; 81 | 82 | module.exports = { 83 | command: "undeploy", 84 | desc: "Undeploy UI5 sources from a SAP ABAP system.", 85 | builder: builder, 86 | handler: handler 87 | }; 88 | -------------------------------------------------------------------------------- /packages/ui5-nwabap-deployer-cli/lib/config/configHandler.js: -------------------------------------------------------------------------------- 1 | const fs = require("fs"); 2 | 3 | const readConfigData = (configFile) => { 4 | if (!fs.existsSync(configFile)) { 5 | return undefined; 6 | } 7 | return JSON.parse(fs.readFileSync(configFile, { encoding: "utf-8" })); 8 | }; 9 | 10 | const mapConfigToOptions = (configData, options) => { 11 | const result = Object.assign(options); 12 | 13 | if (!configData) { 14 | return result; 15 | } 16 | 17 | if (configData.server) { 18 | result.conn.server = configData.server; 19 | } 20 | if (configData.client) { 21 | result.conn.client = configData.client; 22 | } 23 | if (configData.useStrictSSL !== undefined) { 24 | result.conn.useStrictSSL = !!configData.useStrictSSL; 25 | } 26 | if (configData.proxy) { 27 | result.conn.proxy = configData.proxy; 28 | } 29 | if (configData.testMode !== undefined) { 30 | result.conn.testMode = !!configData.testMode; 31 | } 32 | if (configData.customQueryParams) { 33 | Object.keys(configData.customQueryParams).forEach((key) => { 34 | result.conn.customQueryParams[key] = configData.customQueryParams[key]; 35 | }); 36 | } 37 | 38 | if (configData.user) { 39 | result.auth.user = configData.user; 40 | } 41 | if (configData.pwd) { 42 | result.auth.pwd = configData.pwd; 43 | } 44 | if (configData.bearerToken) { 45 | result.auth.bearer_token = configData.bearerToken; 46 | } 47 | 48 | if (configData.language) { 49 | result.ui5.language = configData.language; 50 | } 51 | if (configData.transportNo) { 52 | result.ui5.transportno = configData.transportNo; 53 | } 54 | if (configData.package) { 55 | result.ui5.package = configData.package; 56 | } 57 | if (configData.bspContainer) { 58 | result.ui5.bspcontainer = configData.bspContainer; 59 | } 60 | if (configData.bspContainerText) { 61 | result.ui5.bspcontainer_text = configData.bspContainerText; 62 | } 63 | if (configData.createTransport !== undefined) { 64 | result.ui5.create_transport = !!configData.createTransport; 65 | } 66 | if (configData.transportText) { 67 | result.ui5.transport_text = configData.transportText; 68 | } 69 | if (configData.transportUseUserMatch !== undefined) { 70 | result.ui5.transport_use_user_match = !!configData.transportUseUserMatch; 71 | } 72 | if (configData.transportUseLocked !== undefined) { 73 | result.ui5.transport_use_locked = !!configData.transportUseLocked; 74 | } 75 | 76 | return result; 77 | }; 78 | 79 | const mapArgumentsToOptions = (argv, options) => { 80 | const result = Object.assign(options); 81 | 82 | if (argv.server) { 83 | result.conn.server = argv.server; 84 | } 85 | if (argv.client) { 86 | result.conn.client = argv.client; 87 | } 88 | if (argv.useStrictSSL !== undefined) { 89 | result.conn.useStrictSSL = !!argv.useStrictSSL; 90 | } 91 | if (argv.proxy) { 92 | result.conn.proxy = argv.proxy; 93 | } 94 | if (argv.testMode !== undefined) { 95 | result.conn.testMode = !!argv.testMode; 96 | } 97 | if (argv.customQueryParams) { 98 | argv.customQueryParams.forEach((queryParam) => { 99 | const keyValue = queryParam.split("="); 100 | if (keyValue.length === 2) { 101 | result.conn.customQueryParams[keyValue[0]] = keyValue[1]; 102 | } 103 | }); 104 | } 105 | 106 | if (argv.user) { 107 | result.auth.user = argv.user; 108 | } 109 | if (argv.pwd) { 110 | result.auth.pwd = argv.pwd; 111 | } 112 | if (argv.bearerToken) { 113 | result.auth.bearer_token = argv.bearerToken; 114 | } 115 | 116 | if (argv.language) { 117 | result.ui5.language = argv.language; 118 | } 119 | if (argv.transportNo) { 120 | result.ui5.transportno = argv.transportNo; 121 | } 122 | if (argv.package) { 123 | result.ui5.package = argv.package; 124 | } 125 | if (argv.bspContainer) { 126 | result.ui5.bspcontainer = argv.bspContainer; 127 | } 128 | if (argv.bspContainerText) { 129 | result.ui5.bspcontainer_text = argv.bspContainerText; 130 | } 131 | if (argv.createTransport !== undefined) { 132 | result.ui5.create_transport = !!argv.createTransport; 133 | } 134 | if (argv.transportText) { 135 | result.ui5.transport_text = argv.transportText; 136 | } 137 | if (argv.transportUseUserMatch !== undefined) { 138 | result.ui5.transport_use_user_match = !!argv.transportUseUserMatch; 139 | } 140 | if (argv.transportUseLocked !== undefined) { 141 | result.ui5.transport_use_locked = !!argv.transportUseLocked; 142 | } 143 | 144 | return result; 145 | }; 146 | 147 | module.exports = { 148 | readConfigData: readConfigData, 149 | mapConfigToOptions: mapConfigToOptions, 150 | mapArgumentsToOptions: mapArgumentsToOptions 151 | }; 152 | -------------------------------------------------------------------------------- /packages/ui5-nwabap-deployer-cli/lib/log/Logger.js: -------------------------------------------------------------------------------- 1 | const winston = require("winston"); 2 | 3 | class Logger { 4 | constructor() { 5 | this.logger = winston.createLogger({ 6 | transports: [ 7 | new winston.transports.Console({ 8 | format: winston.format.combine( 9 | winston.format.timestamp({ 10 | format: "YYYY-MM-DD HH:mm:ss" 11 | }), 12 | winston.format.colorize(), 13 | winston.format.printf((msg) => { 14 | if (typeof(msg.message) === "object") { 15 | return `${msg.timestamp} ${msg.level}: ${JSON.stringify(msg.message, null, 4)}`; 16 | } 17 | return `${msg.timestamp} ${msg.level}: ${msg.message}`; 18 | }) 19 | ) 20 | }) 21 | ] 22 | }); 23 | } 24 | 25 | log(message) { 26 | this.logger.info(message); 27 | } 28 | 29 | error(message) { 30 | this.logger.error(message); 31 | } 32 | 33 | logVerbose(message) { 34 | this.logger.verbose(message); 35 | } 36 | } 37 | 38 | module.exports = Logger; 39 | -------------------------------------------------------------------------------- /packages/ui5-nwabap-deployer-cli/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ui5-nwabap-deployer-cli", 3 | "version": "2.2.0", 4 | "description": "Deploying UI5 sources to a SAP NetWeaver ABAP system using CLI", 5 | "author": { 6 | "name": "Florian Pfeffer", 7 | "email": "florian.pfeffer@outlook.com" 8 | }, 9 | "repository": { 10 | "type": "git", 11 | "url": "git+https://github.com/pfefferf/ui5-nwabap-deployer.git" 12 | }, 13 | "bugs": { 14 | "url": "https://github.com/pfefferf/ui5-nwabap-deployer/issues" 15 | }, 16 | "license": "Apache-2.0", 17 | "licenses": [ 18 | { 19 | "type": "Apache-2.0", 20 | "url": "https://github.com/pfefferf/ui5-nwabap-deployer/blob/master/LICENSE" 21 | } 22 | ], 23 | "engines": { 24 | "node": ">= 12", 25 | "npm": ">= 7" 26 | }, 27 | "bin": { 28 | "ui5-deployer": "./bin/cli.js" 29 | }, 30 | "directories": { 31 | "bin": "./bin", 32 | "lib": "./lib" 33 | }, 34 | "scripts": { }, 35 | "dependencies": { 36 | "glob": "^7.1.6", 37 | "ui5-nwabap-deployer-core": "^2.2.0", 38 | "winston": "^3.3.3", 39 | "yargs": "^16.2.0" 40 | }, 41 | "keywords": [ 42 | "UI5", 43 | "SAPUI5", 44 | "OpenUI5", 45 | "CLI", 46 | "ui5-deployer", 47 | "SAP", 48 | "NetWeaver", 49 | "ABAP" 50 | ] 51 | } 52 | -------------------------------------------------------------------------------- /packages/ui5-nwabap-deployer-core/.npmrc: -------------------------------------------------------------------------------- 1 | package-lock=false -------------------------------------------------------------------------------- /packages/ui5-nwabap-deployer-core/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## 2.2.6 (2025-03-13) 2 | 3 | ## Fixes 4 | - Update axios to fix vulnerabilities. 5 | 6 | ## 2.2.5 (2024-09-26) 7 | 8 | ## Fixes 9 | - Downgrade retry-axios package. 10 | 11 | ## 2.2.4 (2024-09-26) 12 | 13 | ## Fixes 14 | - Update axios to fix vulnerabilities. 15 | 16 | ## 2.2.3 (2021-08-09) 17 | 18 | ## Fixes 19 | - Deployment was not cancelled in case an error appeared which was not returned in the errordetails array response. 20 | 21 | ## 2.2.2 (2021-07-23) 22 | 23 | ### Fixes 24 | - Execute transport related checks for test mode too. 25 | 26 | ## 2.2.1 (2021-07-22) 27 | 28 | ### Fixes 29 | - Enhance backend response message handling for test mode to support different SAP_UI component versions. 30 | 31 | ## 2.2.0 (2021-07-21) 32 | 33 | ### Features 34 | - New option `testMode` for deployment execution in test mode. 35 | 36 | ## 2.1.0 (2021-06-21) 37 | 38 | ### Features 39 | - Support for undeploying a UI5 application. 40 | 41 | ## 2.0.0 (2021-06-18) 42 | 43 | ### Features 44 | - Usage of /UI5/ABAP_REPOSITORY_SRV service for deployment instead of Team Provider API. 45 |
Team Provider API was set to deprecated. As a consequence starting from version 2.0.0 this deployer supports only systems with at least SAP_UI 753 component installed. 46 |
For all previous versions, version 1.x.x of this deployer needs to be used. 47 | 48 | ## 1.0.23 (2021-06-10) 49 | 50 | ### Fixes 51 | - Fix of issue in response code handling, which avoided proper error handling (thanks to [Fjaoos](https://github.com/Fjaoos) for analyzing and fixing). 52 | 53 | ## 1.0.22 (2021-05-28) 54 | 55 | ### Fixes 56 | - Improve change detection mode for files with carriage return and line feed characters. 57 | 58 | ## 1.0.21 (2021-05-13) 59 | 60 | ### Features 61 | - Deploy a file only if the content is changed. 62 | 63 | ## 1.0.20 (2021-04-30) 64 | 65 | ### Fixes 66 | - Fix bearer token handling. 67 | 68 | ## 1.0.19 (2021-04-30) 69 | 70 | ### Features 71 | - Support for bearer token authorization. 72 | 73 | ## 1.0.18 (2021-04-16) 74 | 75 | ### Features 76 | - Deployment module returns information like used deployment options. 77 | 78 | ## 1.0.17 (2021-04-13) 79 | 80 | ### Fixes 81 | - Removed mandatory check on client, because if no client is provided in configuration the default client will be used. 82 | 83 | ## 1.0.16 (2021-04-10) 84 | 85 | ### Fixes 86 | - Additional check that server and client information is provided. 87 | 88 | ## 1.0.15 (2021-04-07) 89 | 90 | ### Fixes 91 | - `useStrictSSL` option was not considered correctly. 92 | 93 | ## 1.0.14 (2021-03-24) 94 | 95 | ### Fixes 96 | - Do not create an additional unused transport when a transport creation is required, but the usage of an existing transport is required too (and a transport with a lock on the BSP application exists). 97 | 98 | ## 1.0.13 (2021-03-19) 99 | 100 | ### Fixes 101 | - Correct Content-Type header setting for creation of a transport request on ABAP system >= 7.5 (thanks to [ffleige](https://github.com/ffleige)). 102 | 103 | ## 1.0.12 (2021-03-18) 104 | 105 | ### Fixes 106 | - Removed logging of `retry-axios` error object due to possible circular dependencies. 107 | 108 | ## 1.0.11 (2021-01-25) 109 | 110 | ### Fixes 111 | - Option to use a transport in which the object is already locked did not work in case a BSP container was created and then removed, but the transport lock entry still exists. 112 | 113 | ## 1.0.10 (2020-09-25) 114 | 115 | ### Features 116 | - Support of `connection.customQueryParams` configuration option to be able to transfer custom parameters to the backend (for instance to bypass SAML2 or SPNEGO authentication). 117 | 118 | ## 1.0.9 (2020-09-17) 119 | 120 | ### Fixes 121 | - Set ADT Client request body length to maximum; avoids errors that request body is longer than defined max. value. 122 | 123 | ## 1.0.8 (2020-09-04) 124 | 125 | ### Fixes 126 | - Fixed access to undefined response object in case no connection to backend can be established. 127 | 128 | ## 1.0.7 (2020-05-15) 129 | 130 | ### Fixes 131 | - Client option was not considered. 132 | 133 | ## 1.0.6 (2020-04-27) 134 | 135 | ### Fixes 136 | - Proxy option was not considered correctly. 137 | 138 | ## 1.0.5 (2020-04-15) 139 | 140 | ### Fixes 141 | - Local package detection considers all packages starting with a `$` sign, instead of just the `$TMP` package. 142 | 143 | ## 1.0.4 (2020-03-17) 144 | 145 | ### General 146 | - Replaced deprecated `request` module. 147 | - Updated to `async` version 3. 148 | 149 | ## 1.0.3 (2020-03-11) 150 | 151 | ### Fixes 152 | - In case the `ui5.transport_use_user_match` option was used, but no transport exists, the option `ui5.create_transport` was ignored. 153 | 154 | ## 1.0.2 (2020-01-24) 155 | 156 | ### Fixes 157 | - Upload of files with special characters (e.g. ~) in name was confirmed, but the upload failed, because some special characters are not allowed for file names. 158 | 159 | ## 1.0.1 (2019-12-20) 160 | 161 | ### General 162 | - Enhanced interface to accept file contents. 163 | 164 | ## 1.0.0 (2019-11-16) 165 | 166 | ### General 167 | - Initial release. -------------------------------------------------------------------------------- /packages/ui5-nwabap-deployer-core/README.md: -------------------------------------------------------------------------------- 1 | [![npm version](https://badge.fury.io/js/ui5-nwabap-deployer-core.svg)](https://badge.fury.io/js/ui5-nwabap-deployer-core) 2 | 3 | # ui5-nwabap-deployer-core 4 | 5 | `ui5-nwabap-deployer-core` provides the core functionality to deploy UI5 sources to a SAP NetWeaver ABAP application server. 6 | It is used by the modules [grunt-nwabap-ui5uploader](https://github.com/pfefferf/ui5-nwabap-deployer/blob/master/packages/grunt-nwabap-ui5uploader/README.md) and the UI5 Tooling custom task [ui5-task-nwabap-deployer](https://github.com/pfefferf/ui5-nwabap-deployer/blob/master/packages/ui5-task-nwabap-deployer/README.md). In addition it is used for the UI5 Deployer CLI tooling [ui5-nwabap-deployer-cli](https://github.com/pfefferf/ui5-nwabap-deployer/blob/master/packages/ui5-nwabap-deployer-cli/README.md). 7 | 8 | ## Release History 9 | 10 | [CHANGELOG.md](https://github.com/pfefferf/ui5-nwabap-deployer/blob/master/packages/ui5-nwabap-deployer-core/CHANGELOG.md) 11 | 12 | ## License 13 | 14 | [Apache License 2.0](http://www.apache.org/licenses/LICENSE-2.0) -------------------------------------------------------------------------------- /packages/ui5-nwabap-deployer-core/lib/AdtClient.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | const axios = require("axios"); 4 | const rax = require("retry-axios"); 5 | const https = require("https"); 6 | const util = require("./Util"); 7 | const ADT_BASE_URL = "/sap/bc/adt/"; 8 | 9 | /** 10 | * 11 | * @param {object} oConnection 12 | * @param {string} oConnection.server NW server 13 | * @param {string} oConnection.client NW Client 14 | * @param {boolean} oConnection.useStrictSSL use strict SSL connection 15 | * @param {string} oConnection.proxy proxy 16 | * @param {object} oAuth 17 | * @param {string} oAuth.user user 18 | * @param {string} oAuth.pwd password 19 | * @param {string} sLanguage language 20 | * @param {Logger} oLogger logger 21 | * @constructor 22 | */ 23 | function AdtClient(oConnection, oAuth, sLanguage, oLogger) { 24 | this._oOptions = { 25 | auth: oAuth, 26 | conn: oConnection, 27 | lang: sLanguage 28 | }; 29 | 30 | // remove suffix slashes from server URL 31 | if (this._oOptions.conn && this._oOptions.conn.server) { 32 | this._oOptions.conn.server = this._oOptions.conn.server.replace(/\/*$/, ""); 33 | } 34 | 35 | this._oLogger = oLogger; 36 | } 37 | 38 | /** 39 | * Construct the base Url for server access 40 | * @private 41 | * @return {string} base URL 42 | */ 43 | AdtClient.prototype._constructBaseUrl = function() { 44 | return this._oOptions.conn.server + ADT_BASE_URL; 45 | }; 46 | 47 | /** 48 | * Determine a CSRF Token which is necessary for POST/PUT/DELETE operations; also the sapCookie is determined 49 | * @private 50 | * @param {function} fnCallback callback function 51 | * @return {void} 52 | */ 53 | AdtClient.prototype.determineCSRFToken = function(fnCallback) { 54 | if (this._sCSRFToken !== undefined) { 55 | fnCallback(); 56 | return; 57 | } 58 | 59 | const sUrl = this.buildUrl(ADT_BASE_URL + "discovery"); 60 | 61 | const oRequestOptions = { 62 | url: sUrl, 63 | headers: { 64 | "X-CSRF-Token": "Fetch", 65 | "accept": "*/*" 66 | } 67 | }; 68 | 69 | this.sendRequest(oRequestOptions, function(oError, oResponse) { 70 | if (oError) { 71 | fnCallback(oError); 72 | return; 73 | } else if (oResponse.statusCode !== util.HTTPSTAT.ok) { 74 | fnCallback(new Error(`Operation CSRF Token Determination: Expected status code ${util.HTTPSTAT.ok}, actual status code ${oResponse.statusCode}, response body '${oResponse.body}'`)); 75 | return; 76 | } else { 77 | this._sCSRFToken = oResponse.headers["x-csrf-token"]; 78 | this._sSAPCookie = ""; 79 | for (let i = 0; i < oResponse.headers["set-cookie"].length; i++) { 80 | this._sSAPCookie += oResponse.headers["set-cookie"][i] + ";"; 81 | } 82 | 83 | fnCallback(null); 84 | return; 85 | } 86 | }.bind(this)); 87 | }; 88 | 89 | /** 90 | * Determine a CSRF Token which is necessary for POST/PUT/DELETE operations; also the sapCookie is determined; promisified version 91 | * @private 92 | * @returns {Promise} 93 | */ 94 | AdtClient.prototype.determineCSRFTokenPromise = function() { 95 | const that = this; 96 | return new Promise((resolve, reject) => { 97 | that.determineCSRFToken((error, result) => { 98 | if (error) { 99 | reject(error); 100 | } else { 101 | resolve(result); 102 | } 103 | }); 104 | }); 105 | }; 106 | 107 | AdtClient.prototype.buildUrl = function(sUrl) { 108 | return this._oOptions.conn.server + sUrl; 109 | }; 110 | 111 | /** 112 | * Send a request to the server (adds additional information before sending, e.g. authentication information) 113 | * @param {object} oRequestOptions request options object 114 | * @param {function} fnRequestCallback Callback for request 115 | */ 116 | AdtClient.prototype.sendRequest = async function(oRequestOptions, fnRequestCallback) { 117 | const that = this; 118 | 119 | const fnAddQueryParam = (oOptions, sParamName, sParamValue) => { 120 | if (!sParamValue) { 121 | return; 122 | } 123 | 124 | if (!oOptions.hasOwnProperty("params")) { 125 | oOptions.params = {}; 126 | } 127 | oOptions.params[sParamName] = sParamValue; 128 | }; 129 | 130 | const fnAddHeader = (oOptions, sHeaderKey, sHeaderValue) => { 131 | if (!sHeaderValue) { 132 | return; 133 | } 134 | 135 | if (!oOptions.hasOwnProperty("headers")) { 136 | oOptions.headers = {}; 137 | } 138 | oOptions.headers[sHeaderKey] = sHeaderValue; 139 | }; 140 | 141 | const oAxiosReqOptions = {}; 142 | oAxiosReqOptions.url = oRequestOptions.url || ""; 143 | oAxiosReqOptions.method = oRequestOptions.method || "GET"; 144 | oAxiosReqOptions.headers = oRequestOptions.headers || {}; 145 | oAxiosReqOptions.data = oRequestOptions.body; 146 | 147 | oAxiosReqOptions.httpsAgent = new https.Agent({ 148 | rejectUnauthorized: that._oOptions.conn.useStrictSSL 149 | }); 150 | 151 | if (that._oOptions.auth) { 152 | if (that._oOptions.auth.bearer_token) { 153 | fnAddHeader(oAxiosReqOptions, "authorization", "Bearer " + that._oOptions.auth.bearer_token); 154 | } else { 155 | oAxiosReqOptions.auth = { 156 | username: that._oOptions.auth.user, 157 | password: that._oOptions.auth.pwd 158 | }; 159 | } 160 | } 161 | 162 | if (that._oOptions.conn.proxy) { 163 | try { 164 | const oProxyUrl = new URL(that._oOptions.conn.proxy); 165 | 166 | oAxiosReqOptions.proxy = { 167 | host: oProxyUrl.hostname, 168 | port: oProxyUrl.port 169 | }; 170 | 171 | if (oProxyUrl.username && oProxyUrl.password) { 172 | oAxiosReqOptions.proxy.auth = { 173 | username: oProxyUrl.username, 174 | password: oProxyUrl.password 175 | }; 176 | } 177 | } catch (oError) { 178 | fnRequestCallback(oError, null); 179 | } 180 | } 181 | 182 | fnAddQueryParam(oAxiosReqOptions, "sap-language", that._oOptions.lang); 183 | fnAddQueryParam(oAxiosReqOptions, "sap-client", that._oOptions.conn.client); 184 | 185 | if (that._oOptions.conn.customQueryParams) { 186 | Object.keys(that._oOptions.conn.customQueryParams).forEach((sKey) => { 187 | fnAddQueryParam(oAxiosReqOptions, sKey, encodeURIComponent(that._oOptions.conn.customQueryParams[sKey])); 188 | }); 189 | } 190 | 191 | fnAddHeader(oAxiosReqOptions, "x-csrf-token", this._sCSRFToken); 192 | fnAddHeader(oAxiosReqOptions, "cookie", this._sSAPCookie); 193 | 194 | rax.attach(); 195 | oAxiosReqOptions.raxConfig = { 196 | retry: 5, 197 | retryDelay: 500, 198 | onRetryAttempt: (oRaxError) => { 199 | const oCfg = rax.getConfig(oRaxError); 200 | that._oLogger.log("Connection error has occurred; retry attempt " + oCfg.currentRetryAttempt); 201 | } 202 | }; 203 | 204 | oAxiosReqOptions.validateStatus = (status) => { 205 | return status < 999; // request must always return a response 206 | }; 207 | 208 | oAxiosReqOptions.maxBodyLength = Infinity; 209 | oAxiosReqOptions.maxContentLength = Infinity; 210 | 211 | try { 212 | const oResponse = await axios(oAxiosReqOptions); 213 | oResponse.statusCode = oResponse.status; 214 | oResponse.body = oResponse.data; 215 | fnRequestCallback(null, oResponse); 216 | } catch (oRequestError) { 217 | if (oRequestError.response) { 218 | fnRequestCallback(oRequestError.message + "\n\rError message body: " + oRequestError.response.data, null); 219 | } else { 220 | fnRequestCallback(oRequestError.message, null); 221 | } 222 | } 223 | }; 224 | 225 | /** 226 | * Send a request to the server - promisified 227 | * @param {object} oRequestOptions request options object 228 | */ 229 | AdtClient.prototype.sendRequestPromise = function(oRequestOptions) { 230 | const that = this; 231 | return new Promise((resolve, reject) => { 232 | that.sendRequest(oRequestOptions, (error, result) => { 233 | if (error) { 234 | reject(error); 235 | } else { 236 | resolve(result); 237 | } 238 | }); 239 | }); 240 | }; 241 | 242 | module.exports = AdtClient; 243 | -------------------------------------------------------------------------------- /packages/ui5-nwabap-deployer-core/lib/TransportManager.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | const util = require("util"); 4 | const fsutil = require("./Util"); 5 | const CTS_BASE_URL_TRANSPORTS = "/sap/bc/adt/cts/transports"; 6 | const CTS_BASE_URL_TRANSPORTCHECKS = "/sap/bc/adt/cts/transportchecks"; 7 | const AdtClient = require("./AdtClient"); 8 | const XMLDocument = require("xmldoc").XmlDocument; 9 | 10 | /** 11 | * creates and releases transport requests 12 | * @param {object} oOptions 13 | * @param {object} oOptions.conn connection info 14 | * @param {string} oOptions.conn.server server url 15 | * @param {string} oOptions.conn.client sap client id 16 | * @param {boolean} oOptions.conn.useStrictSSL force encrypted connection 17 | * @param {string} oOptions.conn.proxy set connection proxy 18 | * @param {string} oOptions.auth.user username 19 | * @param {string} oOptions.auth.pwd password 20 | * @param {Logger} oLogger 21 | * @constructor 22 | */ 23 | function TransportManager(oOptions, oLogger) { 24 | this._client = new AdtClient(oOptions.conn, oOptions.auth, undefined, oLogger); 25 | this._oLogger = oLogger; 26 | } 27 | 28 | TransportManager.prototype.createTransport = function(sPackageName, sRequestText, fnCallback) { 29 | const sPayload = this.getCreateTransportPayload(sPackageName, sRequestText); 30 | 31 | const sUrl = this._client.buildUrl(CTS_BASE_URL_TRANSPORTS); 32 | this._client.determineCSRFToken(function() { 33 | const oRequestOptions = { 34 | method: "POST", 35 | url: sUrl, 36 | headers: { 37 | "accept": "*/*", 38 | "content-type": "application/vnd.sap.as+xml; charset=UTF-8; dataname=com.sap.adt.CreateCorrectionRequest" 39 | }, 40 | body: sPayload 41 | }; 42 | 43 | this._client.sendRequest(oRequestOptions, function(oError, oResponse) { 44 | if (oError) { 45 | fnCallback(new Error(fsutil.createResponseError(oError))); 46 | return; 47 | } else if (oResponse.statusCode !== fsutil.HTTPSTAT.ok) { 48 | fnCallback(new Error(`Operation Create Transport: Expected status code ${fsutil.HTTPSTAT.ok}, actual status code ${oResponse.statusCode}`)); 49 | return; 50 | } else { 51 | const sTransportNo = oResponse.body.split("/").pop(); 52 | this._oLogger.log("Creation of transport request required. Number of created transport request: " + sTransportNo); 53 | fnCallback(null, sTransportNo); 54 | return; 55 | } 56 | }.bind(this)); 57 | }.bind(this)); 58 | }; 59 | 60 | TransportManager.prototype.createTransportPromise = function(sPackageName, sRequestText) { 61 | const that = this; 62 | return new Promise((resolve, reject) => { 63 | that.createTransport(sPackageName, sRequestText, (error, result) => { 64 | if (error) { 65 | reject(error); 66 | } else { 67 | resolve(result); 68 | } 69 | }); 70 | }); 71 | }; 72 | 73 | /** 74 | * Determines if a transport with the given text already exists. If true the callback returns the transport no 75 | * otherwise the cb returns null. 76 | * @param {Function} fnCallback 77 | */ 78 | TransportManager.prototype.determineExistingTransport = function(fnCallback) { 79 | const sUrl = this._client.buildUrl(CTS_BASE_URL_TRANSPORTS + "?_action=FIND&trfunction=K"); 80 | 81 | const oRequestOptions = { 82 | url: sUrl, 83 | headers: { 84 | "accept": "*/*" 85 | } 86 | }; 87 | 88 | this._client.sendRequest(oRequestOptions, function(oError, oResponse) { 89 | if (oError) { 90 | fnCallback(new Error(fsutil.createResponseError(oError))); 91 | return; 92 | } else if (oResponse.statusCode !== fsutil.HTTPSTAT.ok) { 93 | fnCallback(new Error(`Operation Existing Transport Determination: Expected status code ${fsutil.HTTPSTAT.ok}, actual status code ${oResponse.statusCode}`)); 94 | return; 95 | } else { 96 | if (!oResponse.body) { 97 | return fnCallback(null, null); 98 | } 99 | const oParsed = new XMLDocument(oResponse.body); 100 | const transportNo = oParsed.valueWithPath("asx:values.DATA.CTS_REQ_HEADER.TRKORR"); 101 | fnCallback(null, transportNo); 102 | return; 103 | } 104 | }); 105 | }; 106 | 107 | TransportManager.prototype.getCreateTransportPayload = function(sPackageName, sRequestText) { 108 | const sTemplate = "" + 109 | "" + 110 | "" + 111 | "" + 112 | "I" + 113 | "%s" + 114 | "%s" + 115 | "" + 116 | "" + 117 | ""; 118 | 119 | return util.format(sTemplate, sPackageName, sRequestText); 120 | }; 121 | 122 | /** 123 | * Determine transport number for BSP Container; available in case BSP Container is already locked. 124 | * @param {string} sPackageName package name 125 | * @param {string} sBspContainer BSP Container 126 | * @returns {Promise} Promise resolving to null or found transport number 127 | */ 128 | TransportManager.prototype.determineExistingTransportForBspContainer = function(sPackageName, sBspContainer) { 129 | return new Promise(function(resolve, reject) { 130 | const oRequestOptions = { 131 | method: "POST", 132 | url: this._client.buildUrl(CTS_BASE_URL_TRANSPORTCHECKS), 133 | headers: { 134 | "accept": "*/*", 135 | "content-type": "application/vnd.sap.as+xml; charset=UTF-8; dataname=com.sap.adt.transport.service.checkData" 136 | }, 137 | body: "" + 138 | "" + 139 | "" + 140 | "" + 141 | "R3TR" + 142 | "WAPA" + 143 | "" + sBspContainer + "" + 144 | "" + sPackageName + "" + 145 | "" + 146 | "I" + 147 | "" + 148 | "" + 149 | "" + 150 | "" 151 | }; 152 | 153 | this._client.determineCSRFToken(function() { 154 | this._client.sendRequest(oRequestOptions, function(oError, oResponse) { 155 | if (oError) { 156 | reject(new Error(fsutil.createResponseError(oError))); 157 | return; 158 | } else if (oResponse.statusCode !== fsutil.HTTPSTAT.ok) { 159 | reject(new Error(`Operation Existing Transport Determination for BSP Container: Expected status code ${fsutil.HTTPSTAT.ok}, actual status code ${oResponse.statusCode}`)); 160 | return; 161 | } else { 162 | if (!oResponse.body) { 163 | return resolve(null); 164 | } 165 | const oParsed = new XMLDocument(oResponse.body); 166 | const transportNo = oParsed.valueWithPath("asx:values.DATA.LOCKS.CTS_OBJECT_LOCK.LOCK_HOLDER.REQ_HEADER.TRKORR"); 167 | return resolve(transportNo); 168 | } 169 | }); 170 | }.bind(this)); 171 | }.bind(this)); 172 | }; 173 | 174 | module.exports = TransportManager; 175 | -------------------------------------------------------------------------------- /packages/ui5-nwabap-deployer-core/lib/UI5ABAPRepoClient.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | const AdtClient = require("./AdtClient"); 4 | const yazl = require("yazl"); 5 | const util = require("./Util"); 6 | 7 | module.exports = class UI5ABAPRepoClient { 8 | /** 9 | * UI5ABAPRepoClient constructor 10 | * @public 11 | * @param {object} oOptions Options for deployment 12 | * @param {object} oLogger 13 | */ 14 | constructor(oOptions, oLogger) { 15 | this.init(oOptions, oLogger); 16 | } 17 | 18 | /** 19 | * Init client 20 | * @param {object} oOptions Options for deployment 21 | * @param {object} oLogger 22 | */ 23 | init(oOptions, oLogger) { 24 | this._oOptions = oOptions; 25 | this._oLogger = oLogger; 26 | this._client = new AdtClient(oOptions.conn, oOptions.auth, oOptions.ui5.language, oLogger); 27 | } 28 | 29 | /** 30 | * Construbt UI5 ABAP Repository Service URL 31 | * @returns {string} UI5 ABAP Repository Service URL 32 | */ 33 | getServiceUrl() { 34 | return `${this._oOptions.conn.server}/sap/opu/odata/UI5/ABAP_REPOSITORY_SRV/Repositories`; 35 | } 36 | 37 | /** 38 | * Check if Repo is existing 39 | * @returns (boolean) repository existing 40 | */ 41 | async isRepoExisting() { 42 | await this._client.determineCSRFTokenPromise(); 43 | 44 | const sUrl = `${this.getServiceUrl()}('${encodeURIComponent(this._oOptions.ui5.bspcontainer)}')`; 45 | 46 | const oRequestOptions = { 47 | method: "GET", 48 | url: sUrl, 49 | headers: { 50 | "Content-Type": "application/json" 51 | }, 52 | body: {} 53 | }; 54 | 55 | const oResponse = await this._client.sendRequestPromise(oRequestOptions); 56 | return oResponse.statusCode === util.HTTPSTAT.ok ? true : false; 57 | } 58 | 59 | /** 60 | * UI5 repo deployment 61 | * @param {Array} aFiles Files to be deployed 62 | */ 63 | async deployRepo(aFiles) { 64 | const isRepoExisting = await this.isRepoExisting(); 65 | await this._client.determineCSRFTokenPromise(); 66 | 67 | function stream2buffer(readableStream) { 68 | return new Promise((resolve, reject) => { 69 | const chunks = []; 70 | readableStream.on("data", (chunk) => { 71 | chunks.push(chunk); 72 | }).on("end", () => { 73 | resolve(Buffer.concat(chunks)); 74 | }).on("error", (err) => { 75 | reject(err); 76 | }); 77 | }); 78 | } 79 | 80 | const zip = new yazl.ZipFile(); 81 | 82 | aFiles.forEach((file) => { 83 | zip.addBuffer(file.content, file.path); 84 | }); 85 | zip.end({ forceZip64Format: false }); 86 | 87 | const zipBuffer = await stream2buffer(zip.outputStream); 88 | 89 | let sUrl = `${this.getServiceUrl()}`; 90 | 91 | if (isRepoExisting) { 92 | sUrl = sUrl + `('${encodeURIComponent(this._oOptions.ui5.bspcontainer)}')`; 93 | } 94 | 95 | sUrl = sUrl + "?CodePage='UTF8'"; 96 | 97 | if (this._oOptions.ui5.transportno) { 98 | sUrl = sUrl + "&TransportRequest=" + this._oOptions.ui5.transportno; 99 | } 100 | 101 | if (this._oOptions.conn.testMode) { 102 | sUrl = sUrl + "&TestMode=TRUE"; 103 | } 104 | 105 | const oRequestOptions = { 106 | method: isRepoExisting ? "PUT" : "POST", 107 | url: sUrl, 108 | headers: { 109 | "Content-Type": "application/json" 110 | }, 111 | body: { 112 | "Name": this._oOptions.ui5.bspcontainer, 113 | "Package": this._oOptions.ui5.package, 114 | "Description": this._oOptions.ui5.bspcontainer_text, 115 | "ZipArchive": zipBuffer.toString("base64"), 116 | "Info": "ui5-nwabap-deployer-core" 117 | } 118 | }; 119 | 120 | const oResponse = await this._client.sendRequestPromise(oRequestOptions); 121 | 122 | if (this.doesResponseContainAnError(oResponse)) { 123 | throw new Error(util.createResponseError(typeof oResponse.body === "object" ? JSON.stringify(oResponse.body, null, 4) : oResponse.body)); 124 | } 125 | } 126 | 127 | /** 128 | * UI5 repo undeployment 129 | */ 130 | async undeployRepo() { 131 | const isRepoExisting = await this.isRepoExisting(); 132 | await this._client.determineCSRFTokenPromise(); 133 | 134 | if (!isRepoExisting) { 135 | throw new Error(util.createResponseError(`BSP Container ${this._oOptions.ui5.bspcontainer} does not exist. Undeploy cancelled.`)); 136 | } 137 | 138 | let sUrl = `${this.getServiceUrl()}`; 139 | sUrl = sUrl + `('${encodeURIComponent(this._oOptions.ui5.bspcontainer)}')`; 140 | 141 | sUrl = sUrl + "?CodePage='UTF8'"; 142 | 143 | if (this._oOptions.ui5.transportno) { 144 | sUrl = sUrl + "&TransportRequest=" + this._oOptions.ui5.transportno; 145 | } 146 | 147 | if (this._oOptions.conn.testMode) { 148 | sUrl = sUrl + "&TestMode=TRUE"; 149 | } 150 | 151 | const oRequestOptions = { 152 | method: "DELETE", 153 | url: sUrl 154 | }; 155 | 156 | const oResponse = await this._client.sendRequestPromise(oRequestOptions); 157 | 158 | if (this.doesResponseContainAnError(oResponse)) { 159 | throw new Error(util.createResponseError(typeof oResponse.body === "object" ? JSON.stringify(oResponse.body, null, 4) : oResponse.body)); 160 | } 161 | } 162 | 163 | /** 164 | * Check if response contains an error. 165 | * @param {Object} oResponse Request response 166 | * @returns {boolean} Info if response contains an error 167 | */ 168 | doesResponseContainAnError(oResponse) { 169 | let bErrorOccurred = false; 170 | 171 | let errorDetails = null; 172 | 173 | if (oResponse.body && oResponse.body.errordetails) { 174 | errorDetails = oResponse.body.errordetails; 175 | } 176 | 177 | if (oResponse.body && oResponse.body.error && oResponse.body.error.innererror && oResponse.body.error.innererror.errordetails && oResponse.body.error.innererror.errordetails.length > 0) { 178 | errorDetails = oResponse.body.error.innererror.errordetails; 179 | } 180 | 181 | if (errorDetails) { 182 | const idx = errorDetails.findIndex((errordetail) => { 183 | return errordetail.severity === "error"; 184 | }); 185 | 186 | bErrorOccurred = idx !== -1; 187 | } else if (oResponse.statusCode !== util.HTTPSTAT.ok && oResponse.statusCode !== util.HTTPSTAT.created && oResponse.statusCode !== util.HTTPSTAT.no_content) { 188 | bErrorOccurred = true; 189 | } 190 | 191 | return bErrorOccurred; 192 | } 193 | }; 194 | -------------------------------------------------------------------------------- /packages/ui5-nwabap-deployer-core/lib/Util.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | /** 4 | * Object type 5 | * @type {{file: string, folder: string}} 6 | */ 7 | const OBJECT_TYPE = { 8 | file: "file", 9 | folder: "folder" 10 | }; 11 | 12 | /** 13 | * HTTP status 14 | * @type {{ok: number, created: number, bad_request: number, not_authorized: number, not_found: number, not_allowed: number}} 15 | */ 16 | const HTTPSTAT = { 17 | ok: 200, 18 | created: 201, 19 | no_content: 204, 20 | bad_request: 400, 21 | not_authorized: 403, 22 | not_found: 404, 23 | not_allowed: 405, 24 | int_error: 500 25 | }; 26 | 27 | /** 28 | * Modification Identifier 29 | * @type {{create: string, update: string, delete: string}} 30 | */ 31 | const MODIDF = { 32 | create: "create", 33 | update: "update", 34 | delete: "delete" 35 | }; 36 | 37 | /** 38 | * Split a value into the path and object information 39 | * @param {string} sValue values like /test/test1.txt 40 | * @return {{path: string, obj: string}} Path object 41 | */ 42 | function splitIntoPathAndObject(sValue) { 43 | const aValues = sValue.split("/"); 44 | const sObject = aValues.pop(); 45 | let sPath = aValues.join("/"); 46 | if (sPath.length > 0 && sPath.charAt(0) !== "/") { 47 | sPath = "/" + sPath; 48 | } 49 | return { 50 | path: sPath, 51 | obj: sObject 52 | }; 53 | } 54 | 55 | /** 56 | * 57 | * @param {object} oError error 58 | * @return {string} response error string 59 | */ 60 | function createResponseError(oError) { 61 | if (oError) { 62 | return String(oError); 63 | } 64 | 65 | return null; 66 | } 67 | 68 | /** 69 | * export 70 | */ 71 | exports.OBJECT_TYPE = OBJECT_TYPE; 72 | exports.HTTPSTAT = HTTPSTAT; 73 | exports.MODIDF = MODIDF; 74 | exports.splitIntoPathAndObject = splitIntoPathAndObject; 75 | exports.createResponseError = createResponseError; 76 | -------------------------------------------------------------------------------- /packages/ui5-nwabap-deployer-core/lib/ui5-nwabap-deployer-core.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | const UI5ABAPRepoClient = require("./UI5ABAPRepoClient"); 4 | const TransportManager = require("./TransportManager"); 5 | 6 | /** 7 | * Set default for language option 8 | * @param {Object} oOptions Options 9 | * @returns {object} Options 10 | */ 11 | function setDefaultLanguage(oOptions) { 12 | if (!oOptions.ui5.language) { 13 | oOptions.ui5.language = "EN"; 14 | } 15 | return oOptions; 16 | } 17 | 18 | /** 19 | * Set default for use strict ssl option 20 | * @param {Object} oOptions Options 21 | * @returns {Object} Options 22 | */ 23 | function setDefaultUseStrictSSL(oOptions) { 24 | if (!oOptions.conn.hasOwnProperty("useStrictSSL")) { 25 | oOptions.conn.useStrictSSL = true; 26 | } 27 | return oOptions; 28 | } 29 | 30 | /** 31 | * Set default test mode to false 32 | * @param {Object} oOptions Options 33 | * @returns {Object} Options 34 | */ 35 | function setDefaultTestMode(oOptions) { 36 | if (!oOptions.conn.hasOwnProperty("testMode")) { 37 | oOptions.conn.testMode = false; 38 | } 39 | return oOptions; 40 | } 41 | 42 | /** 43 | * Checks on Connection Options 44 | * @param {Object} oOptions Options 45 | * @param {Object} oLogger Logger 46 | * @returns {Boolean} checks successful? 47 | */ 48 | function checkConnectionOptions(oOptions, oLogger) { 49 | if (!oOptions.conn || !oOptions.conn.server) { 50 | oLogger.error("Connection configuration not (fully) specified (check server)."); 51 | return false; 52 | } 53 | return true; 54 | } 55 | 56 | /** 57 | * Checks on Authentication Options 58 | * @param {Object} oOptions Options 59 | * @param {Object} oLogger Logger 60 | * @returns {Boolean} checks successful? 61 | */ 62 | function checkAuthenticationOptions(oOptions, oLogger) { 63 | if (!oOptions.auth || (!oOptions.auth.bearer_token && (!oOptions.auth.user || !oOptions.auth.pwd)) ) { 64 | oLogger.error("Authentication configuration not correct (check user/password or bearer token)."); 65 | return false; 66 | } 67 | return true; 68 | } 69 | 70 | /** 71 | * Checks on Create Transport Options 72 | * @param {Object} oOptions Options 73 | * @param {Object} oLogger Logger 74 | * @returns {Boolean} checks successful? 75 | */ 76 | function checkCreateTransport(oOptions, oLogger) { 77 | if (oOptions.ui5.create_transport === true && typeof oOptions.ui5.transport_text !== "string") { 78 | oLogger.error("Please specify a description to be used for the transport to be created."); 79 | return false; 80 | } 81 | return true; 82 | } 83 | 84 | /** 85 | * Checks on Deploy Options 86 | * @param {Object} oOptions Options 87 | * @param {Object} oLogger Logger 88 | * @returns {Boolean} checks successful? 89 | */ 90 | function checkDeployOptions(oOptions, oLogger) { 91 | let bCheckSuccessful = true; 92 | 93 | if (!checkConnectionOptions(oOptions, oLogger)) { 94 | bCheckSuccessful = false; 95 | } 96 | 97 | if (!checkAuthenticationOptions(oOptions, oLogger)) { 98 | bCheckSuccessful = false; 99 | } 100 | 101 | if (!oOptions.ui5 || !oOptions.ui5.package || !oOptions.ui5.bspcontainer || !oOptions.ui5.bspcontainer_text) { 102 | oLogger.error("UI5 configuration not (fully) specified (check package, BSP container, BSP container text information)."); 103 | bCheckSuccessful = false; 104 | } 105 | 106 | if (oOptions.ui5 && oOptions.ui5.package && !oOptions.ui5.package.startsWith("$") && !oOptions.ui5.transportno && 107 | oOptions.ui5.create_transport !== true && oOptions.ui5.transport_use_user_match !== true && oOptions.conn.testMode !== true) { 108 | oLogger.error("For non-local packages (package name does not start with a \"$\") a transport number is necessary."); 109 | bCheckSuccessful = false; 110 | } 111 | 112 | if (!checkCreateTransport(oOptions, oLogger)) { 113 | bCheckSuccessful = false; 114 | } 115 | 116 | if (oOptions.ui5 && oOptions.ui5.bspcontainer) { 117 | const bspcontainerExclNamespace = oOptions.ui5.bspcontainer.substring(oOptions.ui5.bspcontainer.lastIndexOf("/") + 1); 118 | if (bspcontainerExclNamespace.length > 15) { 119 | oLogger.error("BSP Container name must not be longer than 15 characters (exclusive customer specific namespace e.g. /YYY/."); 120 | bCheckSuccessful = false; 121 | } 122 | } 123 | 124 | return bCheckSuccessful; 125 | } 126 | 127 | /** 128 | * Checks on Undeploy Options 129 | * @param {Object} oOptions Options 130 | * @param {Object} oLogger Logger 131 | * @returns {Boolean} checks successful? 132 | */ 133 | function checkUndeployOptions(oOptions, oLogger) { 134 | let bCheckSuccessful = true; 135 | 136 | if (!checkConnectionOptions(oOptions, oLogger)) { 137 | bCheckSuccessful = false; 138 | } 139 | 140 | if (!checkAuthenticationOptions(oOptions, oLogger)) { 141 | bCheckSuccessful = false; 142 | } 143 | 144 | if (!oOptions.ui5 || !oOptions.ui5.package || !oOptions.ui5.bspcontainer ) { 145 | oLogger.error("Please specify a Package and a BSP container."); 146 | bCheckSuccessful = false; 147 | } 148 | 149 | if (oOptions.ui5 && oOptions.ui5.package && !oOptions.ui5.package.startsWith("$") && !oOptions.ui5.transportno && 150 | oOptions.ui5.create_transport !== true && oOptions.conn.testMode !== true) { 151 | oLogger.error("For non-local packages (package name does not start with a \"$\") a transport number is necessary."); 152 | bCheckSuccessful = false; 153 | } 154 | 155 | if (!checkCreateTransport(oOptions, oLogger)) { 156 | bCheckSuccessful = false; 157 | } 158 | 159 | return bCheckSuccessful; 160 | } 161 | 162 | /** 163 | * Synchronize Files 164 | * @param {object} oOptions 165 | * @param {object} oLogger 166 | * @param {array} aFiles 167 | */ 168 | function syncFiles(oOptions, oLogger, aFiles) { 169 | return new Promise(async (resolve, reject) => { 170 | try { 171 | const oRepoClient = new UI5ABAPRepoClient(oOptions, oLogger); 172 | await oRepoClient.deployRepo(aFiles); 173 | resolve(); 174 | return; 175 | } catch (oError) { 176 | reject(oError); 177 | return; 178 | } 179 | }); 180 | } 181 | 182 | /** 183 | * Upload the files with an transport which does the user own. 184 | * @param {Object} oTransportManager Transport manager 185 | * @param {Object} oOptions File Store Options 186 | * @param {Object} oLogger Logger 187 | * @Param {Array} aFiles Files 188 | */ 189 | async function uploadWithTransportUserMatch(oTransportManager, oOptions, oLogger, aFiles) { 190 | return new Promise((resolve, reject) => { 191 | oTransportManager.determineExistingTransport(async function(oError, sTransportNo) { 192 | if (oError) { 193 | reject(oError); 194 | return; 195 | } else if (sTransportNo) { 196 | oOptions.ui5.transportno = sTransportNo; 197 | try { 198 | await syncFiles(oOptions, oLogger, aFiles); 199 | resolve(); 200 | return; 201 | } catch (oError) { 202 | reject(oError); 203 | return; 204 | } 205 | } else if (oOptions.ui5.create_transport === true) { 206 | oTransportManager.createTransport(oOptions.ui5.package, oOptions.ui5.transport_text, async function(oError, sTransportNo) { 207 | if (oError) { 208 | reject(oError); 209 | return; 210 | } 211 | oOptions.ui5.transportno = sTransportNo; 212 | try { 213 | await syncFiles(oOptions, oLogger, aFiles); 214 | resolve(); 215 | return; 216 | } catch (oError) { 217 | reject(oError); 218 | return; 219 | } 220 | }); 221 | } else { 222 | reject(new Error("No transport found and create transport was disabled!")); 223 | return; 224 | } 225 | }); 226 | }); 227 | } 228 | 229 | /** 230 | * Central function to deploy UI5 sources to a SAP NetWeaver ABAP system. 231 | * @param {Object} oOptions Options 232 | * @param {Array} aFiles Files to deploy 233 | * @param {Object} oLogger Logger 234 | */ 235 | exports.deployUI5toNWABAP = async function(oOptions, aFiles, oLogger) { 236 | return new Promise(async function(resolve, reject) { 237 | let oOptionsAdapted = {}; 238 | 239 | oOptionsAdapted = Object.assign(oOptionsAdapted, oOptions); 240 | 241 | oOptionsAdapted = setDefaultLanguage(oOptionsAdapted); 242 | oOptionsAdapted = setDefaultUseStrictSSL(oOptionsAdapted); 243 | oOptionsAdapted = setDefaultTestMode(oOptionsAdapted); 244 | 245 | // checks on options 246 | if (!checkDeployOptions(oOptionsAdapted, oLogger)) { 247 | reject(new Error("Configuration incorrect.")); 248 | return; 249 | } 250 | 251 | // verbose log options 252 | oLogger.logVerbose(`Options: ${JSON.stringify(oOptionsAdapted)}`); 253 | 254 | // info about test mode 255 | if (oOptions.conn.testMode) { 256 | oLogger.log("Running in Test Mode - no changes are done."); 257 | } 258 | 259 | // binary determination 260 | const aFilesAdapted = aFiles.map((oFile) => { 261 | return oFile; 262 | }); 263 | 264 | // verbose log files 265 | oLogger.logVerbose("Files: " + aFilesAdapted); 266 | 267 | // sync files 268 | const oTransportManager = new TransportManager(oOptionsAdapted, oLogger); 269 | let sExistingTransportNo = null; 270 | try { 271 | sExistingTransportNo = await oTransportManager.determineExistingTransportForBspContainer(oOptionsAdapted.ui5.package, oOptionsAdapted.ui5.bspcontainer); 272 | } catch (oError) { 273 | reject(oError); 274 | return; 275 | } 276 | 277 | if (sExistingTransportNo && !oOptionsAdapted.ui5.transport_use_locked && ((!oOptionsAdapted.ui5 || !oOptionsAdapted.ui5.transportno) || (oOptionsAdapted.ui5 && oOptionsAdapted.ui5.transportno !== sExistingTransportNo))) { 278 | reject(new Error(`BSP container already locked in transport ${sExistingTransportNo}. But it was not configured to reuse a transport with an existing lock.`)); 279 | return; 280 | } 281 | 282 | if (sExistingTransportNo && oOptionsAdapted.ui5.transport_use_locked) { 283 | // existing transport lock 284 | oOptionsAdapted.ui5.transportno = sExistingTransportNo; 285 | oLogger.log(`BSP Application ${oOptionsAdapted.ui5.bspcontainer} already locked in transport request ${sExistingTransportNo}. This transport request is used for deployment.`); 286 | try { 287 | await syncFiles(oOptionsAdapted, oLogger, aFilesAdapted); 288 | resolve({ oOptions: oOptionsAdapted }); 289 | return; 290 | } catch (oError) { 291 | reject(oError); 292 | return; 293 | } 294 | } 295 | 296 | if (!oOptionsAdapted.ui5.package.startsWith("$") && oOptionsAdapted.ui5.transportno === undefined) { 297 | if (oOptionsAdapted.ui5.transport_use_user_match) { 298 | try { 299 | await uploadWithTransportUserMatch(oTransportManager, oOptionsAdapted, oLogger, aFilesAdapted); 300 | resolve({ oOptions: oOptionsAdapted }); 301 | return; 302 | } catch (oError) { 303 | reject(oError); 304 | return; 305 | } 306 | } else if (oOptionsAdapted.ui5.create_transport === true) { 307 | try { 308 | let sTransportNo = "A4HK900000"; // dummy transport for test mode 309 | if (!oOptionsAdapted.conn.testMode) { 310 | sTransportNo = await oTransportManager.createTransportPromise(oOptionsAdapted.ui5.package, oOptionsAdapted.ui5.transport_text); 311 | } 312 | oOptionsAdapted.ui5.transportno = sTransportNo; 313 | } catch (oError) { 314 | reject(oError); 315 | return; 316 | } 317 | 318 | try { 319 | await syncFiles(oOptionsAdapted, oLogger, aFilesAdapted); 320 | resolve({ oOptions: oOptionsAdapted }); 321 | return; 322 | } catch (oError) { 323 | reject(oError); 324 | return; 325 | } 326 | } else { 327 | const oError = new Error("No transport configured and 'create transport' and 'use user match' options are disabled."); 328 | reject(oError); 329 | return; 330 | } 331 | } else { 332 | try { 333 | await syncFiles(oOptionsAdapted, oLogger, aFilesAdapted); 334 | resolve({ oOptions: oOptionsAdapted }); 335 | return; 336 | } catch (oError) { 337 | reject(oError); 338 | return; 339 | } 340 | } 341 | }); 342 | }; 343 | 344 | /** 345 | * Central function to undeploy UI5 sources from a SAP NetWeaver ABAP system. 346 | * @param {Object} oOptions Options 347 | * @param {Object} oLogger Logger 348 | */ 349 | exports.undeployUI5fromNWABAP = async function(oOptions, oLogger) { 350 | return new Promise(async function(resolve, reject) { 351 | let oOptionsAdapted = {}; 352 | 353 | oOptionsAdapted = Object.assign(oOptionsAdapted, oOptions); 354 | 355 | oOptionsAdapted = setDefaultLanguage(oOptionsAdapted); 356 | oOptionsAdapted = setDefaultUseStrictSSL(oOptionsAdapted); 357 | oOptionsAdapted = setDefaultTestMode(oOptionsAdapted); 358 | 359 | // info about test mode 360 | if (oOptions.conn.testMode) { 361 | oLogger.log("Running in Test Mode - no changes are done."); 362 | } 363 | 364 | // checks on options 365 | if (!checkUndeployOptions(oOptionsAdapted, oLogger)) { 366 | reject(new Error("Configuration incorrect.")); 367 | return; 368 | } 369 | 370 | // verbose log options 371 | oLogger.logVerbose(`Options: ${JSON.stringify(oOptionsAdapted)}`); 372 | 373 | // determine existing transport 374 | const oTransportManager = new TransportManager(oOptionsAdapted, oLogger); 375 | let sExistingTransportNo = null; 376 | try { 377 | sExistingTransportNo = await oTransportManager.determineExistingTransportForBspContainer(oOptionsAdapted.ui5.package, oOptionsAdapted.ui5.bspcontainer); 378 | } catch (oError) { 379 | reject(oError); 380 | return; 381 | } 382 | 383 | if (sExistingTransportNo && !oOptionsAdapted.ui5.transport_use_locked && ((!oOptionsAdapted.ui5 || !oOptionsAdapted.ui5.transportno) || (oOptionsAdapted.ui5 && oOptionsAdapted.ui5.transportno !== sExistingTransportNo))) { 384 | reject(new Error(`BSP container already locked in transport ${sExistingTransportNo}. But it was not configured to reuse a transport with an existing lock.`)); 385 | return; 386 | } 387 | 388 | // use existing transport if required 389 | if (sExistingTransportNo && oOptionsAdapted.ui5.transport_use_locked) { 390 | oOptionsAdapted.ui5.transportno = sExistingTransportNo; 391 | oLogger.log(`BSP Application ${oOptionsAdapted.ui5.bspcontainer} already locked in transport request ${sExistingTransportNo}. This transport request is used for undeploy.`); 392 | } 393 | 394 | // create new transport if necessary 395 | let oRepoClient = new UI5ABAPRepoClient(oOptionsAdapted, oLogger); 396 | let isRepoExisting = false; 397 | try { 398 | isRepoExisting = await oRepoClient.isRepoExisting(); 399 | } catch (oError) { 400 | reject(oError); 401 | } 402 | 403 | if (oOptionsAdapted.ui5.create_transport && !sExistingTransportNo && isRepoExisting) { 404 | try { 405 | const sTransportNo = await oTransportManager.createTransportPromise(oOptionsAdapted.ui5.package, oOptionsAdapted.ui5.transport_text); 406 | oOptionsAdapted.ui5.transportno = sTransportNo; 407 | } catch (oError) { 408 | reject(oError); 409 | return; 410 | } 411 | } 412 | 413 | // undeploy 414 | try { 415 | oRepoClient = new UI5ABAPRepoClient(oOptionsAdapted, oLogger); 416 | await oRepoClient.undeployRepo(); 417 | resolve({ oOptions: oOptionsAdapted }); 418 | return; 419 | } catch (oError) { 420 | reject(oError); 421 | return; 422 | } 423 | }); 424 | }; 425 | -------------------------------------------------------------------------------- /packages/ui5-nwabap-deployer-core/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ui5-nwabap-deployer-core", 3 | "version": "2.2.6", 4 | "description": "UI5 Deployer to SAP NetWeaver - Core", 5 | "author": { 6 | "name": "Florian Pfeffer", 7 | "email": "florian.pfeffer@outlook.com" 8 | }, 9 | "repository": { 10 | "type": "git", 11 | "url": "git+https://github.com/pfefferf/ui5-nwabap-deployer.git" 12 | }, 13 | "bugs": { 14 | "url": "https://github.com/pfefferf/ui5-nwabap-deployer/issues" 15 | }, 16 | "license": "Apache-2.0", 17 | "licenses": [ 18 | { 19 | "type": "Apache-2.0", 20 | "url": "https://github.com/pfefferf/ui5-nwabap-deployer/blob/master/LICENSE" 21 | } 22 | ], 23 | "engines": { 24 | "node": ">= 12", 25 | "npm": ">= 7" 26 | }, 27 | "main": "lib/ui5-nwabap-deployer-core.js", 28 | "directories": { 29 | "lib": "lib" 30 | }, 31 | "files": [ 32 | "lib" 33 | ], 34 | "scripts": {}, 35 | "dependencies": { 36 | "axios": "^1.8.3", 37 | "retry-axios": "^2.6.0", 38 | "xmldoc": "^1.1.0", 39 | "yazl": "^2.5.1" 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /packages/ui5-task-nwabap-deployer/.npmrc: -------------------------------------------------------------------------------- 1 | package-lock=false -------------------------------------------------------------------------------- /packages/ui5-task-nwabap-deployer/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## 2.1.1 (2023-02-15) 2 | 3 | ### Fixes 4 | - Support UI5 Tooling 3.0 in respect to reduce build time by defining that no dependencies are required. 5 | 6 | ## 2.1.0 (2021-07-21) 7 | 8 | ### Features 9 | - New option `testMode` for deployment execution in test mode. 10 | 11 | ## 2.0.0 (2021-06-18) 12 | 13 | ### Features 14 | - Usage of /UI5/ABAP_REPOSITORY_SRV service for deployment instead of Team Provider API. 15 |
Team Provider API was set to deprecated. As a consequence starting from version 2.0.0 this deployer supports only systems with at least SAP_UI 753 component installed. 16 |
For all previous versions, version 1.x.x of this deployer needs to be used. 17 | 18 | ## 1.0.15 (2020-09-25) 19 | 20 | ### Feature 21 | - Support of `connection.customQueryParams` configuration option to be able to transfer custom parameters to the backend (for instance to bypass SAML2 or SPNEGO authentication). 22 | 23 | ## 1.0.14 (2020-09-04) 24 | 25 | ### Fixes 26 | - Fixed issue of undefined properties in case no complete connection configuration is provided. 27 | 28 | ## 1.0.13 (2020-08-31) 29 | 30 | ### Features 31 | - New environment variable `UI5_TASK_NWABAP_DEPLOYER__CLIENT` to support setting of SAP client via environment. 32 | 33 | ## 1.0.12 (2020-04-29) 34 | 35 | ### Fixes 36 | - Throw error in case connection configuration details are missing (instead of just writing a log entry). 37 | 38 | ## 1.0.11 (2020-04-27) 39 | 40 | ### General 41 | - Update dependency for ui5-nwabap-deployer-core package to v1.0.6 + setting for automatic update of ui5-nwabap-deployer-core package. 42 | 43 | ## 1.0.10 (2020-04-15) 44 | 45 | ### General 46 | - Update dependency for ui5-nwabap-deployer-core package to v1.0.5. 47 | - Update dependency to new UI5 Tooling 2.0 release. 48 | 49 | ## 1.0.9 (2020-03-17) 50 | 51 | ### General 52 | - Update dependency for ui5-nwabap-deployer-core package to v1.0.4. 53 | 54 | ## 1.0.8 (2020-03-11) 55 | 56 | ### General 57 | - Update dependency for ui5-nwabap-deployer-core package to v1.0.3. 58 | 59 | ## 1.0.7 (2020-03-06) 60 | 61 | ### Fixes 62 | - In case of errors, UI5 tooling build infrastructure logic was not correctly informed. Thanks to [@vobu](https://twitter.com/vobu) for the fix. 63 | 64 | ## 1.0.6 (2020-01-24) 65 | 66 | ### General 67 | - Update dependency for ui5-nwabap-deployer-core package to v1.0.2. 68 | 69 | ## 1.0.5 (2019-12-20) 70 | 71 | ### General 72 | - Optimization to use files provided by UI5 Tooling workspace. Before that change, files were read again from file system. Due to that change option `resources.path` is not used anymore. 73 | 74 | ### Features 75 | - Support of `.env` file to define environment variables for local development purposes; applied by the [dotenv](https://www.npmjs.com/package/dotenv) module. 76 | 77 | ## 1.0.4 (2019-12-02) 78 | 79 | ### Fixes 80 | - In case that all UI5 Tooling tasks were excluded (via the --exclude-task option) and just the deployer task was executed, an error occurred, because of the missing resources. 81 | 82 | ## 1.0.3 (2019-11-28) 83 | 84 | ### Features 85 | - `configuration.resources.path` is optional; if not set `dist` is used by default. 86 | - New supported environment variable `UI5_TASK_NWABAP_DEPLOYER__TRANSPORTNO` to be able to set transport number in a dynamic way in the pipeline. Avoids hard coded transport numbers in ui5.yaml configuration files which need to updated again and again. 87 | 88 | ## 1.0.2 (2019-11-28) 89 | 90 | ### Fixes 91 | - Ensure that files are available in resources path when deployment is started. 92 | 93 | ## 1.0.1 (2019-11-26) 94 | 95 | ### Fixes 96 | - Missing ui5.yaml file in published NPM package. 97 | 98 | ## 1.0.0 (2019-11-16) 99 | 100 | ### General 101 | - Initial release. -------------------------------------------------------------------------------- /packages/ui5-task-nwabap-deployer/README.md: -------------------------------------------------------------------------------- 1 | [![npm version](https://badge.fury.io/js/ui5-task-nwabap-deployer.svg)](https://badge.fury.io/js/ui5-task-nwabap-deployer) 2 | 3 | # ui5-task-nwabap-deployer 4 | 5 | `ui5-task-nwabap-deployer` is a custom UI5 Tooling task which allows to directly deploy the builded sources to a SAP NetWeaver ABAP application server. 6 | 7 | Starting from version 2.0.0 this deployer uses the OData Service [/UI5/ABAP_RESPOSITORY_SRV](https://ui5.sap.com/#/topic/a883327a82ef4cc792f3c1e7b7a48de8) for deploying UI5 sources. Please make sure that the service is activated on your system (for details you can check SAP note [2999557](https://launchpad.support.sap.com/#/notes/2999557)). The new service does some sanity checks like e.g. virus scans. If you have not configured virus scan profiles or want to disable virus scanning please have a look to SAP note [2437892](https://launchpad.support.sap.com/#/notes/2437892). 8 |
Current deployer versions starting from version 2.0.0 can be used with SAP systems on which component SAP_UI 753 is installed. On systems with a lower version of component SAP_UI, you have to use version 1.x.x of this deployer. 9 | 10 | ## Install 11 | ```bash 12 | npm install ui5-task-nwabap-deployer --save-dev 13 | ``` 14 | 15 | ## Configuration Options (in `$yourapp/ui5.yaml`) 16 | 17 | - resources 18 | - pattern: optional `string` | `array` glob pattern to match files for deployment (e.g. `**/*.*` to match all files); if not set `**/*.*` is used 19 | - connection 20 | - server: `string` SAP NetWeaver ABAP application server information in form `protocol://host:port` (alternative: set environment variable `UI5_TASK_NWABAP_DEPLOYER__SERVER`) 21 | - client: optional `string` Client of SAP NetWeaver ABAP application server; if not set default client of server is used (alternative: set environment variable `UI5_TASK_NWABAP_DEPLOYER__CLIENT`) 22 | - useStrictSSL: optional `true|false` SSL mode handling. In case of self signed certificates the useStrictSSL mode option can be set to false to allow a deployment of files; default value: `true` 23 | - proxy: optional `string` Proxy to be used for communication to SAP NetWeaver ABAP application server (e.g. `http://myproxyhost:3128`). 24 | - testMode: optional `boolean` Do deployment in test mode (alternative: set environment variable UI5_TASK_NWABAP_DEPLOYER__TESTMODE). 25 | - customQueryParams: option key/value query parameter values added to the call to the SAP NetWeaver ABAP application server 26 | - authentication 27 | - user: `string`User used for logon to SAP NetWeaver ABAP application server (alternative: set environment variable `UI5_TASK_NWABAP_DEPLOYER__USER`) 28 | - password: `string` Password used for logon to SAP NetWeaver ABAP application server (alternative: set environment variable `UI5_TASK_NWABAP_DEPLOYER__PASSWORD`) 29 | - ui5 30 | - language: optional `string` Language for deployment (e.g. EN); default value `EN` 31 | - package: `string` Defines the development package in which the BSP container for the UI5 sources is available or should be created. 32 | - bspContainer: `string` Defines the name of the BSP container used for the storage of the UI5 sources. Length is restricted to 15 characters (exclusive customer specific namespaces, e.g. /YYY/). 33 | - bspContainerText: `string` Defines the description of the BSP container. 34 | - transportNo: optional (in case package is set to `$TMP`) `string` Defines the transport number which logs the changes (alternative: set environment variable `UI5_TASK_NWABAP_DEPLOYER__TRANSPORTNO`). 35 | - createTransport: optional `true|false` Set this option to true in case a new transport should be created each time the application is deployed. 36 | - transportText: optional `string` Text for transport to be created. 37 | - transportUseUserMatch: optional `true|false` It will be tried to find a transport request of the given user. If no transport is found and createTransport is enabled a new one will be created and used for further file deployments. 38 | - transportUseLocked: optional `true|false` If a deployment failed due to the BSP application is locked in another transport, the old (original one) transport will be used to deploy the files. 39 | 40 | ## Usage 41 | 42 | 1. Define the dependency in `$yourapp/package.json`. Since UI5 tooling version 3.0 the `ui5.dependencies` setting is not required anymore. 43 | ```json 44 | ... 45 | "devDependencies": { 46 | // ... 47 | "ui5-task-nwabap-deployer": "*" 48 | // ... 49 | }, 50 | "ui5": { 51 | "dependencies": [ 52 | // ... 53 | "ui5-task-nwabap-deployer", 54 | // ... 55 | ] 56 | } 57 | ... 58 | ``` 59 | 60 | 2. Configure it in `$yourapp/ui5.yaml` 61 | ```yaml 62 | builder: 63 | customTasks: 64 | - name: ui5-task-nwabap-deployer 65 | afterTask: generateVersionInfo 66 | configuration: 67 | resources: 68 | pattern: "**/*.*" 69 | connection: 70 | server: http://myserver:8000 71 | customQueryParams: 72 | myCustomParameter1: myCustomValue1 73 | myCustomParameter2: myCustomValue2 74 | authentication: 75 | user: myUser 76 | password: myPassword 77 | ui5: 78 | language: EN 79 | package: ZZ_UI5_REPO 80 | bspContainer: ZZ_UI5_TRACKED 81 | bspContainerText: UI5 Upload 82 | transportNo: DEVK900000 83 | ``` 84 | 85 | ## Further Information 86 | 87 | ### Environment Variables 88 | For development purposes environment variables can be stored in a `.env` file. They are automatically applied by the [dotenv](https://www.npmjs.com/package/dotenv) module. 89 | 90 | ## Release History 91 | 92 | [CHANGELOG.md](https://github.com/pfefferf/ui5-nwabap-deployer/blob/master/packages/ui5-task-nwabap-deployer/CHANGELOG.md) 93 | 94 | ## License 95 | 96 | [Apache License 2.0](http://www.apache.org/licenses/LICENSE-2.0) 97 | -------------------------------------------------------------------------------- /packages/ui5-task-nwabap-deployer/lib/Logger.js: -------------------------------------------------------------------------------- 1 | const oLogger = require("@ui5/logger").getLogger("builder:customtask:nwabap-deployer"); 2 | 3 | class Logger { 4 | log(message) { 5 | oLogger.info(message); 6 | } 7 | 8 | error(message) { 9 | oLogger.error(message); 10 | } 11 | 12 | logVerbose(message) { 13 | oLogger.verbose(message); 14 | } 15 | } 16 | 17 | module.exports = Logger; 18 | -------------------------------------------------------------------------------- /packages/ui5-task-nwabap-deployer/lib/ui5-task-nwabap-deployer.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | const Logger = require("./Logger"); 4 | const ui5DeployerCore = require("ui5-nwabap-deployer-core"); 5 | require("dotenv").config(); 6 | 7 | /** 8 | * UI5 Tooling Task for deploying UI5 Sources to a SAP NetWeaver ABAP system 9 | * 10 | * @param {Object} parameters Parameters 11 | * @param {module:@ui5/fs.DuplexCollection} parameters.workspace DuplexCollection to read and write files 12 | * @param {module:@ui5/fs.AbstractReader} parameters.dependencies Reader or Collection to read dependency files 13 | * @param {Object} parameters.options Options 14 | * @param {string} parameters.options.projectName Project name 15 | * @param {string} [parameters.options.configuration] Task configuration if given in ui5.yaml 16 | * @returns {Promise} Promise resolving with undefined once data has been written 17 | */ 18 | module.exports = async function({ workspace, options }) { 19 | const oLogger = new Logger(); 20 | 21 | oLogger.log("Start deploying UI5 sources."); 22 | 23 | if ((options.configuration && !options.configuration.connection) && !process.env.UI5_TASK_NWABAP_DEPLOYER__SERVER) { 24 | return Promise.reject(new Error("Please provide a connection configuration.")); 25 | } 26 | 27 | if (options.configuration && !options.configuration.connection) { 28 | options.configuration.connection = {}; 29 | } 30 | 31 | let bTestMode = process.env.UI5_TASK_NWABAP_DEPLOYER__TESTMODE === "true"; 32 | 33 | if (options.configuration && options.configuration.connection.testMode) { 34 | bTestMode = !!options.configuration.connection.testMode; 35 | } 36 | 37 | let sServer = process.env.UI5_TASK_NWABAP_DEPLOYER__SERVER; 38 | 39 | if (options.configuration && options.configuration.connection && options.configuration.connection.server) { 40 | sServer = options.configuration.connection.server; 41 | } 42 | 43 | let sClient = process.env.UI5_TASK_NWABAP_DEPLOYER__CLIENT; 44 | 45 | if (options.configuration && options.configuration.connection && options.configuration.connection.client) { 46 | sClient = options.configuration.connection.client; 47 | } 48 | 49 | if ((options.configuration && !options.configuration.authentication) && 50 | (!process.env.UI5_TASK_NWABAP_DEPLOYER__USER || !process.env.UI5_TASK_NWABAP_DEPLOYER__PASSWORD)) { 51 | return Promise.reject(new Error("Please provide an authentication configuration or set authentication environment variables (user name and password).")); 52 | } 53 | 54 | let sUser = process.env.UI5_TASK_NWABAP_DEPLOYER__USER; 55 | let sPassword = process.env.UI5_TASK_NWABAP_DEPLOYER__PASSWORD; 56 | 57 | if (options.configuration && options.configuration.authentication && options.configuration.authentication.user) { 58 | sUser = options.configuration.authentication.user; 59 | } 60 | 61 | if (options.configuration && options.configuration.authentication && options.configuration.authentication.password) { 62 | sPassword = options.configuration.authentication.password; 63 | } 64 | 65 | if (options.configuration && !options.configuration.ui5) { 66 | return Promise.reject(new Error("Please provide a UI5 configuration.")); 67 | } 68 | 69 | let sTransportNo = process.env.UI5_TASK_NWABAP_DEPLOYER__TRANSPORTNO; 70 | if (options.configuration && options.configuration.ui5 && options.configuration.ui5.transportNo) { 71 | sTransportNo = options.configuration.ui5.transportNo; 72 | } 73 | 74 | let sResourcePattern = "**/*.*"; 75 | if (options.configuration && options.configuration.resources && options.configuration.resources.pattern) { 76 | sResourcePattern = options.configuration.resources.pattern; 77 | } 78 | 79 | return workspace.byGlob(sResourcePattern).then((resources) => { 80 | return Promise.all(resources.map(async (resource) => { 81 | if (options.projectNamespace) { 82 | resource.setPath(resource.getPath().replace( 83 | new RegExp(`^/resources/${options.projectNamespace}`), "")); 84 | } 85 | let sPath = resource.getPath(); 86 | if (sPath.startsWith("/")) { 87 | sPath = sPath.substring(1); 88 | } 89 | return { 90 | path: sPath, 91 | content: await resource.getBuffer() 92 | }; 93 | })); 94 | }).then(async (aFiles) => { 95 | const oDeployOptions = { 96 | conn: { 97 | server: sServer, 98 | client: sClient, 99 | useStrictSSL: options.configuration.connection.useStrictSSL, 100 | proxy: options.configuration.connection.proxy, 101 | customQueryParams: options.configuration.connection.customQueryParams ? options.configuration.connection.customQueryParams : {}, 102 | testMode: bTestMode 103 | }, 104 | auth: { 105 | user: sUser, 106 | pwd: sPassword 107 | }, 108 | ui5: { 109 | language: options.configuration.ui5.language, 110 | transportno: sTransportNo, 111 | package: options.configuration.ui5.package, 112 | bspcontainer: options.configuration.ui5.bspContainer, 113 | bspcontainer_text: options.configuration.ui5.bspContainerText, 114 | create_transport: !!options.configuration.ui5.createTransport, 115 | transport_text: options.configuration.ui5.transportText, 116 | transport_use_user_match: !!options.configuration.ui5.transportUseUserMatch, 117 | transport_use_locked: !!options.configuration.ui5.transportUseLocked 118 | } 119 | }; 120 | 121 | try { 122 | await ui5DeployerCore.deployUI5toNWABAP(oDeployOptions, aFiles, oLogger); 123 | oLogger.log("UI5 sources successfully deployed."); 124 | } catch (oError) { 125 | oLogger.error(oError); 126 | throw new Error(oError); 127 | } 128 | }).then(() => { 129 | return Promise.resolve(); 130 | }).catch((oError) => { 131 | return Promise.reject(oError); 132 | }); 133 | }; 134 | 135 | /** 136 | * Callback function to define the list of required dependencies. No dependencies required for this custom task. 137 | * 138 | */ 139 | module.exports.determineRequiredDependencies = async function({availableDependencies, getDependencies, getProject, options}) { 140 | return new Set(); 141 | }; 142 | -------------------------------------------------------------------------------- /packages/ui5-task-nwabap-deployer/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ui5-task-nwabap-deployer", 3 | "version": "2.1.1", 4 | "description": "UI5 Tooling Task for deploying UI5 sources to a SAP NetWeaver ABAP system", 5 | "author": { 6 | "name": "Florian Pfeffer", 7 | "email": "florian.pfeffer@outlook.com" 8 | }, 9 | "repository": { 10 | "type": "git", 11 | "url": "git+https://github.com/pfefferf/ui5-nwabap-deployer.git" 12 | }, 13 | "bugs": { 14 | "url": "https://github.com/pfefferf/ui5-nwabap-deployer/issues" 15 | }, 16 | "license": "Apache-2.0", 17 | "licenses": [ 18 | { 19 | "type": "Apache-2.0", 20 | "url": "https://github.com/pfefferf/ui5-nwabap-deployer/blob/master/LICENSE" 21 | } 22 | ], 23 | "engines": { 24 | "node": ">= 12", 25 | "npm": ">= 7" 26 | }, 27 | "main": "lib/ui5-task-nwabap-deployer.js", 28 | "directories": { 29 | "lib": "lib" 30 | }, 31 | "scripts": {}, 32 | "dependencies": { 33 | "@ui5/logger": "^2.0.0", 34 | "ui5-nwabap-deployer-core": "^2.2.0", 35 | "dotenv": "^8.2.0" 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /packages/ui5-task-nwabap-deployer/ui5.yaml: -------------------------------------------------------------------------------- 1 | specVersion: "1.0" 2 | kind: extension 3 | type: task 4 | metadata: 5 | name: ui5-task-nwabap-deployer 6 | task: 7 | path: lib/ui5-task-nwabap-deployer.js --------------------------------------------------------------------------------