├── .bumpversion.toml ├── .eslintignore ├── .eslintrc.js ├── .github ├── CODEOWNERS ├── ISSUE_TEMPLATE │ ├── bug_report.md │ └── feature_request.md ├── dco.yml ├── dependabot.yml ├── pull_request_template.md └── workflows │ ├── codeql.yml │ ├── docs.yml │ └── test.yml ├── .gitignore ├── .npmrc-jenkins ├── .prettierrc.js ├── .releaserc ├── .secrets.baseline ├── .whitesource ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── Jenkinsfile ├── KNOWN_ISSUES.md ├── LICENSE ├── README.md ├── SECURITY.md ├── api-changes.md ├── auth ├── couchdbSessionAuthenticator.ts ├── index.ts └── sessionTokenManager.ts ├── cloudant ├── features │ ├── changesFollower.ts │ ├── changesParamsHelper.ts │ ├── changesResultItemStream.ts │ ├── changesResultIterator.ts │ └── stream.ts └── v1.ts ├── examples ├── README.md └── snippets │ ├── deleteAttachment │ └── example_request.js │ ├── deleteDatabase │ └── example_request.js │ ├── deleteDesignDocument │ └── example_request.js │ ├── deleteDocument │ └── example_request.js │ ├── deleteIndex │ └── example_request.js │ ├── deleteLocalDocument │ └── example_request.js │ ├── deleteReplicationDocument │ └── example_request.js │ ├── getActiveTasks │ └── example_request.js │ ├── getActivityTrackerEvents │ └── example_request.js │ ├── getAllDbs │ └── example_request.js │ ├── getAttachment │ └── example_request.js │ ├── getCapacityThroughputInformation │ └── example_request.js │ ├── getCorsInformation │ └── example_request.js │ ├── getCurrentThroughputInformation │ └── example_request.js │ ├── getDatabaseInformation │ └── example_request.js │ ├── getDbUpdates │ └── example_request.js │ ├── getDesignDocument │ └── example_request.js │ ├── getDesignDocumentInformation │ └── example_request.js │ ├── getDocument │ └── example_request.js │ ├── getDocumentShardsInfo │ └── example_request.js │ ├── getIndexesInformation │ └── example_request.js │ ├── getLocalDocument │ └── example_request.js │ ├── getMembershipInformation │ └── example_request.js │ ├── getPartitionInformation │ └── example_request.js │ ├── getReplicationDocument │ └── example_request.js │ ├── getSchedulerDocs │ └── example_request.js │ ├── getSchedulerDocument │ └── example_request.js │ ├── getSchedulerJob │ └── example_request.js │ ├── getSchedulerJobs │ └── example_request.js │ ├── getSearchInfo │ └── example_request.js │ ├── getSecurity │ └── example_request.js │ ├── getServerInformation │ └── example_request.js │ ├── getSessionInformation │ └── example_request.js │ ├── getShardsInformation │ └── example_request.js │ ├── getUpInformation │ └── example_request.js │ ├── getUuids │ └── example_request.js │ ├── headAttachment │ └── example_request.js │ ├── headDatabase │ └── example_request.js │ ├── headDesignDocument │ └── example_request.js │ ├── headDocument │ └── example_request.js │ ├── headReplicationDocument │ └── example_request.js │ ├── headSchedulerJob │ └── example_request.js │ ├── postActivityTrackerEvents │ └── example_request.js │ ├── postAllDocs │ ├── example_request.js │ └── example_request_as_a_stream.js │ ├── postAllDocsQueries │ └── example_request.js │ ├── postApiKeys │ └── example_request.js │ ├── postBulkDocs │ ├── example_request_as_a_stream.js │ ├── example_request_create_documents.js │ └── example_request_delete_documents.js │ ├── postBulkGet │ ├── alternative_example_request_for_atts_since.js │ ├── alternative_example_request_for_open_revs_all.js │ └── example_request.js │ ├── postChanges │ ├── example_request.js │ └── example_request_as_a_stream.js │ ├── postDbsInfo │ └── example_request.js │ ├── postDesignDocs │ └── example_request.js │ ├── postDesignDocsQueries │ └── example_request.js │ ├── postDocument │ └── example_request.js │ ├── postExplain │ └── example_request.js │ ├── postFind │ ├── example_request_for_json_index_type.js │ └── example_request_for_text_index_type.js │ ├── postIndex │ ├── example_request_using_json_type_index.js │ └── example_request_using_text_type_index.js │ ├── postPartitionAllDocs │ └── example_request.js │ ├── postPartitionExplain │ └── example_request.js │ ├── postPartitionFind │ └── example_request.js │ ├── postPartitionSearch │ └── example_request.js │ ├── postPartitionView │ └── example_request.js │ ├── postRevsDiff │ └── example_request.js │ ├── postSearch │ └── example_request.js │ ├── postSearchAnalyze │ └── example_request.js │ ├── postView │ └── example_request.js │ ├── postViewQueries │ └── example_request.js │ ├── putAttachment │ └── example_request.js │ ├── putCapacityThroughputConfiguration │ └── example_request.js │ ├── putCloudantSecurity │ └── example_request.js │ ├── putCorsConfiguration │ └── example_request.js │ ├── putDatabase │ └── example_request.js │ ├── putDesignDocument │ └── example_request.js │ ├── putDocument │ └── example_request.js │ ├── putLocalDocument │ └── example_request.js │ ├── putReplicationDocument │ └── example_request.js │ └── putSecurity │ └── example_request.js ├── index.ts ├── lib ├── cloudantBaseService.ts ├── common.ts ├── errorResponseInterceptor.ts └── getAuthenticatorFromEnvCloudantExtension.ts ├── package-lock.json ├── package.json ├── scripts ├── publish_buildinfo.sh ├── setup_couch.sh ├── setup_wiremock.sh └── typedoc │ ├── generate-index-html.sh │ └── publish-doc.sh ├── stubs ├── gen-its-mappings.json └── mappings.json ├── test ├── examples │ ├── output │ │ ├── CreateDbAndDoc.txt │ │ ├── DeleteDoc.txt │ │ ├── GetInfoFromExistingDatabase.txt │ │ ├── UpdateDoc.txt │ │ └── UpdateDoc2.txt │ └── src │ │ ├── features │ │ ├── js │ │ │ ├── initialize.js │ │ │ ├── start.js │ │ │ ├── startAndProcess.js │ │ │ ├── startOneOff.js │ │ │ ├── startOneOffAndProcess.js │ │ │ └── stop.js │ │ └── ts │ │ │ ├── initialize.ts │ │ │ ├── start.ts │ │ │ ├── startAndProcess.ts │ │ │ ├── startOneOff.ts │ │ │ ├── startOneOffAndProcess.ts │ │ │ └── stop.ts │ │ ├── js │ │ ├── CreateDbAndDoc.js │ │ ├── CreateOutputs.js │ │ ├── DeleteDoc.js │ │ ├── GetInfoFromExistingDatabase.js │ │ └── UpdateDoc.js │ │ ├── ts │ │ ├── CreateDbAndDoc.ts │ │ ├── DeleteDoc.ts │ │ ├── GetInfoFromExistingDatabase.ts │ │ └── UpdateDoc.ts │ │ └── tsconfig.json ├── integration │ ├── cloudant.integration.test.js │ ├── cloudant.v1.test.js │ ├── readme.integration.test.js │ └── timeout.integration.test.js ├── resources │ └── auth-helper.js └── unit │ ├── cloudant.v1.test.js │ ├── cloudantBaseService.test.js │ ├── common.test.js │ ├── couchdbSessionAuthenticator.test.js │ ├── errorResponseInterceptor.test.js │ ├── features │ ├── changesFollower.test.js │ ├── changesParamsHelper.test.js │ ├── changesResultItemStream.test.js │ ├── changesResultIterator.test.js │ ├── mockErrors.js │ ├── testDataProviders.js │ ├── testMocks.js │ ├── testParams.js │ └── testUtils.js │ ├── getAuthenticatorFromEnvCloudantExtension.test.js │ └── sessionTokenManager.test.js ├── tsconfig.json └── typedoc.json /.bumpversion.toml: -------------------------------------------------------------------------------- 1 | [tool.bumpversion] 2 | current_version = "0.12.4" 3 | parse = "^(?P0|[1-9]\\d*)\\.(?P0|[1-9]\\d*)\\.(?P0|[1-9]\\d*)(?:-(?P(?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\\.(?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\\+(?P[0-9a-zA-Z-]+(?:\\.[0-9a-zA-Z-]+)*))?$" 4 | serialize = [ 5 | "{major}.{minor}.{patch}-{prerelease}+{buildmetadata}", 6 | "{major}.{minor}.{patch}-{prerelease}", 7 | "{major}.{minor}.{patch}+{buildmetadata}", 8 | "{major}.{minor}.{patch}", 9 | ] 10 | 11 | [[tool.bumpversion.files]] 12 | filename = "README.md" 13 | search = "{current_version}" 14 | replace = "{new_version}" 15 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | # http://eslint.org/docs/user-guide/configuring#ignoring-files-and-directories 2 | apidocs/ 3 | coverage/ 4 | dist/ 5 | examples/ 6 | gh-pages/ 7 | junitreports/ 8 | node_modules/ 9 | scripts/typedoc/ 10 | stubs 11 | 12 | # ignore emitted js 13 | auth/*.js 14 | cloudant/v1.js 15 | lib/*.js 16 | -------------------------------------------------------------------------------- /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | # Common files: 2 | .github/CODEOWNERS @IBM/cloudant-sdks 3 | .github/ISSUE_TEMPLATE/bug_report.md @IBM/cloudant-sdks 4 | .github/ISSUE_TEMPLATE/feature_request.md @IBM/cloudant-sdks 5 | .github/dco.yml @IBM/cloudant-sdks 6 | .github/dependabot.yml @IBM/cloudant-sdks 7 | .github/pull_request_template.md @IBM/cloudant-sdks 8 | .whitesource @IBM/cloudant-sdks 9 | CODE_OF_CONDUCT.md @IBM/cloudant-sdks 10 | CONTRIBUTING.md @IBM/cloudant-sdks 11 | Jenkinsfile @IBM/cloudant-sdks 12 | KNOWN_ISSUES.md @IBM/cloudant-sdks 13 | LICENSE @IBM/cloudant-sdks 14 | README.md @IBM/cloudant-sdks 15 | SECURITY.md @IBM/cloudant-sdks 16 | scripts/publish_buildinfo.sh @IBM/cloudant-sdks 17 | scripts/setup_couch.sh @IBM/cloudant-sdks 18 | scripts/setup_wiremock.sh @IBM/cloudant-sdks 19 | scripts/typedoc/generate-index-html.sh @IBM/cloudant-sdks 20 | scripts/typedoc/publish-doc.sh @IBM/cloudant-sdks 21 | stubs/gen-its-mappings.json @IBM/cloudant-sdks 22 | stubs/mappings.json @IBM/cloudant-sdks 23 | 24 | # Generated code files: 25 | 26 | cloudant/v1.ts @IBM/cloudant-sdks 27 | test/unit/cloudant.v1.test.js @IBM/cloudant-sdks 28 | 29 | # Version files: 30 | 31 | package-lock.json @IBM/cloudant-sdks 32 | package.json @IBM/cloudant-sdks 33 | 34 | # Examples files: 35 | 36 | /examples/ @IBM/cloudant-sdks 37 | 38 | .bumpversion.cfg @IBM/cloudant-sdks 39 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a bug report to help us improve 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | 12 | 13 | **To Reproduce** 14 | 15 | 16 | **Expected behavior** 17 | 18 | 19 | **Screenshots** 20 | 21 | 22 | **Must gather (please complete the following information):** 23 | - SDK Version [e.g. 1.2.1] 24 | - Node.js Version [e.g. Node.js 20] 25 | - Name of service that you're trying to use (if applicable) 26 | - Name of operation that you're trying to invoke (if applicable) 27 | 28 | **Additional context** 29 | 31 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | 12 | 13 | **Describe the solution you'd like** 14 | 15 | 16 | **Describe alternatives you've considered** 17 | 18 | 19 | **Additional context** 20 | 21 | -------------------------------------------------------------------------------- /.github/dco.yml: -------------------------------------------------------------------------------- 1 | # This enables DCO bot - https://github.com/probot/dco for more details. 2 | # Exclude org members from DCO sign-off on commits 3 | require: 4 | members: false 5 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: "github-actions" 4 | directory: "/" 5 | schedule: 6 | interval: "weekly" 7 | pull-request-branch-name: 8 | separator: "-" 9 | - package-ecosystem: "npm" 10 | directory: "/" 11 | open-pull-requests-limit: 10 12 | schedule: 13 | interval: "daily" 14 | pull-request-branch-name: 15 | separator: "-" 16 | # Custom settings: 17 | groups: 18 | eslint: 19 | dependency-type: "development" 20 | patterns: 21 | - "eslint*" 22 | - "@typescript-eslint/*" 23 | ignore: 24 | - dependency-name: "@types/node" 25 | update-types: ["version-update:semver-major"] 26 | - dependency-name: "eslint*" 27 | update-types: ["version-update:semver-patch"] 28 | - dependency-name: "@typescript-eslint/*" 29 | update-types: ["version-update:semver-patch"] 30 | 31 | -------------------------------------------------------------------------------- /.github/pull_request_template.md: -------------------------------------------------------------------------------- 1 | ## PR summary 2 | 3 | 4 | 5 | Fixes: 6 | 7 | **Note: An existing issue is [required](https://github.com/IBM/cloudant-node-sdk/blob/main/CONTRIBUTING.md#PRs) before opening a PR.** 8 | 9 | ## PR Checklist 10 | 11 | Please make sure that your PR fulfills the following requirements: 12 | 13 | - [ ] The commit message follows the 14 | [Angular Commit Message Guidelines](https://github.com/angular/angular/blob/master/CONTRIBUTING.md#-commit-message-guidelines). 15 | - [ ] Tests for the changes have been added (for bug fixes / features) 16 | - [ ] Docs have been added / updated (for bug fixes / features) 17 | 18 | ## PR Type 19 | 20 | - [ ] Bugfix 21 | - [ ] Feature 22 | - [ ] Code style update (formatting, local variables) 23 | - [ ] Refactoring (no functional changes, no api changes) 24 | - [ ] New tests 25 | - [ ] Build/CI related changes 26 | - [ ] Documentation content changes 27 | - [ ] Other (please describe) 28 | 29 | ## What is the current behavior? 30 | 31 | 32 | ## What is the new behavior? 33 | 34 | 35 | ## Does this PR introduce a breaking change? 36 | 37 | - [ ] Yes 38 | - [ ] No 39 | 40 | 42 | 43 | ## Other information 44 | 45 | 47 | -------------------------------------------------------------------------------- /.github/workflows/codeql.yml: -------------------------------------------------------------------------------- 1 | name: "CodeQL" 2 | on: 3 | push: 4 | branches: ['main'] 5 | pull_request: 6 | branches: ['main'] 7 | schedule: 8 | - cron: '58 3 * * 2' 9 | permissions: {} 10 | jobs: 11 | analyze: 12 | name: Analyze 13 | runs-on: ubuntu-latest 14 | permissions: 15 | security-events: write 16 | strategy: 17 | fail-fast: false 18 | matrix: 19 | language: ['javascript-typescript'] 20 | steps: 21 | - name: Checkout repository 22 | uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 23 | - name: Initialize CodeQL 24 | uses: github/codeql-action/init@08bc0cf022445eacafaa248bf48da20f26b8fd40 # v3.28.6 25 | with: 26 | languages: ${{ matrix.language }} 27 | config: | 28 | paths-ignore: 29 | - 'node_modules' 30 | - 'examples/snippets/**/*.js' 31 | - name: Autobuild 32 | uses: github/codeql-action/autobuild@08bc0cf022445eacafaa248bf48da20f26b8fd40 # v3.28.6 33 | - name: Perform CodeQL Analysis 34 | uses: github/codeql-action/analyze@08bc0cf022445eacafaa248bf48da20f26b8fd40 # v3.28.6 35 | with: 36 | category: "/language:${{matrix.language}}" 37 | -------------------------------------------------------------------------------- /.github/workflows/docs.yml: -------------------------------------------------------------------------------- 1 | name: docs 2 | on: 3 | pull_request: 4 | branches: ['main'] 5 | permissions: {} 6 | jobs: 7 | docs: 8 | runs-on: ubuntu-latest 9 | steps: 10 | - name: Checkout code 11 | uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 12 | with: 13 | persist-credentials: false 14 | - name: Setup node 18 15 | uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0 16 | with: 17 | node-version: '18' 18 | check-latest: false 19 | cache: 'npm' 20 | - name: Install deps 21 | run: npm ci --no-audit 22 | - name: Build docs 23 | run: npm run typedoc 24 | -------------------------------------------------------------------------------- /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | name: test 2 | on: 3 | push: 4 | branches: ['main'] 5 | pull_request: 6 | branches-ignore: ['gh-pages'] 7 | permissions: {} 8 | env: 9 | SERVER_AUTH_TYPE: basic 10 | SERVER_URL: http://127.0.0.1:5984 11 | SERVER_USERNAME: admin 12 | SERVER_PASSWORD: password 13 | WIREMOCK_URL: http://127.0.0.1:8080 14 | WIREMOCK_PORT: 8080 15 | jobs: 16 | test: 17 | strategy: 18 | matrix: 19 | node: ['20', '22', '24'] 20 | runs-on: ubuntu-latest 21 | services: 22 | couchdb: 23 | image: apache/couchdb:3 24 | env: 25 | COUCHDB_USER: ${{ env.SERVER_USERNAME }} 26 | COUCHDB_PASSWORD: ${{ env.SERVER_PASSWORD }} 27 | options: --name couchdb 28 | ports: 29 | - 5984:5984 30 | wiremock: 31 | image: wiremock/wiremock 32 | options: --name wiremock 33 | ports: 34 | - 8080:8080 35 | steps: 36 | - name: Checkout code 37 | uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 38 | with: 39 | persist-credentials: false 40 | - name: Setup node ${{ matrix.node }} 41 | uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0 42 | with: 43 | node-version: ${{ matrix.node }} 44 | check-latest: true 45 | cache: 'npm' 46 | - name: Setup CouchDB 47 | shell: bash 48 | run: ${GITHUB_WORKSPACE}/scripts/setup_couch.sh 49 | - name: Setup Wiremock 50 | shell: bash 51 | run: ${GITHUB_WORKSPACE}/scripts/setup_wiremock.sh 52 | - name: Install deps 53 | run: npm ci --no-audit 54 | - name: Build package 55 | run: npm run build 56 | - name: Run tests 57 | run: npm run test-ci 58 | - name: Run lint 59 | run: npm run lint 60 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ### Specific to this repository ### 2 | # tsc outputs 3 | lib/*.js 4 | auth/*.js 5 | **/*.d.ts 6 | **/*.js.map 7 | 8 | # service-specific tsc outputs (js files) 9 | # IGNORE YOUR SERVICE FILES HERE 10 | 11 | # file holding service credentials 12 | test/resources/auth.js 13 | 14 | # release folder for typescript-generated source files 15 | dist/ 16 | apidocs/ 17 | 18 | ### GitHub recommended ### 19 | # Logs 20 | logs 21 | *.log 22 | npm-debug.log* 23 | yarn-debug.log* 24 | yarn-error.log* 25 | 26 | # Runtime data 27 | pids 28 | *.pid 29 | *.seed 30 | *.pid.lock 31 | 32 | # Directory for instrumented libs generated by jscoverage/JSCover 33 | lib-cov 34 | 35 | # Coverage directory used by tools like istanbul 36 | coverage 37 | 38 | # JUnit reports directory 39 | junitreports 40 | 41 | # nyc test coverage 42 | .nyc_output 43 | 44 | # Compiled binary addons (https://nodejs.org/api/addons.html) 45 | build/Release 46 | 47 | # Dependency directories 48 | node_modules/ 49 | 50 | # TypeScript v1 declaration files 51 | typings/ 52 | 53 | # Optional npm cache directory 54 | .npm 55 | 56 | # Optional eslint cache 57 | .eslintcache 58 | 59 | # Optional REPL history 60 | .node_repl_history 61 | 62 | # Output of 'npm pack' 63 | *.tgz 64 | 65 | # Yarn Integrity file 66 | .yarn-integrity 67 | 68 | # dotenv environment variables file 69 | *.env 70 | 71 | # next.js build output 72 | .next 73 | 74 | # MacOS 75 | .DS_Store 76 | 77 | # IDE files 78 | .idea 79 | .vscode/ 80 | 81 | .project 82 | .classpath 83 | 84 | # SDK generator 85 | .openapi-generator* 86 | -------------------------------------------------------------------------------- /.npmrc-jenkins: -------------------------------------------------------------------------------- 1 | ; NB registry scopes are assumed to have scheme stripped and have a trailing slash 2 | ${NPMRC_REGISTRY}:username=${NPMRC_USER} 3 | ${NPMRC_REGISTRY}:email=${NPMRC_EMAIL} 4 | ${NPMRC_REGISTRY}:_authToken=${NPMRC_TOKEN} 5 | -------------------------------------------------------------------------------- /.prettierrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | semi: true, 3 | trailingComma: "es5", 4 | singleQuote: true, 5 | quoteProps: 'preserve' 6 | }; 7 | -------------------------------------------------------------------------------- /.releaserc: -------------------------------------------------------------------------------- 1 | { 2 | "branch": "master", 3 | "verifyConditions": ["@semantic-release/changelog", "@semantic-release/npm", "@semantic-release/git"], 4 | "prepare": ["@semantic-release/changelog", "@semantic-release/npm", "@semantic-release/git"], 5 | "publish": [ 6 | ["@semantic-release/npm", { 7 | "pkgRoot": "dist" 8 | }], 9 | { 10 | "path": "@semantic-release/github" 11 | } 12 | ] 13 | } 14 | -------------------------------------------------------------------------------- /.secrets.baseline: -------------------------------------------------------------------------------- 1 | { 2 | "exclude": { 3 | "files": "stubs/.+\\.json|package-lock.json|^.secrets.baseline$", 4 | "lines": null 5 | }, 6 | "generated_at": "2024-11-04T12:08:10Z", 7 | "plugins_used": [ 8 | { 9 | "name": "AWSKeyDetector" 10 | }, 11 | { 12 | "name": "ArtifactoryDetector" 13 | }, 14 | { 15 | "name": "AzureStorageKeyDetector" 16 | }, 17 | { 18 | "base64_limit": 4.5, 19 | "name": "Base64HighEntropyString" 20 | }, 21 | { 22 | "name": "BasicAuthDetector" 23 | }, 24 | { 25 | "name": "BoxDetector" 26 | }, 27 | { 28 | "name": "CloudantDetector" 29 | }, 30 | { 31 | "ghe_instance": "github.ibm.com", 32 | "name": "GheDetector" 33 | }, 34 | { 35 | "name": "GitHubTokenDetector" 36 | }, 37 | { 38 | "hex_limit": 3, 39 | "name": "HexHighEntropyString" 40 | }, 41 | { 42 | "name": "IbmCloudIamDetector" 43 | }, 44 | { 45 | "name": "IbmCosHmacDetector" 46 | }, 47 | { 48 | "name": "JwtTokenDetector" 49 | }, 50 | { 51 | "keyword_exclude": null, 52 | "name": "KeywordDetector" 53 | }, 54 | { 55 | "name": "MailchimpDetector" 56 | }, 57 | { 58 | "name": "NpmDetector" 59 | }, 60 | { 61 | "name": "PrivateKeyDetector" 62 | }, 63 | { 64 | "name": "SlackDetector" 65 | }, 66 | { 67 | "name": "SoftlayerDetector" 68 | }, 69 | { 70 | "name": "SquareOAuthDetector" 71 | }, 72 | { 73 | "name": "StripeDetector" 74 | }, 75 | { 76 | "name": "TwilioKeyDetector" 77 | } 78 | ], 79 | "results": { 80 | "Jenkinsfile": [ 81 | { 82 | "hashed_secret": "dd7c0356e7bfc571546237209d7fa15319e25cfa", 83 | "is_secret": false, 84 | "is_verified": false, 85 | "line_number": 32, 86 | "type": "Secret Keyword", 87 | "verified_result": null 88 | }, 89 | { 90 | "hashed_secret": "eebfd4af77aec86f7ad8e41add1d782da79c6e60", 91 | "is_secret": false, 92 | "is_verified": false, 93 | "line_number": 306, 94 | "type": "NPM tokens", 95 | "verified_result": null 96 | } 97 | ], 98 | "README.md": [ 99 | { 100 | "hashed_secret": "32e8612d8ca77c7ea8374aa7918db8e5df9252ed", 101 | "is_secret": false, 102 | "is_verified": false, 103 | "line_number": 158, 104 | "type": "Secret Keyword", 105 | "verified_result": null 106 | } 107 | ], 108 | "auth/sessionTokenManager.ts": [ 109 | { 110 | "hashed_secret": "5b09e6a925c902cc2fe4fbfa12fb10db95cb0542", 111 | "is_secret": false, 112 | "is_verified": false, 113 | "line_number": 106, 114 | "type": "Secret Keyword", 115 | "verified_result": null 116 | } 117 | ], 118 | "cloudant/v1.ts": [ 119 | { 120 | "hashed_secret": "af142925e0da7006101954aae9d4f5753388bc3f", 121 | "is_secret": false, 122 | "is_verified": false, 123 | "line_number": 6523, 124 | "type": "Secret Keyword", 125 | "verified_result": null 126 | }, 127 | { 128 | "hashed_secret": "9010fd9ef3ce50b1d897190013c7fa86ea3f8f6f", 129 | "is_secret": false, 130 | "is_verified": false, 131 | "line_number": 10955, 132 | "type": "Secret Keyword", 133 | "verified_result": null 134 | }, 135 | { 136 | "hashed_secret": "d793ee0a2ff3069299a51062681dbcf9bcbb17e1", 137 | "is_secret": false, 138 | "is_verified": false, 139 | "line_number": 10957, 140 | "type": "Secret Keyword", 141 | "verified_result": null 142 | } 143 | ], 144 | "examples/putReplicationDocument/example_request.js": [ 145 | { 146 | "hashed_secret": "701e4e52db78a3657f5bc71fd174cf29e7868fbd", 147 | "is_secret": false, 148 | "is_verified": false, 149 | "line_number": 13, 150 | "type": "Secret Keyword", 151 | "verified_result": null 152 | } 153 | ], 154 | "lib/cloudantBaseService.ts": [ 155 | { 156 | "hashed_secret": "fc5177639d71196391dd9f4997bc4f240eb29366", 157 | "is_secret": false, 158 | "is_verified": false, 159 | "line_number": 185, 160 | "type": "Secret Keyword", 161 | "verified_result": null 162 | } 163 | ], 164 | "lib/getAuthenticatorFromEnvCloudantExtension.ts": [ 165 | { 166 | "hashed_secret": "c2a6b03f190dfb2b4aa91f8af8d477a9bc3401dc", 167 | "is_secret": false, 168 | "is_verified": false, 169 | "line_number": 45, 170 | "type": "Secret Keyword", 171 | "verified_result": null 172 | } 173 | ], 174 | "scripts/typedoc/generate-index-html.sh": [ 175 | { 176 | "hashed_secret": "da6c2d0c143f88a731d6490d7fe9b55afb799ab6", 177 | "is_secret": false, 178 | "is_verified": false, 179 | "line_number": 12, 180 | "type": "Base64 High Entropy String", 181 | "verified_result": null 182 | } 183 | ], 184 | "test/integration/timeout.integration.test.js": [ 185 | { 186 | "hashed_secret": "37fa265330ad83eaa879efb1e2db6380896cf639", 187 | "is_secret": false, 188 | "is_verified": false, 189 | "line_number": 88, 190 | "type": "Secret Keyword", 191 | "verified_result": null 192 | }, 193 | { 194 | "hashed_secret": "d4c3d66fd0c38547a3c7a4c6bdc29c36911bc030", 195 | "is_secret": false, 196 | "is_verified": false, 197 | "line_number": 142, 198 | "type": "Secret Keyword", 199 | "verified_result": null 200 | } 201 | ], 202 | "test/unit/cloudant.v1.test.js": [ 203 | { 204 | "hashed_secret": "b8473b86d4c2072ca9b08bd28e373e8253e865c4", 205 | "is_secret": false, 206 | "is_verified": false, 207 | "line_number": 7821, 208 | "type": "Secret Keyword", 209 | "verified_result": null 210 | } 211 | ] 212 | }, 213 | "version": "0.13.1+ibm.62.dss", 214 | "word_list": { 215 | "file": null, 216 | "hash": null 217 | } 218 | } 219 | -------------------------------------------------------------------------------- /.whitesource: -------------------------------------------------------------------------------- 1 | { 2 | "settingsInheritedFrom": "whitesource-config/whitesource-config@master", 3 | "issueSettings": { 4 | "issueRepoName": "sdks" 5 | }, 6 | "scanSettings": { 7 | "javaVersion": "17", 8 | "python.requirementsFileIncludes": "pyproject.toml,requirements-dev.txt,requirements-docs.txt" 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /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 [IBM Cloud Support](https://www.ibm.com/cloud/support). 59 | All 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 | If you are having problems using the APIs or have a question about IBM Cloud services, 3 | please ask a question at 4 | [Stack Overflow](http://stackoverflow.com/questions/ask?tags=ibm-cloud). 5 | 6 | # Issues 7 | If you encounter an issue with the project, you are welcome to submit a 8 | [bug report](https://github.com/IBM/cloudant-node-sdk/issues). 9 | Before that, please search for similar issues. It's possible that someone has already reported the problem. 10 | 11 | # PRs 12 | We ask that an **issue is always opened prior to a PR** to give us the 13 | opportunity to discuss the best place for the change before investing 14 | your effort on a patch that we may not be able to accept. 15 | 16 | The code in the repository is partly generated from the Cloudant OpenAPI 17 | specification. This means PRs to code, tests and sometimes even markdown 18 | are often inappropriate and we may need to make changes to the specification 19 | instead. Please, first [look at the list of files](.github/CODEOWNERS) that 20 | _should not_ be modified. 21 | 22 | # General Information 23 | For general guidance on contributing to this project, please see the 24 | [general guidance for contributing](https://github.com/IBM/ibm-cloud-sdk-common/blob/main/CONTRIBUTING_nodejs.md). 25 | -------------------------------------------------------------------------------- /KNOWN_ISSUES.md: -------------------------------------------------------------------------------- 1 | # Limitations, Restrictions, and Known Issues 2 | 3 | ## All Cloudant SDKs 4 | 5 | ### Path elements containing the `+` character 6 | 7 | Path elements containing the `+` character in the SDKs are not interoperable with: 8 | * Cloudant 9 | * Apache CouchDB versions older than 3.2.0 10 | * Apache CouchDB versions 3.2.0 or newer with the setting `decode_plus_to_space = true` 11 | 12 | This is because standard URL encoding libraries following the [RFC3986 URI specification](https://tools.ietf.org/html/rfc3986#section-3.3) do not encode the `+` character in path elements. 13 | * It is possible to workaround for document names with a `+` in the ID (e.g. `docidwith+char`) by using: 14 | * For reading: use the `post` all docs operation and the `key` or `keys` parameter with a value of the document ID including the `+`. 15 | * For writing: use the `post` document operation or `post` bulk docs operation with the value of the document ID including the `+`. 16 | * There is no pre-encoding workaround because the result is a double encoding e.g. using `%2b` in the path element ends up being double encoded as `%252b`. 17 | 18 | ### Views 19 | 20 | #### Objects as keys 21 | 22 | Using JSON objects as keys (e.g. `start_key`, `end_key`, `key`, `keys`) 23 | can cause inconsistent results because the ordering of the members of the JSON 24 | object after serialization is not guaranteed. 25 | 26 | ### Documents 27 | 28 | #### Attachments 29 | 30 | The `atts_since` parameter is not supported when retrieving a document. 31 | The workaround is to call `POST /{db}/_bulk_get` using the `atts_since` field under the `docs` request body. See the [alternative example request for `atts_since` using the `/_bulk_get` endpoint](https://cloud.ibm.com/apidocs/cloudant#postbulkget) in our API Docs. 32 | Example JSON request body: 33 | ```json 34 | { 35 | "docs": [{"id": "order00058", "atts_since": "1-99b02e08da151943c2dcb40090160bb8"}] 36 | } 37 | ``` 38 | 39 | #### Open revisions 40 | 41 | The `open_revs` parameter is not supported when retrieving a document. 42 | If you want to retrieve documents with all leaf revisions (`open_revs=all`), the workaround is to call `POST /{db}/_bulk_get` using the `id` field within the `docs` array request body. 43 | See the [alternative example request for `open_revs=all` using the `/_bulk_get` endpoint](https://cloud.ibm.com/apidocs/cloudant#postbulkget) in our API Docs. 44 | Example JSON request body: 45 | ```json 46 | { 47 | "docs": [{"id": "order00067"}] 48 | } 49 | ``` 50 | 51 | If you want to retrieve documents of specified leaf revisions (e.g. `open_revs=["3-917fa2381192822767f010b95b45325b", "4-a5be949eeb7296747cc271766e9a498b"]`), the workaround is to call `POST /{db}/_bulk_get` using the same `id` value for each unique `rev` value within of the `docs` array request body. 52 | See the [default example request using the `/_bulk_get` endpoint](https://cloud.ibm.com/apidocs/cloudant#postbulkget) in our API Docs. 53 | Example JSON request body: 54 | ```json 55 | { 56 | "docs": [ 57 | { 58 | "id": "order00067", 59 | "rev": "3-917fa2381192822767f010b95b45325b" 60 | }, 61 | { 62 | "id": "order00067", 63 | "rev": "4-a5be949eeb7296747cc271766e9a498b" 64 | } 65 | ] 66 | } 67 | ``` 68 | 69 | ### Compression 70 | 71 | * Manually setting an `Accept-Encoding` header on requests will disable the transparent gzip decompression of response bodies from the server. 72 | * Manually setting a `Content-Encoding` header on requests will disable the transparent gzip compression of request bodies to the server. 73 | 74 | ### Changes feed 75 | 76 | #### Filter functions 77 | 78 | The SDK does not support passing user-defined query or body parameters in `_changes` requests for dynamic filter functions in design documents. 79 | The workaround and recommended option is to use a `selector` type filter. 80 | For example, if you are using a `_changes` request like `/{db}/_changes?filter=myDdoc/byName&name=Jane` with a filter function like: 81 | ```javascript 82 | function(doc, req) { 83 | if (doc.name !== req.query.name) { 84 | return false; 85 | } 86 | return true; 87 | } 88 | ``` 89 | It can be replaced with a request using a selector filter: 90 | ```js 91 | const service = CloudantV1.newInstance({}); 92 | 93 | service.postChanges({ 94 | db: 'example', 95 | filter: '_selector', 96 | selector: {"name": "Jane"} 97 | }).then(response => { ... }); 98 | ``` 99 | 100 | 101 | ## Cloudant SDK for Node.js 102 | 103 | ### Disabling request body compression 104 | 105 | Some issues with older server versions can be worked around by disabling 106 | compression of request bodies. This is an example of how to do that. 107 | 108 | ```js 109 | import { CloudantV1 } from '@ibm-cloud/cloudant'; 110 | const client = CloudantV1.newInstance({ serviceName: 'YOUR_SERVICE_NAME' }); 111 | client.setEnableGzipCompression(false); 112 | ... 113 | ``` 114 | -------------------------------------------------------------------------------- /SECURITY.md: -------------------------------------------------------------------------------- 1 | # Security Policy 2 | 3 | ## Supported Versions 4 | 5 | | Version | Supported | 6 | | ------- | ------------------ | 7 | | latest | :white_check_mark: | 8 | 9 | ## Reporting a Vulnerability 10 | 11 | If you believe you have found a vulnerability please report it responsibly. IBM has documented the process for reporting vulnerabilities at https://www.ibm.com/trust/security-psirt. 12 | 13 | ## Vulnerabilities in dependencies 14 | 15 | The SDK is regularly scanned for known CVEs in dependencies and updates to versions with remediations are applied and released as soon as possible. 16 | Reporting known CVEs in dependencies in this repository is not necessary and will not lead to a faster resolution. 17 | -------------------------------------------------------------------------------- /auth/index.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * © Copyright IBM Corporation 2020. 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 { 18 | NoAuthAuthenticator, 19 | BasicAuthenticator, 20 | IamAuthenticator, 21 | IamTokenManager, 22 | getAuthenticatorFromEnvironment, 23 | } from 'ibm-cloud-sdk-core'; 24 | 25 | export { 26 | CouchdbSessionAuthenticator, 27 | CouchdbSessionAuthenticatorOptions, 28 | } from './couchdbSessionAuthenticator'; 29 | export { SessionTokenManager } from './sessionTokenManager'; 30 | -------------------------------------------------------------------------------- /auth/sessionTokenManager.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * © Copyright IBM Corporation 2020, 2021. 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 { OutgoingHttpHeaders } from 'node:http'; 18 | import { 19 | getCurrentTime, 20 | TokenManager, 21 | UserOptions, 22 | validateInput, 23 | } from 'ibm-cloud-sdk-core'; 24 | 25 | import { getSdkHeaders } from '../lib/common'; 26 | 27 | /** Configuration options for CouchDB session token retrieval. */ 28 | export interface SessionTokenManagerOptions extends UserOptions { 29 | /** The username portion of CouchDB session authentication. */ 30 | username: string; 31 | /** The password portion of CouchDB session authentication. */ 32 | password: string; 33 | } 34 | 35 | /** 36 | * Token Manager of CouchDB session token. 37 | * 38 | * The Token Manager performs basic auth with username and password 39 | * to acquire session tokens. 40 | */ 41 | export class SessionTokenManager extends TokenManager { 42 | protected requiredOptions: string[] = [ 43 | 'username', 44 | 'password', 45 | 'serviceUrl', 46 | 'jar', 47 | ]; 48 | 49 | private tokenName: string; 50 | 51 | private options: SessionTokenManagerOptions; 52 | 53 | /** 54 | * Create a new [[SessionTokenManager]] instance. For internal use by 55 | * CouchdbSessionAuthenticator only. 56 | * 57 | * @param {object} options Configuration options. 58 | * @param {string} options.username The username portion of CouchDB Session authentication. 59 | * @param {string} options.password The password portion of CouchDB Session authentication. 60 | * @param {string} options.serviceUrl The endpoint for session token requests. 61 | * @param {any} options.jar The Cookie jar for session token storage. 62 | * @param {boolean} [options.disableSslVerification] A flag that indicates 63 | * whether verification of the token server's SSL certificate should be 64 | * disabled or not. 65 | * @param {object} [options.headers] Headers to be sent with every 66 | * outbound HTTP requests to token services. 67 | * @constructor 68 | */ 69 | constructor(options: SessionTokenManagerOptions) { 70 | super(options); 71 | 72 | validateInput(options, this.requiredOptions); 73 | 74 | this.options = options; 75 | this.tokenName = 'AuthSession'; 76 | } 77 | 78 | /** 79 | * Only base service specific headers are in use. 80 | * 81 | * @param {OutgoingHttpHeaders} headers - the new set of headers as an object 82 | * @returns {Error} 83 | */ 84 | // eslint-disable-next-line class-methods-use-this 85 | public setHeaders(headers: OutgoingHttpHeaders): void { 86 | const errMsg = 87 | 'During CouchDB Session Authentication only `request` service headers are in use'; 88 | throw new Error(errMsg); 89 | } 90 | 91 | /** 92 | * Request a session token using basic credentials. 93 | * 94 | * @returns {Promise} 95 | */ 96 | protected requestToken(): Promise { 97 | const newHeaders = getSdkHeaders( 98 | 'cloudant', 99 | 'v1', 100 | 'authenticatorPostSession' 101 | ); 102 | if (!this.options.headers) { 103 | Object.assign(this.options, { 'headers': newHeaders }); 104 | } else { 105 | Object.assign(this.options.headers, newHeaders); 106 | } 107 | // these cannot be overwritten 108 | const parameters = { 109 | options: { 110 | headers: this.options.headers, 111 | url: `${this.options.serviceUrl}/_session`, 112 | method: 'POST', 113 | body: { 114 | username: this.options.username, 115 | password: this.options.password, 116 | }, 117 | }, 118 | }; 119 | return this.requestWrapperInstance.sendRequest(parameters); 120 | } 121 | 122 | /** 123 | * From the response parse and save session token into field `accessToken`. 124 | * Calculate expiration and refresh time from the received response 125 | * and store them in fields `expireTime` and `refreshTime`. 126 | * 127 | * @param tokenResponse - Response object from session token request 128 | * @private 129 | * @returns {void} 130 | */ 131 | protected saveTokenInfo(tokenResponse): void { 132 | const sessionCookie = tokenResponse.headers['set-cookie']; 133 | if (!Array.isArray(sessionCookie)) { 134 | const err = 'Set-Cookie header not present in response'; 135 | throw new Error(err); 136 | } 137 | let sessionToken = null; 138 | let expireTime = null; 139 | let refreshTime = null; 140 | for (let i = 0; i < sessionCookie.length && sessionToken == null; i += 1) { 141 | sessionToken = new RegExp('AuthSession=([^;]*);').exec(sessionCookie[i]); 142 | if (sessionToken != null) { 143 | expireTime = new RegExp('.*Expires=([^;]*);').exec(sessionCookie[i]); 144 | refreshTime = new RegExp('.*Max-Age=([^;]*);').exec(sessionCookie[i]); 145 | } 146 | } 147 | if (sessionToken == null) { 148 | const err = 'Session token not present in response'; 149 | throw new Error(err); 150 | } 151 | [, this.accessToken] = sessionToken; 152 | const fractionOfTtl = 0.8; 153 | if (expireTime == null) { 154 | if (refreshTime == null) { 155 | this.expireTime = 0; 156 | this.refreshTime = 0; 157 | } else { 158 | this.expireTime = Number(refreshTime[1]) + getCurrentTime(); 159 | this.refreshTime = 160 | Number(refreshTime[1]) * fractionOfTtl + getCurrentTime(); 161 | } 162 | } else { 163 | // Store expire time in seconds 164 | this.expireTime = Date.parse(expireTime[1]) / 1000; 165 | // Set refresh time from the expire time 166 | const timeToLive = this.expireTime - getCurrentTime(); 167 | this.refreshTime = this.expireTime - timeToLive * (1.0 - fractionOfTtl); 168 | } 169 | } 170 | } 171 | -------------------------------------------------------------------------------- /cloudant/features/changesParamsHelper.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * © Copyright IBM Corporation 2022, 2025. 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 { PostChangesConstants, PostChangesParams } from '../v1'; 18 | import { Mode } from './changesFollower'; 19 | 20 | export class ChangesParamsHelper { 21 | /** 22 | * Set minimum client timeout to 1 minute (= 60 000 ms). 23 | * 1 minute is the sort of number that very likely to be used as a client timeout, 24 | * so it makes sense to set as our minimum client timeout. 25 | */ 26 | static MIN_CLIENT_TIMEOUT: number = 60_000; 27 | 28 | /** 29 | * Set longpoll timeout to {@link MIN_CLIENT_TIMEOUT} - 3 second (= 3000 ms). 30 | * To give the changes request a chance to be answered before the client timeout 31 | * it is set to 3 seconds less. 32 | */ 33 | static LONGPOLL_TIMEOUT = this.MIN_CLIENT_TIMEOUT - 3000; 34 | 35 | static cloneParams( 36 | params: PostChangesParams, 37 | mode?: Mode, 38 | since?: string, 39 | limit?: number 40 | ): PostChangesParams { 41 | let clonedParams: PostChangesParams = { 42 | db: params.db, 43 | attEncodingInfo: params.attEncodingInfo, 44 | attachments: params.attachments, 45 | conflicts: params.conflicts, 46 | // no descending 47 | docIds: params.docIds, 48 | fields: params.fields, 49 | filter: params.filter, 50 | // no heartbeat 51 | includeDocs: params.includeDocs, 52 | // no lastEventId 53 | limit: limit ? limit : params.limit, 54 | selector: params.selector, 55 | seqInterval: params.seqInterval, 56 | since: since ? since : params.since, 57 | style: params.style, 58 | view: params.view, 59 | }; 60 | if (mode === Mode.FINITE) { 61 | clonedParams.feed = PostChangesConstants.Feed.NORMAL; 62 | } else if (mode === Mode.LISTEN) { 63 | clonedParams.feed = PostChangesConstants.Feed.LONGPOLL; 64 | clonedParams.timeout = this.LONGPOLL_TIMEOUT; 65 | } 66 | return clonedParams; 67 | } 68 | 69 | static validateParams(params: PostChangesParams) { 70 | if (!params) { 71 | throw new Error('PostChangesParams configuration is required.'); 72 | } 73 | if (!('db' in params) || !params.db) { 74 | throw new Error('The param db is required for PostChangesParams.'); 75 | } 76 | let invalidParams = []; 77 | let paramsShouldBeUndefined = [ 78 | 'descending', 79 | 'feed', 80 | 'heartbeat', 81 | 'lastEventId', 82 | 'timeout', 83 | ]; 84 | for (let paramName of paramsShouldBeUndefined) { 85 | if (paramName in params) { 86 | invalidParams.push(`'${paramName}'`); 87 | } 88 | } 89 | if ('filter' in params && params.filter !== '_selector') { 90 | invalidParams.push(`'filter=${params.filter}'`); 91 | } 92 | 93 | let errorMsg = ''; 94 | if (invalidParams.length > 0) { 95 | const invalidParamsAsText = invalidParams.join(', '); 96 | errorMsg = `The params ${invalidParamsAsText} are invalid when using ChangesFollower.`; 97 | if (invalidParams.length === 1) { 98 | errorMsg = `The param ${invalidParamsAsText} is invalid when using ChangesFollower.`; 99 | } 100 | } 101 | 102 | if (errorMsg.length > 0) { 103 | throw new Error(errorMsg); 104 | } 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /cloudant/features/changesResultItemStream.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * © Copyright IBM Corporation 2022. 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 { Stream } from './stream'; 18 | import { TransformCallback } from 'node:stream'; 19 | import { validateParams } from 'ibm-cloud-sdk-core'; 20 | import CloudantV1 from '../v1'; 21 | 22 | export class ChangesResultItemStream extends Stream { 23 | _transform( 24 | chunk: CloudantV1.ChangesResult, 25 | encoding: BufferEncoding, 26 | callback: TransformCallback 27 | ) { 28 | const validationError = this.getValidationError(chunk); 29 | if (validationError !== null) { 30 | this.destroy(validationError); 31 | } else { 32 | for (let result of chunk.results) { 33 | this.push(result); 34 | } 35 | } 36 | callback(); 37 | } 38 | 39 | private getValidationError(params) { 40 | const requiredParams = ['lastSeq', 'pending', 'results']; 41 | return validateParams(params, requiredParams, requiredParams); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /cloudant/features/stream.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * © Copyright IBM Corporation 2022. 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 { Transform, TransformOptions } from 'node:stream'; 18 | 19 | export class Stream extends Transform { 20 | constructor(opts?: TransformOptions) { 21 | super({ 22 | ...opts, 23 | objectMode: true, // sets readableObjectMode and writableObjectMode to true 24 | highWaterMark: 1, 25 | }); 26 | } 27 | 28 | push(chunk: T, encoding?: BufferEncoding): boolean { 29 | return super.push(chunk); 30 | } 31 | 32 | read(size?: number): T { 33 | return super.read(size); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /examples/snippets/deleteAttachment/example_request.js: -------------------------------------------------------------------------------- 1 | // section: code 2 | import { CloudantV1 } from '@ibm-cloud/cloudant'; 3 | 4 | const service = CloudantV1.newInstance({}); 5 | 6 | service.deleteAttachment({ 7 | db: 'products', 8 | docId: '1000042', 9 | attachmentName: 'product_details.txt', 10 | rev: '4-1a0d1cd6f40472509e9aac646183736a' 11 | }).then(response => { 12 | console.log(response.result); 13 | }); 14 | // section: markdown 15 | // This example requires the `product_details.txt` attachment in `1000042` document to exist. To create the attachment, see [Create or modify an attachment.](#putattachment) 16 | -------------------------------------------------------------------------------- /examples/snippets/deleteDatabase/example_request.js: -------------------------------------------------------------------------------- 1 | // section: code 2 | import { CloudantV1 } from '@ibm-cloud/cloudant'; 3 | 4 | const service = CloudantV1.newInstance({}); 5 | 6 | service.deleteDatabase({db: ''}).then(response => { 7 | console.log(response.result); 8 | }); 9 | -------------------------------------------------------------------------------- /examples/snippets/deleteDesignDocument/example_request.js: -------------------------------------------------------------------------------- 1 | // section: code 2 | import { CloudantV1 } from '@ibm-cloud/cloudant'; 3 | 4 | const service = CloudantV1.newInstance({}); 5 | 6 | service.deleteDesignDocument({ 7 | db: 'products', 8 | ddoc: 'appliances', 9 | rev: '1-98e6a25b3b45df62e7d47095ac15b16a' 10 | }).then(response => { 11 | console.log(response.result); 12 | }); 13 | -------------------------------------------------------------------------------- /examples/snippets/deleteDocument/example_request.js: -------------------------------------------------------------------------------- 1 | // section: code 2 | import { CloudantV1 } from '@ibm-cloud/cloudant'; 3 | const service = CloudantV1.newInstance({}); 4 | 5 | service.deleteDocument({ 6 | db: 'orders', 7 | docId: 'order00058', 8 | rev: '1-99b02e08da151943c2dcb40090160bb8' 9 | }).then(response => { 10 | console.log(response.result); 11 | }); 12 | -------------------------------------------------------------------------------- /examples/snippets/deleteIndex/example_request.js: -------------------------------------------------------------------------------- 1 | // section: code 2 | import { CloudantV1 } from '@ibm-cloud/cloudant'; 3 | 4 | const service = CloudantV1.newInstance({}); 5 | 6 | service.deleteIndex({ 7 | db: 'users', 8 | ddoc: 'json-index', 9 | index: 'getUserByName', 10 | type: 'json' 11 | }).then(response => { 12 | console.log(response.result); 13 | }); 14 | // section: markdown 15 | // This example will fail if `getUserByName` index doesn't exist. To create the index, see [Create a new index on a database.](#postindex) 16 | -------------------------------------------------------------------------------- /examples/snippets/deleteLocalDocument/example_request.js: -------------------------------------------------------------------------------- 1 | // section: code 2 | import { CloudantV1 } from '@ibm-cloud/cloudant'; 3 | 4 | const service = CloudantV1.newInstance({}); 5 | 6 | service.deleteLocalDocument({ 7 | db: 'orders', 8 | docId: 'local-0007741142412418284' 9 | }).then(response => { 10 | console.log(response.result); 11 | }); 12 | -------------------------------------------------------------------------------- /examples/snippets/deleteReplicationDocument/example_request.js: -------------------------------------------------------------------------------- 1 | // section: code 2 | import { CloudantV1 } from '@ibm-cloud/cloudant'; 3 | 4 | const service = CloudantV1.newInstance({}); 5 | 6 | service.deleteReplicationDocument({ 7 | docId: 'repldoc-example', 8 | rev: '3-a0ccbdc6fe95b4184f9031d086034d85' 9 | }).then(response => { 10 | console.log(response.result); 11 | }); 12 | -------------------------------------------------------------------------------- /examples/snippets/getActiveTasks/example_request.js: -------------------------------------------------------------------------------- 1 | // section: code 2 | import { CloudantV1 } from '@ibm-cloud/cloudant'; 3 | 4 | const service = CloudantV1.newInstance({}); 5 | 6 | service.getActiveTasks().then(response => { 7 | console.log(response.result); 8 | }); 9 | -------------------------------------------------------------------------------- /examples/snippets/getActivityTrackerEvents/example_request.js: -------------------------------------------------------------------------------- 1 | // section: code 2 | import { CloudantV1 } from '@ibm-cloud/cloudant'; 3 | 4 | const service = CloudantV1.newInstance({}); 5 | 6 | service.getActivityTrackerEvents().then(response => { 7 | console.log(response.result); 8 | }); 9 | -------------------------------------------------------------------------------- /examples/snippets/getAllDbs/example_request.js: -------------------------------------------------------------------------------- 1 | // section: code 2 | import { CloudantV1 } from '@ibm-cloud/cloudant'; 3 | 4 | const service = CloudantV1.newInstance({}); 5 | 6 | service.getAllDbs().then(response => { 7 | console.log(response.result); 8 | }); 9 | -------------------------------------------------------------------------------- /examples/snippets/getAttachment/example_request.js: -------------------------------------------------------------------------------- 1 | // section: code 2 | import { CloudantV1 } from '@ibm-cloud/cloudant'; 3 | 4 | const service = CloudantV1.newInstance({}); 5 | 6 | service.getAttachment({ 7 | db: 'products', 8 | docId: '1000042', 9 | attachmentName: 'product_details.txt' 10 | }).then(response => { 11 | let attachment = response.result as Readable; 12 | attachment.pipe(process.stdout); 13 | }); 14 | // section: markdown 15 | // This example requires the `product_details.txt` attachment in `1000042` document to exist. To create the attachment, see [Create or modify an attachment.](#putattachment) 16 | -------------------------------------------------------------------------------- /examples/snippets/getCapacityThroughputInformation/example_request.js: -------------------------------------------------------------------------------- 1 | // section: code 2 | import { CloudantV1 } from '@ibm-cloud/cloudant'; 3 | 4 | const service = CloudantV1.newInstance({}); 5 | 6 | service.getCapacityThroughputInformation().then(response => { 7 | console.log(response.result); 8 | }); 9 | -------------------------------------------------------------------------------- /examples/snippets/getCorsInformation/example_request.js: -------------------------------------------------------------------------------- 1 | // section: code 2 | import { CloudantV1 } from '@ibm-cloud/cloudant'; 3 | 4 | const service = CloudantV1.newInstance({}); 5 | 6 | service.getCorsInformation().then(response => { 7 | console.log(response.result); 8 | }); 9 | -------------------------------------------------------------------------------- /examples/snippets/getCurrentThroughputInformation/example_request.js: -------------------------------------------------------------------------------- 1 | // section: code 2 | import { CloudantV1 } from '@ibm-cloud/cloudant'; 3 | 4 | const service = CloudantV1.newInstance({}); 5 | 6 | service.getCurrentThroughputInformation().then(response => { 7 | console.log(response.result); 8 | }); 9 | -------------------------------------------------------------------------------- /examples/snippets/getDatabaseInformation/example_request.js: -------------------------------------------------------------------------------- 1 | // section: code 2 | import { CloudantV1 } from '@ibm-cloud/cloudant'; 3 | 4 | const service = CloudantV1.newInstance({}); 5 | 6 | service.getDatabaseInformation({db: 'products'}).then(response => { 7 | console.log(response.result); 8 | }); 9 | -------------------------------------------------------------------------------- /examples/snippets/getDbUpdates/example_request.js: -------------------------------------------------------------------------------- 1 | // section: code 2 | import { CloudantV1 } from '@ibm-cloud/cloudant'; 3 | 4 | const service = CloudantV1.newInstance({}); 5 | 6 | service.getDbUpdates({ 7 | feed: 'normal', 8 | heartbeat: 10000, 9 | since: 'now' 10 | }).then(response => { 11 | console.log(response.result); 12 | }); 13 | // section: markdown 14 | // This request requires `server_admin` access. 15 | -------------------------------------------------------------------------------- /examples/snippets/getDesignDocument/example_request.js: -------------------------------------------------------------------------------- 1 | // section: code 2 | import { CloudantV1 } from '@ibm-cloud/cloudant'; 3 | 4 | const service = CloudantV1.newInstance({}); 5 | 6 | service.getDesignDocument({ 7 | db: 'products', 8 | ddoc: 'appliances', 9 | latest: true 10 | }).then(response => { 11 | console.log(response.result); 12 | }); 13 | -------------------------------------------------------------------------------- /examples/snippets/getDesignDocumentInformation/example_request.js: -------------------------------------------------------------------------------- 1 | // section: code 2 | import { CloudantV1 } from '@ibm-cloud/cloudant'; 3 | 4 | const service = CloudantV1.newInstance({}); 5 | 6 | service.getDesignDocumentInformation({ 7 | db: 'products', 8 | ddoc: 'appliances' 9 | }).then(response => { 10 | console.log(response.result); 11 | }); 12 | -------------------------------------------------------------------------------- /examples/snippets/getDocument/example_request.js: -------------------------------------------------------------------------------- 1 | // section: code 2 | import { CloudantV1 } from '@ibm-cloud/cloudant'; 3 | 4 | const service = CloudantV1.newInstance({}); 5 | 6 | service.getDocument({ 7 | db: 'products', 8 | docId: '1000042' 9 | }).then(response => { 10 | console.log(response.result); 11 | }); 12 | -------------------------------------------------------------------------------- /examples/snippets/getDocumentShardsInfo/example_request.js: -------------------------------------------------------------------------------- 1 | // section: code 2 | import { CloudantV1 } from '@ibm-cloud/cloudant'; 3 | 4 | const service = CloudantV1.newInstance({}); 5 | 6 | service.getDocumentShardsInfo({ 7 | db: 'products', 8 | docId: '1000042' 9 | }).then(response => { 10 | console.log(response.result); 11 | }); 12 | -------------------------------------------------------------------------------- /examples/snippets/getIndexesInformation/example_request.js: -------------------------------------------------------------------------------- 1 | // section: code 2 | import { CloudantV1 } from '@ibm-cloud/cloudant'; 3 | 4 | const service = CloudantV1.newInstance({}); 5 | 6 | service.getIndexesInformation({ 7 | db: 'users' 8 | }).then(response => { 9 | console.log(response.result); 10 | }); 11 | -------------------------------------------------------------------------------- /examples/snippets/getLocalDocument/example_request.js: -------------------------------------------------------------------------------- 1 | // section: code 2 | import { CloudantV1 } from '@ibm-cloud/cloudant'; 3 | 4 | const service = CloudantV1.newInstance({}); 5 | 6 | service.getLocalDocument({ 7 | db: 'orders', 8 | docId: 'local-0007741142412418284' 9 | }).then(response => { 10 | console.log(response.result); 11 | }); 12 | -------------------------------------------------------------------------------- /examples/snippets/getMembershipInformation/example_request.js: -------------------------------------------------------------------------------- 1 | // section: code 2 | import { CloudantV1 } from '@ibm-cloud/cloudant'; 3 | 4 | const service = CloudantV1.newInstance({}); 5 | 6 | service.getMembershipInformation().then(response => { 7 | console.log(response.result); 8 | }); 9 | -------------------------------------------------------------------------------- /examples/snippets/getPartitionInformation/example_request.js: -------------------------------------------------------------------------------- 1 | // section: code 2 | import { CloudantV1 } from '@ibm-cloud/cloudant'; 3 | 4 | const service = CloudantV1.newInstance({}); 5 | 6 | service.getPartitionInformation({ 7 | db: 'events', 8 | partitionKey: 'ns1HJS13AMkK' 9 | }).then(response => { 10 | console.log(response.result); 11 | }); 12 | -------------------------------------------------------------------------------- /examples/snippets/getReplicationDocument/example_request.js: -------------------------------------------------------------------------------- 1 | // section: code 2 | import { CloudantV1 } from '@ibm-cloud/cloudant'; 3 | 4 | const service = CloudantV1.newInstance({}); 5 | 6 | service.getReplicationDocument({ 7 | docId: 'repldoc-example' 8 | }).then(response => { 9 | console.log(response.result); 10 | }); 11 | -------------------------------------------------------------------------------- /examples/snippets/getSchedulerDocs/example_request.js: -------------------------------------------------------------------------------- 1 | // section: code 2 | import { CloudantV1 } from '@ibm-cloud/cloudant'; 3 | const service = CloudantV1.newInstance({}); 4 | 5 | service.getSchedulerDocs({ 6 | limit: 100, 7 | states: ['completed'] 8 | }).then(response => { 9 | console.log(response.result); 10 | }) 11 | -------------------------------------------------------------------------------- /examples/snippets/getSchedulerDocument/example_request.js: -------------------------------------------------------------------------------- 1 | // section: code 2 | import { CloudantV1 } from '@ibm-cloud/cloudant'; 3 | 4 | const service = CloudantV1.newInstance({}); 5 | 6 | service.getSchedulerDocument({ 7 | docId: 'repldoc-example' 8 | }).then(response => { 9 | console.log(response.result); 10 | }); 11 | -------------------------------------------------------------------------------- /examples/snippets/getSchedulerJob/example_request.js: -------------------------------------------------------------------------------- 1 | // section: code 2 | import { CloudantV1 } from '@ibm-cloud/cloudant'; 3 | 4 | const service = CloudantV1.newInstance({}); 5 | 6 | service.getSchedulerJob({ 7 | jobId: '7b94915cd8c4a0173c77c55cd0443939+continuous' 8 | }).then(response => { 9 | console.log(response.result); 10 | }); 11 | -------------------------------------------------------------------------------- /examples/snippets/getSchedulerJobs/example_request.js: -------------------------------------------------------------------------------- 1 | // section: code 2 | import { CloudantV1 } from '@ibm-cloud/cloudant'; 3 | 4 | const service = CloudantV1.newInstance({}); 5 | 6 | service.getSchedulerJobs({ 7 | limit: 100 8 | }).then(response => { 9 | console.log(response.result); 10 | }); 11 | -------------------------------------------------------------------------------- /examples/snippets/getSearchInfo/example_request.js: -------------------------------------------------------------------------------- 1 | // section: code 2 | import { CloudantV1 } from '@ibm-cloud/cloudant'; 3 | 4 | const service = CloudantV1.newInstance({}); 5 | 6 | service.getSearchInfo({ 7 | db: 'events', 8 | ddoc: 'checkout', 9 | index: 'findByDate' 10 | }).then(response => { 11 | console.log(response.result); 12 | }); 13 | // section: markdown 14 | // This example requires the `findByDate` Cloudant Search partitioned index to exist. To create the design document with this index, see [Create or modify a design document.](#putdesigndocument) 15 | -------------------------------------------------------------------------------- /examples/snippets/getSecurity/example_request.js: -------------------------------------------------------------------------------- 1 | // section: code 2 | import { CloudantV1 } from '@ibm-cloud/cloudant'; 3 | 4 | const service = CloudantV1.newInstance({}); 5 | 6 | service.getSecurity({ 7 | db: 'products' 8 | }).then(response => { 9 | console.log(response.result); 10 | }); 11 | -------------------------------------------------------------------------------- /examples/snippets/getServerInformation/example_request.js: -------------------------------------------------------------------------------- 1 | // section: code 2 | import { CloudantV1 } from '@ibm-cloud/cloudant'; 3 | 4 | const service = CloudantV1.newInstance({}); 5 | 6 | service.getServerInformation().then(response => { 7 | console.log(response.result); 8 | }); 9 | -------------------------------------------------------------------------------- /examples/snippets/getSessionInformation/example_request.js: -------------------------------------------------------------------------------- 1 | // section: code 2 | import { CloudantV1 } from '@ibm-cloud/cloudant'; 3 | 4 | const service = CloudantV1.newInstance({}); 5 | 6 | service.getSessionInformation().then(response => { 7 | console.log(response.result); 8 | }); 9 | // section: markdown 10 | // For more details on Session Authentication, see [Authentication.](#authentication) 11 | -------------------------------------------------------------------------------- /examples/snippets/getShardsInformation/example_request.js: -------------------------------------------------------------------------------- 1 | // section: code 2 | import { CloudantV1 } from '@ibm-cloud/cloudant'; 3 | 4 | const service = CloudantV1.newInstance({}); 5 | 6 | service.getShardsInformation({ 7 | db: 'products' 8 | }).then(response => { 9 | console.log(response.result); 10 | }); 11 | -------------------------------------------------------------------------------- /examples/snippets/getUpInformation/example_request.js: -------------------------------------------------------------------------------- 1 | // section: code 2 | import { CloudantV1 } from '@ibm-cloud/cloudant'; 3 | 4 | const service = CloudantV1.newInstance({}); 5 | 6 | service.getUpInformation().then(response => { 7 | console.log(response.result); 8 | }); 9 | -------------------------------------------------------------------------------- /examples/snippets/getUuids/example_request.js: -------------------------------------------------------------------------------- 1 | // section: code 2 | import { CloudantV1 } from '@ibm-cloud/cloudant'; 3 | 4 | const service = CloudantV1.newInstance({}); 5 | 6 | const uuidsParams: CloudantV1.GetUuidsParams = { 7 | count: 10 8 | }; 9 | 10 | service.getUuids(uuidsParams).then(response => { 11 | console.log(response.result); 12 | }); 13 | -------------------------------------------------------------------------------- /examples/snippets/headAttachment/example_request.js: -------------------------------------------------------------------------------- 1 | // section: code 2 | import { CloudantV1 } from '@ibm-cloud/cloudant'; 3 | 4 | const service = CloudantV1.newInstance({}); 5 | 6 | service.headAttachment({ 7 | db: 'products', 8 | docId: '1000042', 9 | attachmentName: 'product_details.txt' 10 | }).then(response => { 11 | console.log(response.status); 12 | console.log(response.headers['Content-Length']); 13 | console.log(response.headers['Content-Type']); 14 | }); 15 | // section: markdown 16 | // This example requires the `product_details.txt` attachment in `1000042` document to exist. To create the attachment, see [Create or modify an attachment.](#putattachment) 17 | -------------------------------------------------------------------------------- /examples/snippets/headDatabase/example_request.js: -------------------------------------------------------------------------------- 1 | // section: code 2 | import { CloudantV1 } from '@ibm-cloud/cloudant'; 3 | 4 | const service = CloudantV1.newInstance({}); 5 | 6 | service.headDatabase({db: 'products'}).then(response => { 7 | console.log(response.status); 8 | }); 9 | -------------------------------------------------------------------------------- /examples/snippets/headDesignDocument/example_request.js: -------------------------------------------------------------------------------- 1 | // section: code 2 | import { CloudantV1 } from '@ibm-cloud/cloudant'; 3 | 4 | const service = CloudantV1.newInstance({}); 5 | 6 | service.headDesignDocument({ 7 | db: 'events', 8 | ddoc: 'checkout' 9 | }).then(response => { 10 | console.log(response.status); 11 | console.log(response.headers['ETag']); 12 | }); 13 | -------------------------------------------------------------------------------- /examples/snippets/headDocument/example_request.js: -------------------------------------------------------------------------------- 1 | // section: code 2 | import { CloudantV1 } from '@ibm-cloud/cloudant'; 3 | 4 | const service = CloudantV1.newInstance({}); 5 | 6 | service.headDocument({ 7 | db: 'orders', 8 | docId: 'order00058' 9 | }).then(response => { 10 | console.log(response.status); 11 | console.log(response.headers['ETag']); 12 | }); 13 | -------------------------------------------------------------------------------- /examples/snippets/headReplicationDocument/example_request.js: -------------------------------------------------------------------------------- 1 | // section: code 2 | import { CloudantV1 } from '@ibm-cloud/cloudant'; 3 | 4 | const service = CloudantV1.newInstance({}); 5 | 6 | service.headReplicationDocument({ 7 | docId: 'repldoc-example' 8 | }).then(response => { 9 | console.log(response.status); 10 | console.log(response.headers['ETag']); 11 | }); 12 | -------------------------------------------------------------------------------- /examples/snippets/headSchedulerJob/example_request.js: -------------------------------------------------------------------------------- 1 | // section: code 2 | import { CloudantV1 } from '@ibm-cloud/cloudant'; 3 | 4 | const service = CloudantV1.newInstance({}); 5 | 6 | service.headSchedulerJob({ 7 | jobId: '7b94915cd8c4a0173c77c55cd0443939+continuous' 8 | }).then(response => { 9 | console.log(response.status); 10 | }); 11 | -------------------------------------------------------------------------------- /examples/snippets/postActivityTrackerEvents/example_request.js: -------------------------------------------------------------------------------- 1 | // section: code 2 | import { CloudantV1 } from '@ibm-cloud/cloudant'; 3 | 4 | const service = CloudantV1.newInstance({}); 5 | 6 | service.postActivityTrackerEvents({ 7 | types: ['management'], 8 | }).then(response => { 9 | console.log(response.result); 10 | }); 11 | -------------------------------------------------------------------------------- /examples/snippets/postAllDocs/example_request.js: -------------------------------------------------------------------------------- 1 | // section: code 2 | import { CloudantV1 } from '@ibm-cloud/cloudant'; 3 | 4 | const service = CloudantV1.newInstance({}); 5 | 6 | service.postAllDocs({ 7 | db: 'orders', 8 | includeDocs: true, 9 | startKey: 'abc', 10 | limit: 10 11 | }).then(response => { 12 | console.log(response.result); 13 | }); 14 | -------------------------------------------------------------------------------- /examples/snippets/postAllDocs/example_request_as_a_stream.js: -------------------------------------------------------------------------------- 1 | // section: code 2 | import { CloudantV1 } from '@ibm-cloud/cloudant'; 3 | 4 | const service = CloudantV1.newInstance({}); 5 | 6 | service.postAllDocsAsStream({ 7 | db: 'orders', 8 | includeDocs: true, 9 | startKey: 'abc', 10 | limit: 10 11 | }).then(response => { 12 | let stream = fs.createWriteStream("result.json"); 13 | response.result.pipe(stream); 14 | response.result.on('end', () => stream.end()); 15 | }); 16 | -------------------------------------------------------------------------------- /examples/snippets/postAllDocsQueries/example_request.js: -------------------------------------------------------------------------------- 1 | // section: code 2 | import { CloudantV1 } from '@ibm-cloud/cloudant'; 3 | 4 | const service = CloudantV1.newInstance({}); 5 | 6 | const allDocsQueries: CloudantV1.AllDocsQuery[] = [{ 7 | keys: ['1000042', '1000043'], 8 | }, 9 | { 10 | limit: 3, 11 | skip: 2 12 | }]; 13 | 14 | service.postAllDocsQueries({ 15 | db: 'products', 16 | queries: allDocsQueries 17 | }).then(response => { 18 | console.log(response.result); 19 | }); 20 | -------------------------------------------------------------------------------- /examples/snippets/postApiKeys/example_request.js: -------------------------------------------------------------------------------- 1 | // section: code 2 | import { CloudantV1 } from '@ibm-cloud/cloudant'; 3 | 4 | const service = CloudantV1.newInstance({}); 5 | 6 | service.postApiKeys().then(response => { 7 | console.log(response.result); 8 | }); 9 | -------------------------------------------------------------------------------- /examples/snippets/postBulkDocs/example_request_as_a_stream.js: -------------------------------------------------------------------------------- 1 | // section: code 2 | import { CloudantV1 } from '@ibm-cloud/cloudant'; 3 | 4 | const service = CloudantV1.newInstance({}); 5 | 6 | let stream = fs.createReadStream("upload.json"); 7 | 8 | service.postBulkDocs({ 9 | db: 'events', 10 | bulkDocs: stream 11 | }).then(response => { 12 | console.log(response.result); 13 | }); 14 | // section: markdown 15 | // Content of upload.json 16 | // section: code 17 | { 18 | "docs": [ 19 | { 20 | "_id": "ns1HJS13AMkK:0007241142412418284", 21 | "type": "event", 22 | "userId": "abc123", 23 | "eventType": "addedToBasket", 24 | "productId": "1000042", 25 | "date": "2019-01-28T10:44:22.000Z" 26 | }, 27 | { 28 | "_id": "H8tDIwfadxp9:0007241142412418285", 29 | "type": "event", 30 | "userId": "abc234", 31 | "eventType": "addedToBasket", 32 | "productId": "1000050", 33 | "date": "2019-01-25T20:00:00.000Z" 34 | } 35 | ] 36 | } 37 | -------------------------------------------------------------------------------- /examples/snippets/postBulkDocs/example_request_create_documents.js: -------------------------------------------------------------------------------- 1 | // section: code 2 | import { CloudantV1 } from '@ibm-cloud/cloudant'; 3 | 4 | const service = CloudantV1.newInstance({}); 5 | 6 | const eventDoc1: CloudantV1.Document = { 7 | _id: 'ns1HJS13AMkK:0007241142412418284', 8 | type: 'event', 9 | userId: 'abc123', 10 | eventType:'addedToBasket', 11 | productId: '1000042', 12 | date: '2019-01-28T10:44:22.000Z' 13 | } 14 | const eventDoc2: CloudantV1.Document = { 15 | _id: 'H8tDIwfadxp9:0007241142412418285', 16 | type: 'event', 17 | userId: 'abc234', 18 | eventType: 'addedToBasket', 19 | productId: '1000050', 20 | date: '2019-01-25T20:00:00.000Z' 21 | } 22 | 23 | const bulkDocs: CloudantV1.BulkDocs = { docs: [eventDoc1, eventDoc2] 24 | } 25 | 26 | service.postBulkDocs({ 27 | db: 'events', 28 | bulkDocs: bulkDocs 29 | }).then(response => { 30 | console.log(response.result); 31 | }); 32 | -------------------------------------------------------------------------------- /examples/snippets/postBulkDocs/example_request_delete_documents.js: -------------------------------------------------------------------------------- 1 | // section: code 2 | import { CloudantV1 } from '@ibm-cloud/cloudant'; 3 | 4 | const service = CloudantV1.newInstance({}); 5 | 6 | const eventDoc1: CloudantV1.Document = { 7 | _id: 'ns1HJS13AMkK:0007241142412418284', 8 | _rev: '1-5005d65514fe9e90f8eccf174af5dd64', 9 | _deleted: true, 10 | } 11 | const eventDoc2: CloudantV1.Document = { 12 | _id: 'H8tDIwfadxp9:0007241142412418285', 13 | _rev: '1-2d7810b054babeda4812b3924428d6d6', 14 | _deleted: true, 15 | } 16 | 17 | const bulkDocs: CloudantV1.BulkDocs = { docs: [eventDoc1, eventDoc2] 18 | } 19 | 20 | service.postBulkDocs({ 21 | db: 'events', 22 | bulkDocs: bulkDocs 23 | }).then(response => { 24 | console.log(response.result); 25 | }); 26 | -------------------------------------------------------------------------------- /examples/snippets/postBulkGet/alternative_example_request_for_atts_since.js: -------------------------------------------------------------------------------- 1 | // section: code 2 | const service = CloudantV1.newInstance({}); 3 | 4 | const bulkGetQueryDocuments: CloudantV1.BulkGetQueryDocument[] = [ 5 | { 6 | id: 'order00058', 7 | attsSince: ['1-99b02e08da151943c2dcb40090160bb8'] 8 | }, 9 | ]; 10 | 11 | const postBulkGetParams: CloudantV1.PostBulkGetParams = { 12 | db: 'orders', 13 | docs: bulkGetQueryDocuments, 14 | }; 15 | 16 | service.postBulkGet(postBulkGetParams) 17 | .then(response => { 18 | console.log(response.result); 19 | }); 20 | -------------------------------------------------------------------------------- /examples/snippets/postBulkGet/alternative_example_request_for_open_revs_all.js: -------------------------------------------------------------------------------- 1 | // section: code 2 | const service = CloudantV1.newInstance({}); 3 | 4 | const bulkGetDocs: CloudantV1.BulkGetQueryDocument[] = [ 5 | { 6 | id: 'order00067', 7 | }, 8 | ]; 9 | 10 | const postBulkGetParams: CloudantV1.PostBulkGetParams = { 11 | db: 'orders', 12 | docs: bulkGetDocs, 13 | }; 14 | 15 | service.postBulkGet(postBulkGetParams) 16 | .then(response => { 17 | console.log(response.result); 18 | }); 19 | -------------------------------------------------------------------------------- /examples/snippets/postBulkGet/example_request.js: -------------------------------------------------------------------------------- 1 | // section: code 2 | const service = CloudantV1.newInstance({}); 3 | 4 | const docId = 'order00067'; 5 | 6 | const bulkGetDoc1: CloudantV1.BulkGetQueryDocument = { 7 | id: docId, 8 | rev: '3-917fa2381192822767f010b95b45325b' 9 | }; 10 | const bulkGetDoc2: CloudantV1.BulkGetQueryDocument = { 11 | id: docId, 12 | rev: '4-a5be949eeb7296747cc271766e9a498b' 13 | }; 14 | 15 | const bulkGetDocs: CloudantV1.BulkGetQueryDocument[] = [bulkGetDoc1, bulkGetDoc2]; 16 | 17 | const postBulkGetParams: CloudantV1.PostBulkGetParams = { 18 | db: 'orders', 19 | docs: bulkGetDocs, 20 | }; 21 | 22 | service.postBulkGet(postBulkGetParams) 23 | .then(response => { 24 | console.log(response.result); 25 | }); 26 | -------------------------------------------------------------------------------- /examples/snippets/postChanges/example_request.js: -------------------------------------------------------------------------------- 1 | // section: code 2 | import { CloudantV1 } from '@ibm-cloud/cloudant'; 3 | 4 | const service = CloudantV1.newInstance({}); 5 | 6 | service.postChanges({ 7 | db: 'orders' 8 | }).then(response => { 9 | console.log(response.result); 10 | }); 11 | -------------------------------------------------------------------------------- /examples/snippets/postChanges/example_request_as_a_stream.js: -------------------------------------------------------------------------------- 1 | // section: code 2 | import { CloudantV1 } from '@ibm-cloud/cloudant'; 3 | 4 | const service = CloudantV1.newInstance({}); 5 | 6 | service.postChangesAsStream({ 7 | db: 'orders' 8 | }).then(response => { 9 | let stream = fs.createWriteStream("result.json"); 10 | response.result.pipe(stream); 11 | response.result.on('end', () => stream.end()); 12 | }); 13 | -------------------------------------------------------------------------------- /examples/snippets/postDbsInfo/example_request.js: -------------------------------------------------------------------------------- 1 | // section: code 2 | import { CloudantV1 } from '@ibm-cloud/cloudant'; 3 | 4 | const service = CloudantV1.newInstance({}); 5 | 6 | service.postDbsInfo({ 7 | keys: ['products', 'users', 'orders'] 8 | }).then(response => { 9 | console.log(response.result); 10 | }); 11 | -------------------------------------------------------------------------------- /examples/snippets/postDesignDocs/example_request.js: -------------------------------------------------------------------------------- 1 | // section: code 2 | import { CloudantV1 } from '@ibm-cloud/cloudant'; 3 | 4 | const service = CloudantV1.newInstance({}); 5 | 6 | service.postDesignDocs({ 7 | attachments: true, 8 | db: 'users' 9 | }).then(response => { 10 | console.log(response.result); 11 | }); 12 | -------------------------------------------------------------------------------- /examples/snippets/postDesignDocsQueries/example_request.js: -------------------------------------------------------------------------------- 1 | // section: code 2 | import { CloudantV1 } from '@ibm-cloud/cloudant'; 3 | 4 | const service = CloudantV1.newInstance({}); 5 | 6 | const doc1: CloudantV1.AllDocsQuery = { 7 | descending: true, 8 | includeDocs: true, 9 | limit: 10 10 | }; 11 | const doc2: CloudantV1.AllDocsQuery = { 12 | inclusiveEnd: true, 13 | key: '_design/allusers', 14 | skip: 1 15 | }; 16 | 17 | const allDocsQueries: CloudantV1.AllDocsQuery[] = [doc1, doc2]; 18 | 19 | service.postDesignDocsQueries({ 20 | db: 'users', 21 | queries: allDocsQueries 22 | }).then(response => { 23 | console.log(response.result); 24 | }); 25 | -------------------------------------------------------------------------------- /examples/snippets/postDocument/example_request.js: -------------------------------------------------------------------------------- 1 | // section: code 2 | import { CloudantV1 } from '@ibm-cloud/cloudant'; 3 | 4 | const service = CloudantV1.newInstance({}); 5 | 6 | const productsDoc: CloudantV1.Document = { 7 | _id: '1000042', 8 | type: 'product', 9 | productId: '1000042', 10 | brand: 'Salter', 11 | name: 'Digital Kitchen Scales', 12 | description: 'Slim Colourful Design Electronic Cooking Appliance for Home / Kitchen, Weigh up to 5kg + Aquatronic for Liquids ml + fl. oz. 15Yr Guarantee - Green', 13 | price: 14.99, 14 | image: 'assets/img/0gmsnghhew.jpg' 15 | }; 16 | 17 | service.postDocument({ 18 | db: 'products', 19 | document: productsDoc 20 | }).then(response => { 21 | console.log(response.result); 22 | }); 23 | -------------------------------------------------------------------------------- /examples/snippets/postExplain/example_request.js: -------------------------------------------------------------------------------- 1 | // section: code 2 | import { CloudantV1 } from '@ibm-cloud/cloudant'; 3 | 4 | const service = CloudantV1.newInstance({}); 5 | 6 | const selector = { 7 | type: { 8 | "$eq": "user" 9 | } 10 | }; 11 | 12 | service.postExplain({ 13 | db: 'users', 14 | executionStats: true, 15 | limit: 10, 16 | selector: selector 17 | }).then(response => { 18 | console.log(response.result); 19 | }); 20 | -------------------------------------------------------------------------------- /examples/snippets/postFind/example_request_for_json_index_type.js: -------------------------------------------------------------------------------- 1 | // section: code 2 | import { CloudantV1 } from '@ibm-cloud/cloudant'; 3 | const service = CloudantV1.newInstance({}); 4 | 5 | const selector: CloudantV1.JsonObject = { 6 | email_verified: { 7 | '$eq': true 8 | } 9 | }; 10 | 11 | const sort: CloudantV1.JsonObject = { 12 | email: 'desc' 13 | }; 14 | 15 | service.postFind({ 16 | db: 'users', 17 | selector: selector, 18 | fields: ['_id', 'type', 'name', 'email'], 19 | sort: [sort], 20 | limit: 3 21 | }).then(response => { 22 | console.log(response.result); 23 | }); 24 | // section: markdown 25 | // This example requires the `getUserByEmail` Cloudant Query "json" index to exist. To create the index, see [Create a new index on a database.](#postindex) 26 | -------------------------------------------------------------------------------- /examples/snippets/postFind/example_request_for_text_index_type.js: -------------------------------------------------------------------------------- 1 | // section: code 2 | import { CloudantV1 } from '@ibm-cloud/cloudant'; 3 | const service = CloudantV1.newInstance({}); 4 | 5 | const selector: CloudantV1.JsonObject = { 6 | address: { 7 | '$regex': 'Street' 8 | } 9 | }; 10 | 11 | service.postFind({ 12 | db: 'users', 13 | selector: selector, 14 | fields: ['_id', 'type', 'name', 'email', 'address'], 15 | limit: 3 16 | }).then(response => { 17 | console.log(response.result); 18 | }); 19 | // section: markdown 20 | // This example requires the `getUserByAddress` Cloudant Query "text" index to exist. To create the index, see [Create a new index on a database.](#postindex) 21 | -------------------------------------------------------------------------------- /examples/snippets/postIndex/example_request_using_json_type_index.js: -------------------------------------------------------------------------------- 1 | // section: code 2 | import { CloudantV1 } from '@ibm-cloud/cloudant'; 3 | 4 | const service = CloudantV1.newInstance({}); 5 | 6 | // Type "json" index fields require an object that maps the name of a field to a sort direction. 7 | const indexField: CloudantV1.IndexField = { 8 | email: 'asc' 9 | } 10 | 11 | const index: CloudantV1.IndexDefinition = { 12 | fields: [indexField] 13 | } 14 | 15 | service.postIndex({ 16 | db: 'users', 17 | ddoc: 'json-index', 18 | name: 'getUserByEmail', 19 | index: index, 20 | type: 'json' 21 | }).then(response => { 22 | console.log(response.result); 23 | }); 24 | -------------------------------------------------------------------------------- /examples/snippets/postIndex/example_request_using_text_type_index.js: -------------------------------------------------------------------------------- 1 | // section: code 2 | import { CloudantV1 } from '@ibm-cloud/cloudant'; 3 | 4 | const service = CloudantV1.newInstance({}); 5 | 6 | // Type "text" index fields require an object with a name and type properties for the field. 7 | const indexField: CloudantV1.IndexField = { 8 | name: 'address', 9 | type: 'string' 10 | } 11 | 12 | const index: CloudantV1.IndexDefinition = { 13 | fields: [indexField] 14 | } 15 | 16 | service.postIndex({ 17 | db: 'users', 18 | ddoc: 'text-index', 19 | name: 'getUserByAddress', 20 | index: index, 21 | type: 'text' 22 | }).then(response => { 23 | console.log(response.result); 24 | }); 25 | -------------------------------------------------------------------------------- /examples/snippets/postPartitionAllDocs/example_request.js: -------------------------------------------------------------------------------- 1 | // section: code 2 | import { CloudantV1 } from '@ibm-cloud/cloudant'; 3 | 4 | const service = CloudantV1.newInstance({}); 5 | 6 | service.postPartitionAllDocs({ 7 | db: 'events', 8 | partitionKey: 'ns1HJS13AMkK', 9 | includeDocs: true 10 | }).then(response => { 11 | console.log(response.result); 12 | }); 13 | -------------------------------------------------------------------------------- /examples/snippets/postPartitionExplain/example_request.js: -------------------------------------------------------------------------------- 1 | // section: code 2 | import { CloudantV1 } from '@ibm-cloud/cloudant'; 3 | 4 | const service = CloudantV1.newInstance({}); 5 | 6 | const selector: CloudantV1.Selector = { 7 | userId: {'$eq': 'abc123'} 8 | } 9 | service.postPartitionExplain({ 10 | db: 'events', 11 | executionStats: true, 12 | limit: 10, 13 | partitionKey: 'ns1HJS13AMkK', 14 | selector: selector 15 | }).then(response => { 16 | console.log(response.result); 17 | }); 18 | -------------------------------------------------------------------------------- /examples/snippets/postPartitionFind/example_request.js: -------------------------------------------------------------------------------- 1 | // section: code 2 | import { CloudantV1 } from '@ibm-cloud/cloudant'; 3 | 4 | const service = CloudantV1.newInstance({}); 5 | 6 | const selector: CloudantV1.Selector = { 7 | userId: {'$eq': 'abc123'} 8 | } 9 | service.postPartitionFind({ 10 | db: 'events', 11 | partitionKey: 'ns1HJS13AMkK', 12 | fields: ['productId', 'eventType', 'date'], 13 | selector: selector 14 | }).then(response => { 15 | console.log(response.result); 16 | }); 17 | -------------------------------------------------------------------------------- /examples/snippets/postPartitionSearch/example_request.js: -------------------------------------------------------------------------------- 1 | // section: code 2 | import { CloudantV1 } from '@ibm-cloud/cloudant'; 3 | 4 | const service = CloudantV1.newInstance({}); 5 | 6 | service.postPartitionSearch({ 7 | db: 'events', 8 | partitionKey: 'ns1HJS13AMkK', 9 | ddoc: 'checkout', 10 | index: 'findByDate', 11 | query: 'date:[2019-01-01T12:00:00.000Z TO 2019-01-31T12:00:00.000Z]' 12 | }).then(response => { 13 | console.log(response.result); 14 | }); 15 | // section: markdown 16 | // This example requires the `findByDate` Cloudant Search partitioned index to exist. To create the design document with this index, see [Create or modify a design document.](#putdesigndocument) 17 | -------------------------------------------------------------------------------- /examples/snippets/postPartitionView/example_request.js: -------------------------------------------------------------------------------- 1 | // section: code 2 | import { CloudantV1 } from '@ibm-cloud/cloudant'; 3 | 4 | const service = CloudantV1.newInstance({}); 5 | 6 | service.postPartitionView({ 7 | db: 'events', 8 | ddoc: 'checkout', 9 | includeDocs: true, 10 | limit: 10, 11 | partitionKey: 'ns1HJS13AMkK', 12 | view: 'byProductId' 13 | }).then(response => { 14 | console.log(response.result); 15 | }); 16 | // section: markdown 17 | // This example requires the `byProductId` partitioned view to exist. To create the design document with this view, see [Create or modify a design document.](#putdesigndocument) 18 | -------------------------------------------------------------------------------- /examples/snippets/postRevsDiff/example_request.js: -------------------------------------------------------------------------------- 1 | // section: code 2 | import { CloudantV1 } from '@ibm-cloud/cloudant'; 3 | 4 | const service = CloudantV1.newInstance({}); 5 | 6 | const revsDiff: CloudantV1.DocumentRevisions = { 7 | order00077: [ 8 | "<1-missing-revision>", 9 | "<2-missing-revision>", 10 | "<3-possible-ancestor-revision>" 11 | ] 12 | } 13 | 14 | service.postRevsDiff({ 15 | db: 'orders', 16 | revsDiffRequest: revsDiff 17 | }).then(response => { 18 | console.log(response.result); 19 | }); 20 | // section: markdown 21 | // This example requires the example revisions in the POST body to be replaced with valid revisions. 22 | -------------------------------------------------------------------------------- /examples/snippets/postSearch/example_request.js: -------------------------------------------------------------------------------- 1 | // section: code 2 | import { CloudantV1 } from '@ibm-cloud/cloudant'; 3 | 4 | const service = CloudantV1.newInstance({}); 5 | 6 | service.postSearch({ 7 | db: 'users', 8 | ddoc: 'allusers', 9 | index: 'activeUsers', 10 | query: 'name:Jane* AND active:True' 11 | }).then(response => { 12 | console.log(response.result); 13 | }); 14 | // section: markdown 15 | // This example requires the `activeUsers` Cloudant Search index to exist. To create the design document with this index, see [Create or modify a design document.](#putdesigndocument) 16 | -------------------------------------------------------------------------------- /examples/snippets/postSearchAnalyze/example_request.js: -------------------------------------------------------------------------------- 1 | // section: code 2 | import { CloudantV1 } from '@ibm-cloud/cloudant'; 3 | 4 | const service = CloudantV1.newInstance({}); 5 | 6 | service.postSearchAnalyze({ 7 | analyzer: 'english', 8 | text: 'running is fun', 9 | }).then(response => { 10 | console.log(response.result); 11 | }); 12 | -------------------------------------------------------------------------------- /examples/snippets/postView/example_request.js: -------------------------------------------------------------------------------- 1 | // section: code 2 | import { CloudantV1 } from '@ibm-cloud/cloudant'; 3 | 4 | const service = CloudantV1.newInstance({}); 5 | 6 | service.postView({ 7 | db: 'users', 8 | ddoc: 'allusers', 9 | view: 'getVerifiedEmails' 10 | }).then(response => { 11 | console.log(response.result); 12 | }); 13 | // section: markdown 14 | // This example requires the `getVerifiedEmails` view to exist. To create the design document with this view, see [Create or modify a design document.](#putdesigndocument) 15 | -------------------------------------------------------------------------------- /examples/snippets/postViewQueries/example_request.js: -------------------------------------------------------------------------------- 1 | // section: code 2 | import { CloudantV1 } from '@ibm-cloud/cloudant'; 3 | 4 | const service = CloudantV1.newInstance({}); 5 | 6 | const viewQueries: CloudantV1.ViewQuery[] = [ 7 | { 8 | includeDocs: true, 9 | limit: 5 10 | }, 11 | { 12 | descending: true, 13 | skip: 1 14 | } 15 | ]; 16 | service.postViewQueries({ 17 | db: 'users', 18 | ddoc: 'allusers', 19 | queries: viewQueries, 20 | view: 'getVerifiedEmails' 21 | }).then(response => { 22 | console.log(response.result); 23 | }); 24 | // section: markdown 25 | // This example requires the `getVerifiedEmails` view to exist. To create the design document with this view, see [Create or modify a design document.](#putdesigndocument) 26 | -------------------------------------------------------------------------------- /examples/snippets/putAttachment/example_request.js: -------------------------------------------------------------------------------- 1 | // section: code 2 | import { CloudantV1 } from '@ibm-cloud/cloudant'; 3 | 4 | const service = CloudantV1.newInstance({}); 5 | 6 | const stream = new Readable(); 7 | stream.push('This appliance includes...'); 8 | stream.push(null); 9 | 10 | service.putAttachment({ 11 | db: 'products', 12 | docId: '1000042', 13 | attachmentName: 'product_details.txt', 14 | attachment: stream, 15 | contentType: 'text/plain' 16 | }).then(response => { 17 | console.log(response.result); 18 | }); 19 | -------------------------------------------------------------------------------- /examples/snippets/putCapacityThroughputConfiguration/example_request.js: -------------------------------------------------------------------------------- 1 | // section: code 2 | import { CloudantV1 } from '@ibm-cloud/cloudant'; 3 | 4 | const service = CloudantV1.newInstance({}); 5 | 6 | service.putCapacityThroughputConfiguration({ 7 | blocks: 1, 8 | }).then(response => { 9 | console.log(response.result); 10 | }); 11 | -------------------------------------------------------------------------------- /examples/snippets/putCloudantSecurity/example_request.js: -------------------------------------------------------------------------------- 1 | // section: code 2 | import { CloudantV1 } from '@ibm-cloud/cloudant'; 3 | 4 | const service = CloudantV1.newInstance({}); 5 | 6 | service.putCloudantSecurityConfiguration({ 7 | db: 'products', 8 | cloudant: {'nobody': ['_reader']} 9 | }).then(response => { 10 | console.log(response.result); 11 | }); 12 | // section: markdown 13 | // The `nobody` username applies to all unauthenticated connection attempts. For example, if an application tries to read data from a database, but didn't identify itself, the task can continue only if the `nobody` user has the role `_reader`. 14 | // section: markdown 15 | // If instead of using Cloudant's security model for managing permissions you opt to use the Apache CouchDB `_users` database (that is using legacy credentials _and_ the `couchdb_auth_only:true` option) then be aware that the user must already exist in `_users` database before adding permissions. For information on the `_users` database, see Using the `_users` database with Cloudant. 16 | -------------------------------------------------------------------------------- /examples/snippets/putCorsConfiguration/example_request.js: -------------------------------------------------------------------------------- 1 | // section: code 2 | import { CloudantV1 } from '@ibm-cloud/cloudant'; 3 | 4 | const service = CloudantV1.newInstance({}); 5 | 6 | service.putCorsConfiguration({ 7 | enableCors: true, 8 | origins: ['https://example.com'] 9 | }).then(response => { 10 | console.log(response.result); 11 | }); 12 | -------------------------------------------------------------------------------- /examples/snippets/putDatabase/example_request.js: -------------------------------------------------------------------------------- 1 | // section: code 2 | import { CloudantV1 } from '@ibm-cloud/cloudant'; 3 | 4 | const service = CloudantV1.newInstance({}); 5 | 6 | service.putDatabase({ 7 | db: 'events', 8 | partitioned: true 9 | }).then(response => { 10 | console.log(response.result); 11 | }); 12 | -------------------------------------------------------------------------------- /examples/snippets/putDesignDocument/example_request.js: -------------------------------------------------------------------------------- 1 | // section: code 2 | import { CloudantV1 } from '@ibm-cloud/cloudant'; 3 | 4 | const service = CloudantV1.newInstance({}); 5 | 6 | const emailViewMapReduce: CloudantV1.DesignDocumentViewsMapReduce = { 7 | map: 'function(doc) { if(doc.email_verified === true) { emit(doc.email, [doc.name, doc.email_verified, doc.joined]); }}' 8 | } 9 | 10 | const userIndex: CloudantV1.SearchIndexDefinition = { 11 | index: 'function(doc) { index("name", doc.name); index("active", doc.active); }' 12 | } 13 | 14 | const designDocument: CloudantV1.DesignDocument = { 15 | views: {'getVerifiedEmails': emailViewMapReduce}, 16 | indexes: {'activeUsers': userIndex}} 17 | 18 | service.putDesignDocument({ 19 | db: 'users', 20 | designDocument: designDocument, 21 | ddoc: 'allusers' 22 | }).then(response => { 23 | console.log(response.result); 24 | }); 25 | 26 | const productMap: CloudantV1.DesignDocumentViewsMapReduce = { 27 | map: 'function(doc) { emit(doc.productId, [doc.date, doc.eventType, doc.userId]); }' 28 | } 29 | 30 | const dateIndex: CloudantV1.SearchIndexDefinition = { 31 | index: 'function(doc) { index("date", doc.date); }' 32 | } 33 | 34 | const partitionedDesignDoc: CloudantV1.DesignDocument = { 35 | views: {'byProductId': productMap}, 36 | indexes: {'findByDate': dateIndex}} 37 | 38 | service.putDesignDocument({ 39 | db: 'events', 40 | designDocument: partitionedDesignDoc, 41 | ddoc: 'checkout' 42 | }).then(response => { 43 | console.log(response.result); 44 | }); 45 | // section: markdown 46 | // This example creates `allusers` design document in the `users` database and `checkout` design document in the partitioned `events` database. 47 | -------------------------------------------------------------------------------- /examples/snippets/putDocument/example_request.js: -------------------------------------------------------------------------------- 1 | // section: code 2 | import { CloudantV1 } from '@ibm-cloud/cloudant'; 3 | 4 | const service = CloudantV1.newInstance({}); 5 | 6 | const eventDoc: CloudantV1.Document = { 7 | type: 'event', 8 | userId: 'abc123', 9 | eventType: 'addedToBasket', 10 | productId: '1000042', 11 | date: '2019-01-28T10:44:22.000Z' 12 | }; 13 | 14 | service.putDocument({ 15 | db: 'events', 16 | docId: 'ns1HJS13AMkK:0007241142412418284', 17 | document: eventDoc 18 | }).then(response => { 19 | console.log(response.result); 20 | }); 21 | -------------------------------------------------------------------------------- /examples/snippets/putLocalDocument/example_request.js: -------------------------------------------------------------------------------- 1 | // section: code 2 | import { CloudantV1 } from '@ibm-cloud/cloudant'; 3 | 4 | const service = CloudantV1.newInstance({}); 5 | 6 | const localDocument: CloudantV1.Document = { 7 | type: 'order', 8 | user: 'Bob Smith', 9 | orderId: '0007741142412418284', 10 | userId: 'abc123', 11 | total: 214.98, 12 | deliveryAddress: '19 Front Street, Darlington, DL5 1TY', 13 | delivered: 'true', 14 | courier: 'UPS', 15 | courierId: '15125425151261289', 16 | date: '2019-01-28T10:44:22.000Z' 17 | } 18 | 19 | service.putLocalDocument({ 20 | db: 'orders', 21 | docId: 'local-0007741142412418284', 22 | document: localDocument 23 | }).then(response => { 24 | console.log(response.result); 25 | }); 26 | -------------------------------------------------------------------------------- /examples/snippets/putReplicationDocument/example_request.js: -------------------------------------------------------------------------------- 1 | // section: code 2 | import { CloudantV1 } from '@ibm-cloud/cloudant'; 3 | 4 | const service = CloudantV1.newInstance({}); 5 | 6 | const sourceDb: CloudantV1.ReplicationDatabase = { 7 | url: '/animaldb' 8 | }; 9 | 10 | const targetDb: CloudantV1.ReplicationDatabase = { 11 | auth: { 12 | iam: { 13 | 'apiKey': '' 14 | } 15 | }, 16 | url: '/animaldb-target' 17 | }; 18 | 19 | const replDocument: CloudantV1.ReplicationDocument = { 20 | id: 'repldoc-example', 21 | createTarget: true, 22 | source: sourceDb, 23 | target: targetDb 24 | } 25 | 26 | service.putReplicationDocument({ 27 | docId: 'repldoc-example', 28 | replicationDocument: replDocument 29 | }).then(response => { 30 | console.log(response.result); 31 | }); 32 | -------------------------------------------------------------------------------- /examples/snippets/putSecurity/example_request.js: -------------------------------------------------------------------------------- 1 | // section: code 2 | import { CloudantV1 } from '@ibm-cloud/cloudant'; 3 | 4 | const service = CloudantV1.newInstance({}); 5 | 6 | const members: CloudantV1.SecurityObject = { 7 | names: ['user1', 'user2'], 8 | roles: ['developers'] 9 | }; 10 | 11 | service.putSecurity({ 12 | db: 'products', 13 | members: members 14 | }).then(response => { 15 | console.log(response.result); 16 | }); 17 | // section: markdown 18 | // The `nobody` username applies to all unauthenticated connection attempts. For example, if an application tries to read data from a database, but didn't identify itself, the task can continue only if the `nobody` user has the role `_reader`. 19 | // section: markdown 20 | // If instead of using Cloudant's security model for managing permissions you opt to use the Apache CouchDB `_users` database (that is using legacy credentials _and_ the `couchdb_auth_only:true` option) then be aware that the user must already exist in `_users` database before adding permissions. For information on the `_users` database, see Using the `_users` database with Cloudant. 21 | -------------------------------------------------------------------------------- /index.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * © Copyright IBM Corporation 2020. 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 | /** 18 | * @module cloudant-node-sdk 19 | */ 20 | 21 | import { default as CloudantV1 } from './cloudant/v1'; 22 | export { CloudantV1 }; 23 | 24 | export { 25 | BasicAuthenticator, 26 | IamAuthenticator, 27 | CouchdbSessionAuthenticator, 28 | CouchdbSessionAuthenticatorOptions, 29 | } from './auth'; 30 | 31 | export { ChangesFollower } from './cloudant/features/changesFollower'; 32 | export { Stream } from './cloudant/features/stream'; 33 | -------------------------------------------------------------------------------- /lib/common.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * © Copyright IBM Corporation 2020, 2022. 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 { platform, release, arch } from 'node:os'; 18 | import { version } from '../package.json'; 19 | 20 | export type SdkHeaders = { 21 | 'User-Agent': string; 22 | 'X-IBMCloud-SDK-Analytics': string; 23 | }; 24 | 25 | /** 26 | * Get the request headers to be sent in requests by the SDK. 27 | */ 28 | export function getSdkHeaders( 29 | serviceName: string, 30 | serviceVersion: string, 31 | operationId: string 32 | ): SdkHeaders | {} { 33 | const sdkName = 'cloudant-node-sdk'; 34 | const sdkVersion = version; 35 | const osName = platform(); 36 | const osVersion = release(); 37 | const osArch = arch(); 38 | const nodeVersion = process.version; 39 | 40 | const headers = { 41 | 'User-Agent': `${sdkName}/${sdkVersion} (node.version=${nodeVersion}; os.name=${osName}; os.version=${osVersion}; os.arch=${osArch}; lang=Node.js;)`, 42 | 'X-IBMCloud-SDK-Analytics': `service_name=${serviceName};service_version=${serviceVersion};operation_id=${operationId}`, 43 | }; 44 | 45 | return headers; 46 | } 47 | -------------------------------------------------------------------------------- /lib/errorResponseInterceptor.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * © Copyright IBM Corporation 2024. 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 { pipeline } = require('node:stream/promises'); 18 | const { Writable, Readable } = require('node:stream'); 19 | 20 | export function errorResponseInterceptor(axiosError) { 21 | if ( 22 | axiosError.response && // must have a response 23 | axiosError.response.status >= 400 && 24 | // must have data: 25 | axiosError.response.data && 26 | // must be a JSON request 27 | // which also implies Content-Type starts with application/json: 28 | (axiosError.config.responseType === 'json' || 29 | // or a stream request with application/json 30 | // that has had the response converted to an object 31 | (axiosError.config.responseType === 'stream' && 32 | axiosError.response.headers['content-type'].startsWith( 33 | 'application/json' 34 | ) && 35 | !(axiosError.response.data instanceof Readable))) && 36 | // must be a valid JSON: 37 | // which also implies it is not a HEAD method: 38 | axiosError.response.data instanceof Object && 39 | !axiosError.response.data.trace 40 | ) { 41 | // Map the error/reason if available 42 | // and not already have errors array: 43 | if (!axiosError.response.data.errors && axiosError.response.data.error) { 44 | const error = { 45 | code: axiosError.response.data.error, 46 | message: axiosError.response.data.error, 47 | }; 48 | if (axiosError.response.data.reason) { 49 | error.message += `: ${axiosError.response.data.reason}`; 50 | } 51 | // Add the new error as part of an errors array. 52 | axiosError.response.data.errors = [error]; 53 | } 54 | if (axiosError.response.data.errors) { 55 | // Map x-request-id or x-couch-request-id if available to the trace field 56 | const trace = 57 | axiosError.response.headers['x-request-id'] || 58 | axiosError.response.headers['x-couch-request-id']; 59 | if (trace) { 60 | // Trace should be omitted if there is no value 61 | axiosError.response.data.trace = trace; 62 | } 63 | } 64 | } 65 | 66 | return Promise.reject(axiosError); 67 | } 68 | 69 | /** 70 | * Axios interceptor to convert errors for streaming cases into 71 | * JSON objects. 72 | * 73 | * This means users can handle rejections for AsStream cases in 74 | * the same way as normal error rejections and we can apply the 75 | * errorResponseInterceptor to augment the errors. 76 | * 77 | * @param axiosError 78 | * @returns rejected promise with the stream body transoformed 79 | */ 80 | export async function errorResponseStreamConverter(axiosError) { 81 | if ( 82 | axiosError.response && // must have a response 83 | axiosError.response.status >= 400 && 84 | // must have data: 85 | axiosError.response.data && 86 | // Must be a JSON response 87 | axiosError.response.headers['content-type']?.startsWith( 88 | 'application/json' 89 | ) && 90 | // this is for streaming requests 91 | axiosError.config.responseType === 'stream' && 92 | // must be a stream 93 | axiosError.response.data instanceof Readable 94 | ) { 95 | let data = ''; 96 | await pipeline( 97 | axiosError.response.data, 98 | new Writable({ 99 | write: (c, e, cb) => { 100 | data += c; 101 | cb(); 102 | }, 103 | }) 104 | ); 105 | try { 106 | axiosError.response.data = JSON.parse(data); 107 | } catch (e) { 108 | // JSON parse failure, just use the original 109 | // error stream as a string, matching axios behavior 110 | // for broken JSON 111 | axiosError.response.data = data; 112 | } 113 | } 114 | 115 | return Promise.reject(axiosError); 116 | } 117 | -------------------------------------------------------------------------------- /lib/getAuthenticatorFromEnvCloudantExtension.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * © Copyright IBM Corporation 2020, 2021. 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 { 18 | Authenticator, 19 | getAuthenticatorFromEnvironment, 20 | readExternalSources, 21 | } from 'ibm-cloud-sdk-core'; 22 | import { CouchdbSessionAuthenticator } from '../auth'; 23 | 24 | const COUCHDB_SESSION_AUTH_TYPE = 'couchdb_session'; 25 | 26 | /** 27 | * Extend the creating Authenticator from external configuration function with 28 | * CouchDB specific Session Authenticator possibility. 29 | * 30 | * @param {string} serviceName The service name prefix. 31 | * 32 | */ 33 | export default function getAuthenticatorFromEnvCloudantExtension( 34 | serviceName: string 35 | ): Authenticator { 36 | let auth; 37 | const credentials = readExternalSources(serviceName); 38 | let { authType } = credentials; 39 | if (!authType) { 40 | // this is the alternative "AUTHTYPE" config property 41 | authType = credentials.authtype || ''; 42 | } 43 | 44 | if (authType.toLowerCase() === COUCHDB_SESSION_AUTH_TYPE) { 45 | auth = new CouchdbSessionAuthenticator(credentials); 46 | } else { 47 | auth = getAuthenticatorFromEnvironment(serviceName); 48 | } 49 | return auth; 50 | } 51 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@ibm-cloud/cloudant", 3 | "version": "0.12.4", 4 | "description": "IBM Cloudant Node.js SDK", 5 | "main": "index.js", 6 | "repository": { 7 | "type": "git", 8 | "url": "https://github.com/IBM/cloudant-node-sdk" 9 | }, 10 | "keywords": [ 11 | "IBM", 12 | "Cloudant", 13 | "database", 14 | "client", 15 | "SDK", 16 | "official" 17 | ], 18 | "author": "IBM Corp.", 19 | "scripts": { 20 | "eslint:fix": "eslint . --ext .ts,.js --fix", 21 | "eslint:check": "eslint . --ext .ts,.js --cache", 22 | "lint": "npm run eslint:check", 23 | "lint-fix": "npm run eslint:fix", 24 | "copymeta": "[ -d 'dist' ] && cp package.json LICENSE README.md dist/", 25 | "build": "tsc", 26 | "postbuild": "npm run copymeta", 27 | "version": "bump-my-version bump patch --allow-dirty --new-version $npm_package_version && git add README.md .bumpversion.toml", 28 | "postversion": "npm run copymeta", 29 | "jest": "jest --runInBand", 30 | "test": "npm run build && npm run lint && jest --runInBand test/", 31 | "test-unit": "npm run build && jest --runInBand test/unit/", 32 | "test-integration": "npm run build && jest --runInBand test/integration", 33 | "test-ci": "jest --runInBand --testNamePattern='^((?!@slow).)*$' test/", 34 | "test-unit-ci": "jest --runInBand test/unit/", 35 | "test-integration-ci": "jest --runInBand --no-colors --testNamePattern='^((?!@slow).)*$' --json test/integration > test-output.log", 36 | "typedoc": "typedoc" 37 | }, 38 | "license": "Apache-2.0", 39 | "engines": { 40 | "node": "^20 || ^22" 41 | }, 42 | "dependencies": { 43 | "@types/node": "20.17.58", 44 | "ibm-cloud-sdk-core": "5.4.0" 45 | }, 46 | "devDependencies": { 47 | "@ibm-cloud/sdk-test-utilities": "^1.0.0", 48 | "@types/extend": "3.0.4", 49 | "@types/jest": "29.5.14", 50 | "@typescript-eslint/eslint-plugin": "7.18.0", 51 | "@typescript-eslint/parser": "7.18.0", 52 | "dotenv": "16.5.0", 53 | "eslint": "8.57.0", 54 | "eslint-config-airbnb-base": "15.0.0", 55 | "eslint-config-prettier": "10.1.1", 56 | "eslint-import-resolver-typescript": "4.4.1", 57 | "eslint-plugin-header": "3.1.1", 58 | "eslint-plugin-import": "2.31.0", 59 | "eslint-plugin-jest": "28.12.0", 60 | "eslint-plugin-jsdoc": "50.7.1", 61 | "eslint-plugin-node": "11.1.0", 62 | "eslint-plugin-prettier": "5.4.0", 63 | "jest": "29.7.0", 64 | "jest-junit": "16.0.0", 65 | "nock": "14.0.5", 66 | "prettier": "3.5.3", 67 | "sinon": "20.0.0", 68 | "ts-jest": "29.3.4", 69 | "typedoc": "0.28.5", 70 | "typescript": "5.8.3" 71 | }, 72 | "peerDependencies": { 73 | "@types/tough-cookie": "^4.0.0", 74 | "extend": "^3.0.2", 75 | "tough-cookie": "^4.0.0" 76 | }, 77 | "jest": { 78 | "collectCoverage": true, 79 | "coverageDirectory": "./coverage/", 80 | "coveragePathIgnorePatterns": [ 81 | "/test/" 82 | ], 83 | "preset": "ts-jest", 84 | "testEnvironment": "node", 85 | "testResultsProcessor": "jest-junit" 86 | }, 87 | "jest-junit": { 88 | "suiteName": "Generated unit tests", 89 | "outputDirectory": "./junitreports/", 90 | "outputName": "junit.xml", 91 | "uniqueOutputName": "false", 92 | "classNameTemplate": "{classname}", 93 | "titleTemplate": "{title}", 94 | "ancestorSeparator": " › ", 95 | "usePathForSuiteName": "true" 96 | }, 97 | "files": [ 98 | "/index.js", 99 | "/index.js.map", 100 | "/index.d.ts", 101 | "/auth", 102 | "/cloudant", 103 | "/lib" 104 | ] 105 | } 106 | -------------------------------------------------------------------------------- /scripts/publish_buildinfo.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -ev 4 | file="/tmp/build.json" 5 | tmpfile="/tmp/build.json.tmp" 6 | printf "Current values set:\n Module ID: $MODULE_ID\nBuild name: $BUILD_NAME\nBuild URL: $BUILD_URL\nBuild timestamp: $BUILD_TIMESTAMP\nBuild number: $BUILD_NUMBER\n" 7 | printf "Artifactory URL: $ARTIFACT_URL\n" 8 | ARTIFACTS=$(curl -f -H "Authorization: Bearer $ARTIFACTORY_CREDS_PSW" "$ARTIFACT_URL") 9 | HAS_CHILDREN_RESULT=$(echo $ARTIFACTS | jq 'has("children")') 10 | # if 'children' array exists then grab all aritfact uris, else grab parent uri 11 | if [[ "$HAS_CHILDREN_RESULT" == "false" ]]; then 12 | ARTIFACTS_URI=$(echo $ARTIFACTS | jq -r '.uri' | sed 's:.*/:/:') 13 | else 14 | ARTIFACTS_URI=$(echo $ARTIFACTS | jq -r '.children[] | .uri') 15 | fi 16 | 17 | if [[ -z $ARTIFACTS_URI || -z $MODULE_ID || -z $BUILD_NAME || -z $BUILD_URL || -z $BUILD_TIMESTAMP || -z $BUILD_NUMBER ]]; then 18 | printf "one or more variables are undefined. Check printed statement above." 19 | exit 1 20 | fi 21 | 22 | # create new (or write over existing) build.json file 23 | echo -n "" > $file 24 | # get current published build 25 | CURRENT_BUILD=$(curl -f -H "Authorization: Bearer $ARTIFACTORY_CREDS_PSW" "$STAGE_ROOT"build/"$BUILD_NAME"/"$BUILD_NUMBER") 26 | echo $CURRENT_BUILD 27 | CURRENT_BUILD_INFO=$(echo "$CURRENT_BUILD" | jq -r '.buildInfo') 28 | echo $CURRENT_BUILD_INFO | tee $file 29 | 30 | # put build name, number, and url on published artifacts 31 | URL_WITH_PROPS="$ARTIFACT_URL?properties=build.name=$BUILD_NAME;build.number=$BUILD_NUMBER;build.url=$BUILD_URL" 32 | curl --fail-with-body -i -X PUT -H "Authorization: Bearer $ARTIFACTORY_CREDS_PSW" "$URL_WITH_PROPS" 33 | 34 | # add artifact type and module id to build info file 35 | jq --arg type "$TYPE" '.type = $type' $file > $tmpfile && mv $tmpfile $file 36 | jq --arg id "$MODULE_ID" '.modules[0].id = $id' $file > $tmpfile && mv $tmpfile $file 37 | 38 | # get md5 and sha1 from each artifact and add to build info file 39 | for artifact in $ARTIFACTS_URI; do 40 | # node does not have 'children' artifacts 41 | if [[ "$HAS_CHILDREN_RESULT" == "false" ]]; then 42 | ARTIFACT_INFO_URL="$ARTIFACT_URL" 43 | else 44 | ARTIFACT_INFO_URL="$ARTIFACT_URL$artifact" 45 | fi 46 | GET_ARTIFACT=$(curl -f -H "Authorization: Bearer $ARTIFACTORY_CREDS_PSW" $ARTIFACT_INFO_URL | jq -r .checksums) 47 | MD5=$(echo $GET_ARTIFACT | jq -r .md5) 48 | SHA1=$(echo $GET_ARTIFACT | jq -r .sha1) 49 | NAME="${artifact#*/}" 50 | jq --arg name "$NAME" --arg md5 "$MD5" --arg sha1 "$SHA1" '.modules[0].artifacts += [{"name": $name, "md5": $md5, "sha1": $sha1}]' $file > $tmpfile && mv $tmpfile $file 51 | done 52 | 53 | if jq empty $file 2>/dev/null; then 54 | echo "JSON in $file is valid" 55 | else 56 | echo "JSON in $file is invalid" 57 | exit 1 58 | fi 59 | 60 | curl --fail-with-body -i -X PUT -H "Authorization: Bearer $ARTIFACTORY_CREDS_PSW" -H "Content-Type: application/json" "$STAGE_ROOT"build --upload-file $file 61 | -------------------------------------------------------------------------------- /scripts/setup_couch.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -ev 4 | 5 | printenv | grep "^SERVER_" >> cloudant_v1.env 6 | # if you change the image version please regenerate example output captures 7 | # use image at ARTIFACTORY_DOCKER_REPO_VIRTUAL registry if set, otherwise use default registry 8 | 9 | # shellcheck disable=SC2016 10 | timeout 120 bash -c 'while [[ "$(curl -s -o /dev/null -w ''%{http_code}'' ${SERVER_URL}/_up)" != "200" ]]; do sleep 5; done' || false 11 | curl -XPUT -u "$SERVER_USERNAME":"$SERVER_PASSWORD" "$SERVER_URL"/_users 12 | curl -XPUT -u "$SERVER_USERNAME":"$SERVER_PASSWORD" "$SERVER_URL"/stores 13 | curl -XPOST -u "$SERVER_USERNAME":"$SERVER_PASSWORD" -H "Content-type: application/json" -d '{}' "$SERVER_URL"/stores 14 | -------------------------------------------------------------------------------- /scripts/setup_wiremock.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -ev 4 | 5 | printenv | grep "^WIREMOCK_" > wiremock.env 6 | # setup env file for gen ITs 7 | echo -e "CLOUDANT_AUTH_TYPE=noauth\nCLOUDANT_URL=$WIREMOCK_URL" >> cloudant_v1.env 8 | 9 | # use image at ARTIFACTORY_DOCKER_REPO_VIRTUAL registry if set, otherwise use default registry 10 | timeout 120 bash -c 'while [[ "$(curl -s --location -o /dev/null -w ''%{http_code}'' ${WIREMOCK_URL}/__admin)" != "200" ]]; do sleep 2; done' || false 11 | curl "$WIREMOCK_URL"/__admin/mappings/import -X POST -d @stubs/mappings.json 12 | curl "$WIREMOCK_URL"/__admin/mappings/import -X POST -d @stubs/gen-its-mappings.json 13 | echo "Wiremock started" 14 | -------------------------------------------------------------------------------- /scripts/typedoc/generate-index-html.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh -e 2 | 3 | # based on https://odoepner.wordpress.com/2012/02/17/shell-script-to-generate-simple-index-html/ 4 | 5 | echo ' 6 | 7 | 8 | 9 | 10 | 11 | IBM Cloudant SDK for Node 12 | 13 | 14 | 15 |
16 | 19 | 20 |

Cloudant API Docs 21 | | GitHub 22 |

23 | 24 |

Typedoc by release:

25 | 28 |
29 | 30 | ' 31 | -------------------------------------------------------------------------------- /scripts/typedoc/publish-doc.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -e 4 | 5 | # Store GIT properties in vars 6 | GIT_COMMIT=$(git rev-parse --short HEAD) 7 | GIT_REPO=$(git remote get-url origin) 8 | 9 | # Create documentation 10 | printf ">>>>> Generate new documentation\n" 11 | npm run typedoc 12 | 13 | # Clone gh-pages branch 14 | printf ">>>>> Publishing typedoc for release build: repo=%s branch=%s build_num=%s job_name=%s\n" ${GIT_REPO} ${BRANCH_NAME} ${BUILD_NUMBER} ${JOB_NAME} 15 | printf ">>>>> Cloning repository's gh-pages branch into directory 'gh-pages'\n" 16 | git clone --branch=gh-pages https://github.com/IBM/cloudant-node-sdk.git gh-pages 17 | 18 | printf ">>>>> Finished cloning...\n" 19 | 20 | pushd gh-pages 21 | 22 | # Remove v prefix from TAG_NAME 23 | TAG_NAME=${TAG_NAME#'v'} 24 | 25 | # Semantic version pattern is copied from Semantic Versioning Specification 2.0.0: 26 | # Author: Tom Preston-Werner 27 | # License: Creative Commons ― CC BY 3.0 https://creativecommons.org/licenses/by/3.0/ 28 | # Source: https://semver.org/#is-there-a-suggested-regular-expression-regex-to-check-a-semver-string 29 | # Change: Reduced the reg-exp to match only: .. 30 | if [[ $TAG_NAME =~ ^(0|[1-9][[:digit:]]*)\.(0|[1-9][[:digit:]]*)\.(0|[1-9][[:digit:]]*)$ ]]; then 31 | # Create a new directory for this tag_name and copy the aggregated typedocs there, if it's a tagged release. 32 | printf "\n>>>>> Copying aggregated typedocs to new tagged-release directory: %s\n" ${BRANCH_NAME} 33 | rm -rf docs/${TAG_NAME} 34 | mkdir -p docs/${TAG_NAME} 35 | cp -rf ../apidocs/* docs/${TAG_NAME} 36 | 37 | printf "\n>>>>> Generating gh-pages index.html...\n" 38 | ../scripts/typedoc/generate-index-html.sh > index.html 39 | 40 | # Update the 'latest' symlink to point to this directory 41 | pushd docs 42 | rm -f latest 43 | ln -s ./${TAG_NAME} latest 44 | printf "\n>>>>> Updated 'docs/latest' symlink:\n" 45 | ls -l latest 46 | popd 47 | 48 | printf "\n>>>>> Committing new typedoc...\n" 49 | git add -f . 50 | git commit -m "Typedoc for release ${TAG_NAME} (${GIT_COMMIT})" 51 | git push -f origin gh-pages 52 | 53 | popd 54 | 55 | printf "\n>>>>> Published typedoc for release build: repo=%s branch=%s build_num=%s job_name=%s\n" ${GIT_REPO} ${BRANCH_NAME} ${BUILD_NUMBER} ${JOB_NAME} 56 | else 57 | printf "\n>>>>> Failed to publish typedoc for release build: tag_name=%s does not follow Semantic Version pattern\n" ${TAG_NAME} 58 | exit 1 59 | fi 60 | -------------------------------------------------------------------------------- /test/examples/output/CreateDbAndDoc.txt: -------------------------------------------------------------------------------- 1 | "orders" database created. 2 | You have created the document: 3 | { 4 | "_id": "example", 5 | "name": "Bob Smith", 6 | "joined": "2019-01-24T10:42:59.000Z", 7 | "_rev": "1-1b403633540686aa32d013fda9041a5d" 8 | } -------------------------------------------------------------------------------- /test/examples/output/DeleteDoc.txt: -------------------------------------------------------------------------------- 1 | You have deleted the document. -------------------------------------------------------------------------------- /test/examples/output/GetInfoFromExistingDatabase.txt: -------------------------------------------------------------------------------- 1 | Server version 3.2.1 2 | Document count in "orders" database is 1. 3 | Document retrieved from database: 4 | { 5 | "_id": "example", 6 | "_rev": "1-1b403633540686aa32d013fda9041a5d", 7 | "name": "Bob Smith", 8 | "joined": "2019-01-24T10:42:59.000Z" 9 | } -------------------------------------------------------------------------------- /test/examples/output/UpdateDoc.txt: -------------------------------------------------------------------------------- 1 | You have updated the document: 2 | { 3 | "_id": "example", 4 | "_rev": "2-4e2178e85cffb32d38ba4e451f6ca376", 5 | "name": "Bob Smith", 6 | "address": "19 Front Street, Darlington, DL5 1TY" 7 | } -------------------------------------------------------------------------------- /test/examples/output/UpdateDoc2.txt: -------------------------------------------------------------------------------- 1 | You have updated the document: 2 | { 3 | "_id": "example", 4 | "_rev": "3-4f1787d7a0520f825bd36822d7be627a", 5 | "name": "Bob Smith", 6 | "address": "19 Front Street, Darlington, DL5 1TY" 7 | } -------------------------------------------------------------------------------- /test/examples/src/features/js/initialize.js: -------------------------------------------------------------------------------- 1 | /** 2 | * © Copyright IBM Corporation 2023. 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 | const { ChangesFollower, CloudantV1 } = require('../../../../../index'); 17 | 18 | const client = CloudantV1.newInstance(); 19 | const changesParams = { 20 | db: 'example', // Required: the database name. 21 | limit: 100, // Optional: return only 100 changes (including duplicates). 22 | since: '3-g1AG3...' // Optional: start from this sequence ID (e.g. with a value read from persistent storage). 23 | }; 24 | const changesFollower = new ChangesFollower( 25 | client, // Required: the Cloudant service client instance. 26 | changesParams, // Required: changes feed configuration options dict. 27 | 10000 // Optional: suppress transient errors for at least 10 seconds before terminating. 28 | ); 29 | -------------------------------------------------------------------------------- /test/examples/src/features/js/start.js: -------------------------------------------------------------------------------- 1 | /** 2 | * © Copyright IBM Corporation 2023. 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 | const { ChangesFollower, CloudantV1 } = require('../../../../../index'); 17 | 18 | const client = CloudantV1.newInstance(); 19 | const changesParams = { 20 | db: 'example' 21 | }; 22 | const changesFollower = new ChangesFollower(client, changesParams); 23 | const changesItemsStream = changesFollower.start(); 24 | // Create for-async-loop or pipeline to begin the flow of changes 25 | // e.g. pipeline(changesItemsStream, destinationStream).then(() => { ... }).catch((err) => { ... }); 26 | -------------------------------------------------------------------------------- /test/examples/src/features/js/startAndProcess.js: -------------------------------------------------------------------------------- 1 | /** 2 | * © Copyright IBM Corporation 2023. 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 | const { ChangesFollower, CloudantV1 } = require('../../../../../index'); 17 | const { Writable } = require('node:stream'); 18 | const { pipeline } = require('node:stream/promises'); 19 | 20 | const client = CloudantV1.newInstance(); 21 | // Start from a previously persisted seq 22 | // Normally this would be read by the app from persistent storage 23 | // e.g. previouslyPersistedSeq = yourAppPersistenceReadFunc() 24 | const previouslyPersistedSeq = '3-g1AG3...'; 25 | const changesParams = { 26 | db: 'example', 27 | since: previouslyPersistedSeq 28 | }; 29 | const changesFollower = new ChangesFollower(client, changesParams); 30 | const changesItemsStream = changesFollower.start(); 31 | 32 | const destinationStream = new Writable({ 33 | objectMode: true, 34 | write(changesItem, _, callback) { 35 | // do something with change item 36 | console.log(changesItem.id); 37 | for (const change of changesItem.changes) { 38 | console.log(change.rev); 39 | } 40 | // when change item processing is complete app can store seq 41 | const seq = changesItem.seq; 42 | // write seq to persistent storage for use as since if required to resume later 43 | // e.g. yourAppPersistenceWriteFunc() 44 | callback(); 45 | } 46 | }); 47 | 48 | // A pipeline to keep processing changes until the follower is stopped or some other stop condition is reached 49 | pipeline(changesItemsStream, destinationStream) 50 | .then(() => { 51 | console.log('Stopped'); 52 | }) 53 | .catch((err) => { 54 | console.log(err); 55 | }); 56 | -------------------------------------------------------------------------------- /test/examples/src/features/js/startOneOff.js: -------------------------------------------------------------------------------- 1 | /** 2 | * © Copyright IBM Corporation 2023. 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 | const { ChangesFollower, CloudantV1 } = require('../../../../../index'); 17 | 18 | const client = CloudantV1.newInstance(); 19 | const changesParams = { 20 | db: 'example' 21 | }; 22 | const changesFollower = new ChangesFollower(client, changesParams); 23 | const changesItemsStream = changesFollower.startOneOff(); 24 | // Create for-async-loop or pipeline to begin the flow of changes 25 | // e.g. pipeline(changesItemsStream, destinationStream).then(() => { ... }).catch((err) => { ... }); 26 | -------------------------------------------------------------------------------- /test/examples/src/features/js/startOneOffAndProcess.js: -------------------------------------------------------------------------------- 1 | /** 2 | * © Copyright IBM Corporation 2023. 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 | const { ChangesFollower, CloudantV1 } = require('../../../../../index'); 17 | const { Writable } = require('node:stream'); 18 | const { pipeline } = require('node:stream/promises'); 19 | 20 | const client = CloudantV1.newInstance(); 21 | // Start from a previously persisted seq 22 | // Normally this would be read by the app from persistent storage 23 | // e.g. previouslyPersistedSeq = yourAppPersistenceReadFunc() 24 | const previouslyPersistedSeq = '3-g1AG3...'; 25 | const changesParams = { 26 | db: 'example', 27 | since: previouslyPersistedSeq 28 | }; 29 | const changesFollower = new ChangesFollower(client, changesParams); 30 | const changesItemsStream = changesFollower.startOneOff(); 31 | 32 | const destinationStream = new Writable({ 33 | objectMode: true, 34 | write(changesItem, _, callback) { 35 | // do something with change item 36 | console.log(changesItem.id); 37 | for (const change of changesItem.changes) { 38 | console.log(change.rev); 39 | } 40 | // when change item processing is complete app can store seq 41 | const seq = changesItem.seq; 42 | // write seq to persistent storage for use as since if required to resume later 43 | // e.g. yourAppPersistenceWriteFunc() 44 | callback(); 45 | } 46 | }); 47 | 48 | pipeline(changesItemsStream, destinationStream) 49 | .then(() => { 50 | console.log('All changes done'); 51 | }) 52 | .catch((err) => { 53 | console.log(err); 54 | }); 55 | 56 | // use for-async-loop feature for stream 57 | /* 58 | getChangesFromFollower(changesItemsStream); 59 | async function getChangesFromFollower(changesItemsStream) { 60 | for await (const changesItem of changesItemsStream) { 61 | // do something with change item 62 | // write seq to persistent storage for use as since 63 | console.log(changesItem.id); 64 | for (const change of changesItem.changes) { 65 | console.log(change.rev); 66 | } 67 | // when change item processing is complete app can store seq 68 | seq = changesItem.seq; 69 | // write seq to persistent storage for use as since if required to resume later 70 | // e.g. yourAppPersistenceWriteFunc(); 71 | } 72 | } 73 | */ 74 | -------------------------------------------------------------------------------- /test/examples/src/features/js/stop.js: -------------------------------------------------------------------------------- 1 | /** 2 | * © Copyright IBM Corporation 2023. 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 | const { ChangesFollower, CloudantV1 } = require('../../../../../index'); 17 | const { Writable } = require('node:stream'); 18 | const { pipeline } = require('node:stream/promises'); 19 | 20 | const client = CloudantV1.newInstance(); 21 | const changesParams = { 22 | db: 'example' 23 | }; 24 | const changesFollower = new ChangesFollower(client, changesParams); 25 | const changesItemsStream = changesFollower.start(); 26 | 27 | const destinationStream = new Writable({ 28 | objectMode: true, 29 | write(changesItem, _, callback) { 30 | // Option 1: call stop after some condition 31 | // Note that at least one item 32 | // must be returned to reach to this point. 33 | // Additional changes may be processed before the iterator stops. 34 | changesFollower.stop(); 35 | callback(); 36 | } 37 | }); 38 | 39 | pipeline(changesItemsStream, destinationStream) 40 | .then(() =>{ 41 | console.log('Stopped'); 42 | }) 43 | .catch((err) => { 44 | console.log(err); 45 | }); 46 | 47 | // Option 2: call stop method when you want to end the continuous loop from 48 | // outside the pipeline. 49 | // Normally the call would be made from some other application function 50 | // executing later. 51 | // For example, stop the changesFollower after 1 minute of listening for changes 52 | setTimeout(() => { 53 | changesFollower.stop(); 54 | }, 60000); 55 | -------------------------------------------------------------------------------- /test/examples/src/features/ts/initialize.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * © Copyright IBM Corporation 2023. 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 | import { ChangesFollower, CloudantV1 } from '../../../../../index'; 17 | import { PostChangesParams } from '../../../../../cloudant/v1'; 18 | 19 | const client = CloudantV1.newInstance({}); 20 | const changesParams: PostChangesParams = { 21 | db: 'example', // Required: the database name. 22 | limit: 100, // Optional: return only 100 changes (including duplicates). 23 | since: '3-g1AG3...' // Optional: start from this sequence ID (e.g. with a value read from persistent storage). 24 | }; 25 | const errorTolerance: number = 10000; // 10 second duration to suppress transient errors 26 | const changesFollower: ChangesFollower = new ChangesFollower( 27 | client, // Required: the Cloudant service client instance. 28 | changesParams, // Required: changes feed configuration options dict. 29 | 10000 // Optional: suppress transient errors for at least 10 seconds before terminating. 30 | ); 31 | -------------------------------------------------------------------------------- /test/examples/src/features/ts/start.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * © Copyright IBM Corporation 2023. 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 | import { ChangesFollower, CloudantV1, Stream } from '../../../../../index'; 17 | import { ChangesResultItem, PostChangesParams } from '../../../../../cloudant/v1'; 18 | 19 | const client = CloudantV1.newInstance({}); 20 | const changesParams: PostChangesParams = { 21 | db: 'example' 22 | }; 23 | const changesFollower: ChangesFollower = new ChangesFollower(client, changesParams); 24 | const changesItemsStream: Stream = changesFollower.start(); 25 | // Create for-async-loop or pipeline to begin the flow of changes 26 | // e.g. pipeline(changesItemsStream, destinationStream).then(() => { ... }).catch((err) => { ... }); 27 | -------------------------------------------------------------------------------- /test/examples/src/features/ts/startAndProcess.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * © Copyright IBM Corporation 2023. 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 | import { ChangesFollower, CloudantV1, Stream } from '../../../../../index'; 17 | import { ChangesResultItem, PostChangesParams } from '../../../../../cloudant/v1'; 18 | import { Writable } from 'node:stream'; 19 | import { pipeline } from 'node:stream/promises'; 20 | 21 | const client = CloudantV1.newInstance({}); 22 | // Start from a previously persisted seq 23 | // Normally this would be read by the app from persistent storage 24 | // e.g. previouslyPersistedSeq = yourAppPersistenceReadFunc() 25 | const previouslyPersistedSeq = '3-g1AG3...'; 26 | const changesParams: PostChangesParams = { 27 | db: 'example', 28 | since: previouslyPersistedSeq 29 | }; 30 | const changesFollower = new ChangesFollower(client, changesParams); 31 | const changesItemsStream: Stream = changesFollower.start(); 32 | 33 | const destinationStream = new Writable({ 34 | objectMode: true, 35 | write(changesItem: CloudantV1.ChangesResultItem, _, callback) { 36 | // do something with change item 37 | console.log(changesItem.id); 38 | for (const change of changesItem.changes) { 39 | console.log(change.rev); 40 | } 41 | // when change item processing is complete app can store seq 42 | const seq = changesItem.seq; 43 | // write seq to persistent storage for use as since if required to resume later 44 | // e.g. yourAppPersistenceWriteFunc() 45 | callback(); 46 | } 47 | }); 48 | 49 | // A pipeline to keep processing changes until the follower is stopped or some other stop condition is reached 50 | pipeline(changesItemsStream, destinationStream) 51 | .then(() => { 52 | console.log('Stopped'); 53 | }) 54 | .catch((err) => { 55 | console.log(err); 56 | }); 57 | -------------------------------------------------------------------------------- /test/examples/src/features/ts/startOneOff.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * © Copyright IBM Corporation 2023. 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 | import { ChangesFollower, CloudantV1, Stream } from '../../../../../index'; 17 | import { ChangesResultItem, PostChangesParams } from '../../../../../cloudant/v1'; 18 | 19 | const client = CloudantV1.newInstance({}); 20 | const changesParams: PostChangesParams = { 21 | db: 'example' 22 | }; 23 | const changesFollower: ChangesFollower = new ChangesFollower(client, changesParams); 24 | const changesItemsStream: Stream = changesFollower.startOneOff(); 25 | // Create for-async-loop or pipeline to begin the flow of changes 26 | // e.g. pipeline(changesItemsStream, destinationStream).then(() => { ... }).catch((err) => { ... }); 27 | -------------------------------------------------------------------------------- /test/examples/src/features/ts/startOneOffAndProcess.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * © Copyright IBM Corporation 2023. 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 | import { ChangesFollower, CloudantV1, Stream } from '../../../../../index'; 17 | import { ChangesResultItem, PostChangesParams } from '../../../../../cloudant/v1'; 18 | import { Writable } from 'node:stream'; 19 | import { pipeline } from 'node:stream/promises'; 20 | 21 | const client = CloudantV1.newInstance({}); 22 | // Start from a previously persisted seq 23 | // Normally this would be read by the app from persistent storage 24 | // e.g. previouslyPersistedSeq = yourAppPersistenceReadFunc() 25 | const previouslyPersistedSeq = '3-g1AG3...'; 26 | const changesParams: PostChangesParams = { 27 | db: 'example', 28 | since: previouslyPersistedSeq 29 | }; 30 | const changesFollower: ChangesFollower = new ChangesFollower(client, changesParams); 31 | const changesItemsStream: Stream = changesFollower.startOneOff(); 32 | 33 | const destinationStream = new Writable({ 34 | objectMode: true, 35 | write(changesItem: CloudantV1.ChangesResultItem, _, callback) { 36 | // do something with change item 37 | console.log(changesItem.id); 38 | for (const change of changesItem.changes) { 39 | console.log(change.rev); 40 | } 41 | // when change item processing is complete app can store seq 42 | const seq = changesItem.seq; 43 | // write seq to persistent storage for use as since if required to resume later 44 | // e.g. yourAppPersistenceWriteFunc() 45 | callback(); 46 | } 47 | }); 48 | 49 | pipeline(changesItemsStream, destinationStream) 50 | .then(() => { 51 | console.log('All changes done'); 52 | }) 53 | .catch((err) => { 54 | console.log(err); 55 | }); 56 | 57 | // use for-async-loop feature for stream 58 | /* 59 | getChangesFromFollower(changesItemsStream); 60 | async function getChangesFromFollower(changesItemsStream: Stream) { 61 | for await (const changesItem of changesItemsStream) { 62 | // do something with change item 63 | // write seq to persistent storage for use as since 64 | console.log(changesItem.id); 65 | for (const change of changesItem.changes) { 66 | console.log(change.rev); 67 | } 68 | // when change item processing is complete app can store seq 69 | seq = changesItem.seq; 70 | // write seq to persistent storage for use as since if required to resume later 71 | // e.g. yourAppPersistenceWriteFunc(); 72 | } 73 | } 74 | */ 75 | -------------------------------------------------------------------------------- /test/examples/src/features/ts/stop.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * © Copyright IBM Corporation 2023. 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 | import { ChangesFollower, CloudantV1, Stream } from '../../../../../index'; 17 | import { ChangesResultItem, PostChangesParams } from '../../../../../cloudant/v1'; 18 | import { Writable } from 'node:stream'; 19 | import { pipeline } from 'node:stream/promises'; 20 | 21 | 22 | const client = CloudantV1.newInstance({}); 23 | const changesParams: PostChangesParams = { 24 | db: 'example' 25 | }; 26 | const changesFollower: ChangesFollower = new ChangesFollower(client, changesParams); 27 | const changesItemsStream: Stream = changesFollower.start(); 28 | 29 | const destinationStream = new Writable({ 30 | objectMode: true, 31 | write(changesItem: CloudantV1.ChangesResultItem, _, callback) { 32 | // Option 1: call stop after some condition 33 | // Note that at least one item 34 | // must be returned to reach to this point. 35 | // Additional changes may be processed before the iterator stops. 36 | changesFollower.stop(); 37 | callback(); 38 | } 39 | }); 40 | 41 | pipeline(changesItemsStream, destinationStream) 42 | .then(() => { 43 | console.log('Stopped'); 44 | }) 45 | .catch((err) => { 46 | console.log(err); 47 | }); 48 | 49 | // Option 2: call stop method when you want to end the continuous loop from 50 | // outside the pipeline. 51 | // Normally the call would be made from some other application function 52 | // executing later. 53 | // For example, stop the changesFollower after 1 minute of listening for changes 54 | setTimeout(() => { 55 | changesFollower.stop(); 56 | }, 60000); 57 | -------------------------------------------------------------------------------- /test/examples/src/js/CreateDbAndDoc.js: -------------------------------------------------------------------------------- 1 | /** 2 | * © Copyright IBM Corporation 2020, 2022. 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 | /* eslint-disable no-console */ 18 | 19 | const { CloudantV1 } = require('../../../../index.ts'); 20 | 21 | // when you change this file, please run test/examples/src/js/CreateOutputs.js so that the output files are updated 22 | 23 | const createDbAndDoc = async () => { 24 | // 1. Create a client with `CLOUDANT` default service name ==================== 25 | const client = CloudantV1.newInstance({}); 26 | 27 | // 2. Create a database ======================================================= 28 | const exampleDbName = 'orders'; 29 | 30 | // Try to create database if it doesn't exist 31 | try { 32 | const putDatabaseResult = ( 33 | await client.putDatabase({ 34 | db: exampleDbName, 35 | }) 36 | ).result; 37 | if (putDatabaseResult.ok) { 38 | console.log(`"${exampleDbName}" database created.`); 39 | } 40 | } catch (err) { 41 | if (err.code === 412) { 42 | console.log( 43 | `Cannot create "${exampleDbName}" database, it already exists.` 44 | ); 45 | } 46 | } 47 | 48 | // 3. Create a document ======================================================= 49 | // Create a document object with "example" id 50 | const exampleDocId = 'example'; 51 | 52 | // Setting `_id` for the document is optional when "postDocument" function is used for CREATE. 53 | // When `_id` is not provided the server will generate one for your document. 54 | const exampleDocument = { _id: exampleDocId }; 55 | 56 | // Add "name" and "joined" fields to the document 57 | exampleDocument['name'] = 'Bob Smith'; 58 | exampleDocument.joined = '2019-01-24T10:42:59.000Z'; 59 | 60 | // Save the document in the database with "postDocument" function 61 | const createDocumentResponse = await client.postDocument({ 62 | db: exampleDbName, 63 | document: exampleDocument, 64 | }); 65 | 66 | // ========================================================================== 67 | // Note: saving the document can also be done with the "putDocument" 68 | // function. In this case `docId` is required for a CREATE operation: 69 | /* const createDocumentResponse = await client.putDocument({ 70 | db: exampleDbName, 71 | docId: exampleDocId, 72 | document: exampleDocument, 73 | }); */ 74 | // ========================================================================== 75 | 76 | // Keeping track of the revision number of the document object 77 | // is necessary for further UPDATE/DELETE operations: 78 | exampleDocument._rev = createDocumentResponse.result.rev; 79 | console.log( 80 | 'You have created the document:\n' + 81 | JSON.stringify(exampleDocument, null, 2) 82 | ); 83 | }; 84 | 85 | if (require.main === module) { 86 | createDbAndDoc(); 87 | } 88 | 89 | module.exports = { createDbAndDoc }; 90 | -------------------------------------------------------------------------------- /test/examples/src/js/CreateOutputs.js: -------------------------------------------------------------------------------- 1 | /** 2 | * © Copyright IBM Corporation 2020. 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 | /** 18 | * Run this script from the root folder to regenerate outputs of example codes. 19 | */ 20 | 21 | const fs = require('fs'); 22 | 23 | const { 24 | getInfoFromExistingDatabase, 25 | } = require('./GetInfoFromExistingDatabase.js'); 26 | const { createDbAndDoc } = require('./CreateDbAndDoc.js'); 27 | const { updateDoc } = require('./UpdateDoc.js'); 28 | const { deleteDoc } = require('./DeleteDoc.js'); 29 | 30 | let consoleOutput = ''; // flush consoleOutput 31 | const mockedLog = (output) => { 32 | consoleOutput = `${consoleOutput}\n${output}`; 33 | }; 34 | global.console.log = mockedLog; // Mock console.log 35 | 36 | const run = async () => { 37 | await createDbAndDoc(); 38 | 39 | fs.writeFile( 40 | 'test/examples/output/CreateDbAndDoc.txt', 41 | consoleOutput.trim(), 42 | (err) => { 43 | if (err) throw err; 44 | } 45 | ); 46 | 47 | consoleOutput = ''; // flush consoleOutput 48 | 49 | await getInfoFromExistingDatabase(); 50 | 51 | fs.writeFile( 52 | 'test/examples/output/GetInfoFromExistingDatabase.txt', 53 | consoleOutput.trim(), 54 | (err) => { 55 | if (err) throw err; 56 | } 57 | ); 58 | 59 | consoleOutput = ''; // flush consoleOutput 60 | 61 | await updateDoc(); 62 | 63 | fs.writeFile( 64 | 'test/examples/output/UpdateDoc.txt', 65 | consoleOutput.trim(), 66 | (err) => { 67 | if (err) throw err; 68 | } 69 | ); 70 | 71 | consoleOutput = ''; // flush consoleOutput 72 | 73 | await deleteDoc(); 74 | 75 | fs.writeFile( 76 | 'test/examples/output/DeleteDoc.txt', 77 | consoleOutput.trim(), 78 | (err) => { 79 | if (err) throw err; 80 | } 81 | ); 82 | }; 83 | 84 | run(); 85 | -------------------------------------------------------------------------------- /test/examples/src/js/DeleteDoc.js: -------------------------------------------------------------------------------- 1 | /** 2 | * © Copyright IBM Corporation 2020, 2022. 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 | /* eslint-disable no-console */ 18 | 19 | const { CloudantV1 } = require('../../../../index.ts'); 20 | 21 | // when you change this file, please run test/examples/src/js/CreateOutputs.js so that the output files are updated 22 | 23 | const deleteDoc = async () => { 24 | // 1. Create a client with `CLOUDANT` default service name ==================== 25 | const client = CloudantV1.newInstance({}); 26 | 27 | // 2. Delete the document ===================================================== 28 | // Set the options to get the document out of the database if it exists 29 | const exampleDbName = 'orders'; 30 | const exampleDocId = 'example'; 31 | 32 | // Try to get the document if it previously existed in the database 33 | try { 34 | const document = ( 35 | await client.getDocument({ 36 | docId: exampleDocId, 37 | db: exampleDbName, 38 | }) 39 | ).result; 40 | 41 | await client.deleteDocument({ 42 | db: exampleDbName, 43 | docId: document._id, // `docId` is required for DELETE 44 | rev: document._rev, // `rev` is required for DELETE 45 | }); 46 | console.log('You have deleted the document.'); 47 | } catch (err) { 48 | if (err.code === 404) { 49 | console.log( 50 | `Cannot delete document because either "${exampleDbName}" database or the "example" ` + 51 | `document was not found.` 52 | ); 53 | } 54 | } 55 | }; 56 | 57 | if (require.main === module) { 58 | deleteDoc(); 59 | } 60 | 61 | module.exports = { deleteDoc }; 62 | -------------------------------------------------------------------------------- /test/examples/src/js/GetInfoFromExistingDatabase.js: -------------------------------------------------------------------------------- 1 | /** 2 | * © Copyright IBM Corporation 2020, 2022. 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 | /* eslint-disable no-console */ 18 | 19 | const { CloudantV1 } = require('../../../../index.ts'); 20 | 21 | // when you change this file, please run test/examples/src/js/CreateOutputs.js so that the output files are updated 22 | 23 | const getInfoFromExistingDatabase = async () => { 24 | // 1. Create a client with `CLOUDANT` default service name =================== 25 | const client = CloudantV1.newInstance({}); 26 | 27 | // 2. Get server information ================================================== 28 | // call service without parameters: 29 | const { version } = (await client.getServerInformation()).result; 30 | console.log(`Server version ${version}`); 31 | 32 | // 3. Get database information for "orders" ================================= 33 | const dbName = 'orders'; 34 | 35 | // call service with embedded parameters: 36 | const dbInfo = await client.getDatabaseInformation({ db: dbName }); 37 | const documentCount = dbInfo.result.docCount; 38 | const dbNameResult = dbInfo.result.dbName; 39 | 40 | // 4. Show document count in database ========================================= 41 | console.log( 42 | `Document count in "${dbNameResult}" database is ${documentCount}.` 43 | ); 44 | 45 | // 5. Get "example" document out of the database by document id =================== 46 | const getDocParams = { db: dbName, docId: 'example' }; 47 | 48 | // call service with predefined parameters: 49 | const documentExample = await client.getDocument(getDocParams); 50 | 51 | // result object is defined as a Document here: 52 | const { result } = documentExample; 53 | 54 | console.log( 55 | `Document retrieved from database:\n${JSON.stringify(result, null, 2)}` 56 | ); 57 | }; 58 | 59 | if (require.main === module) { 60 | getInfoFromExistingDatabase(); 61 | } 62 | 63 | module.exports = { getInfoFromExistingDatabase }; 64 | -------------------------------------------------------------------------------- /test/examples/src/js/UpdateDoc.js: -------------------------------------------------------------------------------- 1 | /** 2 | * © Copyright IBM Corporation 2020, 2022. 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 | /* eslint-disable no-console */ 18 | 19 | const { CloudantV1 } = require('../../../../index.ts'); 20 | 21 | // when you change this file, please run test/examples/src/js/CreateOutputs.js so that the output files are updated 22 | 23 | const updateDoc = async () => { 24 | // 1. Create a client with `CLOUDANT` default service name ==================== 25 | const client = CloudantV1.newInstance({}); 26 | // 2. Update the document ===================================================== 27 | // Set the options to get the document out of the database if it exists 28 | const exampleDbName = 'orders'; 29 | 30 | // Try to get the document if it previously existed in the database 31 | try { 32 | const document = ( 33 | await client.getDocument({ 34 | docId: 'example', 35 | db: exampleDbName, 36 | }) 37 | ).result; 38 | 39 | // ========================================================================== 40 | // Note: for response byte stream use: 41 | /* 42 | const documentAsByteStream = ( 43 | await client.getDocumentAsStream({ 44 | docId: 'example', 45 | db: exampleDbName, 46 | }) 47 | ).result; 48 | */ 49 | // ========================================================================== 50 | 51 | // Add Bob Smith's address to the document 52 | document.address = '19 Front Street, Darlington, DL5 1TY'; 53 | 54 | // Remove the joined property from document object 55 | delete document['joined']; 56 | 57 | // Keeping track of the latest revision number of the document object 58 | // is necessary for further UPDATE/DELETE operations: 59 | document._rev = ( 60 | await client.postDocument({ 61 | db: exampleDbName, 62 | document, // _id and _rev MUST be inside the document object 63 | }) 64 | ).result.rev; 65 | 66 | // ========================================================================== 67 | // Note 1: for request byte stream use: 68 | /* 69 | document._rev = ( 70 | await client.postDocument({ 71 | db: exampleDbName, 72 | document: documentAsByteStream, 73 | }) 74 | ).result.rev; 75 | */ 76 | // ========================================================================== 77 | 78 | // ========================================================================== 79 | // Note 2: updating the document can also be done with the "putDocument" function. 80 | // `docId` and `rev` are required for an UPDATE operation, 81 | // but `rev` can be provided in the document object as `_rev` too: 82 | /* 83 | document._rev = ( 84 | await client.putDocument({ 85 | db: exampleDbName, 86 | docId: document._id, // docId is a required parameter 87 | rev: document._rev, 88 | document // _rev in the document object CAN replace above `rev` parameter 89 | }) 90 | ).result.rev; 91 | */ 92 | // ========================================================================== 93 | 94 | console.log( 95 | `You have updated the document:\n${JSON.stringify(document, null, 2)}` 96 | ); 97 | } catch (err) { 98 | if (err.code === 404) { 99 | console.log( 100 | `Cannot update document because either "${exampleDbName}" database or the "example" ` + 101 | `document was not found.` 102 | ); 103 | } 104 | } 105 | }; 106 | 107 | if (require.main === module) { 108 | updateDoc(); 109 | } 110 | 111 | module.exports = { updateDoc }; 112 | -------------------------------------------------------------------------------- /test/examples/src/ts/CreateDbAndDoc.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * © Copyright IBM Corporation 2020, 2022. 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 { CloudantV1 } from '../../../../index'; 18 | 19 | interface OrderDocument extends CloudantV1.Document { 20 | name?: string; 21 | joined?: string; 22 | _id: string; 23 | _rev?: string; 24 | } 25 | 26 | // 1. Create a client with `CLOUDANT` default service name ====================== 27 | const client = CloudantV1.newInstance({}); 28 | 29 | // 2. Create a database ========================================================= 30 | const exampleDbName = 'orders'; 31 | 32 | // Try to create database if it doesn't exist 33 | const createDb = client 34 | .putDatabase({ db: exampleDbName }) 35 | .then((putDatabaseResult) => { 36 | if (putDatabaseResult.result.ok) { 37 | console.log(`"${exampleDbName}" database created."`); 38 | } 39 | }) 40 | .catch((err) => { 41 | if (err.code === 412) { 42 | console.log( 43 | `Cannot create "${exampleDbName}" database, it already exists.` 44 | ); 45 | } 46 | }); 47 | 48 | // 3. Create a document ========================================================= 49 | // Create a document object with "example" id 50 | const exampleDocId = 'example'; 51 | 52 | // Setting `_id` for the document is optional when postDocument function is used for CREATE. 53 | // When `_id` is not provided the server will generate one for your document. 54 | const exampleDocument: OrderDocument = { _id: exampleDocId }; 55 | 56 | // Add "name" and "joined" fields to the document 57 | exampleDocument.name = 'Bob Smith'; 58 | exampleDocument.joined = '2019-01-24T10:42:59.000Z'; 59 | 60 | // Save the document in the database with "postDocument" function 61 | createDb.then(() => { 62 | client 63 | .postDocument({ 64 | db: exampleDbName, 65 | document: exampleDocument, 66 | }) 67 | // ========================================================================== 68 | // Note: saving the document can also be done with the "putDocument" 69 | // function. In this case `docId` is required for a CREATE operation: 70 | /* 71 | .putDocument({ 72 | db: exampleDbName, 73 | docId: exampleDocId, 74 | document: exampleDocument, 75 | }) 76 | */ 77 | // ========================================================================== 78 | .then((createDocumentResponse) => { 79 | // Keeping track of the revision number of the document object 80 | // is necessary for further UPDATE/DELETE operations: 81 | exampleDocument._rev = createDocumentResponse.result.rev; 82 | console.log( 83 | 'You have created the document:\n' + 84 | JSON.stringify(exampleDocument, null, 2) 85 | ); 86 | }); 87 | }); 88 | -------------------------------------------------------------------------------- /test/examples/src/ts/DeleteDoc.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * © Copyright IBM Corporation 2020, 2022. 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 { CloudantV1 } from '../../../../index'; 18 | 19 | interface OrderDocument extends CloudantV1.Document { 20 | name?: string; 21 | address?: string; 22 | joined?: string; 23 | _id?: string; 24 | _rev?: string; 25 | } 26 | 27 | // 1. Create a client with `CLOUDANT` default service name ====================== 28 | const client = CloudantV1.newInstance({}); 29 | 30 | // 2. Delete the document ======================================================= 31 | // Set the options to get the document out of the database if it exists 32 | const exampleDbName = 'orders'; 33 | const exampleDocId = 'example'; 34 | 35 | // Try to get the document if it previously existed in the database 36 | const getDocParams: CloudantV1.GetDocumentParams = { 37 | docId: exampleDocId, 38 | db: exampleDbName, 39 | }; 40 | 41 | client 42 | .getDocument(getDocParams) 43 | .then((docResult) => { 44 | const document: OrderDocument = docResult.result; 45 | 46 | client 47 | .deleteDocument({ 48 | db: exampleDbName, 49 | docId: document._id, // `docId` is required for DELETE 50 | rev: document._rev, // `rev` is required for DELETE 51 | }) 52 | .then(() => { 53 | console.log('You have deleted the document.'); 54 | }); 55 | }) 56 | .catch((err) => { 57 | if (err.code === 404) { 58 | console.log( 59 | `Cannot delete document because either "${exampleDbName}" database or the "example" ` + 60 | `document was not found.` 61 | ); 62 | } 63 | }); 64 | -------------------------------------------------------------------------------- /test/examples/src/ts/GetInfoFromExistingDatabase.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * © Copyright IBM Corporation 2020, 2022. 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 { CloudantV1 } from '../../../../index'; 18 | 19 | // 1. Create a client with `CLOUDANT` default service name ===================== 20 | const client = CloudantV1.newInstance({}); 21 | 22 | // 2. Get server information ==================================================== 23 | // call service without parameters: 24 | client.getServerInformation().then((serverInformation) => { 25 | const { version } = serverInformation.result; 26 | console.log(`Server version ${version}`); 27 | }); 28 | 29 | // 3. Get database information for "orders" =================================== 30 | const dbName = 'orders'; 31 | 32 | // call service with embedded parameters: 33 | client.getDatabaseInformation({ db: dbName }).then((dbInfo) => { 34 | const documentCount = dbInfo.result.docCount; 35 | const dbNameResult = dbInfo.result.dbName; 36 | 37 | // 4. Show document count in database ========================================= 38 | console.log( 39 | `Document count in "${dbNameResult}" database is ${documentCount}.` 40 | ); 41 | }); 42 | 43 | // 5. Get "example" document out of the database by document id ===================== 44 | const getDocParams: CloudantV1.GetDocumentParams = { 45 | db: dbName, 46 | docId: 'example', 47 | }; 48 | 49 | // call service with predefined parameters: 50 | client.getDocument(getDocParams).then((documentExample) => { 51 | // result object is defined as a Document here: 52 | const { result } = documentExample; 53 | console.log( 54 | `Document retrieved from database:\n${JSON.stringify(result, null, 2)}` 55 | ); 56 | }); 57 | -------------------------------------------------------------------------------- /test/examples/src/ts/UpdateDoc.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * © Copyright IBM Corporation 2020, 2022. 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 { CloudantV1 } from '../../../../index'; 18 | 19 | interface OrderDocument extends CloudantV1.Document { 20 | address?: string; 21 | joined?: string; 22 | _id?: string; 23 | _rev?: string; 24 | } 25 | 26 | // 1. Create a client with `CLOUDANT` default service name ====================== 27 | const client = CloudantV1.newInstance({}); 28 | // 2. Update the document ======================================================= 29 | // Set the options to get the document out of the database if it exists 30 | const exampleDbName = 'orders'; 31 | 32 | // Try to get the document if it previously existed in the database 33 | const getDocParams: CloudantV1.GetDocumentParams = { 34 | docId: 'example', 35 | db: exampleDbName, 36 | }; 37 | 38 | // ============================================================================== 39 | // Note : for response byte stream use: 40 | /* 41 | const getdocAsStreamParam: CloudantV1.GetDocumentAsStreamParams = { 42 | docId: 'example', 43 | db: exampleDbName, 44 | }; 45 | client 46 | .getDocumentAsStream(getdocAsStreamParam) 47 | .then((documentAsByteStream) => {...}); 48 | */ 49 | // ============================================================================== 50 | 51 | client 52 | .getDocument(getDocParams) 53 | .then((docResult) => { 54 | // using OrderDocument on getDocument result: 55 | const document: OrderDocument = docResult.result; 56 | 57 | // Add Bob Smith's address to the document 58 | document.address = '19 Front Street, Darlington, DL5 1TY'; 59 | 60 | // Remove the joined property from document object 61 | delete document.joined; 62 | 63 | // Update the document in the database 64 | client 65 | .postDocument({ db: exampleDbName, document }) 66 | // ======================================================================== 67 | // Note 1: for request byte stream use: 68 | // .postDocument( 69 | // {db: exampleDbName, document: documentAsByteStream} 70 | // ) 71 | // ======================================================================== 72 | 73 | // ======================================================================== 74 | // Note 2: updating the document can also be done with the "putDocument" function. 75 | // `docId` and `rev` are required for an UPDATE operation, 76 | // but `rev` can be provided in the document object as `_rev` too: 77 | /* 78 | .putDocument({ 79 | db: exampleDbName, 80 | docId: document._id, // docId is a required parameter 81 | rev: document._rev, 82 | document, // _rev in the document object CAN replace above `rev` parameter 83 | }) 84 | */ 85 | // ======================================================================== 86 | .then((res) => { 87 | // Keeping track of the latest revision number of the document object 88 | // is necessary for further UPDATE/DELETE operations: 89 | document._rev = res.result.rev; 90 | console.log( 91 | `You have updated the document:\n${JSON.stringify(document, null, 2)}` 92 | ); 93 | }); 94 | }) 95 | .catch((err) => { 96 | if (err.code === 404) { 97 | console.log( 98 | `Cannot update document because either "${exampleDbName}" database or the "example" ` + 99 | `document was not found.` 100 | ); 101 | } 102 | }); 103 | -------------------------------------------------------------------------------- /test/examples/src/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "module": "commonjs", 4 | "target": "es2015", 5 | "sourceMap": true 6 | }, 7 | "exclude": [ 8 | "./node_modules" 9 | ], 10 | "files": [ 11 | "ts/CreateDbAndDoc.ts", "ts/DeleteDoc.ts", "ts/GetInfoFromExistingDatabase.ts", "ts/UpdateDoc.ts"] 12 | } 13 | -------------------------------------------------------------------------------- /test/integration/cloudant.integration.test.js: -------------------------------------------------------------------------------- 1 | /** 2 | * © Copyright IBM Corporation 2020, 2022. 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 assert = require('assert'); 18 | const { CloudantV1 } = require('../../index.ts'); 19 | const authHelper = require('../resources/auth-helper.js'); 20 | 21 | // testcase timeout value (10s). 22 | const timeout = 10000; 23 | 24 | // Use this to retrieve test-specific config properties from your credentials file. 25 | const configFile = 'cloudant_v1.env'; 26 | // Use authHelper to skip tests if our configFile is not available 27 | // This step also sets env var IBM_CREDENTIALS_FILE= 28 | const describe = authHelper.prepareTests(configFile); 29 | 30 | const cloudant = CloudantV1.newInstance({ 31 | serviceName: 'server', 32 | }); 33 | const dbName = process.env.DATABASE_NAME || 'stores'; 34 | 35 | describe('validate', () => { 36 | jest.setTimeout(timeout); 37 | 38 | it('server information', () => 39 | cloudant.getServerInformation().then((response) => { 40 | assert.ok(response); 41 | const { result } = response; 42 | assert.ok(result); 43 | assert.ok(result.couchdb); 44 | assert.ok(result.version); 45 | })); 46 | 47 | it('db exists', () => 48 | cloudant.headDatabase({ db: dbName }).then((response) => { 49 | assert.ok(response); 50 | assert.ok(response.headers); 51 | assert.ok(Object.keys(response.headers).length > 0); 52 | })); 53 | 54 | it('all docs', () => 55 | cloudant.postAllDocs({ db: dbName }).then((response) => { 56 | assert.ok(response); 57 | const { result } = response; 58 | assert.ok(result); 59 | assert.ok(result.rows); 60 | assert.ok(result.rows.length > 0); 61 | })); 62 | 63 | it('delete invalid _design', () => 64 | cloudant 65 | .deleteDocument({ 66 | db: dbName, 67 | docId: '_design', 68 | }) 69 | .then(() => { 70 | assert.fail('should not have response'); 71 | }) 72 | .catch((err) => { 73 | expect(err.message).toBe( 74 | 'Document ID _design starts with the invalid _ character.' 75 | ); 76 | })); 77 | 78 | it('get invalid _design document ID', () => 79 | cloudant 80 | .getDocument({ 81 | db: dbName, 82 | docId: '_design', 83 | }) 84 | .then(() => { 85 | assert.fail('should not have response'); 86 | }) 87 | .catch((err) => { 88 | expect(err.message).toBe( 89 | 'Document ID _design starts with the invalid _ character.' 90 | ); 91 | })); 92 | 93 | it('get invalid _design document ID as stream', () => 94 | cloudant 95 | .getDocumentAsStream({ 96 | db: dbName, 97 | docId: '_design', 98 | }) 99 | .then(() => { 100 | assert.fail('should not have response'); 101 | }) 102 | .catch((err) => { 103 | expect(err.message).toBe( 104 | 'Document ID _design starts with the invalid _ character.' 105 | ); 106 | })); 107 | 108 | it('get invalid _att1 attachment name', () => 109 | cloudant 110 | .getAttachment({ 111 | db: dbName, 112 | docId: 'doc1', 113 | attachmentName: '_att1', 114 | }) 115 | .then(() => { 116 | assert.fail('should not have response'); 117 | }) 118 | .catch((err) => { 119 | expect(err.message).toBe( 120 | 'Attachment name _att1 starts with the invalid _ character.' 121 | ); 122 | })); 123 | 124 | it('head invalid _design document ID', () => 125 | cloudant 126 | .headDocument({ 127 | db: dbName, 128 | docId: '_design', 129 | }) 130 | .then(() => { 131 | assert.fail('should not have response'); 132 | }) 133 | .catch((err) => { 134 | expect(err.message).toBe( 135 | 'Document ID _design starts with the invalid _ character.' 136 | ); 137 | })); 138 | 139 | it('put invalid _design document ID', () => 140 | cloudant 141 | .putDocument({ db: dbName, docId: '_design', document: {} }) 142 | .then(() => { 143 | assert.fail('should not have response'); 144 | }) 145 | .catch((err) => { 146 | expect(err.message).toBe( 147 | 'Document ID _design starts with the invalid _ character.' 148 | ); 149 | })); 150 | }); 151 | -------------------------------------------------------------------------------- /test/integration/readme.integration.test.js: -------------------------------------------------------------------------------- 1 | /** 2 | * © Copyright IBM Corporation 2020, 2022. 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 | const http = require('http'); 17 | const fs = require('fs'); 18 | const sinon = require('sinon'); 19 | const { 20 | getInfoFromExistingDatabase, 21 | } = require('../examples/src/js/GetInfoFromExistingDatabase.js'); 22 | const { createDbAndDoc } = require('../examples/src/js/CreateDbAndDoc.js'); 23 | const { updateDoc } = require('../examples/src/js/UpdateDoc.js'); 24 | const { deleteDoc } = require('../examples/src/js/DeleteDoc.js'); 25 | 26 | const setAuthentication = () => { 27 | process.env.CLOUDANT_AUTH_TYPE = 'basic'; 28 | process.env.CLOUDANT_USERNAME = 'admin'; 29 | process.env.CLOUDANT_PASSWORD = 'pass'; 30 | process.env.CLOUDANT_URL = process.env.WIREMOCK_URL; 31 | }; 32 | 33 | describe('Readme integration tests', () => { 34 | let consoleLogSpy; 35 | 36 | // jest --runInBand should be set to run the test cases in order 37 | beforeAll(() => { 38 | const wiremockURL = new URL(process.env.WIREMOCK_URL); 39 | const request = http.request({ 40 | host: wiremockURL.hostname, 41 | port: wiremockURL.port, 42 | path: '/__admin/scenarios/reset', 43 | method: 'POST', 44 | }); 45 | const p = new Promise((resolve, reject) => { 46 | request 47 | .on('response', (resp) => { 48 | resolve(resp); 49 | }) 50 | .on('error', (err) => { 51 | reject(err); 52 | }); 53 | }); 54 | request.end(); 55 | setAuthentication(); 56 | return p; // Reset Wiremock states 57 | }); 58 | 59 | beforeEach(() => { 60 | consoleLogSpy = sinon.spy(console, 'log'); 61 | }); 62 | 63 | afterEach(() => { 64 | consoleLogSpy.restore(); 65 | }); 66 | 67 | it('Create db and doc for the first time', async () => { 68 | const expectedJoinedConsoleOutput = fs.readFileSync( 69 | 'test/examples/output/CreateDbAndDoc.txt', 70 | 'utf8', 71 | (err, data) => { 72 | if (err) { 73 | return console.log(err); 74 | } 75 | return data; 76 | } 77 | ); 78 | await createDbAndDoc(); 79 | const consoleOutput = consoleLogSpy 80 | .getCalls() 81 | .map((a) => a.args[0]) 82 | .join('\n'); 83 | expect(consoleOutput).toStrictEqual(expectedJoinedConsoleOutput); 84 | }); 85 | 86 | it('Get document from orders database', async () => { 87 | const expectedJoinedConsoleOutput = fs.readFileSync( 88 | 'test/examples/output/GetInfoFromExistingDatabase.txt', 89 | 'utf8', 90 | (err, data) => { 91 | if (err) { 92 | return console.log(err); 93 | } 94 | return data; 95 | } 96 | ); 97 | await getInfoFromExistingDatabase(); 98 | const consoleOutput = consoleLogSpy 99 | .getCalls() 100 | .map((a) => a.args[0]) 101 | .join('\n'); 102 | expect(consoleOutput).toStrictEqual(expectedJoinedConsoleOutput); 103 | }); 104 | 105 | it('Update doc for the first time', async () => { 106 | const expectedJoinedConsoleOutput = fs.readFileSync( 107 | 'test/examples/output/UpdateDoc.txt', 108 | 'utf8', 109 | (err, data) => { 110 | if (err) { 111 | return console.log(err); 112 | } 113 | return data; 114 | } 115 | ); 116 | await updateDoc(); 117 | const consoleOutput = consoleLogSpy 118 | .getCalls() 119 | .map((a) => a.args[0]) 120 | .join('\n'); 121 | expect(consoleOutput).toStrictEqual(expectedJoinedConsoleOutput); 122 | }); 123 | 124 | it('Update doc for the second time', async () => { 125 | const expectedJoinedConsoleOutput = fs.readFileSync( 126 | 'test/examples/output/UpdateDoc2.txt', 127 | 'utf8', 128 | (err, data) => { 129 | if (err) { 130 | return console.log(err); 131 | } 132 | return data; 133 | } 134 | ); 135 | await updateDoc(); 136 | const consoleOutput = consoleLogSpy 137 | .getCalls() 138 | .map((a) => a.args[0]) 139 | .join('\n'); 140 | expect(consoleOutput).toStrictEqual(expectedJoinedConsoleOutput); 141 | }); 142 | 143 | it('Delete existing doc', async () => { 144 | await deleteDoc(); 145 | const consoleOutput = consoleLogSpy.getCalls().map((a) => a.args[0]); 146 | expect(consoleOutput.join('\n')).toBe('You have deleted the document.'); 147 | }); 148 | 149 | it('Delete non-existing doc', async () => { 150 | await deleteDoc(); 151 | const consoleOutput = consoleLogSpy 152 | .getCalls() 153 | .map((a) => a.args[0]) 154 | .join('\n'); 155 | expect(consoleOutput).toBe( 156 | 'Cannot delete document because either "orders" database or the "example" document was not found.' 157 | ); 158 | }); 159 | }); 160 | -------------------------------------------------------------------------------- /test/integration/timeout.integration.test.js: -------------------------------------------------------------------------------- 1 | /** 2 | * © Copyright IBM Corporation 2021. 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 assert = require('assert'); 18 | const sinon = require('sinon'); 19 | 20 | const { 21 | BasicAuthenticator, 22 | IamAuthenticator, 23 | NoAuthAuthenticator, 24 | } = require('ibm-cloud-sdk-core'); 25 | const { CloudantV1 } = require('../../index.ts'); 26 | const { CouchdbSessionAuthenticator } = require('../../index.ts'); 27 | 28 | const DEFAULT_TIMEOUT = 150000; // (2.5m=150s) 29 | const CUSTOM_TIMEOUT = 30000; // (30s) 30 | 31 | // Every method tests an authenticator. 32 | describe('Default timeout config tests', () => { 33 | // Every test case tests a timeout settings. 34 | const testCases = [ 35 | // 1. case: check default timeout value 36 | { 37 | options: {}, 38 | expTimeout: DEFAULT_TIMEOUT, 39 | }, 40 | // 2. case: check custom timeout overwrite 41 | { 42 | options: { 43 | timeout: CUSTOM_TIMEOUT, 44 | }, 45 | expTimeout: CUSTOM_TIMEOUT, 46 | }, 47 | ]; 48 | 49 | function assertBaseTimeoutOptions(myService, expTimeoutValue) { 50 | assert.ok(myService.baseOptions.timeout); 51 | assert.equal(myService.baseOptions.timeout, expTimeoutValue); 52 | } 53 | 54 | it('CloudantV1 - BasicAuth', () => { 55 | const basicAuth = new BasicAuthenticator({ 56 | username: 'user', 57 | password: 'pwd', 58 | }); 59 | testCases.forEach((tc) => { 60 | const myService = new CloudantV1({ 61 | authenticator: basicAuth, 62 | ...tc.options, 63 | }); 64 | assertBaseTimeoutOptions(myService, tc.expTimeout); 65 | }); 66 | }); 67 | 68 | it('newInstance - NoAuth', () => { 69 | const noAuth = new NoAuthAuthenticator(); 70 | testCases.forEach((tc) => { 71 | const myService = new CloudantV1({ 72 | authenticator: noAuth, 73 | ...tc.options, 74 | }); 75 | assertBaseTimeoutOptions(myService, tc.expTimeout); 76 | }); 77 | }); 78 | 79 | function assertAuthTokenTimeoutOptions(myService, expTimeoutValue) { 80 | const auth = myService.getAuthenticator(); 81 | assert.ok(auth.tokenOptions.timeout); 82 | assert.equal(auth.tokenOptions.timeout, expTimeoutValue); 83 | } 84 | 85 | it('CloudantV1 - SessionAuth', () => { 86 | const sessionAuth = new CouchdbSessionAuthenticator({ 87 | username: 'name', 88 | password: 'pwd', 89 | }); 90 | testCases.forEach((tc) => { 91 | const myService = new CloudantV1({ 92 | authenticator: sessionAuth, 93 | ...tc.options, 94 | }); 95 | assertBaseTimeoutOptions(myService, tc.expTimeout); 96 | assertAuthTokenTimeoutOptions(myService, tc.expTimeout); 97 | }); 98 | }); 99 | 100 | function assertIamAuthRequestTimeout(myService, expValue) { 101 | const auth = myService.getAuthenticator(); 102 | const spyAuth = sinon.spy(auth, 'authenticate'); 103 | 104 | // Mock out server calls 105 | const getTokenStubFn = sinon.stub(auth.tokenManager, 'getToken'); 106 | getTokenStubFn.returns( 107 | new Promise((resolve) => { 108 | resolve('apikey'); 109 | }) 110 | ); 111 | const sendRequestStubFn = sinon.stub( 112 | myService.requestWrapperInstance, 113 | 'sendRequest' 114 | ); 115 | sendRequestStubFn.returns( 116 | new Promise((resolve) => { 117 | resolve('response'); 118 | }) 119 | ); 120 | 121 | return myService.getServerInformation().then((response) => { 122 | assert.ok(response); 123 | assert.ok(spyAuth.calledOnce); 124 | assert.ok(getTokenStubFn.calledOnce); 125 | // authenticate is called with default timeout 126 | sinon.assert.calledWith(spyAuth, sinon.match.has('timeout', expValue)); 127 | // server request is called with default timeout 128 | assert.ok(sendRequestStubFn.calledOnce); 129 | sinon.assert.calledWith( 130 | sendRequestStubFn, 131 | sinon.match.hasNested('defaultOptions.timeout', expValue) 132 | ); 133 | // restore stubbed functions 134 | getTokenStubFn.restore(); 135 | sendRequestStubFn.restore(); 136 | }); 137 | } 138 | 139 | it('newInstance - IamAuth', () => { 140 | testCases.forEach((tc) => { 141 | const iamAuth = new IamAuthenticator({ 142 | apikey: 'apikey', 143 | }); 144 | const myService = new CloudantV1({ 145 | authenticator: iamAuth, 146 | ...tc.options, 147 | }); 148 | assertBaseTimeoutOptions(myService, tc.expTimeout); 149 | return assertIamAuthRequestTimeout(myService, tc.expTimeout); 150 | }); 151 | }); 152 | }); 153 | -------------------------------------------------------------------------------- /test/resources/auth-helper.js: -------------------------------------------------------------------------------- 1 | /** 2 | * © Copyright IBM Corporation 2020. 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 fs = require('fs'); 18 | const dotenv = require('dotenv'); 19 | 20 | // this variable will either hold the normal `describe` method from `jest` 21 | // or will be an alias for `describe.skip` from `jest` (skipping all tests) 22 | let describeToUse; 23 | 24 | // this variable holds the name of the file passed into prepareTests. 25 | let configFilename; 26 | 27 | let configFileExists; 28 | 29 | // `filename` is the location of the credentials file 30 | // returns the appropriate "describe" to be used for the tests. 31 | module.exports.prepareTests = (filename) => { 32 | // Save off the name of the config file. 33 | configFilename = filename; 34 | 35 | // set the filepath as an environment variable so that the 36 | // service factory can find it. 37 | process.env.IBM_CREDENTIALS_FILE = filename; 38 | 39 | configFileExists = fs.existsSync(filename); 40 | 41 | if (configFileExists) { 42 | describeToUse = describe; 43 | } else { 44 | describeToUse = describe.skip.bind(describe); 45 | describeToUse.skip = describeToUse; 46 | } 47 | 48 | return describeToUse; 49 | }; 50 | 51 | module.exports.getDescribe = () => describeToUse; 52 | 53 | // This function will load the contents of "configFilename" and 54 | // set the properties as environment variables. 55 | module.exports.loadEnv = () => { 56 | if (configFileExists) { 57 | dotenv.config({ path: configFilename }); 58 | } 59 | }; 60 | 61 | // This function will load the contents of "configFilename" and return the 62 | // property/value pairs in an object. 63 | module.exports.loadConfig = () => { 64 | if (configFileExists) { 65 | return dotenv.parse(fs.readFileSync(configFilename)); 66 | } 67 | return {}; 68 | }; 69 | -------------------------------------------------------------------------------- /test/unit/common.test.js: -------------------------------------------------------------------------------- 1 | /** 2 | * © Copyright IBM Corporation 2020, 2022. 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 common = require('../../lib/common.ts'); 18 | 19 | const { getSdkHeaders } = common; 20 | 21 | describe('Tests of Common Library', () => { 22 | describe('getSdkHeaders', () => { 23 | test('should return correct X-IBMCloud-SDK-Analytics and User-Agent headers', () => { 24 | const serviceName = 'service'; 25 | const serviceVersion = 'v1'; 26 | const operationId = 'operation1'; 27 | const headers = getSdkHeaders(serviceName, serviceVersion, operationId); 28 | expect(headers).not.toBeNull(); 29 | expect(headers['X-IBMCloud-SDK-Analytics']).toBe( 30 | `service_name=${serviceName};service_version=${serviceVersion};operation_id=${operationId}` 31 | ); 32 | expect(headers['User-Agent']).toMatch( 33 | /^cloudant-node-sdk\/[0-9.]+ \(.+\)$/ 34 | ); 35 | }); 36 | }); 37 | }); 38 | -------------------------------------------------------------------------------- /test/unit/features/changesParamsHelper.test.js: -------------------------------------------------------------------------------- 1 | /** 2 | * © Copyright IBM Corporation 2022. 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 | const { testParams } = require('./testParams.js'); 17 | const { getExpectedParams, testSeqNumber } = require('./testParams'); 18 | const { getParams } = require('./testDataProviders'); 19 | const { 20 | ChangesParamsHelper, 21 | } = require('../../../cloudant/features/changesParamsHelper.ts'); 22 | 23 | describe('Test ChangesParamsHelper', () => { 24 | it.each(getParams(true))('testCloneOptions', (params) => { 25 | const paramsName = Object.keys(params)[0]; 26 | const expected = getExpectedParams(params[paramsName]); 27 | expect(expected).not.toBeNull(); 28 | const cloned = ChangesParamsHelper.cloneParams(expected); 29 | expect(cloned).not.toBe(expected); 30 | expect(cloned).toEqual(expected); 31 | }); 32 | 33 | it.each(getParams(true))('testCloneOptionsWithNewLimit', (params) => { 34 | const paramsName = Object.keys(params)[0]; 35 | const newLimit = 50; 36 | const original = params[paramsName]; 37 | const expected = getExpectedParams({ 38 | limit: newLimit, 39 | ...params[paramsName], 40 | }); 41 | const newLimitParams = ChangesParamsHelper.cloneParams( 42 | original, 43 | undefined, 44 | undefined, 45 | newLimit 46 | ); 47 | expect(newLimitParams).not.toBe(original); 48 | expect(newLimitParams).not.toEqual(original); 49 | expect(newLimitParams.limit).toEqual(newLimit); 50 | expect(newLimitParams).toEqual(expected); 51 | }); 52 | 53 | it.each(getParams(true))('testCloneOptionsWithNewSince', (params) => { 54 | const paramsName = Object.keys(params)[0]; 55 | const newSince = testSeqNumber; 56 | const original = params[paramsName]; 57 | const expected = getExpectedParams({ 58 | since: newSince, 59 | ...params[paramsName], 60 | }); 61 | const newSinceParams = ChangesParamsHelper.cloneParams( 62 | original, 63 | undefined, 64 | newSince 65 | ); 66 | expect(newSinceParams).not.toBe(original); 67 | expect(newSinceParams).not.toEqual(original); 68 | expect(newSinceParams.since).toEqual(newSince); 69 | expect(newSinceParams).toEqual(expected); 70 | }); 71 | 72 | it.each(getParams(true))('testValidateOptionsValidCases', (params) => { 73 | const paramsName = Object.keys(params)[0]; 74 | expect(() => 75 | ChangesParamsHelper.validateParams(params[paramsName]) 76 | ).not.toThrow(); // no error expected 77 | }); 78 | 79 | it.each(getParams(false))('invalidOptions', (params) => { 80 | const paramsName = Object.keys(params)[0]; 81 | const { expectedError } = testParams[paramsName]; 82 | expect(() => 83 | ChangesParamsHelper.validateParams(params[paramsName]) 84 | ).toThrow(expectedError); 85 | }); 86 | }); 87 | -------------------------------------------------------------------------------- /test/unit/features/changesResultItemStream.test.js: -------------------------------------------------------------------------------- 1 | /** 2 | * © Copyright IBM Corporation 2022. 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 { PassThrough, pipeline } = require('stream'); 18 | const { 19 | ChangesResultItemStream, 20 | } = require('../../../cloudant/features/changesResultItemStream.ts'); 21 | const { generateChangesResultItems, generateSeq } = require('./testMocks'); 22 | const { 23 | ChangesFollower, 24 | } = require('../../../cloudant/features/changesFollower.ts'); 25 | const { listenOnStreamEvents, getDefaultStreamEvents } = require('./testUtils'); 26 | 27 | describe('Test ChangesResultItemStream', () => { 28 | let producerStream; 29 | let consumerStream; // this is the ChangesResultItemStream 30 | 31 | let producerStreamEvents; 32 | let consumerStreamEvents; 33 | 34 | beforeEach(() => { 35 | producerStream = new PassThrough(); 36 | producerStreamEvents = getDefaultStreamEvents(); 37 | consumerStreamEvents = getDefaultStreamEvents(); 38 | listenOnStreamEvents(producerStream, producerStreamEvents); 39 | }); 40 | 41 | it('testObjectValidationDestroysBothStreams', (done) => { 42 | consumerStream = new ChangesResultItemStream(); 43 | listenOnStreamEvents(consumerStream, consumerStreamEvents); 44 | 45 | // producerStream errors after consumerStream errors with validation error 46 | producerStream.on('close', () => { 47 | const errorMsg = 48 | 'Parameter validation errors:\n' + 49 | ' Missing required parameters: lastSeq, pending, results\n' + 50 | ' Found invalid parameters: not'; 51 | expect(consumerStreamEvents).toEqual({ 52 | closed: true, 53 | ended: false, 54 | finished: false, 55 | errored: errorMsg, 56 | }); 57 | expect(producerStreamEvents).toEqual({ 58 | closed: true, 59 | ended: false, 60 | finished: false, 61 | errored: errorMsg, 62 | }); 63 | done(); 64 | }); 65 | pipeline(producerStream, consumerStream, () => {}); 66 | producerStream.emit('data', { not: 'appropriate ChangesResult object' }); 67 | }); 68 | it('testWithoutLimit', (done) => { 69 | consumerStream = new ChangesResultItemStream(); 70 | listenOnStreamEvents(consumerStream, consumerStreamEvents); 71 | 72 | let count = 0; 73 | consumerStream.on('data', () => { 74 | count += 1; 75 | }); 76 | 77 | // consumerStream closes after producerStream is ended 78 | consumerStream.on('close', () => { 79 | expect(consumerStreamEvents).toEqual({ 80 | closed: true, 81 | ended: true, 82 | finished: true, 83 | errored: null, 84 | }); 85 | expect(producerStreamEvents).toEqual({ 86 | closed: true, 87 | ended: true, 88 | finished: true, 89 | errored: null, 90 | }); 91 | expect(count).toBe(ChangesFollower.BATCH_SIZE); 92 | done(); 93 | }); 94 | 95 | pipeline(producerStream, consumerStream, () => {}); 96 | producerStream.emit('data', { 97 | results: generateChangesResultItems(), 98 | pending: 5, 99 | lastSeq: generateSeq(512, '1'), 100 | }); 101 | producerStream.end(); 102 | }); 103 | }); 104 | -------------------------------------------------------------------------------- /test/unit/features/mockErrors.js: -------------------------------------------------------------------------------- 1 | /** 2 | * © Copyright IBM Corporation 2022. 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 MockErrors = { 18 | TERMINAL_400: { error: { message: 'bad_request', code: 400 } }, 19 | TERMINAL_401: { error: { message: 'unauthorized', code: 401 } }, 20 | TERMINAL_403: { error: { message: 'forbidden', code: 403 } }, 21 | TERMINAL_404: { error: { message: 'not_found', code: 404 } }, 22 | TRANSIENT_429: { error: { message: 'too_many_requests', code: 429 } }, 23 | TRANSIENT_500: { error: { message: 'internal_server_error', code: 500 } }, 24 | TRANSIENT_502: { error: { message: 'BAD_GATEWAY', code: 502 } }, 25 | TRANSIENT_503: { error: { message: 'service_unavailable', code: 503 } }, 26 | TRANSIENT_504: { error: { message: 'GATEWAY_TIMEOUT', code: 504 } }, 27 | // bad json is returned from sdk core as a 200, so it will appear as a resolved value 28 | TRANSIENT_BAD_JSON: { 29 | error: new Error( 30 | 'Error processing HTTP response: SyntaxError: Unexpected end of JSON input' 31 | ), 32 | }, 33 | TRANSIENT_IO: { error: { message: 'test bad IO' } }, 34 | }; 35 | 36 | function getTerminalErrors() { 37 | return [ 38 | MockErrors.TERMINAL_400, 39 | MockErrors.TERMINAL_401, 40 | MockErrors.TERMINAL_403, 41 | MockErrors.TERMINAL_404, 42 | ]; 43 | } 44 | 45 | function getTransientErrors() { 46 | return [ 47 | MockErrors.TRANSIENT_429, 48 | MockErrors.TRANSIENT_500, 49 | MockErrors.TRANSIENT_503, 50 | MockErrors.TRANSIENT_504, 51 | MockErrors.TRANSIENT_BAD_JSON, 52 | MockErrors.TRANSIENT_IO, 53 | ]; 54 | } 55 | 56 | function getErrors() { 57 | return getTerminalErrors().concat(getTransientErrors()); 58 | } 59 | 60 | module.exports = { 61 | MockError: MockErrors, 62 | getTransientErrors, 63 | getTerminalErrors, 64 | getErrors, 65 | }; 66 | -------------------------------------------------------------------------------- /test/unit/features/testDataProviders.js: -------------------------------------------------------------------------------- 1 | /** 2 | * © Copyright IBM Corporation 2022, 2024. 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 core = require('ibm-cloud-sdk-core'); 18 | const { testParams } = require('./testParams'); 19 | const { 20 | ChangesFollower, 21 | Mode, 22 | } = require('../../../cloudant/features/changesFollower.ts'); 23 | const CloudantV1 = require('../../../cloudant/v1.ts'); 24 | const { 25 | ChangesParamsHelper, 26 | } = require('../../../cloudant/features/changesParamsHelper.ts'); 27 | 28 | function getParams(isValid) { 29 | const validOptions = []; 30 | for (let i = 0; i < Object.entries(testParams).length; i += 1) { 31 | const testParam = Object.entries(testParams)[i]; 32 | if (testParam[1].isValid === isValid) { 33 | validOptions.push( 34 | JSON.parse( 35 | `{"${testParam[0]}": ${JSON.stringify(testParam[1].params)}}` 36 | ) 37 | ); 38 | } 39 | } 40 | return validOptions; 41 | } 42 | 43 | function getModes() { 44 | return [Mode.FINITE, Mode.LISTEN]; 45 | } 46 | 47 | function getStates() { 48 | const modes = getModes(); 49 | const states = []; 50 | for (let i = 0; i < modes.length; i += 1) { 51 | for (let j = 0; j < modes.length; j += 1) { 52 | states.push({ firstCallMode: i, secondCallMode: j }); 53 | } 54 | } 55 | return states; 56 | } 57 | 58 | function getModesAndLimits(batchSize = ChangesFollower.BATCH_SIZE) { 59 | // Use something smaller, equal and larger than batch size. 60 | // For larger cases make something an exact multiple of batches 61 | // and something not an exact multiple. 62 | const limits = [batchSize / 2, batchSize, batchSize * 2, batchSize * 2 + 3]; 63 | const modes = getModes(); 64 | const modesAndLimits = []; 65 | for (let i = 0; i < modes.length; i += 1) { 66 | for (let j = 0; j < limits.length; j += 1) { 67 | modesAndLimits.push({ mode: modes[i], limit: limits[j] }); 68 | } 69 | } 70 | return modesAndLimits; 71 | } 72 | 73 | const { NoAuthAuthenticator } = core; 74 | 75 | function getCloudantServiceOptions() { 76 | return { 77 | authenticator: new NoAuthAuthenticator(), 78 | serviceUrl: 'http://localhost:5984', 79 | }; 80 | } 81 | 82 | function getClientWithTimeouts(readTimeout) { 83 | return new CloudantV1({ 84 | timeout: readTimeout, 85 | ...getCloudantServiceOptions(), 86 | }); 87 | } 88 | 89 | function getClient() { 90 | return new CloudantV1(getCloudantServiceOptions()); 91 | } 92 | 93 | function getInvalidTimeoutClients() { 94 | return [ 95 | getClientWithTimeouts(15000), 96 | getClientWithTimeouts(30000), 97 | getClientWithTimeouts(ChangesParamsHelper.LONGPOLL_TIMEOUT), 98 | ]; 99 | } 100 | 101 | function getValidTimeoutClients() { 102 | return [ 103 | getClientWithTimeouts(ChangesParamsHelper.MIN_CLIENT_TIMEOUT), 104 | getClientWithTimeouts(150000), 105 | ]; 106 | } 107 | 108 | const Action = { 109 | SUCCESS: 'SUCCESS', 110 | SUPPRESS: 'SUPPRESS', 111 | THROW: 'THROW', 112 | }; 113 | 114 | function getSuppressionSequences() { 115 | const firstAndSecondPlace = [Action.SUCCESS, Action.SUPPRESS]; 116 | const thirdPlace = firstAndSecondPlace.concat([Action.THROW]); 117 | const sequences = []; 118 | for (let i = 0; i < firstAndSecondPlace.length; i += 1) { 119 | for (let j = 0; j < firstAndSecondPlace.length; j += 1) { 120 | for (let k = 0; k < thirdPlace.length; k += 1) { 121 | if ( 122 | firstAndSecondPlace[i] !== firstAndSecondPlace[j] || 123 | firstAndSecondPlace[i] !== thirdPlace[k] 124 | ) { 125 | sequences.push({ 126 | first: firstAndSecondPlace[i], 127 | second: firstAndSecondPlace[j], 128 | third: thirdPlace[k], 129 | all() { 130 | return [this.first, this.second, this.third]; 131 | }, 132 | toString() { 133 | return JSON.stringify(this.all()); 134 | }, 135 | }); 136 | } 137 | } 138 | } 139 | } 140 | return sequences; 141 | } 142 | 143 | module.exports = { 144 | getClient, 145 | getInvalidTimeoutClients, 146 | getModes, 147 | getModesAndLimits, 148 | getParams, 149 | getStates, 150 | getValidTimeoutClients, 151 | getSuppressionSequences, 152 | Action, 153 | }; 154 | -------------------------------------------------------------------------------- /test/unit/features/testMocks.js: -------------------------------------------------------------------------------- 1 | /** 2 | * © Copyright IBM Corporation 2022. 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 core = require('ibm-cloud-sdk-core'); 18 | const { 19 | ChangesFollower, 20 | } = require('../../../cloudant/features/changesFollower.ts'); 21 | const { getTransientErrors } = require('./mockErrors'); 22 | 23 | const { NoAuthAuthenticator } = core; 24 | 25 | function mockAuthenticator() { 26 | const authenticatorMock = jest.spyOn(core, 'getAuthenticatorFromEnvironment'); 27 | authenticatorMock.mockImplementation(() => new NoAuthAuthenticator()); 28 | } 29 | 30 | function generateSeq(size, gen) { 31 | const randomChars = ''.padStart(size, '0123456789abcdef'); 32 | if (gen !== undefined) { 33 | return `${gen}-${randomChars}`; 34 | } 35 | return randomChars; 36 | } 37 | 38 | function mockRandomChangesResultItem(gen = 1) { 39 | return { 40 | changes: { 41 | rev: generateSeq(32, gen), 42 | }, 43 | deleted: false, 44 | doc: null, 45 | id: `doc${gen}`, 46 | seq: generateSeq(512, 1), 47 | }; 48 | } 49 | 50 | function mockChangesResultItems( 51 | size = ChangesFollower.BATCH_SIZE, 52 | startFrom = 1 53 | ) { 54 | const changesResultItems = []; 55 | for (let s = startFrom; s < startFrom + size; s += 1) { 56 | changesResultItems.push(mockRandomChangesResultItem(s)); 57 | } 58 | return changesResultItems; 59 | } 60 | 61 | function mockRandomChangesResult(numberOfBatches) { 62 | const batchSize = ChangesFollower.BATCH_SIZE; 63 | const total = numberOfBatches * batchSize; 64 | let pending = total; 65 | const mocks = []; 66 | for (let i = 0; i < numberOfBatches; i += 1) { 67 | pending -= batchSize; 68 | const changesResultItems = mockChangesResultItems( 69 | batchSize, 70 | i * batchSize + 1 71 | ); 72 | mocks.push({ 73 | result: { 74 | results: changesResultItems, 75 | pending, 76 | lastSeq: changesResultItems[changesResultItems.length - 1].seq, 77 | }, 78 | }); 79 | } 80 | return mocks; 81 | } 82 | 83 | function mockAlternatingBatchesAndErrors(mock, batches) { 84 | const mocks = mockRandomChangesResult(batches); 85 | const transientErrors = getTransientErrors(); 86 | for (let i = 0; i < batches; i += 1) { 87 | // add a successful batch 88 | mock.mockImplementationOnce(() => Promise.resolve(mocks[i])); 89 | // add a transient error 90 | mock.mockImplementationOnce(() => 91 | Promise.reject(transientErrors[i % transientErrors.length]) 92 | ); 93 | } 94 | } 95 | 96 | function mockAlternatingBatchErrorThenPerpetualSupplier(batches) { 97 | // Set the default behavior sends back an empty changes list: 98 | const mockFn = jest.fn(() => 99 | Promise.resolve({ 100 | result: { 101 | results: [], 102 | pending: 0, 103 | lastSeq: generateSeq(512, '1'), 104 | }, 105 | }) 106 | ); 107 | // The first 2 batches have changes alternating with transient errors: 108 | mockAlternatingBatchesAndErrors(mockFn, batches); 109 | return mockFn; 110 | } 111 | 112 | function getPerpetualSupplierResponse(size = ChangesFollower.BATCH_SIZE) { 113 | const changesResultItems = mockChangesResultItems(size); 114 | return { 115 | result: { 116 | results: changesResultItems, 117 | pending: 5, 118 | lastSeq: changesResultItems[changesResultItems.length - 1].seq, 119 | }, 120 | }; 121 | } 122 | 123 | const perpetualSupplierResponse = getPerpetualSupplierResponse(); 124 | 125 | function mockPerpetualSupplier(mockFn) { 126 | const mock = perpetualSupplierResponse; 127 | mockFn.mockResolvedValue(mock); 128 | } 129 | 130 | function mockPostChangesError(mock, error) { 131 | mock.mockRejectedValue(error.error); 132 | } 133 | 134 | function mockPerpetualSupplierRespectingLimit(opts) { 135 | return getPerpetualSupplierResponse(opts.limit); 136 | } 137 | 138 | module.exports = { 139 | mockAuthenticator, 140 | mockAlternatingBatchesAndErrors, 141 | mockAlternatingBatchErrorThenPerpetualSupplier, 142 | mockPostChangesError, 143 | mockPerpetualSupplier, 144 | generateChangesResultItems: mockChangesResultItems, 145 | generateSeq, 146 | generateRandomChangesResults: mockRandomChangesResult, 147 | perpetualSupplierResponse, 148 | mockPerpetualSupplierRespectingLimit, 149 | }; 150 | -------------------------------------------------------------------------------- /test/unit/features/testParams.js: -------------------------------------------------------------------------------- 1 | /** 2 | * © Copyright IBM Corporation 2022, 2025. 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 { PostChangesConstants } = require('../../../cloudant/v1.ts'); 18 | const { 19 | ChangesParamsHelper, 20 | } = require('../../../cloudant/features/changesParamsHelper.ts'); 21 | 22 | const { Mode } = require('../../../cloudant/features/changesFollower.ts'); 23 | 24 | const beginningOfErrorMsg = 'The param'; 25 | const endOfErrorMsg = 'invalid when using ChangesFollower.'; 26 | 27 | const minimumTestParams = { db: 'test' }; 28 | const testSeqNumber = '9876-alotofcharactersthatarenotreallyrandom'; 29 | const testParams = { 30 | NULL: { 31 | isValid: false, 32 | params: null, 33 | expectedError: 'PostChangesParams configuration is required.', 34 | }, 35 | NONE: { 36 | isValid: false, 37 | params: {}, 38 | expectedError: 'The param db is required for PostChangesParams.', 39 | }, 40 | MINIMUM: { isValid: true, params: minimumTestParams }, 41 | INCLUDE_DOCS: { 42 | isValid: true, 43 | params: { 44 | includeDocs: true, 45 | attEncodingInfo: true, 46 | attachments: true, 47 | conflicts: true, 48 | ...minimumTestParams, 49 | }, 50 | }, 51 | DOC_IDS: { 52 | isValid: false, 53 | params: { 54 | includeDocs: true, 55 | filter: '_doc_ids', 56 | doc_ids: ['foo', 'bar', 'baz'], 57 | attEncodingInfo: true, 58 | attachments: true, 59 | conflicts: true, 60 | ...minimumTestParams, 61 | }, 62 | expectedError: `${beginningOfErrorMsg} 'filter=_doc_ids' is ${endOfErrorMsg}`, 63 | }, 64 | SELECTOR: { 65 | isValid: true, 66 | params: { 67 | includeDocs: true, 68 | filter: '_selector', 69 | selector: { selector: { foo: 'bar' } }, 70 | fields: ['foo', 'bar', 'baz'], 71 | ...minimumTestParams, 72 | }, 73 | }, 74 | SELECTOR_FILTER: { 75 | isValid: true, 76 | params: { 77 | filter: '_selector', 78 | ...minimumTestParams, 79 | }, 80 | }, 81 | DDOC_FILTER: { 82 | isValid: false, 83 | params: { 84 | filter: 'foo/bar', 85 | ...minimumTestParams, 86 | }, 87 | expectedError: `${beginningOfErrorMsg} 'filter=foo/bar' is ${endOfErrorMsg}`, 88 | }, 89 | DOC_IDS_FILTER: { 90 | isValid: false, 91 | params: { 92 | filter: '_doc_ids', 93 | ...minimumTestParams, 94 | }, 95 | expectedError: `${beginningOfErrorMsg} 'filter=_doc_ids' is ${endOfErrorMsg}`, 96 | }, 97 | DESIGN_FILTER: { 98 | isValid: false, 99 | params: { 100 | filter: '_design', 101 | ...minimumTestParams, 102 | }, 103 | expectedError: `${beginningOfErrorMsg} 'filter=_design' is ${endOfErrorMsg}`, 104 | }, 105 | VIEW_FILTER: { 106 | isValid: false, 107 | params: { 108 | filter: '_view', 109 | ...minimumTestParams, 110 | }, 111 | expectedError: `${beginningOfErrorMsg} 'filter=_view' is ${endOfErrorMsg}`, 112 | }, 113 | VIEW: { 114 | isValid: false, 115 | params: { 116 | filter: '_view', 117 | view: 'foo/bar', 118 | ...minimumTestParams, 119 | }, 120 | expectedError: `${beginningOfErrorMsg} 'filter=_view' is ${endOfErrorMsg}`, 121 | }, 122 | DESCENDING: { 123 | isValid: false, 124 | params: { 125 | descending: true, 126 | ...minimumTestParams, 127 | }, 128 | expectedError: `${beginningOfErrorMsg} 'descending' is ${endOfErrorMsg}`, 129 | }, 130 | FEED: { 131 | isValid: false, 132 | params: { 133 | feed: PostChangesConstants.Feed.CONTINUOUS, 134 | ...minimumTestParams, 135 | }, 136 | expectedError: `${beginningOfErrorMsg} 'feed' is ${endOfErrorMsg}`, 137 | }, 138 | HEARTBEAT: { 139 | isValid: false, 140 | params: { 141 | heartbeat: 150, 142 | ...minimumTestParams, 143 | }, 144 | expectedError: `${beginningOfErrorMsg} 'heartbeat' is ${endOfErrorMsg}`, 145 | }, 146 | LAST_EVENT_ID: { 147 | isValid: false, 148 | params: { 149 | lastEventId: testSeqNumber, 150 | ...minimumTestParams, 151 | }, 152 | expectedError: `${beginningOfErrorMsg} 'lastEventId' is ${endOfErrorMsg}`, 153 | }, 154 | TIMEOUT: { 155 | isValid: false, 156 | params: { 157 | timeout: 3600000, 158 | ...minimumTestParams, 159 | }, 160 | expectedError: `${beginningOfErrorMsg} 'timeout' is ${endOfErrorMsg}`, 161 | }, 162 | MULTI_INVALID: { 163 | isValid: false, 164 | params: { 165 | descending: true, 166 | feed: PostChangesConstants.Feed.CONTINUOUS, 167 | heartbeat: 150, 168 | lastEventId: testSeqNumber, 169 | timeout: 3600000, 170 | ...minimumTestParams, 171 | }, 172 | expectedError: `${beginningOfErrorMsg}s 'descending', 'feed', 'heartbeat', 'lastEventId', 'timeout' are ${endOfErrorMsg}`, 173 | }, 174 | }; 175 | 176 | function getExpectedParams(params, mode) { 177 | const expectedParams = params; 178 | if (mode === Mode.LISTEN) { 179 | if (params.feed === undefined) { 180 | expectedParams.feed = PostChangesConstants.Feed.LONGPOLL; 181 | } 182 | expectedParams.timeout = ChangesParamsHelper.LONGPOLL_TIMEOUT; 183 | } else if (mode === Mode.FINITE) { 184 | expectedParams.feed = PostChangesConstants.Feed.NORMAL; 185 | } 186 | return expectedParams; 187 | } 188 | 189 | module.exports = { testParams, testSeqNumber, getExpectedParams }; 190 | -------------------------------------------------------------------------------- /test/unit/features/testUtils.js: -------------------------------------------------------------------------------- 1 | /** 2 | * © Copyright IBM Corporation 2022. 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 | /* eslint-disable no-param-reassign */ 18 | function delay(milliseconds) { 19 | return new Promise((resolve) => { 20 | setTimeout(resolve, milliseconds); 21 | }); 22 | } 23 | 24 | function listenOnStreamEvents(stream, events) { 25 | stream.on('end', () => { 26 | events.ended = true; 27 | }); 28 | stream.on('finish', () => { 29 | events.finished = true; 30 | }); 31 | stream.on('error', (err) => { 32 | events.errored = err.message; 33 | }); 34 | stream.on('close', () => { 35 | events.closed = true; 36 | }); 37 | } 38 | 39 | function getDefaultStreamEvents() { 40 | return { 41 | closed: false, 42 | ended: false, 43 | finished: false, 44 | errored: null, 45 | }; 46 | } 47 | 48 | module.exports = { 49 | delay, 50 | listenOnStreamEvents, 51 | getDefaultStreamEvents, 52 | }; 53 | -------------------------------------------------------------------------------- /test/unit/getAuthenticatorFromEnvCloudantExtension.test.js: -------------------------------------------------------------------------------- 1 | /** 2 | * © Copyright IBM Corporation 2020, 2021. 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 | const assert = require('assert'); 17 | const { BasicAuthenticator } = require('ibm-cloud-sdk-core'); 18 | const getAuthenticatorFromEnvCloudantExtension = 19 | require('../../lib/getAuthenticatorFromEnvCloudantExtension.ts').default; 20 | const { CouchdbSessionAuthenticator } = require('../../index.ts'); 21 | 22 | describe('Test getAuthenticatorFromEnvCloudantExtension', () => { 23 | it('Create couchdb_session authenticator', () => { 24 | process.env.TEST1_AUTH_TYPE = 'couchdb_session'; 25 | process.env.TEST1_USERNAME = 'username'; 26 | process.env.TEST1_PASSWORD = 'password'; 27 | const auth = getAuthenticatorFromEnvCloudantExtension('test1'); 28 | 29 | assert.ok(auth instanceof CouchdbSessionAuthenticator); 30 | assert.strictEqual(auth.tokenOptions.username, 'username'); 31 | assert.strictEqual(auth.tokenOptions.password, 'password'); 32 | }); 33 | 34 | it('Create couchdb_session authenticator with non-standard casing', () => { 35 | process.env.TEST2_AUTH_TYPE = 'coucHdb_seSsion'; 36 | process.env.TEST2_USERNAME = 'username'; 37 | process.env.TEST2_PASSWORD = 'password'; 38 | const auth = getAuthenticatorFromEnvCloudantExtension('test2'); 39 | 40 | assert.ok(auth instanceof CouchdbSessionAuthenticator); 41 | }); 42 | 43 | it('Use invalid authenticator type', () => { 44 | process.env.TEST3_AUTH_TYPE = 'basic'; 45 | process.env.TEST3_USERNAME = 'username'; 46 | process.env.TEST3_PASSWORD = 'password'; 47 | const auth = getAuthenticatorFromEnvCloudantExtension('test3'); 48 | assert.ok(auth instanceof BasicAuthenticator); 49 | }); 50 | 51 | it('Create couchdb_session authenticator with env auth type alias', () => { 52 | process.env.TEST4_AUTHTYPE = 'couchdb_session'; 53 | process.env.TEST4_USERNAME = 'username'; 54 | process.env.TEST4_PASSWORD = 'password'; 55 | const auth = getAuthenticatorFromEnvCloudantExtension('test4'); 56 | 57 | assert.ok(auth instanceof CouchdbSessionAuthenticator); 58 | assert.strictEqual(auth.tokenOptions.username, 'username'); 59 | assert.strictEqual(auth.tokenOptions.password, 'password'); 60 | }); 61 | }); 62 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | /* Basic Options */ 4 | "target": "es2022", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', or 'ESNEXT'. */ 5 | "module": "node16", /* Specify module code generation: 'none', commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */ 6 | "lib": [ 7 | "es2022", 8 | "dom" 9 | ], 10 | 11 | /* Build source files into a folder called `dist` to maintain clean directories, free of ts-generated files */ 12 | "outDir": "./dist", 13 | 14 | /* Specify library files to be included in the compilation: */ 15 | "declaration": true, /* Generates corresponding '.d.ts' file. */ 16 | "sourceMap": true, /* Generates corresponding '.map' file. */ 17 | 18 | /* Strict Type-Checking Options */ 19 | "alwaysStrict": true, /* Parse in strict mode and emit "use strict" for each source file. */ 20 | 21 | /* Module Resolution Options */ 22 | "moduleResolution": "node16", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */ 23 | "types": ["node"], /* Type declaration files to be included in compilation. */ 24 | "resolveJsonModule": true, /* Allows importing modules with a .json extension. */ 25 | "esModuleInterop": true 26 | }, 27 | "exclude": [ 28 | "node_modules", 29 | "./dist/**/*", 30 | "./examples/**/*", 31 | "./test/examples/**/*" 32 | ] 33 | } 34 | -------------------------------------------------------------------------------- /typedoc.json: -------------------------------------------------------------------------------- 1 | { 2 | "theme": "default", 3 | "entryPointStrategy": "expand", 4 | "excludeExternals": true, 5 | "tsconfig": "tsconfig.json", 6 | "out": "apidocs", 7 | "includeVersion": true, 8 | "entryPoints": ["index.ts"], 9 | "excludeInternal": true, 10 | "intentionallyNotExported": [ 11 | "SessionTokenManager" 12 | ], 13 | } 14 | --------------------------------------------------------------------------------