├── .gitignore
├── .reuse
└── dep5
├── CONTRIBUTING.md
├── LICENSE
├── LICENSES
└── Apache-2.0.txt
├── README.md
├── applications
├── BPA
│ └── Approval Workflow 2 eyes_1.0.2.mtar
├── CustomProcessUpload
│ ├── .gitattributes
│ ├── .gitignore
│ ├── .npmrc
│ ├── LICENSE
│ ├── README.md
│ ├── codelists
│ │ └── countries.js
│ ├── index.js
│ ├── input
│ │ ├── Sample Content Public.xlsx
│ │ └── diagrams
│ │ │ ├── Idea-to-Market
│ │ │ ├── SPF Create Detailed Design.svg
│ │ │ ├── SPF Develop Requirements & Design Systems.svg
│ │ │ ├── SPF Handover new Material.svg
│ │ │ ├── SPF Handover to Manufacturing.bpmn2
│ │ │ └── SVF Idea to Market for Hybrid Deployment.svg
│ │ │ └── Lead-to-Cash
│ │ │ └── SVF Lead to Cash for Hybrid Deployment.svg
│ ├── log
│ │ └── README.md
│ ├── modules
│ │ ├── Builder.js
│ │ ├── CalmAdapter.js
│ │ └── UserInterface.js
│ └── package.json
├── Readme.md
├── ScopingAPI
│ ├── index.js
│ ├── package.json
│ ├── readme.MD
│ ├── recording
│ │ └── 2022-09-14_11-49-53.mp4
│ ├── secret.js.sample
│ └── service.json
├── calm-api-consumer-java-sample
│ ├── README.md
│ ├── cloud-alm-api-scopes.json
│ ├── documents
│ │ └── cloud-alm-api-sample-architecture.jpg
│ ├── manifest.yml
│ ├── pom.xml
│ └── src
│ │ └── main
│ │ ├── java
│ │ └── com
│ │ │ └── sap
│ │ │ └── refapps
│ │ │ └── cloudalm
│ │ │ ├── CloudALMAPIAccessController.java
│ │ │ ├── ConsumingApplication.java
│ │ │ ├── EnvironmentUtil.java
│ │ │ ├── JWTTokenProvider.java
│ │ │ ├── JWTTokenResponse.java
│ │ │ ├── JwtTokenDto.java
│ │ │ ├── ProjectDto.java
│ │ │ └── VcapServicesDto.java
│ │ └── resources
│ │ └── application.properties
├── calm-api-consumer-ui5-sample
│ ├── .gitignore
│ ├── Makefile_20210726123408.mta
│ ├── README.md
│ ├── approuter
│ │ ├── package.json
│ │ └── xs-app.json
│ ├── mta.yaml
│ ├── package.json
│ ├── ui5-deploy.yaml
│ ├── ui5-local.yaml
│ ├── ui5.yaml
│ ├── webapp
│ │ ├── Component.js
│ │ ├── ModelManager.js
│ │ ├── controller
│ │ │ ├── App.controller.js
│ │ │ ├── Kanban.controller.js
│ │ │ ├── PieChart.controller.js
│ │ │ └── ProjectList.controller.js
│ │ ├── css
│ │ │ └── style.css
│ │ ├── i18n
│ │ │ └── i18n.properties
│ │ ├── index.html
│ │ ├── manifest.json
│ │ ├── models
│ │ │ ├── InfoTask.json
│ │ │ ├── KanbanModel.json
│ │ │ ├── Projects.json
│ │ │ ├── Tasks.json
│ │ │ └── TasksStatistics.json
│ │ ├── utils
│ │ │ ├── Constants.js
│ │ │ └── formatter.js
│ │ └── view
│ │ │ ├── App.view.xml
│ │ │ ├── CreateTask.fragment.xml
│ │ │ ├── Kanban.view.xml
│ │ │ ├── PieChart.view.xml
│ │ │ └── ProjectList.view.xml
│ ├── xs-app.json
│ └── xs-security.json
└── kanban4calm
│ ├── ExampleScreenshot.png
│ ├── HL_Architecture.drawio.png
│ ├── README.md
│ ├── manifest.yaml
│ └── server
│ ├── .gitignore
│ ├── config.json.sample
│ ├── package.json
│ ├── server.js
│ └── static
│ └── index.html
├── jupyternotebooks
├── .gitignore
├── PmApiTest.ipynb
├── Readme.md
├── apidata_template.py
├── calm-copy-template-project.ipynb
├── calm-process-authoring-api-v1.ipynb
├── calm-project-task-2022-02-updates.ipynb
├── calm-project-task-api.ipynb
└── calm-requirements-api.ipynb
├── postmancollections
├── README.md
├── SAP Cloud ALM Projects-Tasks Public Collection.postman_collection.json
└── analytics
│ ├── CALM Analytics Demo.postman_collection.json
│ └── README.md
└── spreadsheet-examples
├── spreadsheet-documents
└── SAP Cloud ALM - Documents template.xlsx
├── spreadsheet-libraries
├── SAP Cloud ALM - Library Elements Template for Application.xlsx
├── SAP Cloud ALM - Library Elements Template for Configuration and Configuration Activity.xlsx
├── SAP Cloud ALM - Library Elements Template.xlsx
└── readme.md
├── spreadsheet-process-authoring
└── SAP Cloud ALM - Process Authoring - Solution Activities template.xlsx
├── spreadsheet-process-hierarchy
└── SAP Cloud ALM - Process Hierarchy template.xlsx
├── spreadsheet-task-creation
├── Example_Template_Create_Tasks.xlsx
├── Example_Template_Update_Tasks.xlsx
├── Excel_Template_Requirement_Hierarchy_upload.xlsx
├── Excel_upload_template_Defects.xlsx
├── Excel_upload_template_Requirements.xlsx
├── Excel_upload_template_tasks.xlsx
└── README.md
└── spreadsheet-testcases
└── SAP Cloud ALM - Test Cases template.xlsx
/.gitignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | logs
3 | *.log
4 | npm-debug.log*
5 | yarn-debug.log*
6 | yarn-error.log*
7 |
8 | #Config Files for node.js applications ( provide config.json.sample instead)
9 | config.json
10 |
11 | #MacOs
12 | .DS_Store
13 |
14 | # Runtime data
15 | pids
16 | *.pid
17 | *.seed
18 | *.pid.lock
19 |
20 | # Directory for instrumented libs generated by jscoverage/JSCover
21 | lib-cov
22 |
23 | # Coverage directory used by tools like istanbul
24 | coverage
25 |
26 | # nyc test coverage
27 | .nyc_output
28 |
29 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
30 | .grunt
31 |
32 | # Bower dependency directory (https://bower.io/)
33 | bower_components
34 |
35 | # node-waf configuration
36 | .lock-wscript
37 |
38 | # Compiled binary addons (https://nodejs.org/api/addons.html)
39 | build/Release
40 |
41 | # Dependency directories
42 | node_modules/
43 | jspm_packages/
44 | package-lock.json
45 |
46 | # TypeScript v1 declaration files
47 | typings/
48 |
49 | # Optional npm cache directory
50 | .npm
51 |
52 | # Optional eslint cache
53 | .eslintcache
54 |
55 | # Optional REPL history
56 | .node_repl_history
57 |
58 | # Output of 'npm pack'
59 | *.tgz
60 |
61 | # Yarn Integrity file
62 | .yarn-integrity
63 |
64 | # dotenv environment variables file
65 | .env
66 |
67 | # parcel-bundler cache (https://parceljs.org/)
68 | .cache
69 |
70 | # next.js build output
71 | .next
72 |
73 | # nuxt.js build output
74 | .nuxt
75 |
76 | # vuepress build output
77 | .vuepress/dist
78 |
79 | # Serverless directories
80 | .serverless
81 |
82 | # FuseBox cache
83 | .fusebox/
84 | .vscode/spellright.dict
85 | .vscode/settings.json
86 |
87 | .vscode/
--------------------------------------------------------------------------------
/.reuse/dep5:
--------------------------------------------------------------------------------
1 | Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
2 | Upstream-Name: cloud-alm-api-examples
3 | Upstream-Contact: michael.kloeffer@sap.com
4 | Source: https://github.com/sap-samples/cloud-alm-api-examples
5 | Disclaimer: The code in this project may include calls to APIs (“API Calls”) of
6 | SAP or third-party products or services developed outside of this project
7 | (“External Products”).
8 | “APIs” means application programming interfaces, as well as their respective
9 | specifications and implementing code that allows software to communicate with
10 | other software.
11 | API Calls to External Products are not licensed under the open source license
12 | that governs this project. The use of such API Calls and related External
13 | Products are subject to applicable additional agreements with the relevant
14 | provider of the External Products. In no event shall the open source license
15 | that governs this project grant any rights in or to any External Products,or
16 | alter, expand or supersede any terms of the applicable additional agreements.
17 | If you have a valid license agreement with SAP for the use of a particular SAP
18 | External Product, then you may make use of any API Calls included in this
19 | project’s code for that SAP External Product, subject to the terms of such
20 | license agreement. If you do not have a valid license agreement for the use of
21 | a particular SAP External Product, then you may only make use of any API Calls
22 | in this project for that SAP External Product for your internal, non-productive
23 | and non-commercial test and evaluation of such API Calls. Nothing herein grants
24 | you any rights to use or access any SAP External Product, or provide any third
25 | parties the right to use of access any SAP External Product, through API Calls.
26 |
27 | Files: *
28 | Copyright: 2021 SAP SE or an SAP affiliate company and cloud-alm-api-examples contributors
29 | License: Apache-2.0
30 |
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # Contributing to API examples for SAP Cloud ALM
2 |
3 | You want to contribute to our API examples? Welcome! Please read this document to understand what you can do:
4 |
5 | ## Contribute Code
6 |
7 | You are welcome to contribute code to this repository in order to help others using the APIs.
8 |
9 | There are three important things to know:
10 |
11 | 1. You must be aware of the Apache License (which describes contributions) and **agree to the Developer Certificate of Origin**. This is common practice in all major Open Source projects. To make this process as simple as possible, we are using *[CLA assistant](https://cla-assistant.io/)*. CLA assistant is an open source tool that integrates with GitHub very well and enables a one-click-experience for accepting the DCO. See the respective section below for details.
12 | 2. **Not all proposed contributions can be accepted**. Some features may e.g. just fit a third-party add-on better. The code must fit the overall direction of SAP Cloud ALM and really improve it, so there should be some "bang for the byte".
13 |
14 | ### Developer Certificate of Origin (DCO)
15 |
16 | Due to legal reasons, contributors will be asked to accept a DCO before they submit the first pull request to this project. SAP uses [the standard DCO text of the Linux Foundation](https://developercertificate.org/).
17 | This happens in an automated fashion during the submission process: the CLA assistant tool will add a comment to the pull request. Click it to check the DCO, then accept it on the following screen. CLA assistant will save this decision for upcoming contributions.
18 |
19 | This DCO replaces the previously used CLA ("Contributor License Agreement") as well as the "Corporate Contributor License Agreement" with new terms which are well-known standards and hence easier to approve by legal departments. Contributors who had already accepted the CLA in the past may be asked once to accept the new DCO.
20 |
21 | ### Contribution Content Guidelines
22 |
23 | Contributed content can be accepted if it is useful for others and helps to improve the API examples.
24 | In addition there are a few more rules that we would like you to follow:
25 |
26 | - Apply a clean coding style adapted to the surrounding code, even though we are aware the existing code is not fully clean
27 | - Use tabs for indentation (except if the modified file consistently uses spaces)
28 | - Only access public APIs of other entities
29 | - Comment your code where it gets non-trivial and remember to keep the public JSDoc documentation up-to-date
30 | - Keep an eye on performance and memory consumption, properly destroy objects when not used anymore (e.g. avoid ancestor selectors in CSS)
31 | - Try to write slim and "modern" HTML and CSS, avoid using images and affecting any non-UI5 content in the page/app
32 | - Always consider the developer who USES your control/code!
33 | - Think about what code and how much code he/she will need to write to use your feature
34 | - Think about what she/he expects your control/feature to do
35 |
36 | ### How to contribute - the Process
37 |
38 | 1. Make sure the change would be welcome (e.g. a useful example of an API call); best do so by proposing it in a GitHub issue
39 | 2. Create a branch forking the examples repository and do your change
40 | 3. Commit and push your changes on that branch
41 | - When you have several commits, squash them into one (see [this explanation](http://davidwalsh.name/squash-commits-git)) - this also needs to be done when additional changes are required after the code review
42 | 4. Create a Pull Request to github.com/SAP-samples/cloud-alm-api-examples
43 | 5. Follow the link posted by the CLA assistant to your pull request and accept the Developer Certificate of Origin, as described in detail above.
44 | 6. Wait for our code review and approval, possibly enhancing your change on request
45 | 7. Once the change has been approved we will inform you in a comment
46 | 8. We will close the pull request, feel free to delete the now obsolete branch
47 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/LICENSES/Apache-2.0.txt:
--------------------------------------------------------------------------------
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 | [](https://api.reuse.software/info/github.com/SAP-samples/cloud-alm-api-examples)
2 | # API examples for SAP Cloud ALM
3 |
4 | In this Github repository we provide open source examples for APIs that get delivered within SAP Cloud ALM.
5 | The actual APIs for SAP Cloud ALM are [documented in the SAP API Hub](https://api.sap.com/package/SAPCloudALM/rest). Here we want to provide you with help on getting started with the APIs, some sample applications, code snippets,...
6 |
7 | If you want to learn more about SAP Cloud ALM:
8 | - you can look at our page in [SAP Support Portal](https://support.sap.com/en/alm/sap-cloud-alm.html)
9 | - check out the [blogs in the community](https://blogs.sap.com/tags/73554900100700002361/)
10 | - or watch some [videos on YouTube on SAP Cloud ALM](
11 | https://www.youtube.com/playlist?list=PLFrwZZeBUtfiJyWpJ2nmokXOFSue_Z7sQ)
12 |
13 | Currently we provide the following assets:
14 | - [Sample Applications](applications)
15 | - [Jupyter Notebooks](jupyternotebooks)
16 | - [Postman collections](postmancollections)
17 |
18 | ## How to obtain support
19 |
20 | [Create an issue](https://github.com/SAP-samples/cloud-alm-api-examples/issues) in this repository if you find a bug or have questions about the content.
21 |
22 | For additional support, [ask a question in SAP Community](https://answers.sap.com/questions/ask.html).
23 |
24 | ## Contributing
25 |
26 | Check out [CONTRIBUTING.md](CONTRIBUTING.md)
27 |
28 | ## License
29 | Copyright (c) 2021 SAP SE or an SAP affiliate company. All rights reserved. This project is licensed under the Apache Software License, version 2.0 except as noted otherwise in the [LICENSE](LICENSES/Apache-2.0.txt) file.
30 |
--------------------------------------------------------------------------------
/applications/BPA/Approval Workflow 2 eyes_1.0.2.mtar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SAP-samples/cloud-alm-api-examples/0503338e01aad752dcf1f9d7dee8660e7a1e275d/applications/BPA/Approval Workflow 2 eyes_1.0.2.mtar
--------------------------------------------------------------------------------
/applications/CustomProcessUpload/.gitattributes:
--------------------------------------------------------------------------------
1 | # Auto detect text files and perform LF normalization
2 | * text=auto
3 |
--------------------------------------------------------------------------------
/applications/CustomProcessUpload/.gitignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | /log/*.json
--------------------------------------------------------------------------------
/applications/CustomProcessUpload/.npmrc:
--------------------------------------------------------------------------------
1 | registry=https://registry.npmjs.com
--------------------------------------------------------------------------------
/applications/CustomProcessUpload/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 |
--------------------------------------------------------------------------------
/applications/CustomProcessUpload/README.md:
--------------------------------------------------------------------------------
1 | # SAP Cloud ALM Custom Content Upload Demo
2 |
3 | This is an example to illustrate how you can use SAP Cloud ALM Custom Processes API. The code is not production ready and you use it on your own risk. :-)
4 | Even though the Scoping API is actually built as batch oriented REST API, this example implements UI interactions. Overall the API is not intended to build UIs but rather synchronization scenarios.
5 |
6 | You can find all the details about the SAP Cloud ALM Custom Processes API on the [SAP API Business Hub](https://api.sap.com/api/CALM_PMGE/overview).
7 |
8 | ## Install libraries
9 |
10 | [Sad but true.](https://www.monkeyuser.com/2017/npm-delivery/)
11 |
12 | Navigate to CustomProcessUpload folder, and then
13 |
14 | > npm install
15 | >
16 | ## Credentials
17 |
18 | Please make sure you have sufficient authorizations to call the APIs.
19 | The required authorisation scopes, to run the sample, are:
20 |
21 | * calm-api.processauthoring.read: Read process authoring entities.
22 | * calm-api.processauthoring.write: Write process authoring entities.
23 | * calm-api.projects.read: to view projects.
24 |
25 | To learn how generate needed client id and secret please check this [blog](https://blogs.sap.com/2021/08/12/sap-cloud-alm-extend-with-api-get-started-with-sap-cloud-alm-api/).
26 |
27 | ## Run the script
28 |
29 | > node index.js
30 |
31 | Please check the logs in the log directory.
32 |
33 | Have fun.
--------------------------------------------------------------------------------
/applications/CustomProcessUpload/index.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | /*******************************************************************************
4 | * index.js main program
5 | *******************************************************************************/
6 | import UserInterface from './modules/UserInterface.js'
7 | import Builder from './modules/Builder.js'
8 | import CalmAdapter from './modules/CalmAdapter.js'
9 | import { writeFileSync } from 'fs'
10 | import loading from 'loading-indicator';
11 |
12 | /******************************************************************************
13 | * Main program
14 | */
15 |
16 | (async () => {
17 |
18 | const userInterface = new UserInterface()
19 | userInterface.welcomeScreen()
20 | const excelFile = await userInterface.getExcelFileDialog()
21 |
22 | const importer = loading.start('Importing...')
23 | const builder = new Builder()
24 | const excelContent = builder.readExcelContent(excelFile)
25 |
26 | try {
27 | builder.validateExcelContent(excelContent)
28 | } catch (error) {
29 | console.log(error)
30 | process.exit(1)
31 | }
32 |
33 | const calmAdapter = new CalmAdapter(builder.getCustomerData(excelContent.customer))
34 |
35 | const businessProcesses = await calmAdapter.postBusinessProcesses(builder.getBusinessProcesses(excelContent.solutionProcesses))
36 | writeFileSync('./log/businessProcesses.json', JSON.stringify(businessProcesses), { encoding: 'utf8', flag: 'w' })
37 |
38 | const solutionProcesses = await calmAdapter.postSolutionProcesses(builder.getSolutionProcesses(excelContent.solutionProcesses, businessProcesses))
39 | writeFileSync('./log/solutionProcesses.json', JSON.stringify(solutionProcesses), { encoding: 'utf8', flag: 'w' })
40 |
41 | const accelerators = await calmAdapter.postAccelerators(builder.getAccelerators(excelContent.accelerators, solutionProcesses))
42 | writeFileSync('./log/accelerators.json', JSON.stringify(accelerators), { encoding: 'utf8', flag: 'w' })
43 |
44 | const svfDiagrams = await calmAdapter.postSolutionValueFlows(builder.getDiagrams(excelContent.diagrams, 'Solution Value Flow Diagram'), solutionProcesses)
45 | writeFileSync('./log/svfDiagrams.json', JSON.stringify(svfDiagrams), { encoding: 'utf8', flag: 'w' })
46 |
47 | const spfDiagrams = await calmAdapter.postSolutionProcessFlowDiagrams(builder.getDiagrams(excelContent.diagrams, 'Solution Process Flow Diagram'), await calmAdapter.findSolutionProcessFlowsBySolutionProcesses(solutionProcesses))
48 | writeFileSync('./log/spfDiagrams.json', JSON.stringify(spfDiagrams), { encoding: 'utf8', flag: 'w' })
49 |
50 | const activatedProcesses = await calmAdapter.activateSolutionProcesses(solutionProcesses)
51 | writeFileSync('./log/activation.json', JSON.stringify(activatedProcesses), { encoding: 'utf8', flag: 'w' })
52 |
53 | loading.stop(importer);
54 | })()
--------------------------------------------------------------------------------
/applications/CustomProcessUpload/input/Sample Content Public.xlsx:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SAP-samples/cloud-alm-api-examples/0503338e01aad752dcf1f9d7dee8660e7a1e275d/applications/CustomProcessUpload/input/Sample Content Public.xlsx
--------------------------------------------------------------------------------
/applications/CustomProcessUpload/input/diagrams/Idea-to-Market/SPF Handover to Manufacturing.bpmn2:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 | a2ff7a1f4-8809-4c0f-bced-222fa843fdce
10 | a4ceffb7f-b0b8-4054-961c-a6f0fa676d6f
11 | c5ad60db-cc49-4a7b-a3c0-f0dc37f0b246
12 | a1597b06d-5bf7-4111-86a4-d54dad32432b
13 | ef792b4b-1205-45fc-adce-efbf711878bf
14 | e0263f8a-8fc9-4a9d-8b19-07dcf5c325b3
15 | a467153f9-ef77-4157-a7fd-d14165652f19
16 | a3979bc0-94a6-4abb-8501-d92c176eba96
17 | a3bd002ce-b28e-4aff-9326-de8fec3146c0
18 | a5f3ac6bf-c763-4421-bcce-70b21f10e4bb
19 |
20 |
21 |
22 | a88601bdb-a70d-48c0-8754-912c7274c82f
23 |
24 |
25 | a88601bdb-a70d-48c0-8754-912c7274c82f
26 | fd54343b-f7ed-4948-943b-2c5dfd61711a
27 |
28 |
29 | fd54343b-f7ed-4948-943b-2c5dfd61711a
30 | a7c9e8b05-2e10-45e4-ac45-c6b62dcb1140
31 |
32 |
33 | a61231daa-332e-48aa-a431-d05fd6842b38
34 | a793f7e98-0364-4bae-9caf-dce6aa2b4887
35 |
36 |
37 | e85c9903-a38f-480f-8f0a-14a7d604614c
38 | c948b71c-a711-4fe9-bc12-7986de63ab10
39 |
40 |
41 | a2f798216-c590-41fb-a3dc-4356bb3e3d92
42 | a5b58d77f-fba0-4c5a-b327-720ca873e11f
43 |
44 |
45 | a7c9e8b05-2e10-45e4-ac45-c6b62dcb1140
46 | a2f798216-c590-41fb-a3dc-4356bb3e3d92
47 | a61231daa-332e-48aa-a431-d05fd6842b38
48 |
49 |
50 | a793f7e98-0364-4bae-9caf-dce6aa2b4887
51 | a5b58d77f-fba0-4c5a-b327-720ca873e11f
52 | a5d9511cd-ef12-49a3-8e2a-2d260dafce77
53 |
54 |
55 | a5d9511cd-ef12-49a3-8e2a-2d260dafce77
56 | e85c9903-a38f-480f-8f0a-14a7d604614c
57 | a3b5a1f88-011b-44e6-aafc-0e98a16400cd
58 |
59 |
60 | c948b71c-a711-4fe9-bc12-7986de63ab10
61 | a3b5a1f88-011b-44e6-aafc-0e98a16400cd
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
119 |
120 |
121 |
122 |
123 |
124 |
125 |
126 |
127 |
128 |
129 |
130 |
131 |
132 |
133 |
134 |
135 |
136 |
137 |
138 |
139 |
140 |
141 |
142 |
143 |
144 |
145 |
146 |
147 |
148 |
149 |
150 |
151 |
152 |
153 |
154 |
155 |
156 |
157 |
158 |
159 |
160 |
161 |
162 |
163 |
164 |
165 |
166 |
167 |
168 |
169 |
170 |
171 |
172 |
173 |
174 |
175 |
176 |
177 |
178 |
179 |
180 |
181 |
182 |
183 |
184 |
185 |
186 |
187 |
188 |
--------------------------------------------------------------------------------
/applications/CustomProcessUpload/log/README.md:
--------------------------------------------------------------------------------
1 | # Logs
2 | All upload logs goes to the log folder.
--------------------------------------------------------------------------------
/applications/CustomProcessUpload/modules/Builder.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | /*******************************************************************************
4 | * Builder Utility
5 | *******************************************************************************/
6 |
7 | import { read, utils } from "xlsx";
8 | import Path from 'path'
9 | import { fileURLToPath } from 'url'
10 | import loadsh from 'loadsh'
11 | import CountryMaster from '../codelists/countries.js'
12 | import { readFileSync } from 'node:fs'
13 |
14 | export default function Builder() {
15 | const requiredColumnsCustomerSheet = ['Property', 'Value']
16 | const requiredRowsCustomerSheet = ['Customer', 'SAP Cloud ALM Tenant URL', 'SAP Cloud ALM Authorization URL', 'Client ID', 'Client Secret']
17 | const requiredColumnsDiagramSheet = ['Solution Process ID', 'Type', 'Diagram Name', 'SVG']
18 | const requiredColumnsSolutionProcessSheet = ['External ID', 'Name', 'Business Process', 'Description', 'Localization']
19 | const requiredColumnsAccelerators = ['Solution Process ID', 'Name', 'Language', 'Host', 'Path']
20 |
21 | function _hasRequriedColumns(obj, columns) {
22 | return columns.every(column => Object.keys(obj).includes(column))
23 | }
24 |
25 | /**
26 | * TODO: Implement Row check for customer
27 | */
28 | function _hasRequiredRows() { }
29 |
30 | function _hasDuplicates(arr, key) {
31 | const uniqueElements = new Set()
32 | arr.map(el => {
33 | uniqueElements.add(el[key])
34 | })
35 | const duplicates = arr.filter(el => {
36 | if (uniqueElements.has(el[key])) {
37 | uniqueElements.delete(el[key])
38 | } else {
39 | return el
40 | }
41 | })
42 | return duplicates
43 | }
44 |
45 | function _isSubset(parentArray, parentKey, subsetArray, subsetKey) {
46 | const parentArraySet = new Set()
47 | parentArray.map(el => {
48 | parentArraySet.add(el[parentKey])
49 | })
50 | const subsetArraySet = new Set()
51 | subsetArray.map(el => {
52 | subsetArraySet.add(el[subsetKey])
53 | })
54 | return [...subsetArraySet].every((el) => {
55 | return [...parentArraySet].includes(el)
56 | })
57 | }
58 |
59 | function _hasValidCountries(arr, key) {
60 | const masterCountries = new Set()
61 | CountryMaster.map(element => {
62 | masterCountries.add(element['ID'])
63 | })
64 | const usedCountries = new Set()
65 | arr.map(element => {
66 | const extractedCountries = element[key].split(",").map(country => country.trim())
67 | extractedCountries.map(country => usedCountries.add(country))
68 | })
69 | return [...usedCountries].every((el) => {
70 | return [...masterCountries].includes(el)
71 | })
72 | }
73 |
74 | function _hasExactlyOneCountry(arr, key) {
75 | let result = true
76 | arr.map(element => {
77 | const extractedCountries = element[key].split(",").map(country => country.trim())
78 | if (extractedCountries.length != 1) result = false
79 | })
80 | return result
81 | }
82 |
83 | return {
84 | readExcelContent: function (file) {
85 | const result = {}
86 | let excel = undefined
87 | try {
88 | const buf = readFileSync(file);
89 | excel = read(buf);
90 | result.customer = utils.sheet_to_json(excel.Sheets['Customer'])
91 | result.solutionProcesses = utils.sheet_to_json(excel.Sheets['Solution Processes'])
92 | result.diagrams = utils.sheet_to_json(excel.Sheets['Diagrams'])
93 | result.accelerators = utils.sheet_to_json(excel.Sheets['Accelerators'])
94 | return result
95 | } catch (error) {
96 | throw new Error(error.message)
97 | }
98 | },
99 |
100 | validateExcelContent: function (excelContent) {
101 | try {
102 | if (!excelContent.customer.length || !excelContent.solutionProcesses.length || !excelContent.diagrams.length || !excelContent.accelerators.length) {
103 | throw new Error('Could not find needed sheets or content.')
104 | }
105 |
106 | if (excelContent.solutionProcesses.length < 1) {
107 | throw new Error('Could not find any Solution Processes in the Excel sheet.')
108 | }
109 |
110 | if (!_hasRequriedColumns(excelContent.customer[0], requiredColumnsCustomerSheet)) {
111 | throw new Error('Could not find needed columns in sheet "Customer".')
112 | }
113 |
114 | if (!_hasRequriedColumns(excelContent.solutionProcesses[0], requiredColumnsSolutionProcessSheet)) {
115 | throw new Error('Could not find needed columns in sheet "Solution Processes".')
116 | }
117 |
118 | if (!_hasRequriedColumns(excelContent.diagrams[0], requiredColumnsDiagramSheet)) {
119 | throw new Error('Could not find needed columns in sheet "Diagrams".')
120 | }
121 |
122 | if (!_hasRequriedColumns(excelContent.accelerators[0], requiredColumnsAccelerators)) {
123 | throw new Error('Could not find needed columns in sheet "Accelerators".')
124 | }
125 |
126 | if (_hasDuplicates(excelContent.solutionProcesses, 'External ID').length > 0) {
127 | throw new Error('Solution Processes has duplicates.')
128 | }
129 |
130 | if (!_isSubset(excelContent.solutionProcesses, 'External ID', excelContent.diagrams, 'Solution Process ID')) {
131 | throw new Error('Diagrams reference non-existing Solution Processes.')
132 | }
133 |
134 | if (!_isSubset(excelContent.solutionProcesses, 'External ID', excelContent.accelerators, 'Solution Process ID')) {
135 | throw new Error('Accelerators reference non-existing Solution Processes.')
136 | }
137 |
138 | if (!_hasValidCountries(excelContent.solutionProcesses, 'Localization')) {
139 | throw new Error('Solution Processes contains invalid Countries/Regions.')
140 | }
141 |
142 | if (!_hasValidCountries(excelContent.accelerators, 'Language')) {
143 | throw new Error('Accelerators contains invalid Languages.')
144 | }
145 |
146 | if (!_hasExactlyOneCountry(excelContent.accelerators, 'Language')) {
147 | throw new Error('Accelerators has malformed or too many languages. One Accelerator must have exactly one language.')
148 | }
149 |
150 | } catch (error) {
151 | throw new Error(error)
152 | }
153 | },
154 |
155 | getCustomerData: function (rawCustomerData) {
156 | const customerData = {
157 | customer: '',
158 | baseURL: '',
159 | authorizationURL: '',
160 | clientId: '',
161 | clientSecret: ''
162 | }
163 |
164 | rawCustomerData.map(element => {
165 | switch (element.Property) {
166 | case 'Customer':
167 | customerData.customer = element.Value
168 | break
169 | case 'SAP Cloud ALM Tenant URL':
170 | customerData.apiBaseUrl = element.Value
171 | break
172 | case 'SAP Cloud ALM Authorization URL':
173 | customerData.authenticationUrl = element.Value
174 | break
175 | case 'Client ID':
176 | customerData.clientId = element.Value
177 | break
178 | case 'Client Secret':
179 | customerData.clientSecret = element.Value
180 | break
181 | default:
182 | break
183 | }
184 | })
185 | return customerData
186 | },
187 |
188 | getBusinessProcesses: function (rawSolutionProcesses) {
189 | const businessProcessSet = new Set()
190 | rawSolutionProcesses.map(solutionProcess => {
191 | businessProcessSet.add(solutionProcess['Business Process'])
192 | })
193 | return [...businessProcessSet]
194 | },
195 |
196 | getSolutionProcesses: function (rawSolutionProcesses, businessProcesses) {
197 | const solutionProcesses = []
198 | rawSolutionProcesses.map(solutionProcess => {
199 | const description = solutionProcess['Description'].length >= 5000 ? solutionProcess['Description'].substring(0, 4999) : solutionProcess['Description']
200 | const sp = {
201 | name: `${solutionProcess['Name']} (${solutionProcess['External ID']})`,
202 | description: description,
203 | businessProcess: businessProcesses.find(businessProcess => {
204 | solutionProcess['Business Process'] === businessProcess['name']
205 | return businessProcess
206 | }),
207 | countries: solutionProcess['Localization'],
208 | externalId: solutionProcess['External ID'],
209 | }
210 | delete sp['businessProcess']['name']
211 | delete sp['businessProcess']['description']
212 | solutionProcesses.push(sp)
213 | })
214 |
215 | return solutionProcesses
216 | },
217 |
218 | getAccelerators: function (rawAccelerators, solutionProcesses) {
219 | const accelerators = []
220 | const groupedByProcessId = loadsh.groupBy(rawAccelerators, 'Solution Process ID')
221 | for (const process in groupedByProcessId) {
222 | const solutionProcessFound = solutionProcesses.find(solution_process => solution_process.externalId === process)
223 | const groupedByName = loadsh.groupBy(groupedByProcessId[process], 'Name')
224 | for (const name in groupedByName) {
225 | const accelerator = {
226 | name: name,
227 | countryUrl: [],
228 | solutionProcess: solutionProcessFound.id,
229 | }
230 | groupedByName[name].map((link) => {
231 | const url = {
232 | url: Path.join(link.Host, link.Path),
233 | country: link.Language
234 | }
235 | accelerator.countryUrl.push(url)
236 | })
237 | accelerators.push(accelerator)
238 | }
239 | }
240 | return accelerators
241 | },
242 |
243 | getDiagrams: function (rawDiagrams, type) {
244 | const __filename = fileURLToPath(import.meta.url);
245 | const __dirname = Path.dirname(__filename);
246 | const result = []
247 | const diagrams = rawDiagrams.filter(diagram => diagram['Type'] === type)
248 | diagrams.map(diagram => {
249 | let svgData = null
250 | let bpmnData = null
251 | try {
252 | if(diagram.BPMN){
253 | bpmnData = readFileSync(Path.join(__dirname, diagram.BPMN), 'utf-8')
254 | }
255 | if(diagram.SVG){
256 | svgData = readFileSync(Path.join(__dirname, diagram.SVG), 'utf-8')
257 | }
258 | result.push({
259 | parentId: diagram['Solution Process ID'],
260 | name: diagram['Diagram Name'],
261 | svg: svgData,
262 | bpmn: bpmnData
263 | })
264 | } catch (error) {
265 | throw new Error(error)
266 | }
267 | })
268 | return result
269 | }
270 | } // return
271 | }
272 |
--------------------------------------------------------------------------------
/applications/CustomProcessUpload/modules/CalmAdapter.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | /*******************************************************************************
4 | * CALM Adapter
5 | *******************************************************************************/
6 | import axios from 'axios'
7 | import oauth from 'axios-oauth-client'
8 | import tokenProvider from 'axios-token-interceptor'
9 | import axiosThrottle from 'axios-request-throttle'
10 |
11 | export default function CalmAdapter(secret) {
12 |
13 | /******************************************************************************
14 | * AXIOS Authenticate and Throttle
15 | */
16 |
17 | axiosThrottle.use(axios, { requestsPerSecond: 100 })
18 |
19 | const getClientCredentials = oauth.clientCredentials(
20 | axios.create({
21 | baseURL: secret.authenticationUrl,
22 | }),
23 | '/oauth/token',
24 | secret.clientId,
25 | secret.clientSecret
26 | )
27 |
28 | const instance = axios.create({
29 | baseURL: secret.apiBaseUrl,
30 | })
31 |
32 | const cache = tokenProvider.tokenCache(
33 | () => getClientCredentials().then(response => response),
34 | { getMaxAge: body => body.expires_in * 1000 }
35 | )
36 |
37 | instance.interceptors.request.use(tokenProvider({
38 | getToken: cache,
39 | headerFormatter: body => 'Bearer ' + body.access_token,
40 | }))
41 |
42 | /******************************************************************************
43 | * Private functions
44 | */
45 |
46 | async function _getSolutionProcessFlows(top = 100, skip = 0) {
47 | const url = `/api/calm-processauthoring/v1/solutionProcessFlows?$top=${top}&$skip=${skip}`
48 | try {
49 | const businessProcesses = await instance.get(url)
50 | return businessProcesses.data.value
51 | } catch (error) {
52 | throw new Error(error)
53 | }
54 | }
55 |
56 | async function _getbusinessProcesses(top = 100, skip = 0) {
57 | const url = `/api/calm-processauthoring/v1/businessProcesses?$top=${top}&$skip=${skip}`
58 | try {
59 | const businessProcesses = await instance.get(url)
60 | return businessProcesses.data.value
61 | } catch (error) {
62 | throw new Error(error)
63 | }
64 | }
65 |
66 |
67 | async function _getBusinessProcessCount() {
68 | const url = '/api/calm-processauthoring/v1/businessProcesses/$count'
69 | try {
70 | const response = await instance.get(url)
71 | return response.data
72 | } catch (error) {
73 | throw new Error(error)
74 | }
75 | }
76 |
77 | async function _getSolutionProcessFlowCount() {
78 | const url = '/api/calm-processauthoring/v1/solutionProcessFlows/$count'
79 | try {
80 | const response = await instance.get(url)
81 | return response.data
82 | } catch (error) {
83 | throw new Error(error)
84 | }
85 | }
86 |
87 | async function _createBusinessProcess(name, description = '') {
88 | const url = '/api/calm-processauthoring/v1/businessProcesses'
89 | if (!name)
90 | throw new Error({ code: '', message: 'BUSINESS_PROCESS_NAME_EMPTY' })
91 | const body = {
92 | name: name,
93 | description: description,
94 | }
95 |
96 | try {
97 | const response = await instance.post(url, body)
98 | return response.data
99 | } catch (error) {
100 | throw new Error(error.data.error)
101 | }
102 | }
103 |
104 | async function _findSolutionProcessByExternalId(externalId) {
105 | const urlByExternalId = `/api/calm-processauthoring/v1/solutionProcesses?externalId=${externalId}`
106 | try {
107 | const responseExternalId = await instance.get(urlByExternalId)
108 | if (responseExternalId.data.value.length > 0) {
109 | return responseExternalId.data.value
110 | }
111 | else {
112 | return false
113 | }
114 | } catch (error) {
115 | throw new error
116 | }
117 | }
118 |
119 | async function _createSolutionProcess(body) {
120 | const url = '/api/calm-processauthoring/v1/solutionProcesses'
121 | try {
122 | const response = await instance.post(url, body)
123 | return response.data
124 | } catch (error) {
125 | throw new Error(error)
126 | }
127 | }
128 |
129 | async function _patchSolutionProcess(solutionProcessId, body) {
130 | const url = `/api/calm-processauthoring/v1/solutionProcesses/${solutionProcessId}`
131 | try {
132 | const solutionProcess = await instance.get(url)
133 | if (solutionProcess.data.status === 'ACTIVE') {
134 | const createDraftUrl = `/api/calm-processauthoring/v1/createDraftSolutionProcess/${solutionProcessId}`
135 | const createdDraft = await instance.post(createDraftUrl, {}, { headers: { 'if-match': solutionProcess.headers.etag } })
136 | const draftUrl = `/api/calm-processauthoring/v1/solutionProcesses/${createdDraft.data.id}`
137 | const solutionProcessAfterDraft = await instance.get(draftUrl)
138 | const patchUrl = `/api/calm-processauthoring/v1/solutionProcesses/${solutionProcessAfterDraft.data.id}`
139 | const response = await instance.patch(patchUrl, body, { headers: { 'if-match': solutionProcessAfterDraft.headers.etag } })
140 | return response.data
141 | } else {
142 | const response = await instance.patch(url, body, { headers: { 'if-match': solutionProcess.headers.etag } })
143 | return response.data
144 | }
145 | } catch (error) {
146 | throw new Error(error)
147 | }
148 | }
149 |
150 | async function _createAccelerator(solutionProcessId, body) {
151 | const url = `/api/calm-processauthoring/v1/solutionProcesses/${solutionProcessId}/assets`
152 | try {
153 | const response = await instance.post(url, body)
154 | return response.data
155 | } catch (error) {
156 | throw new Error(error)
157 | }
158 | }
159 |
160 | async function _patchSolutionProcessValueFlowDiagram(solutionProcessId, body) {
161 | const readUrl = `/api/calm-processauthoring/v1/solutionProcesses/${solutionProcessId}/solutionValueFlowDiagram`
162 | try {
163 | const svfd = await instance.get(readUrl)
164 | const patchUrl = `/api/calm-processauthoring/v1/solutionValueFlowDiagrams/${svfd.data.id}`
165 | const response = await instance.patch(patchUrl, body, { headers: { 'if-match': svfd.headers.etag } })
166 | return response.data
167 | } catch (error) {
168 | throw new Error(error)
169 | }
170 | }
171 |
172 | async function _createSolutionProcessFlowSVGDiagram(solutionProcessFlowId, body) {
173 | const url = `/api/calm-processauthoring/v1/solutionProcessFlows/${solutionProcessFlowId}/solutionProcessFlowDiagrams`
174 | try {
175 | const response = await instance.post(url, body)
176 | return response.data
177 | } catch (error) {
178 | throw new Error(error)
179 | }
180 | }
181 |
182 | async function _createSolutionProcessFlowBPMNDiagram(solutionProcessFlowId, body) {
183 | const url = `/api/calm-processauthoring/v1/solutionProcessFlows/${solutionProcessFlowId}/solutionProcessFlowDiagrams/bpmn`
184 | try {
185 | const response = await instance.post(url, body)
186 | return response.data
187 | } catch (error) {
188 | throw new Error(error)
189 | }
190 | }
191 |
192 | async function _activateSolutionProcess(solutionProcessId) {
193 | const readUrl = `/api/calm-processauthoring/v1/solutionProcesses/${solutionProcessId}`
194 | const postUrl = `/api/calm-processauthoring/v1/publishSolutionProcess/${solutionProcessId}`
195 | try {
196 | const preRead = await instance.get(readUrl)
197 | const response = await instance.post(postUrl, {}, { headers: { 'if-match': preRead.headers.etag } })
198 | return response.data
199 | } catch (error) {
200 | throw new Error(error)
201 | }
202 | }
203 |
204 | function _calculateIterations(entries, limit) {
205 | let iterations = Math.floor(entries / limit)
206 | if (entries % limit > 0) iterations++
207 | return iterations
208 | }
209 |
210 | /******************************************************************************
211 | * Public functions
212 | */
213 | return {
214 |
215 | postBusinessProcesses: async function (neededBusinessProcesses, top = 100) {
216 | try {
217 | const businessProcessCount = await _getBusinessProcessCount()
218 | const iterations = _calculateIterations(businessProcessCount, top)
219 |
220 | const iterationResults = []
221 | for (let i = 1; i <= iterations; i++) {
222 | iterationResults.push({ top: top, skip: (i - 1) * top })
223 | }
224 |
225 | const resultArraysPromise = iterationResults.map(businessProcesses => {
226 | return _getbusinessProcesses(businessProcesses['top'], businessProcesses['skip'])
227 | })
228 | const resultArraysResult = await Promise.all(resultArraysPromise)
229 |
230 | let existingBusinessProcesses = []
231 | resultArraysResult.map((array) => {
232 | existingBusinessProcesses = existingBusinessProcesses.concat(array)
233 | })
234 |
235 | const businessProcesses = neededBusinessProcesses.map(neededBusinessProcess => {
236 | let matchingBusinessProcess = existingBusinessProcesses.find(existingBusinessProcess => existingBusinessProcess.name === neededBusinessProcess)
237 | if (!matchingBusinessProcess) matchingBusinessProcess = neededBusinessProcess
238 |
239 | return matchingBusinessProcess
240 | })
241 |
242 | const complementedPromise = businessProcesses.filter((businessProcess) => businessProcess.id === undefined).map(async (businessProcess) => {
243 | let complementedBusinessProcess = await _createBusinessProcess(businessProcess)
244 |
245 | return complementedBusinessProcess
246 | })
247 | const complementedPromiseResult = await Promise.all(complementedPromise)
248 |
249 | complementedPromiseResult.map(created => {
250 | businessProcesses[businessProcesses.indexOf(created['name'])] = created
251 | })
252 |
253 | return businessProcesses
254 | } catch (error) {
255 | throw new Error(error)
256 | }
257 | },
258 |
259 | postSolutionProcesses: async function (solutionProcesses) {
260 | try {
261 | const solutionProcessesPromise = solutionProcesses.map(async solutionProcess => {
262 | const result = await _findSolutionProcessByExternalId(solutionProcess.externalId)
263 | if (!result) {
264 | solutionProcess._ACTION = 'CREATE'
265 | } else {
266 | if (result.length <= 2) {
267 | result.find(process => {
268 | if (process.status === 'DRAFT') {
269 | solutionProcess.id = process.id
270 | solutionProcess._ACTION = 'PATCH'
271 | }
272 | else {
273 | solutionProcess.id = process.id
274 | solutionProcess._ACTION = 'PATCH'
275 | }
276 | })
277 | } else {
278 | throw new error('External ID doesn\'t seem unique in your SAP Cloud ALM tenant.')
279 | }
280 | }
281 | return solutionProcess
282 | })
283 | const SolutionProcessesPromiseResult = await Promise.all(solutionProcessesPromise)
284 |
285 | const solutionProcessesCreate = SolutionProcessesPromiseResult.filter(solutionProcess => solutionProcess._ACTION === 'CREATE')
286 | const solutionProcessesCreatePromise = solutionProcessesCreate.map(solutionProcess => {
287 | delete solutionProcess._ACTION
288 | const result = _createSolutionProcess(solutionProcess)
289 | return result
290 | })
291 | const solutionProcessesCreatePromiseResult = await Promise.all(solutionProcessesCreatePromise)
292 |
293 | const solutionProcessesPatch = SolutionProcessesPromiseResult.filter(solutionProcess => solutionProcess._ACTION === 'PATCH')
294 | const solutionProcessesPatchPromise = solutionProcessesPatch.map(solutionProcess => {
295 | let body = JSON.parse(JSON.stringify(solutionProcess))
296 | delete body._ACTION
297 | delete body.id
298 | const result = _patchSolutionProcess(solutionProcess.id, body)
299 | return result
300 | })
301 | const solutionProcessesPatchPromiseResult = await Promise.all(solutionProcessesPatchPromise)
302 |
303 | const result = [...solutionProcessesCreatePromiseResult, ...solutionProcessesPatchPromiseResult]
304 | return result
305 | } catch (error) {
306 | throw new Error(error);
307 | }
308 | },
309 |
310 | postAccelerators: async function (accelerators) {
311 | const acceleratorPromise = accelerators.map(async accelerator => {
312 | try {
313 | const body = JSON.parse(JSON.stringify(accelerator))
314 | delete body.solutionProcess
315 | const asset = await _createAccelerator(accelerator.solutionProcess, body)
316 | return asset
317 | } catch (error) {
318 | throw new Error(error);
319 | }
320 | });
321 | const acceleratorPromiseResult = await Promise.all(acceleratorPromise)
322 | return acceleratorPromiseResult
323 | },
324 |
325 | findSolutionProcessFlowsBySolutionProcesses: async function (solutionProcesses, top = 100) {
326 | try {
327 | const solutionProcessFlowCount = await _getSolutionProcessFlowCount()
328 | const iterations = _calculateIterations(solutionProcessFlowCount, top)
329 |
330 | const iterationsArray = []
331 | for (let i = 1; i <= iterations; i++) {
332 | iterationsArray.push({ top: top, skip: (i - 1) * top })
333 | }
334 |
335 | const solutionProcessFlowPromise = iterationsArray.map(iteration => {
336 | return _getSolutionProcessFlows(iteration.top, iteration.skip)
337 | })
338 |
339 | const solutionProcessFlowPromiseResult = await Promise.all(solutionProcessFlowPromise)
340 | let mergedSolutionProcessFlows = []
341 | solutionProcessFlowPromiseResult.map(array => mergedSolutionProcessFlows.push(...array))
342 |
343 | solutionProcesses.map(solutionProcess => {
344 | mergedSolutionProcessFlows.find(solutionProcessFlow => {
345 | if (solutionProcess.id === solutionProcessFlow.solutionProcess.id) {
346 | solutionProcess.solutionProcessFlow = solutionProcessFlow
347 | return solutionProcess
348 | }
349 | })
350 | })
351 | return solutionProcesses
352 | } catch (error) {
353 | throw new Error(error);
354 | }
355 | },
356 |
357 | postSolutionValueFlows: async function (solutionValueFlowDiagrams, solutionProcesses) {
358 | try {
359 | const solutionValueFlowDiagramPromise = solutionValueFlowDiagrams.map(async diagram => {
360 | const solutionProcessFound = solutionProcesses.find(solutionProcess => solutionProcess.externalId === diagram.parentId)
361 | delete diagram.parentId
362 | const result = await _patchSolutionProcessValueFlowDiagram(solutionProcessFound.id, diagram)
363 | return result
364 | })
365 | const solutionValueFlowDiagramPromiseResult = Promise.all(solutionValueFlowDiagramPromise)
366 | return solutionValueFlowDiagramPromiseResult
367 | } catch (error) {
368 | throw new Error(error);
369 | }
370 | },
371 |
372 | postSolutionProcessFlowDiagrams: async function (solutionProcessFlowDiagrams, solutionProcessFlows) {
373 | const solutionProcessFlowDiagramsPromise = solutionProcessFlowDiagrams.map(async diagram => {
374 | const solutionProcess = solutionProcessFlows.find(solutionProcessFlow => diagram.parentId === solutionProcessFlow.externalId)
375 | delete diagram.parentId
376 | let solutionProcessFlowDiagram = null
377 | if(diagram.bpmn){
378 | delete diagram.svg
379 | solutionProcessFlowDiagram = await _createSolutionProcessFlowBPMNDiagram(solutionProcess.solutionProcessFlow.id, diagram)
380 | }else{
381 | delete diagram.bpmn
382 | solutionProcessFlowDiagram = await _createSolutionProcessFlowSVGDiagram(solutionProcess.solutionProcessFlow.id, diagram)
383 | }
384 | return solutionProcessFlowDiagram
385 | })
386 | const solutionProcessFlowDiagramsPromiseResult = Promise.all(solutionProcessFlowDiagramsPromise)
387 | return solutionProcessFlowDiagramsPromiseResult
388 | },
389 |
390 | activateSolutionProcesses: async function (solutionProcesses) {
391 | try {
392 | const solutionProcessesActivationPromise = solutionProcesses.map(async solutionProcess => {
393 | const activation = await _activateSolutionProcess(solutionProcess.id)
394 | return activation
395 | })
396 | const solutionProcessActivationPromiseResult = Promise.all(solutionProcessesActivationPromise)
397 | return solutionProcessActivationPromiseResult
398 | } catch (error) {
399 | throw new error
400 | }
401 | },
402 | } // return
403 | }
--------------------------------------------------------------------------------
/applications/CustomProcessUpload/modules/UserInterface.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | /*******************************************************************************
4 | * User Interface Module
5 | *******************************************************************************/
6 | import clear from 'clear'
7 | import chalk from 'chalk'
8 | import figlet from 'figlet'
9 | import inquirer from 'inquirer'
10 | import inquirerFileTreeSelection from 'inquirer-file-tree-selection-prompt'
11 |
12 | export default function UserInterface() {
13 |
14 | return {
15 | welcomeScreen: function() {
16 | clear()
17 | console.log()
18 | console.log(
19 | chalk.yellow(
20 | figlet.textSync('Process Upload', {
21 | horizontalLayout: 'full',
22 | })
23 | )
24 | )
25 | console.log(
26 | chalk.yellow(' Upload Excel to SAP Cloud ALM')
27 | )
28 | console.log()
29 | },
30 |
31 | getExcelFileDialog: async function () {
32 | inquirer.registerPrompt('file-tree-selection', inquirerFileTreeSelection)
33 | let confirmed = false
34 | let file = undefined
35 |
36 | do {
37 | const answer = await inquirer.prompt([
38 | {
39 | type: 'file-tree-selection',
40 | root: './input',
41 | name: 'file',
42 | },
43 | ])
44 |
45 | const confirmation = await inquirer.prompt([
46 | {
47 | type: 'confirm',
48 | name: 'confirmed',
49 | message: `Are you sure you want to import this file? ${answer['file']}`,
50 | default: false,
51 | },
52 | ])
53 |
54 | if (confirmation['confirmed']) {
55 | confirmed = true
56 | file = answer['file']
57 | }
58 | } while (!confirmed)
59 | return file
60 | }
61 | }
62 | }
--------------------------------------------------------------------------------
/applications/CustomProcessUpload/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "customprocessesupload",
3 | "version": "1.0.0",
4 | "description": "Process Upload for Excel ",
5 | "type": "module",
6 | "main": "index.js",
7 | "scripts": {
8 | "start": "node index.js",
9 | "test": "echo \"Error: no test specified\" && exit 1"
10 | },
11 | "author": "",
12 | "license": "Apache-2.0",
13 | "licenses": [
14 | {
15 | "type": "Apache-2.0",
16 | "url": "http://www.apache.org/licenses/LICENSE-2.0"
17 | }
18 | ],
19 | "dependencies": {
20 | "axios": "^1.4.0",
21 | "axios-oauth-client": "^2.0.2",
22 | "axios-request-throttle": "^1.0.0",
23 | "axios-token-interceptor": "^0.2.0",
24 | "chalk": "^5.2.0",
25 | "clear": "^0.1.0",
26 | "figlet": "^1.6.0",
27 | "inquirer": "^9.2.7",
28 | "inquirer-file-tree-selection-prompt": "^2.0.5",
29 | "loading-indicator": "^2.0.0",
30 | "loadsh": "^0.0.4",
31 | "path": "^0.12.7",
32 | "xlsx": "https://cdn.sheetjs.com/xlsx-0.20.2/xlsx-0.20.2.tgz"
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/applications/Readme.md:
--------------------------------------------------------------------------------
1 | ## Sample Applications for the SAP Cloud ALM APIs
2 |
3 |
4 | In the subdirectories here you find some sample applications for the SAP Cloud ALM APIs
5 |
6 | ### Kanban Board
7 | Simple Kanban style board using JQuery, Bootstrap and SortableJS on frontend with a server side application that manages the OAuth2 authorization against the customers authentication service. The server side application is built in NodeJS and deployable to the CloudFoundry environment on the customer SAP BTP account.
8 |
9 |
10 | ### calm-api-consumer-java-sample
11 | SAPUI5 custom application developed on the SAP BTP platform with the SAP Cloud ALM Rest APIs for Projects and Tasks.
12 |
13 |
14 | ### calm-api-consumer-ui5-sample
15 | Application developed with SAPUI5 to serve as example of what it is possible to achieve with the SAP Cloud ALM Rest APIs.
16 | In this app, we connect to the Rest APIs "project" and "task" of SAP CALM APIs to display the tasks of a project in an interactive kanban.
17 |
18 |
19 | ### Scoping API
20 | Simple console app that show cases how you can interact with SAP Cloud ALM Scopes. The example demos how you can manage scopes, add/remove Solution Scenarios, and how you can set processes in/out of scope.
21 |
22 |
23 | ### Content Upload
24 | Simple console app that show cases how you can upload customer or partner content. The example demos how you can use Excel to define customer or partner practices, and how the Customer Processes API can be used to upload the content.
--------------------------------------------------------------------------------
/applications/ScopingAPI/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "scopingapi",
3 | "version": "1.0.0",
4 | "description": "This is an example to illustrate how you can use SAP Cloud ALM Scoping API. The code is not production ready and you use it on your own risk. :-) Even though the Scoping API is actually built as batch oriented REST API, this example implements UI interactions. Overall the API is not intended to build UIs but rather synchronization scenarios.",
5 | "main": "index.js",
6 | "type": "module",
7 | "scripts": {
8 | "test": "echo \"Error: no test specified\" && exit 1"
9 | },
10 | "repository": {
11 | "type": "git",
12 | "url": "https://github.tools.sap/D033027/ScopingAPI.git"
13 | },
14 | "dependencies": {
15 | "axios": "^0.27.2",
16 | "axios-oauth-client": "^1.5.0",
17 | "axios-token-interceptor": "^0.2.0",
18 | "chalk": "^5.0.1",
19 | "clear": "^0.1.0",
20 | "figlet": "^1.5.2",
21 | "inquirer": "^9.1.1",
22 | "inquirer-search-checkbox": "^1.0.0",
23 | "winston": "^3.8.2"
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/applications/ScopingAPI/readme.MD:
--------------------------------------------------------------------------------
1 | # SAP Cloud ALM Scoping API Sample
2 |
3 | This is an example to illustrate how you can use SAP Cloud ALM Scoping API. The code is not production ready and you use it on your own risk. :-)
4 | Even though the Scoping API is actually built as batch oriented REST API, this example implements UI interactions. Overall the API is not intended to build UIs but rather synchronization scenarios.
5 |
6 | You can find all the details about the SAP Cloud ALM Scoping API on the [SAP API Business Hub](https://api.sap.com/api/CALM_PM/overview).
7 |
8 | ## Demo
9 |
10 | Please find a recording [here](recording/2022-09-14_11-49-53.mp4).
11 | ## Install libraries
12 |
13 | [Sad but true.](https://www.monkeyuser.com/2017/npm-delivery/)
14 |
15 | Navigate to ScopingAPI folder, and then
16 |
17 | > mkdir log && npm install
18 |
19 | ## Maintain credentials
20 |
21 | You need to have a "secret.js"-file in the form as follows with valid data.
22 |
23 | ```
24 | export default {
25 | //no trailing "/"
26 | "apiBaseUrl": "",
27 | "authenticationUrl": "",
28 | "clientId": "",
29 | "clientSecret": "",
30 | }
31 | ```
32 | Please make sure you have sufficient authorizations to call the APIs.
33 | The required authorisation scopes, to run the sample, are:
34 |
35 | * calm-api.processmanagement.read: to read process management entities.
36 | * calm-api.processmanagement.write: to write process management entities.
37 | * calm-api.processmanagement.delete: to delete process management entities.
38 | * calm-api.projects.read: to view projects.
39 |
40 |
41 | You can use the [service.json](service.json) example to generate keys in the BPT Cockpit. To learn how generate the needed client id and secret please check this [blog](https://blogs.sap.com/2021/08/12/sap-cloud-alm-extend-with-api-get-started-with-sap-cloud-alm-api/).
42 |
43 | ## Run the script
44 |
45 | > node index.js
46 |
47 | Please check the logs in the log directory to get insights in service timings.
48 |
49 | Have fun.
--------------------------------------------------------------------------------
/applications/ScopingAPI/recording/2022-09-14_11-49-53.mp4:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SAP-samples/cloud-alm-api-examples/0503338e01aad752dcf1f9d7dee8660e7a1e275d/applications/ScopingAPI/recording/2022-09-14_11-49-53.mp4
--------------------------------------------------------------------------------
/applications/ScopingAPI/secret.js.sample:
--------------------------------------------------------------------------------
1 | export default {
2 | //no trailing "/"
3 | "apiBaseUrl": "",
4 | "authenticationUrl": "",
5 | "clientId": "",
6 | "clientSecret": "",
7 | }
--------------------------------------------------------------------------------
/applications/ScopingAPI/service.json:
--------------------------------------------------------------------------------
1 | {
2 | "xs-security": {
3 | "xsappname": "sap_cloud_alm_api",
4 | "authorities": [
5 | "$XSMASTERAPPNAME.calm-api.projects.write",
6 | "$XSMASTERAPPNAME.calm-api.projects.read",
7 | "$XSMASTERAPPNAME.calm-api.tasks.write",
8 | "$XSMASTERAPPNAME.calm-api.tasks.read",
9 | "$XSMASTERAPPNAME.calm-api.processmanagement.read",
10 | "$XSMASTERAPPNAME.calm-api.processmanagement.write"
11 | ]
12 | }
13 | }
--------------------------------------------------------------------------------
/applications/calm-api-consumer-java-sample/README.md:
--------------------------------------------------------------------------------
1 | # SAP Cloud ALM Public API Reference Consumer Application
2 |
3 |
4 |
5 | SAP Cloud ALM Public API reference application is built to showcase the method of developing a single code-line multi cloud application consuming public APIs of SAP Cloud ALM on SAP Business Technology Platform (SAP BTP) Cloud Foundry Environment.
6 |
7 | ## Description
8 | SAP Cloud ALM offers public APIs for Task Management and Project Management and many more functionalities of SAP Cloud ALM
9 | #### Features of the Application
10 |
11 | • The sample application provides RESTful endpoints to read projects from SAP Cloud ALM.
12 |
13 | • It calls SAP Cloud ALM's public API endpoints to perform the above operations on the provisioned customer tenant of SAP Clod ALM SaaS Application.
14 |
15 | ## Architecture
16 |
17 | 
18 |
19 | A single REST controller accepts the request (GET).
20 |
21 |
22 | ## Requirements
23 | - [Java 8](https://www.oracle.com/technetwork/java/javase/downloads/jdk8-downloads-2133151.html)
24 | - [Apache Maven 3.3+](https://maven.apache.org/download.cgi)
25 | - [Cloud Foundry CLI](https://github.com/cloudfoundry/cli#downloads)
26 | - SAP Business Technology Platform Global account
27 | - [Provision SAP Cloud ALM API Service](https://discovery-center.cloud.sap/#/serviceCatalog/object-store-service).
28 | - A Cloud Foundry user with SpaceDeveloper role to deploy the application
29 |
30 | ## Download and Installation
31 |
32 | #### Build the Application
33 | - [Clone](https://help.github.com/articles/cloning-a-repository/) the application `cloudalm-api-examples` to your system
34 |
35 | Clone URL : `https://github.com/SAP-samples/cloud-alm-api-examples.git`
36 | - Navigate to the root folder of the application and run the below maven command to build the application:
37 | ```
38 | mvn clean install
39 | ```
40 |
41 | #### Prerequisites
42 | - SAP Business Technology Platform with CloudFoundry Runtime enabled
43 | - Subscription to SAP Cloud ALM
44 | #### Deploy the Application on Cloud Foundry
45 |
46 | 1. Logon to the Cloud Foundry environment using the following commands on the command prompt:
47 | ```
48 | cf api
49 | cf login
50 | ```
51 | `api` - [URL of the Cloud Foundry landscape](https://help.sap.com/viewer/65de2977205c403bbc107264b8eccf4b/Cloud/en-US/350356d1dc314d3199dca15bd2ab9b0e.html) that you are trying to connect to.
52 |
53 | Enter username, password, org and space when prompted to. [Please click here for more information](https://help.sap.com/viewer/65de2977205c403bbc107264b8eccf4b/Cloud/en-US/75125ef1e60e490e91eb58fe48c0f9e7.html#loio4ef907afb1254e8286882a2bdef0edf4).
54 |
55 |
56 |
57 | 2. Create the Cloud Foundry SAP Cloud ALM API Service Instance
58 |
59 | - To run the application create a service by executing the below command:
60 |
61 | `cf create-service sap-cloud-alm-api standard calm-public-api-service -c cloud-alm-api-scopes.json
62 |
63 | Note:`A sample scopes file is provided [here](https://github.com/SAP-samples/cloud-alm-api-examples/blob/main/applications/calm-api-consumer-java-sample/cloud-alm-api-scopes.json)
64 |
65 |
66 |
67 | 3. Edit manifest.yml file. Replace the `` placeholder with any unique string. You can use your *User ID* or some unique ID so that the host name is unique in the CF landscape.
68 | ~~~
69 |
70 | ---
71 | applications:
72 | - name: calm-public-api-consumer-sample-svc
73 | ------------------------------------------
74 | | host: -cloud-alm-api-sample-svc |
75 | ------------------------------------------
76 | memory: 2G
77 | buildpack: https://github.com/cloudfoundry/java-buildpack.git
78 | path: target/cloudalm-api-sample-1.0.0.jar
79 | services:
80 | - calm-public-api-service
81 |
82 | ---
83 |
84 | ~~~
85 |
86 | 4. To deploy the application, navigate to the root of the application and execute the below command:
87 | ```
88 | cf push
89 | ```
90 |
91 | #### Test the Application
92 |
93 | [Postman Client](https://www.getpostman.com/apps) can be used to test / access the REST API endpoints.
94 |
95 | Replace the `` placeholder in the below steps with the URL of the application you deployed.
96 |
97 |
98 |
99 |
100 | ##### List all the tasks
101 |
102 | GET
103 |
104 | To get the list of all projects hit the below endpoint url.
105 |
106 | EndPoint URL : `https:///cloud-alm/projects`
107 |
108 | Content-Type : `application/json`
109 |
110 | A successful upload operation gives the following response :
111 |
112 | Status: 200
113 |
114 | Response Body:
115 | ~~~
116 |
117 | ~~~
118 |
119 | ## How to obtain support
120 |
121 | In case you find a bug, or you need additional support, please open an issue here in GitHub.
122 |
123 | ## Known Issues
124 |
125 | ## License
126 |
127 | Copyright (c) 2020 SAP SE or an SAP affiliate company. All rights reserved. This project is licensed under the Apache Software License, version 2.0 except as noted otherwise in the [LICENSE](LICENSES/Apache-2.0.txt)file.
128 |
--------------------------------------------------------------------------------
/applications/calm-api-consumer-java-sample/cloud-alm-api-scopes.json:
--------------------------------------------------------------------------------
1 | {
2 | "xs-security": {
3 | "xsappname": "sapCloudALM_Demo",
4 | "authorities": [
5 | "$XSMASTERAPPNAME.calm-api.projects.read",
6 | "$XSMASTERAPPNAME.calm-api.tasks.read",
7 | "$XSMASTERAPPNAME.calm-api.projects.write",
8 | "$XSMASTERAPPNAME.calm-api.tasks.write"
9 | ]
10 | }
11 | }
--------------------------------------------------------------------------------
/applications/calm-api-consumer-java-sample/documents/cloud-alm-api-sample-architecture.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SAP-samples/cloud-alm-api-examples/0503338e01aad752dcf1f9d7dee8660e7a1e275d/applications/calm-api-consumer-java-sample/documents/cloud-alm-api-sample-architecture.jpg
--------------------------------------------------------------------------------
/applications/calm-api-consumer-java-sample/manifest.yml:
--------------------------------------------------------------------------------
1 | ---
2 | applications:
3 | - name: calm-public-api-consumer-sample-svc
4 | host: calm-public-api-consumer-sample-svc
5 | memory: 2G
6 | buildpack: https://github.com/cloudfoundry/java-buildpack.git
7 | path: target/cloudalm-api-sample-1.0.0.jar
8 | services:
9 | - calm-public-api-service
10 |
--------------------------------------------------------------------------------
/applications/calm-api-consumer-java-sample/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 | 4.0.0
5 |
6 | com.sap.refapps.cloudalm
7 | cloudalm-api-sample
8 | 1.0.0
9 | jar
10 |
11 | cloudalm-api-sample
12 | SAP Cloud ALM API reference consumer application
13 |
14 |
15 | org.springframework.boot
16 | spring-boot-starter-parent
17 | 2.2.4.RELEASE
18 |
19 |
20 |
21 |
22 | UTF-8
23 | UTF-8
24 | 1.8
25 | 2.3.0
26 | 3.1.1
27 |
28 |
29 |
30 |
31 |
32 |
33 | org.springframework.boot
34 | spring-boot-starter-web
35 |
36 |
37 | org.springframework.boot
38 | spring-boot-configuration-processor
39 | true
40 |
41 |
42 | org.springframework.boot
43 | spring-boot-starter-test
44 |
45 |
46 | com.fasterxml.jackson.core
47 | jackson-databind
48 |
49 |
50 |
51 |
52 |
53 | org.springframework.boot
54 | spring-boot-maven-plugin
55 |
56 |
57 |
58 |
59 |
--------------------------------------------------------------------------------
/applications/calm-api-consumer-java-sample/src/main/java/com/sap/refapps/cloudalm/CloudALMAPIAccessController.java:
--------------------------------------------------------------------------------
1 | package com.sap.refapps.cloudalm;
2 |
3 | import com.fasterxml.jackson.core.JsonProcessingException;
4 | import com.fasterxml.jackson.databind.ObjectMapper;
5 | import org.springframework.beans.factory.annotation.Autowired;
6 | import org.springframework.beans.factory.annotation.Value;
7 | import org.springframework.boot.web.client.RestTemplateBuilder;
8 | import org.springframework.context.annotation.Bean;
9 | import org.springframework.core.env.Environment;
10 | import org.springframework.http.*;
11 | import org.springframework.web.bind.annotation.RestController;
12 | import org.springframework.web.bind.annotation.RequestMapping;
13 | import org.springframework.web.client.RestTemplate;
14 |
15 | @RestController
16 | public class CloudALMAPIAccessController {
17 |
18 | @Autowired
19 | private Environment env;
20 | @Value("${sap.cloudalm.public.api.url.eu10}")
21 | private String cloudAlmApiUrl;
22 | @RequestMapping("/cloud-alm/projects")
23 | public String getCloudALMProjects(RestTemplate restTemplate) throws JsonProcessingException {
24 | JWTTokenProvider jwtTokenProvider = new JWTTokenProvider(restTemplate);
25 | String accessToken = jwtTokenProvider.generateAccessToken();
26 |
27 | if (accessToken != null) {
28 | System.out.println("Request Successful");
29 | HttpHeaders headers = new HttpHeaders();
30 | headers.setContentType(MediaType.APPLICATION_JSON);
31 | headers.set("Authorization", "Bearer "+accessToken);
32 |
33 | HttpEntity requestEntity = new HttpEntity(null,headers);
34 | ResponseEntity responseEntity = restTemplate.exchange(cloudAlmApiUrl, HttpMethod.GET, requestEntity, ProjectDto[].class);
35 | ObjectMapper mapper = new ObjectMapper();
36 | String jsonString = mapper.writeValueAsString(responseEntity);
37 | System.out.println(jsonString);
38 | return jsonString;
39 | } else {
40 | System.out.println("Request Failed");
41 | }
42 | return "Sorry could not fetch Access Token!";
43 | }
44 | @Bean
45 | public RestTemplate restTemplate(RestTemplateBuilder builder) {
46 | return builder.build();
47 | }
48 |
49 | }
50 |
--------------------------------------------------------------------------------
/applications/calm-api-consumer-java-sample/src/main/java/com/sap/refapps/cloudalm/ConsumingApplication.java:
--------------------------------------------------------------------------------
1 | package com.sap.refapps.cloudalm;
2 |
3 |
4 | import com.fasterxml.jackson.databind.ObjectMapper;
5 | import org.springframework.boot.CommandLineRunner;
6 | import org.springframework.boot.SpringApplication;
7 | import org.springframework.boot.autoconfigure.SpringBootApplication;
8 | import org.springframework.boot.web.client.RestTemplateBuilder;
9 | import org.springframework.context.annotation.Bean;
10 | import org.springframework.http.*;
11 | import org.springframework.web.client.RestTemplate;
12 |
13 | @SpringBootApplication
14 | public class ConsumingApplication {
15 |
16 |
17 | public static void main(String[] args) {
18 | SpringApplication.run(ConsumingApplication.class, args);
19 | }
20 |
21 |
22 |
23 | }
24 |
--------------------------------------------------------------------------------
/applications/calm-api-consumer-java-sample/src/main/java/com/sap/refapps/cloudalm/EnvironmentUtil.java:
--------------------------------------------------------------------------------
1 | package com.sap.refapps.cloudalm;
2 |
3 |
4 | import java.io.IOException;
5 | import java.util.Optional;
6 |
7 | import org.slf4j.Logger;
8 | import org.slf4j.LoggerFactory;
9 |
10 | import com.fasterxml.jackson.databind.JsonNode;
11 | import com.fasterxml.jackson.databind.ObjectMapper;
12 |
13 | public class EnvironmentUtil {
14 |
15 | private static final Logger logger = LoggerFactory.getLogger(EnvironmentUtil.class);
16 |
17 | private static String activeProfile;
18 |
19 | private static final String VCAP_SERVICE = "VCAP_SERVICES";
20 | private static final String SAPCLOUDALMAPIs = "SAPCloudALMAPIs";
21 | private static final String UAA = "uaa";
22 | private static final String URL = "url";
23 | private static final String CLIENT_ID = "clientid";
24 | private static final String CLIENT_SECRET = "clientsecret";
25 | private static final String CREDENTIALS = "credentials";
26 |
27 |
28 |
29 | /**
30 | * This method is used to parse the service plan name from VCAP_SERVICES
31 | *
32 | * @return service plan name
33 | */
34 | public static VcapServicesDto getVcapService() {
35 | Optional xsuaaUrl = Optional.empty();
36 | Optional clientID = Optional.empty();
37 | Optional clientSecret = Optional.empty();
38 | VcapServicesDto vcapServicesDto = new VcapServicesDto();
39 |
40 | final String jsonString = System.getenv(VCAP_SERVICE);
41 | if (jsonString != null) {
42 | System.out.println(jsonString);
43 | try {
44 | ObjectMapper mapper = new ObjectMapper();
45 | JsonNode root = mapper.readTree(jsonString);
46 | JsonNode credentialsNode = root.path(SAPCLOUDALMAPIs).path(0).path(CREDENTIALS);
47 |
48 |
49 | xsuaaUrl = Optional.of(credentialsNode.path(UAA).path(URL).asText());
50 | vcapServicesDto.setXsuaaURL(xsuaaUrl.get());
51 | clientID = Optional.of(credentialsNode.path(UAA).path(CLIENT_ID).asText());
52 | vcapServicesDto.setClientId(clientID.get());
53 | clientSecret = Optional.of(credentialsNode.path(UAA).path(CLIENT_SECRET).asText());
54 | vcapServicesDto.setClientSecret(clientSecret.get());
55 |
56 |
57 | } catch (IOException e) {
58 | logger.error("Exception occurred: " + e);
59 | }
60 | }
61 | return vcapServicesDto;
62 | }
63 |
64 |
65 | }
66 |
--------------------------------------------------------------------------------
/applications/calm-api-consumer-java-sample/src/main/java/com/sap/refapps/cloudalm/JWTTokenProvider.java:
--------------------------------------------------------------------------------
1 | package com.sap.refapps.cloudalm;
2 |
3 |
4 | import org.springframework.beans.factory.annotation.Autowired;
5 | import org.springframework.beans.factory.annotation.Value;
6 | import org.springframework.http.ResponseEntity;
7 | import org.springframework.http.client.support.BasicAuthorizationInterceptor;
8 | import org.springframework.web.client.RestTemplate;
9 |
10 |
11 | public class JWTTokenProvider {
12 | private final String xsuaaUrl;
13 |
14 |
15 | private static final String RETRIEVE_JWT_TOKEN_PATH = "/oauth/token?grant_type=client_credentials&response_type=token";
16 |
17 |
18 | private RestTemplate restTemplate;
19 |
20 | @Autowired
21 | public JWTTokenProvider(RestTemplate restTemplate) {
22 | VcapServicesDto vcapServicesDto = EnvironmentUtil.getVcapService();
23 | this.xsuaaUrl = vcapServicesDto.getXsuaaURL();
24 | this.restTemplate = restTemplate;
25 | setupRestClient(vcapServicesDto.getClientId(), vcapServicesDto.getClientSecret());
26 | }
27 |
28 | private void setupRestClient(String uaaClientId, String uaaClientSecret) {
29 | restTemplate.getInterceptors()
30 | .add(new BasicAuthorizationInterceptor(uaaClientId, uaaClientSecret));
31 | }
32 |
33 |
34 | public String generateAccessToken() {
35 | ResponseEntity jwtTokenResponseEntity = restTemplate
36 | .getForEntity(xsuaaUrl + RETRIEVE_JWT_TOKEN_PATH, JwtTokenDto.class);
37 |
38 | JwtTokenDto retrievedToken = jwtTokenResponseEntity.getBody();
39 |
40 | return retrievedToken.getAccessToken();
41 | }
42 | }
--------------------------------------------------------------------------------
/applications/calm-api-consumer-java-sample/src/main/java/com/sap/refapps/cloudalm/JWTTokenResponse.java:
--------------------------------------------------------------------------------
1 | package com.sap.refapps.cloudalm;
2 |
3 | public class JWTTokenResponse {
4 | public void setAccess_token(String access_token) {
5 | this.access_token = access_token;
6 | }
7 |
8 | public String getAccess_token() {
9 | return access_token;
10 | }
11 |
12 | public String access_token;
13 | }
14 |
--------------------------------------------------------------------------------
/applications/calm-api-consumer-java-sample/src/main/java/com/sap/refapps/cloudalm/JwtTokenDto.java:
--------------------------------------------------------------------------------
1 | package com.sap.refapps.cloudalm;
2 |
3 |
4 | import com.fasterxml.jackson.annotation.JsonProperty;
5 |
6 | public class JwtTokenDto {
7 |
8 | private final String accessToken;
9 |
10 | public JwtTokenDto(@JsonProperty("access_token") String accessToken) {
11 | this.accessToken = accessToken;
12 | }
13 |
14 | public String getAccessToken() {
15 | return accessToken;
16 | }
17 |
18 |
19 | }
20 |
21 |
--------------------------------------------------------------------------------
/applications/calm-api-consumer-java-sample/src/main/java/com/sap/refapps/cloudalm/ProjectDto.java:
--------------------------------------------------------------------------------
1 | package com.sap.refapps.cloudalm;
2 |
3 | public class ProjectDto {
4 | public String name;
5 |
6 | public String getName() {
7 | return name;
8 | }
9 |
10 | public void setName(String name) {
11 | this.name = name;
12 | }
13 |
14 | public String getId() {
15 | return id;
16 | }
17 |
18 | public void setId(String id) {
19 | this.id = id;
20 | }
21 |
22 | public String getStatus() {
23 | return status;
24 | }
25 |
26 | public void setStatus(String status) {
27 | this.status = status;
28 | }
29 |
30 | public String id;
31 | public String status;
32 | }
33 |
--------------------------------------------------------------------------------
/applications/calm-api-consumer-java-sample/src/main/java/com/sap/refapps/cloudalm/VcapServicesDto.java:
--------------------------------------------------------------------------------
1 | package com.sap.refapps.cloudalm;
2 |
3 | public class VcapServicesDto {
4 | public String xsuaaURL;
5 |
6 | public String getXsuaaURL() {
7 | return xsuaaURL;
8 | }
9 |
10 | public void setXsuaaURL(String xsuaaURL) {
11 | this.xsuaaURL = xsuaaURL;
12 | }
13 |
14 | public String getClientId() {
15 | return clientId;
16 | }
17 |
18 | public void setClientId(String clientId) {
19 | this.clientId = clientId;
20 | }
21 |
22 | public String getClientSecret() {
23 | return clientSecret;
24 | }
25 |
26 | public void setClientSecret(String clientSecret) {
27 | this.clientSecret = clientSecret;
28 | }
29 |
30 | public String clientId;
31 | public String clientSecret;
32 |
33 |
34 |
35 | }
36 |
--------------------------------------------------------------------------------
/applications/calm-api-consumer-java-sample/src/main/resources/application.properties:
--------------------------------------------------------------------------------
1 | #server port
2 | server.port=5555
3 |
4 | #spring.servlet.multipart.max-file-size=2050MB
5 | #spring.servlet.multipart.maxRequestSize=2050MB
6 | spring.servlet.multipart.max-file-size=-1
7 | spring.servlet.multipart.maxRequestSize=-1
8 | spring.servlet.multipart.enabled=false
9 | sap.cloudalm.public.api.url.eu10=https://eu10.alm.cloud.sap/api/calm-projects/v1/projects
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/applications/calm-api-consumer-ui5-sample/.gitignore:
--------------------------------------------------------------------------------
1 | .vscode
2 | node_modules
3 | dist
4 | mta_archives
5 | resources
6 |
--------------------------------------------------------------------------------
/applications/calm-api-consumer-ui5-sample/Makefile_20210726123408.mta:
--------------------------------------------------------------------------------
1 | # Generated with Cloud MTA Build Tool version 1.2.1
2 | version=0.0.1
3 | MBT=mbt
4 | ifndef p
5 | $(error platform flag is expected. e.g. use make -f makefile.mta p=cf)
6 | endif
7 | target_provided=true
8 | ifndef t
9 | t="$(CURDIR)"
10 | target_provided=false
11 | endif
12 | ifndef strict
13 | strict=true
14 | endif
15 | ifndef mtar
16 | mtar="*"
17 | endif
18 | modules := $(shell $(MBT) provide modules -d=dev)
19 | modules := $(subst ],,$(subst [,,$(modules)))
20 | # List of all the recipes to be executed during the build process
21 | .PHONY: all pre_validate pre_build validate $(modules) post_build meta mtar cleanup
22 | # Default target compile all
23 | all: pre_validate pre_build validate $(modules) post_build meta mtar cleanup
24 | # Validate mta.yaml
25 | pre_validate:
26 | @$(MBT) validate -r=${strict} -x="paths"
27 | pre_build: pre_validate
28 | @$(MBT) project build -p=pre
29 |
30 |
31 | # Execute module build
32 | define build_rule
33 | $(1): validate
34 | @$(MBT) module build -m=$(1) -p=${p} -t=${t}
35 | endef
36 |
37 | $(foreach mod,$(modules),$(eval $(call build_rule,$(mod))))# Create META-INF folder with MANIFEST.MF & mtad.yaml
38 | meta: $(modules) post_build
39 | @$(MBT) gen meta -p=${p} -t=${t}
40 |
41 | post_build: $(modules)
42 | @$(MBT) project build -p=post -t=${t}
43 |
44 | # Validate mta.yaml
45 | validate: pre_build
46 | @$(MBT) validate -r=${strict}
47 |
48 | # Pack as MTAR artifact
49 | mtar: $(modules) meta
50 | @$(MBT) gen mtar --mtar=${mtar} --target_provided=${target_provided} -t=${t}
51 |
52 | cleanup: mtar
53 | # Remove tmp folder
54 | @$(MBT) clean -t=${t}
--------------------------------------------------------------------------------
/applications/calm-api-consumer-ui5-sample/README.md:
--------------------------------------------------------------------------------
1 | # SAP Cloud ALM Extend with APIs
2 |
3 |
4 | ## Description
5 | This SAPUI5 custom application has been developed on SAP BTP platform and serves as an example of what it is possible to achieve with the [SAP Cloud ALM Rest APIs](https://api.sap.com/package/SAPCloudALM/rest). Indeed, only two of the four offered APIs ([Projects](https://api.sap.com/api/CALM_PJM/overview) and [Tasks](https://api.sap.com/api/CALM_TKM/overview)) are used to realize this application, but many other possibilities are available with the full set of provided APIs.
6 |
7 | With this application, the user will be able to consult all projects that are defined in SAP Cloud ALM for Implementation. He will be able to manage all tasks part of a project:
8 | - display tasks within a Kanban view.
9 | - add a task to a project, modify its content, or even delete it.
10 | - change a task status using drag and drop capabilities of SAPUI5.
11 | An additional feature offered by this application is a statistical view that gives a task distribution overview chart (for a given project) based on the task due date (On time vs Overdue tasks). Figures are computed on-the-fly and asynchronously when retrieving the tasks list from the Rest APIs.
12 |
13 | **You will find in this README.md file all the information you need to run this app.**
14 |
15 | ## Prerequisites
16 | *Skip those parts if you already have everything, you can go to [Download and installation](#IMPORT)*
17 | - Have a working SAP BTP Cockpit environment
18 | - Have a working SAP Business Application Studio *(see explanation below)*
19 | - [Have a valid Cloud ALM instance with API Keys](https://blogs.sap.com/2021/08/12/sap-cloud-alm-extend-with-api-get-started-with-sap-cloud-alm-api/)
20 |
21 | #### Subscribe and run the Dev Space Manager in SAP Business Application Studio
22 | To open the SAP Business Application Studio, go in your SAP BTP Cockpit:
23 |
24 |
25 |
26 | From that, create a dev space by clicking on the *Create Dev Space* button and choosing a space for Full Stack Cloud Applications. You can now start it with the associated button you will see on the right. It can take some time but when it displays `STARTED`, its name becomes clickable and you can enter it.
27 |
28 | When it is opened, do not forget to connect to your Cloud Foundry account. Here are the steps to do it:
29 | - Open the preferences (bottom left of the screen)
30 | - Search “cloud foundry” in the search bar
31 | - Check the “Display the current Cloud Foundry target information in the status bar” checkbox
32 | - Click on “Connect to CF” in the bottom left corner
33 | - Fill in the successive fields with the right information for your subaccount (you can find the endpoint in the cockpit from your subaccount overview)
34 | - On the bottom left corner is now written your BTP tenant.
35 |
36 |
37 | ## Download and installation
38 | - [Create destinations in your BTP Cockpit](#destinations)
39 | - [Clone this repository in your workspace](#importProject)
40 | - [Install dependencies and build](#builds)
41 | - [Run locally](#local)
42 | - [Deploy and run in the BTP Cockpit](#deploy)
43 |
44 |
45 | #### 1. Create destinations in your BTP Cockpit
46 |
47 | As explained before, you need to create two specific destinations to run the application correctly. Connect to your SAP BTP Cockpit account, select one of your subaccounts and click on *Destinations* in the ***Connectivity*** section of the left menu. Click on *"New Destination"* to create each destination as in the images below :
48 |
49 |
50 |
51 |
52 | *The URL field should follow this template : https://{region}.alm.cloud.sap/ with the {region} parameter corresponding to the acronym of the region of the servers that host your cloud foundry account*.
53 |
54 | #### 2. Clone this repository in your workspace
55 | Open a Dev Space in your Dev Space Manager and then, from the "Welcome" menu, click on "Clone from git". You can now fill the field at the top (*"Provide Repository URL"*) and fill the needed fields until it is done.
56 |
57 | #### 3. Install dependencies and build
58 | In a terminal, go in the root directory, and run:
59 | ```
60 | npm install
61 | ```
62 | Then, among the NPM Scripts (on the left of your screen in the SAP Business Application Studio), right click on the ***build*** script and run it.
63 | Once the task is finished, you can also run the ***build:mta*** script. It builds a MTA Archive that you can deploy later in your BTP Cockpit if you want to.
64 |
65 | #### 4. Run locally
66 | Among the NPM Scripts, you can run the ***start*** script. It will launch the application in a new tab.
67 |
68 | #### 5. Deploy and run in the BTP Cockpit
69 | In the file explorer (on the left of your screen in the SAP Business Application Studio), open the ***mta_archives*** directory and right click on the *archive.mtar* file. Then, select *Deploy MTA Archive*. Wait for the task to finish and you can now find your app in your BTP Cockpit.
70 |
--------------------------------------------------------------------------------
/applications/calm-api-consumer-ui5-sample/approuter/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "html5-apps-approuter",
3 | "description": "Node.js based application router service for html5-apps",
4 | "dependencies": {
5 | "@sap/approuter": "^6.8.0"
6 | },
7 | "scripts": {
8 | "start": "node node_modules/@sap/approuter/approuter.js"
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/applications/calm-api-consumer-ui5-sample/approuter/xs-app.json:
--------------------------------------------------------------------------------
1 | {
2 | "welcomeFile": "sapuidemocloudalmapi/index.html",
3 | "authenticationMethod": "none",
4 | "routes": [ ]
5 | }
--------------------------------------------------------------------------------
/applications/calm-api-consumer-ui5-sample/mta.yaml:
--------------------------------------------------------------------------------
1 | _schema-version: "3.2"
2 | ID: sap-btp-sapui5
3 | description: A Fiori application.
4 | version: 0.0.1
5 | modules:
6 | - name: sap-btp-sapui5-app-router
7 | type: approuter.nodejs
8 | path: approuter
9 | parameters:
10 | disk-quota: 256M
11 | memory: 256M
12 | requires:
13 | - name: sap-btp-sapui5-repo-runtime
14 | - name: sap-btp-sapui5-dest-srv
15 | - name: sap-btp-sapui5-uaa
16 | - name: sap-btp-sapui5-dest-content
17 | type: com.sap.application.content
18 | requires:
19 | - name: sap-btp-sapui5-dest-srv
20 | parameters:
21 | content-target: true
22 | - name: sap-btp-sapui5-repo-host
23 | parameters:
24 | service-key:
25 | name: sap-btp-sapui5-repo-host-key
26 | - name: sap-btp-sapui5-uaa
27 | parameters:
28 | service-key:
29 | name: sap-btp-sapui5-uaa-key
30 | parameters:
31 | content:
32 | instance:
33 | destinations:
34 | - Name: sap-btp-sapui5_repo_host
35 | ServiceInstanceName: sap-btp-sapui5-html5-srv
36 | ServiceKeyName: sap-btp-sapui5-repo-host-key
37 | sap.cloud.service: sap-btp-sapui5
38 | - Authentication: OAuth2UserTokenExchange
39 | Name: sap-btp-sapui5_uaa
40 | ServiceInstanceName: sap-btp-sapui5-xsuaa-srv
41 | ServiceKeyName: sap-btp-sapui5-uaa-key
42 | sap.cloud.service: sap-btp-sapui5
43 | existing_destinations_policy: ignore
44 | build-parameters:
45 | no-source: true
46 | - name: sap-btp-sapui5-app-content
47 | type: com.sap.application.content
48 | path: .
49 | requires:
50 | - name: sap-btp-sapui5-repo-host
51 | parameters:
52 | content-target: true
53 | build-parameters:
54 | build-result: resources
55 | requires:
56 | - artifacts:
57 | - sapbtpsapui5.zip
58 | name: sapbtpsapui5
59 | target-path: resources/
60 | - name: sapbtpsapui5
61 | type: html5
62 | path: .
63 | build-parameters:
64 | build-result: dist
65 | builder: custom
66 | commands:
67 | - npm install
68 | - npm run build:cf
69 | supported-platforms: []
70 | resources:
71 | - name: sap-btp-sapui5-dest-srv
72 | type: org.cloudfoundry.managed-service
73 | parameters:
74 | config:
75 | HTML5Runtime_enabled: true
76 | init_data:
77 | instance:
78 | destinations:
79 | - Authentication: NoAuthentication
80 | Name: ui5
81 | ProxyType: Internet
82 | Type: HTTP
83 | URL: https://ui5.sap.com
84 | existing_destinations_policy: update
85 | version: 1.0.0
86 | service: destination
87 | service-name: sap-btp-sapui5-dest-srv
88 | service-plan: lite
89 | - name: sap-btp-sapui5-uaa
90 | type: org.cloudfoundry.managed-service
91 | parameters:
92 | path: ./xs-security.json
93 | service: xsuaa
94 | service-name: sap-btp-sapui5-xsuaa-srv
95 | service-plan: application
96 | - name: sap-btp-sapui5-repo-host
97 | type: org.cloudfoundry.managed-service
98 | parameters:
99 | service: html5-apps-repo
100 | service-name: sap-btp-sapui5-html5-srv
101 | service-plan: app-host
102 | - name: sap-btp-sapui5-repo-runtime
103 | type: org.cloudfoundry.managed-service
104 | parameters:
105 | service: html5-apps-repo
106 | service-plan: app-runtime
107 | parameters:
108 | deploy_mode: html5-repo
109 | enable-parallel-deployments: true
110 |
111 |
--------------------------------------------------------------------------------
/applications/calm-api-consumer-ui5-sample/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "sapui5",
3 | "version": "0.0.1",
4 | "private": true,
5 | "description": "A Fiori application.",
6 | "keywords": [
7 | "ui5",
8 | "openui5",
9 | "sapui5"
10 | ],
11 | "main": "webapp/index.html",
12 | "scripts": {
13 | "start": "fiori run --open 'index.html'",
14 | "start-local": "fiori run --config ./ui5-local.yaml --open 'index.html'",
15 | "start-noflp": "fiori run --open 'index.html'",
16 | "build": "ui5 build -a --clean-dest --include-task=generateManifestBundle generateCachebusterInfo",
17 | "deploy": "cf deploy mta_archives/archive.mtar",
18 | "deploy-config": "fiori add deploy-config",
19 | "unit-tests": "fiori run --open test/unit/unitTests.qunit.html",
20 | "int-tests": "fiori run --open test/integration/opaTests.qunit.html",
21 | "build:cf": "ui5 build preload --clean-dest --config ui5-deploy.yaml --include-task=generateManifestBundle generateCachebusterInfo",
22 | "build:mta": "rimraf resources mta_archives && mbt build --mtar archive",
23 | "undeploy": "cf undeploy sap-btp-sapui5 --delete-services --delete-service-keys"
24 | },
25 | "devDependencies": {
26 | "@sap/ui5-builder-webide-extension": "1.0.x",
27 | "@sap/ux-ui5-tooling": "^1.2.5",
28 | "@ui5/cli": "^2.11.1",
29 | "@ui5/fs": "^2.0.6",
30 | "@ui5/logger": "^2.0.1",
31 | "mbt": "^1.0.15",
32 | "rimraf": "3.0.2",
33 | "ui5-task-zipper": "^0.3.1"
34 | },
35 | "ui5": {
36 | "dependencies": [
37 | "@sap/ux-ui5-tooling",
38 | "@sap/ui5-builder-webide-extension",
39 | "ui5-task-zipper",
40 | "mbt"
41 | ]
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/applications/calm-api-consumer-ui5-sample/ui5-deploy.yaml:
--------------------------------------------------------------------------------
1 | # yaml-language-server: $schema=https://sap.github.io/ui5-tooling/schema/ui5.yaml.json
2 | specVersion: '1.0'
3 | metadata:
4 | name: sap.btp.sapui5
5 | type: application
6 | ui5Version:
7 | ui5Theme: sap_fiori_3
8 | resources:
9 | configuration:
10 | propertiesFileSourceEncoding: UTF-8
11 | builder:
12 | resources:
13 | excludes:
14 | - "/test/**"
15 | - "/localService/**"
16 | customTasks:
17 | - name: webide-extension-task-updateManifestJson
18 | beforeTask: generateManifestBundle
19 | configuration:
20 | appFolder: webapp
21 | destDir: dist
22 | - name: ui5-task-zipper
23 | afterTask: generateCachebusterInfo
24 | configuration:
25 | archiveName: sapbtpsapui5
26 | additionalFiles:
27 | - xs-app.json
28 |
--------------------------------------------------------------------------------
/applications/calm-api-consumer-ui5-sample/ui5-local.yaml:
--------------------------------------------------------------------------------
1 | specVersion: '2.0'
2 | metadata:
3 | name: 'sapui5'
4 | type: application
5 | framework:
6 | name: SAPUI5
7 | version: '1.91.0'
8 | libraries:
9 | - name: sap.f
10 | - name: sap.m
11 | - name: sap.suite.ui.generic.template
12 | - name: sap.ui.comp
13 | - name: sap.ui.core
14 | - name: sap.ui.generic.app
15 | - name: sap.ui.table
16 | - name: sap.ushell
17 | - name: themelib_sap_fiori_3
18 | server:
19 | customMiddleware:
20 | - name: fiori-tools-proxy
21 | afterMiddleware: compression
22 | configuration:
23 | ignoreCertError: false # If set to true, certificate errors will be ignored. E.g. self-signed certificates will be accepted
24 | backend:
25 | - path: /V2
26 | url: https://services.odata.org
27 | - name: fiori-tools-appreload
28 | afterMiddleware: compression
29 | configuration:
30 | port: 35729
31 | path: webapp
32 |
--------------------------------------------------------------------------------
/applications/calm-api-consumer-ui5-sample/ui5.yaml:
--------------------------------------------------------------------------------
1 | specVersion: '1.0'
2 | metadata:
3 | name: 'sapui5'
4 | type: application
5 | ui5Theme: sap_fiori_3
6 | server:
7 | customMiddleware:
8 | - name: fiori-tools-proxy
9 | afterMiddleware: compression
10 | configuration:
11 | debug: true
12 | ignoreCertError: true # If set to true, certificate errors will be ignored. E.g. self-signed certificates will be accepted
13 | backend:
14 | - path: /V3
15 | url: https://services.odata.org
16 | - path: /SAPCALM
17 | destination: cloudalmapi_sandbox
18 | - path: /api
19 | destination: cloudalmapi
20 | ui5:
21 | path:
22 | - /resources
23 | - /test-resources
24 | url: https://ui5.sap.com
25 | version: # The UI5 version, for instance, 1.78.1. Empty means latest version
26 | - name: fiori-tools-appreload
27 | afterMiddleware: compression
28 | configuration:
29 | port: 35729
30 | path: webapp
31 |
--------------------------------------------------------------------------------
/applications/calm-api-consumer-ui5-sample/webapp/Component.js:
--------------------------------------------------------------------------------
1 | sap.ui.define([
2 | "sap/ui/core/UIComponent",
3 | "sap/ui/demo/cloudalmapi/ModelManager",
4 | "./utils/Constants"
5 | ], function (UIComponent, ModelManager, Constants) {
6 | "use strict";
7 |
8 |
9 | return UIComponent.extend("sap.ui.demo.cloudalmapi.Component", {
10 |
11 | init : function () {
12 | // call the init function of the parent
13 | UIComponent.prototype.init.apply(this, arguments);
14 | this.modelManager = new ModelManager(this);
15 | this.modelManager.init();
16 |
17 | // set constants model
18 | var oConstantsModel = new sap.ui.model.json.JSONModel();
19 | oConstantsModel.setData(Constants);
20 | this.setModel(oConstantsModel,"constants");
21 | },
22 |
23 | getModelManager : function() {
24 | return this.modelManager;
25 | }
26 |
27 |
28 | });
29 |
30 | });
--------------------------------------------------------------------------------
/applications/calm-api-consumer-ui5-sample/webapp/controller/App.controller.js:
--------------------------------------------------------------------------------
1 | sap.ui.define([
2 | "sap/ui/core/mvc/Controller"
3 | ], function (Controller) {
4 | "use strict";
5 |
6 | return Controller.extend("sap.ui.demo.cloudalmapi.controller.App", {
7 |
8 | onInit : function() {
9 | this.getView().byId("grid").setSnapToRow(true);
10 | this.mModelManager = this.getOwnerComponent().getModelManager();
11 |
12 | },
13 |
14 | /**
15 | * Set the destination in the ModelManager
16 | * Load and display the projects
17 | * @param {Event} oEvent - Selection of an item from the "Select a Destination" dropdown
18 | */
19 | onRESTCall: function(oEvent){
20 | var destination = oEvent.getParameter("item").getKey();
21 | this.mModelManager.setDestination(destination);
22 | this.mModelManager.attachRequestLoadProjectsEnd(
23 | function(){this.getView().byId("menuBTN").setText("Connected: " + destination);}.bind(this)
24 | );
25 | this.mModelManager.attachRequestLoadProjectsError(
26 | function(){this.getView().byId("menuBTN").setText("Select a destination");}.bind(this)
27 | );
28 | this.mModelManager.getProjects();
29 |
30 | }
31 | });
32 |
33 | });
--------------------------------------------------------------------------------
/applications/calm-api-consumer-ui5-sample/webapp/controller/PieChart.controller.js:
--------------------------------------------------------------------------------
1 | sap.ui.define([
2 | "sap/ui/core/mvc/Controller",
3 | "sap/ui/model/Filter",
4 | "sap/ui/model/FilterOperator"
5 | ], function(Controller, Filter, FilterOperator) {
6 | "use strict";
7 | return Controller.extend("sap.ui.demo.cloudalmapi.controller.PieChart", {
8 |
9 | onInit: function () {
10 | this.mModelManager = this.getOwnerComponent().getModelManager();
11 | // register event
12 | this.mModelManager.attachRequestLoadTasksStart(this.enableBusyIndicator.bind(this));
13 | this.mModelManager.attachRequestLoadTasksEnd(this.disableBusyIndicator.bind(this));
14 | },
15 |
16 | enableBusyIndicator : function() {
17 | this.byId("idVizFrame").setBusy(true);
18 | },
19 |
20 | disableBusyIndicator : function() {
21 | this.byId("idVizFrame").setBusy(false);
22 | }
23 | })
24 | })
--------------------------------------------------------------------------------
/applications/calm-api-consumer-ui5-sample/webapp/controller/ProjectList.controller.js:
--------------------------------------------------------------------------------
1 | sap.ui.define([
2 | "sap/ui/core/mvc/Controller",
3 | "sap/ui/model/Filter",
4 | "sap/ui/model/FilterOperator"
5 | ], function (Controller, Filter, FilterOperator) {
6 | "use strict";
7 | return Controller.extend("sap.ui.demo.cloudalmapi.controller.ProjectList", {
8 |
9 | onInit : function(){
10 | var oTable = this.byId("projectList");
11 | this.mModelManager = this.getOwnerComponent().getModelManager();
12 |
13 | // Hide the ugly selector, now the user can select by clicking anywhere on the row
14 | oTable.setSelectionBehavior("RowOnly");
15 |
16 | // Register to the events
17 | this.mModelManager.attachRequestLoadProjectsStart(this.enableBusyIndicator.bind(this));
18 | this.mModelManager.attachRequestLoadProjectsEnd(this.disableBusyIndicator.bind(this));
19 | },
20 |
21 | enableBusyIndicator : function() {
22 | this.byId("tablehbox").setBusy(true);
23 | },
24 |
25 | disableBusyIndicator : function() {
26 | this.byId("tablehbox").setBusy(false);
27 | },
28 |
29 | /**
30 | * Filter the projects from the search bar
31 | * Called everytime a letter is typed or deleted in the search bar
32 | * @param {Event} oEvent - Change of the content of the search bar
33 | */
34 | onFilterProjects : function (oEvent) {
35 |
36 | // Build a filter array
37 | var aFilter = [];
38 |
39 | // Add the needed filter
40 | var sQuery = this.getView().byId("input").getValue();
41 | if (sQuery) {
42 | aFilter.push(new Filter("name", FilterOperator.Contains, sQuery));
43 | }
44 |
45 | // Bind the filter to the rows of the table
46 | var oList = this.byId("projectList");
47 | var oBinding = oList.getBinding("rows");
48 | oBinding.filter(aFilter);
49 | },
50 |
51 | /**
52 | * Load the Task model and dispay the tasks in the Kanban and the Pie Chart
53 | * Called everytime the user selects or unselects a row in the Project List Table
54 | * @param {Event} oEvent - Project selection
55 | */
56 | showTasks : function(oEvent) {
57 | if(this.byId("projectList").getSelectedIndices().length != 0){
58 | this.mModelManager.getTasks( oEvent.getParameter("rowContext").getObject().id);
59 | } else {
60 | // No project selected so no tasks to show
61 | this.mModelManager.flushTasks();
62 | }
63 | },
64 |
65 | /**
66 | * Reload all the projects
67 | * Triggered by a button at the top right corner of the table
68 | */
69 | onRefreshProjects : function() {
70 | this.mModelManager.getProjects();
71 | this.mModelManager.flushTasks();
72 | }
73 | });
74 | });
--------------------------------------------------------------------------------
/applications/calm-api-consumer-ui5-sample/webapp/css/style.css:
--------------------------------------------------------------------------------
1 | html[dir="ltr"] .myAppDemoWT .myCustomButton.sapMBtn {
2 | margin-right: 0.125rem
3 | }
4 |
5 | html[dir="rtl"] .myAppDemoWT .myCustomButton.sapMBtn {
6 | margin-left: 0.125rem
7 | }
8 |
9 | .myAppDemoWT .myCustomText {
10 | display: inline-block;
11 | font-weight: bold;
12 | }
13 |
14 | [data-iconcolor='darkred'] .sapUiIcon{
15 | color: darkred !important;
16 | }
17 |
18 | [data-iconcolor='red'] .sapUiIcon{
19 | color: red !important;
20 | }
21 |
22 | [data-iconcolor='orange'] .sapUiIcon{
23 | color: orange !important;
24 | }
25 |
26 | [data-iconcolor='green'] .sapUiIcon{
27 | color: green !important;
28 | }
29 |
--------------------------------------------------------------------------------
/applications/calm-api-consumer-ui5-sample/webapp/i18n/i18n.properties:
--------------------------------------------------------------------------------
1 | # App Descriptor
2 | appTitle=SAPUI5 Cloud ALM API
3 | appDescription=A simple app to consume Cloud ALM APIs
4 |
5 | # Project List
6 | projectListTitle=Projects
7 | homePageTitle=SAPUI5 Cloud ALM APIs
8 |
9 | # Error
10 | errorMsgProjects=Please check the Destination configuration in your BTP Cockpit or try using another Destination.
--------------------------------------------------------------------------------
/applications/calm-api-consumer-ui5-sample/webapp/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | SAPUI5 Cloud ALM API
7 |
18 |
19 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/applications/calm-api-consumer-ui5-sample/webapp/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "_version": "1.21.0",
3 | "sap.app": {
4 | "id": "sap.ui.demo.cloudalmapi",
5 | "type": "application",
6 | "i18n": {
7 | "bundleUrl": "i18n/i18n.properties",
8 | "supportedLocales": [
9 | ""
10 | ],
11 | "fallbackLocale": ""
12 | },
13 | "title": "{{appTitle}}",
14 | "description": "{{appDescription}}",
15 | "applicationVersion": {
16 | "version": "1.0.0"
17 | },
18 | "dataSources": {
19 | "projectRemote": {
20 | "uri": "/SAPCALM/calm-projects/v1/projects",
21 | "type": "JSON"
22 | }
23 | }
24 | },
25 | "sap.ui": {
26 | "technology": "UI5",
27 | "deviceTypes": {
28 | "desktop": true,
29 | "tablet": true,
30 | "phone": true
31 | }
32 | },
33 | "sap.ui5": {
34 | "rootView": {
35 | "viewName": "sap.ui.demo.cloudalmapi.view.App",
36 | "type": "XML",
37 | "async": true,
38 | "id": "app"
39 | },
40 | "dependencies": {
41 | "minUI5Version": "1.60",
42 | "libs": {
43 | "sap.m": {}
44 | }
45 | },
46 | "models": {
47 | "i18n": {
48 | "type": "sap.ui.model.resource.ResourceModel",
49 | "settings": {
50 | "bundleName": "sap.ui.demo.cloudalmapi.i18n.i18n",
51 | "supportedLocales": [
52 | ""
53 | ],
54 | "fallbackLocale": ""
55 | }
56 | },
57 | "tasksStatistic": {
58 | "type": "sap.ui.model.json.JSONModel",
59 | "uri": "models/TasksStatistics.json"
60 | },
61 | "kanbanModel": {
62 | "type": "sap.ui.model.json.JSONModel",
63 | "uri": "models/KanbanModel.json"
64 | },
65 | "project": {
66 | "type": "sap.ui.model.json.JSONModel",
67 | "uri": "models/Projects.json"
68 | },
69 | "task": {
70 | "type": "sap.ui.model.json.JSONModel",
71 | "uri": "models/Tasks.json"
72 | },
73 | "infoTask": {
74 | "type": "sap.ui.model.json.JSONModel",
75 | "uri": "models/InfoTask.json"
76 | }
77 |
78 | },
79 | "resources": {
80 | "css": [
81 | {
82 | "uri": "css/style.css"
83 | }
84 | ]
85 | }
86 | }
87 | }
88 |
--------------------------------------------------------------------------------
/applications/calm-api-consumer-ui5-sample/webapp/models/InfoTask.json:
--------------------------------------------------------------------------------
1 | [
2 |
3 | ]
--------------------------------------------------------------------------------
/applications/calm-api-consumer-ui5-sample/webapp/models/KanbanModel.json:
--------------------------------------------------------------------------------
1 | {
2 | "open": [],
3 | "blocked": [],
4 | "in_progress": [],
5 | "done": []
6 | }
7 |
--------------------------------------------------------------------------------
/applications/calm-api-consumer-ui5-sample/webapp/models/Projects.json:
--------------------------------------------------------------------------------
1 |
2 | [
3 |
4 | ]
5 |
--------------------------------------------------------------------------------
/applications/calm-api-consumer-ui5-sample/webapp/models/Tasks.json:
--------------------------------------------------------------------------------
1 | [
2 |
3 | ]
--------------------------------------------------------------------------------
/applications/calm-api-consumer-ui5-sample/webapp/models/TasksStatistics.json:
--------------------------------------------------------------------------------
1 | [
2 |
3 | ]
4 |
--------------------------------------------------------------------------------
/applications/calm-api-consumer-ui5-sample/webapp/utils/Constants.js:
--------------------------------------------------------------------------------
1 | sap.ui.define([], function () {
2 | "use strict";
3 |
4 | return {
5 | TASKS_STATUS: {
6 | TASKOPEN: "OPEN",
7 | TASKBLOCKED: "BLK",
8 | TASKINPROGRESS: "INP",
9 | TASKDONE: "CLOSE"
10 | }
11 | };
12 | });
--------------------------------------------------------------------------------
/applications/calm-api-consumer-ui5-sample/webapp/utils/formatter.js:
--------------------------------------------------------------------------------
1 | sap.ui.define([], function () {
2 | "use strict";
3 | return {
4 | priorityTextToId: function (priority) {
5 | switch (priority) {
6 | case "VeryHigh":
7 | return 10;
8 | case "High":
9 | return 20;
10 | case "Medium":
11 | return 30;
12 | case "Low":
13 | return 40;
14 | default:
15 | return 0;
16 | }
17 | },
18 |
19 | priorityColorScheme: function(priorityId){
20 | switch (priorityId) {
21 | case 10:
22 | return "darkred";
23 | case 20:
24 | return "red";
25 | case 30:
26 | return "orange";
27 | default:
28 | return "green";
29 | }
30 | },
31 |
32 | priorityIcon: function(priorityId){
33 | switch (priorityId) {
34 | case 10:
35 | return "sap-icon://collapse-group";
36 | case 20:
37 | return "sap-icon://navigation-up-arrow";
38 | case 30:
39 | return "sap-icon://less";
40 | default:
41 | return "sap-icon://navigation-down-arrow";
42 | }
43 | }
44 | };
45 | });
--------------------------------------------------------------------------------
/applications/calm-api-consumer-ui5-sample/webapp/view/App.view.xml:
--------------------------------------------------------------------------------
1 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
--------------------------------------------------------------------------------
/applications/calm-api-consumer-ui5-sample/webapp/view/CreateTask.fragment.xml:
--------------------------------------------------------------------------------
1 |
4 |
86 |
--------------------------------------------------------------------------------
/applications/calm-api-consumer-ui5-sample/webapp/view/Kanban.view.xml:
--------------------------------------------------------------------------------
1 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
41 |
42 |
43 |
44 |
50 |
51 |
52 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |