├── .cra └── .cveignore ├── .editorconfig ├── .eslintignore ├── .eslintrc.js ├── .gitattributes ├── .github ├── issue_template.md ├── pull_request_template.md └── workflows │ └── build.yaml ├── .gitignore ├── .npmignore ├── .npmrc ├── .prettierrc ├── .releaserc ├── .secrets.baseline ├── .travis.yml ├── Authentication.md ├── CHANGELOG.md ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── LICENSE.md ├── MIGRATION-V4.md ├── MIGRATION-V5.md ├── README.md ├── api-extractor.json ├── appscan-config.xml ├── auth ├── authenticators │ ├── authenticator-interface.ts │ ├── authenticator.ts │ ├── basic-authenticator.ts │ ├── bearer-token-authenticator.ts │ ├── cloud-pak-for-data-authenticator.ts │ ├── container-authenticator.ts │ ├── iam-assume-authenticator.ts │ ├── iam-authenticator.ts │ ├── iam-request-based-authenticator-immutable.ts │ ├── iam-request-based-authenticator.ts │ ├── index.ts │ ├── mcsp-authenticator.ts │ ├── mcspv2-authenticator.ts │ ├── no-auth-authenticator.ts │ ├── token-request-based-authenticator-immutable.ts │ ├── token-request-based-authenticator.ts │ └── vpc-instance-authenticator.ts ├── index.ts ├── token-managers │ ├── container-token-manager.ts │ ├── cp4d-token-manager.ts │ ├── iam-assume-token-manager.ts │ ├── iam-request-based-token-manager.ts │ ├── iam-token-manager.ts │ ├── index.ts │ ├── jwt-token-manager.ts │ ├── mcsp-token-manager.ts │ ├── mcspv2-token-manager.ts │ ├── token-manager.ts │ └── vpc-instance-token-manager.ts └── utils │ ├── file-reading-helpers.browser.ts │ ├── file-reading-helpers.ts │ ├── get-authenticator-from-environment.ts │ ├── helpers.ts │ ├── index.ts │ └── read-external-sources.ts ├── commitlint.config.js ├── etc └── ibm-cloud-sdk-core.api.md ├── index.ts ├── lib ├── axios-types.ts ├── base-service.ts ├── build-user-agent.ts ├── chain-error.ts ├── content-type.ts ├── cookie-support.ts ├── get-new-logger.ts ├── helper.ts ├── logger.ts ├── private-helpers.ts ├── querystring.ts ├── request-wrapper.ts └── stream-to-promise.ts ├── package-lock.json ├── package.json ├── sdk-test-utilities ├── .npmignore ├── README.md ├── index.js ├── package-lock.json └── package.json ├── test ├── .eslintrc.js ├── integration │ ├── cp4d-authenticator.test.js │ ├── iam-assume-authenticator.test.js │ ├── iam-authenticator.test.js │ ├── mcsp-authenticator.test.js │ └── mcspv2-authenticator.test.js ├── resources │ ├── blank.wav │ ├── cp4dtest.env.example │ ├── empty-file │ ├── ibm-credentials.env │ ├── mockSerDeService.js │ ├── other-file.env │ ├── symlink-creds.txt │ ├── vault-token │ ├── vcap.json │ └── weather-data-train.csv └── unit │ ├── authenticator.test.js │ ├── base-service.test.js │ ├── basic-authenticator.test.js │ ├── bearer-token-authenticator.test.js │ ├── build-request-file-object.test.js │ ├── container-authenticator.test.js │ ├── container-token-manager.test.js │ ├── content-type.test.js │ ├── cookiejar.test.js │ ├── cp4d-authenticator.test.js │ ├── cp4d-token-manager.test.js │ ├── export-unit-test-utils.test.js │ ├── file-reading-helpers.test.js │ ├── get-authenticator-from-environment.test.js │ ├── get-content-type.test.js │ ├── get-credentials-from-vcap.test.js │ ├── get-format.test.js │ ├── get-missing-params.test.js │ ├── get-new-logger.test.js │ ├── get-query-param.test.js │ ├── iam-assume-authenticator.test.js │ ├── iam-assume-token-manager.test.js │ ├── iam-authenticator.test.js │ ├── iam-request-based-authenticator.test.js │ ├── iam-request-based-token-manager.test.js │ ├── iam-token-manager.test.js │ ├── is-empty-object.test.js │ ├── is-html.test.js │ ├── is-json-mime-type.test.js │ ├── jwt-token-manager.test.js │ ├── logger.test.js │ ├── mcsp-authenticator.test.js │ ├── mcsp-token-manager.test.js │ ├── mcspv2-authenticator.test.js │ ├── mcspv2-token-manager.test.js │ ├── no-auth-authenticator.test.js │ ├── parameterized-url.test.js │ ├── querystring.test.js │ ├── read-external-sources.test.js │ ├── redact-secrets.test.js │ ├── request-wrapper.test.js │ ├── retry.test.js │ ├── serializer-deserializer.test.js │ ├── strip-trailing-slash.test.js │ ├── to-lower-keys.test.js │ ├── to-promise.test.js │ ├── token-request-based-authenticator.test.js │ ├── utils.js │ ├── validate-params.test.js │ ├── vpc-instance-authenticator.test.js │ └── vpc-instance-token-manager.test.js ├── tsconfig-es6.json └── tsconfig.json /.cra/.cveignore: -------------------------------------------------------------------------------- 1 | [ 2 | ] -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # http://editorconfig.org/ 2 | 3 | root = true 4 | 5 | [*] 6 | end_of_line = lf 7 | charset = utf-8 8 | trim_trailing_whitespace = true 9 | insert_final_newline = true 10 | indent_style = space 11 | indent_size = 2 12 | 13 | # break out .yml settings even though they match, because you always want .yml on soaced indentation - it can choke on tabs 14 | [*.yml] 15 | indent_style = space 16 | indent_size = 2 17 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | # http://eslint.org/docs/user-guide/configuring#ignoring-files-and-directories 2 | jsdoc/ 3 | doc/ 4 | coverage/ 5 | dist/ 6 | node_modules/ 7 | # ignore emitted js 8 | **/*v*.js 9 | !test/**/*.js 10 | lib/*.js 11 | auth/**/*.js 12 | index.js 13 | scripts/typedoc/ -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | overrides: [ 4 | { 5 | env: { 6 | node: true, 7 | }, 8 | files: ['**/*.js', '**/*.jsx'], 9 | plugins: ['node', 'jest', 'prettier'], 10 | extends: ['airbnb-base', 'plugin:jest/recommended', 'plugin:jest/style', 'prettier'], 11 | rules: { 12 | 'prefer-const': 'error', 13 | 'prettier/prettier': 'error', 14 | }, 15 | }, 16 | { 17 | files: ['*.test.js', '*.test.jsx'], 18 | rules: { 19 | 'global-require': 'off', 20 | 'import/no-dynamic-require': 'off', 21 | 'jest/no-commented-out-tests': 'off', 22 | 'jest/no-done-callback': 'off', 23 | 'no-plusplus': 'off', 24 | 'no-unused-vars': 'off', 25 | 'no-use-before-define': 'off', 26 | }, 27 | }, 28 | { 29 | files: ['**/*.ts', '**/*.tsx'], 30 | env: { 31 | node: true, 32 | }, 33 | extends: ['airbnb-typescript/base', 'prettier'], 34 | parser: '@typescript-eslint/parser', 35 | parserOptions: { 36 | project: 'tsconfig.json', 37 | sourceType: 'module', 38 | }, 39 | plugins: ['eslint-plugin-jsdoc', 'eslint-plugin-import', '@typescript-eslint', 'prettier'], 40 | rules: { 41 | '@typescript-eslint/no-use-before-define': 'off', 42 | 'import/prefer-default-export': 'off', 43 | 'no-else-return': 'off', 44 | 'no-param-reassign': 'off', 45 | 'prettier/prettier': 'error', 46 | 'spaced-comment': ['error', 'always', { exceptions: ['*'] }], 47 | }, 48 | }, 49 | ], 50 | }; 51 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # https://git-scm.com/book/en/v2/Customizing-Git-Git-Attributes 2 | 3 | # Code linting require line-endings be LF rather than CRLF in js files. This file should prevent git from converting line-endings. 4 | # (On Windows, git is often configured to automatically convert between the two - See https://git-scm.com/docs/git-config#git-config-coreautocrlf ) 5 | 6 | *.js text eol=lf 7 | *.ts text eol=lf 8 | -------------------------------------------------------------------------------- /.github/issue_template.md: -------------------------------------------------------------------------------- 1 | Remember, an issue is not the place to ask questions. You can use 2 | [Stack Overflow](http://stackoverflow.com/questions) for that, or 3 | you may want to start a discussion on [dW Answers](https://developer.ibm.com/answers/questions/ask). 4 | 5 | Before you open an issue, please check if a similar issue already exists or has been closed before. 6 | 7 | ### When reporting a bug, please be sure to include the following: 8 | - [ ] Steps to reproduce 9 | - [ ] Expected behavior 10 | - [ ] Actual behavior 11 | - [ ] Node version 12 | - [ ] SDK version 13 | 14 | ### When you open an issue for a feature request, please add as much detail as possible: 15 | - [ ] A descriptive title starting with the service name 16 | - [ ] A description of the problem you're trying to solve 17 | - [ ] A suggested solution if possible 18 | -------------------------------------------------------------------------------- /.github/pull_request_template.md: -------------------------------------------------------------------------------- 1 | 8 | 9 | ##### Checklist 10 | 11 | 12 | - [ ] `npm test` passes (tip: `npm run lint-fix` can correct most style issues) 13 | - [ ] tests are included 14 | - [ ] documentation is changed or added 15 | -------------------------------------------------------------------------------- /.github/workflows/build.yaml: -------------------------------------------------------------------------------- 1 | # This workflow will build and unit test the project. 2 | # If the workflow is running on the "main" branch, then 3 | # semantic-release is also run to create a new release (if 4 | # warranted by the new commits being built). 5 | 6 | name: Build/Test/Publish 7 | 8 | on: 9 | push: 10 | branches: ['**'] 11 | pull_request: 12 | branches: ['**'] 13 | workflow_dispatch: 14 | # Allow workflow to be triggered manually. 15 | 16 | jobs: 17 | detect-secrets: 18 | if: "!contains(github.event.head_commit.message, '[skip ci]')" 19 | name: detect-secrets 20 | runs-on: ubuntu-latest 21 | 22 | steps: 23 | - name: Checkout repository 24 | uses: actions/checkout@v4 25 | 26 | - name: Setup Python 27 | uses: actions/setup-python@v5 28 | with: 29 | python-version: 3.12 30 | 31 | - name: Install detect-secrets 32 | run: | 33 | pip install --upgrade "git+https://github.com/ibm/detect-secrets.git@master#egg=detect-secrets" 34 | 35 | - name: Run detect-secrets 36 | run: | 37 | detect-secrets scan --update .secrets.baseline 38 | detect-secrets -v audit --report --fail-on-unaudited --fail-on-live --fail-on-audited-real .secrets.baseline 39 | 40 | build: 41 | name: build-test (node v${{ matrix.node-version }}) 42 | needs: detect-secrets 43 | runs-on: ubuntu-latest 44 | strategy: 45 | matrix: 46 | node-version: ['18', '20', '22'] 47 | 48 | steps: 49 | - name: Checkout repository 50 | uses: actions/checkout@v4 51 | 52 | - name: Setup Node.js ${{ matrix.node-version }} 53 | uses: actions/setup-node@v4 54 | with: 55 | node-version: ${{ matrix.node-version }} 56 | 57 | - name: Build/Test 58 | run: | 59 | npm ci 60 | cd sdk-test-utilities && npm ci && cd .. 61 | npm run build 62 | npm run lint 63 | npm run test-travis 64 | 65 | publish-release: 66 | needs: build 67 | name: semantic-release 68 | if: "github.ref_name == 'main' && github.event_name != 'pull_request'" 69 | runs-on: ubuntu-latest 70 | 71 | steps: 72 | - name: Checkout repository 73 | uses: actions/checkout@v4 74 | with: 75 | persist-credentials: false 76 | 77 | - name: Setup Node 78 | uses: actions/setup-node@v4 79 | with: 80 | node-version: 22 81 | 82 | - name: Build 83 | run: | 84 | npm ci 85 | cd sdk-test-utilities && npm ci && cd .. 86 | npm run build 87 | 88 | - name: Run semantic-release 89 | env: 90 | GH_TOKEN: ${{ secrets.GH_TOKEN }} 91 | NPM_TOKEN: ${{ secrets.NPM_TOKEN }} 92 | run: npm run semantic-release 93 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | npm-debug.log 3 | npm-debug.log.* 4 | coverage 5 | .DS_Store 6 | .idea 7 | .nvmrc 8 | doc/ 9 | .vscode/ 10 | *.env 11 | .eslintcache 12 | lib/*.js 13 | auth/**/*.js 14 | iam-token-manager/*.js 15 | index.js 16 | !test/**/index.js 17 | .nyc_output 18 | **/*.d.ts 19 | !*/blob.d.ts 20 | **/*.js.map 21 | coverage.lcov 22 | .swagger-codegen-ignore 23 | /.settings/ 24 | /.project 25 | .pre-commit-config.yaml 26 | dist/ 27 | temp/ 28 | build/ 29 | es/tsdoc-metadata.json 30 | 31 | # For sub-package with test utilities 32 | !sdk-test-utilities/index.js 33 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | test 2 | coverage 3 | .jshintignore 4 | .hshintrc 5 | examples 6 | .editorconfig 7 | .jshintrc 8 | .travis.yml 9 | # documentation files 10 | CODE_OF_CONDUCT.md 11 | CONTRIBUTING.md 12 | RELEASE.md 13 | jsdoc/ 14 | doc/ 15 | dependency-lint.yml 16 | karma.conf.js 17 | transform.js 18 | scripts/ 19 | # this is created in the build on travis ci when it updates the docs in the gh-pages branch 20 | gh-pages/ 21 | tsconfig.json 22 | tslint.json 23 | typings.json 24 | typings 25 | .vscode 26 | .nyc_output 27 | .github 28 | .eslintrc.js 29 | .eslintcache 30 | .eslintignore 31 | .gitattributes 32 | .prettierrc 33 | .releaserc 34 | commitlint.config.js 35 | *.ts 36 | !*.d.ts 37 | -------------------------------------------------------------------------------- /.npmrc: -------------------------------------------------------------------------------- 1 | registry=https://registry.npmjs.com/ -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "useTabs": false, 3 | "printWidth": 100, 4 | "tabWidth": 2, 5 | "singleQuote": true, 6 | "trailingComma": "es5", 7 | } 8 | -------------------------------------------------------------------------------- /.releaserc: -------------------------------------------------------------------------------- 1 | { 2 | "debug": true, 3 | "branches": [ 4 | "main" 5 | ], 6 | "plugins": [ 7 | "@semantic-release/commit-analyzer", 8 | "@semantic-release/release-notes-generator", 9 | "@semantic-release/changelog", 10 | [ 11 | "@semantic-release/npm", 12 | { 13 | "pkgRoot": "dist" 14 | } 15 | ], 16 | "@semantic-release/git", 17 | "@semantic-release/github" 18 | ] 19 | } 20 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | 3 | dist: jammy 4 | 5 | cache: 6 | npm: false 7 | 8 | stages: 9 | - name: Build-Test 10 | - name: Semantic-Release 11 | if: (branch = main) AND (type IN (push, api)) AND (fork = false) 12 | 13 | # Default "before_install" and "script" steps. 14 | script: 15 | # Prepare sub-package for tests 16 | - cd sdk-test-utilities && npm ci && cd .. 17 | 18 | # Run standard build steps. 19 | - npm run build 20 | - npm run lint 21 | - npm run test-travis 22 | 23 | jobs: 24 | include: 25 | - stage: Build-Test 26 | language: node_js 27 | node_js: 18 28 | - node_js: 20 29 | - node_js: 22 30 | 31 | - name: Detect-Secrets 32 | language: python 33 | python: 3.12 34 | install: 35 | - pip install --upgrade "git+https://github.com/ibm/detect-secrets.git@master#egg=detect-secrets" 36 | script: 37 | - detect-secrets scan --update .secrets.baseline 38 | - detect-secrets -v audit --report --fail-on-unaudited --fail-on-live --fail-on-audited-real .secrets.baseline 39 | 40 | - stage: Semantic-Release 41 | language: node_js 42 | node_js: 22 43 | script: 44 | - npm run semantic-release 45 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as 6 | contributors and maintainers pledge to making participation in our project and 7 | our community a harassment-free experience for everyone, regardless of age, body 8 | size, disability, ethnicity, sex characteristics, gender identity and expression, 9 | level of experience, education, socio-economic status, nationality, personal 10 | appearance, race, religion, or sexual identity and orientation. 11 | 12 | ## Our Standards 13 | 14 | Examples of behavior that contributes to creating a positive environment 15 | include: 16 | 17 | * Using welcoming and inclusive language 18 | * Being respectful of differing viewpoints and experiences 19 | * Gracefully accepting constructive criticism 20 | * Focusing on what is best for the community 21 | * Showing empathy towards other community members 22 | 23 | Examples of unacceptable behavior by participants include: 24 | 25 | * The use of sexualized language or imagery and unwelcome sexual attention or 26 | advances 27 | * Trolling, insulting/derogatory comments, and personal or political attacks 28 | * Public or private harassment 29 | * Publishing others' private information, such as a physical or electronic 30 | address, without explicit permission 31 | * Other conduct which could reasonably be considered inappropriate in a 32 | professional setting 33 | 34 | ## Our Responsibilities 35 | 36 | Project maintainers are responsible for clarifying the standards of acceptable 37 | behavior and are expected to take appropriate and fair corrective action in 38 | response to any instances of unacceptable behavior. 39 | 40 | Project maintainers have the right and responsibility to remove, edit, or 41 | reject comments, commits, code, wiki edits, issues, and other contributions 42 | that are not aligned to this Code of Conduct, or to ban temporarily or 43 | permanently any contributor for other behaviors that they deem inappropriate, 44 | threatening, offensive, or harmful. 45 | 46 | ## Scope 47 | 48 | This Code of Conduct applies both within project spaces and in public spaces 49 | when an individual is representing the project or its community. Examples of 50 | representing a project or community include using an official project e-mail 51 | address, posting via an official social media account, or acting as an appointed 52 | representative at an online or offline event. Representation of a project may be 53 | further defined and clarified by project maintainers. 54 | 55 | ## Enforcement 56 | 57 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 58 | reported by contacting the project team at phil_adams@us.ibm.com. All 59 | complaints will be reviewed and investigated and will result in a response that 60 | is deemed necessary and appropriate to the circumstances. The project team is 61 | obligated to maintain confidentiality with regard to the reporter of an incident. 62 | Further details of specific enforcement policies may be posted separately. 63 | 64 | Project maintainers who do not follow or enforce the Code of Conduct in good 65 | faith may face temporary or permanent repercussions as determined by other 66 | members of the project's leadership. 67 | 68 | ## Attribution 69 | 70 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, 71 | available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html 72 | 73 | [homepage]: https://www.contributor-covenant.org 74 | 75 | For answers to common questions about this code of conduct, see 76 | https://www.contributor-covenant.org/faq 77 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Questions 2 | 3 | If you are having problems using this SDK or have a question about IBM Cloud services, 4 | please ask a question on [Stack Overflow](http://stackoverflow.com/questions/ask) or 5 | [dW Answers](https://developer.ibm.com/answers/questions/ask). 6 | 7 | # Coding Style 8 | 9 | * Our style guide is based on [Google's](https://google.github.io/styleguide/jsguide.html). 10 | Most of it is automatically enforced and can be automatically applied with `npm run lint-fix`. 11 | * Commits should follow the [Angular commit message guidelines](https://github.com/angular/angular/blob/master/CONTRIBUTING.md#-commit-message-guidelines). 12 | This is because our release tool uses this format for determining release versions and generating changelogs. 13 | To make this easier, we recommend using the [Commitizen CLI](https://github.com/commitizen/cz-cli) with the `cz-conventional-changelog` adapter. 14 | 15 | # Issues 16 | 17 | If you encounter an issue with the Node SDK, you are welcome to submit an issue. 18 | Before that, please search for similar issues. It's possible somebody has 19 | already encountered this issue. 20 | 21 | # Pull Requests 22 | 23 | If you want to contribute to the repository, follow these steps: 24 | 25 | 1. Fork the repo. 26 | 2. Install dependencies: `npm install` 27 | 3. Build the code: `npm run build` 28 | 4. Verify the build before beginning your changes: `npm test` 29 | 2. Develop and test your code changes. 30 | 4. Add a test for your changes. Only refactoring and documentation changes require no new tests. 31 | 5. Make sure all the tests pass. 32 | 2. Check code style with `npm run lint` and fix any issues. 33 | 6. Commit your changes. Remember the follow the correct commit message guidelines. 34 | 7. Push to your fork and submit a pull request. 35 | 3. Travis-CI will run the tests once your changes are merged. 36 | 8. Be sure to sign the CLA. 37 | 38 | ## Tests 39 | 40 | `npm test` runs the full suite of unit tests. 41 | 42 | To run only specific tests, invoke jest with the `--testNamePattern` or `-t` flag: 43 | 44 | ``` 45 | npm run jest -- /test/unit -t "resources key" 46 | ``` 47 | 48 | This will only run tests that match the test name pattern you provide. 49 | See the [Jest docs](https://jestjs.io/docs/en/cli#testnamepattern-regex) for more details. 50 | 51 | # Developer's Certificate of Origin 1.1 52 | 53 | By making a contribution to this project, I certify that: 54 | 55 | (a) The contribution was created in whole or in part by me and I 56 | have the right to submit it under the open source license 57 | indicated in the file; or 58 | 59 | (b) The contribution is based upon previous work that, to the best 60 | of my knowledge, is covered under an appropriate open source 61 | license and I have the right under that license to submit that 62 | work with modifications, whether created in whole or in part 63 | by me, under the same open source license (unless I am 64 | permitted to submit under a different license), as indicated 65 | in the file; or 66 | 67 | (c) The contribution was provided directly to me by some other 68 | person who certified (a), (b) or (c) and I have not modified 69 | it. 70 | 71 | (d) I understand and agree that this project and the contribution 72 | are public and that a record of the contribution (including all 73 | personal information I submit with it, including my sign-off) is 74 | maintained indefinitely and may be redistributed consistent with 75 | this project or the open source license(s) involved. 76 | -------------------------------------------------------------------------------- /MIGRATION-V4.md: -------------------------------------------------------------------------------- 1 | # Migration Guide for v4 2 | 3 | ## Breaking Changes 4 | 5 | ### Node Version 6 | Node version 12 is no longer supported - 14 is the minimum version supported. 7 | 8 | ### TypeScript Version 9 | The `typescript` development dependnecy is now required to be at least version 4. 10 | 11 | Run `npm install -D typescript@4` to upgrade to version 4. 12 | 13 | Code produced by the IBM OpenAPI SDK Generator should already be compliant with TypeScript v4. 14 | 15 | Additionally, you may need to upgrade `@typescript-eslint/eslint-plugin` and `@typescript-eslint/parser` to 16 | v5 to be compatible with the new version of TypeScript. This upgrade should be safe and compatible. 17 | 18 | Run `npm i -D '@typescript-eslint/eslint-plugin@latest' '@typescript-eslint/parser@latest'` to upgrade. 19 | 20 | ### Jest Version 21 | The `jest` development dependency is now required to be at least version 29. 22 | 23 | Run `npm install -D jest@29` to upgrade to version 29. 24 | 25 | Code produced by the IBM OpenAPI Generator should already be compliant with Jest v29. 26 | 27 | Note: Ensure your test code does not contain the anti-pattern of using a `done` callback with an `async` function. 28 | This is no longer permitted in Jest 29, whereas it was discouraged but permitted in previous versions. An old version of the IBM OpenAPI SDK Generator included a bug that generated this pattern. If your generated test code has not been updated since then, you may run into this issue. 29 | 30 | Example diff showing proper resolution: 31 | ``` 32 | diff --git a/test/integration/example.v1.test.js b/test/integration/example.v1.test.js 33 | index be0bec3..62f03f8 100644 34 | --- a/test/integration/example.v1.test.js 35 | +++ b/test/integration/example.v1.test.js 36 | @@ -38,7 +38,7 @@ describe('ExampleV1_integration', () => { 37 | jest.setTimeout(timeout); 38 | let example; 39 | 40 | - beforeAll(async (done) => { 41 | + beforeAll(async () => { 42 | example = ExampleV1.newInstance({}); 43 | 44 | const config = readExternalSources(ExampleV1.DEFAULT_SERVICE_NAME); 45 | @@ -58,8 +58,6 @@ describe('ExampleV1_integration', () => { 46 | 47 | log(`Service URL: ${example.baseOptions.serviceUrl}`); 48 | log('Finished setup.'); 49 | - 50 | - done(); 51 | }); 52 | 53 | test('getSomething()', async () => { 54 | ``` 55 | -------------------------------------------------------------------------------- /MIGRATION-V5.md: -------------------------------------------------------------------------------- 1 | # Migration Guide for v5 2 | 3 | ## Breaking Changes 4 | 5 | ### Node Version 6 | Node version 16 is no longer supported - 18 is the minimum version supported. 7 | 8 | ### SDK Unit Test Helpers 9 | The formerly available helper functions exposed in this package have been removed. These 10 | functions were intended for use in the unit tests of a generated SDK project. 11 | 12 | The functions are now present in a new package called `@ibm-cloud/sdk-test-utilities`. 13 | To continue using the helper functions in SDK test code, install this new package as a 14 | development dependency and import the functions from there. The interface and functions 15 | remain completely identical to the functions formerly exposed in this package. 16 | -------------------------------------------------------------------------------- /api-extractor.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://developer.microsoft.com/json-schemas/api-extractor/v7/api-extractor.schema.json", 3 | "mainEntryPointFilePath": "/dist/es/index.d.ts", 4 | "bundledPackages": [], 5 | "apiReport": { 6 | "enabled": true 7 | }, 8 | "docModel": { 9 | "enabled": true 10 | }, 11 | "dtsRollup": { 12 | "enabled": true 13 | }, 14 | "tsdocMetadata": { 15 | "enabled": true 16 | }, 17 | "messages": { 18 | "compilerMessageReporting": { 19 | "default": { 20 | "logLevel": "warning" 21 | } 22 | }, 23 | 24 | "extractorMessageReporting": { 25 | "default": { 26 | "logLevel": "warning" 27 | }, 28 | "ae-missing-release-tag": { 29 | "logLevel": "none" 30 | } 31 | }, 32 | 33 | "tsdocMessageReporting": { 34 | "default": { 35 | "logLevel": "warning" 36 | } 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /appscan-config.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | scripts/ 5 | test/ 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /auth/authenticators/authenticator-interface.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * (C) Copyright IBM Corp. 2019, 2022. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | import { OutgoingHttpHeaders } from 'http'; 18 | 19 | // This could just be the type for the `baseOptions` field of the Base Service 20 | // but to avoid a circular dependency or a refactor, this will do for now 21 | 22 | /** 23 | * The request object containing the headers property that 24 | * authentication information will be added to. 25 | */ 26 | export interface AuthenticateOptions { 27 | /** 28 | * Headers to augment with authentication information. 29 | */ 30 | headers?: OutgoingHttpHeaders; 31 | 32 | [propName: string]: any; 33 | } 34 | 35 | /** 36 | * This interface defines the common methods associated with an Authenticator 37 | * implementation. 38 | */ 39 | export interface AuthenticatorInterface { 40 | /** 41 | * Add authentication information to the specified request. 42 | * 43 | * @param requestOptions - The request to which authentication information is added 44 | * (in the headers field). 45 | */ 46 | authenticate(requestOptions: AuthenticateOptions): Promise; 47 | 48 | /** 49 | * Returns a string that indicates the authentication type. 50 | */ 51 | authenticationType(): string; 52 | } 53 | -------------------------------------------------------------------------------- /auth/authenticators/authenticator.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable @typescript-eslint/no-unused-vars, class-methods-use-this */ 2 | 3 | /** 4 | * (C) Copyright IBM Corp. 2019, 2025. 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | 19 | import { AuthenticateOptions, AuthenticatorInterface } from './authenticator-interface'; 20 | 21 | /** 22 | * Base Authenticator class for other Authenticators to extend. Not intended 23 | * to be used as a stand-alone authenticator. 24 | */ 25 | export class Authenticator implements AuthenticatorInterface { 26 | /** 27 | * Constants that define the various authenticator types. 28 | */ 29 | static AUTHTYPE_BASIC = 'basic'; 30 | 31 | static AUTHTYPE_BEARERTOKEN = 'bearerToken'; 32 | 33 | static AUTHTYPE_IAM = 'iam'; 34 | 35 | static AUTHTYPE_IAM_ASSUME = 'iamAssume'; 36 | 37 | static AUTHTYPE_CONTAINER = 'container'; 38 | 39 | static AUTHTYPE_CP4D = 'cp4d'; 40 | 41 | static AUTHTYPE_NOAUTH = 'noAuth'; 42 | 43 | static AUTHTYPE_VPC = 'vpc'; 44 | 45 | static AUTHTYPE_MCSP = 'mcsp'; 46 | 47 | static AUTHTYPE_MCSPV2 = 'mcspv2'; 48 | 49 | static AUTHTYPE_UNKNOWN = 'unknown'; 50 | 51 | /** 52 | * Create a new Authenticator instance. 53 | * 54 | * @throws Error: the "new" keyword was not used to construct the authenticator. 55 | */ 56 | constructor() { 57 | if (!(this instanceof Authenticator)) { 58 | throw new Error('the "new" keyword is required to create authenticator instances'); 59 | } 60 | } 61 | 62 | /** 63 | * Augment the request with authentication information. 64 | * 65 | * @param requestOptions - The request to augment with authentication information. 66 | * @throws Error: The authenticate method was not implemented by a subclass. 67 | */ 68 | public authenticate(requestOptions: AuthenticateOptions): Promise { 69 | const error = new Error('Should be implemented by subclass!'); 70 | return Promise.reject(error); 71 | } 72 | 73 | /** 74 | * Retrieves the authenticator's type. 75 | * The returned value will be the same string that is used 76 | * when configuring an instance of the authenticator with the 77 | * \_AUTH_TYPE configuration property 78 | * (e.g. "basic", "iam", etc.). 79 | * This function should be overridden in each authenticator 80 | * implementation class that extends this class. 81 | * 82 | * @returns a string that indicates the authenticator's type 83 | */ 84 | public authenticationType(): string { 85 | return Authenticator.AUTHTYPE_UNKNOWN; 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /auth/authenticators/basic-authenticator.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * (C) Copyright IBM Corp. 2019, 2024. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | import extend from 'extend'; 18 | import { computeBasicAuthHeader, validateInput } from '../utils/helpers'; 19 | import { Authenticator } from './authenticator'; 20 | import { AuthenticateOptions } from './authenticator-interface'; 21 | import logger from '../../lib/logger'; 22 | 23 | /** Configuration options for basic authentication. */ 24 | export type Options = { 25 | /** The username to be used in basic authorization. */ 26 | username: string; 27 | /** The password to be used in basic authorization. */ 28 | password: string; 29 | }; 30 | 31 | /** 32 | * The BasicAuthenticator is used to add basic authentication information to 33 | * requests. 34 | * 35 | * Basic Authorization will be sent as an Authorization header in the form: 36 | * 37 | * Authorization: Basic \ 38 | * 39 | */ 40 | export class BasicAuthenticator extends Authenticator { 41 | protected requiredOptions = ['username', 'password']; 42 | 43 | protected authHeader: { 44 | Authorization: string; 45 | }; 46 | 47 | /** 48 | * Create a new BasicAuthenticator instance. 49 | * 50 | * @param options - Configuration options for basic authentication. 51 | * This should be an object containing these fields: 52 | * - username: the username portion of basic authentication 53 | * - password: the password portion of basic authentication 54 | * 55 | * @throws Error: the configuration options are not valid. 56 | */ 57 | constructor(options: Options) { 58 | super(); 59 | validateInput(options, this.requiredOptions); 60 | const { username, password } = options; 61 | const authHeaderString = computeBasicAuthHeader(username, password); 62 | this.authHeader = { Authorization: authHeaderString }; 63 | } 64 | 65 | /** 66 | * Add basic authentication information to `requestOptions`. The basic authentication information 67 | * will be set in the Authorization property of `requestOptions.headers` in the form: 68 | * 69 | * Authorization: Basic \ 70 | * 71 | * @param requestOptions - The request to augment with authentication information. 72 | */ 73 | public authenticate(requestOptions: AuthenticateOptions): Promise { 74 | return new Promise((resolve) => { 75 | requestOptions.headers = extend(true, {}, requestOptions.headers, this.authHeader); 76 | logger.debug(`Authenticated outbound request (type=${this.authenticationType()})`); 77 | resolve(); 78 | }); 79 | } 80 | 81 | /** 82 | * Returns the authenticator's type ('basic'). 83 | * 84 | * @returns a string that indicates the authenticator's type 85 | */ 86 | // eslint-disable-next-line class-methods-use-this 87 | public authenticationType(): string { 88 | return Authenticator.AUTHTYPE_BASIC; 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /auth/authenticators/bearer-token-authenticator.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * (C) Copyright IBM Corp. 2019, 2023. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | import extend from 'extend'; 18 | import { validateInput } from '../utils/helpers'; 19 | import { Authenticator } from './authenticator'; 20 | import { AuthenticateOptions } from './authenticator-interface'; 21 | import logger from '../../lib/logger'; 22 | 23 | /** Configuration options for bearer authentication. */ 24 | export type Options = { 25 | /** The bearer token to be added to requests. */ 26 | bearerToken: string; 27 | }; 28 | 29 | /** 30 | * The BearerTokenAuthenticator will set a user-provided bearer token 31 | * in requests. 32 | * 33 | * The bearer token will be sent as an Authorization header in the form: 34 | * 35 | * Authorization: Bearer \ 36 | */ 37 | export class BearerTokenAuthenticator extends Authenticator { 38 | protected requiredOptions = ['bearerToken']; 39 | 40 | private bearerToken: string; 41 | 42 | /** 43 | * Create a new BearerTokenAuthenticator instance. 44 | * 45 | * @param options - Configuration options for bearer authentication. 46 | * This should be an object containing the "bearerToken" field. 47 | * 48 | * @throws Error: the options.bearerToken field is not valid, or unspecified 49 | */ 50 | constructor(options: Options) { 51 | super(); 52 | 53 | validateInput(options, this.requiredOptions); 54 | 55 | this.bearerToken = options.bearerToken; 56 | } 57 | 58 | /** 59 | * Set a new bearer token to be sent in subsequent requests. 60 | * 61 | * @param bearerToken - The bearer token that will be sent in service 62 | * requests. 63 | */ 64 | public setBearerToken(bearerToken: string): void { 65 | this.bearerToken = bearerToken; 66 | } 67 | 68 | /** 69 | * Add a bearer token to `requestOptions`. The bearer token information 70 | * will be set in the Authorization property of "requestOptions.headers" in the form: 71 | * 72 | * Authorization: Bearer \ 73 | * 74 | * @param requestOptions - The request to augment with authentication information. 75 | */ 76 | public authenticate(requestOptions: AuthenticateOptions): Promise { 77 | return new Promise((resolve) => { 78 | const authHeader = { Authorization: `Bearer ${this.bearerToken}` }; 79 | requestOptions.headers = extend(true, {}, requestOptions.headers, authHeader); 80 | logger.debug(`Authenticated outbound request (type=${this.authenticationType()})`); 81 | resolve(); 82 | }); 83 | } 84 | 85 | /** 86 | * Returns the authenticator's type ('bearertoken'). 87 | * 88 | * @returns a string that indicates the authenticator's type 89 | */ 90 | // eslint-disable-next-line class-methods-use-this 91 | public authenticationType(): string { 92 | return Authenticator.AUTHTYPE_BEARERTOKEN; 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /auth/authenticators/cloud-pak-for-data-authenticator.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * (C) Copyright IBM Corp. 2019, 2023. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | import { Authenticator } from './authenticator'; 18 | import { Cp4dTokenManager } from '../token-managers/cp4d-token-manager'; 19 | import { BaseOptions, TokenRequestBasedAuthenticator } from './token-request-based-authenticator'; 20 | 21 | /** Configuration options for CloudPakForData authentication. */ 22 | export interface Options extends BaseOptions { 23 | /** The username used to obtain a bearer token. */ 24 | username: string; 25 | /** The password used to obtain a bearer token [required if apikey not specified]. */ 26 | password?: string; 27 | /** The API key used to obtain a bearer token [required if password not specified]. */ 28 | apikey?: string; 29 | /** The URL representing the Cloud Pak for Data token service endpoint. */ 30 | url: string; 31 | } 32 | 33 | /** 34 | * The CloudPakForDataAuthenticator will either use a username/password pair or a username/apikey pair to obtain 35 | * a bearer token from a token server. When the bearer token expires, a new token is obtained from the token server. 36 | * 37 | * The bearer token will be sent as an Authorization header in the form: 38 | * 39 | * Authorization: Bearer \ 40 | */ 41 | export class CloudPakForDataAuthenticator extends TokenRequestBasedAuthenticator { 42 | protected requiredOptions = ['username', 'url']; 43 | 44 | protected tokenManager: Cp4dTokenManager; 45 | 46 | private username: string; 47 | 48 | private password: string; 49 | 50 | private apikey: string; 51 | 52 | /** 53 | * Create a new CloudPakForDataAuthenticator instance. 54 | * 55 | * @param options - Configuration options for CloudPakForData authentication. 56 | * This should be an object containing these fields: 57 | * - url: (required) the endpoint URL for the CloudPakForData token service 58 | * - username: (required) the username used to obtain a bearer token 59 | * - password: (optional) the password used to obtain a bearer token (required if apikey is not specified) 60 | * - apikey: (optional) the API key used to obtain a bearer token (required if password is not specified) 61 | * - disableSslVerification: (optional) a flag that indicates whether verification of the token server's SSL certificate 62 | * should be disabled or not 63 | * - headers: (optional) a set of HTTP headers to be sent with each request to the token service 64 | * 65 | * @throws Error: the username, password, and/or url are not valid, or unspecified, for Cloud Pak For Data token requests. 66 | */ 67 | constructor(options: Options) { 68 | super(options); 69 | 70 | this.username = options.username; 71 | this.password = options.password; 72 | this.apikey = options.apikey; 73 | 74 | // the param names are shared between the authenticator and the token 75 | // manager so we can just pass along the options object. 76 | // also, the token manager will handle input validation 77 | this.tokenManager = new Cp4dTokenManager(options); 78 | } 79 | 80 | /** 81 | * Returns the authenticator's type ('cp4d'). 82 | * 83 | * @returns a string that indicates the authenticator's type 84 | */ 85 | // eslint-disable-next-line class-methods-use-this 86 | public authenticationType(): string { 87 | return Authenticator.AUTHTYPE_CP4D; 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /auth/authenticators/iam-assume-authenticator.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * (C) Copyright IBM Corp. 2024. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | import { Authenticator } from './authenticator'; 18 | import { IamAssumeTokenManager } from '../token-managers'; 19 | import { 20 | IamRequestOptions, 21 | IamRequestBasedAuthenticatorImmutable, 22 | } from './iam-request-based-authenticator-immutable'; 23 | 24 | /** Configuration options for IAM Assume authentication. */ 25 | export interface Options extends IamRequestOptions { 26 | /** The IAM api key */ 27 | apikey: string; 28 | 29 | /** 30 | * Specify exactly one of [iamProfileId, iamProfileCrn, or iamProfileName] to 31 | * identify the trusted profile whose identity should be used. If iamProfileId 32 | * or iamProfileCrn is used, the trusted profile must exist in the same account. 33 | * If and only if iamProfileName is used, then iamAccountId must also be 34 | * specified to indicate the account that contains the trusted profile. 35 | */ 36 | iamProfileId?: string; 37 | iamProfileCrn?: string; 38 | iamProfileName?: string; 39 | 40 | /** 41 | * If and only if iamProfileName is used to specify the trusted profile, then 42 | * iamAccountId must also be specified to indicate the account that contains 43 | * the trusted profile. 44 | */ 45 | iamAccountId?: string; 46 | } 47 | 48 | /** 49 | * The IamAssumeAuthenticator obtains an IAM access token using the IAM "get-token" 50 | * operation's "assume" grant type. The authenticator obtains an initial IAM access 51 | * token from a user-supplied apikey, then exchanges this initial IAM access token 52 | * for another IAM access token that has "assumed the identity" of the specified 53 | * trusted profile. 54 | * 55 | * The bearer token will be sent as an Authorization header in the form: 56 | * 57 | * Authorization: Bearer \ 58 | */ 59 | export class IamAssumeAuthenticator extends IamRequestBasedAuthenticatorImmutable { 60 | protected tokenManager: IamAssumeTokenManager; 61 | 62 | /** 63 | * 64 | * Create a new IamAssumeAuthenticator instance. 65 | * 66 | * @param options - Configuration options for IAM authentication. 67 | * This should be an object containing these fields: 68 | * - apikey: (required) the IAM api key for initial token request 69 | * - iamProfileId: (optional) the ID of the trusted profile to use 70 | * - iamProfileCrn: (optional) the CRN of the trusted profile to use 71 | * - iamProfileName: (optional) the name of the trusted profile to use (must be specified with iamAccountId) 72 | * - iamAccountId: (optional) the ID of the account the trusted profile is in (must be specified with iamProfileName) 73 | * - url: (optional) the endpoint URL for the token service 74 | * - disableSslVerification: (optional) a flag that indicates whether verification of the token server's SSL certificate 75 | * should be disabled or not 76 | * - headers: (optional) a set of HTTP headers to be sent with each request to the token service 77 | * - clientId: (optional) the "clientId" and "clientSecret" fields are used to form a Basic 78 | * Authorization header to be included in each request to the token service 79 | * - clientSecret: (optional) the "clientId" and "clientSecret" fields are used to form a Basic 80 | * Authorization header to be included in each request to the token service 81 | * - scope: (optional) the "scope" parameter to use when fetching the bearer token from the token service 82 | * 83 | * @throws Error: the configuration options are not valid. 84 | */ 85 | constructor(options: Options) { 86 | super(options); 87 | 88 | // The param names are shared between the authenticator and the token 89 | // manager so we can just pass along the options object. This will 90 | // also perform input validation on the options. 91 | this.tokenManager = new IamAssumeTokenManager(options); 92 | } 93 | 94 | /** 95 | * Returns the authenticator's type ('iamAssume'). 96 | * 97 | * @returns a string that indicates the authenticator's type 98 | */ 99 | // eslint-disable-next-line class-methods-use-this 100 | public authenticationType(): string { 101 | return Authenticator.AUTHTYPE_IAM_ASSUME; 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /auth/authenticators/iam-authenticator.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * (C) Copyright IBM Corp. 2019, 2023. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | import { Authenticator } from './authenticator'; 18 | import { IamTokenManager } from '../token-managers/iam-token-manager'; 19 | import { validateInput } from '../utils/helpers'; 20 | import { IamRequestOptions, IamRequestBasedAuthenticator } from './iam-request-based-authenticator'; 21 | 22 | /** Configuration options for IAM authentication. */ 23 | export interface Options extends IamRequestOptions { 24 | /** The IAM api key */ 25 | apikey: string; 26 | } 27 | 28 | /** 29 | * The IamAuthenticator will use the user-supplied `apikey` 30 | * value to obtain a bearer token from a token server. When the bearer token 31 | * expires, a new token is obtained from the token server. If specified, the 32 | * optional, mutually inclusive "clientId" and "clientSecret" pair can be used to 33 | * influence rate-limiting for requests to the IAM token server. 34 | * 35 | * The bearer token will be sent as an Authorization header in the form: 36 | * 37 | * Authorization: Bearer \ 38 | */ 39 | export class IamAuthenticator extends IamRequestBasedAuthenticator { 40 | protected requiredOptions = ['apikey']; 41 | 42 | protected tokenManager: IamTokenManager; 43 | 44 | private apikey: string; 45 | 46 | /** 47 | * 48 | * Create a new IamAuthenticator instance. 49 | * 50 | * @param options - Configuration options for IAM authentication. 51 | * This should be an object containing these fields: 52 | * - url: (optional) the endpoint URL for the token service 53 | * - apikey: (required) the IAM api key 54 | * - disableSslVerification: (optional) a flag that indicates whether verification of the token server's SSL certificate 55 | * should be disabled or not 56 | * - headers: (optional) a set of HTTP headers to be sent with each request to the token service 57 | * - clientId: (optional) the "clientId" and "clientSecret" fields are used to form a Basic 58 | * Authorization header to be included in each request to the token service 59 | * - clientSecret: (optional) the "clientId" and "clientSecret" fields are used to form a Basic 60 | * Authorization header to be included in each request to the token service 61 | * - scope: (optional) the "scope" parameter to use when fetching the bearer token from the token service 62 | * 63 | * @throws Error: the configuration options are not valid. 64 | */ 65 | constructor(options: Options) { 66 | super(options); 67 | 68 | validateInput(options, this.requiredOptions); 69 | 70 | this.apikey = options.apikey; 71 | 72 | // the param names are shared between the authenticator and the token 73 | // manager so we can just pass along the options object 74 | this.tokenManager = new IamTokenManager(options); 75 | } 76 | 77 | /** 78 | * Returns the authenticator's type ('iam'). 79 | * 80 | * @returns a string that indicates the authenticator's type 81 | */ 82 | // eslint-disable-next-line class-methods-use-this 83 | public authenticationType(): string { 84 | return Authenticator.AUTHTYPE_IAM; 85 | } 86 | 87 | /** 88 | * Return the most recently stored refresh token. 89 | * 90 | * @returns the refresh token string 91 | */ 92 | public getRefreshToken(): string { 93 | return this.tokenManager.getRefreshToken(); 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /auth/authenticators/iam-request-based-authenticator-immutable.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * (C) Copyright IBM Corp. 2024. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | import { IamRequestBasedTokenManager } from '../token-managers/iam-request-based-token-manager'; 18 | import { 19 | BaseOptions, 20 | TokenRequestBasedAuthenticatorImmutable, 21 | } from './token-request-based-authenticator-immutable'; 22 | 23 | /** Configuration options for IAM Request based authentication. */ 24 | export interface IamRequestOptions extends BaseOptions { 25 | /** 26 | * The `clientId` and `clientSecret` fields are used to form a "basic" 27 | * authorization header for IAM token requests. 28 | */ 29 | clientId?: string; 30 | /** 31 | * The `clientId` and `clientSecret` fields are used to form a "basic" 32 | * authorization header for IAM token requests. 33 | */ 34 | clientSecret?: string; 35 | 36 | /** 37 | * The "scope" parameter to use when fetching the bearer token from the IAM token server. 38 | */ 39 | scope?: string; 40 | } 41 | 42 | /** 43 | * The IamRequestBasedAuthenticatorImmutable provides shared configuration and functionality 44 | * for authenticators that interact with the IAM token service. This authenticator 45 | * is not meant for use on its own. 46 | */ 47 | export class IamRequestBasedAuthenticatorImmutable extends TokenRequestBasedAuthenticatorImmutable { 48 | protected tokenManager: IamRequestBasedTokenManager; 49 | 50 | protected clientId: string; 51 | 52 | protected clientSecret: string; 53 | 54 | protected scope: string; 55 | 56 | /** 57 | * 58 | * Create a new IamRequestBasedAuthenticatorImmutable instance. 59 | * 60 | * @param options - Configuration options for IAM authentication. 61 | * This should be an object containing these fields: 62 | * - url: (optional) the endpoint URL for the token service 63 | * - disableSslVerification: (optional) a flag that indicates whether verification of the token server's SSL certificate 64 | * should be disabled or not 65 | * - headers: (optional) a set of HTTP headers to be sent with each request to the token service 66 | * - clientId: (optional) the "clientId" and "clientSecret" fields are used to form a Basic 67 | * Authorization header to be included in each request to the token service 68 | * - clientSecret: (optional) the "clientId" and "clientSecret" fields are used to form a Basic 69 | * Authorization header to be included in each request to the token service 70 | * - scope: (optional) the "scope" parameter to use when fetching the bearer token from the token service 71 | * 72 | * @throws Error: the configuration options are not valid. 73 | */ 74 | constructor(options: IamRequestOptions) { 75 | // all parameters are optional 76 | options = options || ({} as IamRequestOptions); 77 | 78 | super(options); 79 | 80 | this.clientId = options.clientId; 81 | this.clientSecret = options.clientSecret; 82 | this.scope = options.scope; 83 | 84 | this.tokenManager = new IamRequestBasedTokenManager(options); 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /auth/authenticators/iam-request-based-authenticator.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * (C) Copyright IBM Corp. 2019, 2024. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | import { OutgoingHttpHeaders } from 'http'; 18 | import { IamRequestBasedAuthenticatorImmutable } from './iam-request-based-authenticator-immutable'; 19 | 20 | /** Shared configuration options for IAM Request based authentication. */ 21 | export { IamRequestOptions } from './iam-request-based-authenticator-immutable'; 22 | 23 | /** 24 | * The IamRequestBasedAuthenticator provides shared configuration and functionality 25 | * for authenticators that interact with the IAM token service. This authenticator 26 | * is not meant for use on its own. 27 | */ 28 | export class IamRequestBasedAuthenticator extends IamRequestBasedAuthenticatorImmutable { 29 | /** 30 | * Setter for the mutually inclusive "clientId" and the "clientSecret" fields. 31 | * @param clientId - the "clientId" value used to form a Basic Authorization header for IAM token requests 32 | * @param clientSecret - the "clientSecret" value used to form a Basic Authorization header for IAM token requests 33 | */ 34 | public setClientIdAndSecret(clientId: string, clientSecret: string): void { 35 | this.clientId = clientId; 36 | this.clientSecret = clientSecret; 37 | 38 | // update properties in token manager 39 | this.tokenManager.setClientIdAndSecret(clientId, clientSecret); 40 | } 41 | 42 | /** 43 | * Setter for the "scope" parameter to use when fetching the bearer token from the IAM token server. 44 | * @param scope - (optional) a space-separated string that specifies one or more scopes to be 45 | * associated with IAM token requests 46 | */ 47 | public setScope(scope: string): void { 48 | this.scope = scope; 49 | 50 | // update properties in token manager 51 | this.tokenManager.setScope(scope); 52 | } 53 | 54 | /** 55 | * Set the flag that indicates whether verification of the server's SSL 56 | * certificate should be disabled or not. 57 | * 58 | * @param value - a flag that indicates whether verification of the 59 | * token server's SSL certificate should be disabled or not. 60 | */ 61 | public setDisableSslVerification(value: boolean): void { 62 | // if they try to pass in a non-boolean value, 63 | // use the "truthy-ness" of the value 64 | this.disableSslVerification = Boolean(value); 65 | this.tokenManager.setDisableSslVerification(this.disableSslVerification); 66 | } 67 | 68 | /** 69 | * Set headers. 70 | * 71 | * @param headers - a set of HTTP headers to be sent with each outbound token server request. 72 | * Overwrites previous default headers. 73 | */ 74 | public setHeaders(headers: OutgoingHttpHeaders): void { 75 | if (typeof headers !== 'object') { 76 | // do nothing, for now 77 | return; 78 | } 79 | this.headers = headers; 80 | this.tokenManager.setHeaders(this.headers); 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /auth/authenticators/index.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * (C) Copyright IBM Corp. 2019, 2023. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | /** 18 | * @module authenticators 19 | * The ibm-cloud-sdk-core module supports the following types of authentication: 20 | * 21 | * Basic Authentication 22 | * Bearer Token 23 | * Identity and Access Management (IAM, grant type: apikey) 24 | * Identity and Access Management (IAM, grant type: assume) 25 | * Container (IKS, etc) 26 | * VPC Instance 27 | * Cloud Pak for Data 28 | * No Authentication 29 | * 30 | * The supported authentication types may vary from service to service. Each 31 | * authentication type is implemented as an Authenticator for consumption by a service. 32 | * 33 | * classes: 34 | * AuthenticatorInterface: Implement this interface to provide custom authentication schemes to services. 35 | * Authenticator: Extend this class to provide custom authentication schemes to services. 36 | * BasicAuthenticator: Authenticator for passing supplied basic authentication information to service endpoint. 37 | * BearerTokenAuthenticator: Authenticator for passing supplied bearer token to service endpoint. 38 | * CloudPakForDataAuthenticator: Authenticator for passing CP4D authentication information to service endpoint. 39 | * IAMAuthenticator: Authenticator for passing IAM authentication information to service endpoint. 40 | * IAMAssumeAuthenticator: Authenticator for passing IAM authentication information to service endpoint, assuming a trusted profile. 41 | * ContainerAuthenticator: Authenticator for passing IAM authentication to a service, based on a token living on the container. 42 | * VpcInstanceAuthenticator: Authenticator that uses the VPC Instance Metadata Service API to retrieve an IAM token. 43 | * McspAuthenticator: Authenticator for passing MCSP v1 authentication to a service endpoint. 44 | * McspV2Authenticator: Authenticator for passing MCSP v2 authentication to a service endpoint. 45 | * NoAuthAuthenticator: Performs no authentication. Useful for testing purposes. 46 | */ 47 | 48 | export { AuthenticatorInterface } from './authenticator-interface'; 49 | export { Authenticator } from './authenticator'; 50 | export { BasicAuthenticator } from './basic-authenticator'; 51 | export { BearerTokenAuthenticator } from './bearer-token-authenticator'; 52 | export { CloudPakForDataAuthenticator } from './cloud-pak-for-data-authenticator'; 53 | export { IamAuthenticator } from './iam-authenticator'; 54 | export { ContainerAuthenticator } from './container-authenticator'; 55 | export { NoAuthAuthenticator } from './no-auth-authenticator'; 56 | export { IamRequestBasedAuthenticator } from './iam-request-based-authenticator'; 57 | export { TokenRequestBasedAuthenticator } from './token-request-based-authenticator'; 58 | export { VpcInstanceAuthenticator } from './vpc-instance-authenticator'; 59 | export { McspAuthenticator } from './mcsp-authenticator'; 60 | export { McspV2Authenticator } from './mcspv2-authenticator'; 61 | export { IamAssumeAuthenticator } from './iam-assume-authenticator'; 62 | -------------------------------------------------------------------------------- /auth/authenticators/mcsp-authenticator.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * (C) Copyright IBM Corp. 2023, 2025. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | import { Authenticator } from './authenticator'; 18 | import { McspTokenManager } from '../token-managers/mcsp-token-manager'; 19 | import { BaseOptions, TokenRequestBasedAuthenticator } from './token-request-based-authenticator'; 20 | 21 | /** Configuration options for Multi-Cloud Saas Platform (MCSP) authentication. */ 22 | export interface Options extends BaseOptions { 23 | /** The API key used to obtain an MCSP access token. */ 24 | apikey: string; 25 | /** The URL representing the MCSP token service endpoint. */ 26 | url: string; 27 | } 28 | 29 | /** 30 | * The McspAuthenticator uses an apikey to obtain an access token from the MCSP token server. 31 | * When the access token expires, a new access token is obtained from the token server. 32 | * The access token will be added to outbound requests via the Authorization header 33 | * of the form: "Authorization: Bearer " 34 | */ 35 | export class McspAuthenticator extends TokenRequestBasedAuthenticator { 36 | protected requiredOptions = ['apikey', 'url']; 37 | 38 | protected tokenManager: McspTokenManager; 39 | 40 | private apikey: string; 41 | 42 | /** 43 | * Create a new McspAuthenticator instance. 44 | * 45 | * @param options - Configuration options for CloudPakForData authentication. 46 | * This should be an object containing these fields: 47 | * - url: (required) the endpoint URL for the CloudPakForData token service 48 | * - apikey: (optional) the API key used to obtain a bearer token (required if password is not specified) 49 | * - disableSslVerification: (optional) a flag that indicates whether verification of the token server's SSL certificate 50 | * should be disabled or not 51 | * - headers: (optional) a set of HTTP headers to be sent with each request to the token service 52 | * 53 | * @throws Error: the username, password, and/or url are not valid, or unspecified, for Cloud Pak For Data token requests. 54 | */ 55 | constructor(options: Options) { 56 | super(options); 57 | 58 | this.apikey = options.apikey; 59 | this.url = options.url; 60 | 61 | // the param names are shared between the authenticator and the token 62 | // manager so we can just pass along the options object. 63 | // also, the token manager will handle input validation 64 | this.tokenManager = new McspTokenManager(options); 65 | } 66 | 67 | /** 68 | * Returns the authenticator's type ('mcsp'). 69 | * 70 | * @returns a string that indicates the authenticator's type 71 | */ 72 | // eslint-disable-next-line class-methods-use-this 73 | public authenticationType(): string { 74 | return Authenticator.AUTHTYPE_MCSP; 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /auth/authenticators/no-auth-authenticator.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable @typescript-eslint/no-unused-vars, class-methods-use-this */ 2 | 3 | /** 4 | * (C) Copyright IBM Corp. 2019, 2021. 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | 19 | import { Authenticator } from './authenticator'; 20 | import { AuthenticateOptions } from './authenticator-interface'; 21 | 22 | /** 23 | * NoAuthAuthenticator is a placeholder authenticator implementation which 24 | * performs no authentication of outgoing REST API requests. It might be 25 | * useful during development and testing. 26 | */ 27 | export class NoAuthAuthenticator extends Authenticator { 28 | public authenticate(requestOptions: AuthenticateOptions): Promise { 29 | // immediately proceed to request. it will probably fail 30 | return Promise.resolve(); 31 | } 32 | 33 | /** 34 | * Returns the authenticator's type ('noauth'). 35 | * 36 | * @returns a string that indicates the authenticator's type 37 | */ 38 | public authenticationType(): string { 39 | return Authenticator.AUTHTYPE_NOAUTH; 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /auth/authenticators/token-request-based-authenticator-immutable.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * (C) Copyright IBM Corp. 2024. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | import extend from 'extend'; 18 | import { OutgoingHttpHeaders } from 'http'; 19 | import { JwtTokenManager } from '../token-managers/jwt-token-manager'; 20 | import { Authenticator } from './authenticator'; 21 | import { AuthenticateOptions } from './authenticator-interface'; 22 | import logger from '../../lib/logger'; 23 | 24 | /** Configuration options for token-based authentication. */ 25 | export type BaseOptions = { 26 | /** Headers to be sent with every outbound HTTP requests to token services. */ 27 | headers?: OutgoingHttpHeaders; 28 | /** 29 | * A flag that indicates whether verification of the token server's SSL 30 | * certificate should be disabled or not. 31 | */ 32 | disableSslVerification?: boolean; 33 | /** Endpoint for HTTP token requests. */ 34 | url?: string; 35 | /** Allow additional request config parameters */ 36 | [propName: string]: any; 37 | }; 38 | 39 | /** 40 | * Class for common functionality shared by token-request authenticators. 41 | * Token-request authenticators use token managers to retrieve, store, 42 | * and refresh tokens. Not intended to be used as stand-alone authenticator, 43 | * but as base class to authenticators that have their own token manager 44 | * implementations. 45 | * 46 | * The token will be added as an Authorization header in the form: 47 | * 48 | * Authorization: Bearer \ 49 | */ 50 | export class TokenRequestBasedAuthenticatorImmutable extends Authenticator { 51 | protected tokenManager: JwtTokenManager; 52 | 53 | protected url: string; 54 | 55 | protected headers: OutgoingHttpHeaders; 56 | 57 | protected disableSslVerification: boolean; 58 | 59 | /** 60 | * Create a new TokenRequestBasedAuthenticatorImmutable instance with an internal JwtTokenManager. 61 | * 62 | * @param options - Configuration options. 63 | * This should be an object containing these fields: 64 | * - url: (optional) the endpoint URL for the token service 65 | * - disableSslVerification: (optional) a flag that indicates whether verification of the token server's SSL certificate 66 | * should be disabled or not 67 | * - headers: (optional) a set of HTTP headers to be sent with each request to the token service 68 | */ 69 | constructor(options: BaseOptions) { 70 | super(); 71 | 72 | this.disableSslVerification = Boolean(options.disableSslVerification); 73 | this.url = options.url; 74 | 75 | // default to empty object 76 | this.headers = options.headers || {}; 77 | 78 | this.tokenManager = new JwtTokenManager(options); 79 | } 80 | 81 | /** 82 | * Adds bearer token information to "requestOptions". The bearer token information 83 | * will be set in the Authorization property of "requestOptions.headers" in the form: 84 | * 85 | * Authorization: Bearer \ 86 | * 87 | * @param requestOptions - The request to augment with authentication information. 88 | */ 89 | public authenticate(requestOptions: AuthenticateOptions): Promise { 90 | return this.tokenManager.getToken().then((token) => { 91 | const authHeader = { Authorization: `Bearer ${token}` }; 92 | requestOptions.headers = extend(true, {}, requestOptions.headers, authHeader); 93 | logger.debug(`Authenticated outbound request (type=${this.authenticationType()})`); 94 | }); 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /auth/authenticators/token-request-based-authenticator.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * (C) Copyright IBM Corp. 2019, 2024. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | import { OutgoingHttpHeaders } from 'http'; 18 | import { TokenRequestBasedAuthenticatorImmutable } from './token-request-based-authenticator-immutable'; 19 | 20 | /** Configuration options for token-based authentication. */ 21 | export { BaseOptions } from './token-request-based-authenticator-immutable'; 22 | 23 | /** 24 | * Class for common functionality shared by token-request authenticators. 25 | * TokenRequestBasedAuthenticators use token managers to retrieve, store, 26 | * and refresh tokens. Not intended to be used as stand-alone authenticator, 27 | * but as base class to authenticators that have their own token manager 28 | * implementations. 29 | * 30 | * The token will be added as an Authorization header in the form: 31 | * 32 | * Authorization: Bearer \ 33 | */ 34 | export class TokenRequestBasedAuthenticator extends TokenRequestBasedAuthenticatorImmutable { 35 | /** 36 | * Set the flag that indicates whether verification of the server's SSL 37 | * certificate should be disabled or not. 38 | * 39 | * @param value - a flag that indicates whether verification of the 40 | * token server's SSL certificate should be disabled or not. 41 | */ 42 | public setDisableSslVerification(value: boolean): void { 43 | // if they try to pass in a non-boolean value, 44 | // use the "truthy-ness" of the value 45 | this.disableSslVerification = Boolean(value); 46 | this.tokenManager.setDisableSslVerification(this.disableSslVerification); 47 | } 48 | 49 | /** 50 | * Set headers. 51 | * 52 | * @param headers - a set of HTTP headers to be sent with each outbound token server request. 53 | * Overwrites previous default headers. 54 | */ 55 | public setHeaders(headers: OutgoingHttpHeaders): void { 56 | if (typeof headers !== 'object') { 57 | // do nothing, for now 58 | return; 59 | } 60 | this.headers = headers; 61 | this.tokenManager.setHeaders(this.headers); 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /auth/authenticators/vpc-instance-authenticator.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * (C) Copyright IBM Corp. 2021, 2023. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | import { Authenticator } from './authenticator'; 18 | import { VpcInstanceTokenManager } from '../token-managers/vpc-instance-token-manager'; 19 | import { BaseOptions, TokenRequestBasedAuthenticator } from './token-request-based-authenticator'; 20 | 21 | /** Configuration options for VpcInstance authentication. */ 22 | export interface Options extends BaseOptions { 23 | /** The CRN of the linked trusted IAM profile to be used as the identity of the compute resource */ 24 | iamProfileCrn?: string; 25 | /** The ID of the linked trusted IAM profile to be used when obtaining the IAM access token */ 26 | iamProfileId?: string; 27 | } 28 | 29 | /** 30 | * The VpcInstanceAuthenticator implements an authentication scheme in which it retrieves an "instance identity token" 31 | * and exchanges that for an IAM access token using the VPC Instance Metadata Service API which is available on the local 32 | * compute resource (VM). The instance identity token is similar to an IAM apikey, except that it is managed automatically 33 | * by the compute resource provider (VPC). 34 | * 35 | * The resulting IAM access token is then added to outbound requests in an Authorization header 36 | * 37 | * Authorization: Bearer \ 38 | */ 39 | export class VpcInstanceAuthenticator extends TokenRequestBasedAuthenticator { 40 | protected tokenManager: VpcInstanceTokenManager; 41 | 42 | private iamProfileCrn: string; 43 | 44 | private iamProfileId: string; 45 | 46 | /** 47 | * Create a new VpcInstanceAuthenticator instance. 48 | * 49 | * @param options - Configuration options for VpcInstance authentication. 50 | * This should be an object containing these fields: 51 | * - url: (optional) the endpoint URL for the VPC Instance Metadata Service (default value: "http://169.254.169.254") 52 | * - iamProfileCrn: (optional) the CRN of the linked IAM trusted profile to be used to obtain the IAM access token 53 | * - iamProfileId: (optional) the ID of the linked IAM trusted profile to be used to obtain the IAM access token 54 | * 55 | * @remarks 56 | * At most one of "iamProfileCrn" or "iamProfileId" may be specified. If neither one is specified, 57 | * then the default IAM profile defined for the compute resource will be used. 58 | */ 59 | constructor(options: Options) { 60 | // all parameters are optional 61 | options = options || ({} as Options); 62 | 63 | super(options); 64 | 65 | if (options.iamProfileCrn) { 66 | this.iamProfileCrn = options.iamProfileCrn; 67 | } 68 | if (options.iamProfileId) { 69 | this.iamProfileId = options.iamProfileId; 70 | } 71 | 72 | // the param names are shared between the authenticator and the token 73 | // manager so we can just pass along the options object. 74 | // also, the token manager will handle input validation 75 | this.tokenManager = new VpcInstanceTokenManager(options); 76 | } 77 | 78 | /** 79 | * Sets the "iamProfileCrn" value to be used when obtaining an IAM access token 80 | * @param iamProfileCrn - the CRN of the linked IAM trusted profile to use when obtaining an IAM access token 81 | */ 82 | public setIamProfileCrn(iamProfileCrn: string): void { 83 | this.iamProfileCrn = iamProfileCrn; 84 | 85 | // update properties in token manager 86 | this.tokenManager.setIamProfileCrn(iamProfileCrn); 87 | } 88 | 89 | /** 90 | * Sets the "iamProfileId" value to be used when obtaining an IAM access token 91 | * @param iamProfileId - the ID of the linked IAM trusted profile to use when obtaining an IAM access token 92 | */ 93 | public setIamProfileId(iamProfileId: string): void { 94 | this.iamProfileId = iamProfileId; 95 | 96 | // update properties in token manager 97 | this.tokenManager.setIamProfileId(iamProfileId); 98 | } 99 | 100 | /** 101 | * Returns the authenticator's type ('vpc'). 102 | * 103 | * @returns a string that indicates the authenticator's type 104 | */ 105 | // eslint-disable-next-line class-methods-use-this 106 | public authenticationType(): string { 107 | return Authenticator.AUTHTYPE_VPC; 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /auth/index.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2019 IBM Corp. All Rights Reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | export * from './authenticators'; 18 | export * from './token-managers'; 19 | export * from './utils'; 20 | -------------------------------------------------------------------------------- /auth/token-managers/cp4d-token-manager.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * (C) Copyright IBM Corp. 2019, 2024. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | import extend from 'extend'; 18 | import { validateInput } from '../utils/helpers'; 19 | import { buildUserAgent } from '../../lib/build-user-agent'; 20 | import { JwtTokenManager, JwtTokenManagerOptions } from './jwt-token-manager'; 21 | import logger from '../../lib/logger'; 22 | 23 | /** Configuration options for CP4D token retrieval. */ 24 | interface Options extends JwtTokenManagerOptions { 25 | /** The endpoint for CP4D token requests. */ 26 | url: string; 27 | /** The username used to obtain a bearer token. */ 28 | username: string; 29 | /** The password used to obtain a bearer token [required if apikey not specified]. */ 30 | password?: string; 31 | /** The API key used to obtain a bearer token [required if password not specified]. */ 32 | apikey?: string; 33 | } 34 | 35 | // this interface is a representation of the response 36 | // object from the CP4D authentication service 37 | export interface CpdTokenData { 38 | username: string; 39 | role: string; 40 | permissions: string[]; 41 | sub: string; 42 | iss: string; 43 | aud: string; 44 | uid: string; 45 | _messageCode_: string; 46 | message: string; 47 | accessToken: string; 48 | } 49 | 50 | /** 51 | * Token Manager of CloudPak for data. 52 | * 53 | * The Token Manager performs basic auth with a username and password 54 | * to acquire CP4D tokens. 55 | */ 56 | export class Cp4dTokenManager extends JwtTokenManager { 57 | protected requiredOptions = ['username', 'url']; 58 | 59 | private username: string; 60 | 61 | private password: string; 62 | 63 | private apikey: string; 64 | 65 | /** 66 | * Create a new Cp4dTokenManager instance. 67 | * 68 | * @param options - Configuration options 69 | * This should be an object containing these fields: 70 | * - url: (required) the endpoint URL for the CloudPakForData token service 71 | * - username: (required) the username used to obtain a bearer token 72 | * - password: (optional) the password used to obtain a bearer token (required if apikey is not specified) 73 | * - apikey: (optional) the API key used to obtain a bearer token (required if password is not specified) 74 | * - disableSslVerification: (optional) a flag that indicates whether verification of the token server's SSL certificate 75 | * should be disabled or not 76 | * - headers: (optional) a set of HTTP headers to be sent with each request to the token service 77 | * 78 | * @throws Error: the configuration options were invalid. 79 | */ 80 | constructor(options: Options) { 81 | super(options); 82 | 83 | this.tokenName = 'token'; 84 | 85 | if ((!options.password && !options.apikey) || (options.password && options.apikey)) { 86 | throw new Error('Exactly one of `apikey` or `password` must be specified.'); 87 | } 88 | 89 | validateInput(options, this.requiredOptions); 90 | 91 | const tokenApiPath = '/v1/authorize'; 92 | 93 | // do not append the path if user already has 94 | if (this.url && !this.url.endsWith(tokenApiPath)) { 95 | this.url += tokenApiPath; 96 | } 97 | 98 | this.username = options.username; 99 | this.password = options.password; 100 | this.apikey = options.apikey; 101 | 102 | this.userAgent = buildUserAgent('cp4d-authenticator'); 103 | } 104 | 105 | protected requestToken(): Promise { 106 | // these cannot be overwritten 107 | const requiredHeaders = { 108 | 'Content-Type': 'application/json', 109 | 'User-Agent': this.userAgent, 110 | }; 111 | 112 | const parameters = { 113 | options: { 114 | url: this.url, 115 | body: { 116 | username: this.username, 117 | password: this.password, 118 | api_key: this.apikey, 119 | }, 120 | method: 'POST', 121 | headers: extend(true, {}, this.headers, requiredHeaders), 122 | rejectUnauthorized: !this.disableSslVerification, 123 | }, 124 | }; 125 | logger.debug(`Invoking CP4D token service operation: ${parameters.options.url}`); 126 | return this.requestWrapperInstance.sendRequest(parameters).then((response) => { 127 | logger.debug('Returned from CP4D token service operation'); 128 | return response; 129 | }); 130 | } 131 | } 132 | -------------------------------------------------------------------------------- /auth/token-managers/iam-token-manager.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * (C) Copyright IBM Corp. 2019, 2024. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | import { validateInput } from '../utils/helpers'; 18 | import { buildUserAgent } from '../../lib/build-user-agent'; 19 | import { IamRequestBasedTokenManager, IamRequestOptions } from './iam-request-based-token-manager'; 20 | 21 | /** Configuration options for IAM token retrieval. */ 22 | interface Options extends IamRequestOptions { 23 | apikey: string; 24 | } 25 | 26 | /** 27 | * The IamTokenManager takes an api key and performs the necessary interactions with 28 | * the IAM token service to obtain and store a suitable bearer token. Additionally, the IamTokenManager 29 | * will retrieve bearer tokens via basic auth using a supplied "clientId" and "clientSecret" pair. 30 | */ 31 | export class IamTokenManager extends IamRequestBasedTokenManager { 32 | protected requiredOptions = ['apikey']; 33 | 34 | private apikey: string; 35 | 36 | /** 37 | * 38 | * Create a new IamTokenManager instance. 39 | * 40 | * @param options - Configuration options. 41 | * This should be an object containing these fields: 42 | * - url: (optional) the endpoint URL for the IAM token service (default value: "https://iam.cloud.ibm.com") 43 | * - apikey: (required) the IAM api key 44 | * - disableSslVerification: (optional) a flag that indicates whether verification of the token server's SSL certificate 45 | * should be disabled or not 46 | * - headers: (optional) a set of HTTP headers to be sent with each request to the token service 47 | * - clientId: (optional) the "clientId" and "clientSecret" fields are used to form a Basic 48 | * Authorization header to be included in each request to the token service 49 | * - clientSecret: (optional) the "clientId" and "clientSecret" fields are used to form a Basic 50 | * Authorization header to be included in each request to the token service 51 | * - scope: (optional) the "scope" parameter to use when fetching the bearer token from the token service 52 | * 53 | * @throws Error: the configuration options are not valid. 54 | */ 55 | constructor(options: Options) { 56 | super(options); 57 | 58 | validateInput(options, this.requiredOptions); 59 | 60 | this.apikey = options.apikey; 61 | 62 | // construct form data for the apikey use case of iam token management 63 | this.formData.apikey = this.apikey; 64 | this.formData.grant_type = 'urn:ibm:params:oauth:grant-type:apikey'; 65 | this.formData.response_type = 'cloud_iam'; 66 | 67 | this.userAgent = buildUserAgent('iam-authenticator'); 68 | } 69 | 70 | /** 71 | * Returns the most recently stored refresh token. 72 | * 73 | * @returns the refresh token 74 | */ 75 | public getRefreshToken(): string { 76 | return this.refreshToken; 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /auth/token-managers/index.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * (C) Copyright IBM Corp. 2019, 2025. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | /** 18 | * @module token-managers 19 | * The ibm-cloud-sdk-core module supports the following types of token authentication: 20 | * Identity and Access Management (IAM, grant type: apikey) 21 | * Identity and Access Management (IAM, grant type: assume) 22 | * Cloud Pak for Data 23 | * Container (IKS, etc) 24 | * VPC Instance 25 | * Multi-Cloud Saas Platform (MCSP) V1 26 | * Multi-Cloud Saas Platform (MCSP) V2 27 | * 28 | * The token managers sit inside of an authenticator and do the work to retrieve 29 | * tokens, whereas the authenticators add these tokens to the actual request. 30 | * 31 | * classes: 32 | * IamTokenManager: Token Manager of IAM via apikey. 33 | * IamAssumeTokenManager: Token Manager of IAM via apikey and trusted profile. 34 | * Cp4dTokenManager: Token Manager of CloudPak for data. 35 | * ContainerTokenManager: Token manager of IAM via compute resource token. 36 | * VpcInstanceTokenManager: Token manager of VPC Instance Metadata Service API tokens. 37 | * McspTokenManager: Token Manager of MCSP v1 via apikey. 38 | * McspV2TokenManager: Token Manager of MCSP v2 via apikey. 39 | * JwtTokenManager: A class for shared functionality for parsing, storing, and requesting JWT tokens. 40 | */ 41 | 42 | export { IamTokenManager } from './iam-token-manager'; 43 | export { Cp4dTokenManager } from './cp4d-token-manager'; 44 | export { ContainerTokenManager } from './container-token-manager'; 45 | export { IamRequestBasedTokenManager, IamRequestOptions } from './iam-request-based-token-manager'; 46 | export { JwtTokenManager, JwtTokenManagerOptions } from './jwt-token-manager'; 47 | export { TokenManager, TokenManagerOptions } from './token-manager'; 48 | export { VpcInstanceTokenManager } from './vpc-instance-token-manager'; 49 | export { McspTokenManager } from './mcsp-token-manager'; 50 | export { McspV2TokenManager } from './mcspv2-token-manager'; 51 | export { IamAssumeTokenManager } from './iam-assume-token-manager'; 52 | -------------------------------------------------------------------------------- /auth/token-managers/jwt-token-manager.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable class-methods-use-this */ 2 | 3 | /** 4 | * (C) Copyright IBM Corp. 2019, 2025. 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | 19 | import { decode } from 'jsonwebtoken'; 20 | import logger from '../../lib/logger'; 21 | import { TokenManager, TokenManagerOptions } from './token-manager'; 22 | 23 | /** Configuration options for JWT token retrieval. */ 24 | export type JwtTokenManagerOptions = TokenManagerOptions; 25 | 26 | /** 27 | * A class for shared functionality for parsing, storing, and requesting 28 | * JWT tokens. Intended to be used as a parent to be extended for token 29 | * request management. Child classes should implement `requestToken()` 30 | * to retrieve the bearer token from intended sources. 31 | */ 32 | export class JwtTokenManager extends TokenManager { 33 | protected tokenName: string; 34 | 35 | protected tokenInfo: any; 36 | 37 | /** 38 | * Create a new JwtTokenManager instance. 39 | * 40 | * @param options - Configuration options. 41 | * This should be an object containing these fields: 42 | * - url: (optional) the endpoint URL for the token service 43 | * - disableSslVerification: (optional) a flag that indicates whether verification of the token server's SSL certificate 44 | * should be disabled or not 45 | * - headers: (optional) a set of HTTP headers to be sent with each request to the token service 46 | */ 47 | constructor(options: JwtTokenManagerOptions) { 48 | // all parameters are optional 49 | options = options || ({} as JwtTokenManagerOptions); 50 | super(options); 51 | 52 | this.tokenName = 'access_token'; 53 | this.tokenInfo = {}; 54 | } 55 | 56 | /** 57 | * Request a JWT using an API key. 58 | * 59 | * @returns Promise 60 | */ 61 | protected requestToken(): Promise { 62 | const errMsg = '`requestToken` MUST be overridden by a subclass of JwtTokenManagerV1.'; 63 | const err = new Error(errMsg); 64 | logger.error(errMsg); 65 | return Promise.reject(err); 66 | } 67 | 68 | /** 69 | * Save the JWT service response and the calculated expiration time to the object's state. 70 | * 71 | * @param tokenResponse - the response object from JWT service request 72 | */ 73 | protected saveTokenInfo(tokenResponse): void { 74 | const responseBody = tokenResponse.result || {}; 75 | this.accessToken = responseBody[this.tokenName]; 76 | 77 | if (!this.accessToken) { 78 | const err = 'Access token not present in response'; 79 | logger.error(err); 80 | throw new Error(err); 81 | } 82 | 83 | const decodedResponse = decode(this.accessToken); 84 | if (!decodedResponse) { 85 | const err = 'Access token received is not a valid JWT'; 86 | logger.error(err); 87 | throw new Error(err); 88 | } 89 | 90 | // The expiration time is found by decoding the JWT access token. 91 | // 'exp' is the "expiration time" claim. 92 | // 'iat' is the 'issued at' claim. 93 | const { exp, iat } = decodedResponse; 94 | 95 | // There are no required claims in JWT 96 | if (!exp || !iat) { 97 | this.expireTime = 0; 98 | this.refreshTime = 0; 99 | } else { 100 | const fractionOfTtl = 0.8; 101 | const timeToLive = exp - iat; 102 | this.expireTime = exp; 103 | 104 | // The refresh time represents the time when the token has effectively 105 | // existed for 80% of its time to live. 106 | this.refreshTime = exp - timeToLive * (1.0 - fractionOfTtl); 107 | } 108 | 109 | this.tokenInfo = { ...responseBody }; 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /auth/token-managers/mcsp-token-manager.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * (C) Copyright IBM Corp. 2023, 2024. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | import extend from 'extend'; 18 | import { validateInput } from '../utils/helpers'; 19 | import { buildUserAgent } from '../../lib/build-user-agent'; 20 | import { JwtTokenManager, JwtTokenManagerOptions } from './jwt-token-manager'; 21 | import logger from '../../lib/logger'; 22 | 23 | /** 24 | * Configuration options for MCSP token retrieval. 25 | */ 26 | interface Options extends JwtTokenManagerOptions { 27 | /** The API key used to obtain an access token. */ 28 | apikey: string; 29 | 30 | /** The base endpoint URL for MCSP token requests. */ 31 | url: string; 32 | } 33 | 34 | /** 35 | * This interface models the response object received from the MCSP token service. 36 | */ 37 | export interface McspTokenData { 38 | token: string; 39 | token_type: string; 40 | expires_in: number; 41 | } 42 | 43 | /** 44 | * This is the path associated with the operation used to obtain 45 | * an access token from the MCSP token service. 46 | */ 47 | const OPERATION_PATH = '/siusermgr/api/1.0/apikeys/token'; 48 | 49 | /** 50 | * Token Manager for Multi-Cloud Saas Platform (MCSP) authenticator. 51 | * 52 | * The Token Manager will invoke the MCSP token service's 'POST /siusermgr/api/1.0/apikeys/token' 53 | * operation to obtain an MCSP access token for a user-supplied apikey. 54 | */ 55 | export class McspTokenManager extends JwtTokenManager { 56 | protected requiredOptions = ['apikey', 'url']; 57 | 58 | private apikey: string; 59 | 60 | /** 61 | * Create a new McspTokenManager instance. 62 | * 63 | * @param options - Configuration options 64 | * This should be an object containing these fields: 65 | * - url: (required) the base endpoint URL for the MCSP token service 66 | * - apikey: (required) the API key used to obtain the MCSP access token. 67 | * - disableSslVerification: (optional) a flag that indicates whether verification of the token server's SSL certificate 68 | * should be disabled or not 69 | * - headers: (optional) a set of HTTP headers to be sent with each request to the token service 70 | * 71 | * @throws Error: the configuration options were invalid. 72 | */ 73 | constructor(options: Options) { 74 | super(options); 75 | 76 | this.tokenName = 'token'; 77 | 78 | validateInput(options, this.requiredOptions); 79 | 80 | this.apikey = options.apikey; 81 | 82 | this.userAgent = buildUserAgent('mcsp-authenticator'); 83 | } 84 | 85 | protected requestToken(): Promise { 86 | const requiredHeaders = { 87 | Accept: 'application/json', 88 | 'Content-Type': 'application/json', 89 | 'User-Agent': this.userAgent, 90 | }; 91 | 92 | const parameters = { 93 | options: { 94 | url: this.url + OPERATION_PATH, 95 | body: { 96 | apikey: this.apikey, 97 | }, 98 | method: 'POST', 99 | headers: extend(true, {}, this.headers, requiredHeaders), 100 | rejectUnauthorized: !this.disableSslVerification, 101 | }, 102 | }; 103 | logger.debug(`Invoking MCSP token service operation: ${parameters.options.url}`); 104 | return this.requestWrapperInstance.sendRequest(parameters).then((response) => { 105 | logger.debug('Returned from MCSP token service operation'); 106 | return response; 107 | }); 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /auth/utils/file-reading-helpers.browser.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2021 IBM Corp. All Rights Reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | // Dummy for browser 18 | export function readCredentialsFile() { 19 | return {}; 20 | } 21 | 22 | export function readCrTokenFile() { 23 | return ''; 24 | } 25 | -------------------------------------------------------------------------------- /auth/utils/file-reading-helpers.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2021, 2024 IBM Corp. All Rights Reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | import { parse } from 'dotenv'; 18 | import { existsSync, readFileSync, lstatSync } from 'fs'; 19 | import { homedir } from 'os'; 20 | import { join } from 'path'; 21 | import logger from '../../lib/logger'; 22 | 23 | // Putting all file-reading related code in this file to isolate the usage of the 24 | // `fs` module, as it causes problems in browser environments. 25 | 26 | const defaultCredsFilename: string = 'ibm-credentials.env'; 27 | 28 | /** 29 | * Return a config object based on a credentials file. Credentials files can 30 | * be specified filepath via the environment variable: `IBM_CREDENTIALS_FILE`. 31 | */ 32 | export function readCredentialsFile() { 33 | if (!existsSync) { 34 | return {}; 35 | } 36 | 37 | // first look for an env variable called IBM_CREDENTIALS_FILE 38 | // it should be the path to the file 39 | 40 | // then look at the current working directory 41 | // then at the os-dependent home directory 42 | 43 | const givenFilepath: string = process.env.IBM_CREDENTIALS_FILE || ''; 44 | const workingDir: string = constructFilepath(process.cwd()); 45 | const homeDir: string = constructFilepath(homedir()); 46 | 47 | let filepathToUse: string; 48 | 49 | if (givenFilepath) { 50 | if (fileExistsAtPath(givenFilepath)) { 51 | // see if user gave a path to a file named something other than `ibm-credentials.env` 52 | filepathToUse = givenFilepath; 53 | } else if (fileExistsAtPath(constructFilepath(givenFilepath))) { 54 | // see if user gave a path to the directory where file is located 55 | filepathToUse = constructFilepath(givenFilepath); 56 | } 57 | } else if (fileExistsAtPath(workingDir)) { 58 | filepathToUse = workingDir; 59 | } else if (fileExistsAtPath(homeDir)) { 60 | filepathToUse = homeDir; 61 | } else { 62 | // file does not exist anywhere, will not be used 63 | logger.info('Credential file does not exist. Will not be used'); 64 | return {}; 65 | } 66 | 67 | const credsFile = readFileSync(filepathToUse); 68 | 69 | return parse(credsFile) as any; 70 | } 71 | 72 | export function fileExistsAtPath(filepath: string): boolean { 73 | if (existsSync(filepath)) { 74 | const stats = lstatSync(filepath); 75 | return stats.isFile() || stats.isSymbolicLink(); 76 | } 77 | 78 | return false; 79 | } 80 | 81 | export function constructFilepath(filepath: string): string { 82 | // ensure filepath includes the filename 83 | if (!filepath.endsWith(defaultCredsFilename)) { 84 | filepath = join(filepath, defaultCredsFilename); 85 | } 86 | 87 | return filepath; 88 | } 89 | 90 | export function readCrTokenFile(filepath: string): string { 91 | if (!existsSync) { 92 | return ''; 93 | } 94 | 95 | try { 96 | let token: string = ''; 97 | logger.debug(`Attempting to read CR token from file: ${filepath}`); 98 | token = readFileSync(filepath, 'utf8'); 99 | logger.debug(`Successfully read CR token from file: ${filepath}`); 100 | return token; 101 | } catch (err) { 102 | const msg = `Error reading CR token: ${err.toString()}`; 103 | logger.debug(msg); 104 | throw new Error(msg); 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /auth/utils/get-authenticator-from-environment.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * (C) Copyright IBM Corp. 2019, 2025. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | import { 18 | Authenticator, 19 | BasicAuthenticator, 20 | BearerTokenAuthenticator, 21 | CloudPakForDataAuthenticator, 22 | IamAuthenticator, 23 | IamAssumeAuthenticator, 24 | ContainerAuthenticator, 25 | NoAuthAuthenticator, 26 | VpcInstanceAuthenticator, 27 | McspAuthenticator, 28 | McspV2Authenticator, 29 | } from '../authenticators'; 30 | 31 | import { readExternalSources } from './read-external-sources'; 32 | 33 | /** 34 | * Look for external configuration of authenticator. 35 | * 36 | * Try to get authenticator from external sources, with the following priority: 37 | * 1. Credentials file (ibm-credentials.env) 38 | * 2. Environment variables 39 | * 3. VCAP Services (Cloud Foundry) 40 | * 41 | * @param serviceName - the service name prefix. 42 | * 43 | */ 44 | export function getAuthenticatorFromEnvironment(serviceName: string): Authenticator { 45 | if (!serviceName) { 46 | throw new Error('Service name is required.'); 47 | } 48 | 49 | // construct the credentials object from the environment 50 | const credentials = readExternalSources(serviceName); 51 | if (credentials === null) { 52 | throw new Error('Unable to create an authenticator from the environment.'); 53 | } 54 | 55 | // remove client-level properties 56 | delete credentials.url; 57 | delete credentials.disableSsl; 58 | 59 | // convert "auth" properties to their proper keys 60 | if (credentials.authUrl) { 61 | credentials.url = credentials.authUrl; 62 | delete credentials.authUrl; 63 | } 64 | 65 | if (credentials.authDisableSsl) { 66 | credentials.disableSslVerification = credentials.authDisableSsl; 67 | delete credentials.authDisableSsl; 68 | } 69 | 70 | // in the situation where the auth type is not provided: 71 | // if an apikey is provided, default to IAM 72 | // if not, default to container auth 73 | let { authType } = credentials; 74 | 75 | if (!authType) { 76 | // Support the alternate "AUTHTYPE" config property. 77 | authType = credentials.authtype; 78 | } 79 | if (!authType || typeof authType !== 'string') { 80 | authType = credentials.apikey ? Authenticator.AUTHTYPE_IAM : Authenticator.AUTHTYPE_CONTAINER; 81 | } 82 | 83 | // Create and return the appropriate authenticator. 84 | let authenticator; 85 | 86 | // Compare the authType against our constants case-insensitively to 87 | // determine which authenticator type needs to be constructed. 88 | authType = authType.toLowerCase(); 89 | if (authType === Authenticator.AUTHTYPE_NOAUTH.toLowerCase()) { 90 | authenticator = new NoAuthAuthenticator(); 91 | } else if (authType === Authenticator.AUTHTYPE_BASIC.toLowerCase()) { 92 | authenticator = new BasicAuthenticator(credentials); 93 | } else if (authType === Authenticator.AUTHTYPE_BEARERTOKEN.toLowerCase()) { 94 | authenticator = new BearerTokenAuthenticator(credentials); 95 | } else if (authType === Authenticator.AUTHTYPE_CP4D.toLowerCase()) { 96 | authenticator = new CloudPakForDataAuthenticator(credentials); 97 | } else if (authType === Authenticator.AUTHTYPE_IAM.toLowerCase()) { 98 | authenticator = new IamAuthenticator(credentials); 99 | } else if (authType === Authenticator.AUTHTYPE_IAM_ASSUME.toLowerCase()) { 100 | authenticator = new IamAssumeAuthenticator(credentials); 101 | } else if (authType === Authenticator.AUTHTYPE_CONTAINER.toLowerCase()) { 102 | authenticator = new ContainerAuthenticator(credentials); 103 | } else if (authType === Authenticator.AUTHTYPE_VPC.toLowerCase()) { 104 | authenticator = new VpcInstanceAuthenticator(credentials); 105 | } else if (authType === Authenticator.AUTHTYPE_MCSP.toLowerCase()) { 106 | authenticator = new McspAuthenticator(credentials); 107 | } else if (authType === Authenticator.AUTHTYPE_MCSPV2.toLowerCase()) { 108 | authenticator = new McspV2Authenticator(credentials); 109 | } else { 110 | throw new Error(`Invalid value for AUTH_TYPE: ${authType}`); 111 | } 112 | 113 | return authenticator; 114 | } 115 | -------------------------------------------------------------------------------- /auth/utils/index.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * (C) Copyright IBM Corp. 2019, 2021. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | /** 18 | * @module utils 19 | * Helper functions used by generated SDKs. 20 | * 21 | * functions: 22 | * getAuthenticatorFromEnvironment: Get authenticator from external sources. 23 | * readExternalSources: Get config object from external sources. 24 | */ 25 | 26 | export * from './helpers'; 27 | export * from './file-reading-helpers'; 28 | export { getAuthenticatorFromEnvironment } from './get-authenticator-from-environment'; 29 | export { readExternalSources } from './read-external-sources'; 30 | -------------------------------------------------------------------------------- /commitlint.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | extends: ['@commitlint/config-conventional'], 3 | rules: { 4 | 'subject-case': [2, 'always', ['lower-case', 'sentence-case']], 5 | }, 6 | }; 7 | -------------------------------------------------------------------------------- /index.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * (C) Copyright IBM Corp. 2019, 2024. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | /** 18 | * @module ibm-cloud-sdk-core 19 | */ 20 | 21 | export * from './lib/axios-types'; 22 | export { BaseService, UserOptions } from './lib/base-service'; 23 | export * from './auth'; 24 | export * from './lib/helper'; 25 | export { default as qs } from './lib/querystring'; 26 | export { default as contentType } from './lib/content-type'; 27 | export * from './lib/stream-to-promise'; 28 | export { getNewLogger, SDKLogger } from './lib/get-new-logger'; 29 | -------------------------------------------------------------------------------- /lib/axios-types.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * (C) Copyright IBM Corp. 2025. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | export { GenericAbortSignal as AbortSignal } from 'axios'; 18 | -------------------------------------------------------------------------------- /lib/build-user-agent.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * (C) Copyright IBM Corp. 2024. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | const os = require('os'); 18 | const { version } = require('../package.json'); 19 | 20 | /** 21 | * Returns a string suitable as a value for the User-Agent header 22 | * @param componentName optional name of a component to be included in the returned string 23 | * @returns the user agent header value 24 | */ 25 | export function buildUserAgent(componentName: string = null): string { 26 | const subComponent = componentName ? `/${componentName}` : ''; 27 | const userAgent = `ibm-node-sdk-core${subComponent}-${version} os.name=${os.platform()} os.version=${os.release()} node.version=${ 28 | process.version 29 | }`; 30 | return userAgent; 31 | } 32 | -------------------------------------------------------------------------------- /lib/chain-error.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * (C) Copyright IBM Corp. 2023. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | /** 18 | * Given two Error instances 'error' and 'causedBy', this function will 19 | * update 'error' by chaining 'causedBy' to it. 20 | * Specifically, 'causedBy''s message and stack will be appended 21 | * to 'error''s message and stack, respectively, to simulate chained Errors. 22 | * 23 | * @param error the Error object to be updated 24 | * @param causedBy an Error object that represents the cause of 'error' 25 | * @returns 'error' after updating its message and stack fields 26 | */ 27 | export function chainError(error: Error, causedBy: Error): Error { 28 | error.message += ` ${causedBy.toString()}`; 29 | if (causedBy.stack) { 30 | error.stack += `\nCaused by: ${causedBy.stack}`; 31 | } 32 | return error; 33 | } 34 | -------------------------------------------------------------------------------- /lib/content-type.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable prettier/prettier */ 2 | 3 | /** 4 | * (C) Copyright IBM Corp. 2019, 2022. 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | 19 | import { extname } from 'path'; 20 | import { FileObject } from "./helper"; 21 | 22 | // This module attempts to identify common content-types based on the filename or header 23 | // It is not exhaustive, and for best results, you should always manually specify the content-type option. 24 | // See the complete list of supported content-types at 25 | // https://cloud.ibm.com/docs/services/speech-to-text?topic=speech-to-text-input#formats 26 | 27 | // *some* file types can be identified by the first 3-4 bytes of the file 28 | const headerContentTypes: { [key: string]: string } = { 29 | fLaC: 'audio/flac', 30 | RIFF: 'audio/wav', 31 | OggS: 'audio/ogg', 32 | ID3: 'audio/mp3', 33 | '\u001aEߣ': 'audio/webm' // String for first four hex's of webm: [1A][45][DF][A3] (https://www.matroska.org/technical/specs/index.html#EBML) 34 | }; 35 | 36 | const filenameContentTypes: { [key: string]: string } = { 37 | '.mp3': 'audio/mp3', 38 | '.wav': 'audio/wav', 39 | '.flac': 'audio/flac', 40 | '.ogg': 'audio/ogg', 41 | '.oga': 'audio/ogg', 42 | '.opus': 'audio/ogg; codec=opus', 43 | '.webm': 'audio/webm' 44 | }; 45 | 46 | /** 47 | * Takes the beginning of an audio file and returns the associated content-type / mime type 48 | * 49 | * @param buffer - a Buffer containing at least the first 4 bytes of the file 50 | * @return sthe contentType or undefined 51 | */ 52 | const fromHeader = (buffer: Buffer): string => { 53 | const headerStr = buffer 54 | .slice(0, 4) 55 | .toString() 56 | .substr(0, 4); 57 | // mp3's are only consistent for the first 3 characters 58 | return ( 59 | headerContentTypes[headerStr] || headerContentTypes[headerStr.substr(0, 3)] 60 | ); 61 | } 62 | 63 | /** 64 | * Guess the content type from the filename 65 | * 66 | * Note: Blob and File objects include a .type property, but we're ignoring it because it's frequently either 67 | * incorrect (e.g. video/ogg instead of audio/ogg) or else a different format than what's expected (e.g. audio/x-wav) 68 | * 69 | * @param file - a String filename or url, or binary File/Blob object. 70 | * @returns the content type 71 | */ 72 | const fromFilename = (file: String | NodeJS.ReadableStream | FileObject | Buffer | File): string => { 73 | const ext: string = extname( 74 | // eslint-disable-next-line @typescript-eslint/dot-notation 75 | (typeof file === 'string' && file) || file['name'] || '' 76 | ); 77 | return filenameContentTypes[ext]; 78 | } 79 | 80 | export default { 81 | fromFilename, 82 | fromHeader 83 | } 84 | -------------------------------------------------------------------------------- /lib/cookie-support.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * (C) Copyright IBM Corp. 2022, 2023. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | import { Axios, AxiosResponse, InternalAxiosRequestConfig, isAxiosError } from 'axios'; 18 | import extend from 'extend'; 19 | import { Cookie, CookieJar } from 'tough-cookie'; 20 | import logger from './logger'; 21 | 22 | const internalCreateCookieInterceptor = (cookieJar: CookieJar) => { 23 | /** 24 | * This is called by Axios when a request is about to be sent in order to 25 | * set the "cookie" header in the request. 26 | * 27 | * @param config the Axios request config 28 | * @returns the request config 29 | */ 30 | async function requestInterceptor(config: InternalAxiosRequestConfig) { 31 | logger.debug('CookieInterceptor: intercepting request'); 32 | if (config && config.url) { 33 | logger.debug(`CookieInterceptor: getting cookies for: ${config.url}`); 34 | const cookieHeaderValue = await cookieJar.getCookieString(config.url); 35 | if (cookieHeaderValue) { 36 | logger.debug('CookieInterceptor: setting cookie header'); 37 | const cookieHeader = { cookie: cookieHeaderValue }; 38 | config.headers = extend(true, {}, config.headers, cookieHeader); 39 | } else { 40 | logger.debug(`CookieInterceptor: no cookies for: ${config.url}`); 41 | } 42 | } else { 43 | logger.debug('CookieInterceptor: no request URL.'); 44 | } 45 | return config; 46 | } 47 | 48 | /** 49 | * This is called by Axios when a 2xx response has been received. 50 | * We'll invoke the configured cookie jar's setCookie() method to handle 51 | * the "set-cookie" header. 52 | * @param response the Axios response object 53 | * @returns the response object 54 | */ 55 | async function responseInterceptor(response: AxiosResponse) { 56 | logger.debug('CookieInterceptor: intercepting response to check for set-cookie headers.'); 57 | if (response && response.headers) { 58 | const cookies: string[] = response.headers['set-cookie']; 59 | if (cookies) { 60 | logger.debug(`CookieInterceptor: setting cookies in jar for URL ${response.config.url}.`); 61 | // Write cookies sequentially by chaining the promises in a reduce 62 | await cookies.reduce( 63 | (cookiePromise: Promise, cookie: string) => 64 | cookiePromise.then(() => cookieJar.setCookie(cookie, response.config.url)), 65 | Promise.resolve(null) 66 | ); 67 | } else { 68 | logger.debug('CookieInterceptor: no set-cookie headers.'); 69 | } 70 | } else { 71 | logger.debug('CookieInterceptor: no response headers.'); 72 | } 73 | 74 | return response; 75 | } 76 | 77 | /** 78 | * This is called by Axios when a non-2xx response has been received. 79 | * We'll simply delegate to the "responseInterceptor" method since we want to 80 | * do the same cookie handling as for a success response. 81 | * @param error the Axios error object that describes the non-2xx response 82 | * @returns the error object 83 | */ 84 | async function responseRejected(error: any) { 85 | logger.debug('CookieIntercepter: intercepting error response'); 86 | 87 | if (isAxiosError(error) && error.response) { 88 | logger.debug('CookieIntercepter: delegating to responseInterceptor()'); 89 | await responseInterceptor(error.response); 90 | } else { 91 | logger.debug('CookieInterceptor: no response field in error object, skipping...'); 92 | } 93 | 94 | return Promise.reject(error); 95 | } 96 | 97 | return (axios: Axios) => { 98 | axios.interceptors.request.use(requestInterceptor); 99 | axios.interceptors.response.use(responseInterceptor, responseRejected); 100 | }; 101 | }; 102 | 103 | export const createCookieInterceptor = (cookieJar: CookieJar | boolean) => { 104 | if (cookieJar) { 105 | if (cookieJar === true) { 106 | logger.debug('CookieInterceptor: creating new CookieJar'); 107 | return internalCreateCookieInterceptor(new CookieJar()); 108 | } else { 109 | logger.debug('CookieInterceptor: using supplied CookieJar'); 110 | return internalCreateCookieInterceptor(cookieJar); 111 | } 112 | } else { 113 | throw new Error('Must supply a cookie jar or true.'); 114 | } 115 | }; 116 | -------------------------------------------------------------------------------- /lib/get-new-logger.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * (C) Copyright IBM Corp. 2022. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | import logger, { Debugger } from 'debug'; 18 | 19 | export interface SDKLogger { 20 | error: Debugger; 21 | warn: Debugger; 22 | info: Debugger; 23 | verbose: Debugger; 24 | debug: Debugger; 25 | } 26 | 27 | /** 28 | * Return a new logger, formatted with a particular name. The logging functions, in 29 | * order of increasing verbosity, are: `error`, `warn`, `info`, `verbose`, and `debug`. 30 | * 31 | * The logger will be an instance of the `debug` package and utilizes its support for 32 | * configuration with environment variables. 33 | * 34 | * Additionally, the logger will be turned on automatically if the "NODE_DEBUG" 35 | * environment variable is set to "axios". 36 | * 37 | * @param moduleName - the namespace for the logger. The name will appear in 38 | * the logs and it will be the name used for configuring the log level. 39 | * 40 | * @returns the new logger 41 | */ 42 | export function getNewLogger(moduleName: string): SDKLogger { 43 | const debug = logger(`${moduleName}:debug`); 44 | const error = logger(`${moduleName}:error`); 45 | const info = logger(`${moduleName}:info`); 46 | const verbose = logger(`${moduleName}:verbose`); 47 | const warn = logger(`${moduleName}:warning`); 48 | 49 | // enable loggers if axios flag is set & mimic log levels severity 50 | if (process.env.NODE_DEBUG === 'axios') { 51 | debug.enabled = true; 52 | } 53 | if (debug.enabled) { 54 | verbose.enabled = true; 55 | } 56 | if (verbose.enabled) { 57 | info.enabled = true; 58 | } 59 | if (info.enabled) { 60 | warn.enabled = true; 61 | } 62 | if (warn.enabled) { 63 | error.enabled = true; 64 | } 65 | 66 | const newLogger: SDKLogger = { 67 | debug, 68 | error, 69 | info, 70 | verbose, 71 | warn, 72 | }; 73 | 74 | return newLogger; 75 | } 76 | -------------------------------------------------------------------------------- /lib/logger.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * (C) Copyright IBM Corp. 2019, 2022. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | import { getNewLogger } from './get-new-logger'; 18 | 19 | export default getNewLogger('ibm-cloud-sdk-core'); 20 | -------------------------------------------------------------------------------- /lib/private-helpers.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * (C) Copyright IBM Corp. 2024. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | // Keywords that should be redacted. 18 | const redactedKeywords = [ 19 | 'apikey', 20 | 'api_key', 21 | 'passcode', 22 | 'password', 23 | 'token', 24 | 25 | 'aadClientId', 26 | 'aadClientSecret', 27 | 'auth', 28 | 'auth_provider_x509_cert_url', 29 | 'auth_uri', 30 | 'client_email', 31 | 'client_id', 32 | 'client_x509_cert_url', 33 | 'key', 34 | 'project_id', 35 | 'secret', 36 | 'subscriptionId', 37 | 'tenantId', 38 | 'thumbprint', 39 | 'token_uri', 40 | ]; 41 | 42 | const redactedTokens = redactedKeywords.join('|'); 43 | 44 | // Pre-compiled regular expressions used by redactSecrets(). 45 | const reAuthHeader = new RegExp(`^(Authorization|X-Auth\\S*): .*$`, 'gim'); 46 | const rePropertySetting = new RegExp(`(${redactedTokens})=[^&]*(&|$)`, 'gi'); 47 | const reJsonField = new RegExp(`"([^"]*(${redactedTokens})[^"_]*)":\\s*"[^\\,]*"`, 'gi'); 48 | 49 | // RedactSecrets() returns the input string with secrets redacted. 50 | /** 51 | * Redacts secrets found in "input" so that the resulting string 52 | * is suitable for debug logging. 53 | * @param input - the string that potentially contains secrets 54 | * @returns the input string with secrets replaced with "[redacted]" 55 | */ 56 | export function redactSecrets(input: string): string { 57 | const redacted = '[redacted]'; 58 | 59 | let redactedString = input; 60 | redactedString = redactedString.replace(reAuthHeader, `$1: ${redacted}`); 61 | redactedString = redactedString.replace(rePropertySetting, `$1=${redacted}$2`); 62 | redactedString = redactedString.replace(reJsonField, `"$1":"${redacted}"`); 63 | 64 | return redactedString; 65 | } 66 | -------------------------------------------------------------------------------- /lib/querystring.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * (C) Copyright IBM Corp. 2019, 2022. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | /** 18 | * Lightweight implementation for stringify-ing query params 19 | * 20 | * @param queryParams - the query params 21 | * @returns the query string 22 | */ 23 | const stringify = (queryParams: Object): string => 24 | Object.keys(queryParams) 25 | .map((key) => `${key}=${encodeURIComponent(queryParams[key])}`) 26 | .join('&'); 27 | 28 | export default { 29 | stringify, 30 | }; 31 | -------------------------------------------------------------------------------- /lib/stream-to-promise.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * (C) Copyright IBM Corp. 2019, 2022. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | import { Stream } from 'stream'; 18 | 19 | /** 20 | * Helper method that can be bound to a stream - it captures all of the results, and returns a promise that resolves to the final buffer 21 | * or array of text chunks 22 | * Essentially a smaller version of concat-stream wrapped in a promise 23 | * 24 | * @param stream - optional stream param for when not bound to an existing stream instance. 25 | * @returns Promise 26 | */ 27 | export function streamToPromise(stream: Stream): Promise { 28 | stream = stream || this; 29 | return new Promise((resolve, reject) => { 30 | const results = []; 31 | stream 32 | .on('data', (result) => { 33 | results.push(result); 34 | }) 35 | .on('end', () => { 36 | resolve(Buffer.isBuffer(results[0]) ? Buffer.concat(results) : results); 37 | }) 38 | .on('error', reject); 39 | }); 40 | } 41 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ibm-cloud-sdk-core", 3 | "version": "5.1.2", 4 | "description": "Core functionality to support SDKs generated with IBM's OpenAPI SDK Generator.", 5 | "main": "./index.js", 6 | "typings": "./es/index.d.ts", 7 | "sideEffects": false, 8 | "repository": { 9 | "type": "git", 10 | "url": "git+https://github.com/IBM/node-sdk-core.git" 11 | }, 12 | "keywords": [ 13 | "ibm", 14 | "sdk", 15 | "openapi", 16 | "core" 17 | ], 18 | "author": "IBM Corp.", 19 | "contributors": [ 20 | { 21 | "name": "Dustin Popp", 22 | "email": "dustinpopp@ibm.com" 23 | }, 24 | { 25 | "name": "Phil Adams", 26 | "email": "phil_adams@us.ibm.com" 27 | } 28 | ], 29 | "license": "Apache-2.0", 30 | "publishConfig": { 31 | "access": "public" 32 | }, 33 | "bugs": { 34 | "url": "https://github.com/IBM/node-sdk-core/issues" 35 | }, 36 | "devDependencies": { 37 | "@commitlint/cli": "^12.0.1", 38 | "@commitlint/config-conventional": "^7.0.1", 39 | "@masterodin/publisher": "^0.10.0", 40 | "@microsoft/api-documenter": "^7.24.1", 41 | "@microsoft/api-extractor": "^7.43.0", 42 | "@semantic-release/changelog": "6.0.3", 43 | "@semantic-release/git": "10.0.1", 44 | "@types/extend": "^3.0.4", 45 | "@types/file-type": "~5.2.1", 46 | "@types/isstream": "^0.1.0", 47 | "@types/mime-types": "^2.1.4", 48 | "@typescript-eslint/eslint-plugin": "^5.47.0", 49 | "@typescript-eslint/parser": "^5.47.0", 50 | "eslint": "^7.26.0", 51 | "eslint-config-airbnb-base": "^14.2.1", 52 | "eslint-config-airbnb-typescript": "^12.3.1", 53 | "eslint-config-prettier": "^8.3.0", 54 | "eslint-plugin-import": "^2.23.2", 55 | "eslint-plugin-jest": "^24.3.6", 56 | "eslint-plugin-jsdoc": "^34.6.3", 57 | "eslint-plugin-node": "^9.0.0", 58 | "eslint-plugin-prettier": "^3.0.1", 59 | "jest": "^29.7.0", 60 | "nock": "^13.5.4", 61 | "npm-run-all": "4.1.5", 62 | "package-json-reducer": "1.0.18", 63 | "prettier": "~2.3.0", 64 | "semantic-release": "^24.2.3", 65 | "typescript": "~4.9.4" 66 | }, 67 | "dependencies": { 68 | "@types/debug": "^4.1.12", 69 | "@types/node": "^18.19.80", 70 | "@types/tough-cookie": "^4.0.0", 71 | "axios": "^1.8.2", 72 | "camelcase": "^6.3.0", 73 | "debug": "^4.3.4", 74 | "dotenv": "^16.4.5", 75 | "extend": "3.0.2", 76 | "file-type": "16.5.4", 77 | "form-data": "4.0.0", 78 | "isstream": "0.1.2", 79 | "jsonwebtoken": "^9.0.2", 80 | "mime-types": "2.1.35", 81 | "retry-axios": "^2.6.0", 82 | "tough-cookie": "^4.1.3" 83 | }, 84 | "overrides": { 85 | "semver": "^7.5.3", 86 | "micromatch": "4.0.8" 87 | }, 88 | "browser": { 89 | "./auth/utils/read-credentials-file": "./auth/utils/read-credentials-file.browser" 90 | }, 91 | "engines": { 92 | "node": ">=18" 93 | }, 94 | "scripts": { 95 | "clean": "rm -fr node_modules sdk-test-utilities/node_modules", 96 | "commitmsg": "commitlint -E GIT_PARAMS", 97 | "eslint:config": "eslint --print-config .eslintrc.js | eslint-config-prettier-check", 98 | "eslint:fix": "eslint . --fix", 99 | "eslint:check": "eslint . --cache", 100 | "lint": "npm run eslint:check", 101 | "fix": "npm run eslint:fix", 102 | "jest": "jest", 103 | "test": "jest test/unit/", 104 | "test-travis": "jest --runInBand test/unit/", 105 | "build:clean": "rimraf dist", 106 | "build": "npm-run-all build:code build:doc copy:pkg", 107 | "build:code": "npm-run-all -p build:umd build:es", 108 | "build:umd": "tsc", 109 | "build:es": "tsc -p tsconfig-es6.json", 110 | "build:api": "api-extractor run --local", 111 | "build:md": "api-documenter markdown -i temp --output-folder build/docs", 112 | "build:doc": "npm-run-all build:api build:md copy:doc", 113 | "copy:doc": "mkdir -p dist/docs && cp \"temp/ibm-cloud-sdk-core.api.json\" dist/docs", 114 | "copy:pkg": "package-json-reducer -s \"config devDependencies directories scripts jestSonar jest\" -o ./dist/package.json package.json", 115 | "postversion": "publisher --no-checks --dry-run", 116 | "all": "npm-run-all build test lint", 117 | "semantic-release": "semantic-release" 118 | }, 119 | "jest": { 120 | "collectCoverage": true, 121 | "coverageDirectory": "./coverage/", 122 | "testEnvironment": "node" 123 | } 124 | } 125 | -------------------------------------------------------------------------------- /sdk-test-utilities/.npmignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | -------------------------------------------------------------------------------- /sdk-test-utilities/README.md: -------------------------------------------------------------------------------- 1 | # SDK Test Utilities 2 | 3 | A set of utility methods for unit tests in IBM Cloud SDKs. 4 | 5 | This package is published separately from `ibm-cloud-sdk-core`. It includes `expect` 6 | as a dependency, which is not intended for inclusion in production packages. This 7 | utility package should only be installed as a _development_ dependency and the logic 8 | it contains will only ever be needed for tests. 9 | -------------------------------------------------------------------------------- /sdk-test-utilities/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@ibm-cloud/sdk-test-utilities", 3 | "version": "1.0.0", 4 | "description": "A set of utility methods for unit tests in IBM Cloud SDKs", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"N/A\"" 8 | }, 9 | "author": "Dustin Popp", 10 | "license": "Apache-2.0", 11 | "dependencies": { 12 | "expect": "^29.7.0" 13 | }, 14 | "overrides": { 15 | "micromatch": "4.0.8" 16 | }, 17 | "engines": { 18 | "node": ">=18" 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /test/.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | env: { 3 | node: true, 4 | jest: true, 5 | }, 6 | rules: { 7 | 'require-jsdoc': 'off', 8 | 'valid-jsdoc': 'off', 9 | }, 10 | }; 11 | -------------------------------------------------------------------------------- /test/integration/cp4d-authenticator.test.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2021 IBM Corp. All Rights Reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | const { getAuthenticatorFromEnvironment } = require('../../dist'); 18 | 19 | // Note: Only the unit tests are run by default. 20 | // 21 | // In order to test with a live CP4D server, rename "cp4dtest.env.example" to 22 | // "cp4dtest.env" in the test/resources folder and populate the fields. 23 | // Then run this command: 24 | // npm run jest -- test/integration/cp4d-authenticator.test.js 25 | 26 | describe('CP4D Authenticator - Integration Test', () => { 27 | process.env.IBM_CREDENTIALS_FILE = `${__dirname}/../resources/cp4dtest.env`; 28 | 29 | it('should retrieve a live access token with username/password', async () => { 30 | // set up environment 31 | const authenticator = getAuthenticatorFromEnvironment('cp4d-password-test'); 32 | 33 | // build a mock request 34 | const requestOptions = {}; 35 | 36 | // authenticate the request 37 | await authenticator.authenticate(requestOptions); 38 | 39 | // check for proper authentication 40 | expect(requestOptions.headers.Authorization).toBeDefined(); 41 | expect(requestOptions.headers.Authorization.startsWith('Bearer')).toBe(true); 42 | }); 43 | 44 | it('should retrieve a live access token with username/apikey', async () => { 45 | // set up environment 46 | const authenticator = getAuthenticatorFromEnvironment('cp4d-apikey-test'); 47 | 48 | // build a mock request 49 | const requestOptions = {}; 50 | 51 | // authenticate the request 52 | await authenticator.authenticate(requestOptions); 53 | 54 | // check for proper authentication 55 | expect(requestOptions.headers.Authorization).toBeDefined(); 56 | expect(requestOptions.headers.Authorization.startsWith('Bearer')).toBe(true); 57 | }); 58 | }); 59 | -------------------------------------------------------------------------------- /test/integration/iam-assume-authenticator.test.js: -------------------------------------------------------------------------------- 1 | /** 2 | * (C) Copyright IBM Corp. 2024. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | const { getAuthenticatorFromEnvironment } = require('../../dist'); 18 | 19 | // Note: Only the unit tests are run by default. 20 | // 21 | // In order to test with a live IAM server, create file "iamassume.env" in the project root. 22 | // It should look something like this: 23 | // 24 | // ASSUMETEST_AUTH_URL= e.g. https://iam.cloud.ibm.com 25 | // ASSUMETEST_AUTH_TYPE=iamAssume 26 | // ASSUMETEST_APIKEY= 27 | // ASSUMETEST_IAM_PROFILE_ID= 28 | // 29 | // Then run this command from the project root: 30 | // npm run jest test/integration/iam-assume-authenticator.test.js 31 | 32 | describe('IAM Assume Authenticator - Integration Test', () => { 33 | process.env.IBM_CREDENTIALS_FILE = `${__dirname}/../../iamassume.env`; 34 | 35 | it('should retrieve an IAM access token successfully', async () => { 36 | // Set up environment. 37 | const authenticator = getAuthenticatorFromEnvironment('assumetest'); 38 | 39 | // Build a mock request. 40 | const requestOptions = {}; 41 | 42 | // Authenticate the request. 43 | await authenticator.authenticate(requestOptions); 44 | 45 | // Check for proper authentication. 46 | expect(requestOptions.headers.Authorization).toBeDefined(); 47 | expect(requestOptions.headers.Authorization.startsWith('Bearer')).toBe(true); 48 | }); 49 | }); 50 | -------------------------------------------------------------------------------- /test/integration/iam-authenticator.test.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2022 IBM Corp. All Rights Reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | const { getAuthenticatorFromEnvironment } = require('../../dist'); 18 | 19 | // Note: Only the unit tests are run by default. 20 | // 21 | // In order to test with a live IAM server, create file "iamtest.env" in the project root. 22 | // It should look like this: 23 | // 24 | // IAMTEST_AUTH_URL= e.g. https://iam.cloud.ibm.com 25 | // IAMTEST_AUTH_TYPE=iam 26 | // IAMTEST_APIKEY= 27 | // 28 | // Then run this command from the project root: 29 | // npm run jest test/integration/iam-authenticator.test.js 30 | 31 | describe('IAM Authenticator - Integration Test', () => { 32 | process.env.IBM_CREDENTIALS_FILE = `${__dirname}/../../iamtest.env`; 33 | 34 | it('should retrieve an IAM access token successfully', async () => { 35 | // set up environment 36 | const authenticator = getAuthenticatorFromEnvironment('iamtest'); 37 | 38 | // build a mock request 39 | const requestOptions = {}; 40 | 41 | // authenticate the request 42 | await authenticator.authenticate(requestOptions); 43 | 44 | // check for proper authentication 45 | expect(requestOptions.headers.Authorization).toBeDefined(); 46 | expect(requestOptions.headers.Authorization.startsWith('Bearer')).toBe(true); 47 | }); 48 | }); 49 | -------------------------------------------------------------------------------- /test/integration/mcsp-authenticator.test.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2023 IBM Corp. All Rights Reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | const { getAuthenticatorFromEnvironment } = require('../../dist'); 18 | 19 | // Note: Only the unit tests are run by default. 20 | // 21 | // In order to test with a live MCSP server, create file "mcsptest.env" in the project root. 22 | // It should look like this: 23 | // 24 | // MCSPTEST_AUTH_URL= e.g. https://iam.platform.test.saas.ibm.com 25 | // MCSPTEST_AUTH_TYPE=mcsp 26 | // MCSPTEST_APIKEY= 27 | // 28 | // Then run this command from the project root: 29 | // npm run jest test/integration/mcsp-authenticator.test.js 30 | 31 | describe('MCSP V1 Authenticator - Integration Test', () => { 32 | process.env.IBM_CREDENTIALS_FILE = `${__dirname}/../../mcsptest.env`; 33 | 34 | it('should retrieve an MCSP access token successfully', async () => { 35 | // set up environment 36 | const authenticator = getAuthenticatorFromEnvironment('mcsptest1'); 37 | 38 | // build a mock request 39 | const requestOptions = {}; 40 | 41 | // authenticate the request 42 | await authenticator.authenticate(requestOptions); 43 | 44 | // check for proper authentication 45 | expect(requestOptions.headers.Authorization).toBeDefined(); 46 | expect(requestOptions.headers.Authorization.startsWith('Bearer')).toBe(true); 47 | }); 48 | }); 49 | -------------------------------------------------------------------------------- /test/integration/mcspv2-authenticator.test.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2025 IBM Corp. All Rights Reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | const { getAuthenticatorFromEnvironment } = require('../../dist'); 18 | const { McspV2Authenticator } = require('../../dist/auth'); 19 | 20 | // Note: Only the unit tests are run by default. 21 | // 22 | // In order to test with a live token server, create file "mcspv2test.env" in the project root. 23 | // It should look like this: 24 | // 25 | // required properties: 26 | // 27 | // MCSPV2TEST1_AUTH_URL= e.g. https://account-iam.platform.dev.saas.ibm.com 28 | // MCSPV2TEST1_AUTH_TYPE=mcspv2 29 | // MCSPV2TEST1_APIKEY= 30 | // MCSPV2TEST1_SCOPE_COLLECTION_TYPE=accounts (use any valid collection type value) 31 | // MCSPV2TEST1_SCOPE_ID=global_account (use any valid scope id) 32 | // 33 | // optional properties: 34 | // 35 | // MCSPV2TEST1_INCLUDE_BUILTIN_ACTIONS=true|false 36 | // MCSPV2TEST1_INCLUDE_CUSTOM_ACTIONS=true|false 37 | // MCSPV2TEST1_INCLUDE_ROLES=true|false 38 | // MCSPV2TEST1_PREFIX_ROLES=true|false 39 | // MCSPV2TEST1_CALLER_EXT_CLAIM={"productID":"prod123"} 40 | // 41 | // 42 | // Then run this command from the project root: 43 | // npm run jest test/integration/mcspv2-authenticator.test.js 44 | 45 | describe('MCSP V2 Authenticator - Integration Test', () => { 46 | process.env.IBM_CREDENTIALS_FILE = `${__dirname}/../../mcspv2test.env`; 47 | 48 | it('should retrieve an MCSP access token successfully', async () => { 49 | // set up environment 50 | const authenticator = getAuthenticatorFromEnvironment('mcspv2test1'); 51 | 52 | // build a mock request 53 | const requestOptions = {}; 54 | 55 | // authenticate the request 56 | await authenticator.authenticate(requestOptions); 57 | 58 | // check for proper authentication 59 | expect(requestOptions.headers.Authorization).toBeDefined(); 60 | expect(requestOptions.headers.Authorization.startsWith('Bearer')).toBe(true); 61 | }); 62 | }); 63 | -------------------------------------------------------------------------------- /test/resources/blank.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IBM/node-sdk-core/f2d146438b282dc418615d8f6fd4db58b84d83b3/test/resources/blank.wav -------------------------------------------------------------------------------- /test/resources/cp4dtest.env.example: -------------------------------------------------------------------------------- 1 | CP4D_PASSWORD_TEST_AUTH_URL= e.g. https://cpd350-cpd-cpd350.apps.wml-kf-cluster.os.fyre.ibm.com/icp4d-api 2 | CP4D_PASSWORD_TEST_AUTH_TYPE=cp4d 3 | CP4D_PASSWORD_TEST_USERNAME= 4 | CP4D_PASSWORD_TEST_PASSWORD= 5 | CP4D_PASSWORD_TEST_AUTH_DISABLE_SSL=true 6 | 7 | CP4D_APIKEY_TEST_AUTH_URL= e.g. https://cpd350-cpd-cpd350.apps.wml-kf-cluster.os.fyre.ibm.com/icp4d-api 8 | CP4D_APIKEY_TEST_AUTH_TYPE=cp4d 9 | CP4D_APIKEY_TEST_USERNAME= 10 | CP4D_APIKEY_TEST_APIKEY= 11 | CP4D_APIKEY_TEST_AUTH_DISABLE_SSL=true 12 | -------------------------------------------------------------------------------- /test/resources/empty-file: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IBM/node-sdk-core/f2d146438b282dc418615d8f6fd4db58b84d83b3/test/resources/empty-file -------------------------------------------------------------------------------- /test/resources/ibm-credentials.env: -------------------------------------------------------------------------------- 1 | # auth properties 2 | TEST_SERVICE_AUTH_TYPE=iam 3 | TEST_SERVICE_APIKEY=12345 4 | TEST_SERVICE_AUTH_URL=iam.staging.com/api 5 | TEST_SERVICE_CLIENT_ID=my-id 6 | TEST_SERVICE_CLIENT_SECRET=my-secret 7 | TEST_SERVICE_AUTH_DISABLE_SSL=true 8 | 9 | # service properties 10 | TEST_SERVICE_URL=service.com/api 11 | TEST_SERVICE_DISABLE_SSL=true 12 | TEST_SERVICE_SCOPE=A B C D 13 | 14 | # retry properties 15 | TEST_SERVICE_ENABLE_RETRIES=true 16 | TEST_SERVICE_MAX_RETRIES=1234 17 | TEST_SERVICE_RETRY_INTERVAL=5678 18 | 19 | # Service1 auth properties configured with IAM and a token containing '=' 20 | SERVICE_1_AUTH_TYPE=iam 21 | SERVICE_1_APIKEY=V4HXmoUtMjohnsnow=KotN 22 | SERVICE_1_CLIENT_ID=somefake========id 23 | SERVICE_1_CLIENT_SECRET===my-client-secret== 24 | SERVICE_1_AUTH_URL=https://iamhost/iam/api= 25 | SERVICE_1_AUTH_DISABLE_SSL= 26 | 27 | # Service1 service properties 28 | SERVICE_1_URL=service1.com/api 29 | 30 | # Service2 configured with IAM w/scope 31 | SERVICE_2_AUTH_TYPE=iam 32 | SERVICE_2_APIKEY=V4HXmoUtMjohnsnow=KotN 33 | SERVICE_2_CLIENT_ID=somefake========id 34 | SERVICE_2_CLIENT_SECRET===my-client-secret== 35 | SERVICE_2_AUTH_URL=https://iamhost/iam/api= 36 | SERVICE_2_SCOPE=A B C D 37 | 38 | # Service3 configured with basic auth 39 | SERVICE_3_AUTHTYPE=basic 40 | SERVICE_3_USERNAME=user1 41 | SERVICE_3_PASSWORD=password1 42 | -------------------------------------------------------------------------------- /test/resources/other-file.env: -------------------------------------------------------------------------------- 1 | NATURAL_LANGUAGE_UNDERSTANDING_USERNAME=username 2 | NATURAL_LANGUAGE_UNDERSTANDING_PASSWORD=password 3 | -------------------------------------------------------------------------------- /test/resources/symlink-creds.txt: -------------------------------------------------------------------------------- 1 | ibm-credentials.env -------------------------------------------------------------------------------- /test/resources/vault-token: -------------------------------------------------------------------------------- 1 | my-cr-token-123 -------------------------------------------------------------------------------- /test/resources/vcap.json: -------------------------------------------------------------------------------- 1 | { 2 | "discovery": [{ 3 | "name": "discovery1", 4 | "label": "discovery", 5 | "plan": "experimental", 6 | "credentials": { 7 | "url": "https://gateway.watsonplatform1.net/discovery-experimental/api", 8 | "username": "not-a-username", 9 | "password": "not-a-password" 10 | } 11 | }, 12 | { 13 | "name": "discovery2", 14 | "label": "discovery2", 15 | "plan": "experimental", 16 | "credentials": { 17 | "url": "https://gateway.watsonplatform2.net/discovery-experimental/api", 18 | "username": "not-a-username", 19 | "password": "not-a-password" 20 | } 21 | } 22 | ], 23 | "language_translator": [{ 24 | "name": "language_translator_docs", 25 | "label": "language_translator", 26 | "plan": "standard", 27 | "credentials": { 28 | "apikey": "123456789", 29 | "iam_apikey_description": "Auto generated apikey...", 30 | "iam_apikey_name": "auto-generated-apikey-111-222-333", 31 | "iam_role_crn": "crn:v1:bluemix:public:iam::::serviceRole:Manager", 32 | "iam_serviceid_crn": "crn:v1:staging:public:iam-identity::a/::serviceid:ServiceID-1234", 33 | "url": "https://gateway.watsonplatform.net/language-translator/api", 34 | "iam_url": "https://iam.cloud.ibm.com/identity/token" 35 | } 36 | }], 37 | "alchemy_api": [{ 38 | "name": "alchemy_api_free_docs", 39 | "label": "alchemy_api", 40 | "plan": "Free", 41 | "credentials": { 42 | "url": "https://gateway-a.watsonplatform.net/calls", 43 | "apikey": "not-a-apikey" 44 | } 45 | }], 46 | "empty_service": [ 47 | 48 | ], 49 | "no-creds-service": [{ 50 | "name": "no-creds-service-one", 51 | "label": "devops-insights", 52 | "plan": "elite", 53 | "credentials": { 54 | "url": "https://on.the.toolchainplatform.net/no-creds-service-one/api", 55 | "username": "not-a-username-2", 56 | "password": "not-a-password-2" 57 | } 58 | }, 59 | { 60 | "name": "no-creds-service-two", 61 | "label": "devops-insights", 62 | "plan": "elite" 63 | } 64 | ] 65 | } 66 | -------------------------------------------------------------------------------- /test/resources/weather-data-train.csv: -------------------------------------------------------------------------------- 1 | How hot is it today?,temperature Is it hot outside?,temperature Will it be uncomfortably hot?,temperature Will it be sweltering?,temperature How cold is it today?,temperature Is it cold outside?,temperature Will it be uncomfortably cold?,temperature Will it be frigid?,temperature What is the expected high for today?,temperature What is the expected temperature?,temperature Will high temperatures be dangerous?,temperature Is it dangerously cold?,temperature When will the heat subside?,temperature Is it hot?,temperature Is it cold?,temperature How cold is it now?,temperature Will we have a cold day today?,temperature When will the cold subside?,temperature What highs are we expecting?,temperature What lows are we expecting?,temperature Is it warm?,temperature Is it chilly?,temperature What's the current temp in Celsius?,temperature What is the temperature in Fahrenheit?,temperature Is it windy?,conditions Will it rain today?,conditions What are the chances for rain?,conditions Will we get snow?,conditions Are we expecting sunny conditions?,conditions Is it overcast?,conditions Will it be cloudy?,conditions How much rain will fall today?,conditions How much snow are we expecting?,conditions Is it windy outside?,conditions How much snow do we expect?,conditions Is the forecast calling for snow today?,conditions Will we see some sun?,conditions When will the rain subside?,conditions Is it cloudy?,conditions Is it sunny now?,conditions Will it rain?,conditions Will we have much snow?,conditions Are the winds dangerous?,conditions What is the expected snowfall today?,conditions Will it be dry?,conditions Will it be breezy?,conditions Will it be humid?,conditions What is today's expected humidity?,conditions Will the blizzard hit us?,conditions Is it drizzling?,conditions -------------------------------------------------------------------------------- /test/unit/authenticator.test.js: -------------------------------------------------------------------------------- 1 | /** 2 | * (C) Copyright IBM Corp. 2019, 2021. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | const { Authenticator } = require('../../dist/auth'); 18 | 19 | describe('Authenticator', () => { 20 | it('should throw if "new" keyword is not used to create an instance', () => { 21 | expect(() => { 22 | // prettier-ignore 23 | // eslint-disable-next-line new-cap 24 | Authenticator(); 25 | }).toThrow(); 26 | }); 27 | 28 | // relying on individual authenticator tests to test the rest of this implementation 29 | }); 30 | -------------------------------------------------------------------------------- /test/unit/basic-authenticator.test.js: -------------------------------------------------------------------------------- 1 | /** 2 | * (C) Copyright IBM Corp. 2019, 2021. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | const { Authenticator, BasicAuthenticator } = require('../../dist/auth'); 18 | 19 | const USERNAME = 'dave'; 20 | const PASSWORD = 'grohl'; 21 | const CONFIG = { 22 | username: USERNAME, 23 | password: PASSWORD, 24 | }; 25 | 26 | describe('Basic Authenticator', () => { 27 | it('should store the username and password on the class', () => { 28 | const authenticator = new BasicAuthenticator(CONFIG); 29 | expect(authenticator.authenticationType()).toEqual(Authenticator.AUTHTYPE_BASIC); 30 | expect(authenticator.authHeader).toEqual({ 31 | Authorization: 'Basic ZGF2ZTpncm9obA==', 32 | }); 33 | }); 34 | 35 | it('should throw an error when username is not provided', () => { 36 | expect(() => { 37 | const unused = new BasicAuthenticator({ password: PASSWORD }); 38 | }).toThrow(); 39 | }); 40 | 41 | it('should throw an error when password is not provided', () => { 42 | expect(() => { 43 | const unused = new BasicAuthenticator({ username: USERNAME }); 44 | }).toThrow(); 45 | }); 46 | 47 | it('should throw an error when username has a bad character', () => { 48 | expect(() => { 49 | const unused = new BasicAuthenticator({ username: '""', password: PASSWORD }); 50 | }).toThrow(/Revise these credentials/); 51 | }); 52 | 53 | it('should throw an error when password has a bad character', () => { 54 | expect(() => { 55 | const unused = new BasicAuthenticator({ username: USERNAME, password: '{some-password}' }); 56 | }).toThrow(/Revise these credentials/); 57 | }); 58 | 59 | it('should update the options and resolve the Promise with `null`', async () => { 60 | const authenticator = new BasicAuthenticator(CONFIG); 61 | const options = {}; 62 | const result = await authenticator.authenticate(options); 63 | 64 | expect(result).toBeUndefined(); 65 | expect(options.headers.Authorization).toBe('Basic ZGF2ZTpncm9obA=='); 66 | }); 67 | }); 68 | -------------------------------------------------------------------------------- /test/unit/bearer-token-authenticator.test.js: -------------------------------------------------------------------------------- 1 | /** 2 | * (C) Copyright IBM Corp. 2019, 2021. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | const { Authenticator, BearerTokenAuthenticator } = require('../../dist/auth'); 18 | 19 | describe('Bearer Token Authenticator', () => { 20 | const config = { 21 | bearerToken: 'thisisthetoken', 22 | }; 23 | 24 | it('should store the bearer token on the class', () => { 25 | const authenticator = new BearerTokenAuthenticator(config); 26 | 27 | expect(authenticator.authenticationType()).toEqual(Authenticator.AUTHTYPE_BEARERTOKEN); 28 | expect(authenticator.bearerToken).toBe(config.bearerToken); 29 | }); 30 | 31 | it('should throw an error when bearer token is not provided', () => { 32 | expect(() => { 33 | const unused = new BearerTokenAuthenticator(); 34 | }).toThrow(); 35 | }); 36 | 37 | it('should update the options and resolve with `null`', async () => { 38 | const authenticator = new BearerTokenAuthenticator(config); 39 | const options = {}; 40 | const result = await authenticator.authenticate(options); 41 | 42 | expect(result).toBeUndefined(); 43 | expect(options.headers.Authorization).toBe(`Bearer ${config.bearerToken}`); 44 | }); 45 | 46 | it('should re-set the bearer token using the setter', () => { 47 | const authenticator = new BearerTokenAuthenticator(config); 48 | expect(authenticator.bearerToken).toBe(config.bearerToken); 49 | 50 | const newToken = 'updatedtoken'; 51 | authenticator.setBearerToken(newToken); 52 | expect(authenticator.bearerToken).toBe(newToken); 53 | }); 54 | }); 55 | -------------------------------------------------------------------------------- /test/unit/content-type.test.js: -------------------------------------------------------------------------------- 1 | /** 2 | * (C) Copyright IBM Corp. 2019, 2021. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | const fs = require('fs'); 18 | const path = require('path'); 19 | 20 | const contentType = require('../../dist/lib/content-type').default; 21 | 22 | describe('contentType', () => { 23 | const mp3 = 'audio/mp3'; 24 | const wav = 'audio/wav'; 25 | 26 | it('should return content type from a filename', () => { 27 | const fname = 'fake.mp3'; 28 | expect(contentType.fromFilename(fname)).toBe(mp3); 29 | }); 30 | 31 | it('should return content type from a File object', () => { 32 | const File = { name: 'fake.mp3' }; 33 | expect(contentType.fromFilename(File)).toBe(mp3); 34 | }); 35 | 36 | it('should return undefined for an empty input', () => { 37 | expect(contentType.fromFilename({})).toBeUndefined(); 38 | }); 39 | 40 | it('should return content type from a buffer', () => { 41 | const buffer = fs.readFileSync(path.join(__dirname, '../resources/blank.wav')); 42 | expect(contentType.fromHeader(buffer)).toBe(wav); 43 | }); 44 | }); 45 | -------------------------------------------------------------------------------- /test/unit/export-unit-test-utils.test.js: -------------------------------------------------------------------------------- 1 | /** 2 | * (C) Copyright IBM Corp. 2020, 2024. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | const unitTestUtils = require('../../sdk-test-utilities'); 18 | 19 | describe('Unit Test Utils', () => { 20 | it('should be defined', () => { 21 | expect(unitTestUtils).toBeDefined(); 22 | }); 23 | 24 | it('should have function checkUrlAndMethod', () => { 25 | expect(unitTestUtils.checkUrlAndMethod).toBeDefined(); 26 | }); 27 | 28 | it('should have function checkMediaHeaders', () => { 29 | expect(unitTestUtils.checkMediaHeaders).toBeDefined(); 30 | }); 31 | 32 | it('should have function checkUserHeader', () => { 33 | expect(unitTestUtils.checkUserHeader).toBeDefined(); 34 | }); 35 | 36 | it('should have function checkForSuccessfulExecution', () => { 37 | expect(unitTestUtils.checkForSuccessfulExecution).toBeDefined(); 38 | }); 39 | 40 | it('should have function getOptions', () => { 41 | expect(unitTestUtils.getOptions).toBeDefined(); 42 | }); 43 | 44 | it('should have function expectToBePromise', () => { 45 | expect(unitTestUtils.expectToBePromise).toBeDefined(); 46 | }); 47 | }); 48 | -------------------------------------------------------------------------------- /test/unit/get-content-type.test.js: -------------------------------------------------------------------------------- 1 | /** 2 | * (C) Copyright IBM Corp. 2019, 2021. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | const fs = require('fs'); 18 | const { getContentType } = require('../../dist/lib/helper'); 19 | 20 | const filepath = `${__dirname}/../resources/blank.wav`; 21 | 22 | describe('getContentType', () => { 23 | it('should read content type from read stream', async () => { 24 | const streamFile = fs.createReadStream(filepath); 25 | expect(await getContentType(streamFile)).toBe('audio/wave'); 26 | }); 27 | 28 | it('should not get content type from read stream with corrupted path property', async () => { 29 | // Note add an on error handler to avoid unhandled error events 30 | const streamFile = fs.createReadStream(filepath).on('error', () => {}); 31 | streamFile.path = 'unrecognizeable-format'; 32 | expect(await getContentType(streamFile)).toBeNull(); 33 | }); 34 | 35 | it('should read content type from buffer', async () => { 36 | const bufferFile = fs.readFileSync(filepath); 37 | expect(await getContentType(bufferFile)).toBe('audio/vnd.wave'); 38 | }); 39 | 40 | it('should not read content type from a string', async () => { 41 | const str = 'a,b,c,d,e'; 42 | expect(await getContentType(str)).toBeNull(); 43 | }); 44 | 45 | it('should not read content type from a number', async () => { 46 | const number = 4; 47 | expect(await getContentType(number)).toBeNull(); 48 | }); 49 | }); 50 | -------------------------------------------------------------------------------- /test/unit/get-credentials-from-vcap.test.js: -------------------------------------------------------------------------------- 1 | /** 2 | * (C) Copyright IBM Corp. 2019, 2021. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | const fs = require('fs'); 18 | const { readExternalSources } = require('../../dist/auth'); 19 | 20 | describe('getCredentialsFromVcap', () => { 21 | beforeAll(() => { 22 | process.env.VCAP_SERVICES = fs.readFileSync(`${__dirname}/../resources/vcap.json`); 23 | }); 24 | 25 | it('should return empty credential object when service key is empty_service', () => { 26 | const credentials = readExternalSources('empty_service'); 27 | expect(credentials).toBeDefined(); 28 | expect(Object.keys(credentials)).toHaveLength(0); 29 | }); 30 | 31 | it('should return credential object matching service name field is not first list element', () => { 32 | const credentials = readExternalSources('discovery2'); 33 | expect(credentials.url).toEqual( 34 | 'https://gateway.watsonplatform2.net/discovery-experimental/api' 35 | ); 36 | expect(credentials.username).toEqual('not-a-username'); 37 | expect(credentials.password).toEqual('not-a-password'); 38 | }); 39 | 40 | it('should return credential object when matching service name field on first list element', () => { 41 | const credentials = readExternalSources('discovery1'); 42 | expect(credentials.url).toEqual( 43 | 'https://gateway.watsonplatform1.net/discovery-experimental/api' 44 | ); 45 | expect(credentials.username).toEqual('not-a-username'); 46 | expect(credentials.password).toEqual('not-a-password'); 47 | }); 48 | 49 | it('should return first service in the list matching on primary service key', () => { 50 | const credentials = readExternalSources('discovery'); 51 | expect(credentials.url).toEqual( 52 | 'https://gateway.watsonplatform1.net/discovery-experimental/api' 53 | ); 54 | expect(credentials.username).toEqual('not-a-username'); 55 | expect(credentials.password).toEqual('not-a-password'); 56 | }); 57 | 58 | it('should return empty credential object when matching on service name with no credentials field', () => { 59 | const credentials = readExternalSources('no-creds-service-two'); 60 | expect(credentials).toBeDefined(); 61 | expect(Object.keys(credentials)).toHaveLength(0); 62 | }); 63 | }); 64 | -------------------------------------------------------------------------------- /test/unit/get-format.test.js: -------------------------------------------------------------------------------- 1 | /** 2 | * (C) Copyright IBM Corp. 2019, 2021. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | const { getFormat } = require('../../dist/lib/helper'); 18 | 19 | describe('getFormat', () => { 20 | test('should return null if params is undefined', () => { 21 | expect(getFormat(undefined, [])).toBeNull(); 22 | }); 23 | 24 | test('should return null if params is null', () => { 25 | expect(getFormat(null, [])).toBeNull(); 26 | }); 27 | 28 | test('should return null if formats is undefined', () => { 29 | expect(getFormat({}, undefined)).toBeNull(); 30 | }); 31 | 32 | test('should return null if formats is null', () => { 33 | expect(getFormat({}, null)).toBeNull(); 34 | }); 35 | 36 | test('should return null if formats is the empty list', () => { 37 | expect(getFormat({ a: 1 }, [])).toBeNull(); 38 | }); 39 | 40 | test('should return null if no format match is found', () => { 41 | expect(getFormat({}, ['a'])).toBeNull(); 42 | }); 43 | 44 | test('should return the first match found', () => { 45 | expect(getFormat({ a: 1 }, ['a', 'b', 'c'])).toEqual('a'); 46 | }); 47 | 48 | test('should return the first match found even if other formats match', () => { 49 | expect(getFormat({ c: 3, b: 2, a: 1 }, ['a', 'b', 'c'])).toEqual('a'); 50 | }); 51 | }); 52 | -------------------------------------------------------------------------------- /test/unit/get-missing-params.test.js: -------------------------------------------------------------------------------- 1 | /** 2 | * (C) Copyright IBM Corp. 2019, 2021. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | const { getMissingParams } = require('../../dist/lib/helper'); 18 | 19 | describe('getMissingParams', () => { 20 | it('should return null when both params and requires are null', () => { 21 | expect(getMissingParams(null, null)).toBeNull(); 22 | }); 23 | 24 | it('should return null when both params and requires are undefined', () => { 25 | expect(getMissingParams(undefined, undefined)).toBeNull(); 26 | }); 27 | 28 | it('should return null when params is null and requires is undefined', () => { 29 | expect(getMissingParams(null, undefined)).toBeNull(); 30 | }); 31 | 32 | it('should return null if params is undefined and requires is null', () => { 33 | expect(getMissingParams(undefined, null)).toBeNull(); 34 | }); 35 | 36 | it('should return null if params is undefined and require is an empty list', () => { 37 | expect(getMissingParams(undefined, [])).toBeNull(); 38 | }); 39 | 40 | it('should return null if params is an empty object and require is undefined', () => { 41 | expect(getMissingParams({}, undefined)).toBeNull(); 42 | }); 43 | 44 | it('should return null if params is null and require is an empty list', () => { 45 | expect(getMissingParams(null, [])).toBeNull(); 46 | }); 47 | 48 | it('should return null if params is an empty object and require is null', () => { 49 | expect(getMissingParams({}, null)).toBeNull(); 50 | }); 51 | 52 | it('should return null if params is non-empty and require is null', () => { 53 | expect(getMissingParams(['a'], null)).toBeNull(); 54 | }); 55 | 56 | it('should return null if params is non-empty and require is undefined', () => { 57 | expect(getMissingParams({ a: 'a' }, undefined)).toBeNull(); 58 | }); 59 | 60 | it('should return null if params is non-empty and require is an empty list', () => { 61 | expect(getMissingParams({ a: 'a' }, [])).toBeNull(); 62 | }); 63 | 64 | it('should return null if no parameters are missing', () => { 65 | expect(getMissingParams({ a: 'a', b: 'b', c: 'c' }, ['b', 'c'])).toBeNull(); 66 | }); 67 | 68 | it('should throw an error if there are missing parameters', () => { 69 | expect(getMissingParams({ a: 'a' }, ['a', 'b']).message).toBe('Missing required parameters: b'); 70 | }); 71 | 72 | it('should throw an error if params is null and there are missing parameters', () => { 73 | expect(getMissingParams(null, ['a', 'b']).message).toBe('Missing required parameters: a, b'); 74 | }); 75 | 76 | it('should throw an error if params is undefined and there are missing parameters', () => { 77 | expect(getMissingParams(undefined, ['a', 'b']).message).toBe( 78 | 'Missing required parameters: a, b' 79 | ); 80 | }); 81 | 82 | it('should not throw an error if a required parameter is given and set to false', () => { 83 | expect(getMissingParams({ a: 'a', b: false }, ['a', 'b'])).toBeNull(); 84 | }); 85 | }); 86 | -------------------------------------------------------------------------------- /test/unit/get-query-param.test.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2021 IBM Corp. All Rights Reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | const { getQueryParam } = require('../../dist/lib/helper'); 18 | 19 | describe('getQueryParam', () => { 20 | it('should return the parm value from a relative URL', () => { 21 | const nextUrl = '/api/v1/offerings?start=foo&limit=10'; 22 | expect(getQueryParam(nextUrl, 'start')).toBe('foo'); 23 | }); 24 | 25 | it('should return the param value from an absolute URL', () => { 26 | const nextUrl = 'https://acme.com/api/v1/offerings?start=bar&limit=10'; 27 | expect(getQueryParam(nextUrl, 'start')).toBe('bar'); 28 | }); 29 | 30 | it('should return null when the requested param is not present', () => { 31 | const nextUrl = 'https://acme.com/api/v1/offerings?start=bar&limit=10'; 32 | expect(getQueryParam(nextUrl, 'token')).toBeNull(); 33 | }); 34 | 35 | it('should return null when urlStr is null', () => { 36 | const nextUrl = null; 37 | expect(getQueryParam(nextUrl, 'start')).toBeNull(); 38 | }); 39 | 40 | it('should return null when urlStr is the empty string', () => { 41 | const nextUrl = ''; 42 | expect(getQueryParam(nextUrl, 'start')).toBeNull(); 43 | }); 44 | 45 | it('should return null when urlStr has no query string', () => { 46 | const nextUrl = '/api/v1/offerings'; 47 | expect(getQueryParam(nextUrl, 'start')).toBeNull(); 48 | }); 49 | 50 | it('should throw and exception when urlStr is an invalid URL', () => { 51 | const nextUrl = 'https://foo.bar:baz/api/v1/offerings?start=foo'; 52 | expect(() => { 53 | getQueryParam(nextUrl, 'start'); 54 | }).toThrow(/Invalid URL/); 55 | }); 56 | 57 | it('should return null when the query string is invalid', () => { 58 | const nextUrl = '/api/v1/offerings?start%XXfoo'; 59 | expect(getQueryParam(nextUrl, 'start')).toBeNull(); 60 | }); 61 | 62 | it('should return the first value when the query string has duplicate parameters', () => { 63 | const nextUrl = '/api/v1/offerings?start=foo&start=bar&limit=10'; 64 | expect(getQueryParam(nextUrl, 'start')).toBe('foo'); 65 | }); 66 | }); 67 | -------------------------------------------------------------------------------- /test/unit/iam-request-based-authenticator.test.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2021 IBM Corp. All Rights Reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | const { IamRequestBasedAuthenticator, IamRequestBasedTokenManager } = require('../../dist/auth'); 18 | 19 | const SCOPE = 'some-scope'; 20 | const CLIENT_ID = 'some-id'; 21 | const CLIENT_SECRET = 'some-secret'; 22 | 23 | describe('IAM Request Based Authenticator', () => { 24 | describe('constructor', () => { 25 | it('should store all config options on the class', () => { 26 | const authenticator = new IamRequestBasedAuthenticator({ 27 | scope: SCOPE, 28 | clientId: CLIENT_ID, 29 | clientSecret: CLIENT_SECRET, 30 | }); 31 | 32 | expect(authenticator.clientId).toBe(CLIENT_ID); 33 | expect(authenticator.clientSecret).toBe(CLIENT_SECRET); 34 | expect(authenticator.scope).toEqual(SCOPE); 35 | 36 | // should also create a token manager 37 | expect(authenticator.tokenManager).toBeInstanceOf(IamRequestBasedTokenManager); 38 | }); 39 | }); 40 | 41 | describe('setters', () => { 42 | it('should re-set the scope using the setter', () => { 43 | const authenticator = new IamRequestBasedAuthenticator(); 44 | expect(authenticator.scope).toBeUndefined(); 45 | 46 | expect(authenticator.tokenManager.scope).toBeUndefined(); 47 | 48 | authenticator.setScope(SCOPE); 49 | expect(authenticator.scope).toEqual(SCOPE); 50 | 51 | // also, verify that the underlying token manager has been updated 52 | expect(authenticator.tokenManager.scope).toEqual(SCOPE); 53 | }); 54 | 55 | it('should re-set the client id and secret using the setter', () => { 56 | const authenticator = new IamRequestBasedAuthenticator(); 57 | expect(authenticator.clientId).toBeUndefined(); 58 | expect(authenticator.clientSecret).toBeUndefined(); 59 | 60 | expect(authenticator.tokenManager.clientId).toBeUndefined(); 61 | expect(authenticator.tokenManager.clientSecret).toBeUndefined(); 62 | 63 | authenticator.setClientIdAndSecret(CLIENT_ID, CLIENT_SECRET); 64 | expect(authenticator.clientId).toEqual(CLIENT_ID); 65 | expect(authenticator.clientSecret).toEqual(CLIENT_SECRET); 66 | 67 | // also, verify that the underlying token manager has been updated 68 | expect(authenticator.tokenManager.clientId).toEqual(CLIENT_ID); 69 | expect(authenticator.tokenManager.clientSecret).toEqual(CLIENT_SECRET); 70 | }); 71 | }); 72 | }); 73 | -------------------------------------------------------------------------------- /test/unit/is-empty-object.test.js: -------------------------------------------------------------------------------- 1 | /** 2 | * (C) Copyright IBM Corp. 2019, 2021. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | const { isEmptyObject } = require('../../dist/lib/helper'); 18 | 19 | describe('isEmptyObject', () => { 20 | it('should return true for an empty object', () => { 21 | const emptyObj = {}; 22 | expect(isEmptyObject(emptyObj)).toBe(true); 23 | }); 24 | 25 | it('should return false for empty array', () => { 26 | const emptyArr = []; 27 | expect(isEmptyObject(emptyArr)).toBe(false); 28 | }); 29 | 30 | it('should return false for empty string', () => { 31 | const emptyStr = ''; 32 | expect(isEmptyObject(emptyStr)).toBe(false); 33 | }); 34 | 35 | it('should return false for zero', () => { 36 | const zero = 0; 37 | expect(isEmptyObject(zero)).toBe(false); 38 | }); 39 | 40 | it('should return false for non-empty object', () => { 41 | const obj = { a: 1, b: 2 }; 42 | expect(isEmptyObject(obj)).toBe(false); 43 | }); 44 | 45 | it('should return true for an object with its properties deleted', () => { 46 | const obj = { a: 1, b: 2 }; 47 | delete obj.a; 48 | delete obj.b; 49 | expect(isEmptyObject(obj)).toBe(true); 50 | }); 51 | }); 52 | -------------------------------------------------------------------------------- /test/unit/is-html.test.js: -------------------------------------------------------------------------------- 1 | /** 2 | * (C) Copyright IBM Corp. 2019, 2021. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | const { isHTML } = require('../../dist/lib/helper'); 18 | 19 | describe('isHTML', () => { 20 | it('should return false on undefined', () => { 21 | expect(isHTML(undefined)).toBe(false); 22 | }); 23 | 24 | it('should return false on null', () => { 25 | expect(isHTML(null)).toBe(false); 26 | }); 27 | 28 | it('should return false on empty string', () => { 29 | expect(isHTML('')).toBe(false); 30 | }); 31 | 32 | it('should return false on non-HTML string', () => { 33 | expect(isHTML('hello world!')).toBe(false); 34 | }); 35 | 36 | it('should return true on string with valid HTML elements', () => { 37 | expect(isHTML('foobar')).toBe(true); 38 | }); 39 | 40 | it('should return true on string with invalid HTML-like elements', () => { 41 | expect(isHTML('')).toBe(true); 42 | }); 43 | }); 44 | -------------------------------------------------------------------------------- /test/unit/is-json-mime-type.test.js: -------------------------------------------------------------------------------- 1 | /** 2 | * (C) Copyright IBM Corp. 2023. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | const { isJsonMimeType } = require('../../dist/lib/helper'); 18 | const logger = require('../../dist/lib/logger').default; 19 | 20 | const debugLogSpy = jest.spyOn(logger, 'debug').mockImplementation(() => {}); 21 | 22 | describe('isJsonMimeType()', () => { 23 | afterEach(() => { 24 | debugLogSpy.mockClear(); 25 | }); 26 | 27 | it('should return `false` for `undefined`', async () => { 28 | expect(isJsonMimeType(undefined)).toBe(false); 29 | expect(debugLogSpy.mock.calls[0][0]).toBe( 30 | "Determining if the mime type 'undefined' specifies JSON content." 31 | ); 32 | }); 33 | 34 | it('should return `false` for `null`', async () => { 35 | expect(isJsonMimeType(null)).toBe(false); 36 | expect(debugLogSpy.mock.calls[0][0]).toBe( 37 | "Determining if the mime type 'null' specifies JSON content." 38 | ); 39 | }); 40 | 41 | it('should return `false` for empty-string', async () => { 42 | expect(isJsonMimeType('')).toBe(false); 43 | expect(debugLogSpy.mock.calls[0][0]).toBe( 44 | "Determining if the mime type '' specifies JSON content." 45 | ); 46 | }); 47 | 48 | it('should return `false` for non-JSON mimetype', async () => { 49 | expect(isJsonMimeType('application/octect-stream')).toBe(false); 50 | expect(isJsonMimeType('text/plain')).toBe(false); 51 | expect(isJsonMimeType('multipart/form-data; charset=utf-8')).toBe(false); 52 | expect(debugLogSpy.mock.calls[0][0]).toBe( 53 | "Determining if the mime type 'application/octect-stream' specifies JSON content." 54 | ); 55 | expect(debugLogSpy.mock.calls[1][0]).toBe( 56 | "Determining if the mime type 'text/plain' specifies JSON content." 57 | ); 58 | expect(debugLogSpy.mock.calls[2][0]).toBe( 59 | "Determining if the mime type 'multipart/form-data; charset=utf-8' specifies JSON content." 60 | ); 61 | }); 62 | 63 | it('should return `true` for a JSON mimetype', async () => { 64 | expect(isJsonMimeType('application/json')).toBe(true); 65 | expect(isJsonMimeType('application/json;charset=utf-8')).toBe(true); 66 | expect(debugLogSpy.mock.calls[0][0]).toBe( 67 | "Determining if the mime type 'application/json' specifies JSON content." 68 | ); 69 | expect(debugLogSpy.mock.calls[1][0]).toBe( 70 | "Determining if the mime type 'application/json;charset=utf-8' specifies JSON content." 71 | ); 72 | }); 73 | 74 | it('should return `true` for a JSON mimetype including optional whitespace', async () => { 75 | expect(isJsonMimeType('application/json ; charset=utf-8')).toBe(true); 76 | expect(debugLogSpy.mock.calls[0][0]).toBe( 77 | "Determining if the mime type 'application/json ; charset=utf-8' specifies JSON content." 78 | ); 79 | }); 80 | }); 81 | -------------------------------------------------------------------------------- /test/unit/logger.test.js: -------------------------------------------------------------------------------- 1 | /** 2 | * (C) Copyright IBM Corp. 2019, 2022. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | describe('Logger', () => { 18 | let env; 19 | 20 | beforeEach(() => { 21 | jest.resetModules(); 22 | env = process.env; 23 | process.env = {}; 24 | }); 25 | afterEach(() => { 26 | process.env = env; 27 | }); 28 | 29 | it('should return an instance of the logger', () => { 30 | const logger = require('../../dist/lib/logger'); 31 | expect(logger).toBeTruthy(); 32 | }); 33 | 34 | it('no logger should be enabled', () => { 35 | const logger = require('../../dist/lib/logger').default; 36 | expect(logger.warn.enabled).toBeFalsy(); 37 | expect(logger.verbose.enabled).toBeFalsy(); 38 | expect(logger.info.enabled).toBeFalsy(); 39 | expect(logger.error.enabled).toBeFalsy(); 40 | expect(logger.debug.enabled).toBeFalsy(); 41 | }); 42 | 43 | it('should enable all loggers using the name "ibm-cloud-sdk-core"', () => { 44 | process.env.DEBUG = 'ibm-cloud-sdk-core*'; 45 | const logger = require('../../dist/lib/logger').default; 46 | 47 | expect(logger.debug.enabled).toBe(true); 48 | expect(logger.error.enabled).toBe(true); 49 | expect(logger.info.enabled).toBe(true); 50 | expect(logger.verbose.enabled).toBe(true); 51 | expect(logger.warn.enabled).toBe(true); 52 | }); 53 | 54 | it('should enable scoped loggers using the name "ibm-cloud-sdk-core"', () => { 55 | process.env.DEBUG = 'ibm-cloud-sdk-core:warning'; 56 | const logger = require('../../dist/lib/logger').default; 57 | 58 | expect(logger.debug.enabled).toBe(false); 59 | expect(logger.verbose.enabled).toBe(false); 60 | expect(logger.info.enabled).toBe(false); 61 | expect(logger.warn.enabled).toBe(true); 62 | expect(logger.error.enabled).toBe(true); 63 | }); 64 | }); 65 | -------------------------------------------------------------------------------- /test/unit/mcsp-authenticator.test.js: -------------------------------------------------------------------------------- 1 | /** 2 | * (C) Copyright IBM Corp. 2023. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | const { Authenticator, McspAuthenticator } = require('../../dist/auth'); 18 | const { McspTokenManager } = require('../../dist/auth'); 19 | 20 | const APIKEY = '32611'; 21 | const URL = 'https://mcsp.ibm.com'; 22 | const CONFIG = { 23 | apikey: APIKEY, 24 | url: URL, 25 | disableSslVerification: true, 26 | headers: { 27 | 'X-My-Header': 'some-value', 28 | }, 29 | }; 30 | 31 | // mock the `getToken` method in the token manager - dont make any rest calls 32 | const fakeToken = 'mcsp-acess-token'; 33 | const mockedTokenManager = new McspTokenManager({ 34 | url: URL, 35 | apikey: APIKEY, 36 | }); 37 | 38 | const getTokenSpy = jest 39 | .spyOn(mockedTokenManager, 'getToken') 40 | .mockImplementation(() => Promise.resolve(fakeToken)); 41 | 42 | describe('MCSP Authenticator', () => { 43 | it('should store all CONFIG options on the class', () => { 44 | const authenticator = new McspAuthenticator(CONFIG); 45 | expect(authenticator.authenticationType()).toEqual(Authenticator.AUTHTYPE_MCSP); 46 | expect(authenticator.apikey).toBe(CONFIG.apikey); 47 | expect(authenticator.url).toBe(CONFIG.url); 48 | expect(authenticator.disableSslVerification).toBe(CONFIG.disableSslVerification); 49 | expect(authenticator.headers).toEqual(CONFIG.headers); 50 | 51 | // should also create a token manager 52 | expect(authenticator.tokenManager).toBeInstanceOf(McspTokenManager); 53 | }); 54 | 55 | it('should store apikey and url on the class if provided', () => { 56 | const authenticator = new McspAuthenticator({ 57 | url: URL, 58 | apikey: APIKEY, 59 | }); 60 | 61 | expect(authenticator.apikey).toBe(APIKEY); 62 | expect(authenticator.url).toBe(URL); 63 | }); 64 | 65 | it('should throw an error when apikey is not provided', () => { 66 | expect(() => { 67 | const unused = new McspAuthenticator({ url: URL }); 68 | }).toThrow(/Missing required parameter/); 69 | }); 70 | 71 | it('should throw an error when url is not provided', () => { 72 | expect(() => { 73 | const unused = new McspAuthenticator({ apikey: APIKEY }); 74 | }).toThrow(/Missing required parameter/); 75 | }); 76 | 77 | it('should update the options and resolve with `null`', async () => { 78 | const authenticator = new McspAuthenticator(CONFIG); 79 | 80 | // override the created token manager with the mocked one 81 | authenticator.tokenManager = mockedTokenManager; 82 | 83 | const options = { headers: { 'X-Some-Header': 'user-supplied header' } }; 84 | const result = await authenticator.authenticate(options); 85 | 86 | expect(result).toBeUndefined(); 87 | expect(options.headers.Authorization).toBe(`Bearer ${fakeToken}`); 88 | expect(getTokenSpy).toHaveBeenCalled(); 89 | 90 | // verify that the original options are kept intact 91 | expect(options.headers['X-Some-Header']).toBe('user-supplied header'); 92 | }); 93 | 94 | it('should re-set disableSslVerification using the setter', () => { 95 | const authenticator = new McspAuthenticator(CONFIG); 96 | expect(authenticator.disableSslVerification).toBe(CONFIG.disableSslVerification); 97 | 98 | const newValue = false; 99 | authenticator.setDisableSslVerification(newValue); 100 | expect(authenticator.disableSslVerification).toBe(newValue); 101 | 102 | // also, verify that the underlying token manager has been updated 103 | expect(authenticator.tokenManager.disableSslVerification).toBe(newValue); 104 | }); 105 | 106 | it('should re-set the headers using the setter', () => { 107 | const authenticator = new McspAuthenticator(CONFIG); 108 | expect(authenticator.headers).toEqual(CONFIG.headers); 109 | 110 | const newHeader = { 'X-New-Header': 'updated-header' }; 111 | authenticator.setHeaders(newHeader); 112 | expect(authenticator.headers).toEqual(newHeader); 113 | 114 | // also, verify that the underlying token manager has been updated 115 | expect(authenticator.tokenManager.headers).toEqual(newHeader); 116 | }); 117 | }); 118 | -------------------------------------------------------------------------------- /test/unit/no-auth-authenticator.test.js: -------------------------------------------------------------------------------- 1 | /** 2 | * (C) Copyright IBM Corp. 2019, 2021. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | const { Authenticator, NoAuthAuthenticator } = require('../../dist/auth'); 18 | 19 | describe('NoAuth Authenticator', () => { 20 | it('should resolve Promise on authenticate', async () => { 21 | const authenticator = new NoAuthAuthenticator(); 22 | expect(authenticator.authenticationType()).toEqual(Authenticator.AUTHTYPE_NOAUTH); 23 | const result = await authenticator.authenticate({}); 24 | 25 | expect(result).toBeUndefined(); 26 | }); 27 | }); 28 | -------------------------------------------------------------------------------- /test/unit/parameterized-url.test.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2021 IBM Corp. All Rights Reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | const { constructServiceUrl } = require('../../dist/lib/helper'); 18 | 19 | const parameterizedUrl = '{scheme}://{domain}:{port}'; 20 | const defaultUrlVariables = new Map([ 21 | ['scheme', 'http'], 22 | ['domain', 'ibm.com'], 23 | ['port', '9300'], 24 | ]); 25 | 26 | describe('constructServiceUrl', () => { 27 | it('should use default variable values when null is passed', () => { 28 | expect(constructServiceUrl(parameterizedUrl, defaultUrlVariables, null)).toBe( 29 | 'http://ibm.com:9300' 30 | ); 31 | }); 32 | 33 | it('should use the values provided and defaults for the rest', () => { 34 | const providedUrlVariables = new Map([ 35 | ['scheme', 'https'], 36 | ['port', '22'], 37 | ]); 38 | 39 | expect(constructServiceUrl(parameterizedUrl, defaultUrlVariables, providedUrlVariables)).toBe( 40 | 'https://ibm.com:22' 41 | ); 42 | }); 43 | 44 | it('should use all provided values', () => { 45 | const providedUrlVariables = new Map([ 46 | ['scheme', 'https'], 47 | ['domain', 'google.com'], 48 | ['port', '22'], 49 | ]); 50 | 51 | expect(constructServiceUrl(parameterizedUrl, defaultUrlVariables, providedUrlVariables)).toBe( 52 | 'https://google.com:22' 53 | ); 54 | }); 55 | 56 | it('should throw an error if a provided variable name is wrong', () => { 57 | const providedUrlVariables = new Map([['server', 'value']]); 58 | 59 | expect(() => 60 | constructServiceUrl(parameterizedUrl, defaultUrlVariables, providedUrlVariables) 61 | ).toThrow( 62 | /'server' is an invalid variable name\.\n\s*Valid variable names: \[domain,port,scheme\]\./ 63 | ); 64 | }); 65 | }); 66 | -------------------------------------------------------------------------------- /test/unit/querystring.test.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2021 IBM Corp. All Rights Reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | const { stringify } = require('../../dist/lib/querystring').default; 18 | 19 | describe('querystring', () => { 20 | it('should convert params to query string format', () => { 21 | const params = { foo: 'bar', baz: ['qux', 'quux'], corge: '' }; 22 | expect(stringify(params)).toBe('foo=bar&baz=qux%2Cquux&corge='); 23 | }); 24 | }); 25 | -------------------------------------------------------------------------------- /test/unit/redact-secrets.test.js: -------------------------------------------------------------------------------- 1 | /** 2 | * (C) Copyright IBM Corp. 2024. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | const { redactSecrets } = require('../../dist/lib/private-helpers'); 18 | 19 | describe('redactSecrets()', () => { 20 | it('misc. tests', async () => { 21 | const inputStrings = [ 22 | 'Authorization: Bearer secret', 23 | 'Authorization: secret\nX-Author: secret', 24 | 'apikey=81KiI5Zm2kjOWnrSglhtnDJn3u0kfv&grant_type=apikey&response_type=cloud_iam', 25 | `{"apikey":"secret"}`, 26 | 'apikey=secret&project_id=secret&api_key=secret&passcode=secret&password=secret&token=secret&response_type=not_a_secret', 27 | '{"aadClientId":"secret", "auth":"secret", "key":"secret", "secret":"foo", "token_uri":"secret", "client_id":"secret", "tenantId":"secret"}', 28 | ]; 29 | const outputStrings = [ 30 | 'Authorization: [redacted]', 31 | 'Authorization: [redacted]\nX-Author: [redacted]', 32 | 'apikey=[redacted]&grant_type=apikey&response_type=cloud_iam', 33 | `{"apikey":"[redacted]"}`, 34 | 'apikey=[redacted]&project_id=[redacted]&api_key=[redacted]&passcode=[redacted]&password=[redacted]&token=[redacted]&response_type=not_a_secret', 35 | '{"aadClientId":"[redacted]", "auth":"[redacted]", "key":"[redacted]", "secret":"[redacted]", "token_uri":"[redacted]", "client_id":"[redacted]", "tenantId":"[redacted]"}', 36 | ]; 37 | 38 | for (let i = 0; i < inputStrings.length; i++) { 39 | expect(redactSecrets(inputStrings[i])).toBe(outputStrings[i]); 40 | } 41 | }); 42 | it('test debug output', async () => { 43 | const input = ` 44 | POST https://iam.cloud.ibm.com/identity/token 45 | Accept: application/json, text/plain, */* 46 | Content-Type: application/x-www-form-urlencoded 47 | Authorization: Bearer secret 48 | User-Agent: ibm-node-sdk-core/iam-authenticator-5.0.1 os.name=linux os.version=6.10.5-100.fc39.x86_64 node.version=v20.12.2 49 | apikey=secret&grant_type=urn%3Aibm%3Aparams%3Aoauth%3Agrant-type%3Aapikey&response_type=cloud_iam 50 | transaction-id: cXpoZmY-262c12615bae4b6e92d10faf37033ad4 51 | content-type: application/json 52 | content-language: en-US 53 | strict-transport-security: max-age=31536000; includeSubDomains 54 | vary: Accept-Encoding 55 | content-length: 986 56 | connection: keep-alive 57 | {"access_token":"secret","refresh_token":"secret","token_type":"Bearer","expires_in":3600,"expiration":1724788046,"scope":"ibm openid"} 58 | `; 59 | 60 | const output = ` 61 | POST https://iam.cloud.ibm.com/identity/token 62 | Accept: application/json, text/plain, */* 63 | Content-Type: application/x-www-form-urlencoded 64 | Authorization: [redacted] 65 | User-Agent: ibm-node-sdk-core/iam-authenticator-5.0.1 os.name=linux os.version=6.10.5-100.fc39.x86_64 node.version=v20.12.2 66 | apikey=[redacted]&grant_type=urn%3Aibm%3Aparams%3Aoauth%3Agrant-type%3Aapikey&response_type=cloud_iam 67 | transaction-id: cXpoZmY-262c12615bae4b6e92d10faf37033ad4 68 | content-type: application/json 69 | content-language: en-US 70 | strict-transport-security: max-age=31536000; includeSubDomains 71 | vary: Accept-Encoding 72 | content-length: 986 73 | connection: keep-alive 74 | {"access_token":"[redacted]","refresh_token":"[redacted]","token_type":"Bearer","expires_in":3600,"expiration":1724788046,"scope":"ibm openid"} 75 | `; 76 | 77 | expect(redactSecrets(input)).toBe(output); 78 | }); 79 | }); 80 | -------------------------------------------------------------------------------- /test/unit/retry.test.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2021 IBM Corp. All Rights Reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | const nock = require('nock'); 18 | const rax = require('retry-axios'); 19 | 20 | const url = 'http://example.test-url.com'; 21 | nock.disableNetConnect(); 22 | 23 | const { NoAuthAuthenticator, BaseService } = require('../../dist'); 24 | 25 | const AUTHENTICATOR = new NoAuthAuthenticator(); 26 | 27 | const service = new BaseService({ 28 | authenticator: AUTHENTICATOR, 29 | serviceUrl: url, 30 | }); 31 | 32 | const parameters = { 33 | options: { 34 | method: 'GET', 35 | url: '/', 36 | headers: {}, 37 | }, 38 | defaultOptions: { 39 | serviceUrl: url, 40 | }, 41 | }; 42 | 43 | describe('Node Core retries', () => { 44 | beforeEach(() => { 45 | service.enableRetries(); 46 | }); 47 | 48 | afterEach(() => { 49 | nock.cleanAll(); 50 | service.disableRetries(); 51 | }); 52 | 53 | it('should retry after we call enableRetries', async () => { 54 | const scopes = [ 55 | nock(url).get('/').reply(429, undefined), 56 | nock(url).get('/').reply(200, 'retry success!'), 57 | ]; 58 | 59 | const result = await service.createRequest(parameters); 60 | expect(result.result).toBe('retry success!'); 61 | // ensure all mocks satisfied 62 | scopes.forEach((s) => s.done()); 63 | }); 64 | 65 | it('should retry after we call enableRetries with POST verb', async () => { 66 | const scopes = [ 67 | nock(url).post('/').reply(429, undefined), 68 | nock(url).post('/').reply(200, 'retry success!'), 69 | ]; 70 | 71 | parameters.options.method = 'POST'; 72 | const result = await service.createRequest(parameters); 73 | expect(result.result).toBe('retry success!'); 74 | // ensure all mocks satisfied 75 | scopes.forEach((s) => s.done()); 76 | }); 77 | 78 | it('should not retry more than `maxRetries`', async () => { 79 | const scopes = [ 80 | nock(url).get('/').reply(500, undefined), 81 | nock(url).get('/').reply(500, undefined), // should stop after this response 82 | nock(url).get('/').reply(200, 'should not get this!'), 83 | ]; 84 | 85 | service.enableRetries({ maxRetries: 1 }); 86 | 87 | // ensure 1 assertion executed in this test (i.e. promise not resolved.) 88 | expect.assertions(1); 89 | // eslint-disable-next-line jest/no-conditional-expect 90 | await service.createRequest(parameters).catch((err) => expect(err).toBeDefined()); 91 | }); 92 | 93 | it('should not retry on 501`', async () => { 94 | const scopes = [ 95 | nock(url).get('/').reply(501, undefined), 96 | nock(url).get('/').reply(200, 'retry success!'), 97 | ]; 98 | 99 | let err; 100 | await service.createRequest(parameters).catch((error) => { 101 | err = error; 102 | }); 103 | expect(err).toBeDefined(); 104 | }); 105 | 106 | it('should not retry after we call disableRetries', async () => { 107 | const scopes = [ 108 | nock(url).get('/').reply(500, undefined), 109 | nock(url).get('/').reply(200, 'should not get this!'), 110 | ]; 111 | 112 | // disable retries 113 | service.disableRetries(); 114 | 115 | // ensure 1 assertion executed in this test (i.e. promise not resolved.) 116 | expect.assertions(1); 117 | // eslint-disable-next-line jest/no-conditional-expect 118 | await service.createRequest(parameters).catch((err) => expect(err).toBeDefined()); 119 | }); 120 | }); 121 | -------------------------------------------------------------------------------- /test/unit/strip-trailing-slash.test.js: -------------------------------------------------------------------------------- 1 | /** 2 | * (C) Copyright IBM Corp. 2019, 2021. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | const { stripTrailingSlash } = require('../../dist/lib/helper'); 18 | 19 | describe('stripTrailingSlash', () => { 20 | test('should strip one slash from the end of url with a single trailing slash', () => { 21 | const url = 'https://ibmcloud.net'; 22 | const urlWithSlash = `${url}/`; 23 | expect(stripTrailingSlash(urlWithSlash)).toEqual(url); 24 | }); 25 | 26 | test('should not strip anything from a url without trailing slashes', () => { 27 | const url = 'https://ibmcloud.net'; 28 | expect(stripTrailingSlash(url)).toEqual(url); 29 | }); 30 | 31 | test('should return an empty string on empty string', () => { 32 | const url = ''; 33 | expect(stripTrailingSlash(url)).toEqual(url); 34 | }); 35 | }); 36 | -------------------------------------------------------------------------------- /test/unit/to-lower-keys.test.js: -------------------------------------------------------------------------------- 1 | /** 2 | * (C) Copyright IBM Corp. 2019, 2021. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | const { toLowerKeys } = require('../../dist/lib/helper'); 18 | 19 | describe('toLowerKeys', () => { 20 | it('should convert all keys of object to lower case', () => { 21 | const original = { 22 | ALLCAPS: 'a', 23 | MixedCase: 'b', 24 | lowercase: 'c', 25 | withNumbers123: 'd', 26 | sNaKe_CaSe: 'e', 27 | }; 28 | const convertedKeys = Object.keys(toLowerKeys(original)); 29 | const originalKeys = Object.keys(original); 30 | const allLowerCase = convertedKeys.every((key) => key === key.toLowerCase()); 31 | const allKeysPresent = originalKeys.every( 32 | (key) => convertedKeys.indexOf(key.toLowerCase()) > -1 33 | ); 34 | expect(allLowerCase).toBe(true); 35 | expect(allKeysPresent && originalKeys.length === convertedKeys.length).toBe(true); 36 | }); 37 | }); 38 | -------------------------------------------------------------------------------- /test/unit/to-promise.test.js: -------------------------------------------------------------------------------- 1 | /** 2 | * (C) Copyright IBM Corp. 2019, 2021. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | const crypto = require('crypto'); 18 | const fs = require('fs'); 19 | const path = require('path'); 20 | const { Transform } = require('stream'); 21 | const toPromise = require('../../dist/lib/stream-to-promise').streamToPromise; 22 | 23 | describe('toPromise()', () => { 24 | // Testing stream of text in Buffer mode 25 | it('should resolve with results buffer (text)', () => { 26 | const fileChecksum = crypto.createHash('md5'); 27 | const file = fs.createReadStream(path.join(__dirname, '../resources/weather-data-train.csv')); 28 | const p = toPromise(file); 29 | file.pipe(fileChecksum); 30 | return p 31 | .then((buffer) => { 32 | // Check we received a buffer 33 | expect(buffer).toBeInstanceOf(Buffer); 34 | // Check it was of the correct length 35 | expect(buffer).toHaveLength(1794); 36 | // Calculate a checksum 37 | const promiseChecksum = crypto.createHash('md5'); 38 | promiseChecksum.update(buffer); 39 | return promiseChecksum; 40 | }) 41 | .then((promiseChecksum) => { 42 | // Verify the checksum from the promise buffer matches the original file 43 | expect(promiseChecksum.digest('hex')).toEqual(fileChecksum.digest('hex')); 44 | }); 45 | }); 46 | 47 | // Testing stream of text in Buffer mode 48 | it('should resolve with results buffer (binary)', () => { 49 | const fileChecksum = crypto.createHash('md5'); 50 | const file = fs.createReadStream(path.join(__dirname, '../resources/blank.wav')); 51 | const p = toPromise(file); 52 | file.pipe(fileChecksum); 53 | return p 54 | .then((buffer) => { 55 | // Check we received a buffer 56 | expect(buffer).toBeInstanceOf(Buffer); 57 | // Check it was of the correct length 58 | expect(buffer).toHaveLength(113520); 59 | // Calculate a checksum 60 | const promiseChecksum = crypto.createHash('md5'); 61 | promiseChecksum.update(buffer); 62 | return promiseChecksum; 63 | }) 64 | .then((promiseChecksum) => { 65 | // Verify the checksum from the promise buffer matches the original file 66 | expect(promiseChecksum.digest('hex')).toEqual(fileChecksum.digest('hex')); 67 | }); 68 | }); 69 | 70 | // Testing stream of text in String mode 71 | it('should resolve with results string as an array', () => { 72 | const file = fs.createReadStream(path.join(__dirname, '../resources/weather-data-train.csv')); 73 | file.setEncoding('utf-8'); 74 | return expect(toPromise(file)).resolves.toBeInstanceOf(Array); 75 | }); 76 | }); 77 | -------------------------------------------------------------------------------- /test/unit/token-request-based-authenticator.test.js: -------------------------------------------------------------------------------- 1 | /** 2 | * (C) Copyright IBM Corp. 2019, 2024. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | const { TokenRequestBasedAuthenticator } = require('../../dist/auth'); 18 | const { JwtTokenManager } = require('../../dist/auth'); 19 | 20 | describe('Token Request Based Authenticator', () => { 21 | const config = { 22 | url: 'auth.com', 23 | disableSslVerification: true, 24 | headers: { 25 | 'X-My-Header': 'some-value', 26 | }, 27 | }; 28 | 29 | it('should store all config options on the class', () => { 30 | const authenticator = new TokenRequestBasedAuthenticator(config); 31 | 32 | expect(authenticator.url).toBe(config.url); 33 | expect(authenticator.disableSslVerification).toBe(config.disableSslVerification); 34 | expect(authenticator.headers).toEqual(config.headers); 35 | 36 | // should also create a token manager 37 | expect(authenticator.tokenManager).toBeInstanceOf(JwtTokenManager); 38 | }); 39 | 40 | it('should not re-set headers when a non-object is passed to the setter', () => { 41 | const authenticator = new TokenRequestBasedAuthenticator(config); 42 | expect(authenticator.headers).toEqual(config.headers); 43 | 44 | const badHeader = 42; 45 | authenticator.setHeaders(badHeader); 46 | expect(authenticator.headers).toEqual(config.headers); 47 | 48 | // also, verify that the underlying token manager has been updated 49 | expect(authenticator.tokenManager.headers).toEqual(config.headers); 50 | }); 51 | 52 | it('should reject the Promise in authenticate with an error if the token request fails', (done) => { 53 | const authenticator = new TokenRequestBasedAuthenticator(config); 54 | const fakeError = new Error('fake error'); 55 | const getTokenSpy = jest 56 | .spyOn(authenticator.tokenManager, 'getToken') 57 | .mockImplementation(() => Promise.reject(fakeError)); 58 | 59 | authenticator.authenticate({}).then( 60 | (res) => { 61 | done(`Promise unexpectedly resolved with value: ${res}`); 62 | }, 63 | (err) => { 64 | expect(getTokenSpy).toHaveBeenCalled(); 65 | expect(err).toBe(fakeError); 66 | done(); 67 | } 68 | ); 69 | }); 70 | }); 71 | -------------------------------------------------------------------------------- /test/unit/utils.js: -------------------------------------------------------------------------------- 1 | /** 2 | * (C) Copyright IBM Corp. 2024. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | // a function to pull the arguments out of the `sendRequest` mock 18 | // and verify the structure looks like it is supposed to 19 | function getRequestOptions(sendRequestMock, requestIndex = 0) { 20 | const sendRequestArgs = sendRequestMock.mock.calls[requestIndex][0]; 21 | expect(sendRequestArgs).toBeDefined(); 22 | expect(sendRequestArgs.options).toBeDefined(); 23 | 24 | return sendRequestArgs.options; 25 | } 26 | 27 | module.exports = { getRequestOptions }; 28 | -------------------------------------------------------------------------------- /test/unit/vpc-instance-authenticator.test.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2021 IBM Corp. All Rights Reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | const { Authenticator, VpcInstanceAuthenticator } = require('../../dist/auth'); 18 | const { VpcInstanceTokenManager } = require('../../dist/auth'); 19 | 20 | // mock the `getToken` method in the token manager - dont make any rest calls 21 | const fakeToken = 'iam-acess-token'; 22 | const mockedTokenManager = new VpcInstanceTokenManager(); 23 | 24 | const getTokenSpy = jest 25 | .spyOn(mockedTokenManager, 'getToken') 26 | .mockImplementation(() => Promise.resolve(fakeToken)); 27 | 28 | describe('VPC Instance Authenticator', () => { 29 | const config = { 30 | iamProfileId: 'some-id', 31 | url: 'someurl.com', 32 | }; 33 | 34 | it('should store all config options on the class', () => { 35 | const authenticator = new VpcInstanceAuthenticator(config); 36 | 37 | expect(authenticator.authenticationType()).toEqual(Authenticator.AUTHTYPE_VPC); 38 | expect(authenticator.iamProfileCrn).not.toBeDefined(); 39 | expect(authenticator.iamProfileId).toBe(config.iamProfileId); 40 | expect(authenticator.url).toBe(config.url); 41 | 42 | // should also create a token manager 43 | expect(authenticator.tokenManager).toBeInstanceOf(VpcInstanceTokenManager); 44 | }); 45 | 46 | it('should throw an error when both iamProfileCrn and iamProfileId are provided', () => { 47 | expect(() => { 48 | const unused = new VpcInstanceAuthenticator({ 49 | iamProfileCrn: 'crn', 50 | iamProfileId: 'id', 51 | }); 52 | }).toThrow('At most one of `iamProfileId` or `iamProfileCrn` may be specified.'); 53 | }); 54 | 55 | it('should re-set iamProfileCrn using the setter', () => { 56 | const authenticator = new VpcInstanceAuthenticator({ iamProfileCrn: 'test' }); 57 | expect(authenticator.iamProfileCrn).not.toBe(config.iamProfileCrn); 58 | expect(authenticator.tokenManager.iamProfileCrn).not.toBe(config.iamProfileCrn); 59 | 60 | authenticator.setIamProfileCrn(config.iamProfileCrn); 61 | expect(authenticator.iamProfileCrn).toEqual(config.iamProfileCrn); 62 | 63 | // also, verify that the underlying token manager has been updated 64 | expect(authenticator.tokenManager.iamProfileCrn).toEqual(config.iamProfileCrn); 65 | }); 66 | 67 | it('should re-set iamProfileId using the setter', () => { 68 | const authenticator = new VpcInstanceAuthenticator(); 69 | expect(authenticator.iamProfileId).toBeUndefined(); 70 | expect(authenticator.tokenManager.iamProfileId).toBeUndefined(); 71 | 72 | authenticator.setIamProfileId(config.iamProfileId); 73 | expect(authenticator.iamProfileId).toEqual(config.iamProfileId); 74 | 75 | // also, verify that the underlying token manager has been updated 76 | expect(authenticator.tokenManager.iamProfileId).toEqual(config.iamProfileId); 77 | }); 78 | 79 | // "end to end" style test, to make sure this authenticator integrates properly with parent classes 80 | it('should update the options and resolve with `null` when `authenticate` is called', async () => { 81 | const authenticator = new VpcInstanceAuthenticator({ iamProfileCrn: config.iamProfileCrn }); 82 | 83 | // override the created token manager with the mocked one 84 | authenticator.tokenManager = mockedTokenManager; 85 | 86 | const options = {}; 87 | const result = await authenticator.authenticate(options); 88 | 89 | expect(result).toBeUndefined(); 90 | expect(options.headers.Authorization).toBe(`Bearer ${fakeToken}`); 91 | expect(getTokenSpy).toHaveBeenCalled(); 92 | }); 93 | }); 94 | -------------------------------------------------------------------------------- /tsconfig-es6.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "target": "ES6", 5 | "module": "ESNext", 6 | "outDir": "./dist/es", 7 | "declaration": true 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | /* Basic Options */ 4 | "target": "es5", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', or 'ESNEXT'. */ 5 | "module": "commonjs", /* Specify module code generation: 'none', commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */ 6 | "lib": [ 7 | "es5", 8 | "es2015", 9 | "dom", 10 | "scripthost"], /* Specify library files to be included in the compilation: */ 11 | // "allowJs": true, /* Allow javascript files to be compiled. */ 12 | // "checkJs": true, /* Report errors in .js files. */ 13 | // "jsx": "preserve", /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */ 14 | "declaration": true, /* Generates corresponding '.d.ts' file. */ 15 | // "sourceMap": true, /* Generates corresponding '.map' file. */ 16 | // "outFile": "./", /* Concatenate and emit output to single file. */ 17 | "outDir": "./dist", /* Redirect output structure to the directory. */ 18 | // "rootDir": "./", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */ 19 | // "removeComments": true, /* Do not emit comments to output. */ 20 | // "noEmit": true, /* Do not emit outputs. */ 21 | // "importHelpers": true, /* Import emit helpers from 'tslib'. */ 22 | // "downlevelIteration": true, /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */ 23 | // "isolatedModules": true, /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */ 24 | 25 | /* Strict Type-Checking Options */ 26 | // "strict": true, /* Enable all strict type-checking options. */ 27 | // "noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */ 28 | // "strictNullChecks": true, /* Enable strict null checks. */ 29 | // "noImplicitThis": true, /* Raise error on 'this' expressions with an implied 'any' type. */ 30 | "alwaysStrict": true, /* Parse in strict mode and emit "use strict" for each source file. */ 31 | 32 | /* Additional Checks */ 33 | // "noUnusedLocals": true, /* Report errors on unused locals. */ 34 | // "noUnusedParameters": true, /* Report errors on unused parameters. */ 35 | // "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */ 36 | // "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */ 37 | 38 | /* Module Resolution Options */ 39 | "moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */ 40 | // "baseUrl": "./", /* Base directory to resolve non-absolute module names. */ 41 | // "paths": {}, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */ 42 | // "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */ 43 | // "typeRoots": [], /* List of folders to include type definitions from. */ 44 | // "types": ["node", "extend", "isstream"]/* Type declaration files to be included in compilation. */ 45 | // "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */ 46 | // "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */ 47 | 48 | /* Source Map Options */ 49 | // "sourceRoot": "./", /* Specify the location where debugger should locate TypeScript files instead of source locations. */ 50 | // "mapRoot": "./", /* Specify the location where debugger should locate map files instead of generated locations. */ 51 | // "inlineSourceMap": true, /* Emit a single file with source maps instead of having a separate file. */ 52 | // "inlineSources": true, /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */ 53 | 54 | /* Experimental Options */ 55 | // "experimentalDecorators": true, /* Enables experimental support for ES7 decorators. */ 56 | // "emitDecoratorMetadata": true, /* Enables experimental support for emitting type metadata for decorators. */ 57 | "esModuleInterop": true 58 | }, 59 | "include": [ 60 | "./lib/*.ts", 61 | "./auth/**/*.ts", 62 | "index.ts" 63 | ] 64 | } 65 | --------------------------------------------------------------------------------