├── .eslintrc.json ├── .github └── workflows │ └── node.js.yml ├── .gitignore ├── .npmignore ├── .prettierrc.json ├── .reuse └── dep5 ├── .vscode ├── launch.json └── settings.json ├── LICENSE ├── LICENSES └── Apache-2.0.txt ├── README.md ├── __tests__ ├── authentication.test.ts ├── client.test.ts ├── configuration-api │ └── configuration-client.test.ts ├── destination-configuration.test.ts ├── keyStore.test.ts ├── producer-api │ └── event-producer-client.test.ts ├── test-utils.ts ├── tsconfig.test.json └── utils │ ├── axios-utils.test.ts │ └── region.test.ts ├── babel.config.json ├── docs ├── .gitignore ├── 404.html ├── Gemfile ├── Gemfile.lock ├── README.md ├── _config.yml ├── _data │ ├── action.json │ ├── affected-resource.json │ ├── alert-notification-client.json │ ├── basic-authentication.json │ ├── certificate-authentication.json │ ├── common-query-parameters.json │ ├── condition.json │ ├── configuration.json │ ├── consumer-event-metadata.json │ ├── consumer-event.json │ ├── consumer-paged-response.json │ ├── consumer-query-parameters.json │ ├── destination-service.json │ ├── failure-reason.json │ ├── oauth-authentication.json │ ├── page-metadata.json │ ├── page-response.json │ ├── region-utils.json │ ├── resource-event.json │ ├── retry-configuration.json │ └── subscription.json ├── _includes │ └── footer_custom.html ├── authentication │ ├── authentication-overview.md │ ├── basic-authentication.md │ ├── certificate-authentication.md │ ├── destination-service-authentication.md │ └── oauth-authentication.md ├── common-objects │ ├── common-objects-overview.md │ ├── common-query-params.md │ ├── page-metadata.md │ ├── region-utils.md │ └── retry-configuration.md ├── configuration-api │ ├── action.md │ ├── condition.md │ ├── configuration.md │ ├── entity-type.md │ ├── objects-overview.md │ ├── page-response.md │ ├── state.md │ └── subscription.md ├── event-api-objects │ ├── affected-resource.md │ ├── category.md │ ├── consumer-event.md │ ├── consumer-paged-response.md │ ├── consumer-query-parameters.md │ ├── objects-overview.md │ ├── resource-event.md │ └── severity.md ├── examples │ ├── create-and-get.md │ ├── exporting-and-importing-config.md │ ├── import-configuration-and-send.md │ ├── overview.md │ ├── send-event-and-consume.md │ ├── setup-authentication.md │ └── setup-retriability.md └── overview.md ├── jest.config.js ├── jks-js.d.ts ├── package-lock.json ├── package.json ├── src ├── authentication.ts ├── client.ts ├── configuration-api │ ├── configuration-client.ts │ └── models.ts ├── index.ts ├── producer-api │ ├── event-producer-client.ts │ └── models.ts └── utils │ ├── axios-utils.ts │ ├── common.ts │ ├── destination-configuration.ts │ ├── key-store.ts │ └── region.ts └── tsconfig.json /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "browser": false 4 | }, 5 | "extends": [ 6 | "google", 7 | "prettier", 8 | "eslint:recommended", 9 | "plugin:@typescript-eslint/recommended" 10 | ], 11 | "parser": "@typescript-eslint/parser", 12 | "parserOptions": { 13 | "ecmaVersion": 6, 14 | "sourceType": "module" 15 | }, 16 | "plugins": [ 17 | "prettier", 18 | "@typescript-eslint" 19 | ], 20 | "ignorePatterns": ["**/node_modules/**"], 21 | "rules": { 22 | "semi": "off", 23 | "@typescript-eslint/semi": "error", 24 | "prettier/prettier": "error", 25 | "no-unused-vars": ["error", { "varsIgnorePattern": "^[A-Z]", "vars": "all", "args": "none" }], 26 | "@typescript-eslint/no-explicit-any": "off" 27 | }, 28 | "settings": { 29 | "import/resolver": { 30 | "node": { 31 | "extensions": [ 32 | ".js", 33 | ".jsx", 34 | ".ts", 35 | ".tsx" 36 | ], 37 | "moduleDirectory": [ 38 | "node_modules", 39 | "src/**/*" 40 | ] 41 | } 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /.github/workflows/node.js.yml: -------------------------------------------------------------------------------- 1 | # This workflow will do a clean install of node dependencies, build the source code and run tests across different versions of node 2 | # For more information see: https://help.github.com/actions/language-and-framework-guides/using-nodejs-with-github-actions 3 | 4 | name: Build and test before merge 5 | 6 | on: 7 | pull_request: 8 | branches: 9 | - main 10 | - rel-1.* 11 | types: [opened, edited, synchronize, ready_for_review, reopened] 12 | 13 | jobs: 14 | build-and-test: 15 | 16 | runs-on: ubuntu-latest 17 | 18 | strategy: 19 | matrix: 20 | node-version: [18.x] 21 | 22 | steps: 23 | - uses: actions/checkout@v2 24 | - name: Use Node.js ${{ matrix.node-version }} 25 | uses: actions/setup-node@v1 26 | with: 27 | node-version: ${{ matrix.node-version }} 28 | - run: npm install 29 | - run: npm test 30 | 31 | check-code-style: 32 | 33 | runs-on: ubuntu-latest 34 | 35 | strategy: 36 | matrix: 37 | node-version: [18.x] 38 | 39 | steps: 40 | - uses: actions/checkout@v2 41 | - name: Use Node.js ${{ matrix.node-version }} 42 | uses: actions/setup-node@v1 43 | with: 44 | node-version: ${{ matrix.node-version }} 45 | - run: npm install 46 | - run: npm run lint 47 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | 8 | # Runtime data 9 | pids 10 | *.pid 11 | *.seed 12 | *.pid.lock 13 | 14 | # Directory for instrumented libs generated by jscoverage/JSCover 15 | lib-cov 16 | 17 | # Coverage directory used by tools like istanbul 18 | coverage 19 | 20 | # nyc test coverage 21 | .nyc_output 22 | 23 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 24 | .grunt 25 | 26 | # Bower dependency directory (https://bower.io/) 27 | bower_components 28 | 29 | # node-waf configuration 30 | .lock-wscript 31 | 32 | # Compiled binary addons (https://nodejs.org/api/addons.html) 33 | build/Release 34 | 35 | # Dependency directories 36 | node_modules/ 37 | jspm_packages/ 38 | 39 | # TypeScript v1 declaration files 40 | typings/ 41 | 42 | # Optional npm cache directory 43 | .npm 44 | 45 | # Optional eslint cache 46 | .eslintcache 47 | 48 | # Optional REPL history 49 | .node_repl_history 50 | 51 | # Output of 'npm pack' 52 | *.tgz 53 | 54 | # Yarn Integrity file 55 | .yarn-integrity 56 | 57 | # dotenv environment variables file 58 | .env 59 | 60 | # next.js build output 61 | .next 62 | 63 | # Ignore built ts files 64 | dist/**/* 65 | 66 | # OS metadata 67 | .DS_Store 68 | Thumbs.db 69 | 70 | # Jekyll 71 | .jekyll-metadata -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | logs 2 | *.log 3 | npm-debug.log* 4 | yarn-debug.log* 5 | yarn-error.log* 6 | .vscode 7 | __tests__/ 8 | docs/ 9 | node_modules/ 10 | jest.config.js 11 | tsconfig.json 12 | .eslintrc.json 13 | .prettierrc.json 14 | .reuse -------------------------------------------------------------------------------- /.prettierrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "singleQuote": true, 3 | "tabWidth": 4, 4 | "trailingComma": "none", 5 | "printWidth": 100, 6 | "semi": true 7 | } -------------------------------------------------------------------------------- /.reuse/dep5: -------------------------------------------------------------------------------- 1 | Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ 2 | Upstream-Name: alert-notification-node-client 3 | Upstream-Contact: sap.cloud.platform.alert.notification@groups.sap.com 4 | Source: https://github.com/SAP/alert-notification-node-client 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: 2020 SAP SE or an SAP affiliate company and alert-notification-client contributors 29 | License: Apache-2.0 -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "0.2.0", 3 | "configurations": [ 4 | { 5 | "name": "Debug Jest Tests", 6 | "type": "node", 7 | "request": "launch", 8 | "runtimeArgs": [ 9 | "--inspect-brk", 10 | "${workspaceRoot}/node_modules/.bin/jest", 11 | "--runInBand" 12 | ], 13 | "console": "externalTerminal", 14 | "internalConsoleOptions": "neverOpen", 15 | "port": 9229 16 | } 17 | ] 18 | } -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "editor.formatOnSave": true, 3 | } -------------------------------------------------------------------------------- /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, and distribution 10 | as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by the copyright 13 | owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all other entities 16 | that control, are controlled by, or are under common control with that entity. 17 | For the purposes of this definition, "control" means (i) the power, direct 18 | or indirect, to cause the direction or management of such entity, whether 19 | by contract or otherwise, or (ii) ownership of fifty percent (50%) or more 20 | of the outstanding shares, or (iii) beneficial ownership of such entity. 21 | 22 | "You" (or "Your") shall mean an individual or Legal Entity exercising permissions 23 | granted by this License. 24 | 25 | "Source" form shall mean the preferred form for making modifications, including 26 | but not limited to software source code, documentation source, and configuration 27 | files. 28 | 29 | "Object" form shall mean any form resulting from mechanical transformation 30 | or translation of a Source form, including but not limited to compiled object 31 | code, generated documentation, and conversions to other media types. 32 | 33 | "Work" shall mean the work of authorship, whether in Source or Object form, 34 | made available under the License, as indicated by a copyright notice that 35 | is included in or attached to the work (an example is provided in the Appendix 36 | below). 37 | 38 | "Derivative Works" shall mean any work, whether in Source or Object form, 39 | that is based on (or derived from) the Work and for which the editorial revisions, 40 | annotations, elaborations, or other modifications represent, as a whole, an 41 | original work of authorship. For the purposes of this License, Derivative 42 | Works shall not include works that remain separable from, or merely link (or 43 | bind by name) to the interfaces of, the Work and Derivative Works thereof. 44 | 45 | "Contribution" shall mean any work of authorship, including the original version 46 | of the Work and any modifications or additions to that Work or Derivative 47 | Works thereof, that is intentionally submitted to Licensor for inclusion in 48 | the Work by the copyright owner or by an individual or Legal Entity authorized 49 | to submit on behalf of the copyright owner. For the purposes of this definition, 50 | "submitted" means any form of electronic, verbal, or written communication 51 | sent to the Licensor or its representatives, including but not limited to 52 | communication on electronic mailing lists, source code control systems, and 53 | issue tracking systems that are managed by, or on behalf of, the Licensor 54 | for the purpose of discussing and improving the Work, but excluding communication 55 | that is conspicuously marked or otherwise designated in writing by the copyright 56 | owner as "Not a Contribution." 57 | 58 | "Contributor" shall mean Licensor and any individual or Legal Entity on behalf 59 | of whom a Contribution has been received by Licensor and subsequently incorporated 60 | within the Work. 61 | 62 | 2. Grant of Copyright License. Subject to the terms and conditions of this 63 | License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, 64 | no-charge, royalty-free, irrevocable copyright license to reproduce, prepare 65 | Derivative Works of, publicly display, publicly perform, sublicense, and distribute 66 | the Work and such Derivative Works in Source or Object form. 67 | 68 | 3. Grant of Patent License. Subject to the terms and conditions of this License, 69 | each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, 70 | no-charge, royalty-free, irrevocable (except as stated in this section) patent 71 | license to make, have made, use, offer to sell, sell, import, and otherwise 72 | transfer the Work, where such license applies only to those patent claims 73 | licensable by such Contributor that are necessarily infringed by their Contribution(s) 74 | alone or by combination of their Contribution(s) with the Work to which such 75 | Contribution(s) was submitted. If You institute patent litigation against 76 | any entity (including a cross-claim or counterclaim in a lawsuit) alleging 77 | that the Work or a Contribution incorporated within the Work constitutes direct 78 | or contributory patent infringement, then any patent licenses granted to You 79 | under this License for that Work shall terminate as of the date such litigation 80 | is filed. 81 | 82 | 4. Redistribution. You may reproduce and distribute copies of the Work or 83 | Derivative Works thereof in any medium, with or without modifications, and 84 | in Source or Object form, provided that You meet the following conditions: 85 | 86 | (a) You must give any other recipients of the Work or Derivative Works a copy 87 | of this License; and 88 | 89 | (b) You must cause any modified files to carry prominent notices stating that 90 | You changed the files; and 91 | 92 | (c) You must retain, in the Source form of any Derivative Works that You distribute, 93 | all copyright, patent, trademark, and attribution notices from the Source 94 | form of the Work, excluding those notices that do not pertain to any part 95 | of the Derivative Works; and 96 | 97 | (d) If the Work includes a "NOTICE" text file as part of its distribution, 98 | then any Derivative Works that You distribute must include a readable copy 99 | of the attribution notices contained within such NOTICE file, excluding those 100 | notices that do not pertain to any part of the Derivative Works, in at least 101 | one of the following places: within a NOTICE text file distributed as part 102 | of the Derivative Works; within the Source form or documentation, if provided 103 | along with the Derivative Works; or, within a display generated by the Derivative 104 | Works, if and wherever such third-party notices normally appear. The contents 105 | of the NOTICE file are for informational purposes only and do not modify the 106 | License. You may add Your own attribution notices within Derivative Works 107 | that You distribute, alongside or as an addendum to the NOTICE text from the 108 | Work, provided that such additional attribution notices cannot be construed 109 | as modifying the License. 110 | 111 | You may add Your own copyright statement to Your modifications and may provide 112 | additional or different license terms and conditions for use, reproduction, 113 | or distribution of Your modifications, or for any such Derivative Works as 114 | a whole, provided Your use, reproduction, and distribution of the Work otherwise 115 | complies with the conditions stated in this License. 116 | 117 | 5. Submission of Contributions. Unless You explicitly state otherwise, any 118 | Contribution intentionally submitted for inclusion in the Work by You to the 119 | Licensor shall be under the terms and conditions of this License, without 120 | any additional terms or conditions. Notwithstanding the above, nothing herein 121 | shall supersede or modify the terms of any separate license agreement you 122 | may have executed with Licensor regarding such Contributions. 123 | 124 | 6. Trademarks. This License does not grant permission to use the trade names, 125 | trademarks, service marks, or product names of the Licensor, except as required 126 | for reasonable and customary use in describing the origin of the Work and 127 | reproducing the content of the NOTICE file. 128 | 129 | 7. Disclaimer of Warranty. Unless required by applicable law or agreed to 130 | in writing, Licensor provides the Work (and each Contributor provides its 131 | Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 132 | KIND, either express or implied, including, without limitation, any warranties 133 | or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR 134 | A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness 135 | of using or redistributing the Work and assume any risks associated with Your 136 | exercise of permissions under this License. 137 | 138 | 8. Limitation of Liability. In no event and under no legal theory, whether 139 | in tort (including negligence), contract, or otherwise, unless required by 140 | applicable law (such as deliberate and grossly negligent acts) or agreed to 141 | in writing, shall any Contributor be liable to You for damages, including 142 | any direct, indirect, special, incidental, or consequential damages of any 143 | character arising as a result of this License or out of the use or inability 144 | to use the Work (including but not limited to damages for loss of goodwill, 145 | work stoppage, computer failure or malfunction, or any and all other commercial 146 | damages or losses), even if such Contributor has been advised of the possibility 147 | of such damages. 148 | 149 | 9. Accepting Warranty or Additional Liability. While redistributing the Work 150 | or Derivative Works thereof, You may choose to offer, and charge a fee for, 151 | acceptance of support, warranty, indemnity, or other liability obligations 152 | and/or rights consistent with this License. However, in accepting such obligations, 153 | You may act only on Your own behalf and on Your sole responsibility, not on 154 | behalf of any other Contributor, and only if You agree to indemnify, defend, 155 | and hold each Contributor harmless for any liability incurred by, or claims 156 | asserted against, such Contributor by reason of your accepting any such warranty 157 | or additional liability. 158 | 159 | END OF TERMS AND CONDITIONS 160 | 161 | APPENDIX: How to apply the Apache License to your work. 162 | 163 | To apply the Apache License to your work, attach the following boilerplate 164 | notice, with the fields enclosed by brackets "[]" replaced with your own identifying 165 | information. (Don't include the brackets!) The text should be enclosed in 166 | the appropriate comment syntax for the file format. We also recommend that 167 | a file or class name and description of purpose be included on the same "printed 168 | page" as the copyright notice for easier identification within third-party 169 | archives. 170 | 171 | Copyright [yyyy] [name of copyright owner] 172 | 173 | Licensed under the Apache License, Version 2.0 (the "License"); 174 | you may not use this file except in compliance with the License. 175 | You may obtain a copy of the License at 176 | 177 | http://www.apache.org/licenses/LICENSE-2.0 178 | 179 | Unless required by applicable law or agreed to in writing, software 180 | distributed under the License is distributed on an "AS IS" BASIS, 181 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 182 | See the License for the specific language governing permissions and 183 | limitations under the License. 184 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | CF logo 2 | 3 | # SAP Alert Notification service for SAP BTP Client 4 | >*Promise based Node.js client library to support the usage of Alert Notification service* 5 | 6 | [![Documentation](https://img.shields.io/badge/Service_Documentation-@SAP%20Help%20Portal-ff9900.svg)](https://help.sap.com/viewer/product/ALERT_NOTIFICATION/Cloud/en-US) 7 | [![Blog](https://img.shields.io/badge/Service--related_Blogs-@SAP%20Community%20Portal-3399ff.svg)](https://blogs.sap.com/tag/sap-cloud-platform-alert-notification/) 8 | [![REUSE status](https://api.reuse.software/badge/github.com/SAP/alert-notification-node-client)](https://api.reuse.software/info/github.com/SAP/alert-notification-node-client) 9 | 10 | ## About 11 | Alert Notification service is part of the DevOps portfolio of the SAP Business Technology Platform. The service is specialized in instant delivery of events coming straight from the core platform services, e.g. database or application monitoring tools. This way you're always the first one notified whenever an issue with your dependency occurs. Additionally, Alert Notification service provides means for posting real-time 12 | crucial events directly from your application. All those events altogether - either your custom events, or the platform ones, could be received on whatever channel is preferred - e-mail, Slack, custom webhook, etc. 13 | Furthermore, events can be even stored in Alert Notification service storage and pulled from it later. 14 | 15 | ### Library Features 16 | 17 | * Post custom events; 18 | * Pull your stored events – either the stored on purpose ones or those that have for some reason failed to be delivered to the desired point; 19 | * Manage your configuration – actions, conditions and subscriptions 20 | 21 | ### Installation 22 | 23 | ```bash 24 | $ npm i @sap_oss/alert-notification-client 25 | ``` 26 | 27 | ## Getting Started 28 | 29 | Anything you need to get quickly started with the library is available in our [documentation page](https://sap.github.io/alert-notification-node-client/). 30 | 31 | Here is a very simple example on how to import and create your first Alert Notification service client: 32 | 33 | ```js 34 | import { 35 | AlertNotificationClient, 36 | EntityType, 37 | BasicAuthentication, 38 | RegionUtils, 39 | Severity, 40 | Category 41 | } from '@sap_oss/alert-notification-client'; 42 | 43 | const client = new AlertNotificationClient({ 44 | authentication: new BasicAuthentication({ 45 | username: '', 46 | password: ' { 22 | axios.create = jest.fn((_config: AxiosRequestConfig) => axios); 23 | }); 24 | 25 | describe('BasicAuthentication', () => { 26 | test('can be correctly instantiated', () => { 27 | expect(new BasicAuthentication(basicCredentials)).toBeDefined(); 28 | }); 29 | 30 | test('when getAuthorizationHeaderValue is called then correct value is returned', () => { 31 | const expectedValue = `Basic ${Buffer.from( 32 | `${basicCredentials.username}:${basicCredentials.password}` 33 | ).toString('base64')}`; 34 | return new BasicAuthentication(basicCredentials) 35 | .getAuthorizationHeaderValue() 36 | .then((actualValue) => expect(actualValue).toBe(expectedValue)); 37 | }); 38 | }); 39 | 40 | describe('OAuthAuthentication', () => { 41 | let oauthResponse: { data: { access_token: string; expires_in: number } }; 42 | 43 | beforeEach(() => { 44 | oauthResponse = { 45 | data: { 46 | access_token: 'test-token', 47 | expires_in: Date.now() + 50000000 48 | } 49 | }; 50 | axios.request = jest.fn().mockImplementation(() => Promise.resolve(oauthResponse)); 51 | }); 52 | 53 | test('can be correctly instantiated', () => { 54 | expect( 55 | new OAuthAuthentication({ 56 | oAuthTokenUrl: testUrl, 57 | ...basicCredentials 58 | }) 59 | ).toBeDefined(); 60 | }); 61 | 62 | test('can be correctly instantiated with certificate', () => { 63 | expect( 64 | new OAuthAuthentication({ 65 | oAuthTokenUrl: testUrl, 66 | ...oAuthCredentialsWithCertificate 67 | }) 68 | ).toBeDefined(); 69 | }); 70 | 71 | test('can not be correctly instantiated when privateKey and certificate are empty strings', () => { 72 | expect(() => { 73 | new OAuthAuthentication({ 74 | oAuthTokenUrl: testUrl, 75 | username: username, 76 | privateKey: '', 77 | certificate: '' 78 | }); 79 | }).toThrow('Password is missing.'); 80 | }); 81 | 82 | test('can not be correctly instantiated when password, certificate and privateKey are not provided', () => { 83 | expect(() => { 84 | new OAuthAuthentication({ 85 | oAuthTokenUrl: testUrl, 86 | username: username 87 | }); 88 | }).toThrow('Password is missing.'); 89 | }); 90 | 91 | test('on instantiation retry interceptor is set', () => { 92 | new OAuthAuthentication({ 93 | oAuthTokenUrl: testUrl, 94 | ...basicCredentials 95 | }); 96 | 97 | expect(configureDefaultRetryInterceptor).toBeCalledTimes(1); 98 | }); 99 | 100 | test('correct axios request config is set', () => { 101 | new OAuthAuthentication({ 102 | oAuthTokenUrl: testUrl, 103 | ...basicCredentials 104 | }); 105 | 106 | expect(axios.create).toHaveBeenCalledWith({ 107 | auth: { 108 | username: basicCredentials.username, 109 | password: basicCredentials.password 110 | }, 111 | baseURL: testUrl, 112 | timeout: 5000, 113 | params: { 114 | grant_type: 'client_credentials' 115 | }, 116 | retryConfig: { 117 | maxRetries: 3, 118 | retryBackoff: 1000 119 | } 120 | }); 121 | }); 122 | 123 | describe('when getAuthorizationHeaderValue is called', () => { 124 | let classUnderTest: OAuthAuthentication; 125 | 126 | beforeEach(() => { 127 | classUnderTest = new OAuthAuthentication({ 128 | oAuthTokenUrl: 'test-url', 129 | ...basicCredentials 130 | }); 131 | }); 132 | 133 | test('promise is resolved with token value', () => { 134 | return classUnderTest 135 | .getAuthorizationHeaderValue() 136 | .then((actualValue) => 137 | expect(actualValue).toEqual(`Bearer ${oauthResponse.data.access_token}`) 138 | ); 139 | }); 140 | 141 | test('promise with oAuth certificate is resolved with token value', () => { 142 | classUnderTest = new OAuthAuthentication({ 143 | oAuthTokenUrl: 'test-url', 144 | ...oAuthCredentialsWithCertificate 145 | }); 146 | return classUnderTest 147 | .getAuthorizationHeaderValue() 148 | .then((actualValue) => 149 | expect(actualValue).toEqual(`Bearer ${oauthResponse.data.access_token}`) 150 | ); 151 | }); 152 | 153 | test('promise is rejected when request to OAuth provider fails', () => { 154 | const error = { 155 | status: 400, 156 | data: { 157 | message: 'bad request' 158 | } 159 | }; 160 | 161 | axios.request = jest.fn().mockImplementationOnce(() => Promise.reject(error)); 162 | 163 | return classUnderTest 164 | .getAuthorizationHeaderValue() 165 | .catch((actualError) => expect(actualError).toEqual(error)); 166 | }); 167 | 168 | test('when token is not expired request is not executed', () => { 169 | return classUnderTest.getAuthorizationHeaderValue().then(() => { 170 | return classUnderTest 171 | .getAuthorizationHeaderValue() 172 | .then(() => expect(axios.request).toHaveBeenCalledTimes(1)); 173 | }); 174 | }); 175 | 176 | test('can not be correctly instantiated', () => { 177 | expect(() => { 178 | new OAuthAuthentication({ 179 | oAuthTokenUrl: testUrl, 180 | username: username, 181 | privateKey: '', 182 | certificate: '' 183 | }); 184 | }).toThrow('Password is missing.'); 185 | }); 186 | 187 | test('when token is expired request is executed', () => { 188 | oauthResponse.data.expires_in = -10000; 189 | 190 | return classUnderTest.getAuthorizationHeaderValue().then(() => { 191 | return classUnderTest 192 | .getAuthorizationHeaderValue() 193 | .then(() => expect(axios.request).toHaveBeenCalledTimes(2)); 194 | }); 195 | }); 196 | }); 197 | }); 198 | 199 | describe('CertificatеAuthentication', () => { 200 | const classUnderTest = new CertificateAuthentication(mTLSCredentials); 201 | test('can be correctly instantiated', () => { 202 | expect(new CertificateAuthentication(mTLSCredentials)).toBeDefined(); 203 | }); 204 | 205 | test('when getCertificate is called then correct value is returned', () => { 206 | expect(classUnderTest.getCertificate()).toBe(certificate); 207 | }); 208 | 209 | test('when getPrivateKey is called then correct value is returned', () => { 210 | expect(classUnderTest.getPrivateKey()).toBe(privateKey); 211 | }); 212 | }); 213 | -------------------------------------------------------------------------------- /__tests__/client.test.ts: -------------------------------------------------------------------------------- 1 | import axios, { AxiosRequestConfig } from 'axios'; 2 | import AlertNotificationClient from '../src/client'; 3 | import * as RegionUtils from '../src/utils/region'; 4 | import { 5 | configureDefaultRetryInterceptor, 6 | extractDataOnResponseInterceptor, 7 | setupAuthorizationHeaderOnRequestInterceptor 8 | } from '../src/utils/axios-utils'; 9 | import { EntityType } from '../src/configuration-api/models'; 10 | import ConfigurationApiClient from '../src/configuration-api/configuration-client'; 11 | 12 | import { buildAction, buildEvent } from './test-utils'; 13 | import EventsApiClient from '../src/producer-api/event-producer-client'; 14 | import { BasicAuthentication, CertificateAuthentication } from '../src/authentication'; 15 | import { DestinationConfiguration } from '../src/utils/destination-configuration'; 16 | 17 | jest.mock('axios'); 18 | jest.mock('../src/configuration-api/configuration-client'); 19 | jest.mock('../src/producer-api/event-producer-client'); 20 | jest.mock('../src/utils/axios-utils'); 21 | jest.mock('../src/utils/destination-configuration'); 22 | 23 | const mockedAuthentication = { 24 | getAuthorizationHeaderValue: jest.fn(() => Promise.resolve('test-authorization-value')) 25 | }; 26 | 27 | const region = RegionUtils.EU10; 28 | 29 | beforeEach(() => { 30 | axios.create = jest.fn((_config: AxiosRequestConfig) => axios); 31 | }); 32 | 33 | describe('when instantiating alert notification client', () => { 34 | describe('and retryConfig is provided', () => { 35 | test('and both maxRetries and retryBackoff are provided', () => { 36 | expect( 37 | () => 38 | new AlertNotificationClient({ 39 | authentication: mockedAuthentication, 40 | region: region, 41 | retryConfig: { 42 | maxRetries: 5, 43 | retryBackoff: 1000 44 | } 45 | }) 46 | ).toBeDefined(); 47 | }); 48 | 49 | test('then retry interceptor is set', () => { 50 | new AlertNotificationClient({ 51 | authentication: mockedAuthentication, 52 | region: region, 53 | retryConfig: { 54 | maxRetries: 5, 55 | retryBackoff: 1000 56 | } 57 | }); 58 | 59 | expect(configureDefaultRetryInterceptor).toBeCalledTimes(1); 60 | expect(configureDefaultRetryInterceptor).toBeCalledWith(axios); 61 | }); 62 | }); 63 | 64 | test('destination_configuration_is_provided_and_authentication_is_not_provided', () => { 65 | expect( 66 | () => 67 | new AlertNotificationClient({ 68 | destinationConfiguration: new DestinationConfiguration({ 69 | username: 'username', 70 | destinationName: 'dest-name', 71 | password: 'password', 72 | destinationServiceBaseUrl: 'destUrl', 73 | oAuthTokenUrl: 'oAuthUrl' 74 | }), 75 | region: region 76 | }) 77 | ).toBeDefined(); 78 | }); 79 | 80 | test('destination_configuration_is_provided_and_authentication_is_provided', () => { 81 | expect( 82 | () => 83 | new AlertNotificationClient({ 84 | authentication: new BasicAuthentication({ 85 | username: 'username', 86 | password: 'password' 87 | }), 88 | destinationConfiguration: new DestinationConfiguration({ 89 | username: 'username', 90 | destinationName: 'dest-name', 91 | password: 'password', 92 | destinationServiceBaseUrl: 'destUrl', 93 | oAuthTokenUrl: 'oAuthUrl' 94 | }), 95 | region: region 96 | }) 97 | ).toThrow('Either Authentication or Destination configuration object must be provided'); 98 | }); 99 | 100 | test('authentication_with_certificate_service_is_provided', () => { 101 | expect( 102 | () => 103 | new AlertNotificationClient({ 104 | authentication: new CertificateAuthentication({ 105 | certificate: 'certificate', 106 | privateKey: 'key' 107 | }), 108 | region: region 109 | }) 110 | ).toBeDefined(); 111 | }); 112 | 113 | test('authorization header interceptor is set when authentication is provided', () => { 114 | new AlertNotificationClient({ 115 | authentication: mockedAuthentication, 116 | region: region, 117 | retryConfig: { 118 | maxRetries: 5, 119 | retryBackoff: 1000 120 | } 121 | }); 122 | 123 | expect(setupAuthorizationHeaderOnRequestInterceptor).toBeCalledTimes(1); 124 | expect(setupAuthorizationHeaderOnRequestInterceptor).toBeCalledWith( 125 | axios, 126 | Promise.resolve(mockedAuthentication) 127 | ); 128 | }); 129 | 130 | test('response data extractor interceptor is set', () => { 131 | new AlertNotificationClient({ 132 | authentication: mockedAuthentication, 133 | region: region, 134 | retryConfig: { 135 | maxRetries: 5, 136 | retryBackoff: 1000 137 | } 138 | }); 139 | 140 | expect(extractDataOnResponseInterceptor).toBeCalledTimes(1); 141 | expect(extractDataOnResponseInterceptor).toBeCalledWith(axios); 142 | }); 143 | 144 | test('setupAuthorizationHeader is not called when authentication is with Certificate service', () => { 145 | const axiosRequestConfig = new CertificateAuthentication({ 146 | certificate: 'certificate', 147 | privateKey: 'key' 148 | }); 149 | new AlertNotificationClient({ 150 | authentication: axiosRequestConfig, 151 | region: region, 152 | retryConfig: { 153 | maxRetries: 5, 154 | retryBackoff: 1000 155 | } 156 | }); 157 | 158 | expect(setupAuthorizationHeaderOnRequestInterceptor).toBeCalledTimes(0); 159 | }); 160 | }); 161 | 162 | describe('alert configuration client methods', () => { 163 | let classUnderTest: AlertNotificationClient; 164 | 165 | beforeEach(() => { 166 | classUnderTest = new AlertNotificationClient({ 167 | authentication: mockedAuthentication, 168 | region: region 169 | }); 170 | }); 171 | 172 | test('get calls configurationClient.getOne', () => { 173 | classUnderTest.get(EntityType.ACTION, 'entity'); 174 | expect(ConfigurationApiClient.prototype.getOne).toBeCalledTimes(1); 175 | }); 176 | 177 | test('getAll calls configurationClient.getAll', () => { 178 | classUnderTest.getAll(EntityType.ACTION); 179 | expect(ConfigurationApiClient.prototype.getAll).toBeCalledTimes(1); 180 | }); 181 | 182 | test('update calls configurationClient.update', () => { 183 | classUnderTest.update(EntityType.ACTION, buildAction()); 184 | expect(ConfigurationApiClient.prototype.update).toBeCalledTimes(1); 185 | }); 186 | 187 | test('create calls configurationClient.create', () => { 188 | classUnderTest.create(EntityType.ACTION, buildAction()); 189 | expect(ConfigurationApiClient.prototype.create).toBeCalledTimes(1); 190 | }); 191 | 192 | test('delete calls configurationClient.delete', () => { 193 | classUnderTest.delete(EntityType.ACTION, 'entity'); 194 | expect(ConfigurationApiClient.prototype.delete).toBeCalledTimes(1); 195 | }); 196 | 197 | test('importConfiguration calls configurationClient.import', () => { 198 | classUnderTest.importConfiguration({ actions: [], conditions: [], subscriptions: [] }); 199 | expect(ConfigurationApiClient.prototype.import).toBeCalledTimes(1); 200 | }); 201 | 202 | test('exportConfiguration calls configurationClient.export', () => { 203 | classUnderTest.exportConfiguration(); 204 | expect(ConfigurationApiClient.prototype.export).toBeCalledTimes(1); 205 | }); 206 | 207 | test('sendEvent calls configurationClient.sendEvent', () => { 208 | classUnderTest.sendEvent(buildEvent()); 209 | expect(EventsApiClient.prototype.sendEvent).toBeCalledTimes(1); 210 | }); 211 | 212 | test('sendEvents calls configurationClient.sendEvents', () => { 213 | classUnderTest.sendEvents([buildEvent()]); 214 | expect(EventsApiClient.prototype.sendEvents).toBeCalledTimes(1); 215 | }); 216 | 217 | test('getMatchedEvent calls configurationClient.getMatchedEvents', () => { 218 | classUnderTest.getMatchedEvent('eventId'); 219 | expect(EventsApiClient.prototype.getMatchedEvents).toBeCalledTimes(1); 220 | }); 221 | 222 | test('getMatchedEvents calls configurationClient.getOne', () => { 223 | classUnderTest.getMatchedEvents(); 224 | expect(EventsApiClient.prototype.getMatchedEvents).toBeCalledTimes(1); 225 | }); 226 | 227 | test('getUndeliveredEvent calls configurationClient.getUndeliveredEvents', () => { 228 | classUnderTest.getUndeliveredEvent('eventId'); 229 | expect(EventsApiClient.prototype.getUndeliveredEvents).toBeCalledTimes(1); 230 | }); 231 | 232 | test('getUndeliveredEvents calls configurationClient.getUndeliveredEvents', () => { 233 | classUnderTest.getUndeliveredEvents(); 234 | expect(EventsApiClient.prototype.getUndeliveredEvents).toBeCalledTimes(1); 235 | }); 236 | }); 237 | -------------------------------------------------------------------------------- /__tests__/configuration-api/configuration-client.test.ts: -------------------------------------------------------------------------------- 1 | import axios from 'axios'; 2 | import ConfigurationApiClient from '../../src/configuration-api/configuration-client'; 3 | 4 | import { Platform } from '../../src/utils/region'; 5 | import { EntityType } from '../../src/configuration-api/models'; 6 | 7 | import * as TestUtils from '../test-utils'; 8 | 9 | jest.mock('axios'); 10 | 11 | const mockedAxios = axios as jest.Mocked; 12 | const platform = Platform.CF; 13 | const configurationEntities = [EntityType.ACTION, EntityType.CONDITION, EntityType.SUBSCRIPTION]; 14 | const expectedPaths = { 15 | [EntityType.ACTION]: `/${platform}/configuration/v1/action`, 16 | [EntityType.CONDITION]: `/${platform}/configuration/v1/condition`, 17 | [EntityType.SUBSCRIPTION]: `/${platform}/configuration/v1/subscription` 18 | }; 19 | const params = { 20 | page: 0, 21 | pageSize: 2 22 | }; 23 | 24 | let classUnderTest: ConfigurationApiClient; 25 | 26 | beforeAll(() => { 27 | classUnderTest = new ConfigurationApiClient(platform, mockedAxios); 28 | 29 | mockedAxios.request.mockImplementation(() => Promise.resolve({})); 30 | }); 31 | 32 | describe('when getAll is called', () => { 33 | 34 | test('all entities shall be returned and axios shall be called with the expected arguments', () => { 35 | configurationEntities.forEach((type) => { 36 | return classUnderTest.getAll({ type, params }) 37 | .then(_entity => { 38 | expect(mockedAxios.request).toBeCalledWith({ 39 | method: 'get', 40 | url: expectedPaths[type], 41 | params 42 | }); 43 | }); 44 | }); 45 | }); 46 | }); 47 | 48 | describe('when getOne is called', () => { 49 | 50 | test('then axios is called with correct arguments', () => { 51 | configurationEntities.forEach((type) => { 52 | let name = 'test-name'; 53 | 54 | return classUnderTest.getOne({ type, name }) 55 | .then(_entity => { 56 | expect(mockedAxios.request).toBeCalledWith({ 57 | method: 'get', 58 | url: `${expectedPaths[type]}/${name}` 59 | }); 60 | }); 61 | }) 62 | }); 63 | }); 64 | 65 | describe('when create is called', () => { 66 | 67 | test('then axios is called with correct arguments', () => { 68 | configurationEntities.forEach((type) => { 69 | let data = TestUtils.buildAction(); 70 | 71 | return classUnderTest.create({ type, data }) 72 | .then(_entity => { 73 | expect(mockedAxios.request).toBeCalledWith({ 74 | method: 'post', 75 | url: expectedPaths[type], 76 | data 77 | }); 78 | }); 79 | }) 80 | }); 81 | }); 82 | 83 | describe('when update is called', () => { 84 | 85 | test('then axios is called with correct arguments', () => { 86 | configurationEntities.forEach((type) => { 87 | let name = 'test-action-name'; 88 | let data = TestUtils.buildAction(); 89 | 90 | return classUnderTest.update({ type, name, data }) 91 | .then(_entity => { 92 | expect(mockedAxios.request).toBeCalledWith({ 93 | method: 'put', 94 | url: `${expectedPaths[type]}/${name}`, 95 | data 96 | }); 97 | }); 98 | }) 99 | }); 100 | }); 101 | 102 | describe('when delete is called', () => { 103 | 104 | test('then axios is called with correct arguments', () => { 105 | configurationEntities.forEach((type) => { 106 | let name = 'test-name'; 107 | 108 | return classUnderTest.delete({ type, name }) 109 | .then(_entity => { 110 | expect(mockedAxios.request).toBeCalledWith({ 111 | method: 'delete', 112 | url: `${expectedPaths[type]}/${name}` 113 | }); 114 | }); 115 | }) 116 | }); 117 | }); 118 | 119 | describe('when import is called', () => { 120 | 121 | test('ten axios is called with correct arguments', () => { 122 | let configuration = { 123 | actions: [], 124 | conditions: [], 125 | subscriptions: [] 126 | } 127 | 128 | return classUnderTest.import(configuration) 129 | .then(_entity => { 130 | expect(mockedAxios.request).toBeCalledWith({ 131 | method: 'post', 132 | url: `/${platform}/configuration/v1/configuration`, 133 | data: configuration 134 | }); 135 | }); 136 | }); 137 | }); 138 | 139 | describe('when export is called', () => { 140 | 141 | test('then axios is called with correct arguments', () => { 142 | return classUnderTest.export() 143 | .then(_entity => { 144 | expect(mockedAxios.request).toBeCalledWith({ 145 | method: 'get', 146 | url: `/${platform}/configuration/v1/configuration` 147 | }); 148 | }); 149 | }); 150 | }); 151 | 152 | -------------------------------------------------------------------------------- /__tests__/keyStore.test.ts: -------------------------------------------------------------------------------- 1 | import { KeyStore, KeystoreFormat } from '../src/utils/key-store'; 2 | 3 | const passphrase = 'password'; 4 | const keystore = Buffer.from('keystore'); 5 | const certificate = 'certificate'; 6 | const key = 'key'; 7 | 8 | describe('Key store', () => { 9 | test('can be correctly instantiated with Pem keystore', () => { 10 | const classUnderTest = new KeyStore( 11 | KeystoreFormat.PEM, 12 | passphrase, 13 | undefined, 14 | certificate, 15 | key 16 | ); 17 | expect(classUnderTest).toBeDefined(); 18 | expect(classUnderTest.getHttpsAgent().options.cert).toEqual(certificate); 19 | expect(classUnderTest.getHttpsAgent().options.key).toEqual(key); 20 | expect(classUnderTest.getHttpsAgent().options.passphrase).toEqual(passphrase); 21 | expect(classUnderTest.getHttpsAgent().options.pfx).toBeUndefined(); 22 | }); 23 | 24 | test('can be correctly instantiated with JKS keystore', () => { 25 | const classUnderTest = new KeyStore( 26 | KeystoreFormat.JKS, 27 | passphrase, 28 | undefined, 29 | certificate, 30 | key 31 | ); 32 | expect(classUnderTest).toBeDefined(); 33 | expect(classUnderTest.getHttpsAgent().options.cert).toEqual(certificate); 34 | expect(classUnderTest.getHttpsAgent().options.key).toEqual(key); 35 | expect(classUnderTest.getHttpsAgent().options.passphrase).toEqual(passphrase); 36 | expect(classUnderTest.getHttpsAgent().options.pfx).toBeUndefined(); 37 | }); 38 | 39 | test('can be correctly instantiated with PFX keystore', () => { 40 | const classUnderTest = new KeyStore( 41 | KeystoreFormat.PFX, 42 | passphrase, 43 | keystore, 44 | undefined, 45 | undefined 46 | ); 47 | expect(classUnderTest).toBeDefined(); 48 | expect(classUnderTest.getHttpsAgent().options.pfx).toEqual(keystore); 49 | expect(classUnderTest.getHttpsAgent().options.passphrase).toEqual(passphrase); 50 | expect(classUnderTest.getHttpsAgent().options.cert).toBeUndefined(); 51 | expect(classUnderTest.getHttpsAgent().options.key).toBeUndefined(); 52 | }); 53 | }); 54 | -------------------------------------------------------------------------------- /__tests__/producer-api/event-producer-client.test.ts: -------------------------------------------------------------------------------- 1 | import axios from 'axios'; 2 | import EventApiClient from '../../src/producer-api/event-producer-client'; 3 | import { Platform } from '../../src/utils/region'; 4 | import { Severity, Category } from '../../src/producer-api/models'; 5 | 6 | jest.mock('axios'); 7 | 8 | const mockedAxios = axios as jest.Mocked; 9 | const platform = Platform.CF; 10 | const resourceEvent = { 11 | body: 'test-body', 12 | eventType: 'test-eventType', 13 | subject: 'test-subject', 14 | severity: Severity.INFO, 15 | category: Category.NOTIFICATION, 16 | resource: { 17 | resourceName: 'test-resource-name', 18 | resourceType: 'test-resource-type' 19 | } 20 | }; 21 | const params = { 22 | severity: 'test-severity' 23 | }; 24 | 25 | let classUnderTest: EventApiClient; 26 | 27 | beforeAll(() => { 28 | classUnderTest = new EventApiClient(platform, mockedAxios); 29 | 30 | mockedAxios.request.mockImplementation(() => Promise.resolve({})); 31 | }); 32 | 33 | describe('when sendEvent is called', () => { 34 | 35 | test('then axios is called with correct arguments', () => { 36 | return classUnderTest.sendEvent(resourceEvent) 37 | .then(_event => { 38 | expect(mockedAxios.request).toBeCalledWith({ 39 | method: 'post', 40 | url: `${platform}/producer/v1/resource-events`, 41 | data: resourceEvent 42 | }); 43 | }); 44 | }); 45 | }); 46 | 47 | describe('when sendEvents is called', () => { 48 | 49 | test('then axios is called with correct arguments', () => { 50 | return classUnderTest.sendEvents([ resourceEvent ]) 51 | .then(_event => { 52 | expect(mockedAxios.request).toBeCalledWith({ 53 | method: 'post', 54 | url: `${platform}/producer/v1/resource-events-batch`, 55 | data: [ resourceEvent ] 56 | }); 57 | }); 58 | }); 59 | }); 60 | 61 | describe('when getUndeliveredEvents is called', () => { 62 | 63 | test('and eventId is not provided then axios is called with correct arguments', () => { 64 | return classUnderTest.getUndeliveredEvents({ params }) 65 | .then(_event => { 66 | expect(mockedAxios.request).toBeCalledWith({ 67 | method: 'get', 68 | url: `${platform}/consumer/v1/undelivered-events`, 69 | params 70 | }); 71 | }); 72 | }); 73 | 74 | test('and eventId is provided then axios is called with correct arguments', () => { 75 | let eventId = 'test-event-id'; 76 | return classUnderTest.getUndeliveredEvents({ eventId, params }) 77 | .then(_event => { 78 | expect(mockedAxios.request).toBeCalledWith({ 79 | method: 'get', 80 | url: `${platform}/consumer/v1/undelivered-events/${eventId}`, 81 | params 82 | }); 83 | }); 84 | }); 85 | }); 86 | 87 | describe('when getMatchedEvents is called', () => { 88 | 89 | test('and eventId is not provided then axios is called with correct arguments', () => { 90 | return classUnderTest.getMatchedEvents({ params }) 91 | .then(_event => { 92 | expect(mockedAxios.request).toBeCalledWith({ 93 | method: 'get', 94 | url: `${platform}/consumer/v1/matched-events`, 95 | params 96 | }); 97 | }); 98 | }); 99 | 100 | test('and eventId is provided then axios is called with correct arguments', () => { 101 | let eventId = 'test-event-id'; 102 | return classUnderTest.getMatchedEvents({ eventId, params }) 103 | .then(_event => { 104 | expect(mockedAxios.request).toBeCalledWith({ 105 | method: 'get', 106 | url: `${platform}/consumer/v1/matched-events/${eventId}`, 107 | params: params 108 | }); 109 | }); 110 | }); 111 | }); -------------------------------------------------------------------------------- /__tests__/test-utils.ts: -------------------------------------------------------------------------------- 1 | import { Action, State, Condition, Predicate, Subscription, EntityType } from '../src/configuration-api/models'; 2 | import { PageResponse } from '../src/utils/common'; 3 | import { ResourceEvent, Severity, Category } from '../src/producer-api/models'; 4 | 5 | export const test_action_id = 'test-action-id'; 6 | export const test_condition_id = 'test-condition-id'; 7 | export const test_subscription_id = 'test-subscription-id'; 8 | export const test_type_store = 'STORE'; 9 | export const test_action_name = 'test-action-name'; 10 | export const test_condition_name = 'test-condition-name'; 11 | export const test_subscription_name = 'test-subscription-name'; 12 | export const test_state = State.ENABLED; 13 | export const test_description = 'test-description'; 14 | export const test_labels = []; 15 | export const test_fallback_action = ''; 16 | export const test_fallback_time = 0; 17 | export const test_discard_after = 10; 18 | export const test_enable_delivery_status = false; 19 | export const test_properties = {}; 20 | export const test_property_key = 'test-property-key'; 21 | export const test_property_value = 'test-property-value'; 22 | export const test_predicate = Predicate.ANY; 23 | export const test_snoozeTimestamp = 0; 24 | 25 | export interface ExpectedEntityConfiguration { 26 | entityType: EntityType, 27 | pageResponse: PageResponse, 28 | expectedPath: string, 29 | method: string, 30 | params: object 31 | 32 | } 33 | 34 | export function buildAction(action?: Action): Action { 35 | return { 36 | id: test_action_id, 37 | type: test_type_store, 38 | name: test_action_name, 39 | state: test_state, 40 | description: test_description, 41 | labels: test_labels, 42 | fallbackAction: test_fallback_action, 43 | fallbackTime: test_fallback_time, 44 | discardAfter: test_discard_after, 45 | enableDeliveryStatus: test_enable_delivery_status, 46 | properties: test_properties, 47 | ...{ action } 48 | }; 49 | } 50 | 51 | export function buildCondition(condition?: Condition): Condition { 52 | return { 53 | id: test_condition_id, 54 | name: test_condition_name, 55 | propertyKey: test_property_key, 56 | description: test_description, 57 | predicate: test_predicate, 58 | propertyValue: test_property_value, 59 | labels: test_labels, 60 | ...{ condition } 61 | }; 62 | } 63 | 64 | export function buildSubscription(subscription?: Subscription): Subscription { 65 | return { 66 | id: test_subscription_id, 67 | name: test_subscription_name, 68 | state: test_state, 69 | snoozeTimestamp: test_snoozeTimestamp, 70 | description: test_description, 71 | labels: test_labels, 72 | actions: [test_action_name], 73 | conditions: [test_condition_name], 74 | ...{ subscription } 75 | }; 76 | } 77 | 78 | export function buildEvent(event?: ResourceEvent): ResourceEvent { 79 | return { 80 | body: 'test-body', 81 | eventType: 'test-body', 82 | subject: 'test-body', 83 | severity: Severity.INFO, 84 | category: Category.NOTIFICATION, 85 | resource: { 86 | resourceName: 'test-resource-name', 87 | resourceType: 'test-resource-type' 88 | }, 89 | ...{ event } 90 | } 91 | } -------------------------------------------------------------------------------- /__tests__/tsconfig.test.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "declaration": true, 4 | "module": "commonjs", 5 | "esModuleInterop": true, 6 | "target": "es6", 7 | "moduleResolution": "node", 8 | "outDir": "dist", 9 | } 10 | } -------------------------------------------------------------------------------- /__tests__/utils/axios-utils.test.ts: -------------------------------------------------------------------------------- 1 | import axios, { AxiosInstance, AxiosRequestConfig } from 'axios'; 2 | 3 | import { 4 | configureDefaultRetryInterceptor, 5 | extractDataOnResponseInterceptor, 6 | setupAuthorizationHeaderOnRequestInterceptor 7 | } from '../../src/utils/axios-utils'; 8 | import { KeyStore, KeystoreFormat } from '../../src/utils/key-store'; 9 | 10 | jest.useFakeTimers(); 11 | 12 | const authorizationValue = 'test-authorization-value'; 13 | const mockedAuthentication = { 14 | getAuthorizationHeaderValue: jest.fn(() => Promise.resolve(authorizationValue)) 15 | }; 16 | 17 | let classUnderTest: AxiosInstance; 18 | let axiosRequestConfig: AxiosRequestConfig; 19 | 20 | beforeEach(() => { 21 | classUnderTest = axios.create(); 22 | axiosRequestConfig = {}; 23 | }); 24 | 25 | describe('when setupAuthorizationHeaderOnRequestInterceptor is called', () => { 26 | describe('sets up a request handler when destination is provided', () => { 27 | test('build httpsAgent with pem keystore', () => { 28 | const keyStore = new KeyStore( 29 | KeystoreFormat.P12, 30 | 'passphrase', 31 | Buffer.from('keystore'), 32 | null, 33 | null 34 | ); 35 | setupAuthorizationHeaderOnRequestInterceptor(classUnderTest, Promise.resolve(keyStore)); 36 | 37 | const fullfilledHandler = classUnderTest.interceptors.request['handlers'][0].fulfilled; 38 | return fullfilledHandler(axiosRequestConfig).then((adjustedConfig) => { 39 | expect(adjustedConfig.httpsAgent.options.pfx).toBeDefined(); 40 | expect(adjustedConfig.httpsAgent.options.passphrase).toBe('passphrase'); 41 | expect(adjustedConfig.httpsAgent.options.cert).toBeUndefined(); 42 | expect(adjustedConfig.httpsAgent.options.key).toBeUndefined(); 43 | }); 44 | }); 45 | 46 | test('build httpsAgent with jks keystore', () => { 47 | const keyStore = new KeyStore(KeystoreFormat.JKS, 'passphrase', null, 'cert', 'key'); 48 | setupAuthorizationHeaderOnRequestInterceptor(classUnderTest, Promise.resolve(keyStore)); 49 | 50 | const fullfilledHandler = classUnderTest.interceptors.request['handlers'][0].fulfilled; 51 | return fullfilledHandler(axiosRequestConfig).then((adjustedConfig) => { 52 | expect(adjustedConfig.httpsAgent.options.pfx).toBeUndefined(); 53 | expect(adjustedConfig.httpsAgent.options.passphrase).toBe('passphrase'); 54 | expect(adjustedConfig.httpsAgent.options.cert).toBeDefined(); 55 | expect(adjustedConfig.httpsAgent.options.key).toBeDefined(); 56 | }); 57 | }); 58 | }); 59 | 60 | describe('sets up a request handler which', () => { 61 | beforeEach(() => { 62 | axiosRequestConfig = { 63 | headers: { 64 | 'content-length': 'application-json', 65 | 'test-header': 'test-value' 66 | } 67 | }; 68 | 69 | setupAuthorizationHeaderOnRequestInterceptor( 70 | classUnderTest, 71 | Promise.resolve(mockedAuthentication) 72 | ); 73 | }); 74 | 75 | test('removes auth field from axios request config', () => { 76 | const auth = { 77 | username: 'user', 78 | password: 'pass' 79 | }; 80 | axiosRequestConfig = { ...axiosRequestConfig, auth }; 81 | 82 | const fullfilledHandler = classUnderTest.interceptors.request['handlers'][0].fulfilled; 83 | return fullfilledHandler(axiosRequestConfig).then((adjustedConfig) => 84 | expect(adjustedConfig.auth).not.toBeDefined() 85 | ); 86 | }); 87 | 88 | test('populates headers field with Authorization header', () => { 89 | const fullfilledHandler = classUnderTest.interceptors.request['handlers'][0].fulfilled; 90 | 91 | return fullfilledHandler(axiosRequestConfig).then((adjustedConfig) => 92 | expect(adjustedConfig.headers.Authorization).toEqual(authorizationValue) 93 | ); 94 | }); 95 | }); 96 | }); 97 | 98 | describe('when extractDataOnResponseInterceptor is called', () => { 99 | describe('sets up a response handler which', () => { 100 | test('and ok response then only data object is returned in promise', () => { 101 | extractDataOnResponseInterceptor(classUnderTest); 102 | 103 | const fullfilledHandler = classUnderTest.interceptors.response['handlers'][0].fulfilled; 104 | const responseObject = { 105 | data: { 106 | test: 'test-value' 107 | }, 108 | status: 200, 109 | otherObject: { 110 | other: 'other-data' 111 | } 112 | }; 113 | 114 | return fullfilledHandler(responseObject).then((actualdata) => 115 | expect(actualdata).toEqual(responseObject.data) 116 | ); 117 | }); 118 | }); 119 | }); 120 | 121 | describe('when configureDefaultRetryInterceptor is called', () => { 122 | let retryConfig; 123 | 124 | beforeEach(() => { 125 | retryConfig = { 126 | maxRetries: 1, 127 | retryBackoff: 2500, 128 | instance: classUnderTest 129 | }; 130 | 131 | configureDefaultRetryInterceptor(classUnderTest); 132 | }); 133 | 134 | describe('sets up a request handler which', () => { 135 | test('sets default retry configuration if it is not provided', () => { 136 | const fullfilledHandler = classUnderTest.interceptors.request['handlers'][0].fulfilled; 137 | const adjustedRequestConfig = fullfilledHandler(axiosRequestConfig); 138 | 139 | expect(adjustedRequestConfig.retryConfig).toStrictEqual({ 140 | currentAttempt: 0, 141 | maxRetries: 0, 142 | retryBackoff: 0, 143 | instance: classUnderTest 144 | }); 145 | }); 146 | 147 | test('sets the provided retry configuration', () => { 148 | axiosRequestConfig = { ...axiosRequestConfig, ...{ retryConfig } }; 149 | 150 | const fullfilledHandler = classUnderTest.interceptors.request['handlers'][0].fulfilled; 151 | const adjustedRequestConfig = fullfilledHandler(axiosRequestConfig); 152 | 153 | expect(adjustedRequestConfig.retryConfig).toStrictEqual({ 154 | ...retryConfig, 155 | ...{ currentAttempt: 0 } 156 | }); 157 | }); 158 | }); 159 | 160 | describe('sets response handler which', () => { 161 | let error; 162 | let errorWithRetryConfig; 163 | let onRejectedHandler; 164 | 165 | beforeEach(() => { 166 | error = { 167 | config: axiosRequestConfig 168 | }; 169 | errorWithRetryConfig = { 170 | config: { 171 | ...axiosRequestConfig, 172 | retryConfig: { 173 | currentAttempt: 0, 174 | maxRetries: 5, 175 | retryBackoff: 2500, 176 | instance: classUnderTest 177 | } 178 | } 179 | }; 180 | onRejectedHandler = classUnderTest.interceptors.response['handlers'][0].rejected; 181 | }); 182 | 183 | test('rejects the request if retry config is not present', () => { 184 | return onRejectedHandler(error).catch((actualError) => { 185 | expect(actualError).toBe(error); 186 | }); 187 | }); 188 | 189 | test('rejects the request if instance is not present in retry config', () => { 190 | errorWithRetryConfig.config.retryConfig.instance = undefined; 191 | 192 | return onRejectedHandler(errorWithRetryConfig).catch((actualError) => { 193 | expect(actualError).toBe(errorWithRetryConfig); 194 | }); 195 | }); 196 | 197 | test('rejects the request if it is canceled', () => { 198 | const originalIsCancel = axios.isCancel; 199 | jest.spyOn(axios, 'isCancel').mockReturnValue(true); 200 | 201 | return onRejectedHandler(errorWithRetryConfig).catch((actualError) => { 202 | expect(actualError).toBe(errorWithRetryConfig); 203 | axios.isCancel = originalIsCancel; 204 | }); 205 | }); 206 | 207 | test('rejects the request if current attempts are more than max retries', () => { 208 | errorWithRetryConfig.config.retryConfig.currentAttempt = 5; 209 | 210 | return onRejectedHandler(errorWithRetryConfig).catch((actualError) => { 211 | expect(actualError).toBe(errorWithRetryConfig); 212 | }); 213 | }); 214 | 215 | test('retries the request', () => { 216 | classUnderTest.request = jest.fn(); 217 | 218 | const promise = onRejectedHandler(errorWithRetryConfig).then((_success) => { 219 | errorWithRetryConfig.config.retryConfig.currentAttempt = 1; 220 | expect(classUnderTest.request).toHaveBeenCalledWith(errorWithRetryConfig.config); 221 | }); 222 | 223 | jest.advanceTimersByTime(errorWithRetryConfig.config.retryConfig.retryBackoff); 224 | 225 | return promise; 226 | }); 227 | }); 228 | }); 229 | -------------------------------------------------------------------------------- /__tests__/utils/region.test.ts: -------------------------------------------------------------------------------- 1 | import { Region, Platform } from '../../src/utils/region'; 2 | 3 | describe('when creating region', () => { 4 | const baseUrl = 'test-url'; 5 | const meshUrl = 'mesh-url'; 6 | let region: Region; 7 | 8 | beforeAll(() => { 9 | region = new Region(Platform.CF, baseUrl, meshUrl); 10 | }); 11 | 12 | test('getPlatform returns platform', () => { 13 | expect(region.getPlatform()).toBe(Platform.CF); 14 | }); 15 | 16 | test('getUrl returns the url', () => { 17 | expect(region.getUrl()).toBe(baseUrl); 18 | }); 19 | 20 | test('getUrl returns the url', () => { 21 | expect(region.getmTLSUrl()).toBe(meshUrl); 22 | }); 23 | }); 24 | -------------------------------------------------------------------------------- /babel.config.json: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["@babel/preset-env"] 3 | } -------------------------------------------------------------------------------- /docs/.gitignore: -------------------------------------------------------------------------------- 1 | _site 2 | .sass-cache 3 | .jekyll-cache 4 | .jekyll-metadata 5 | vendor 6 | -------------------------------------------------------------------------------- /docs/404.html: -------------------------------------------------------------------------------- 1 | --- 2 | permalink: /404.html 3 | layout: default 4 | --- 5 | 6 | 19 | 20 |
21 |

404

22 | 23 |

Page not found :(

24 |

The requested page could not be found.

25 |
26 | -------------------------------------------------------------------------------- /docs/Gemfile: -------------------------------------------------------------------------------- 1 | # If you have any plugins, put them here! 2 | group :jekyll_plugins do 3 | gem "github-pages" 4 | gem "jekyll-feed", "~> 0.12" 5 | gem "jekyll-include-cache" 6 | end 7 | 8 | # Windows and JRuby does not include zoneinfo files, so bundle the tzinfo-data gem 9 | # and associated library. 10 | platforms :mingw, :x64_mingw, :mswin, :jruby do 11 | gem "tzinfo", "~> 1.2" 12 | gem "tzinfo-data" 13 | end 14 | 15 | # Performance-booster for watching directories on Windows 16 | gem 'wdm', '>= 0.1.0' if Gem.win_platform? 17 | gem "webrick", "~> 1.7" 18 | -------------------------------------------------------------------------------- /docs/Gemfile.lock: -------------------------------------------------------------------------------- 1 | GEM 2 | specs: 3 | activesupport (6.0.5) 4 | concurrent-ruby (~> 1.0, >= 1.0.2) 5 | i18n (>= 0.7, < 2) 6 | minitest (~> 5.1) 7 | tzinfo (~> 1.1) 8 | zeitwerk (~> 2.2, >= 2.2.2) 9 | addressable (2.8.0) 10 | public_suffix (>= 2.0.2, < 5.0) 11 | coffee-script (2.4.1) 12 | coffee-script-source 13 | execjs 14 | coffee-script-source (1.11.1) 15 | colorator (1.1.0) 16 | commonmarker (0.23.5) 17 | concurrent-ruby (1.1.10) 18 | dnsruby (1.61.9) 19 | simpleidn (~> 0.1) 20 | em-websocket (0.5.3) 21 | eventmachine (>= 0.12.9) 22 | http_parser.rb (~> 0) 23 | ethon (0.15.0) 24 | ffi (>= 1.15.0) 25 | eventmachine (1.2.7) 26 | execjs (2.8.1) 27 | faraday (1.10.0) 28 | faraday-em_http (~> 1.0) 29 | faraday-em_synchrony (~> 1.0) 30 | faraday-excon (~> 1.1) 31 | faraday-httpclient (~> 1.0) 32 | faraday-multipart (~> 1.0) 33 | faraday-net_http (~> 1.0) 34 | faraday-net_http_persistent (~> 1.0) 35 | faraday-patron (~> 1.0) 36 | faraday-rack (~> 1.0) 37 | faraday-retry (~> 1.0) 38 | ruby2_keywords (>= 0.0.4) 39 | faraday-em_http (1.0.0) 40 | faraday-em_synchrony (1.0.0) 41 | faraday-excon (1.1.0) 42 | faraday-httpclient (1.0.1) 43 | faraday-multipart (1.0.3) 44 | multipart-post (>= 1.2, < 3) 45 | faraday-net_http (1.0.1) 46 | faraday-net_http_persistent (1.2.0) 47 | faraday-patron (1.0.0) 48 | faraday-rack (1.0.0) 49 | faraday-retry (1.0.3) 50 | ffi (1.15.5-x64-mingw-ucrt) 51 | forwardable-extended (2.6.0) 52 | gemoji (3.0.1) 53 | github-pages (226) 54 | github-pages-health-check (= 1.17.9) 55 | jekyll (= 3.9.2) 56 | jekyll-avatar (= 0.7.0) 57 | jekyll-coffeescript (= 1.1.1) 58 | jekyll-commonmark-ghpages (= 0.2.0) 59 | jekyll-default-layout (= 0.1.4) 60 | jekyll-feed (= 0.15.1) 61 | jekyll-gist (= 1.5.0) 62 | jekyll-github-metadata (= 2.13.0) 63 | jekyll-include-cache (= 0.2.1) 64 | jekyll-mentions (= 1.6.0) 65 | jekyll-optional-front-matter (= 0.3.2) 66 | jekyll-paginate (= 1.1.0) 67 | jekyll-readme-index (= 0.3.0) 68 | jekyll-redirect-from (= 0.16.0) 69 | jekyll-relative-links (= 0.6.1) 70 | jekyll-remote-theme (= 0.4.3) 71 | jekyll-sass-converter (= 1.5.2) 72 | jekyll-seo-tag (= 2.8.0) 73 | jekyll-sitemap (= 1.4.0) 74 | jekyll-swiss (= 1.0.0) 75 | jekyll-theme-architect (= 0.2.0) 76 | jekyll-theme-cayman (= 0.2.0) 77 | jekyll-theme-dinky (= 0.2.0) 78 | jekyll-theme-hacker (= 0.2.0) 79 | jekyll-theme-leap-day (= 0.2.0) 80 | jekyll-theme-merlot (= 0.2.0) 81 | jekyll-theme-midnight (= 0.2.0) 82 | jekyll-theme-minimal (= 0.2.0) 83 | jekyll-theme-modernist (= 0.2.0) 84 | jekyll-theme-primer (= 0.6.0) 85 | jekyll-theme-slate (= 0.2.0) 86 | jekyll-theme-tactile (= 0.2.0) 87 | jekyll-theme-time-machine (= 0.2.0) 88 | jekyll-titles-from-headings (= 0.5.3) 89 | jemoji (= 0.12.0) 90 | kramdown (= 2.3.2) 91 | kramdown-parser-gfm (= 1.1.0) 92 | liquid (= 4.0.3) 93 | mercenary (~> 0.3) 94 | minima (= 2.5.1) 95 | nokogiri (>= 1.13.6, < 2.0) 96 | rouge (= 3.26.0) 97 | terminal-table (~> 1.4) 98 | github-pages-health-check (1.17.9) 99 | addressable (~> 2.8.0) 100 | dnsruby (~> 1.60) 101 | octokit (~> 4.0) 102 | public_suffix (>= 3.0, < 5.0) 103 | typhoeus (~> 1.3) 104 | html-pipeline (2.14.1) 105 | activesupport (>= 2) 106 | nokogiri (>= 1.13.6) 107 | http_parser.rb (0.8.0) 108 | i18n (0.9.5) 109 | concurrent-ruby (~> 1.0) 110 | jekyll (3.9.2) 111 | addressable (~> 2.8.0) 112 | colorator (~> 1.0) 113 | em-websocket (~> 0.5) 114 | i18n (~> 0.7) 115 | jekyll-sass-converter (~> 1.0) 116 | jekyll-watch (~> 2.0) 117 | kramdown (>= 2.3.1, < 3) 118 | liquid (~> 4.0) 119 | mercenary (~> 0.3.3) 120 | pathutil (~> 0.9) 121 | rouge (>= 1.7, < 4) 122 | safe_yaml (~> 1.0) 123 | jekyll-avatar (0.7.0) 124 | jekyll (>= 3.0, < 5.0) 125 | jekyll-coffeescript (1.1.1) 126 | coffee-script (~> 2.2) 127 | coffee-script-source (~> 1.11.1) 128 | jekyll-commonmark (1.4.0) 129 | commonmarker (~> 0.23.4) 130 | jekyll-commonmark-ghpages (0.2.0) 131 | commonmarker (~> 0.23.4) 132 | jekyll (~> 3.9.0) 133 | jekyll-commonmark (~> 1.4.0) 134 | rouge (>= 2.0, < 4.0) 135 | jekyll-default-layout (0.1.4) 136 | jekyll (~> 3.0) 137 | jekyll-feed (0.15.1) 138 | jekyll (>= 3.7, < 5.0) 139 | jekyll-gist (1.5.0) 140 | octokit (~> 4.2) 141 | jekyll-github-metadata (2.13.0) 142 | jekyll (>= 3.4, < 5.0) 143 | octokit (~> 4.0, != 4.4.0) 144 | jekyll-include-cache (0.2.1) 145 | jekyll (>= 3.7, < 5.0) 146 | jekyll-mentions (1.6.0) 147 | html-pipeline (~> 2.3) 148 | jekyll (>= 3.7, < 5.0) 149 | jekyll-optional-front-matter (0.3.2) 150 | jekyll (>= 3.0, < 5.0) 151 | jekyll-paginate (1.1.0) 152 | jekyll-readme-index (0.3.0) 153 | jekyll (>= 3.0, < 5.0) 154 | jekyll-redirect-from (0.16.0) 155 | jekyll (>= 3.3, < 5.0) 156 | jekyll-relative-links (0.6.1) 157 | jekyll (>= 3.3, < 5.0) 158 | jekyll-remote-theme (0.4.3) 159 | addressable (~> 2.8.0) 160 | jekyll (>= 3.5, < 5.0) 161 | jekyll-sass-converter (>= 1.0, <= 3.0.0, != 2.0.0) 162 | rubyzip (>= 1.3.0, < 3.0) 163 | jekyll-sass-converter (1.5.2) 164 | sass (~> 3.4) 165 | jekyll-seo-tag (2.8.0) 166 | jekyll (>= 3.8, < 5.0) 167 | jekyll-sitemap (1.4.0) 168 | jekyll (>= 3.7, < 5.0) 169 | jekyll-swiss (1.0.0) 170 | jekyll-theme-architect (0.2.0) 171 | jekyll (> 3.5, < 5.0) 172 | jekyll-seo-tag (~> 2.0) 173 | jekyll-theme-cayman (0.2.0) 174 | jekyll (> 3.5, < 5.0) 175 | jekyll-seo-tag (~> 2.0) 176 | jekyll-theme-dinky (0.2.0) 177 | jekyll (> 3.5, < 5.0) 178 | jekyll-seo-tag (~> 2.0) 179 | jekyll-theme-hacker (0.2.0) 180 | jekyll (> 3.5, < 5.0) 181 | jekyll-seo-tag (~> 2.0) 182 | jekyll-theme-leap-day (0.2.0) 183 | jekyll (> 3.5, < 5.0) 184 | jekyll-seo-tag (~> 2.0) 185 | jekyll-theme-merlot (0.2.0) 186 | jekyll (> 3.5, < 5.0) 187 | jekyll-seo-tag (~> 2.0) 188 | jekyll-theme-midnight (0.2.0) 189 | jekyll (> 3.5, < 5.0) 190 | jekyll-seo-tag (~> 2.0) 191 | jekyll-theme-minimal (0.2.0) 192 | jekyll (> 3.5, < 5.0) 193 | jekyll-seo-tag (~> 2.0) 194 | jekyll-theme-modernist (0.2.0) 195 | jekyll (> 3.5, < 5.0) 196 | jekyll-seo-tag (~> 2.0) 197 | jekyll-theme-primer (0.6.0) 198 | jekyll (> 3.5, < 5.0) 199 | jekyll-github-metadata (~> 2.9) 200 | jekyll-seo-tag (~> 2.0) 201 | jekyll-theme-slate (0.2.0) 202 | jekyll (> 3.5, < 5.0) 203 | jekyll-seo-tag (~> 2.0) 204 | jekyll-theme-tactile (0.2.0) 205 | jekyll (> 3.5, < 5.0) 206 | jekyll-seo-tag (~> 2.0) 207 | jekyll-theme-time-machine (0.2.0) 208 | jekyll (> 3.5, < 5.0) 209 | jekyll-seo-tag (~> 2.0) 210 | jekyll-titles-from-headings (0.5.3) 211 | jekyll (>= 3.3, < 5.0) 212 | jekyll-watch (2.2.1) 213 | listen (~> 3.0) 214 | jemoji (0.12.0) 215 | gemoji (~> 3.0) 216 | html-pipeline (~> 2.2) 217 | jekyll (>= 3.0, < 5.0) 218 | kramdown (2.3.2) 219 | rexml 220 | kramdown-parser-gfm (1.1.0) 221 | kramdown (~> 2.3.1) 222 | liquid (4.0.3) 223 | listen (3.7.1) 224 | rb-fsevent (~> 0.10, >= 0.10.3) 225 | rb-inotify (~> 0.9, >= 0.9.10) 226 | mercenary (0.3.6) 227 | minima (2.5.1) 228 | jekyll (>= 3.5, < 5.0) 229 | jekyll-feed (~> 0.9) 230 | jekyll-seo-tag (~> 2.1) 231 | minitest (5.15.0) 232 | multipart-post (2.1.1) 233 | nokogiri (1.13.6-x64-mingw-ucrt) 234 | racc (~> 1.4) 235 | octokit (4.22.0) 236 | faraday (>= 0.9) 237 | sawyer (~> 0.8.0, >= 0.5.3) 238 | pathutil (0.16.2) 239 | forwardable-extended (~> 2.6) 240 | public_suffix (4.0.7) 241 | racc (1.6.0) 242 | rb-fsevent (0.11.1) 243 | rb-inotify (0.10.1) 244 | ffi (~> 1.0) 245 | rexml (3.2.5) 246 | rouge (3.26.0) 247 | ruby2_keywords (0.0.5) 248 | rubyzip (2.3.2) 249 | safe_yaml (1.0.5) 250 | sass (3.7.4) 251 | sass-listen (~> 4.0.0) 252 | sass-listen (4.0.0) 253 | rb-fsevent (~> 0.9, >= 0.9.4) 254 | rb-inotify (~> 0.9, >= 0.9.7) 255 | sawyer (0.8.2) 256 | addressable (>= 2.8.0) 257 | faraday (> 0.8, < 2.0) 258 | simpleidn (0.2.1) 259 | unf (~> 0.1.4) 260 | terminal-table (1.8.0) 261 | unicode-display_width (~> 1.1, >= 1.1.1) 262 | thread_safe (0.3.6) 263 | typhoeus (1.4.0) 264 | ethon (>= 0.9.0) 265 | tzinfo (1.2.9) 266 | thread_safe (~> 0.1) 267 | unf (0.1.4) 268 | unf_ext 269 | unf_ext (0.0.8.1) 270 | unicode-display_width (1.8.0) 271 | wdm (0.1.1) 272 | webrick (1.7.0) 273 | zeitwerk (2.5.4) 274 | 275 | PLATFORMS 276 | ruby 277 | 278 | DEPENDENCIES 279 | github-pages 280 | jekyll-feed (~> 0.12) 281 | jekyll-include-cache 282 | tzinfo (~> 1.2) 283 | tzinfo-data 284 | wdm (>= 0.1.0) 285 | webrick (~> 1.7) 286 | 287 | BUNDLED WITH 288 | 2.3.7 289 | -------------------------------------------------------------------------------- /docs/README.md: -------------------------------------------------------------------------------- 1 | 2 | ## Overview 3 | 4 | This is the documentation of Alert Notification service node client. It provides insights on common objects, classes and examples of the client. 5 | 6 | ## How to run? 7 | 8 | In order to run it you have to make sure that you've followed the [Jekyll's installation guide](https://jekyllrb.com/docs/installation/). 9 | 10 | After the successfuly installation run: 11 | 12 | ```bash 13 | bundle install 14 | ``` 15 | 16 | After the installation of gems is completed run the following command: 17 | 18 | ```bash 19 | bundle exec jekyll serve 20 | ``` 21 | 22 | It will start the documentation on ```http://localhost:4000/alert-notification-node-client/```. -------------------------------------------------------------------------------- /docs/_config.yml: -------------------------------------------------------------------------------- 1 | # Just docs specific configuration 2 | logo: 'https://user-images.githubusercontent.com/11653294/64466233-7cd17480-d119-11e9-8965-e036c1e23c9a.png' 3 | search_enabled: true 4 | heading_anchors: true 5 | back_to_top: true 6 | back_to_top_text: 'Back to top' 7 | aux_links: 8 | 'SAP Alert Notification service for SAP BTP node client on GitHub': 9 | - '//github.com/SAP/alert-notification-node-client' 10 | 11 | title: SAP Alert Notification service for SAP BTP Node client 12 | baseurl: '/alert-notification-node-client' # the subpath of your site, e.g. /blog 13 | url: '' # the base hostname & protocol for your site, e.g. http://example.com 14 | 15 | # Build settings 16 | remote_theme: pmarsceill/just-the-docs 17 | plugins: 18 | - github-pages 19 | - jekyll-feed 20 | - jekyll-include-cache 21 | -------------------------------------------------------------------------------- /docs/_data/action.json: -------------------------------------------------------------------------------- 1 | { 2 | "propertyFields": [ 3 | { 4 | "name": "id?", 5 | "type": "string", 6 | "description": "An identification string which will be generated on its creation" 7 | }, 8 | { 9 | "name": "type", 10 | "type": "string", 11 | "description": "Action's type, e.g. STORE, EMAIL, etc" 12 | }, 13 | { 14 | "name": "name", 15 | "type": "string", 16 | "description": "Unique name, used for identification" 17 | }, 18 | { 19 | "name": "state", 20 | "type": "[State](/alert-notification-node-client/configuration-api-objects/state) (string)", 21 | "description": "Identifies the action's current state, that is, if it's currently enabled or disabled" 22 | }, 23 | { 24 | "name": "description", 25 | "type": "string", 26 | "description": "Brief description of the action, e.g. explaining what it will be used for" 27 | }, 28 | { 29 | "name": "properties", 30 | "type": "object", 31 | "description": "Action specific key-value pairs describing configuration properties" 32 | }, 33 | { 34 | "name": "fallbackAction?", 35 | "type": "string", 36 | "description": "Action to fallback to if execution of current action fails" 37 | }, 38 | { 39 | "name": "fallbackTime?", 40 | "type": "number", 41 | "description": "Time in seconds to allow the current action to be retried before executing the fallback action. If 0, undefined or null the action will be retried for its maximum times and if still fails, then the fallback action will be executed" 42 | }, 43 | { 44 | "name": "discardAfter?", 45 | "type": "number", 46 | "description": "Time in seconds to allow the current action to be retried before being discarded. If 0, undefined or null the action will be retried for its maximum times." 47 | }, 48 | { 49 | "name": "enableDeliveryStatus?", 50 | "type": "boolean", 51 | "description": "Choose to trigger delivery status event for each event matched for delivery through this action. To receive this event, subscribe to it by referring to the Delivery Status event catalog topic outlined in the SAP Alert Notification service Help Portal documentation." 52 | }, 53 | { 54 | "name": "labels?", 55 | "type": "string[]", 56 | "description": "Representing meaningful identifiers, which enable custom displaying & filtering capabilities" 57 | } 58 | ] 59 | } -------------------------------------------------------------------------------- /docs/_data/affected-resource.json: -------------------------------------------------------------------------------- 1 | { 2 | "propertyFields": [ 3 | { 4 | "name": "resourceName", 5 | "type": "string", 6 | "description": "Name of the affected resource, e.g. application's name" 7 | }, 8 | { 9 | "name": "resourceType", 10 | "type": "string", 11 | "description": "Type of the affected resource, e.g. can be of type 'application'" 12 | }, 13 | { 14 | "name": "resourceInstance?", 15 | "type": "string", 16 | "description": "Resource instance, e.g. could be number of an instance" 17 | }, 18 | { 19 | "name": "tags?", 20 | "type": "object", 21 | "description": "Any other useful information about the event.

Some useful known tags by Alert Notification service are:
- **ans:sourceEventId?** - generated by the source, will be used for further stateful interactions with Alert Notification service
- **ans:correlationId?** - generated by the source in order to correlate this event with other activities or issues
- **ans:status?** - will be used for incient management systems. Possible values: 'CREATE_OR_UPDATE', 'CREATE', 'UPDATE', 'COMMENT' or 'CLOSE'
- **ans:recommendedActionLink?** - a URL that contains details for recommended actions regarding this event
- ans:detailsLink? - a URL that contains details for this event, e.g. dashboards showing what triggered it
" 22 | } 23 | ] 24 | } -------------------------------------------------------------------------------- /docs/_data/basic-authentication.json: -------------------------------------------------------------------------------- 1 | { 2 | "propertyFields": [ 3 | { 4 | "name": "username", 5 | "type": "string", 6 | "description": "A tecnical client's username" 7 | }, 8 | { 9 | "name": "password", 10 | "type": "string", 11 | "description": "A technical client's password" 12 | } 13 | ], 14 | "methods": [ 15 | { 16 | "name": "getAuthorizationHeaderValue()", 17 | "returnValue": "string", 18 | "description": "Gets the base64 encoded value of username and password, which will be added in the Authorization Header" 19 | } 20 | ] 21 | } -------------------------------------------------------------------------------- /docs/_data/certificate-authentication.json: -------------------------------------------------------------------------------- 1 | { 2 | "propertyFields": [ 3 | { 4 | "name": "certificate", 5 | "type": "string", 6 | "description": "x509 certificate chain generated by Alert Notification service" 7 | }, 8 | { 9 | "name": "privateKey", 10 | "type": "string", 11 | "description": "Private key related to the generated certificate" 12 | } 13 | ], 14 | "methods": [ 15 | { 16 | "name": "getCertificate()", 17 | "returnValue": "string", 18 | "description": "Get the certificate." 19 | }, 20 | { 21 | "name": "getPrivateKey()", 22 | "returnValue": "string", 23 | "description": "Get the privateKey." 24 | } 25 | ] 26 | } -------------------------------------------------------------------------------- /docs/_data/common-query-parameters.json: -------------------------------------------------------------------------------- 1 | { 2 | "propertyFields": [ 3 | { 4 | "name": "page?", 5 | "type": "number", 6 | "description": "Looked up page" 7 | }, 8 | { 9 | "name": "pageSize?", 10 | "type": "number", 11 | "description": "Size of a page" 12 | } 13 | ] 14 | } -------------------------------------------------------------------------------- /docs/_data/condition.json: -------------------------------------------------------------------------------- 1 | { 2 | "propertyFields": [ 3 | { 4 | "name": "id?", 5 | "type": "string", 6 | "description": "An identification string which will be generated on its creation" 7 | }, 8 | { 9 | "name": "name", 10 | "type": "string", 11 | "description": "Unique name, used for identification" 12 | }, 13 | { 14 | "name": "description", 15 | "type": "string", 16 | "description": "Brief description of the condition, e.g. explaining what it will be used for" 17 | }, 18 | { 19 | "name": "propertyKey", 20 | "type": "string", 21 | "description": "Property key of the event, e.g. eventType" 22 | }, 23 | { 24 | "name": "predicate", 25 | "type": "[Predicate](/alert-notification-node-client/configuration-api-objects/condition/#predicate) (string)", 26 | "description": "Predefined matching criteria" 27 | }, 28 | { 29 | "name": "propertyValue", 30 | "type": "string", 31 | "description": "Value to be expected when matching the propertyKey with the given predicate" 32 | }, 33 | { 34 | "name": "labels?", 35 | "type": "string[]", 36 | "description": "Representing meaningful identifiers, which enable custom displaying & filtering capabilities" 37 | }, 38 | { 39 | "name": "mandatory?", 40 | "type": "boolean", 41 | "description": "Explicitly set which conditions must be mandatory. If not set conditions will be treated as follows:
- Conditions with different property keys will be evaluated with the AND operator
- Conditions with the same property key will be evaluated with OR operator" 42 | } 43 | ] 44 | } -------------------------------------------------------------------------------- /docs/_data/configuration.json: -------------------------------------------------------------------------------- 1 | { 2 | "propertyFields": [ 3 | { 4 | "name": "actions", 5 | "type": "[Action](/alert-notification-node-client/configuration-api-objects/action)[] (object[])", 6 | "description": "Actions in the configuration" 7 | }, 8 | { 9 | "name": "conditions", 10 | "type": "[Condition](/alert-notification-node-client/configuration-api-objects/condition)[] (object[])", 11 | "description": "Conditions in a configuration" 12 | }, 13 | { 14 | "name": "subscriptions", 15 | "type": "[Subscription](/alert-notification-node-client/configuration-api-objects/subscription)[] (object[])", 16 | "description": "Subscriptions in a configuration" 17 | } 18 | ] 19 | } -------------------------------------------------------------------------------- /docs/_data/consumer-event-metadata.json: -------------------------------------------------------------------------------- 1 | { 2 | "propertyFields": [ 3 | { 4 | "name": "cacheTime", 5 | "type": "string", 6 | "description": "Time in UNIX epoch format when the event was stored by Alert Notification service" 7 | }, 8 | { 9 | "name": "affectedActionId", 10 | "type": "string", 11 | "description": "Id of the action which was executed for the matched/undelivered event" 12 | }, 13 | { 14 | "name": "deliveryStatus", 15 | "type": "[DeliveryStatus](/alert-notification-node-client/event-api-objects/consumer-event#deliverystatus) (object)", 16 | "description": "Describes if the event has been delivered or not" 17 | }, 18 | { 19 | "name": "failureReasons?", 20 | "type": "[FailureReason](/alert-notification-node-client/event-api-objects/consumer-event#failurereason) (object)", 21 | "description": "Describes why the delivery of an event failed, will only be returned if a query parameter is present" 22 | } 23 | ] 24 | } -------------------------------------------------------------------------------- /docs/_data/consumer-event.json: -------------------------------------------------------------------------------- 1 | { 2 | "propertyFields": [ 3 | { 4 | "name": "id?", 5 | "type": "string", 6 | "description": "Identification guid of the event, created by Alert Notification service" 7 | }, 8 | { 9 | "name": "body", 10 | "type": "string", 11 | "description": "Describes what the event is about in details" 12 | }, 13 | { 14 | "name": "subject", 15 | "type": "string", 16 | "description": "Self explanatory title, which summarises what the sent event is about" 17 | }, 18 | { 19 | "name": "eventType", 20 | "type": "string", 21 | "description": "Type of the event, e.g. it can be HighCPUUsage, MemoryTooLow, etc." 22 | }, 23 | { 24 | "name": "region", 25 | "type": "[Region](/alert-notification-node-client/common-objects/region-utils#region)", 26 | "description": "Region on which the event was executed, e.g. 'CF_AWS_FRANKFURT' (its alias is 'EU10')" 27 | }, 28 | { 29 | "name": "regionType", 30 | "type": "string", 31 | "description": "Type of the region, e.g. 'sap-cp'" 32 | }, 33 | { 34 | "name": "severity", 35 | "type": "[Severity](/alert-notification-node-client/event-api-objects/severity) (string)", 36 | "description": "Represents the event impact in the context of the affected resource" 37 | }, 38 | { 39 | "name": "category", 40 | "type": "[Category](/alert-notification-node-client/event-api-objects/category/) (string)", 41 | "description": "Represents the event impact in the context of the affected resource" 42 | }, 43 | { 44 | "name": "resource", 45 | "type": "[AffectedResource](/alert-notification-node-client/event-api-objects/affected-resource/) (object)", 46 | "description": "Identifies the action's current state, that is, if it's currently enabled or disabled" 47 | }, 48 | { 49 | "name": "metadata", 50 | "type": "[ConsumerEventMetadata](/alert-notification-node-client/event-api-objects/consumer-event#consumereventmetadata) (object)", 51 | "description": "Holds a useful data about the stored event" 52 | }, 53 | { 54 | "name": "eventTimestamp?", 55 | "type": "string", 56 | "description": "Event timestamp, represents when it was created in the source, if missing Alert Notification service will populate it for you and will set the time it was ingested for processing" 57 | }, 58 | { 59 | "name": "priority?", 60 | "type": "number", 61 | "description": "Priority of the raised event" 62 | }, 63 | { 64 | "name": "tags?", 65 | "type": "object", 66 | "description": "Any other useful information about the event.

Some useful known tags by Alert Notification service are:
- **ans:sourceEventId?** - generated by the source, will be used for further stateful interactions with Alert Notification service
- **ans:correlationId?** - generated by the source in order to correlate this event with other activities or issues
- **ans:status?** - will be used for incient management systems. Possible values: 'CREATE_OR_UPDATE', 'CREATE', 'UPDATE', 'COMMENT' or 'CLOSE'
- **ans:recommendedActionLink?** - a URL that contains details for recommended actions regarding this event
- **ans:detailsLink?** - a URL that contains details for this event, e.g. dashboards showing what triggered it
" 67 | } 68 | ] 69 | } -------------------------------------------------------------------------------- /docs/_data/consumer-paged-response.json: -------------------------------------------------------------------------------- 1 | { 2 | "propertyFields": [ 3 | { 4 | "name": "responseMetadata", 5 | "type": "[PageMetadata](/alert-notification-node-client/common-objects/page-metadata) (object)", 6 | "description": "Represents page's metadata, e.g. what is the currently looked page, how many pages there are, etc." 7 | }, 8 | { 9 | "name": "results", 10 | "type": "[ConsumerEvent](/alert-notification-node-client/event-api-objects/consumer-event)[] (array)", 11 | "description": "An array of matched/undelivered events" 12 | } 13 | ] 14 | } -------------------------------------------------------------------------------- /docs/_data/consumer-query-parameters.json: -------------------------------------------------------------------------------- 1 | { 2 | "propertyFields": [ 3 | { 4 | "name": "sourceEventId?", 5 | "type": "string", 6 | "description": "Event identifier assigned by the source that issued the event" 7 | }, 8 | { 9 | "name": "correlationId?", 10 | "type": "string", 11 | "description": "An event correlation identifier assigned by the source that issued the event. It is intended to correlate the event with other activities or issues" 12 | }, 13 | { 14 | "name": "resourceName?", 15 | "type": "string", 16 | "description": "Name of the affected resource, e.g. application's name" 17 | }, 18 | { 19 | "name": "eventType?", 20 | "type": "string", 21 | "description": "Type of the event, e.g. it can be HighCPUUsage, MemoryTooLow, etc." 22 | }, 23 | { 24 | "name": "severity?", 25 | "type": "[Severity](/alert-notification-node-client/event-api-objects/severity/) (string)", 26 | "description": "Represents the event impact in the context of the affected resource" 27 | }, 28 | { 29 | "name": "category?", 30 | "type": "[Category](/alert-notification-node-client/event-api-objects/category/) (string)", 31 | "description": "Represents the event impact in the context of the affected resource" 32 | }, 33 | { 34 | "name": "include?", 35 | "type": "string", 36 | "description": "Additional properties included in the response, e.g. 'FAILURE_REASON'" 37 | }, 38 | { 39 | "name": "cacheTimeInterval?", 40 | "type": "string", 41 | "description": "Time interval in UNIX epoch format that specifies the time range when the event was cached by the service. The start and end times are not included in the interval. Example:(1517216384;1517216388)" 42 | }, 43 | { 44 | "name": "creationTimeInterval?", 45 | "type": "string", 46 | "description": "Time interval in UNIX epoch format that specifies the time range when the event was created at the source. The start and end times are not included in the interval. Example:(1517216384;1517216388)" 47 | }, 48 | { 49 | "name": "page?", 50 | "type": "number", 51 | "description": "Looked up page" 52 | }, 53 | { 54 | "name": "pageSize?", 55 | "type": "number", 56 | "description": "Size of a page" 57 | } 58 | ] 59 | } -------------------------------------------------------------------------------- /docs/_data/destination-service.json: -------------------------------------------------------------------------------- 1 | { 2 | "propertyFields": [ 3 | { 4 | "name": "username", 5 | "type": "string", 6 | "description": "Client id provided form Destination Service binding" 7 | }, 8 | { 9 | "name": "password", 10 | "type": "string", 11 | "description": "Client secret provided from Destination Service binding" 12 | }, 13 | { 14 | "name": "destinationUrl", 15 | "type": "string", 16 | "description": "Destination Service configuration URL provided from Destination Service binding" 17 | }, 18 | { 19 | "name": "destinationName", 20 | "type": "string", 21 | "description": "Name of configured destination" 22 | }, 23 | { 24 | "name": "oAuthTokenUrl", 25 | "type": "string", 26 | "description": "OAuth URL to retrieve access token" 27 | } 28 | ], 29 | "methods": [ 30 | { 31 | "name": "getAuthentication", 32 | "returnValue": "Promise", 33 | "description": "Get the authentication from the destination." 34 | } 35 | ] 36 | } -------------------------------------------------------------------------------- /docs/_data/failure-reason.json: -------------------------------------------------------------------------------- 1 | { 2 | "propertyFields": [ 3 | { 4 | "name": "code", 5 | "type": "number", 6 | "description": "The error code that represents the failure" 7 | }, 8 | { 9 | "name": "reason", 10 | "type": "string", 11 | "description": "The reason why the delivery of the event has failed" 12 | }, 13 | { 14 | "name": "timestamp", 15 | "type": "number", 16 | "description": "Time in UNIX epoch format that specifies when the event failure occurred" 17 | } 18 | ] 19 | } -------------------------------------------------------------------------------- /docs/_data/oauth-authentication.json: -------------------------------------------------------------------------------- 1 | { 2 | "propertyFields": [ 3 | { 4 | "name": "username", 5 | "type": "string", 6 | "description": "A tecnical client's username" 7 | }, 8 | { 9 | "name": "password", 10 | "type": "string", 11 | "description": "A technical client's password" 12 | }, 13 | { 14 | "name": "certificate", 15 | "type": "string", 16 | "description": "x509 certificate chain generated by UAA service" 17 | }, 18 | { 19 | "name": "privateKey", 20 | "type": "string", 21 | "description": "Private key related to the generated certificate" 22 | }, 23 | { 24 | "name": "oAuthTokenUrl", 25 | "type": "string", 26 | "description": "OAuth token URL to call on retrieving token" 27 | } 28 | ], 29 | "methods": [ 30 | { 31 | "name": "getAuthorizationHeaderValue()", 32 | "returnValue": "string", 33 | "description": "Calls the oAuthTokenUrl and if username and password or username, certificate and privateKey are correct will retrieve an access token which will be cached for further use. If the access token is about to expire a new request to OAuth service will be made" 34 | } 35 | ] 36 | } -------------------------------------------------------------------------------- /docs/_data/page-metadata.json: -------------------------------------------------------------------------------- 1 | { 2 | "propertyFields": [ 3 | { 4 | "name": "page", 5 | "type": "number", 6 | "description": "Currently looked page" 7 | }, 8 | { 9 | "name": "pageSize", 10 | "type": "number", 11 | "description": "Describes how much entities are present per page" 12 | }, 13 | { 14 | "name": "totalPages", 15 | "type": "number", 16 | "description": "Total number of pages for the given page size" 17 | }, 18 | { 19 | "name": "totalResultsCount", 20 | "type": "number", 21 | "description": "Total results entities accross all pages" 22 | } 23 | ] 24 | } -------------------------------------------------------------------------------- /docs/_data/page-response.json: -------------------------------------------------------------------------------- 1 | { 2 | "propertyFields": [ 3 | { 4 | "name": "results", 5 | "type": "[Action](/alert-notification-node-client/configuration-api-objects/action) \\| [Condition](/alert-notification-node-client/configuration-api-objects/condition) \\| [Subscription](/alert-notification-node-client/configuration-api-objects/subscription)[] (object[])", 6 | "description": "An array of entities" 7 | }, 8 | { 9 | "name": "metadata", 10 | "type": "[PageMetadata](/alert-notification-node-client/common-objects/page-metadata) (object)", 11 | "description": "Describes the metadata of a paginated response" 12 | } 13 | ] 14 | } -------------------------------------------------------------------------------- /docs/_data/region-utils.json: -------------------------------------------------------------------------------- 1 | { 2 | "propertyFields": [ 3 | { 4 | "name": "platform", 5 | "type": "[Platform](/alert-notification-node-client/common-objects/region-utils#platform) (object)", 6 | "description": "Platform on which Alert Notification service resides" 7 | }, 8 | { 9 | "name": "url", 10 | "type": "string", 11 | "description": "Base URL of Alert Notification service" 12 | } 13 | ], 14 | "methods": [ 15 | { 16 | "name": "getPlatform()", 17 | "returnValue": "string", 18 | "description": "Gets the platform of the Region" 19 | }, 20 | { 21 | "name": "getUrl()", 22 | "returnValue": "string", 23 | "description": "Gets the URL of the Region" 24 | } 25 | ] 26 | } -------------------------------------------------------------------------------- /docs/_data/resource-event.json: -------------------------------------------------------------------------------- 1 | { 2 | "propertyFields": [ 3 | { 4 | "name": "id?", 5 | "type": "string", 6 | "description": "Identification guid of the resource event. It will be populated by Alert Notification service and you will have access to it on response." 7 | }, 8 | { 9 | "name": "body", 10 | "type": "string", 11 | "description": "Describes what the event is about in details" 12 | }, 13 | { 14 | "name": "subject", 15 | "type": "string", 16 | "description": "Self explanatory title, which summarises what the sent event is about" 17 | }, 18 | { 19 | "name": "eventType", 20 | "type": "string", 21 | "description": "Type of the event, e.g. it can be HighCPUUsage, MemoryTooLow, etc." 22 | }, 23 | { 24 | "name": "severity", 25 | "type": "[Severity](/alert-notification-node-client/event-api-objects/severity/) (string)", 26 | "description": "Represents the event impact in the context of the affected resource" 27 | }, 28 | { 29 | "name": "category", 30 | "type": "[Category](/alert-notification-node-client/event-api-objects/category/) (string)", 31 | "description": "Represents the event impact in the context of the affected resource" 32 | }, 33 | { 34 | "name": "resource", 35 | "type": "[AffectedResource](/alert-notification-node-client/event-api-objects/affected-resource/) (object)", 36 | "description": "Identifies the action's current state, that is, if it's currently enabled or disabled" 37 | }, 38 | { 39 | "name": "eventTimestamp?", 40 | "type": "string", 41 | "description": "Event timestamp, represents when it was created in the source, if missing Alert Notification service will populate it for you and will set the time it was ingested for processing" 42 | }, 43 | { 44 | "name": "priority?", 45 | "type": "number", 46 | "description": "Priority of the raised event" 47 | }, 48 | { 49 | "name": "tags?", 50 | "type": "object", 51 | "description": "Any other useful information about the event.

Some useful known tags by Alert Notification service are:
- **ans:sourceEventId?** - generated by the source, will be used for further stateful interactions with Alert Notification service
- **ans:correlationId?** - generated by the source in order to correlate this event with other activities or issues
- **ans:status?** - will be used for incient management systems. Possible values: 'CREATE_OR_UPDATE', 'CREATE', 'UPDATE', 'COMMENT' or 'CLOSE'
- **ans:recommendedActionLink?** - a URL that contains details for recommended actions regarding this event
- **ans:detailsLink?** - a URL that contains details for this event, e.g. dashboards showing what triggered it
" 52 | } 53 | ] 54 | } -------------------------------------------------------------------------------- /docs/_data/retry-configuration.json: -------------------------------------------------------------------------------- 1 | { 2 | "propertyFields": [ 3 | { 4 | "name": "maxRetries", 5 | "type": "number", 6 | "description": "Maximum number of retries" 7 | }, 8 | { 9 | "name": "retryBackoff", 10 | "type": "number", 11 | "description": "Timeout in milliseconds used to wait before executing the next retry" 12 | } 13 | ] 14 | } -------------------------------------------------------------------------------- /docs/_data/subscription.json: -------------------------------------------------------------------------------- 1 | { 2 | "propertyFields": [ 3 | { 4 | "name": "id?", 5 | "type": "string", 6 | "description": "An identification string which will be generated on its creation" 7 | }, 8 | { 9 | "name": "name", 10 | "type": "string", 11 | "description": "Unique name, used for its identification" 12 | }, 13 | { 14 | "name": "state", 15 | "type": "[State](/alert-notification-node-client/configuration-api-objects/state) (string)", 16 | "description": "Identifies the subscription's current state, that is, if it's currently enabled or disabled" 17 | }, 18 | { 19 | "name": "description?", 20 | "type": "string", 21 | "description": "Brief description of the action, e.g. explaining what it will be used for" 22 | }, 23 | { 24 | "name": "snoozeTimestamp?", 25 | "type": "number", 26 | "description": "Unix timestamp format representing disablement of the subscription until the specified date and time" 27 | }, 28 | { 29 | "name": "actions?", 30 | "type": "string[]", 31 | "description": "Action names that are executed on any matched event" 32 | }, 33 | { 34 | "name": "conditions?", 35 | "type": "string[]", 36 | "description": "Condition names that are evaluated for any incoming event" 37 | }, 38 | { 39 | "name": "labels?", 40 | "type": "string[]", 41 | "description": "Representing meaningful identifiers, which enable custom displaying & filtering capabilities" 42 | } 43 | ] 44 | } -------------------------------------------------------------------------------- /docs/_includes/footer_custom.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |
4 |

© Copyright , SAP SE and SAP Alert Notification service for SAP BTP Contributors

5 |
6 |
7 |
8 |

Terms

9 |
10 |
11 |

Privacy

12 |
13 |
14 |

Trademarks

15 |
16 |
17 |

Legal Impressum

18 |
19 |
20 | 21 | -------------------------------------------------------------------------------- /docs/authentication/authentication-overview.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: default 3 | title: Authentication 4 | nav_order: 2 5 | has_children: true 6 | has_toc: true 7 | permalink: /authentication/ 8 | --- 9 | 10 | ## Overview 11 | 12 | Alert Notification service supports four types of authentication: 13 | 14 | * _Basic_ 15 | * _OAuth_ 16 | * _mTLS_ 17 | * _mTLS, Basic, OAuth using the Destination Service_ 18 | ## Resources 19 | 20 | * [Credential management](https://help.sap.com/viewer/5967a369d4b74f7a9c2b91f5df8e6ab6/Cloud/en-US/b90ed0f3a9604f8e844c73a78d5fad45.html) -------------------------------------------------------------------------------- /docs/authentication/basic-authentication.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: default 3 | title: Basic Authentication 4 | parent: Authentication 5 | nav_order: 1 6 | permalink: /authentication/basic 7 | --- 8 | 9 | # Basic Authentication 10 | {: .no_toc } 11 | 12 | ## Table of contents 13 | {: .no_toc .text-delta } 14 | 15 | 1. TOC 16 | {:toc} 17 | 18 | --- 19 | 20 | ## Description 21 | 22 | BasicAuthentication is a class in the context of Alert Notification service node client. It helps you with acquiring the authorization header value, when using Basic Authentication mechanism, in order to authenticate yourself against Alert Notification service. For further information go to [Credential Management](https://help.sap.com/docs/ALERT_NOTIFICATION/5967a369d4b74f7a9c2b91f5df8e6ab6/80fe24f86bde4e3aac2903ac05511835.html?locale=en-US) and select Basic Access Authentication option. 23 | 24 | ## Constructor properties 25 | 26 | [comment]: <> For loop must remain on the lines, if changed table won't behave normally 27 | 28 | | Field | Type | Description | 29 | |:---:|:--:|:---------:| {% for field in site.data.basic-authentication.propertyFields %} 30 | | {{field.name}} | {{ field.type }} | {{field.description}} | {% endfor %} 31 | 32 | ## Methods 33 | 34 | [comment]: <> For loop must remain on the lines, if changed table won't behave normally 35 | 36 | | Name | Returns | Description | 37 | |:---:|:--:|:---------:| {% for method in site.data.basic-authentication.methods %} 38 | | {{method.name}} | {{method.returnValue}}|{{method.description}} | {% endfor %} 39 | 40 | ## @Example 41 | 42 | ```js 43 | import { BasicAuthentication } from '@sap_oss/alert-notification-client'; 44 | 45 | const basicAuthentication = new BasicAuthentication({ 46 | username: 'test-username', 47 | password: 'test-password' 48 | }); 49 | 50 | basicAuthentication.getAuthorizationHeaderValue() 51 | .then(authHeaderValue => console.log(authHeaderValue)) 52 | .catch(error => console.log(error)); // The current call will print the basic authorization header value with encoded username and password in base64 format 'Basic dGVzdC11c2VybmFtZTp0ZXN0LXBhc3N3b3Jk' 53 | ``` -------------------------------------------------------------------------------- /docs/authentication/certificate-authentication.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: default 3 | title: mTLS Authentication 4 | parent: Authentication 5 | nav_order: 2 6 | permalink: /authentication/mtls-authentication 7 | --- 8 | 9 | # mTLS Authentication 10 | {: .no_toc } 11 | 12 | ## Table of contents 13 | {: .no_toc .text-delta } 14 | 15 | 1. TOC 16 | {:toc} 17 | 18 | --- 19 | 20 | ## Description 21 | 22 | CertificateAuthentication is a class in the context of Alert Notification service node client. It is used to trigger the mTLS based authentication, you just need to provide the certificate and private key generated from your Alert Notification service instance (e.g. from a Service Key or Service Binding). For further information go to [Credential Management](https://help.sap.com/docs/ALERT_NOTIFICATION/5967a369d4b74f7a9c2b91f5df8e6ab6/80fe24f86bde4e3aac2903ac05511835.html?locale=en-US) and select Mutual TLS Authentication option. 23 | 24 | ## Constructor properties 25 | 26 | [comment]: <> For loop must remain on the lines, if changed table won't behave normally 27 | 28 | | Field | Type | Description | 29 | |:---:|:--:|:---------:| {% for field in site.data.certificate-authentication.propertyFields %} 30 | | {{field.name}} | {{ field.type }} | {{field.description}} | {% endfor %} 31 | 32 | ## Methods 33 | 34 | [comment]: <> For loop must remain on the lines, if changed table won't behave normally 35 | 36 | | Name | Returns | Description | 37 | |:---:|:--:|:---------:| {% for method in site.data.certificate-authentication.methods %} 38 | | {{method.name}} | {{method.returnValue}}|{{method.description}} | {% endfor %} 39 | 40 | ## @Example 41 | 42 | ```js 43 | import { CertificateAuthentication } from '@sap_oss/alert-notification-client'; 44 | 45 | const certificateAuthentication = new CertificateAuthentication({ 46 | certificate: 'test-certificate', 47 | privateKey: 'test-privateKey', 48 | }); 49 | 50 | console.log(certificateAuthentication.getCertificate()); 51 | console.log(certificateAuthentication.getPrivateKey()); 52 | ``` -------------------------------------------------------------------------------- /docs/authentication/destination-service-authentication.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: default 3 | title: mTLS Authentication using the Destination Service 4 | parent: Authentication 5 | nav_order: 2 6 | permalink: /authentication/mtls-authentication-using-the-Destination-service 7 | --- 8 | 9 | # mTLS Authentication using the Destination Service 10 | {: .no_toc } 11 | 12 | ## Table of contents 13 | {: .no_toc .text-delta } 14 | 15 | 1. TOC 16 | {:toc} 17 | 18 | --- 19 | 20 | ## Description 21 | 22 | DestinationConfiguration is a class in the context of Alert Notification service Node client. It is used to trigger the mTLS authentication, Basic authentication or oAuth authentication using the Destination service, you just need to provide the clientID, Client secret, OAuth url, Destination service configuration URL generated from your Destination Service instance (e.g. from a Service Key or Service Binding) and the name of the destination where your Alert Notification credentials are configured. For further information go to [Credential Management](https://help.sap.com/docs/ALERT_NOTIFICATION/5967a369d4b74f7a9c2b91f5df8e6ab6/80fe24f86bde4e3aac2903ac05511835.html?locale=en-US) and select Mutual TLS Authentication with Destination Service option. 23 | ## Constructor properties 24 | 25 | [comment]: <> For loop must remain on the lines, if changed table won't behave normally 26 | 27 | | Field | Type | Description | 28 | |:---:|:--:|:---------:| {% for field in site.data.destination-service.propertyFields %} 29 | | {{field.name}} | {{ field.type }} | {{field.description}} | {% endfor %} 30 | 31 | ## Methods 32 | 33 | [comment]: <> For loop must remain on the lines, if changed table won't behave normally 34 | 35 | | Name | Returns | Description | 36 | |:---:|:--:|:---------:| {% for method in site.data.destination-service.methods %} 37 | | {{method.name}} | {{method.returnValue}}|{{method.description}} | {% endfor %} 38 | 39 | ## @Example 40 | 41 | ```js 42 | import { DestinationConfiguration } from '@sap_oss/alert-notification-client'; 43 | 44 | const destinationConfiguration = new DestinationConfiguration({ 45 | username: 'test-certificate', 46 | password: 'test-privateKey', 47 | oAuthTokenUrl: 'https://test.oauth.cert.service.com/oauth/token', 48 | destinationUrl: 'https://destination-configuration.test.com', 49 | destinationName: 'test-destination' 50 | }); 51 | 52 | destinationConfiguration.getAuthentication() 53 | .then(authentication => console.log(authentication)) 54 | .catch(error => console.log(error)); // The current call will print the authentication configured in the destination.' 55 | 56 | ``` -------------------------------------------------------------------------------- /docs/authentication/oauth-authentication.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: default 3 | title: OAuth Authentication 4 | parent: Authentication 5 | nav_order: 2 6 | permalink: /authentication/oauth 7 | --- 8 | 9 | # OAuth Authentication 10 | {: .no_toc } 11 | 12 | ## Table of contents 13 | {: .no_toc .text-delta } 14 | 15 | 1. TOC 16 | {:toc} 17 | 18 | --- 19 | 20 | ## Description 21 | 22 | OAuthAuthentication is a class in the context of Alert Notification service node client. It helps you with acquiring the authorization header value, when using OAuth Authentication mechanism, in order to authenticate yourself against Alert Notification service. For further information go to [Credential Management](https://help.sap.com/docs/ALERT_NOTIFICATION/5967a369d4b74f7a9c2b91f5df8e6ab6/80fe24f86bde4e3aac2903ac05511835.html?locale=en-US) and select OAuth2.0 Authentication with client ID and client secret (default) option. 23 | 24 | _**Note**_: Grant type query parameter will be added automatically by the client with value __client_credentials__. 25 | 26 | _**Note**_: In case you want the acquiring of the authorization header value to be realized with __certificate__ and __privateKey__, client id query parameter will be added automatically by the client with value of the __username__ field. 27 | 28 | ## Constructor properties 29 | 30 | [comment]: <> For loop must remain on the lines, if changed table won't behave normally 31 | 32 | | Field | Type | Description | 33 | |:---:|:--:|:---------:| {% for field in site.data.oauth-authentication.propertyFields %} 34 | | {{field.name}} | {{ field.type }} | {{field.description}} | {% endfor %} 35 | 36 | ## Methods 37 | 38 | [comment]: <> For loop must remain on the lines, if changed table won't behave normally 39 | 40 | | Name | Returns | Description | 41 | |:---:|:--:|:---------:| {% for method in site.data.oauth-authentication.methods %} 42 | | {{method.name}} | {{method.returnValue}}|{{method.description}} | {% endfor %} 43 | 44 | ## @Example 45 | 46 | ```js 47 | import { OAuthAuthentication } from '@sap_oss/alert-notification-client'; 48 | 49 | const oAuthAuthentication = new OAuthAuthentication({ 50 | username: 'test-username', 51 | password: 'test-password', 52 | oAuthTokenUrl: 'https://test.oauth.service.com/oauth/token' 53 | }); 54 | 55 | oAuthAuthentication.getAuthorizationHeaderValue() 56 | .then(authHeaderValue => console.log(authHeaderValue)) 57 | .catch(error => console.log(error)); // In the current case an error will be logged, as the provided arguments are invalid. In order for the call to pass you must provide valid arguments. 58 | ``` 59 | 60 | ```js 61 | import { OAuthAuthentication } from '@sap_oss/alert-notification-client'; 62 | 63 | const oAuthAuthentication = new OAuthAuthentication({ 64 | username: 'test-username', 65 | certificate: 'test-certificate', 66 | privateKey: 'test-privateKey', 67 | oAuthTokenUrl: 'https://test.oauth.cert.service.com/oauth/token' 68 | }); 69 | 70 | oAuthAuthentication.getAuthorizationHeaderValue() 71 | .then(authHeaderValue => console.log(authHeaderValue)) 72 | .catch(error => console.log(error)); // In the current case an error will be logged, as the provided arguments are invalid. In order for the call to pass you must provide valid arguments. 73 | ``` -------------------------------------------------------------------------------- /docs/common-objects/common-objects-overview.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: default 3 | title: Common objects 4 | nav_order: 3 5 | has_children: true 6 | has_toc: true 7 | permalink: /common-objects/ 8 | --- 9 | 10 | ## Overview 11 | 12 | This section describes the common objects which are used/received accross Alert Notification service APIs. 13 | -------------------------------------------------------------------------------- /docs/common-objects/common-query-params.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: default 3 | title: CommonQueryParams 4 | nav_order: 4 5 | parent: Common objects 6 | permalink: /common-objects/query-params 7 | --- 8 | 9 | # CommonQueryParams 10 | {: .no_toc } 11 | 12 | ## Table of contents 13 | {: .no_toc .text-delta } 14 | 15 | 1. TOC 16 | {:toc} 17 | 18 | --- 19 | 20 | ## Description 21 | 22 | Common query parameters used when making requests to Alert Notification service. 23 | 24 | ## Properties 25 | 26 | [comment]: <> For loop must remain on the lines, if changed table won't behave normally 27 | 28 | | Field | Type | Description | 29 | |:---:|:--:|:---------:| {% for field in site.data.common-query-parameters.propertyFields %} 30 | | {{field.name}} | {{ field.type }} | {{field.description}} | {% endfor %} 31 | 32 | ## @Example 33 | 34 | ```js 35 | const queryParameters = { 36 | page: 1, 37 | pageSize: 20 38 | }; 39 | ``` -------------------------------------------------------------------------------- /docs/common-objects/page-metadata.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: default 3 | title: PageMetadata 4 | nav_order: 3 5 | parent: Common objects 6 | permalink: /common-objects/page-metadata 7 | --- 8 | 9 | # PagedMetadata 10 | {: .no_toc } 11 | 12 | ## Table of contents 13 | {: .no_toc .text-delta } 14 | 15 | 1. TOC 16 | {:toc} 17 | 18 | --- 19 | 20 | ## Description 21 | 22 | A common object which is contained inside a paginated response. It holds metadata related to the requested pages. 23 | 24 | _**Note:**_ The index page starts from 0. 25 | 26 | ## Properties 27 | 28 | [comment]: <> For loop must remain on the lines, if changed table won't behave normally 29 | 30 | | Field | Type | Description | 31 | |:---:|:--:|:---------:| {% for field in site.data.page-metadata.propertyFields %} 32 | | {{field.name}} | {{ field.type }} | {{field.description}} | {% endfor %} 33 | 34 | ## @Example 35 | 36 | ```js 37 | const pagedResponseMetadata = { 38 | page: 0, 39 | pageSize: 100, 40 | totalPages: 1, 41 | totalResultsCount: 20 42 | } 43 | ``` -------------------------------------------------------------------------------- /docs/common-objects/region-utils.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: default 3 | title: RegionUtils 4 | nav_order: 4 5 | parent: Common objects 6 | permalink: /common-objects/region-utils 7 | --- 8 | 9 | # RegionUtils 10 | {: .no_toc } 11 | 12 | ## Table of contents 13 | {: .no_toc .text-delta } 14 | 15 | 1. TOC 16 | {:toc} 17 | 18 | --- 19 | 20 | ## Description 21 | 22 | RegionUtils contains: 23 | * constants with predefined regions which represent where Alert Notification service is currently onboarded 24 | * a class which can be used to fully customize your region with a specific URL and platform. 25 | * an enum representing the environment on which Alert Notification service is onboarded 26 | 27 | ## Region 28 | 29 | ### Description 30 | 31 | A class which can be used to fully customize your region with a specific URL and platform. 32 | 33 | ### Constructor properties 34 | 35 | [comment]: <> For loop must remain on the lines, if changed table won't behave normally 36 | 37 | | Field | Type | Description | 38 | |:---:|:--:|:---------:| {% for field in site.data.region-utils.propertyFields %} 39 | | {{field.name}} | {{ field.type }} | {{field.description}} | {% endfor %} 40 | 41 | ### Methods 42 | 43 | [comment]: <> For loop must remain on the lines, if changed table won't behave normally 44 | 45 | | Name | Returns | Description | 46 | |:---:|:--:|:---------:| {% for method in site.data.region-utils.methods %} 47 | | {{method.name}} | {{method.returnValue}}|{{method.description}} | {% endfor %} 48 | 49 | ## Platform 50 | 51 | ### Description 52 | 53 | Platform is an enum representing the environment on which Alert Notification service is onboarded. 54 | 55 | ### Properties 56 | 57 | | Type | Available values | 58 | |:-----:|:----------------:| 59 | |string | CF | 60 | |string | NEO | 61 | 62 | ### @Example 63 | 64 | ```js 65 | import { RegionUtils } from '@sap_oss/alert-notification-client'; 66 | 67 | console.log(RegionUtils.Platform.CF); // will print 'CF' 68 | console.log(RegionUtils.Platform.NEO); // will print 'NEO' 69 | ``` 70 | 71 | ## Constants 72 | 73 | ### Description 74 | 75 | RegionUtils contains exported constants which represent predefined regions, on which Alert Notification service is onboarded. 76 | 77 | ### Available Constants 78 | 79 | | Constants | 80 | |:---------:| 81 | | AE1 | 82 | | AP1 | 83 | | AP2 | 84 | | BR1 | 85 | | CA1 | 86 | | CA2 | 87 | | CN1 | 88 | | RU1 | 89 | | SA1 | 90 | | EU1 | 91 | | EU2 | 92 | | EU3 | 93 | | US1 | 94 | | US2 | 95 | | US3 | 96 | | US4 | 97 | | JP1 | 98 | | EU10 | 99 | | EU20 | 100 | | AP10 | 101 | | BR10 | 102 | | CA10 | 103 | | AP11 | 104 | | US20 | 105 | | US21 | 106 | | AP21 | 107 | | JP20 | 108 | | US10 | 109 | | JP10 | 110 | | NEO_ROT | 111 | | NEO_FRANKFURT | 112 | | NEO_AMSTERDAM | 113 | | NEO_ASHBURN | 114 | | NEO_CHANDLER | 115 | | NEO_STERLING | 116 | | NEO_COLORADO_SPRINGS | 117 | | NEO_TOKYO | 118 | | NEO_DUBAI | 119 | | NEO_SYDNEY | 120 | | NEO_SYDNEY_DR | 121 | | NEO_SAO_PAULO | 122 | | NEO_TORONTO | 123 | | NEO_TORONTO_DR | 124 | | NEO_RIYADH | 125 | | NEO_SHANGHAI | 126 | | NEO_MOSCOW | 127 | | CF_AWS_SYDNEY | 128 | | CF_AWS_SINGAPORE | 129 | | CF_AWS_SAO_PAULO | 130 | | CF_AWS_MONTREAL | 131 | | CF_AWS_FRANKFURT | 132 | | CF_AWS_TOKYO | 133 | | CF_AWS_US_EAST | 134 | | CF_AZURE_SINGAPORE | 135 | | CF_AZURE_NETHERLANDS | 136 | | CF_AZURE_TOKYO | 137 | | CF_AZURE_WA | 138 | | CF_AZURE_VA | 139 | 140 | ### @Example 141 | 142 | ```js 143 | import { RegionUtils } from '@sap_oss/alert-notification-client'; 144 | 145 | console.log(RegionUtils.EU10); // will print EU10 region instance 146 | ``` -------------------------------------------------------------------------------- /docs/common-objects/retry-configuration.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: default 3 | title: RetryConfig 4 | nav_order: 3 5 | parent: Common objects 6 | permalink: /common-objects/retry-configuration 7 | --- 8 | 9 | # RetryConfig 10 | {: .no_toc } 11 | 12 | ## Table of contents 13 | {: .no_toc .text-delta } 14 | 15 | 1. TOC 16 | {:toc} 17 | 18 | --- 19 | 20 | ## Description 21 | 22 | This object is used to setup a retry configuration for the Alert Notification service client. The logic behind is as simple as follows - if a request to Alert Notification service fails it will be retried until the _maxRetries_ are reached and each retry will be attempted after the given _retryBackoff_. 23 | 24 | ## Properties 25 | 26 | [comment]: <> For loop must remain on the lines, if changed table won't behave normally 27 | 28 | | Field | Type | Description | 29 | |:---:|:--:|:---------:| {% for field in site.data.retry-configuration.propertyFields %} 30 | | {{field.name}} | {{ field.type }} | {{field.description}} | {% endfor %} 31 | 32 | ## @Example 33 | 34 | ```js 35 | const retryConfiguration = { 36 | maxRetries: 5, 37 | retryBackoff: 2500 38 | }; 39 | 40 | // Maximum numbers of retry is 5 and the timeout between each retry is 2500 ms 41 | ``` -------------------------------------------------------------------------------- /docs/configuration-api/action.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: default 3 | title: Action 4 | parent: Configuration's API objects 5 | nav_order: 3 6 | permalink: /configuration-api-objects/action/ 7 | --- 8 | 9 | # Action 10 | {: .no_toc } 11 | 12 | ## Table of contents 13 | {: .no_toc .text-delta } 14 | 15 | 1. TOC 16 | {:toc} 17 | 18 | --- 19 | 20 | ## Description 21 | 22 | Action is an entity which represents the destination on which Alert Notification service will send a processed event, e.g. Email, Slack, Webhook, etc. 23 | 24 | ## Properties 25 | 26 | [comment]: <> For loop must remain on the lines, if changed table won't behave normally 27 | 28 | | Field | Type | Description | 29 | |:---:|:--:|:---------:| {% for field in site.data.action.propertyFields %} 30 | | {{field.name}} | {{ field.type }} | {{field.description}} | {% endfor %} 31 | 32 | 33 | _**@Example:**_ 34 | ```js 35 | import { State } from '@sap_oss/alert-notification-client'; 36 | 37 | const action = { 38 | name: 'to-my-email', 39 | type: 'EMAIL', 40 | description: 'send to my mail', 41 | state: State.ENABLED, 42 | properties: { 43 | destination: '@.com' 44 | } 45 | }; 46 | ``` -------------------------------------------------------------------------------- /docs/configuration-api/condition.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: default 3 | title: Condition 4 | parent: Configuration's API objects 5 | nav_order: 4 6 | permalink: /configuration-api-objects/condition/ 7 | --- 8 | 9 | # Condition 10 | {: .no_toc } 11 | 12 | ## Table of contents 13 | {: .no_toc .text-delta } 14 | 15 | 1. TOC 16 | {:toc} 17 | 18 | --- 19 | 20 | ## Description 21 | 22 | Condition is an entity which defines a condition in the context of the Alert Notification service which must be met in order for some Action to be triggered 23 | 24 | ## Properties 25 | 26 | [comment]: <> For loop must remain on the lines, if changed table won't behave normally 27 | 28 | | Field | Type | Description | 29 | |:---:|:--:|:---------:| {% for field in site.data.condition.propertyFields %} 30 | | {{field.name}} | {{ field.type }} | {{field.description}} | {% endfor %} 31 | 32 | 33 | ## Example 34 | ```js 35 | import { Predicate } from '@sap_oss/alert-notification-client'; 36 | 37 | const condition = { 38 | name: 'event-type-contains-HighCpu', 39 | description: 'Match events which body contains HighCpu', 40 | propertyKey: 'eventType', 41 | predicate: Predicate.CONTAINS, 42 | propertyValue: 'HighCpu' 43 | }; 44 | ``` 45 | ## Associated types 46 | 47 | ### Predicate 48 | 49 | #### Description 50 | 51 | Predicate is an enum value representing a matching criteria for a condition. 52 | 53 | #### Properties 54 | 55 | | Type | Available values | 56 | |:------:|:-----------------:| 57 | | string | CONTAINS | 58 | | string | DOES_NOT_CONTAIN | 59 | | string | STARTS_WITH | 60 | | string |DOES_NOT_START_WITH| 61 | | string | ENDS_WITH | 62 | | string | DOES_NOT_END_WITH | 63 | | string | EQUALS | 64 | | string | NOT_EQUALS | 65 | | string | ANY | 66 | | string | LESS_THAN | 67 | | string | NO_VALUE | 68 | | string | GREATHER_THAN | 69 | | string | NUMBER_EQUALS | 70 | 71 | #### Example 72 | ```js 73 | import { Predicate } from '@sap_oss/alert-notification-client'; 74 | 75 | console.log(Predicate.CONTAINS); // will print 'CONTAINS' 76 | ``` -------------------------------------------------------------------------------- /docs/configuration-api/configuration.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: default 3 | title: Configuration 4 | parent: Configuration's API objects 5 | nav_order: 6 6 | permalink: /configuration-api-objects/configuration/ 7 | --- 8 | 9 | # Configuration 10 | {: .no_toc } 11 | 12 | ## Table of contents 13 | {: .no_toc .text-delta } 14 | 15 | 1. TOC 16 | {:toc} 17 | 18 | --- 19 | 20 | ## Description 21 | 22 | Configuration represents the assembly of actions, conditions and subscriptions 23 | 24 | ## Properties 25 | 26 | [comment]: <> For loop must remain on the lines, if changed table won't behave normally 27 | 28 | | Field | Type | Description | 29 | |:---:|:--:|:---------:| {% for field in site.data.configuration.propertyFields %} 30 | | {{field.name}} | {{ field.type }} | {{field.description}} | {% endfor %} 31 | 32 | 33 | _**@Example:**_ 34 | ```js 35 | import { State, Predicate } from '@sap_oss/alert-notification-client'; 36 | 37 | const configuration = 38 | { 39 | actions: [ 40 | { 41 | name: 'to-my-email', 42 | type: 'EMAIL', 43 | description: 'send to my mail', 44 | state: State.ENABLED, 45 | properties: { 46 | destination: '@.com' 47 | } 48 | } 49 | ], 50 | conditions: [ 51 | { 52 | name: 'event-type-contains-HighCpu', 53 | description: 'Match events which body contains HighCpu', 54 | propertyKey: 'eventType', 55 | predicate: Predicate.CONTAINS, 56 | propertyValue: 'HighCpu' 57 | } 58 | ], 59 | subscriptions: [ 60 | { 61 | name: 'event-with-eventType-HighCpu-to-mail', 62 | state: State.ENABLED, 63 | actions: ['to-my-email'], 64 | conditions: ['event-type-contains-HighCpu'], 65 | description: 'Subscription will act when an event with eventType - HighCpu is received and will send an email to me' 66 | } 67 | ] 68 | }; 69 | ``` -------------------------------------------------------------------------------- /docs/configuration-api/entity-type.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: default 3 | title: EntityType 4 | parent: Configuration's API objects 5 | nav_order: 2 6 | permalink: /configuration-api-objects/entity-type/ 7 | --- 8 | 9 | # EntityType 10 | {: .no_toc } 11 | 12 | ## Table of contents 13 | {: .no_toc .text-delta } 14 | 15 | 1. TOC 16 | {:toc} 17 | 18 | --- 19 | 20 | ## Description 21 | 22 | EntityType is an enum which helps deduce what type of entity you are trying to manage. 23 | 24 | ## Properties 25 | 26 | | Type | Available values | 27 | |:-----:|:----------------:| 28 | |string | ACTION | 29 | |string | CONDITION | 30 | |string | SUBSCRIPTION | 31 | 32 | ## @Example 33 | 34 | ```js 35 | import { EntityType } from '@sap_oss/alert-notification-client'; 36 | 37 | console.log(EntityType.ACTION); // will print 'ACTION' 38 | console.log(EntityType.CONDITION); // will print 'CONDITION' 39 | console.log(EntityType.SUBSCRIPTION); // will print 'SUBSCRIPTION' 40 | ``` 41 | -------------------------------------------------------------------------------- /docs/configuration-api/objects-overview.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: default 3 | title: Configuration's API objects 4 | has_toc: true 5 | has_children: true 6 | nav_order: 4 7 | permalink: /configuration-api-objects/ 8 | --- 9 | 10 | # Configuration API overview 11 | 12 | Alert Notification service Configuration API is responsible for managing customer's configuration. 13 | 14 | ## Configuration API's objects 15 | 16 | In this section you will learn about the objects which have to be used in order to manage your configuration. -------------------------------------------------------------------------------- /docs/configuration-api/page-response.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: default 3 | title: PageResponse 4 | parent: Configuration's API objects 5 | nav_order: 7 6 | permalink: /configuration-api-objects/page-response/ 7 | --- 8 | 9 | # PageResponse 10 | {: .no_toc } 11 | 12 | ## Table of contents 13 | {: .no_toc .text-delta } 14 | 15 | 1. TOC 16 | {:toc} 17 | 18 | --- 19 | 20 | 21 | ## Description 22 | 23 | Paged response is a common object for the Configuration API returned when searching for all entities of a given type. 24 | 25 | ## Constructor properties 26 | 27 | [comment]: <> For loop must remain on the lines, if changed table won't behave normally 28 | 29 | | Field | Type | Description | 30 | |:---:|:--:|:---------:| {% for field in site.data.page-response.propertyFields %} 31 | | {{field.name}} | {{ field.type }} | {{field.description}} | {% endfor %} 32 | 33 | ## @Example 34 | 35 | ```js 36 | const pagedResponse = { 37 | results: [], 38 | page: 0, 39 | pageSize: 100, 40 | totalPages: 1, 41 | totalResultsCount: 0 42 | } 43 | ``` -------------------------------------------------------------------------------- /docs/configuration-api/state.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: default 3 | title: State 4 | parent: Configuration's API objects 5 | nav_order: 2 6 | permalink: /configuration-api-objects/state/ 7 | --- 8 | 9 | # State 10 | {: .no_toc } 11 | 12 | ## Table of contents 13 | {: .no_toc .text-delta } 14 | 15 | 1. TOC 16 | {:toc} 17 | 18 | --- 19 | 20 | ## Description 21 | 22 | State is an enum holding a value for enablement and disablement of an entity 23 | 24 | ## Properties 25 | 26 | | Type | Available values | 27 | |:------:|:----------------:| 28 | | string | ENABLED | 29 | | string | DISABLED | 30 | 31 | ## Example 32 | 33 | ```js 34 | import { State } from '@sap_oss/alert-notification-client'; 35 | 36 | console.log(State.ENABLED); // will print ENABLED 37 | console.log(State.DISABLED); // will print DISABLED 38 | ``` 39 | -------------------------------------------------------------------------------- /docs/configuration-api/subscription.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: default 3 | title: Subscription 4 | parent: Configuration's API objects 5 | nav_order: 5 6 | permalink: /configuration-api-objects/subscription/ 7 | --- 8 | 9 | # Subscription 10 | {: .no_toc } 11 | 12 | ## Table of contents 13 | {: .no_toc .text-delta } 14 | 15 | 1. TOC 16 | {:toc} 17 | 18 | --- 19 | 20 | ## Description 21 | 22 | Subscription is the aggregation of actions and conditions. When an event is received in Alert Notification service, it will only be processed if a subscription exists and the matching criteria of the conditions are fulfilled. 23 | 24 | ## Properties 25 | 26 | [comment]: <> For loop must remain on the lines, if changed table won't behave normally 27 | 28 | | Field | Type | Description | 29 | |:---:|:--:|:---------:| {% for field in site.data.subscription.propertyFields %} 30 | | {{field.name}} | {{ field.type }} | {{field.description}} | {% endfor %} 31 | 32 | 33 | ## Example 34 | ```js 35 | import { State } from '@sap_oss/alert-notification-client'; 36 | 37 | const subscription = { 38 | name: 'event-with-eventType-HighCpu-to-mail', 39 | state: State.ENABLED, 40 | actions: ['to-my-email'], 41 | conditions: ['event-type-contains-HighCpu'], 42 | description: 'Subscription will act when an event with eventType - HighCpu is received and will send an email to me' 43 | }; 44 | ``` -------------------------------------------------------------------------------- /docs/event-api-objects/affected-resource.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: default 3 | title: AffectedResource 4 | parent: Event API's objects 5 | nav_order: 3 6 | permalink: /event-api-objects/affected-resource 7 | --- 8 | 9 | # AffectedResource 10 | {: .no_toc } 11 | 12 | ## Table of contents 13 | {: .no_toc .text-delta } 14 | 15 | 1. TOC 16 | {:toc} 17 | 18 | --- 19 | 20 | ## Description 21 | 22 | Represents your resource. 23 | 24 | ## Properties 25 | 26 | [comment]: <> For loop must remain on the lines, if changed table won't behave normally 27 | 28 | | Field | Type | Description | 29 | |:---:|:--:|:---------:| {% for field in site.data.affected-resource.propertyFields %} 30 | | {{field.name}} | {{ field.type }} | {{field.description}} | {% endfor %} 31 | 32 | 33 | ## @Example 34 | ```js 35 | const affectedResource = { 36 | resourceName: 'test-resource', 37 | resourceType: 'application', 38 | resourceInstance: '123456', 39 | tags: { 40 | detailsLink: 'https://example.details.com' 41 | } 42 | }; 43 | ``` -------------------------------------------------------------------------------- /docs/event-api-objects/category.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: default 3 | title: Category 4 | parent: Event API's objects 5 | nav_order: 2 6 | permalink: /event-api-objects/category 7 | --- 8 | 9 | # Category 10 | {: .no_toc } 11 | 12 | ## Table of contents 13 | {: .no_toc .text-delta } 14 | 15 | 1. TOC 16 | {:toc} 17 | 18 | --- 19 | 20 | ## Description 21 | 22 | Represents the category of the event. 23 | 24 | ## Properties 25 | 26 | | Type | Available values | 27 | |:-----:|:----------------:| 28 | |string | ALERT | 29 | |string | EXCEPTION | 30 | |string | NOTIFICATION | 31 | 32 | ## @Example 33 | 34 | ```js 35 | import { Category } from '@sap_oss/alert-notification-client'; 36 | 37 | console.log(Category.ALERT); // will print 'ALERT' 38 | console.log(Category.EXCEPTION); // will print 'EXCEPTION' 39 | console.log(Category.NOTIFICATION); // will print 'NOTIFICATION' 40 | ``` 41 | -------------------------------------------------------------------------------- /docs/event-api-objects/consumer-event.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: default 3 | title: ConsumerEvent 4 | parent: Event API's objects 5 | nav_order: 5 6 | permalink: /event-api-objects/consumer-event 7 | --- 8 | 9 | # ConsumerEvent 10 | {: .no_toc } 11 | 12 | ## Table of contents 13 | {: .no_toc .text-delta } 14 | 15 | 1. TOC 16 | {:toc} 17 | 18 | --- 19 | 20 | ## Description 21 | 22 | Received when requesting Undelivered or Matched APIs. Represents a stored event which was either delivered or undelivered. To see which methods are returning such response see [here](/alert-notification-node-client/#alert-notification-client-api). 23 | 24 | **Note**: This object is returned on response from Alert Notification service Undelivered and Matched APIs 25 | 26 | ## Properties 27 | 28 | [comment]: <> For loop must remain on the lines, if changed table won't behave normally 29 | 30 | | Field | Type | Description | 31 | |:---:|:--:|:---------:| {% for field in site.data.consumer-event.propertyFields %} 32 | | {{field.name}} | {{ field.type }} | {{field.description}} | {% endfor %} 33 | 34 | 35 | _**@Example:**_ 36 | ```js 37 | import { Severity, Category } from '@sap_oss/alert-notification-client'; 38 | 39 | const consumerEvent = { 40 | id: 'matched-or-undelivered-event-id', 41 | body: 'Your test-resource has been', 42 | subject: 'test-resource exceeds cpu limits', 43 | eventType: 'HighCpu', 44 | severity: 'WARNING', 45 | category: 'ALERT, 46 | resource: { 47 | resourceName: 'test-resource', 48 | resourceType: 'application', 49 | resourceInstance: '123456', 50 | tags: { 51 | detailsLink: 'https://example.details.com' 52 | } 53 | }, 54 | eventTimestamp: 1602787032, 55 | priority: 1, 56 | metadata: { 57 | cacheTime: 1602788032, 58 | affectedActionId: 'to-store', 59 | deliveryStatus: 'MATCHED', 60 | failureReasons: [ 61 | { 62 | code: 400, 63 | reason: 'Bad request', 64 | timestamp: 1602788020 65 | } 66 | ] 67 | } 68 | }; 69 | ``` 70 | 71 | ### FailureReason 72 | 73 | #### Description 74 | 75 | Describes why the delivery of the event has failed. This property is only returned if you have specified the value _**FAILURE_REASON**_ in the _include_ query parameter. 76 | 77 | #### Properties 78 | 79 | | Field | Type | Description | 80 | |:---:|:--:|:---------:| {% for field in site.data.failure-reason.propertyFields %} 81 | | {{field.name}} | {{ field.type }} | {{field.description}} | {% endfor %} 82 | 83 | #### @Example 84 | 85 | ```js 86 | const failureReason = { 87 | code: 400, 88 | reason: 'Bad request', 89 | timestamp: 1602788020 90 | }; 91 | ``` 92 | 93 | ### DeliveryStatus 94 | 95 | #### Description 96 | 97 | An enum which shows the status of an event. If the event was not delivered its status will be _UNDELIVERED_, on the other hand if a _STORE_ action was delivered and saved the status of the event will be _MATCHED_. 98 | 99 | #### Properties 100 | 101 | | Type | Available values | 102 | |:-----:|:----------------:| 103 | |string | UNDELIVERED | 104 | |string | MATCHED | 105 | 106 | #### @Example 107 | 108 | ```js 109 | import { DeliveryStatus } from '@sap_oss/alert-notification-client'; 110 | 111 | console.log(DeliveryStatus.UNDELIVERED); // will print 'UNDELIVERED' 112 | console.log(DeliveryStatus.MATCHED); // will print 'MATCHED' 113 | ``` 114 | 115 | ### ConsumerEventMetadata 116 | 117 | #### Description 118 | 119 | Describes details regarding the undelivered/matched event, e.g. when it was stored in Alert Notification service, what is the delivery status, etc. 120 | 121 | #### Properties 122 | 123 | | Field | Type | Description | 124 | |:---:|:--:|:---------:| {% for field in site.data.consumer-event-metadata.propertyFields %} 125 | | {{field.name}} | {{ field.type }} | {{field.description}} | {% endfor %} 126 | 127 | #### @Example 128 | 129 | ```js 130 | const failureReason = { 131 | cacheTime: 1602788020, 132 | affectedActionId: 'action-name', 133 | deliveryStatus: 'UNDELIVERED', 134 | failuerReasons: [ 135 | { 136 | code: 400, 137 | reason: 'Bad request', 138 | timestamp: 1602788020 139 | } 140 | ] 141 | }; 142 | ``` -------------------------------------------------------------------------------- /docs/event-api-objects/consumer-paged-response.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: default 3 | title: ConsumerPagedResponse 4 | parent: Event API's objects 5 | nav_order: 6 6 | permalink: /event-api-objects/consumer-paged-response 7 | --- 8 | 9 | # ConsumerPagedResponse 10 | {: .no_toc } 11 | 12 | ## Table of contents 13 | {: .no_toc .text-delta } 14 | 15 | 1. TOC 16 | {:toc} 17 | 18 | --- 19 | 20 | ## Description 21 | 22 | The actual response object received when requesting from the Undelivered and Matched APIs. 23 | 24 | ## Properties 25 | 26 | [comment]: <> For loop must remain on the lines, if changed table won't behave normally 27 | 28 | | Field | Type | Description | 29 | |:---:|:--:|:---------:| {% for field in site.data.consumer-paged-response.propertyFields %} 30 | | {{field.name}} | {{ field.type }} | {{field.description}} | {% endfor %} 31 | 32 | ## @Example 33 | 34 | _**Note**_: This is the payload you will receive on response. 35 | 36 | ```js 37 | const consumerPagedResponse = { 38 | responseMetadata: { 39 | page: 0, 40 | pageSize: 100, 41 | totalPages: 1, 42 | totalResultsCount: 1 43 | }, 44 | results: [ 45 | { 46 | id: 'matched-or-undelivered-event-id', 47 | body: 'Your test-resource has been', 48 | subject: 'test-resource exceeds cpu limits', 49 | eventType: 'HighCpu', 50 | severity: 'WARNING', 51 | category: 'ALERT', 52 | resource: { 53 | resourceName: 'test-resource', 54 | resourceType: 'application' 55 | }, 56 | eventTimestamp: 1602787032, 57 | metadata: { 58 | cacheTime: 1602788032, 59 | affectedActionId: 'to-store', 60 | deliveryStatus: 'MATCHED' 61 | } 62 | } 63 | ] 64 | }; 65 | ``` -------------------------------------------------------------------------------- /docs/event-api-objects/consumer-query-parameters.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: default 3 | title: ConsumerQueryParameters 4 | parent: Event API's objects 5 | nav_order: 7 6 | permalink: /event-api-objects/consumer-query-parameters 7 | --- 8 | 9 | # ConsumerQueryParameters 10 | {: .no_toc } 11 | 12 | ## Table of contents 13 | {: .no_toc .text-delta } 14 | 15 | 1. TOC 16 | {:toc} 17 | 18 | --- 19 | 20 | ## Description 21 | 22 | Describes the query parameters which are passed to the request to Undelivered or Matched APIs. They help you with filtering the returned events by different identifiers and time period. 23 | 24 | ## Properties 25 | 26 | [comment]: <> For loop must remain on the lines, if changed table won't behave normally 27 | 28 | | Field | Type | Description | 29 | |:---:|:--:|:---------:| {% for field in site.data.consumer-query-parameters.propertyFields %} 30 | | {{field.name}} | {{ field.type }} | {{field.description}} | {% endfor %} -------------------------------------------------------------------------------- /docs/event-api-objects/objects-overview.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: default 3 | title: Event API's objects 4 | has_toc: true 5 | has_children: true 6 | nav_order: 5 7 | permalink: /event-api-objects 8 | --- 9 | 10 | # Event API overview 11 | 12 | Alert Notification service Event API is responsible for ingesting customer events for further processing. They will further be evaluated for any matching criteria regarding the received event and will be delivered accordingly. 13 | 14 | ## Event API's objects 15 | 16 | In this section you will learn about the objects which have to be used in order to send/get event/_s_. -------------------------------------------------------------------------------- /docs/event-api-objects/resource-event.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: default 3 | title: ResourceEvent 4 | parent: Event API's objects 5 | grand_parent: Event API 6 | nav_order: 4 7 | permalink: /event-api-objects/resource-event 8 | --- 9 | 10 | # ResourceEvent 11 | {: .no_toc } 12 | 13 | ## Table of contents 14 | {: .no_toc .text-delta } 15 | 16 | 1. TOC 17 | {:toc} 18 | 19 | --- 20 | 21 | ## Description 22 | 23 | Object to use when you want to ingest an event in Alert Notification to alert/notify about what is happening with a resource of yours. To see the methods which are using it check [here](/alert-notification-node-client/#alert-notification-client-api) 24 | 25 | ## Properties 26 | 27 | [comment]: <> For loop must remain on the lines, if changed table won't behave normally 28 | 29 | | Field | Type | Description | 30 | |:---:|:--:|:---------:| {% for field in site.data.resource-event.propertyFields %} 31 | | {{field.name}} | {{ field.type }} | {{field.description}} | {% endfor %} 32 | 33 | 34 | ## @Example 35 | ```js 36 | import { Severity, Category } from '@sap_oss/alert-notification-client'; 37 | 38 | const resourceEvent = { 39 | body: 'Your test-resource has been', 40 | subject: 'test-resource exceeds cpu limits', 41 | eventType: 'HighCpu', 42 | severity: Severity.WARNING, 43 | category: Category.ALERT, 44 | resource: { 45 | resourceName: 'test-resource', 46 | resourceType: 'application', 47 | resourceInstance: '123456', 48 | tags: { 49 | detailsLink: 'https://example.details.com' 50 | } 51 | }, 52 | eventTimestamp: 1602787032, 53 | priority: 1 54 | }; 55 | ``` -------------------------------------------------------------------------------- /docs/event-api-objects/severity.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: default 3 | title: Severity 4 | parent: Event API's objects 5 | grand_parent: Event API 6 | nav_order: 1 7 | permalink: /event-api-objects/severity 8 | --- 9 | 10 | # Severity 11 | {: .no_toc } 12 | 13 | ## Table of contents 14 | {: .no_toc .text-delta } 15 | 16 | 1. TOC 17 | {:toc} 18 | 19 | --- 20 | 21 | ## Description 22 | 23 | Represents the event impact in the context of the affected resource. 24 | 25 | ## Properties 26 | 27 | | Type | Available values | 28 | |:-----:|:----------------:| 29 | |string | INFO | 30 | |string | FATAL | 31 | |string | ERROR | 32 | |string | NOTICE | 33 | |string | WARNING | 34 | 35 | ## @Example 36 | 37 | ```js 38 | import { Severity } from '@sap_oss/alert-notification-client'; 39 | 40 | console.log(Severity.NOTICE); // will print 'NOTICE' 41 | ``` 42 | -------------------------------------------------------------------------------- /docs/examples/create-and-get.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: default 3 | title: Create and get an entity 4 | parent: Examples 5 | nav_order: 1 6 | permalink: /examples/create-and-get/ 7 | --- 8 | 9 | # Create an action and get it afterwards 10 | {: .no_toc } 11 | 12 | ## Table of contents 13 | {: .no_toc .text-delta } 14 | 15 | 1. TOC 16 | {:toc} 17 | 18 | --- 19 | 20 | ## Description 21 | 22 | In this example you will create an action and get it after its been created. 23 | 24 | _**Note:**_ This can be applied to any other [EntityType](../configuration-api/entity-type.md). You just need to follow the method signatures from [here](/alert-notification-node-client/#alert-notification-client-api). 25 | 26 | ## @Example 27 | 28 | In order for this example to work you need to replace _username_, _password_, _region_ and _destination_ with ones specific for you. 29 | 30 | ```js 31 | import { 32 | AlertNotificationClient, 33 | EntityType, 34 | BasicAuthentication, 35 | RegionUtils, 36 | State 37 | } from '@sap_oss/alert-notification-client'; 38 | 39 | const client = new AlertNotificationClient({ 40 | authentication: new BasicAuthentication({ 41 | username: '', 42 | password: '@.com' 54 | } 55 | }) 56 | .then(action => { 57 | client.get(EntityType.Action, action.name) 58 | .then(action => console.log(action)) // Action you have created 59 | .catch(error => console.log(error)); 60 | }) 61 | .catch(error => console.log(error)); // Shouldn't happen if everything above is setup correctly 62 | ``` -------------------------------------------------------------------------------- /docs/examples/exporting-and-importing-config.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: default 3 | title: Export and import configuration 4 | parent: Examples 5 | nav_order: 3 6 | permalink: /examples/export-and-import/ 7 | --- 8 | 9 | # Export and import configuration in a different Alert Notification service instance 10 | {: .no_toc } 11 | 12 | ## Table of contents 13 | {: .no_toc .text-delta } 14 | 15 | 1. TOC 16 | {:toc} 17 | 18 | --- 19 | 20 | ## Description 21 | 22 | In this example you will export a configuration from one instance of Alert Notification service and import it in a brand new one recently created on newly onboarded region. 23 | 24 | _**Note:**_ Keep in mind that Alert Notification service will never return actions' passwords and you will have to traverse the returned json and populate them yourself, of course in a secure manner. 25 | 26 | ## @Example 27 | 28 | In order for this example to work you need to replace _username_, _password_ and _region_ with ones specific for you. 29 | 30 | ```js 31 | import { 32 | AlertNotificationClient, 33 | EntityType, 34 | BasicAuthentication, 35 | RegionUtils 36 | } from '@sap_oss/alert-notification-client'; 37 | 38 | const eu10Client = new AlertNotificationClient({ 39 | authentication: new BasicAuthentication({ 40 | username: '', 41 | password: '', 49 | password: ' { 56 | // ... 57 | // Any modifications of the exported configuration should happen here. 58 | // ... 59 | us10Client.importConfiguration(configuration) 60 | .then(action => console.log(action)) // Configuration is successfully imported 61 | .catch(error => console.log(error)); 62 | }) 63 | .catch(error => console.log(error)); // Shouldn't happen if everything above is setup correctly 64 | ``` -------------------------------------------------------------------------------- /docs/examples/import-configuration-and-send.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: default 3 | title: Import configuration and send a test event 4 | parent: Examples 5 | nav_order: 2 6 | permalink: /examples/import-configuration-and-send/ 7 | --- 8 | 9 | # Import configuration and send a test event 10 | {: .no_toc } 11 | 12 | ## Table of contents 13 | {: .no_toc .text-delta } 14 | 15 | 1. TOC 16 | {:toc} 17 | 18 | --- 19 | 20 | ## Description 21 | 22 | In this example you will import a configuration and send an event with _eventType_ HighCpu, which will be matched by the imported configuration. 23 | 24 | ## @Example 25 | 26 | In order for this example to work you need to replace _username_, _password_, _region_ and _destination_ with ones specific for you. 27 | 28 | ```js 29 | import { 30 | AlertNotificationClient, 31 | EntityType, 32 | BasicAuthentication, 33 | RegionUtils, 34 | Severity, 35 | Category, 36 | State, 37 | Predicate 38 | } from '@sap_oss/alert-notification-client'; 39 | 40 | const client = new AlertNotificationClient({ 41 | authentication: new BasicAuthentication({ 42 | username: '', 43 | password: '@.com' 57 | } 58 | } 59 | ], 60 | conditions: [ 61 | { 62 | name: 'event-type-contains-HighCpu', 63 | description: 'Match events which body contains HighCpu', 64 | propertyKey: 'eventType', 65 | predicate: Predicate.CONTAINS, 66 | propertyValue: 'HighCpu' 67 | } 68 | ], 69 | subscriptions: [ 70 | { 71 | name: 'event-with-eventType-HighCpu-to-mail', 72 | state: State.ENABLED, 73 | actions: ['to-my-email'], 74 | conditions: ['event-type-contains-HighCpu'], 75 | description: 'Subscription will act when an event with eventType - HighCpu is received and will send an email to me' 76 | } 77 | ] 78 | }) 79 | .then(_configuration => { 80 | client.sendEvent({ 81 | body: 'Your test-resource has exceeed the cpu limit', 82 | subject: 'test-resource exceeds cpu limits', 83 | eventType: 'HighCpu', 84 | severity: Severity.WARNING, 85 | category: Category.ALERT, 86 | resource: { 87 | resourceName: 'test-resource', 88 | resourceType: 'application', 89 | resourceInstance: '123456', 90 | tags: { 91 | detailsLink: 'https://example.details.com' 92 | } 93 | }, 94 | eventTimestamp: 1602787032, 95 | priority: 1 96 | }) 97 | .then(event => console.log(event)) // Ingested event 98 | .catch(error => console.log(error)); 99 | }) 100 | .catch(error => console.log(error)); // Shouldn't happen if everything above is setup correctly 101 | ``` -------------------------------------------------------------------------------- /docs/examples/overview.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: default 3 | title: Examples 4 | nav_order: 5 5 | has_children: true 6 | has_toc: true 7 | permalink: /examples/ 8 | --- 9 | 10 | ## Overview 11 | 12 | In this section we have provided some examples and tutorials on how to setup and use the Alert Notification service node client. 13 | -------------------------------------------------------------------------------- /docs/examples/send-event-and-consume.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: default 3 | title: Send event and consume 4 | parent: Examples 5 | nav_order: 2 6 | permalink: /examples/send-event-and-consume/ 7 | --- 8 | 9 | # Send event and consume from Store 10 | {: .no_toc } 11 | 12 | ## Table of contents 13 | {: .no_toc .text-delta } 14 | 15 | 1. TOC 16 | {:toc} 17 | 18 | --- 19 | 20 | ## Description 21 | 22 | In this example send an event with _eventType_ HighCpu, which will be matched by the imported configuration, then you will consume the stored event and print it. But firstly you will have to import a configuration (or create a new one if you would like). 23 | 24 | ## @Example 25 | 26 | In order for this example to work you need to replace _username_, _password_ and _region_ with ones specific for you. 27 | 28 | ```js 29 | import { 30 | AlertNotificationClient, 31 | EntityType, 32 | BasicAuthentication, 33 | RegionUtils, 34 | State, 35 | Predicate 36 | } from '@sap_oss/alert-notification-client'; 37 | 38 | const client = new AlertNotificationClient({ 39 | authentication: new BasicAuthentication({ 40 | username: '', 41 | password: ' { 75 | client.sendEvent({ 76 | body: 'Your test-resource has been', 77 | subject: 'test-resource exceeds cpu limits', 78 | eventType: 'consumeInStore', 79 | severity: Severity.WARNING, 80 | category: Category.ALERT, 81 | resource: { 82 | resourceName: 'test-resource', 83 | resourceType: 'application', 84 | resourceInstance: '123456', 85 | tags: { 86 | detailsLink: 'https://example.details.com' 87 | } 88 | }, 89 | eventTimestamp: 1602787032, 90 | priority: 1 91 | }) 92 | .then(event => { 93 | // Wait for the event to be processed and stored, in most of the cases it will take less time than 5 seconds, but just to be on the safe side 94 | setTimeout(() => { 95 | client.getMatchedEvent(event.id) 96 | .then(consumedEvent => console.log(consumedEvent)) // Print the consumed event 97 | .catch(error => console.log(error)); 98 | }, 5000) 99 | }) 100 | .catch(error => console.log(error)); 101 | }) 102 | .catch(error => console.log(error)); // Shouldn't happen if everything above is setup correctly 103 | ``` -------------------------------------------------------------------------------- /docs/examples/setup-authentication.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: default 3 | title: Setting up authentication 4 | parent: Examples 5 | nav_order: 1 6 | permalink: /examples/setup-authentication/ 7 | --- 8 | 9 | # Setting up an authentication for your client 10 | {: .no_toc } 11 | 12 | ## Table of contents 13 | {: .no_toc .text-delta } 14 | 15 | 1. TOC 16 | {:toc} 17 | 18 | --- 19 | 20 | ## Description 21 | 22 | In this example you will get familiar on how to setup an authentication object for your Alert Notification service client. The setup is nothing more than just providing the authentication object of your choice as an argument to your client instance's constructor 23 | 24 | ## @Example - basic authentication 25 | 26 | You must replace _username_, _password_ and _region_ with ones specific for you. 27 | 28 | ```js 29 | import { 30 | AlertNotificationClient, 31 | BasicAuthentication, 32 | RegionUtils 33 | } from '@sap_oss/alert-notification-client'; 34 | 35 | const client = new AlertNotificationClient({ 36 | authentication: new BasicAuthentication({ 37 | username: '', 38 | password: '', 62 | password: '' 64 | }), 65 | region: RegionUtils.EU10; 66 | }); 67 | 68 | // If you have setup correct credentials for your authentication you 69 | // will be able to access the endpoints to which your technical client has scopes to 70 | ``` -------------------------------------------------------------------------------- /docs/examples/setup-retriability.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: default 3 | title: Setup retriability 4 | parent: Examples 5 | nav_order: 1 6 | permalink: /examples/setup-retriability/ 7 | --- 8 | 9 | # Setup retriability 10 | {: .no_toc } 11 | 12 | ## Table of contents 13 | {: .no_toc .text-delta } 14 | 15 | 1. TOC 16 | {:toc} 17 | 18 | --- 19 | 20 | ## Description 21 | 22 | In order to setup retriability you will have to provide to the constructor a [RetryConfig](../common-objects/retry-configuration.md) object. 23 | 24 | ## @Example 25 | 26 | You must replace _username_, _password_ and _region_ with ones specific for you. 27 | 28 | ```js 29 | import { 30 | AlertNotificationClient, 31 | BasicAuthentication, 32 | RegionUtils 33 | } from '@sap_oss/alert-notification-client'; 34 | 35 | const client = new AlertNotificationClient({ 36 | authentication: new BasicAuthentication({ 37 | username: '', 38 | password: '', // Replace with your username 53 | password: '' // Replace with your password 54 | }), 55 | region: RegionUtils.EU10 // Choose your region 56 | }); 57 | ``` 58 | 59 | ## Resources 60 | 61 | * [Configuration management by using the SAP Business Technology Platform cockpit](https://help.sap.com/viewer/5967a369d4b74f7a9c2b91f5df8e6ab6/Cloud/en-US/033cbf7cfab2484abad90276d3d3e776.html) 62 | * [Credential management](https://help.sap.com/viewer/5967a369d4b74f7a9c2b91f5df8e6ab6/Cloud/en-US/80fe24f86bde4e3aac2903ac05511835.html) 63 | * [Catalog of available events](https://help.sap.com/viewer/5967a369d4b74f7a9c2b91f5df8e6ab6/Cloud/en-US/80fe24f86bde4e3aac2903ac05511835.html) 64 | * [Integration with Alert Notification service](https://help.sap.com/viewer/5967a369d4b74f7a9c2b91f5df8e6ab6/Cloud/en-US/04c9ed027b824e93896f59c4081a704a.html) 65 | * [Undelivered event troubleshooting](https://help.sap.com/viewer/5967a369d4b74f7a9c2b91f5df8e6ab6/Cloud/en-US/7272271fb0a74c2db22b03dbaa48546f.html) 66 | * _SAP API Business Hub related resources_ 67 | * [CloudFoundry Configuration API](https://api.sap.com/api/cf_configuration_api/resource) 68 | * [NEO Configuration API](https://api.sap.com/api/neo_configuration_api/resource) 69 | * [CloudFoundry Event Producer API](https://api.sap.com/api/cf_producer_api/resource) 70 | * [NEO Event Producer API](https://api.sap.com/api/neo_producer_api/resource) 71 | * [CloudFoundry Consumer API](https://api.sap.com/api/cf_consumer_api/resource) 72 | * [NEO Consumer API](https://api.sap.com/api/neo_consumer_api/resource) 73 | 74 | # Alert Notification service Client API 75 | 76 | ## Constructor 77 | 78 | [comment]: <> For loop must remain on the lines, if changed table won't behave normally 79 | 80 | | Field | Type | Description | 81 | |:---:|:--:|:---------:| {% for field in site.data.alert-notification-client.propertyFields %} 82 | | {{field.name}} | {{ field.type }} | {{field.description}} | {% endfor %} 83 | 84 | ## Methods 85 | 86 | {% for method in site.data.alert-notification-client.methods %} 87 | 88 | ### {{method.name}} 89 | 90 | * _**Description:**_ 91 | 92 | * {{ method.description }} 93 | 94 | * _**Arguments:**_ 95 | 96 | {% for argument in method.arguments %} 97 | * {{argument.name}} - {{argument.description}} 98 | {% endfor %} 99 | 100 | * _**Return value:**_ 101 | 102 | * {{method.returnValue}} 103 | 104 | {% endfor %} 105 | 106 | For examples regarding the client, please check [Examples](./examples/overview.md) section. -------------------------------------------------------------------------------- /jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | clearMocks: true, 3 | coverageDirectory: "coverage", 4 | coverageProvider: "v8", 5 | coverageThreshold: { 6 | global: { 7 | branches: 80, 8 | functions: 80, 9 | lines: 80, 10 | statements: 80 11 | } 12 | }, 13 | roots: [ 14 | "" 15 | ], 16 | preset: 'ts-jest', 17 | globals: { 18 | 'ts-jest': { 19 | tsconfig: './__tests__/tsconfig.test.json' 20 | } 21 | }, 22 | testEnvironment: "node", 23 | testMatch: [ 24 | "**/?(*.)+(spec|test).[tj]s?(x)" 25 | ], 26 | testPathIgnorePatterns: [ 27 | "/dist/", 28 | "/node_modules/" 29 | ], 30 | transform: { 31 | "^.+\\.(ts|tsx)$": "ts-jest", 32 | "^.+\\.[t|j]sx?$": "babel-jest" 33 | }, 34 | transformIgnorePatterns: ['/node_modules/(?!axios)'], 35 | }; 36 | -------------------------------------------------------------------------------- /jks-js.d.ts: -------------------------------------------------------------------------------- 1 | declare module 'jks-js' { 2 | export function toPem(encodedKeystore: Buffer, password: string): any; 3 | } 4 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@sap_oss/alert-notification-client", 3 | "version": "1.10.0", 4 | "description": "SAP Alert Notification service for SAP BTP Node.js API client", 5 | "main": "dist/index.js", 6 | "types": "dist/index.d.ts", 7 | "scripts": { 8 | "test": "jest", 9 | "test:watch": "jest --watch", 10 | "prepublishOnly": "npm run test && npm run lint && tsc", 11 | "lint": "eslint --ext .ts src/**", 12 | "lint:fix": "eslint --ext .ts --fix src/**" 13 | }, 14 | "author": "SAP Alert Notification service for SAP BTP Development Team", 15 | "email": "sap.cloud.platform.alert.notification@groups.sap.com", 16 | "license": "Apache-2.0", 17 | "repository": { 18 | "type": "git", 19 | "url": "https://github.com/SAP/alert-notification-node-client" 20 | }, 21 | "keywords": [ 22 | "alerts", 23 | "notifications", 24 | "client", 25 | "promise", 26 | "node" 27 | ], 28 | "husky": { 29 | "hooks": { 30 | "pre-commit": "lint-staged" 31 | } 32 | }, 33 | "dependencies": { 34 | "axios": ">=1.6.0", 35 | "jks-js": "^1.1.0", 36 | "follow-redirects": ">=1.15.6" 37 | }, 38 | "devDependencies": { 39 | "@babel/preset-env": "^7.18.10", 40 | "@types/jest": "^26.0.14", 41 | "@types/node": "^14.18.63", 42 | "@typescript-eslint/eslint-plugin": "^6.20.0", 43 | "@typescript-eslint/parser": "^6.20.0", 44 | "eslint": "^8.56.0", 45 | "eslint-config-google": "^0.14.0", 46 | "eslint-config-prettier": "^9.1.0", 47 | "eslint-plugin-import": "^2.29.1", 48 | "eslint-plugin-prettier": "^5.1.3", 49 | "husky": "^8.0.1", 50 | "jest": "^29.7.0", 51 | "lint-staged": "^10.4.2", 52 | "prettier": "^3.2.4", 53 | "semver-regex": "^3.1.4", 54 | "ts-jest": "^29.1.4", 55 | "tslint": "^6.1.3", 56 | "typescript": "^4.9.4" 57 | }, 58 | "lint-staged": { 59 | "*.ts": "eslint --cache --fix" 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /src/authentication.ts: -------------------------------------------------------------------------------- 1 | import Axios, { AxiosInstance } from 'axios'; 2 | import { configureDefaultRetryInterceptor } from './utils/axios-utils'; 3 | import * as https from 'https'; 4 | 5 | export interface Credentials { 6 | /** 7 | * Username 8 | */ 9 | username: string; 10 | /** 11 | * Password 12 | */ 13 | password: string; 14 | } 15 | 16 | export interface OAuthConfig { 17 | /** 18 | * Username 19 | */ 20 | username: string; 21 | /** 22 | * Password 23 | */ 24 | password?: string; 25 | /** 26 | * Certificate 27 | */ 28 | certificate?: string; 29 | /** 30 | * Private key 31 | */ 32 | privateKey?: string; 33 | /** 34 | * OAuth token url to call on retrieving token 35 | */ 36 | oAuthTokenUrl: string; 37 | } 38 | 39 | export interface CertificateConfig { 40 | /** 41 | * Certificate 42 | */ 43 | certificate: string; 44 | /** 45 | * Private key 46 | */ 47 | privateKey: string; 48 | } 49 | 50 | export interface Authentication { 51 | getAuthorizationHeaderValue(): Promise; 52 | } 53 | 54 | /** 55 | * Basic Authentication class. Retrieves the basic authorization header value. 56 | */ 57 | export class BasicAuthentication implements Authentication { 58 | private basicAuthorizationHeader: string; 59 | 60 | /** 61 | * Creates an instance of BasicAuthentication 62 | * 63 | * @param {Credentials} creds - object which contains the username and password. Username and password 64 | * must be provided else an Error will be thrown. 65 | */ 66 | constructor(creds: Credentials) { 67 | if (!creds) { 68 | throw new Error('Credentials must not be null or undefined'); 69 | } 70 | 71 | if (!creds.username && !creds.password) { 72 | throw new Error('Username and password must be provided'); 73 | } 74 | 75 | this.basicAuthorizationHeader = Buffer.from(`${creds.username}:${creds.password}`).toString( 76 | 'base64' 77 | ); 78 | } 79 | 80 | /** 81 | * Get basic authorization header value 82 | * 83 | * @return {Promise} - promise containing the basic authorization header value 84 | */ 85 | getAuthorizationHeaderValue(): Promise { 86 | return Promise.resolve(`Basic ${this.basicAuthorizationHeader}`); 87 | } 88 | } 89 | 90 | /** 91 | * OAuth Authentication class. Retrieves the OAuth authorization header value. 92 | */ 93 | export class OAuthAuthentication implements Authentication { 94 | private static readonly EXPIRES_IN_KEY = 'expires_in'; 95 | private static readonly ACCESS_TOKEN_KEY = 'access_token'; 96 | private static readonly TIME_DELTA_IN_MILLIS = 60000; 97 | private static readonly HTTP_CLIENT_TIMEOUT = 5000; 98 | private static readonly APPLICATION_X_WWW_FORM_URLENCODED_CONTENT_TYPE = 99 | 'application/x-www-form-urlencoded'; 100 | private static readonly CLIENT_CREDENTIALS = 'client_credentials'; 101 | 102 | private accessToken: string; 103 | private expiresIn: number; 104 | private axiosInstance: AxiosInstance; 105 | 106 | /** 107 | * Creates an instance of OAuthAuthentication. 108 | * 109 | * @param {OAuthConfig} config - contains username, password, oAuthTokenUrl and platform. All 110 | * of them must be provided else Error will be thrown. 111 | */ 112 | constructor(config: OAuthConfig) { 113 | if (!config) { 114 | throw new Error('OAuth configuration must be provided'); 115 | } 116 | 117 | if (!config.username) { 118 | throw new Error('Username must be provided'); 119 | } 120 | 121 | if (!config.oAuthTokenUrl) { 122 | throw new Error('OAuthTokenUrl is missing.'); 123 | } 124 | 125 | this.accessToken = ''; 126 | this.expiresIn = 0; 127 | let defaultConfig = { 128 | retryConfig: { 129 | maxRetries: 3, 130 | retryBackoff: 1000 131 | }, 132 | timeout: OAuthAuthentication.HTTP_CLIENT_TIMEOUT 133 | }; 134 | if (this.isCertificateAuthentication(config)) { 135 | defaultConfig = { 136 | ...defaultConfig, 137 | ...{ 138 | baseURL: config.oAuthTokenUrl, 139 | headers: { 140 | 'Content-Type': 141 | OAuthAuthentication.APPLICATION_X_WWW_FORM_URLENCODED_CONTENT_TYPE 142 | }, 143 | method: 'POST', 144 | params: { 145 | grant_type: OAuthAuthentication.CLIENT_CREDENTIALS, 146 | client_id: `${config.username}` 147 | }, 148 | httpsAgent: new https.Agent({ 149 | cert: config.certificate, 150 | key: config.privateKey 151 | }) 152 | } 153 | }; 154 | } else { 155 | if (!config.password) { 156 | throw new Error('Password is missing.'); 157 | } 158 | defaultConfig = { 159 | ...defaultConfig, 160 | ...{ 161 | auth: { 162 | username: config.username, 163 | password: config.password 164 | }, 165 | baseURL: config.oAuthTokenUrl, 166 | params: { 167 | grant_type: OAuthAuthentication.CLIENT_CREDENTIALS 168 | } 169 | } 170 | }; 171 | } 172 | // eslint-disable-next-line @typescript-eslint/ban-ts-comment 173 | // @ts-ignore 174 | this.axiosInstance = Axios.create(defaultConfig); 175 | configureDefaultRetryInterceptor(this.axiosInstance); 176 | } 177 | 178 | /** 179 | * Get OAuth authorization header value. If token is not present or is expired, the OAuth token url will 180 | * be called in order to get a new one or renew the old token. 181 | * 182 | * @return {Promise} - promise containing the OAuth authorization header value 183 | */ 184 | async getAuthorizationHeaderValue(): Promise { 185 | const isTokenAboutToExpire = () => { 186 | return OAuthAuthentication.TIME_DELTA_IN_MILLIS > this.expiresIn - Date.now(); 187 | }; 188 | 189 | if (this.accessToken === '' || isTokenAboutToExpire()) { 190 | return this.renewToken() 191 | .then((data) => { 192 | this.expiresIn = Date.now() + 1000 * data[OAuthAuthentication.EXPIRES_IN_KEY]; 193 | this.accessToken = data[OAuthAuthentication.ACCESS_TOKEN_KEY]; 194 | 195 | return Promise.resolve(`Bearer ${this.accessToken}`); 196 | }) 197 | .catch((error) => { 198 | return Promise.reject(error); 199 | }); 200 | } 201 | 202 | return Promise.resolve(`Bearer ${this.accessToken}`); 203 | } 204 | 205 | /* eslint-disable camelcase */ 206 | /** 207 | * Renew OAuth token. 208 | * 209 | * @return {Promise<{ expires_in: number, access_token: string }>} - promise containing the response from OAuth server 210 | */ 211 | private async renewToken(): Promise<{ expires_in: number; access_token: string }> { 212 | return new Promise((resolve, reject) => { 213 | this.axiosInstance 214 | .request({}) // No need to apply any other configuration 215 | .then((response) => { 216 | return resolve(response.data); 217 | }) 218 | .catch((error) => { 219 | return reject(error); 220 | }); 221 | }); 222 | } 223 | 224 | // eslint-disable-next-line require-jsdoc 225 | private isCertificateAuthentication(config: OAuthConfig): boolean { 226 | return !config.password && !!config.certificate && !!config.privateKey; 227 | } 228 | } 229 | 230 | /** 231 | * Certificate Authentication class. Provides the certificate and the private key for the building of the keystore. 232 | */ 233 | export class CertificateAuthentication { 234 | private certificate: string; 235 | private privateKey: string; 236 | /** 237 | * Creates an instance of CertificateServiceAuthentication 238 | * 239 | * @param {Credentials} creds - object which contains the certificate and private key. Certificate and privateKey 240 | * must be provided else an Error will be thrown. 241 | */ 242 | constructor(creds: CertificateConfig) { 243 | if (!creds || !creds.certificate || !creds.privateKey) { 244 | throw new Error( 245 | 'Both certificate and privateKey must be provided when using CertificateAuthentication' 246 | ); 247 | } 248 | this.certificate = creds.certificate; 249 | this.privateKey = creds.privateKey; 250 | } 251 | 252 | // eslint-disable-next-line require-jsdoc 253 | public getCertificate(): string { 254 | return this.certificate; 255 | } 256 | // eslint-disable-next-line require-jsdoc 257 | public getPrivateKey(): string { 258 | return this.privateKey; 259 | } 260 | } 261 | -------------------------------------------------------------------------------- /src/configuration-api/configuration-client.ts: -------------------------------------------------------------------------------- 1 | import { AxiosResponse, AxiosInstance, AxiosPromise } from 'axios'; 2 | 3 | import { PageResponse } from '../utils/common'; 4 | 5 | import { 6 | Action, 7 | Condition, 8 | Subscription, 9 | Configuration, 10 | ConfigurationRequest, 11 | EntityType 12 | } from './models'; 13 | 14 | /** 15 | * Class used to access SAP Alert Notification service for SAP BTP Configuration API 16 | */ 17 | export default class ConfigurationApiClient { 18 | private actionPath: string; 19 | private conditionPath: string; 20 | private subscriptionPath: string; 21 | private importPath: string; 22 | private exportPath: string; 23 | private axios: AxiosInstance; 24 | 25 | /** 26 | * Constructs an instance of ConfigurationApiClient 27 | * 28 | * 29 | * @param {string} platform - platform, which can be CloudFoundry or Neo, 30 | * used to decide which endpoints to call 31 | * @param {AxiosInstance} axios - http client used for making http requests 32 | * 33 | */ 34 | constructor(platform: string, axios: AxiosInstance) { 35 | this.actionPath = `/${platform}/configuration/v1/action`; 36 | this.conditionPath = `/${platform}/configuration/v1/condition`; 37 | this.subscriptionPath = `/${platform}/configuration/v1/subscription`; 38 | this.importPath = `/${platform}/configuration/v1/configuration`; 39 | this.exportPath = `/${platform}/configuration/v1/configuration`; 40 | this.axios = axios; 41 | } 42 | 43 | /** 44 | * Executes a request which if successful will return a paged response 45 | * of the entities for the given type 46 | * 47 | * @param {ConfigurationRequest} request - an object which contains all the needed data for this request 48 | * to be executed: 49 | * - {type} - value which helps deduce the path to call 50 | * - {params} - query parameters to append to the url 51 | * 52 | * @return {AxiosPromise>} promise, 53 | * which contains the paginated entities for the given type 54 | */ 55 | public getAll({ 56 | params = {}, 57 | ...others 58 | }: ConfigurationRequest): AxiosPromise> { 59 | return this.axios.request({ 60 | method: 'get', 61 | url: this.choosePath(others.type), 62 | params 63 | }); 64 | } 65 | 66 | /** 67 | * Executes a request which if successful will return the entity of 68 | * requested type and name 69 | * 70 | * @param {ConfigurationRequest} request - an object which contains all the needed data for this request 71 | * to be executed: 72 | * - {type} - value which helps deduce the path to call 73 | * - {name} - entity's identifier 74 | * 75 | * @return {AxiosPromise} promise, which contains the entity 76 | * for the given type 77 | */ 78 | public getOne({ 79 | name = '', 80 | ...others 81 | }: ConfigurationRequest): AxiosPromise { 82 | return this.axios.request({ 83 | url: `${this.choosePath(others.type)}/${name}`, 84 | method: 'get' 85 | }); 86 | } 87 | 88 | /** 89 | * Executes a request which if successful will create the given entity 90 | * 91 | * @param {ConfigurationRequest} request - an object which contains all the needed data for this request 92 | * to be executed. 93 | * 94 | * @return {AxiosPromise} promise, which contains the created 95 | * entity 96 | */ 97 | public create({ 98 | data = {} as Action, 99 | ...others 100 | }: ConfigurationRequest): AxiosPromise { 101 | return this.axios.request({ 102 | method: 'post', 103 | url: this.choosePath(others.type), 104 | data 105 | }); 106 | } 107 | 108 | /** 109 | * Executes a request which if successful will update the given entity 110 | * 111 | * @param {ConfigurationRequest} request - an object which contains all the needed data for this request 112 | * to be executed. 113 | * 114 | * @return {AxiosPromise} promise, which contains the updated 115 | * entity 116 | */ 117 | public update({ 118 | data = {} as Action, 119 | ...others 120 | }: ConfigurationRequest): AxiosPromise { 121 | return this.axios.request({ 122 | method: 'put', 123 | url: `${this.choosePath(others.type)}/${data.name}`, 124 | data 125 | }); 126 | } 127 | 128 | /** 129 | * Executes a request which if successful will delete the given entity 130 | * 131 | * @param {ConfigurationRequest} request - an object which contains all the needed data for this request 132 | * to be executed. 133 | * 134 | * @return {AxiosPromise>} promise, which contains nothing 135 | */ 136 | public delete({ 137 | name = '', 138 | ...others 139 | }: ConfigurationRequest): AxiosPromise> { 140 | return this.axios.request({ 141 | method: 'delete', 142 | url: `${this.choosePath(others.type)}/${name}` 143 | }); 144 | } 145 | 146 | /** 147 | * Executes a request which if successful will import the given configuration. 148 | * 149 | * NOTE: Importing a configuration overwrites any current configuration you have 150 | * 151 | * @param {Configuration} data - configuration object, which is an aggregation of actions, 152 | * conditions and subscriptions 153 | * 154 | * @return {AxiosPromise} promise, which contains the imported configuration 155 | */ 156 | public import(data: Configuration): AxiosPromise { 157 | return this.axios.request({ 158 | method: 'post', 159 | url: this.importPath, 160 | data 161 | }); 162 | } 163 | 164 | /** 165 | * Executes a request which if successful will export the current configuration. 166 | * 167 | * @return {AxiosPromise} promise, which contains the exported configuration 168 | */ 169 | public export(): AxiosPromise { 170 | return this.axios.request({ 171 | method: 'get', 172 | url: this.exportPath 173 | }); 174 | } 175 | 176 | /** 177 | * Decide which url path to use based on the given entity type 178 | * 179 | * @param {EntityType} entityType - type of the configuration entity 180 | * 181 | * @return {string} path to the entity 182 | */ 183 | private choosePath(entityType: EntityType): string { 184 | switch (entityType) { 185 | case EntityType.ACTION: 186 | return this.actionPath; 187 | case EntityType.CONDITION: 188 | return this.conditionPath; 189 | case EntityType.SUBSCRIPTION: 190 | return this.subscriptionPath; 191 | default: 192 | throw new Error('Unknown entity, must be a type from EntityType object'); 193 | } 194 | } 195 | } 196 | -------------------------------------------------------------------------------- /src/configuration-api/models.ts: -------------------------------------------------------------------------------- 1 | import { CommonQueryParams } from '../utils/common'; 2 | 3 | /** 4 | * @enum { 5 | * CONTAINS,DOES_NOT_CONTAIN, 6 | * STARTS_WITH, 7 | * DOES_NOT_START_WITH, 8 | * ENDS_WITH, 9 | * DOES_NOT_END_WITH, 10 | * EQUALS, 11 | * NOT_EQUALS, 12 | * ANY, 13 | * NO_VALUE, 14 | * LESS_THAN, 15 | * GREATHER_THAN, 16 | * NUMBER_EQUALS 17 | * } 18 | */ 19 | export enum Predicate { 20 | CONTAINS = 'CONTAINS', 21 | DOES_NOT_CONTAIN = 'DOES_NOT_CONTAIN', 22 | STARTS_WITH = 'STARTS_WITH', 23 | DOES_NOT_START_WITH = 'DOES_NOT_START_WITH', 24 | ENDS_WITH = 'ENDS_WITH', 25 | DOES_NOT_END_WITH = 'DOES_NOT_END_WITH', 26 | EQUALS = 'EQUALS', 27 | NOT_EQUALS = 'NOT_EQUALS', 28 | ANY = 'ANY', 29 | NO_VALUE = 'NO_VALUE', 30 | LESS_THAN = 'LESS_THAN', 31 | GREATER_THAN = 'GREATER_THAN', 32 | NUMBER_EQUALS = 'NUMBER_EQUALS' 33 | } 34 | 35 | /** 36 | * @enum {ENABLED, DISABLED} 37 | */ 38 | export enum State { 39 | ENABLED = 'ENABLED', 40 | DISABLED = 'DISABLED' 41 | } 42 | 43 | export interface Action { 44 | /** 45 | * Action's id which will be generated on its creation. 46 | */ 47 | id?: string; 48 | /** 49 | * Action's type, e.g. STORE, EMAIL, etc. 50 | */ 51 | type: string; 52 | /** 53 | * Unique name of an action, used for its identification. 54 | */ 55 | name: string; 56 | /** 57 | * Identifies the action's current state, that is, if it's currently enabled or disabled. 58 | */ 59 | state: State; 60 | /** 61 | * Brief description of an action, e.g. explaining what it will be used for. 62 | */ 63 | description?: string; 64 | /** 65 | * Representing meaningful identifiers, which enable custom displaying & filtering capabilities. 66 | */ 67 | labels?: string[]; 68 | /** 69 | * Action to fallback to if execution of current action fails. 70 | */ 71 | fallbackAction?: string; 72 | /** 73 | * Time in seconds to allow the current action to be retried before executing the fallback action. 74 | * If 0, undefined or null the action will be retried for its maximum times and if still fails, then 75 | * the fallback action will be executed. 76 | */ 77 | fallbackTime?: number; 78 | /** 79 | * Time in seconds to allow the current action to be retried before being discarded. 80 | * If 0, undefined or null the action will be retried for its maximum times. 81 | */ 82 | discardAfter?: number; 83 | /** 84 | * Choose to trigger delivery status event for each event matched for delivery through this action. 85 | * To receive this event, subscribe to it by referring to the Delivery Status event catalog topic. 86 | * The page can be found on the SAP Alert Notification service Help Portal documentation. 87 | */ 88 | enableDeliveryStatus?: boolean; 89 | /** 90 | * Action specific key-value pairs describing configuration properties. 91 | */ 92 | properties?: Record; 93 | } 94 | 95 | export interface Condition { 96 | /** 97 | * Condition's id which will be generated on its creation. 98 | */ 99 | id?: string; 100 | /** 101 | * Unique name of a condition, used for its identification. 102 | */ 103 | name: string; 104 | /** 105 | * Brief description of a condition, e.g. explaining what it will be used for. 106 | */ 107 | description?: string; 108 | /** 109 | * Property key of the event, e.g. eventType. 110 | */ 111 | propertyKey: string; 112 | /** 113 | * Predefined matching criteria. 114 | */ 115 | predicate: Predicate; 116 | /** 117 | * Value to be expected when matching the propertyKey with the given predicate. 118 | */ 119 | propertyValue?: string; 120 | /** 121 | * Representing meaningful identifiers, which enable custom displaying & filtering capabilities. 122 | */ 123 | labels?: string[]; 124 | /** 125 | * Explicitly set which conditions must be mandatory. If not set conditions will be treated as follows: 126 | * - Conditions with different property keys will be evaluated with the AND operator 127 | * - Conditions with the same property key will be evaluated with OR operator 128 | */ 129 | mandatory?: boolean; 130 | } 131 | 132 | export interface Subscription { 133 | /** 134 | * Subscription's id which will be generated on its creation. 135 | */ 136 | id?: string; 137 | /** 138 | * Unique name of a subscription, used for its identification. 139 | */ 140 | name: string; 141 | /** 142 | * Identifies the subscription's current state, that is, if it's currently enabled or disabled. 143 | */ 144 | state: State; 145 | /** 146 | * Unix timestamp format representing disablement of the subscription until the specified date and time. 147 | */ 148 | snoozeTimestamp?: number; 149 | /** 150 | * Brief description of a condition, e.g. explaining what it will be used for. 151 | */ 152 | description?: string; 153 | /** 154 | * Representing meaningful identifiers, which enable custom displaying & filtering capabilities. 155 | */ 156 | labels?: string[]; 157 | /** 158 | * Action names that are executed on any matched event. 159 | */ 160 | actions?: string[]; 161 | /** 162 | * Condition names that are evaluated for any incoming event 163 | */ 164 | conditions?: string[]; 165 | } 166 | 167 | export interface Configuration { 168 | /** 169 | * Actions in the configuration. 170 | */ 171 | actions: Action[]; 172 | /** 173 | * Conditions in the configuration. 174 | */ 175 | conditions: Condition[]; 176 | /** 177 | * Subscriptions in the configuration. 178 | */ 179 | subscriptions: Subscription[]; 180 | } 181 | 182 | export interface ConfigurationRequest { 183 | /** 184 | * Type of entity. 185 | */ 186 | type: EntityType; 187 | /** 188 | * Entity's unique identifier. 189 | */ 190 | name?: string; 191 | /** 192 | * Data of the requeste entity. 193 | */ 194 | data?: Action | Condition | Subscription; 195 | /** 196 | * Request parameters. 197 | */ 198 | params?: CommonQueryParams; 199 | } 200 | 201 | /** 202 | * @enum {ACTION, CONDITION, SUBSCRIPTION} 203 | */ 204 | export enum EntityType { 205 | ACTION = 'ACTION', 206 | CONDITION = 'CONDITION', 207 | SUBSCRIPTION = 'SUBSCRIPTION' 208 | } 209 | -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | import AlertNotificationClient, { AlertNotificationConfiguration } from './client'; 2 | 3 | export { 4 | DestinationConfiguration, 5 | CredentialsForDestinationService 6 | } from './utils/destination-configuration'; 7 | 8 | export { 9 | Credentials, 10 | OAuthConfig, 11 | BasicAuthentication, 12 | OAuthAuthentication, 13 | CertificateAuthentication 14 | } from './authentication'; 15 | 16 | export * as RegionUtils from './utils/region'; 17 | 18 | export { PageMetadata, PageResponse, CommonQueryParams } from './utils/common'; 19 | 20 | export { 21 | State, 22 | Action, 23 | Condition, 24 | Predicate, 25 | Subscription, 26 | Configuration, 27 | EntityType 28 | } from './configuration-api/models'; 29 | 30 | export { 31 | Severity, 32 | Category, 33 | AffectedResource, 34 | ResourceEvent, 35 | ConsumerQueryParameters, 36 | ConsumerEvent, 37 | ConsumerPagedResponse 38 | } from './producer-api/models'; 39 | 40 | export { AlertNotificationClient, AlertNotificationConfiguration }; 41 | -------------------------------------------------------------------------------- /src/producer-api/event-producer-client.ts: -------------------------------------------------------------------------------- 1 | import { AxiosInstance, AxiosPromise } from 'axios'; 2 | 3 | import { ResourceEvent, ConsumerRequest, ConsumerPagedResponse } from './models'; 4 | 5 | /** 6 | * Class used to access SAP Alert Notification service for SAP BTP Producer API. 7 | * 8 | */ 9 | export default class EventsApiClient { 10 | private axios: AxiosInstance; 11 | private resourceEventsPath: string; 12 | private resourceEventsBatchPath: string; 13 | private matchedEventsPath: string; 14 | private undeliveredEventsPath: string; 15 | 16 | /** 17 | * Constructs an instance of EventsApiClient, which is used for making 18 | * requests to SAP Alert Notification service for SAP BTP Proucer API. 19 | * 20 | * 21 | * @param {string} platform - platform, which can be CloudFoundry or Neo, 22 | * used to decide which endpoints to call 23 | * @param {AxiosInstance} axios - http client used for making http requests 24 | */ 25 | constructor(platform: string, axios: AxiosInstance) { 26 | this.axios = axios; 27 | this.resourceEventsBatchPath = `${platform}/producer/v1/resource-events-batch`; 28 | this.resourceEventsPath = `${platform}/producer/v1/resource-events`; 29 | this.matchedEventsPath = `${platform}/consumer/v1/matched-events`; 30 | this.undeliveredEventsPath = `${platform}/consumer/v1/undelivered-events`; 31 | } 32 | 33 | /** 34 | * Executes a request which if successful will ingest a resource event into 35 | * SAP Alert Notification service for SAP BTP and the system will start processing it. 36 | * 37 | * @param {ResourceEvent} event - resource event to be ingested into SAP Alert Notification service for SAP BTP 38 | * 39 | * @return {AxiosPromise} promise, which contains the successfully ingested event 40 | */ 41 | public sendEvent(event: ResourceEvent): AxiosPromise { 42 | return this.axios.request({ 43 | method: 'post', 44 | url: this.resourceEventsPath, 45 | data: event 46 | }); 47 | } 48 | 49 | /** 50 | * Executes a request of ingesting multiple resource events into SAP Alert Notification service for SAP BTP. 51 | * If an error occurs and one of them cannot be ingested, the ingestion will be discontinued 52 | * and an error will be returned. The error response will contain the accepted and rejected events. 53 | * 54 | * @param {ResourceEvent[]} events - resource events to be ingested into SAP Alert Notification service, note that 55 | * they will be processed in order starting from index 0 56 | * 57 | * @return {AxiosPromise} promise, which contains the successfully ingested events 58 | */ 59 | public sendEvents(events: ResourceEvent[]): AxiosPromise { 60 | return this.axios.request({ 61 | method: 'post', 62 | url: this.resourceEventsBatchPath, 63 | data: events 64 | }); 65 | } 66 | 67 | /** 68 | * Executes a request for finding undelivered events 69 | * 70 | * @param {ConsumerRequest} consumerRequest - parameters which will be used to search an undelivered 71 | * event 72 | * 73 | * @return {AxiosPromise} promise, which contains undelivered events if any 74 | */ 75 | public getUndeliveredEvents( 76 | consumerRequest: ConsumerRequest 77 | ): AxiosPromise { 78 | return this.axios.request({ 79 | method: 'get', 80 | url: consumerRequest.eventId 81 | ? `${this.undeliveredEventsPath}/${consumerRequest.eventId}` 82 | : this.undeliveredEventsPath, 83 | params: consumerRequest.params 84 | }); 85 | } 86 | 87 | /** 88 | * Executes a request for finding matched events 89 | * 90 | * @param {ConsumerRequest} consumerRequest - parameters which will be used to search a matched 91 | * event 92 | * 93 | * @return {AxiosPromise} promise, which contains matched events if any 94 | */ 95 | public getMatchedEvents(consumerRequest: ConsumerRequest): AxiosPromise { 96 | return this.axios.request({ 97 | method: 'get', 98 | url: consumerRequest.eventId 99 | ? `${this.matchedEventsPath}/${consumerRequest.eventId}` 100 | : this.matchedEventsPath, 101 | params: consumerRequest.params 102 | }); 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /src/producer-api/models.ts: -------------------------------------------------------------------------------- 1 | import { CommonQueryParams, PageMetadata } from '../utils/common'; 2 | 3 | /** 4 | * @enum {INFO, NOTICE, WARNING, ERROR, FATAL} 5 | */ 6 | export enum Severity { 7 | INFO = 'INFO', 8 | NOTICE = 'NOTICE', 9 | WARNING = 'WARNING', 10 | ERROR = 'ERROR', 11 | FATAL = 'FATAL' 12 | } 13 | 14 | /** 15 | * @enum {EXCEPTION, ALERT, NOTIFICATION} 16 | */ 17 | export enum Category { 18 | ALERT = 'ALERT', 19 | EXCEPTION = 'EXCEPTION', 20 | NOTIFICATION = 'NOTIFICATION' 21 | } 22 | 23 | export interface AffectedResource { 24 | /** 25 | * Name of the affected resource, e.g. application's name 26 | */ 27 | resourceName: string; 28 | /** 29 | * Type of the affected resource, e.g. can be of type 'application' 30 | */ 31 | resourceType: string; 32 | /** 33 | * Resource instance, e.g. could be number of an instance 34 | */ 35 | resourceInstance?: string; 36 | /** 37 | * Tags, any other information related to the affected resource can be 38 | * added here 39 | */ 40 | tags?: Record; 41 | } 42 | 43 | export interface ResourceEvent { 44 | /** 45 | * ID of the resource event, it will be populated by SAP Alert Notification service for SAP BTP service and you 46 | * will have access to it on response 47 | */ 48 | id?: string; 49 | /** 50 | * Describes what the event is about in details. 51 | */ 52 | body: string; 53 | /** 54 | * Self explanatory title, which summarises what the sent event is about. 55 | */ 56 | subject: string; 57 | /** 58 | * Type of the event, e.g. it can be HighCPUUsage, MemoryTooLow, etc. 59 | */ 60 | eventType: string; 61 | /** 62 | * Represents the event impact in the context of the affected resource. 63 | */ 64 | severity: Severity; 65 | /** 66 | * Identifies the category of the event. 67 | */ 68 | category: Category; 69 | /** 70 | * The affected resource for which the event was created. 71 | */ 72 | resource: AffectedResource; 73 | /** 74 | * Event timestamp, represents when it was created in the source, if missing SAP Alert Notification service for SAP BTP 75 | * will populate it for you and will set the time it was ingested for processing 76 | */ 77 | eventTimestamp?: number; 78 | /** 79 | * Priority of the raised event. 80 | */ 81 | priority?: number; 82 | /** 83 | * Any other useful information about the event. 84 | * 85 | * Some useful known tags by SAP Alert Notification service for SAP BTP are: 86 | * - ans:sourceEventId? - generated by the source, will be used for further stateful interactions with SAP Alert Notification service for SAP BTP 87 | * - ans:correlationId? - generated by the source in order to correlate this event with other activities or issues 88 | * - ans:status? - will be used for incient management systems. Possible values: 'CREATE_OR_UPDATE', 'CREATE', 'UPDATE', 'COMMENT' or 'CLOSE' 89 | * - ans:recommendedActionLink? - a URL that contains details for recommended actions regarding this event 90 | * - ans:detailsLink? - a URL that contains details for this event, e.g. dashboards showing what triggered it 91 | */ 92 | tags?: Record; 93 | } 94 | 95 | export interface ConsumerQueryParameters extends CommonQueryParams { 96 | severity?: string; 97 | category?: string; 98 | eventType?: string; 99 | resourceName?: string; 100 | correlationId?: string; 101 | sourceEventId?: string; 102 | cacheTimeInterval?: string; 103 | creationTimeInterval?: string; 104 | include?: string; 105 | } 106 | 107 | export interface ConsumerRequest { 108 | eventId?: string; 109 | params?: ConsumerQueryParameters; 110 | } 111 | 112 | export interface FailureReason { 113 | code: number; 114 | reason: string; 115 | timestamp: number; 116 | } 117 | 118 | export enum DeliveryStatus { 119 | UNDELIVERED = 'UNDELIVERED', 120 | MATCHED = 'MATCHED' 121 | } 122 | 123 | export interface ConsumerEventMetadata { 124 | cacheTime: number; 125 | affectedActionId: string; 126 | deliveryStatus: DeliveryStatus; 127 | failureReasons: FailureReason[]; 128 | } 129 | 130 | export interface ConsumerEvent { 131 | id: string; 132 | body: string; 133 | region: string; 134 | subject: string; 135 | eventType: string; 136 | regionType: string; 137 | eventTimestamp: number; 138 | severity: Severity; 139 | category: Category; 140 | resource: AffectedResource; 141 | metadata: ConsumerEventMetadata; 142 | tags?: Record; 143 | priority?: number; 144 | } 145 | 146 | export interface ConsumerPagedResponse { 147 | responseMetadata: PageMetadata; 148 | results: ConsumerEvent[]; 149 | } 150 | -------------------------------------------------------------------------------- /src/utils/axios-utils.ts: -------------------------------------------------------------------------------- 1 | import axios, { AxiosError, AxiosInstance, InternalAxiosRequestConfig, AxiosResponse } from 'axios'; 2 | import { KeyStore } from './key-store'; 3 | 4 | export interface RetryConfig { 5 | /** 6 | * Maximum number of retries. 7 | */ 8 | maxRetries: number; 9 | /** 10 | * Timeout in milliseconds used to wait before executing the next retry. 11 | */ 12 | retryBackoff: number; 13 | } 14 | 15 | interface RetryConfigInternal extends RetryConfig { 16 | /** 17 | * Current attempt of the retry. 18 | */ 19 | currentAttempt: number; 20 | /** 21 | * Axios instance. 22 | */ 23 | instance: AxiosInstance; 24 | } 25 | 26 | export interface ExtendedAxiosRequestConfig extends InternalAxiosRequestConfig { 27 | retryConfig?: RetryConfigInternal; 28 | } 29 | 30 | /** 31 | * Sets up a request interceptor which adds the Authorization header value, for the given authentication object, 32 | * also removes auth field from the current axios request config. 33 | * 34 | * @param {AxiosInstance} axiosInstance - given axios instance 35 | * @param {Authentication} authenticationPromise - authentication object, which will take care of getting 36 | * the authorization header value 37 | */ 38 | export function setupAuthorizationHeaderOnRequestInterceptor( 39 | axiosInstance: AxiosInstance, 40 | authenticationPromise: Promise 41 | ): void { 42 | axiosInstance.interceptors.request.use( 43 | (config: InternalAxiosRequestConfig) => { 44 | return authenticationPromise 45 | .then((authentication) => { 46 | if (authentication instanceof KeyStore) { 47 | return { 48 | ...config, 49 | ...{ 50 | httpsAgent: authentication.getHttpsAgent() 51 | } 52 | }; 53 | } 54 | return authentication 55 | .getAuthorizationHeaderValue() 56 | .then((headerValue: string) => { 57 | const headers = { 58 | ...config.headers, 59 | ...{ Authorization: headerValue } 60 | }; 61 | let auth; 62 | return { ...config, ...{ headers, auth } }; 63 | }) 64 | .catch((error: AxiosError) => { 65 | return Promise.reject(error); 66 | }); 67 | }) 68 | .catch((error) => { 69 | return Promise.reject(error); 70 | }); 71 | }, 72 | (error: AxiosError) => { 73 | return Promise.reject(error); 74 | } 75 | ); 76 | } 77 | 78 | /** 79 | * Sets up a response interceptor which extracts the data when response is successful. 80 | * 81 | * @param {AxiosInstance} axiosInstance - given axios instance 82 | */ 83 | export function extractDataOnResponseInterceptor(axiosInstance: AxiosInstance): void { 84 | axiosInstance.interceptors.response.use( 85 | (response: AxiosResponse) => Promise.resolve(response.data), 86 | (error: AxiosError) => Promise.reject(error) 87 | ); 88 | } 89 | 90 | /** 91 | * Sets up request and retry interceptors for the given axios instance. These interceptors 92 | * will take care of the retry on failure logic. 93 | * 94 | * The request interceptor will populate and adjust the retry configuration in axios request configuration 95 | * on each request. If etry configuration is not present maxRetries, retryBackoff will be set to 0. 96 | * 97 | * The response interceptor holds the retry logic, it will retry the request in the following cases: 98 | * - retry configuration and axios instance are present 99 | * - axios request is not canceled 100 | * - current attempt is not greater or equal than the maxRetries property 101 | * 102 | * @param {AxiosInstance} axiosInstance - given axios instance 103 | */ 104 | export function configureDefaultRetryInterceptor(axiosInstance: AxiosInstance): void { 105 | axiosInstance.interceptors.request.use( 106 | (config: ExtendedAxiosRequestConfig) => { 107 | const defaultRetryConfig = { 108 | currentAttempt: config.retryConfig?.currentAttempt || 0, 109 | maxRetries: config.retryConfig?.maxRetries || 0, 110 | retryBackoff: config.retryConfig?.retryBackoff || 0, 111 | instance: axiosInstance 112 | }; 113 | return { 114 | ...config, 115 | ...{ retryConfig: { ...defaultRetryConfig, ...config.retryConfig } } 116 | }; 117 | }, 118 | (error: AxiosError) => Promise.reject(error) 119 | ); 120 | 121 | axiosInstance.interceptors.response.use( 122 | (response: AxiosResponse) => response, 123 | (error: AxiosError) => { 124 | let config = error.config as ExtendedAxiosRequestConfig; 125 | 126 | if (!config.retryConfig) { 127 | return Promise.reject(error); 128 | } 129 | 130 | const { retryConfig } = config; 131 | const axiosInstance = retryConfig.instance as AxiosInstance; 132 | 133 | if (axios.isCancel(error) || !axiosInstance) { 134 | return Promise.reject(error); 135 | } 136 | 137 | let currentAttempt = retryConfig.currentAttempt || 0; 138 | const maxRetries = retryConfig.maxRetries || 0; 139 | 140 | if (currentAttempt >= maxRetries) { 141 | return Promise.reject(error); 142 | } 143 | 144 | const retryBackoff = retryConfig.retryBackoff || 0; 145 | 146 | config = { 147 | ...config, 148 | ...{ 149 | retryConfig: { 150 | currentAttempt: ++currentAttempt, 151 | instance: axiosInstance, 152 | maxRetries, 153 | retryBackoff 154 | } 155 | } 156 | }; 157 | 158 | return new Promise((resolve) => 159 | setTimeout(() => { 160 | resolve(axiosInstance.request(config)); 161 | }, retryBackoff) 162 | ); 163 | } 164 | ); 165 | } 166 | -------------------------------------------------------------------------------- /src/utils/common.ts: -------------------------------------------------------------------------------- 1 | export interface PageMetadata { 2 | /** 3 | * Current page. 4 | */ 5 | page: number; 6 | /** 7 | * Shows how much entities are present per page. 8 | */ 9 | pageSize: number; 10 | /** 11 | * Total number of pages for the given page size. 12 | */ 13 | totalPages: number; 14 | /** 15 | * Total results entities accross all pages. 16 | */ 17 | totalResultsCount: number; 18 | } 19 | 20 | export interface PageResponse { 21 | /** 22 | * Metadata. 23 | */ 24 | metadata: PageMetadata; 25 | /** 26 | * An array of entities. 27 | */ 28 | results: T[]; 29 | } 30 | 31 | export interface CommonQueryParams { 32 | /** 33 | * Page, default is 0. 34 | */ 35 | page?: number; 36 | /** 37 | * Page size, default is 100. 38 | */ 39 | pageSize?: number; 40 | } 41 | -------------------------------------------------------------------------------- /src/utils/destination-configuration.ts: -------------------------------------------------------------------------------- 1 | import axios, { AxiosInstance } from 'axios'; 2 | import { setupAuthorizationHeaderOnRequestInterceptor } from './axios-utils'; 3 | import { BasicAuthentication, OAuthAuthentication } from '../authentication'; 4 | import { KeyStore, KeystoreFormat } from './key-store'; 5 | import { toPem } from 'jks-js'; 6 | 7 | export interface CredentialsForDestinationService { 8 | /** 9 | * Username 10 | */ 11 | username: string; 12 | /** 13 | * Password 14 | */ 15 | password: string; 16 | /** 17 | * Destination name 18 | */ 19 | destinationName: string; 20 | /** 21 | * Destination service configuration URL used to retrieve destination 22 | */ 23 | destinationServiceBaseUrl: string; 24 | /** 25 | * Oauth token URL used to retrieve access token 26 | */ 27 | oAuthTokenUrl: string; 28 | } 29 | 30 | /** 31 | * Destination Service Configuration class. Retrieves the destination from Destination service and build authentication context 32 | */ 33 | export class DestinationConfiguration { 34 | private axiosInstance: AxiosInstance; 35 | private static readonly DESTINATION_PATH = '/destination-configuration/v1/destinations/'; 36 | private static readonly BASIC_AUTHENTICATION = 'BasicAuthentication'; 37 | private static readonly OAUTH_2_CLIENT_CREDENTIALS = 'OAuth2ClientCredentials'; 38 | private static readonly CLIENT_CERTIFICATE_AUTHENTICATION = 'ClientCertificateAuthentication'; 39 | private static readonly END_ENCRYPTED_PRIVATE_KEY = '-----END ENCRYPTED PRIVATE KEY-----'; 40 | private static readonly END_PRIVATE_KEY = '-----END PRIVATE KEY-----'; 41 | /** 42 | * Creates an instance of DestinationServiceConfiguration. 43 | * 44 | * @param {CredentialsForDestinationService} config - contains username, password, destinationName, destinationUrl and oAuthTokenUrl. All 45 | * of them must be provided else Error will be thrown. 46 | */ 47 | constructor(config: CredentialsForDestinationService) { 48 | if (!config) { 49 | throw new Error('Configuration cannot be null, undefined or empty object'); 50 | } 51 | 52 | if ( 53 | !config.username || 54 | !config.password || 55 | !config.destinationName || 56 | !config.destinationServiceBaseUrl || 57 | !config.oAuthTokenUrl 58 | ) { 59 | throw new Error( 60 | 'Username, password, destinationName, destinationUrl and oAuthTokenUrl configuration must be provided' 61 | ); 62 | } 63 | const oAuthAuthentication = new OAuthAuthentication({ 64 | username: config.username, 65 | password: config.password, 66 | oAuthTokenUrl: config.oAuthTokenUrl 67 | }); 68 | 69 | const axiosRequestConfig = { 70 | baseURL: 71 | config.destinationServiceBaseUrl + 72 | DestinationConfiguration.DESTINATION_PATH + 73 | config.destinationName, 74 | timeout: 2500, 75 | retryConfig: { 76 | maxRetries: 3, 77 | retryBackoff: 1000 78 | } 79 | }; 80 | 81 | this.axiosInstance = axios.create(axiosRequestConfig); 82 | setupAuthorizationHeaderOnRequestInterceptor( 83 | this.axiosInstance, 84 | Promise.resolve(oAuthAuthentication) 85 | ); 86 | } 87 | 88 | // eslint-disable-next-line require-jsdoc 89 | async getAuthentication(): Promise { 90 | return new Promise((resolve, reject) => { 91 | this.axiosInstance 92 | .request({}) 93 | .then((destination) => { 94 | const destinationConfiguration = destination.data.destinationConfiguration; 95 | if ( 96 | destinationConfiguration.Authentication == 97 | DestinationConfiguration.BASIC_AUTHENTICATION 98 | ) { 99 | return resolve( 100 | new BasicAuthentication({ 101 | username: destinationConfiguration.User, 102 | password: destinationConfiguration.Password 103 | }) 104 | ); 105 | } 106 | if ( 107 | destinationConfiguration.Authentication == 108 | DestinationConfiguration.OAUTH_2_CLIENT_CREDENTIALS 109 | ) { 110 | return resolve( 111 | new OAuthAuthentication({ 112 | username: destinationConfiguration.clientId, 113 | password: destinationConfiguration.clientSecret, 114 | oAuthTokenUrl: destinationConfiguration.tokenServiceURL 115 | }) 116 | ); 117 | } 118 | if ( 119 | destinationConfiguration.Authentication == 120 | DestinationConfiguration.CLIENT_CERTIFICATE_AUTHENTICATION 121 | ) { 122 | return resolve(this.buildKeyStore(destination.data)); 123 | } 124 | }) 125 | .catch((error) => { 126 | return reject(error); 127 | }); 128 | }); 129 | } 130 | 131 | // eslint-disable-next-line require-jsdoc 132 | private buildKeyStore(destination: any): KeyStore { 133 | const keyStoreFormat = destination.certificates[0].Name.substr( 134 | destination.certificates[0].Name.length - 3 135 | ); 136 | if (keyStoreFormat == KeystoreFormat.JKS) { 137 | return this.buildJksKeyStore( 138 | destination.certificates[0].Content, 139 | destination.destinationConfiguration.KeyStorePassword 140 | ); 141 | } 142 | if (keyStoreFormat == KeystoreFormat.PEM) { 143 | return this.buildPemKeyStore( 144 | destination.certificates[0].Content, 145 | destination.destinationConfiguration.KeyStorePassword 146 | ); 147 | } else { 148 | return this.buildP12KeyStore( 149 | destination.certificates[0].Content, 150 | destination.destinationConfiguration.KeyStorePassword 151 | ); 152 | } 153 | } 154 | 155 | // eslint-disable-next-line require-jsdoc 156 | private decodeKeyStore(keyStore: string): Buffer { 157 | return Buffer.from(keyStore, 'base64'); 158 | } 159 | 160 | // eslint-disable-next-line require-jsdoc 161 | private retrieveDelimiterForPem(password: string): string { 162 | if (password == '') { 163 | return DestinationConfiguration.END_PRIVATE_KEY; 164 | } else { 165 | return DestinationConfiguration.END_ENCRYPTED_PRIVATE_KEY; 166 | } 167 | } 168 | 169 | // eslint-disable-next-line require-jsdoc 170 | private buildPemKeyStore(encodedKeyStore: string, password: string): KeyStore { 171 | const keyStore = this.decodeKeyStore(encodedKeyStore) 172 | .toString() 173 | .split(this.retrieveDelimiterForPem(password)); 174 | const key = keyStore[0] + this.retrieveDelimiterForPem(password); 175 | const certificate = keyStore[1]; 176 | return new KeyStore(KeystoreFormat.PEM, password, undefined, certificate, key); 177 | } 178 | 179 | // eslint-disable-next-line require-jsdoc 180 | private buildP12KeyStore(encodedKeyStore: string, password: string): KeyStore { 181 | const keyStore = this.decodeKeyStore(encodedKeyStore); 182 | return new KeyStore(KeystoreFormat.PFX, password, keyStore, undefined, undefined); 183 | } 184 | 185 | // eslint-disable-next-line require-jsdoc 186 | private buildJksKeyStore(encodedKeyStore: string, password: string): KeyStore { 187 | const pemKeyStore = toPem(this.decodeKeyStore(encodedKeyStore), password); 188 | const commonName = Object.keys(pemKeyStore).toString(); 189 | return new KeyStore( 190 | KeystoreFormat.JKS, 191 | password, 192 | undefined, 193 | pemKeyStore[commonName].cert, 194 | pemKeyStore[commonName].key 195 | ); 196 | } 197 | } 198 | -------------------------------------------------------------------------------- /src/utils/key-store.ts: -------------------------------------------------------------------------------- 1 | import * as https from 'https'; 2 | 3 | export enum KeystoreFormat { 4 | JKS = 'jks', 5 | P12 = 'p12', 6 | PFX = 'pfx', 7 | PEM = 'pem' 8 | } 9 | 10 | // eslint-disable-next-line require-jsdoc 11 | export class KeyStore { 12 | private format: KeystoreFormat; 13 | private keyStore: Buffer | undefined; 14 | private certificate: string | undefined; 15 | private key: string | undefined; 16 | private passPhrase: string; 17 | private httpsAgent: https.Agent; 18 | 19 | // eslint-disable-next-line require-jsdoc 20 | constructor( 21 | format: KeystoreFormat, 22 | passphrase: string, 23 | keystore?: Buffer, 24 | certificate?: string, 25 | key?: string 26 | ) { 27 | this.format = format; 28 | this.keyStore = keystore; 29 | this.certificate = certificate; 30 | this.key = key; 31 | this.passPhrase = passphrase; 32 | this.httpsAgent = this.buildHttpsAgent(); 33 | } 34 | 35 | // eslint-disable-next-line require-jsdoc 36 | getFormat(): KeystoreFormat { 37 | return this.format; 38 | } 39 | 40 | // eslint-disable-next-line require-jsdoc 41 | getHttpsAgent(): https.Agent { 42 | return this.httpsAgent; 43 | } 44 | 45 | // eslint-disable-next-line require-jsdoc 46 | private buildHttpsAgent(): https.Agent { 47 | if (this.getFormat() == KeystoreFormat.JKS || this.getFormat() == KeystoreFormat.PEM) { 48 | return new https.Agent({ 49 | cert: this.certificate, 50 | key: this.key, 51 | passphrase: this.passPhrase 52 | }); 53 | } else { 54 | return new https.Agent({ 55 | pfx: this.keyStore, 56 | passphrase: this.passPhrase 57 | }); 58 | } 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "declaration": true, 4 | "module": "commonjs", 5 | "esModuleInterop": true, 6 | "target": "es6", 7 | "moduleResolution": "node", 8 | "sourceMap": true, 9 | "outDir": "dist", 10 | "noImplicitReturns": true, 11 | "noImplicitAny": true, 12 | "strict": true, 13 | "noUnusedLocals": true, 14 | "forceConsistentCasingInFileNames": true 15 | }, 16 | "include": [ 17 | "src/**/*", 18 | "jks-js.d.ts" 19 | ], 20 | "exclude": [ 21 | "node_modules", "__tests__" 22 | ], 23 | "lib": ["es2015"] 24 | } --------------------------------------------------------------------------------