diff --git a/.bumpversion.cfg b/.bumpversion.cfg deleted file mode 100644 index 861d505b..00000000 --- a/.bumpversion.cfg +++ /dev/null @@ -1,26 +0,0 @@ -[bumpversion] -current_version = 0.1.4 -commit = True -message = Update version {current_version} -> {new_version} -parse = ^(?P0|[1-9]\d*)\.(?P0|[1-9]\d*)\.(?P0|[1-9]\d*)(?P[a|b|rc](?:0|[1-9]\d*))?(?P(?:\.dev)(?:0|[1-9]\d*))?(?P\+[0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*)?$ -serialize = - {major}.{minor}.{patch}{prerelease}{devrelease}{buildmetadata} - {major}.{minor}.{patch}{prerelease}{devrelease} - {major}.{minor}.{patch}{prerelease}{buildmetadata} - {major}.{minor}.{patch}{prerelease} - {major}.{minor}.{patch}{devrelease}{buildmetadata} - {major}.{minor}.{patch}{devrelease} - {major}.{minor}.{patch}{buildmetadata} - {major}.{minor}.{patch} - -[bumpversion:file:ibmcloudant/version.py] -search = __version__ = '{current_version}' -replace = __version__ = '{new_version}' - -[bumpversion:file:setup.py] -search = __version__ = '{current_version}' -replace = __version__ = '{new_version}' - -[bumpversion:file:README.md] -search = {current_version} -replace = {new_version} diff --git a/.bumpversion.toml b/.bumpversion.toml new file mode 100644 index 00000000..4551724f --- /dev/null +++ b/.bumpversion.toml @@ -0,0 +1,36 @@ +[tool.bumpversion] +current_version = "0.11.10" +commit = true +message = "Update version {current_version} -> {new_version}" +parse = "^(?P0|[1-9]\\d*)\\.(?P0|[1-9]\\d*)\\.(?P0|[1-9]\\d*)(?P[a|b|rc](?:0|[1-9]\\d*))?(?P(?:\\.dev)(?:0|[1-9]\\d*))?(?P\\+[0-9a-zA-Z-]+(?:\\.[0-9a-zA-Z-]+)*)?$" +serialize = [ + "{major}.{minor}.{patch}{prerelease}{devrelease}{buildmetadata}", + "{major}.{minor}.{patch}{prerelease}{devrelease}", + "{major}.{minor}.{patch}{prerelease}{buildmetadata}", + "{major}.{minor}.{patch}{prerelease}", + "{major}.{minor}.{patch}{devrelease}{buildmetadata}", + "{major}.{minor}.{patch}{devrelease}", + "{major}.{minor}.{patch}{buildmetadata}", + "{major}.{minor}.{patch}", +] + +[[tool.bumpversion.files]] +filename = "ibmcloudant/version.py" +search = "__version__ = '{current_version}'" +replace = "__version__ = '{new_version}'" + +[[tool.bumpversion.files]] +filename = "pyproject.toml" +search = "version = \"{current_version}\"" +replace = "version = \"{new_version}\"" + +[[tool.bumpversion.files]] +filename = "README.md" +search = "{current_version}" +replace = "{new_version}" + +[[tool.bumpversion.files]] +glob = "docs/*.md" +ignore_missing_version = true +search = "{current_version}" +replace = "{new_version}" diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 90a42520..369b19d4 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -5,17 +5,26 @@ .github/dco.yml @IBM/cloudant-sdks .github/dependabot.yml @IBM/cloudant-sdks .github/pull_request_template.md @IBM/cloudant-sdks +.whitesource @IBM/cloudant-sdks +AI_CODE_POLICY.md @IBM/cloudant-sdks CODE_OF_CONDUCT.md @IBM/cloudant-sdks CONTRIBUTING.md @IBM/cloudant-sdks Jenkinsfile @IBM/cloudant-sdks KNOWN_ISSUES.md @IBM/cloudant-sdks LICENSE @IBM/cloudant-sdks README.md @IBM/cloudant-sdks +SECURITY.md @IBM/cloudant-sdks +docs/Authentication.md @IBM/cloudant-sdks +docs/Changes_Follower.md @IBM/cloudant-sdks +docs/Examples.md @IBM/cloudant-sdks +docs/Pagination.md @IBM/cloudant-sdks +docs/README.md @IBM/cloudant-sdks scripts/publish_buildinfo.sh @IBM/cloudant-sdks scripts/pydoc/generate-index-html.sh @IBM/cloudant-sdks scripts/pydoc/publish-doc.sh @IBM/cloudant-sdks scripts/setup_couch.sh @IBM/cloudant-sdks scripts/setup_wiremock.sh @IBM/cloudant-sdks +stubs/gen-its-mappings.json @IBM/cloudant-sdks stubs/mappings.json @IBM/cloudant-sdks # Generated code files: diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md index 022be875..ad356ebe 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -21,7 +21,7 @@ assignees: '' **Must gather (please complete the following information):** - SDK Version [e.g. 1.2.1] - - Python Version [e.g. Python 3.7] + - Python Version [e.g. Python 3.10] - Name of service that you're trying to use (if applicable) - Name of operation that you're trying to invoke (if applicable) diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 09d2400e..5c10e7de 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -1,8 +1,34 @@ version: 2 updates: + - package-ecosystem: "github-actions" + directory: "/" + schedule: + interval: "weekly" + cooldown: + default-days: 4 + pull-request-branch-name: + separator: "-" + groups: + actions-deps: + patterns: + - "*" - package-ecosystem: "pip" directory: "/" open-pull-requests-limit: 10 schedule: interval: "daily" + cooldown: + default-days: 4 + pull-request-branch-name: + separator: "-" # Custom settings + - package-ecosystem: "github-actions" + directory: "/" + schedule: + interval: "weekly" + cooldown: + default-days: 4 + target-branch: "gh-pages" + pull-request-branch-name: + separator: "-" + diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index 4ea2852a..24d034ed 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -4,7 +4,7 @@ Fixes: -**Note: An existing issue is [required](https://github.com/IBM/cloudant-python-sdk/blob/master/CONTRIBUTING.md#PRs) before opening a PR.** +**Note: An existing issue is [required](https://github.com/IBM/cloudant-python-sdk/blob/main/CONTRIBUTING.md#PRs) before opening a PR.** ## PR Checklist diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml new file mode 100644 index 00000000..de1f1f7d --- /dev/null +++ b/.github/workflows/codeql.yml @@ -0,0 +1,35 @@ +name: "CodeQL" +on: + push: + branches: ['main'] + pull_request: + branches: ['main'] + schedule: + - cron: '00 4 * * 2' +permissions: {} +jobs: + analyze: + name: Analyze + runs-on: ubuntu-latest + permissions: + security-events: write + strategy: + fail-fast: false + matrix: + language: ['python'] + steps: + - name: Checkout repository + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + - name: Initialize CodeQL + uses: github/codeql-action/init@ff2f1c621b7f889edc0d3c761ac2e6a3f8cdb0dd # v4.37.7 + with: + languages: ${{ matrix.language }} + config: | + paths-ignore: + - 'examples/snippets/**/*.py' + - name: Autobuild + uses: github/codeql-action/autobuild@ff2f1c621b7f889edc0d3c761ac2e6a3f8cdb0dd # v4.37.7 + - name: Perform CodeQL Analysis + uses: github/codeql-action/analyze@ff2f1c621b7f889edc0d3c761ac2e6a3f8cdb0dd # v4.37.7 + with: + category: "/language:${{matrix.language}}" diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml new file mode 100644 index 00000000..301b5cb4 --- /dev/null +++ b/.github/workflows/docs.yml @@ -0,0 +1,22 @@ +name: docs +on: + pull_request: + branches: ['main'] +permissions: {} +jobs: + docs: + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + persist-credentials: false + - name: Setup python 3.14 + uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0 + with: + python-version: '3.14' + cache: 'pip' + - name: Install deps + run: pip install -r requirements-docs.txt + - name: Build docs + run: sphinx-build source apidocs -bhtml diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 754502c6..14004fac 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -1,19 +1,22 @@ name: test on: push: - branches-ignore: ['gh-pages'] + branches: ['main'] pull_request: branches-ignore: ['gh-pages'] +permissions: {} env: SERVER_AUTH_TYPE: basic SERVER_URL: http://127.0.0.1:5984 SERVER_USERNAME: admin SERVER_PASSWORD: password + WIREMOCK_URL: http://127.0.0.1:8080 + WIREMOCK_PORT: 8080 jobs: test: strategy: matrix: - python: ['3.7', '3.8', '3.9', '3.10'] + python: ['3.10', '3.11', '3.12', '3.13', '3.14'] runs-on: ubuntu-latest services: couchdb: @@ -24,22 +27,31 @@ jobs: options: --name couchdb ports: - 5984:5984 + wiremock: + image: wiremock/wiremock + options: --name wiremock + ports: + - 8080:8080 steps: - name: Checkout code - uses: actions/checkout@v3 + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: persist-credentials: false - name: Setup python ${{ matrix.python }} - uses: actions/setup-python@v4 + uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0 with: python-version: ${{ matrix.python }} cache: 'pip' - name: Setup CouchDB shell: bash run: ${GITHUB_WORKSPACE}/scripts/setup_couch.sh + - name: Setup Wiremock + shell: bash + run: ${GITHUB_WORKSPACE}/scripts/setup_wiremock.sh - name: Install deps run: | python -m pip install --upgrade pip - pip install tox + pip install -r requirements-dev.txt + flit install --only-deps - name: Run tests - run: tox -e py + run: python -m pytest diff --git a/.gitignore b/.gitignore index d5720c66..9f9c0457 100644 --- a/.gitignore +++ b/.gitignore @@ -59,6 +59,7 @@ python3/ # resources resources/output.wav +apidocs/ docs/_build/ deploy.sh docs/apis diff --git a/.pylintrc b/.pylintrc index 74f46e48..6bf7a061 100644 --- a/.pylintrc +++ b/.pylintrc @@ -45,6 +45,7 @@ confidence=HIGH # no Warning level messages displayed, use "--disable=all --enable=classes # --disable=W". disable=too-many-arguments, + too-many-positional-arguments, too-many-public-methods, too-few-public-methods, too-many-instance-attributes, @@ -52,19 +53,8 @@ disable=too-many-arguments, too-many-branches, too-many-lines, line-too-long, - bad-continuation, similarities, - print-statement, - parameter-unpacking, - unpacking-in-except, - old-raise-syntax, - backtick, - long-suffix, - old-ne-operator, - old-octal-literal, import-error, - import-star-module-level, - non-ascii-bytes-literal, raw-checker-failed, bad-inline-option, locally-disabled, @@ -73,67 +63,6 @@ disable=too-many-arguments, useless-suppression, deprecated-pragma, use-symbolic-message-instead, - apply-builtin, - basestring-builtin, - buffer-builtin, - cmp-builtin, - coerce-builtin, - execfile-builtin, - file-builtin, - long-builtin, - raw_input-builtin, - reduce-builtin, - standarderror-builtin, - unicode-builtin, - xrange-builtin, - coerce-method, - delslice-method, - getslice-method, - setslice-method, - no-absolute-import, - old-division, - dict-iter-method, - dict-view-method, - next-method-called, - metaclass-assignment, - indexing-exception, - raising-string, - reload-builtin, - oct-method, - hex-method, - nonzero-method, - cmp-method, - input-builtin, - round-builtin, - intern-builtin, - unichr-builtin, - map-builtin-not-iterating, - zip-builtin-not-iterating, - range-builtin-not-iterating, - filter-builtin-not-iterating, - using-cmp-argument, - eq-without-hash, - div-method, - idiv-method, - rdiv-method, - exception-message-attribute, - invalid-str-codec, - sys-max-int, - bad-python3-import, - deprecated-string-function, - deprecated-str-translate-call, - deprecated-itertools-function, - deprecated-types-field, - next-method-defined, - dict-items-not-iterating, - dict-keys-not-iterating, - dict-values-not-iterating, - deprecated-operator-function, - deprecated-urllib-function, - xreadlines-attribute, - deprecated-sys-function, - exception-escape, - comprehension-escape, invalid-name # Enable the message, report, category or checker with the given id(s). You can @@ -573,5 +502,5 @@ valid-metaclass-classmethod-first-arg=cls # Exceptions that will emit a warning when being caught. Defaults to # "BaseException, Exception". -overgeneral-exceptions=BaseException, - Exception +overgeneral-exceptions=builtin.BaseException, + builtin.Exception diff --git a/.secrets.baseline b/.secrets.baseline index 1677433e..e75f570f 100644 --- a/.secrets.baseline +++ b/.secrets.baseline @@ -1,9 +1,9 @@ { "exclude": { - "files": "stubs/.+\\.json|^.secrets.baseline$", - "lines": null + "files": "^.secrets.baseline$", + "lines": "(?i)^\\s*\"(?:transaction|x-couch-request|x-request)-id\": \"[^\"]+\",?$|^\\s*\"(?:last_|update_|purge_|checkpointed_source_|source_|through_)?seq\": \"[^\"]+\",?$|^\\s*\"(?:doc_)?id\": \"[^\"]+\",?$|^\\s*\"(?:signature|name|uuid)\": \"[^\"]+\",?$" }, - "generated_at": "2022-06-08T07:56:57Z", + "generated_at": "2025-10-03T09:46:18Z", "plugins_used": [ { "name": "AWSKeyDetector" @@ -79,40 +79,82 @@ "results": { "Jenkinsfile": [ { - "hashed_secret": "dd7c0356e7bfc571546237209d7fa15319e25cfa", + "hashed_secret": "bb589d0621e5472f470fa3425a234c74b1e202e8", "is_secret": false, "is_verified": false, - "line_number": 32, + "line_number": 341, "type": "Secret Keyword", "verified_result": null } ], "README.md": [ { - "hashed_secret": "32e8612d8ca77c7ea8374aa7918db8e5df9252ed", + "hashed_secret": "7740cecbbf3c508248997e0b3b1c24cc00a2759a", "is_secret": false, "is_verified": false, - "line_number": 165, + "line_number": 122, "type": "Secret Keyword", "verified_result": null } ], - "examples/putReplicationDocument/example_request.py": [ + "docs/Authentication.md": [ { - "hashed_secret": "701e4e52db78a3657f5bc71fd174cf29e7868fbd", + "hashed_secret": "7740cecbbf3c508248997e0b3b1c24cc00a2759a", "is_secret": false, "is_verified": false, - "line_number": 11, + "line_number": 62, + "type": "Secret Keyword", + "verified_result": null + } + ], + "examples/README.md": [ + { + "hashed_secret": "7740cecbbf3c508248997e0b3b1c24cc00a2759a", + "is_secret": false, + "is_verified": false, + "line_number": 375, + "type": "Base64 High Entropy String", + "verified_result": null + }, + { + "hashed_secret": "7740cecbbf3c508248997e0b3b1c24cc00a2759a", + "is_secret": false, + "is_verified": false, + "line_number": 375, + "type": "Secret Keyword", + "verified_result": null + }, + { + "hashed_secret": "68416db59b46c60492505e00404d757825cd918f", + "is_secret": false, + "is_verified": false, + "line_number": 377, + "type": "Secret Keyword", + "verified_result": null + }, + { + "hashed_secret": "665f9d90d71be71ec9a1f352f320e93136770392", + "is_secret": false, + "is_verified": false, + "line_number": 381, "type": "Secret Keyword", "verified_result": null } ], - "ibmcloudant/cloudant_v1.py": [ + "examples/snippets/putReplicationDocument/example_request.py": [ + { + "hashed_secret": "7740cecbbf3c508248997e0b3b1c24cc00a2759a", + "is_secret": false, + "is_verified": false, + "line_number": 11, + "type": "Base64 High Entropy String", + "verified_result": null + }, { - "hashed_secret": "5788c5ac01fd15d76b11bacbb480f7bc9f38bee3", + "hashed_secret": "7740cecbbf3c508248997e0b3b1c24cc00a2759a", "is_secret": false, "is_verified": false, - "line_number": 13963, + "line_number": 11, "type": "Secret Keyword", "verified_result": null } @@ -127,18 +169,70 @@ "verified_result": null } ], + "stubs/gen-its-mappings.json": [ + { + "hashed_secret": "f1d68726fe147018f28a80efd621b43580196979", + "is_secret": false, + "is_verified": false, + "line_number": 136, + "type": "Hex High Entropy String", + "verified_result": null + }, + { + "hashed_secret": "f1d68726fe147018f28a80efd621b43580196979", + "is_secret": false, + "is_verified": false, + "line_number": 136, + "type": "Secret Keyword", + "verified_result": null + }, + { + "hashed_secret": "870cc58f071983f7977b829365dc77509ab1056f", + "is_secret": false, + "is_verified": false, + "line_number": 1363, + "type": "Hex High Entropy String", + "verified_result": null + } + ], + "test/integration/test_cloudant_v1.py": [ + { + "hashed_secret": "b8473b86d4c2072ca9b08bd28e373e8253e865c4", + "is_secret": false, + "is_verified": false, + "line_number": 1642, + "type": "Secret Keyword", + "verified_result": null + }, + { + "hashed_secret": "166b7db1d38ae47816b3840e6187b7a2631e04a8", + "is_secret": false, + "is_verified": false, + "line_number": 1685, + "type": "Base64 High Entropy String", + "verified_result": null + } + ], "test/unit/test_cloudant_v1.py": [ + { + "hashed_secret": "166b7db1d38ae47816b3840e6187b7a2631e04a8", + "is_secret": false, + "is_verified": false, + "line_number": 10664, + "type": "Base64 High Entropy String", + "verified_result": null + }, { "hashed_secret": "b8473b86d4c2072ca9b08bd28e373e8253e865c4", "is_secret": false, "is_verified": false, - "line_number": 16137, + "line_number": 17086, "type": "Secret Keyword", "verified_result": null } ] }, - "version": "0.13.1+ibm.48.dss", + "version": "0.13.1+ibm.64.dss", "word_list": { "file": null, "hash": null diff --git a/.whitesource b/.whitesource new file mode 100644 index 00000000..e916ce12 --- /dev/null +++ b/.whitesource @@ -0,0 +1,10 @@ +{ + "settingsInheritedFrom": "whitesource-config/whitesource-config@master", + "issueSettings": { + "issueRepoName": "sdks" + }, + "scanSettings": { + "javaVersion": "17", + "python.requirementsFileIncludes": "pyproject.toml,requirements-dev.txt,requirements-docs.txt" + } +} diff --git a/AI_CODE_POLICY.md b/AI_CODE_POLICY.md new file mode 100644 index 00000000..58d83510 --- /dev/null +++ b/AI_CODE_POLICY.md @@ -0,0 +1,10 @@ +# Policy on submitting AI-generated code to IBM Open Source software projects (v.1.0) + +Although IBM does not generally prohibit AI-generated code (“AI Code”) from being contributed to IBM- maintained Open Source software projects (“IBM Projects”), any such contributions should comply with this policy (“Policy”). This Policy applies to contributions to IBM Projects made by IBMers and non-IBMers alike. + +AI Code can create potential copyright infringement issues for an IBM Project if it is substantially similar to code used to train the generative AI model (“Training Code”) that outputs the AI Code. As a result, IBM asks that contributors comply with the following guidance when deciding whether it is appropriate to submit a piece of AI Code to an IBM Project: + +1. Only use a generative AI tool (“AI Tool”) with functionality specifically designed to mitigate the risk of AI Code that is substantially similar to Training Code, either by trying to (a) filter AI Code that is substantially similar to Training Code or (b) identify AI Code that is substantially similar to the Training Code such that the contributor can identify and comply with the Training Code’s license, including its notice and attribution requirements. You must ensure the mitigation functionality is enabled during your use of the AI Tool. +2. Ensure that the terms and conditions of the generative AI tool (“AI Tool”) (a) allow its AI Code to be used for external Open Source development and (b) are consistent with submitting the AI Code under the IBM Project’s Developer Certificate of Origin (DCO) or Contributor License Agreement (CLA), including enabling it to be used by the IBM Project under its Open Source license. +3. Review and understand the terms of the DCO or CLA specific to the IBM Project and ensure that contribution of the AI Code complies with such terms or other applicable project guidance. If the AI Tool indicates the AI Code is substantially similar to Training Code, comply with the license requirements of the Training Code’s license (including notice and attribution terms) when contributing the AI Code to the IBM Project, and only submit if its license is compatible with the IBM Project license. +4. As a best practice, if your contribution to this project contains AI Code, indicate so with a pair of inline comment tags that note the beginning and end of the AI Code content that identify the AI Tool used (including version if applicable). For example, if you modified a file and your contribution contains AI Code, include a comment before the AI-generated content such as “// Begin modifications with assistance from AI Tool [X v.Y] ” paired with a comment after the content “// End modifications with assistance from AI Tool [X v.Y].” If you have substantially altered the file using an AI tool, we recommend adding a comment in the file header (“// this file has been modified with the assistance of AI Tool [X v.Y]”). diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 00e5ead0..75d7bc9b 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -19,6 +19,8 @@ are often inappropriate and we may need to make changes to the specification instead. Please, first [look at the list of files](.github/CODEOWNERS) that _should not_ be modified. +Before submitting your pull request, please ensure you've reviewed and adhere to our [AI policy](AI_CODE_POLICY.md). + # General Information For general guidance on contributing to this project, please see the -[general guidance for contributing](https://github.com/IBM/ibm-cloud-sdk-common/blob/master/CONTRIBUTING_python.md). +[general guidance for contributing](https://github.com/IBM/ibm-cloud-sdk-common/blob/main/CONTRIBUTING_python.md). diff --git a/Jenkinsfile b/Jenkinsfile index b6555e9b..4c1b0e0d 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -2,35 +2,35 @@ pipeline { agent { - label 'sdks-executor' - } - options { - skipDefaultCheckout() + kubernetes { + yaml kubePodTemplate(name: 'gauge.yaml') + } } parameters { validatingString( name: 'TARGET_VERSION', defaultValue: 'NONE', description: 'Tag to create after successful QA', failedValidationMessage: 'Tag name must be NONE or a semantic version release or pre-release (i.e. no build metadata)', - regex: /NONE|${SVRE_PRE_RELEASE}/) + regex: /NONE|${globals.SVRE_PRE_RELEASE}/) } environment { - GH_CREDS = credentials('gh-sdks-automation') + ARTIFACTORY_CREDS = credentials('artifactory') + ARTIFACTORY_URL_UP = "${Artifactory.server('taas-artifactory-upload').getUrl()}" + ARTIFACTORY_URL_DOWN = "${Artifactory.server('taas-artifactory').getUrl()}" } stages { - stage('Checkout') { + stage('Detect Secrets') { + steps { + detectSecrets() + } + } + + stage('Init') { steps { script { defaultInit() applyCustomizations() - checkoutResult = checkout scm - commitHash = "${checkoutResult.GIT_COMMIT[0..6]}" - sh ''' - git config --global user.email $GH_SDKS_AUTOMATION_MAIL - git config --global user.name $GH_CREDS_USR - git config --global credential.username $GH_CREDS_USR - git config --global credential.helper '!f() { echo password=\$GH_CREDS_PSW; echo; }; f' - ''' + commitHash = "${env.GIT_COMMIT.take(7)}" } } } @@ -47,8 +47,10 @@ pipeline { usernameVariable: 'SERVER_USERNAME', passwordVariable: 'SERVER_PASSWORD') ]) { - sh './scripts/setup_couch.sh' - sh './scripts/setup_wiremock.sh' + script { + sh './scripts/setup_couch.sh' + sh './scripts/setup_wiremock.sh' + } runTests() } } @@ -62,14 +64,51 @@ pipeline { } } } + + stage('SonarQube analysis') { + environment { + scannerHome = tool 'SonarQubeScanner' + } + // Scanning runs on PRs (except dependabot for non-core updates) and primary branch + when { + anyOf { + changeRequest() + expression { env.BRANCH_IS_PRIMARY } + } + not { + allOf { + changeRequest branch: 'dependabot*', comparator: 'GLOB' + not { + changeRequest branch: /^dependabot.*(?i)(ibm)[\.-].+sdk-core.*$/, comparator: 'REGEXP' + } + } + } + } + steps { + scanCode() + } + } + stage('Publish[staging]') { + when { + not { + buildingTag() + } + } environment { - STAGE_ROOT = 'https://na.artifactory.swg-devops.com/artifactory/api/' + STAGE_ROOT = "${ARTIFACTORY_URL_UP}/api/" } steps { bumpVersion(true) - publishStaging() - publishArtifactoryBuildInfo() + customizePublishingInfo() + withEnv(["LIB_NAME=${libName}", + "TYPE=${buildType}", + "ARTIFACT_URL=${artifactUrl}", + "MODULE_ID=${moduleId}", + "BUILD_NAME=${env.JOB_NAME}"]) { + publishStaging() + publishArtifactoryBuildInfo() + } } // This post stage resets the temporary version bump used to publish to staging post { @@ -78,20 +117,16 @@ pipeline { } } } + stage('Run Gauge tests') { + when { + not { + buildingTag() + } + } steps { script { buildResults = null - prefixedSdkVersion = '' - if (libName == 'go') { - prefixedSdkVersion = "@$commitHash" - } else if (libName == 'node') { - prefixedSdkVersion = "@${env.NEW_SDK_VERSION}" - } else if (libName == 'python') { - prefixedSdkVersion = "==${env.NEW_SDK_VERSION}" - } else if (libName == 'java') { - prefixedSdkVersion = "${env.NEW_SDK_VERSION}" - } // For standard builds attempt to run on a matching env.BRANCH_NAME branch first and if it doesn't exist // then fallback to TARGET_GAUGE_RELEASE_BRANCH_NAME if set or env.TARGET_GAUGE_DEFAULT_BRANCH_NAME. @@ -107,14 +142,14 @@ pipeline { try { buildResults = build job: "/${env.SDKS_GAUGE_PIPELINE_PROJECT}/${gaugeBranchName}", parameters: [ string(name: 'SDK_RUN_LANG', value: "$libName"), - string(name: "SDK_VERSION_${libName.toUpperCase()}", value: "$prefixedSdkVersion")] + string(name: "SDK_VERSION_${libName.toUpperCase()}", value: "${env.NEW_SDK_VERSION}")] } catch (hudson.AbortException ae) { - // only run build in sdks-gauge master branch if BRANCH_NAME doesn't exist + // only run build in sdks-gauge default branch if BRANCH_NAME doesn't exist if (ae.getMessage().contains("No item named /${env.SDKS_GAUGE_PIPELINE_PROJECT}/${gaugeBranchName} found")) { echo "No matching branch named '${gaugeBranchName}' in sdks-gauge, building ${fallbackBranchName} branch" build job: "/${env.SDKS_GAUGE_PIPELINE_PROJECT}/${fallbackBranchName}", parameters: [ string(name: 'SDK_RUN_LANG', value: "$libName"), - string(name: "SDK_VERSION_${libName.toUpperCase()}", value: "$prefixedSdkVersion")] + string(name: "SDK_VERSION_${libName.toUpperCase()}", value: "${env.NEW_SDK_VERSION}")] } else { throw ae } @@ -122,22 +157,37 @@ pipeline { } } } + + stage('Mend scan') { + when { + expression { env.BRANCH_IS_PRIMARY } + } + environment { + WS_PROJECTNAME="cloudant-${libName}-sdk" + } + steps { + mendScan() + } + } + stage('Update version and tag') { when { beforeAgent true allOf { - // We only bump the version and create a tag when building master with a TARGET_VERSION - branch 'master' + // We only bump the version and create a tag when building the default primary branch with a TARGET_VERSION + branch 'main' not { equals expected: 'NONE', actual: "${params.TARGET_VERSION}" } } } steps { - // bump the version - bumpVersion(false) - // Push the version bump and release tag - sh 'git push --tags origin HEAD:master' + gitsh('github.com') { + // bump the version + bumpVersion(false) + // Push the version bump and release tag + sh 'git push --tags origin HEAD:main' + } } } stage('Publish[repository]') { @@ -153,41 +203,45 @@ pipeline { } steps { publishPublic() - publishDocs() + gitsh('github.com') { + publishDocs() + } } } } } +// Note default values cannot be assigned here. def libName def commitHash def bumpVersion def customizeVersion def getNewVersion -// Default no-op, may be overridden -def customizePublishingInfo = {} +def testVersionPrefix +def customizePublishingInfo def publishArtifactoryBuildInfo -def artifactUrl = '' -def moduleId = '' -def buildName = '' -def buildType = '' +def publishArtifactoryBuildInfoScript +def artifactUrl +def moduleId +def buildType +def scanCode void defaultInit() { - // Default to using bump2version + // Default to using bump-my-version bumpVersion = { isDevRelease -> - newVersion = getNewVersion(isDevRelease, true) + newVersion = getNewVersion(isDevRelease) // Set an env var with the new version env.NEW_SDK_VERSION = newVersion doVersionBump(isDevRelease, newVersion) } doVersionBump = { isDevRelease, newVersion, allowDirty -> - sh "bump2version --new-version ${newVersion} ${allowDirty ? '--allow-dirty': ''} ${isDevRelease ? '--no-commit' : '--tag --tag-message "Release {new_version}"'} patch" + sh "bump-my-version bump patch --new-version ${newVersion} ${allowDirty ? '--allow-dirty': ''} ${isDevRelease ? '--no-commit' : '--tag --tag-message "Release {new_version}"'}" } - getNewVersion = { isDevRelease, includeBuildMeta -> + getNewVersion = { isDevRelease -> // Get a staging or target version and customize with lang specific requirements - return customizeVersion(isDevRelease ? getDevVersion(includeBuildMeta) : getTargetVersion()) + return customizeVersion(isDevRelease ? getDevVersion() : getTargetVersion()) } getTargetVersion = { @@ -196,24 +250,25 @@ void defaultInit() { version = params.TARGET_VERSION } else { // If a target version is not provided default to a patch bump - version = sh returnStdout: true, script: 'bump2version --list --dry-run patch | grep new_version=.* | cut -f2 -d=' + version = sh returnStdout: true, script: 'bump-my-version show-bump --ascii | grep patch | rev | cut -f1 -d " " | rev' } return version.trim() } - getDevVersion = { includeBuildMeta -> + getDevVersion = { devVersion = getTargetVersion() if (devVersion ==~ /${env.SVRE_RELEASE}/) { - // For a release (e.g. 1.0.0) make a -dev pre-release (e.g. 1.0.0-devTS) - devVersion += "-dev${currentBuild.startTimeInMillis}" + // For a release (e.g. 1.0.0) use a hyphen separator (e.g. 1.0.0-dev) + devVersion += "-" } else if (devVersion ==~ /${env.SVRE_PRE_RELEASE}/) { - // For a pre-release (e.g. 1.0.0-b7), add .dev identifier (e.g. 1.0.0-b7.devTS) - devVersion += ".dev${currentBuild.startTimeInMillis}" - } - if (includeBuildMeta) { - // Add uniqueness and build metadata when requested to dev build versions - devVersion += "+${commitHash}.${currentBuild.number}" + // For a pre-release (e.g. 1.0.0-b7), add dot separator (e.g. 1.0.0-b7.dev) + devVersion += "." } + // Now add dev identifier (a number is required by some package managers) + devVersion += "dev0" + // Add uniqueness with build metadata to dev build versions + devVersion += "+git${commitHash}.${currentBuild.startTimeInMillis}.${currentBuild.number}" + return devVersion } @@ -222,34 +277,32 @@ void defaultInit() { semverFormatVersion } + publishArtifactoryBuildInfoScript = { + // put build info on module/artifacts then overwrite and publish artifactory build + sh './scripts/publish_buildinfo.sh' + } + publishArtifactoryBuildInfo = { - // create custom build name e.g. cloudant-sdks/cloudant-node-sdk/generated-branch - buildName = "${env.JOB_NAME}" - buildType = 'GENERIC' // default, may be overridden - customizePublishingInfo() - withEnv(["LIB_NAME=${libName}", - "TYPE=${buildType}", - "ARTIFACT_URL=${artifactUrl}", - "MODULE_ID=${moduleId}", - "BUILD_NAME=${buildName}"]) { - withCredentials([usernamePassword(credentialsId: 'artifactory', passwordVariable: 'ARTIFACTORY_APIKEY', usernameVariable: 'ARTIFACTORY_USER')]) { - // create base build info - rtBuildInfo ( - buildName: "${env.BUILD_NAME}", - buildNumber: "${env.BUILD_NUMBER}", - includeEnvPatterns: ['BRANCH_NAME'], - maxDays: 90, - deleteBuildArtifacts: true, - asyncBuildRetention: true - ) - rtPublishBuildInfo ( - buildName: "${env.BUILD_NAME}", - buildNumber: "${env.BUILD_NUMBER}", - serverId: 'taas-artifactory-upload' - ) - // put build info on module/artifacts then overwrite and publish artifactory build - sh './scripts/publish_buildinfo.sh' - } + // create base build info + rtBuildInfo ( + buildName: "${env.BUILD_NAME}", + buildNumber: "${env.BUILD_NUMBER}", + includeEnvPatterns: ['BRANCH_NAME'], + maxDays: 90, + deleteBuildArtifacts: true, + asyncBuildRetention: true + ) + rtPublishBuildInfo ( + buildName: "${env.BUILD_NAME}", + buildNumber: "${env.BUILD_NUMBER}", + serverId: 'taas-artifactory-upload' + ) + publishArtifactoryBuildInfoScript() + } + + scanCode = { + withSonarQubeEnv(installationName: 'SonarQubeServer') { + sh "${scannerHome}/bin/sonar-scanner -Dsonar.qualitygate.wait=true -Dsonar.projectKey=cloudant-${libName}-sdk -Dsonar.exclusions=examples/**" } } } @@ -269,48 +322,45 @@ void applyCustomizations() { } customizePublishingInfo = { // Set the publishing names and types + buildType = 'GENERIC' artifactUrl = "${STAGE_ROOT}storage/cloudant-sdks-pypi-local/ibmcloudant/${env.NEW_SDK_VERSION}" - moduleId = "ibmcloudant-${env.NEW_SDK_VERSION}" + moduleId = "ibmcloudant:${env.NEW_SDK_VERSION}" } } void runTests() { sh ''' - . /home/jenkins/pythonvenv/bin/activate - python3 -m tox -e py310 + export PIP_INDEX_URL=https://${ARTIFACTORY_CREDS_USR}:${ARTIFACTORY_CREDS_PSW}@${ARTIFACTORY_URL_DOWN##'https://'}/api/pypi/cloudant-sdks-pypi-virtual/simple + pip install -r requirements-dev.txt && flit install --only-deps + pylint --rcfile=.pylintrc ibmcloudant + python3 -m pytest --junitxml=junitreports/junit-pytest.xml --cov=ibmcloudant ''' } void publishStaging() { - withCredentials([usernamePassword(credentialsId: 'artifactory', passwordVariable: 'TWINE_PASSWORD', usernameVariable: 'TWINE_USERNAME')]) { - withEnv(["TWINE_REPOSITORY_URL=${env.STAGE_ROOT}pypi/cloudant-sdks-pypi-local"]) { - publishTwine() - } + withEnv(["FLIT_INDEX_URL=${env.STAGE_ROOT}pypi/cloudant-sdks-pypi-local", 'FLIT_USERNAME=' + env.ARTIFACTORY_CREDS_USR, 'FLIT_PASSWORD=' + env.ARTIFACTORY_CREDS_PSW]) { + publishFlit() } } void publishPublic() { - withCredentials([usernamePassword(credentialsId: 'pypi', passwordVariable: 'TWINE_PASSWORD', usernameVariable: 'TWINE_USERNAME')]) { - publishTwine() + withCredentials([usernamePassword(credentialsId: 'pypi', passwordVariable: 'FLIT_PASSWORD', usernameVariable: 'FLIT_USERNAME')]) { + publishFlit() } } -void publishTwine() { +void publishFlit() { dir('dist') { deleteDir() } sh ''' - . /home/jenkins/pythonvenv/bin/activate python3 --version - python3 setup.py sdist - python3 -m twine upload dist/* + flit publish --no-use-vcs ''' } void publishDocs() { sh ''' - . /home/jenkins/pythonvenv/bin/activate ./scripts/pydoc/publish-doc.sh ''' } - diff --git a/KNOWN_ISSUES.md b/KNOWN_ISSUES.md index 5d9552fd..a33f65cb 100644 --- a/KNOWN_ISSUES.md +++ b/KNOWN_ISSUES.md @@ -1,14 +1,70 @@ - # Limitations, Restrictions, and Known Issues ## All Cloudant SDKs +### Cloudant (Gen 2) Compatibility + +Cloudant Gen 2 is compatible with IAM authentication only. +The SDK authentication types `BASIC` and `COUCHDB_SESSION` do not work with Cloudant Gen 2 instances. + +Instances of Cloudant Gen 2 do not provide some configuration and monitoring APIs that were available in Gen 1. + +Migrating an application using this SDK from Cloudant Gen 1 to Cloudant Gen 2 requires either removing calls to these operations +or replacing them with alternatives. + +This table summarizes the SDK operations that are incompatible with Cloudant Gen 2 instances and the recommended replacement operations. + +| Gen 1 operation | Summary | Gen 1 Endpoint | API docs link | Replacement operation for Gen 2 | +|---|---|---|---|---| +| **Authentication and authorization** | | | | | +| `post_api_keys` | Generates API keys for apps or persons to enable database access | `POST /_api/v2/api_keys` | [API docs link](https://cloud.ibm.com/apidocs/cloudant/cloudant-gen1?code=python#postapikeys) | Use IAM authentication | +| `put_cloudant_security_configuration` | Modify only Cloudant related database permissions | `PUT /_api/v2/db/{db}/_security` | [API docs link](https://cloud.ibm.com/apidocs/cloudant/cloudant-gen1?code=python#putcloudantsecurity) | Use [database level IAM policies](https://cloud.ibm.com/docs/cloudant-gen2?topic=cloudant-gen2-managing-access-for-cloudant#database-level-iam-policies) | +| **Audit events configuration** | | | | | +| `get_activity_tracker_events` | Retrieve activity tracking events information | `GET /_api/v2/user/activity_tracker/events` | [API docs link](https://cloud.ibm.com/apidocs/cloudant/cloudant-gen1?code=python#getactivitytrackerevents) | The `management` events are always enabled. To view the `data` events configuration, use the [Platform Services SDK](#using-the-platform-services-sdk). | +| `post_activity_tracker_events` | Modify activity tracking events configuration | `POST /_api/v2/user/activity_tracker/events` | [API docs link](https://cloud.ibm.com/apidocs/cloudant/cloudant-gen1?code=python#postactivitytrackerevents) | The `management` events are always enabled. To enable the `data` events, use the [Platform Services SDK](#using-the-platform-services-sdk). | +| **CORS configuration** | | | | | +| `get_cors_information` | Retrieve CORS configuration information | `GET /_api/v2/user/config/cors` | [API docs link](https://cloud.ibm.com/apidocs/cloudant/cloudant-gen1?code=python#getcorsinformation) | Use the [Platform Services SDK](#using-the-platform-services-sdk) +| `put_cors_configuration` | Modify CORS configuration | `PUT /_api/v2/user/config/cors` | [API docs link](https://cloud.ibm.com/apidocs/cloudant/cloudant-gen1?code=python#putcorsconfiguration) | Use the [Platform Services SDK](#using-the-platform-services-sdk) +| **Capacity configuration** | | | | | +| `get_capacity_databases_information` | Retrieve maximum allowed database count | `GET /_api/v2/user/capacity/databases` | [API docs link](https://cloud.ibm.com/apidocs/cloudant/cloudant-gen1?code=python#getcapacitydatabasesinformation) | Use the [Platform Services SDK](#using-the-platform-services-sdk) +| `get_capacity_throughput_information` | Retrieve provisioned throughput capacity information | `GET /_api/v2/user/capacity/throughput` | [API docs link](https://cloud.ibm.com/apidocs/cloudant/cloudant-gen1?code=python#getcapacitythroughputinformation) | Use the [Platform Services SDK](#using-the-platform-services-sdk) +| `put_capacity_throughput_configuration` | Update the target provisioned throughput capacity | `PUT /_api/v2/user/capacity/throughput` | [API docs link](https://cloud.ibm.com/apidocs/cloudant/cloudant-gen1?code=python#putcapacitythroughputconfiguration) | Use the [Platform Services SDK](#using-the-platform-services-sdk) +| **Capacity monitoring** | | | | | +| `get_current_databases_information` | Retrieve current database count | `GET /_api/v2/user/current/databases` | [API docs link](https://cloud.ibm.com/apidocs/cloudant/cloudant-gen1?code=python#getcurrentdatabasesinformation) | Use the length of the list returned from the `get_all_dbs` operation. +| `get_current_throughput_information` | Retrieve the current provisioned throughput capacity consumption | `GET /_api/v2/user/current/throughput` | [API docs link](https://cloud.ibm.com/apidocs/cloudant/cloudant-gen1?code=python#getcurrentthroughputinformation) | Use a Prometheus instant query with IBM Cloud Monitoring APIs. For example to get the capacity consumption rate over the last minute use a query like `rate(ibm_cloudant_permitted_operations_total[1m])`. See the [IBM Cloud Monitoring docs for an example](https://cloud.ibm.com/docs/monitoring?topic=monitoring-metrics_api#metrics-api-sample-prom). + +#### Using the Platform Services SDK + +Use the [IBM Cloud Platform Services Python SDK](https://github.com/IBM/platform-services-python-sdk) Resource Controller APIs to programmatically configure your Cloudant Gen 2 instance. + +Update the configuration values in the `parameters` mapping of key-value pairs. +Read the current values from the `extensions` mapping of key-value pairs. + +| Key path in `parameters` or `extensions` | Value description | +| --- | --- | +`dataservices.cloudant.capacity_units` | The provisioned throughput capacity of the instance in [units](https://cloud.ibm.com/docs/cloudant-gen2?topic=cloudant-gen2-usage-and-charges#provisioned-throughput-capacity-units) +`dataservices.cloudant.configuration.cors` | The CORS configuration object of the instance with booleans for `enabled` and `allowCredentials` and a string array of `origins` +`dataservices.cloudant.configuration.audit.data_events` | Boolean to enable (`true`) or disable (`false`) `data` type audit events for the instance +`dataservices.cloudant.configuration.db_count_limit` | Read only value of the maximum number of databases allowed on the instance + +##### Viewing configuration + +Use [Get a resource instance](https://cloud.ibm.com/apidocs/resource-controller/resource-controller?code=python#get-resource-instance) to retrieve the resource instance configuration. + +##### Modifying configuration + +For a new instance use [Create (provision) a new resource instance](https://cloud.ibm.com/apidocs/resource-controller/resource-controller?code=python#create-resource-instance). +For a pre-existing instance use [Update a resource instance](https://cloud.ibm.com/apidocs/resource-controller/resource-controller?code=python#update-resource-instance). + + ### Path elements containing the `+` character -Path elements containing the `+` character in the SDKs are not interoperable with Apache CouchDB and Cloudant. -* This is because standard URL encoding libraries following the [RFC3986 URI specification](https://tools.ietf.org/html/rfc3986#section-3.3) do not encode this character in path elements. -* Apache CouchDB violates the specification by treating the `+` in path elements as a space character (see https://github.com/apache/couchdb/issues/2235). -* Path elements include database names, all document names, and index and view names. +Path elements containing the `+` character in the SDKs are not interoperable with: +* Cloudant +* Apache CouchDB versions older than 3.2.0 +* Apache CouchDB versions 3.2.0 or newer with the setting `decode_plus_to_space = true` + +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. * It is possible to workaround for document names with a `+` in the ID (e.g. `docidwith+char`) by using: * For reading: use the `post` all docs operation and the `key` or `keys` parameter with a value of the document ID including the `+`. * For writing: use the `post` document operation or `post` bulk docs operation with the value of the document ID including the `+`. @@ -22,23 +78,6 @@ Using JSON objects as keys (e.g. `start_key`, `end_key`, `key`, `keys`) can cause inconsistent results because the ordering of the members of the JSON object after serialization is not guaranteed. -### Search - -#### Cannot use `drilldown` parameters - -Drilldown parameters cannot be used for search queries with server versions: -* CouchDB versions < 3.2.0 -* Cloudant (Classic) <= 8158 - -### Changes - -#### Terminated connections - -When using the `post` changes operation the connection may intermittently terminate with an early `EOF` when using -server versions: -* CouchDB versions < 3.2.0 -* Cloudant (Classic) <= 8169 - ### Documents #### Attachments @@ -82,44 +121,40 @@ Example JSON request body: } ``` -### Monitoring, Authorization, and CORS - -The server (Cloudant (Classic) <= 8169) incorrectly processes gzip compressed request bodies for the following endpoints: -| Endpoint | HTTP operation | -|---------------------------------------|----------------| -|`/_api/v2/user/activity_tracker/events`|`POST` | -|`/_api/v2/user/capacity/throughput` |`PUT` | -|`/_api/v2/api_keys` |`POST` | -|`/_api/v2/db/{db}/_security` |`PUT` | -|`/_api/v2/user/config/cors` |`PUT` | - -The workaround is to [disable request body compression](#disabling-request-body-compression). - -### Replication - -The server (Cloudant (Classic) <= 8169) incorrectly processes gzip compressed request bodies for `_replicate` endpoint. -The workaround is to [disable request body compression](#disabling-request-body-compression). +### Compression -The `basic` property of `ReplicationDatabaseAuth` is not available in CouchDB < 3.2.0 and Cloudant <= 8914. For those versions use the `headers` property to add a header with a key of `Authorization` and a value of `Basic `. +* Manually setting an `Accept-Encoding` header on requests will disable the transparent gzip decompression of response bodies from the server. +* Manually setting a `Content-Encoding` header on requests will disable the transparent gzip compression of request bodies to the server. -### Cloudant on Transaction Engine +### Changes feed -Whilst most SDK methods will work with _Cloudant on Transaction Engine_ there are some limitations. -It should be noted that not all existing API options are applicable to _Cloudant on Transaction Engine_ -and new API options added in _Cloudant on Transaction Engine_ are not yet available in the SDKs. Please -consult the Cloudant documentation for further information. +#### Filter functions -### Compression +The SDK does not support passing user-defined query or body parameters in `_changes` requests for dynamic filter functions in design documents. +The workaround and recommended option is to use a `selector` type filter. +For example, if you are using a `_changes` request like `/{db}/_changes?filter=myDdoc/byName&name=Jane` with a filter function like: +```javascript +function(doc, req) { + if (doc.name !== req.query.name) { + return false; + } + return true; +} +``` +It can be replaced with a request using a selector filter: +```python +service = CloudantV1.new_instance() -* Manually setting an `Accept-Encoding` header on requests will disable the transparent gzip decompression of response bodies from the server. -* Manually setting a `Content-Encoding` header on requests will disable the transparent gzip compression of request bodies to the server. +response = service.post_changes( + db='orders', + filter='_selector', + selector={'name': 'Jane'} +).get_result() +``` - - - ## Cloudant SDK for Python - + ### Request bodies containing the `headers` parameter The `headers` dict is always reserved for the API request headers. @@ -154,9 +189,35 @@ The example above represents this JSON body: ### Disabling request body compression +Some issues with older server versions can be worked around by disabling +compression of request bodies. This is an example of how to do that. + ```python from ibmcloudant.cloudant_v1 import CloudantV1 client = CloudantV1.new_instance(service_name="YOUR_SERVICE_NAME") client.set_enable_gzip_compression(False) ... ``` + +### Attachments with `content-type: application/json` + +Calling [`get_attachment`](https://ibm.github.io/cloudant-python-sdk/docs/latest/apidocs/ibmcloudant/ibmcloudant.cloudant_v1.html#ibmcloudant.cloudant_v1.CloudantV1.get_attachment) +typically returns a `DetailedResponse` with `BinaryIO` result. +For attachments uploaded with a `content-type: application/json` +header or as a `.json` file in the dashboard then calls to `get_attachment` +return a `DetailedResponse` with, for example, a `dict` result for a JSON +object. +The JSON is automatically loaded by the underlying SDK core +and client to the default Python object types. + +To get a `BinaryIO` result with an `application/json` attachment +pass `stream=True` on the `get_attachment` request. + +```python +json_attachment: bytes = service.get_attachment( + db='products', + doc_id='1000042', + attachment_name='product_details.json', + stream=True +).get_result().content # content bytes that can be decoded +``` diff --git a/MANIFEST.in b/MANIFEST.in deleted file mode 100644 index 68bc171c..00000000 --- a/MANIFEST.in +++ /dev/null @@ -1,3 +0,0 @@ -include requirements.txt -include requirements-dev.txt -include LICENSE diff --git a/README.md b/README.md index 619999b1..3d3be2c8 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,8 @@ -[![Build Status](https://github.com/IBM/cloudant-python-sdk/actions/workflows/test.yml/badge.svg?branch=master&event=push)](https://github.com/IBM/cloudant-python-sdk/actions/workflows/test.yml) +[![Build Status](https://github.com/IBM/cloudant-python-sdk/actions/workflows/test.yml/badge.svg?branch=main&event=push)](https://github.com/IBM/cloudant-python-sdk/actions/workflows/test.yml) [![Release](https://img.shields.io/github/v/release/IBM/cloudant-python-sdk?include_prereleases&sort=semver)](https://github.com/IBM/cloudant-python-sdk/releases/latest) [![Docs](https://img.shields.io/static/v1?label=Pydoc&message=latest&color=blue)](https://ibm.github.io/cloudant-python-sdk/) -# IBM Cloudant Python SDK Version 0.1.4 +# IBM Cloudant Python SDK Version 0.11.10 IBM Cloudant Python SDK is a client library that interacts with the [IBM Cloudant APIs](https://cloud.ibm.com/apidocs/cloudant?code=python). @@ -18,47 +18,27 @@ to avoid surprises.
Table of Contents - - - - [Overview](#overview) - [Features](#features) - [Prerequisites](#prerequisites) - [Installation](#installation) -- [Authentication](#authentication) - * [Authentication with environment variables](#authentication-with-environment-variables) - + [IAM authentication](#iam-authentication) - + [Session cookie authentication](#session-cookie-authentication) - + [Basic authentication](#basic-authentication) - * [Authentication with external configuration](#authentication-with-external-configuration) - * [Programmatic authentication](#programmatic-authentication) - [Using the SDK](#using-the-sdk) + * [Authentication](#authentication) + * [Automatic retries](#automatic-retries) * [Request timeout configuration](#request-timeout-configuration) * [Code examples](#code-examples) - + [1. Retrieve information from an existing database](#1-retrieve-information-from-an-existing-database) - + [2. Create your own database and add a document](#2-create-your-own-database-and-add-a-document) - + [3. Update your previously created document](#3-update-your-previously-created-document) - + [4. Delete your previously created document](#4-delete-your-previously-created-document) - + [Further code examples](#further-code-examples) * [Error handling](#error-handling) * [Raw IO](#raw-io) * [Model classes vs dictionaries](#model-classes-vs-dictionaries) * [Further resources](#further-resources) - [Questions](#questions) - [Issues](#issues) +- [Versioning and LTS support](#versioning-and-lts-support) - [Open source at IBM](#open-source-at-ibm) - [Contributing](#contributing) - [License](#license) - -
## Overview @@ -79,502 +59,110 @@ project: - Supported by IBM Cloudant. - Server compatibility with: - - IBM Cloudant "Classic". - - [Cloudant "Standard on Transaction Engine"](https://cloud.ibm.com/docs/Cloudant?topic=Cloudant-overview-te) for APIs compatible with Cloudant "Classic". For more information, see the [Feature Parity](https://cloud.ibm.com/docs/Cloudant?topic=Cloudant-overview-te#feature-parity-between-ibm-cloudant-on-the-transaction-engine-vs-classic-architecture) page. - - [Apache CouchDB 3.x](https://docs.couchdb.org/en/stable/) for data operations. + - IBM Cloudant (Gen 1). + - IBM Cloudant (Gen 2) - see [compatibility notes](https://github.com/IBM/cloudant-python-sdk/tree/v0.11.10/KNOWN_ISSUES.md#cloudant-gen-2-compatibility). + - [Apache CouchDB 3.x](https://docs.couchdb.org/en/stable/) for data operations. - Includes all the most popular and latest supported endpoints for applications. - Handles the authentication. - Familiar user experience with IBM Cloud SDKs. - Flexibility to use either built-in models or byte-based requests and responses for documents. +- Built-in [Changes feed follower](https://github.com/IBM/cloudant-python-sdk/tree/v0.11.10/docs/Changes_Follower.md) +- Built-in [Pagination](https://github.com/IBM/cloudant-python-sdk/tree/v0.11.10/docs/Pagination.md) - Instances of the client are unconditionally thread-safe. ## Prerequisites - A - [Cloudant](https://cloud.ibm.com/docs/Cloudant/getting-started.html#step-1-connect-to-your-cloudant-nosql-db-service-instance-on-ibm-cloud) + [Cloudant](https://cloud.ibm.com/docs/Cloudant?topic=Cloudant-getting-started-with-cloudant) service instance or a [CouchDB](https://docs.couchdb.org/en/latest/install/index.html) server. -- Python 3.7 or above. +- Python 3.10, 3.11, 3.12, 3.13 or 3.14 ## Installation To install, use `pip` or `easy_install`: ```bash -pip install --upgrade "ibmcloudant>=0.1.4" +pip install --upgrade "ibmcloudant>=0.11.10" ``` or ```bash -easy_install --upgrade "ibmcloudant>=0.1.4" +easy_install --upgrade "ibmcloudant>=0.11.10" ``` -## Authentication +## Using the SDK -[service-credentials]: https://cloud.ibm.com/docs/Cloudant?topic=Cloudant-locating-your-service-credentials -[cloud-IAM-mgmt]: https://cloud.ibm.com/docs/Cloudant?topic=Cloudant-managing-access-for-cloudant#introduction-iam-ai -[couch-cookie-auth]: https://docs.couchdb.org/en/stable/api/server/authn.html#cookie-authentication -[cloudant-cookie-auth]: https://cloud.ibm.com/docs/Cloudant?topic=Cloudant-work-with-your-account#cookie-authentication -[couch-basic-auth]: https://docs.couchdb.org/en/stable/api/server/authn.html#basic-authentication -[cloudant-basic-auth]: https://cloud.ibm.com/docs/Cloudant?topic=Cloudant-work-with-your-account#basic-authentication +For fundamental SDK usage information and config options, please see the common [IBM Cloud SDK](https://github.com/IBM/ibm-cloud-sdk-common/blob/main/README.md) documentation. -This library requires some of your +This library requires configuration with a service URL and [Cloudant service credentials][service-credentials] to authenticate with your account. -1. `IAM`, `COUCHDB_SESSION`, `BASIC` or `NOAUTH` **authentication type**. - 1. [*IAM authentication*](#iam-authentication) is highly recommended when your - back-end database server is [**Cloudant**][cloud-IAM-mgmt]. This - authentication type requires a server-generated `apikey` instead of a - user-given password. You can create one - [here](https://cloud.ibm.com/iam/apikeys). - 1. [*Session cookie (`COUCHDB_SESSION`) authentication*](#session-cookie-authentication) - is recommended for [Apache CouchDB][couch-cookie-auth] or for - [Cloudant][cloudant-cookie-auth] when IAM is unavailable. It exchanges username - and password credentials for an `AuthSession` cookie from the `/_session` - endpoint. - 1. [*Basic* (or legacy) *authentication*](#basic-authentication) is a fallback - for both [Cloudant][cloudant-basic-auth] and [Apache CouchDB][couch-basic-auth] - back-end database servers. This authentication type requires the good old - `username` and `password` credentials. - 1. *Noauth* authentication does not require credentials. Note that this - authentication type only works with queries against a database with read - access for everyone. -1. The service `url`. - -There are several ways to **set** these properties: - -1. As [environment variables](#authentication-with-environment-variables) -1. The [programmatic approach](#programmatic-authentication) -1. With an [external credentials file](#authentication-with-external-configuration) - -### Authentication with environment variables - -#### IAM authentication - -For Cloudant *IAM authentication*, set the following environmental variables by -replacing the `` and `` with your proper -[service credentials][service-credentials]. There is no need to set -`CLOUDANT_AUTH_TYPE` to `IAM` because it is the default. - -```bash -CLOUDANT_URL= -CLOUDANT_APIKEY= -``` +There are several ways to **set** these authentication properties: -#### Session cookie authentication +1. As [environment variables](https://github.com/IBM/cloudant-python-sdk/tree/v0.11.10/docs/Authentication.md#authentication-with-environment-variables) +2. The [programmatic approach](https://github.com/IBM/cloudant-python-sdk/tree/v0.11.10/docs/Authentication.md#programmatic-authentication) +3. With an [external credentials file](https://github.com/IBM/cloudant-python-sdk/tree/v0.11.10/docs/Authentication.md#authentication-with-external-configuration) -For `COUCHDB_SESSION` authentication, set the following environmental variables -by replacing the ``, `` and `` with your proper -[service credentials][service-credentials]. +The following section describes the different authentication types and provides environment variable examples. +Examples for other configuration methods are available by following the provided links. -```bash -CLOUDANT_AUTH_TYPE=COUCHDB_SESSION -CLOUDANT_URL= -CLOUDANT_USERNAME= -CLOUDANT_PASSWORD= -``` - -#### Basic authentication +### Authentication -For *Basic authentication*, set the following environmental variables by -replacing the ``, `` and `` with your proper -[service credentials][service-credentials]. +Consult the [authentication document](https://github.com/IBM/cloudant-python-sdk/tree/v0.11.10/docs/Authentication.md) +for comprehensive details of all the available authentication methods and how to configure them with environment settings +or programmatically. -```bash -CLOUDANT_AUTH_TYPE=BASIC -CLOUDANT_URL= -CLOUDANT_USERNAME= -CLOUDANT_PASSWORD= +Quick start for Cloudant with an IAM API key: +```sh +CLOUDANT_URL=https://~replace-with-cloudant-host~.cloudantnosqldb.appdomain.cloud # use your own Cloudant public or private URL +CLOUDANT_APIKEY=a1b2c3d4e5f6f1g4h7j3k6l9m2p5q8s1t4v7x0z3 # use your own IAM API key ``` -**Note**: There are also additional [*Bearer token*](https://github.com/IBM/python-sdk-core/blob/main/Authentication.md#bearer-token-authentication), [*Container*](https://github.com/IBM/python-sdk-core/blob/main/Authentication.md#container-authentication) and [*VPC Instance*](https://github.com/IBM/python-sdk-core/blob/main/Authentication.md#vpc-instance-authentication) authentication methods. For more details, please follow the provided links. -We recommend that you use [IAM](#iam-authentication) for Cloudant and -[Session](#session-cookie-authentication) for CouchDB authentication. - -### Authentication with external configuration - -To use an external configuration file, the -[Cloudant API docs](https://cloud.ibm.com/apidocs/cloudant?code=python#authentication-with-external-configuration), -or the -[general SDK usage information](https://github.com/IBM/ibm-cloud-sdk-common#using-external-configuration) -will guide you. - -### Programmatic authentication +Quick start for Apache CouchDB with a username/password session: +```sh +CLOUDANT_AUTH_TYPE=COUCHDB_SESSION +CLOUDANT_URL=https://~replace-with-your-unique-host~.example:6984 # use your CouchDB URL +CLOUDANT_USERNAME=username # replace with your username +CLOUDANT_PASSWORD=password # replace with your password +``` -To learn more about how to use programmatic authentication, see the related -documentation in the -[Cloudant API docs](https://cloud.ibm.com/apidocs/cloudant?code=python#programmatic-authentication) -or in the -[Python SDK Core document](https://github.com/IBM/python-sdk-core/blob/master/Authentication.md) about authentication. +### Automatic retries -## Using the SDK +The SDK supports a generalized retry feature that can automatically retry on common errors. -For fundamental SDK usage information and config options, please see the common [IBM Cloud SDK](https://github.com/IBM/ibm-cloud-sdk-common/blob/master/README.md) documentation. +The [automatic retries](https://github.com/IBM/ibm-cloud-sdk-common#automatic-retries) section has details on how to enable the retries with default values and customize the retries programmatically or with external configuration. ### Request timeout configuration No request timeout is defined, but a 2.5m read and a 60s connect timeout are set by default. Be sure to set a request timeout appropriate to your application usage and environment. -The [request timeout](https://github.com/IBM/ibm-cloud-sdk-common/blob/master/README.md) section contains details on how to change the value. +The [request timeout](https://github.com/IBM/ibm-cloud-sdk-common#configuring-request-timeouts) section contains details on how to change the value. **Note:** System settings may take precedence over configured timeout values. ### Code examples -The following code examples -[authenticate with the environment variables](#authenticate-with-environment-variables). - -#### 1. Retrieve information from an existing database - -**Note:** This example code assumes that `animaldb` database does not exist in your account. - -This example code gathers information about an existing database hosted on -the https://examples.cloudant.com/ service `url`. To connect, you must -extend your environment variables with the *service url* and *authentication -type* to use `NOAUTH` authentication while you connect to the `animaldb` database. -This step is necessary for the SDK to distinguish the `EXAMPLES` custom service -name from the default service name which is `CLOUDANT`. - -Cloudant environment variable naming starts with a *service name* prefix that identifies your service. -By default this is `CLOUDANT`, see the settings in the -[authentication with environment variables section](#authentication-with-environment-variables). - -If you would like to rename your Cloudant service from `CLOUDANT`, -you must use your defined service name as the prefix for all Cloudant related environment variables. -The code block below provides an example of instantiating a user-defined `EXAMPLES` service name. - -```bash -EXAMPLES_URL=https://examples.cloudant.com -EXAMPLES_AUTH_TYPE=NOAUTH -``` - -Once the environment variables are set, you can try out the code examples. - -[embedmd]:# (test/examples/src/get_info_from_existing_database.py /import/ $) -```py -import json - -from ibmcloudant.cloudant_v1 import CloudantV1 - -# 1. Create a Cloudant client with "EXAMPLES" service name ============ -client = CloudantV1.new_instance(service_name="EXAMPLES") - -# 2. Get server information =========================================== -server_information = client.get_server_information( -).get_result() - -print(f'Server Version: {server_information["version"]}') - -# 3. Get database information for "animaldb" ========================== -db_name = "animaldb" - -db_information = client.get_database_information( - db=db_name -).get_result() - -# 4. Show document count in database ================================== -document_count = db_information["doc_count"] - -print(f'Document count in \"{db_information["db_name"]}\" ' - f'database is {document_count}.') - -# 5. Get zebra document out of the database by document id ============ -document_about_zebra = client.get_document( - db=db_name, - doc_id="zebra" -).get_result() - -print(f'Document retrieved from database:\n' - f'{json.dumps(document_about_zebra, indent=2)}') -``` - -When you run the code, you see a result similar to the following output. - -[embedmd]:# (test/examples/output/get_info_from_existing_database.txt) -```txt -Server Version: 2.1.1 -Document count in "animaldb" database is 11. -Document retrieved from database: -{ - "_id": "zebra", - "_rev": "3-750dac460a6cc41e6999f8943b8e603e", - "wiki_page": "http://en.wikipedia.org/wiki/Plains_zebra", - "min_length": 2, - "max_length": 2.5, - "min_weight": 175, - "max_weight": 387, - "class": "mammal", - "diet": "herbivore" -} -``` - -#### 2. Create your own database and add a document - -**Note:** This example code assumes that `orders` database does not exist in your account. - -Now comes the exciting part, you create your own `orders` database and add a document about *Bob Smith* with your own [IAM](#iam-authentication) or -[Basic](#basic-authentication) service credentials. - -
-Create code example - -[embedmd]:# (test/examples/src/create_db_and_doc.py /import/ $) -```py -import logging - -from ibm_cloud_sdk_core import ApiException -from ibmcloudant.cloudant_v1 import CloudantV1, Document - -# Set logging level to show only critical logs -logging.basicConfig(level=logging.CRITICAL) - -# 1. Create a client with `CLOUDANT` default service name ============= -client = CloudantV1.new_instance() - -# 2. Create a database ================================================ -example_db_name = "orders" - -# Try to create database if it doesn't exist -try: - put_database_result = client.put_database( - db=example_db_name - ).get_result() - if put_database_result["ok"]: - print(f'"{example_db_name}" database created.') -except ApiException as ae: - if ae.code == 412: - print(f'Cannot create "{example_db_name}" database, ' + - 'it already exists.') - -# 3. Create a document ================================================ -# Create a document object with "example" id -example_doc_id = "example" -# Setting `id` for the document is optional when "post_document" -# function is used for CREATE. When `id` is not provided the server -# will generate one for your document. -example_document: Document = Document(id=example_doc_id) - -# Add "name" and "joined" fields to the document -example_document.name = "Bob Smith" -example_document.joined = "2019-01-24T10:42:59.000Z" - -# Save the document in the database with "post_document" function -create_document_response = client.post_document( - db=example_db_name, - document=example_document -).get_result() - -# ===================================================================== -# Note: saving the document can also be done with the "put_document" -# function. In this case `doc_id` is required for a CREATE operation: -""" -create_document_response = client.put_document( - db=example_db_name, - doc_id=example_doc_id, - document=example_document -).get_result() -""" -# ===================================================================== - -# Keeping track of the revision number of the document object -# is necessary for further UPDATE/DELETE operations: -example_document.rev = create_document_response["rev"] -print(f'You have created the document:\n{example_document}') -``` - - -
-When you run the code, you see a result similar to the following output. - -[embedmd]:# (test/examples/output/create_db_and_doc.txt) -```txt -"orders" database created. -You have created the document: -{ - "_id": "example", - "_rev": "1-1b403633540686aa32d013fda9041a5d", - "name": "Bob Smith", - "joined": "2019-01-24T10:42:99.000Z" -} -``` - -#### 3. Update your previously created document - -**Note**: This example code assumes that you have created both the `orders` -database and the `example` document by -[running the previous example code](#2-create-your-own-database-and-add-a-document) -successfully. Otherwise, the following error message occurs, "Cannot update document because either 'orders' -database or 'example' document was not found." - -
-Update code example - -[embedmd]:# (test/examples/src/update_doc.py /import/ $) -```py -import json -import logging - -from ibm_cloud_sdk_core import ApiException -from ibmcloudant.cloudant_v1 import CloudantV1 - -# Set logging level to show only critical logs -logging.basicConfig(level=logging.CRITICAL) - -# 1. Create a client with `CLOUDANT` default service name ============= -client = CloudantV1.new_instance() - -# 2. Update the document ============================================== -example_db_name = "orders" -example_doc_id = "example" - -# Try to get the document if it previously existed in the database -try: - document = client.get_document( - db=example_db_name, - doc_id=example_doc_id - ).get_result() - - # ================================================================= - # Note: for response byte stream use: - """ - document_as_byte_stream = client.get_document_as_stream( - db=example_db_name, - doc_id=example_doc_id - ).get_result() - """ - # ================================================================= - - # Add Bob Smith's address to the document - document["address"] = "19 Front Street, Darlington, DL5 1TY" - - # Remove the joined property from document object - if "joined" in document: - document.pop("joined") - - # Update the document in the database - update_document_response = client.post_document( - db=example_db_name, - document=document - ).get_result() - - # ================================================================= - # Note 1: for request byte stream use: - """ - update_document_response = client.post_document( - db=example_db_name, - document=document_as_byte_stream - ).get_result() - """ - # ================================================================= - - # ================================================================= - # Note 2: updating the document can also be done with the - # "put_document" function. `doc_id` and `rev` are required for an - # UPDATE operation, but `rev` can be provided in the document - # object as `_rev` too: - """ - update_document_response = client.put_document( - db=example_db_name, - doc_id=example_doc_id, # doc_id is a required parameter - rev=document["_rev"], - document=document # _rev in the document object CAN replace above `rev` parameter - ).get_result() - """ - # ================================================================= - - # Keeping track of the latest revision number of the document - # object is necessary for further UPDATE/DELETE operations: - document["_rev"] = update_document_response["rev"] - print(f'You have updated the document:\n' + - json.dumps(document, indent=2)) - -except ApiException as ae: - if ae.code == 404: - print('Cannot delete document because either ' + - f'"{example_db_name}" database or "{example_doc_id}" ' + - 'document was not found.') -``` - - -
-When you run the code, you see a result similar to the following output. - -[embedmd]:# (test/examples/output/update_doc.txt) -```txt -{ - "_id": "example", - "_rev": "2-4e2178e85cffb32d38ba4e451f6ca376", - "name": "Bob Smith", - "address": "19 Front Street, Darlington, DL5 1TY" -} -``` - -#### 4. Delete your previously created document +Quick start example to list all databases (assumes environment variable [authentication](#authentication)): -**Note**: This example code assumes that you have created both the `orders` -database and the `example` document by -[running the previous example code](#2-create-your-own-database-and-add-a-document) -successfully. Otherwise, the following error message occurs, "Cannot delete document because either 'orders' -database or 'example' document was not found." - -
-Delete code example - -[embedmd]:# (test/examples/src/delete_doc.py /import/ $) ```py -import logging - -from ibm_cloud_sdk_core import ApiException +# section: code from ibmcloudant.cloudant_v1 import CloudantV1 -# Set logging level to show only critical logs -logging.basicConfig(level=logging.CRITICAL) +service = CloudantV1.new_instance() -# 1. Create a client with `CLOUDANT` default service name ============= -client = CloudantV1.new_instance() +response = service.get_all_dbs().get_result() -# 2. Delete the document ============================================== -example_db_name = "orders" -example_doc_id = "example" - -# Try to get the document if it previously existed in the database -try: - document = client.get_document( - db=example_db_name, - doc_id=example_doc_id - ).get_result() - - delete_document_response = client.delete_document( - db=example_db_name, - doc_id=example_doc_id, # `doc_id` is required for DELETE - rev=document["_rev"] # `rev` is required for DELETE - ).get_result() - - if delete_document_response["ok"]: - print('You have deleted the document.') - -except ApiException as ae: - if ae.code == 404: - print('Cannot delete document because either ' + - f'"{example_db_name}" database or "{example_doc_id}"' + - 'document was not found.') -``` - - -
-When you run the code, you see the following output. - -[embedmd]:# (test/examples/output/delete_doc.txt) -```txt -You have deleted the document. +print(response) ``` -#### Further code examples +[More tutorial examples](https://github.com/IBM/cloudant-python-sdk/tree/v0.11.10/docs/Examples.md) for creating a database +and document create, read, update and delete operations. -For a complete list of code examples, see the [examples directory](examples#examples-for-python). +For a complete list of code examples, see the [examples directory](https://github.com/IBM/cloudant-python-sdk/tree/v0.11.10/examples#examples-for-python). ### Error handling @@ -597,7 +185,7 @@ Response byte stream is supported in functions with the suffix of `_as_stream`. The returned byte stream allows the response body to be consumed without triggering JSON unmarshalling that is typically performed by the SDK. -The [update document](#3-update-your-previously-created-document) section +The [update document](https://github.com/IBM/cloudant-python-sdk/tree/v0.11.10/docs/Examples.md#3-update-your-previously-created-document) section contains examples for both request and response byte stream cases. The API reference contains further examples of using byte streams. @@ -618,7 +206,6 @@ This SDK supports two possible formats to define an HTTP request. One approach u
Example using model class structure -[embedmd]:# (test/examples/src/model_vs_dict/put_ddoc_class.py /from/ $) ```py from ibmcloudant.cloudant_v1 import DesignDocument, CloudantV1, DesignDocumentOptions, SearchIndexDefinition @@ -651,7 +238,6 @@ print(response)
Same example using dictionary structure -[embedmd]:# (test/examples/src/model_vs_dict/put_ddoc_dict.py /from/ $) ```py from ibmcloudant.cloudant_v1 import CloudantV1 @@ -682,7 +268,6 @@ Since model classes and dicts are different data structures, they cannot be comb
This solution will be invalid -[embedmd]:# (test/examples/src/model_vs_dict/put_ddoc_invalid.py /from/ $) ```py from ibmcloudant.cloudant_v1 import CloudantV1, DesignDocument @@ -710,6 +295,7 @@ print(response) ### Further resources +- [Cloudant Python SDK feature docs](https://github.com/IBM/cloudant-python-sdk/tree/v0.11.10/docs) - [Cloudant API docs](https://cloud.ibm.com/apidocs/cloudant?code=python): API reference including usage examples for Cloudant Python SDK API. - [Pydoc](https://ibm.github.io/cloudant-python-sdk/): @@ -729,18 +315,41 @@ IBM Cloud services, ask a question on If you encounter an issue with the project, you are welcome to submit a [bug report](https://github.com/IBM/cloudant-python-sdk/issues). + Before you submit a bug report, search for [similar issues](https://github.com/IBM/cloudant-python-sdk/issues?q=is%3Aissue) and review the -[KNOWN_ISSUES file](KNOWN_ISSUES.md) to verify that your issue hasn't been reported yet. +[KNOWN_ISSUES file](https://github.com/IBM/cloudant-python-sdk/tree/v0.11.10/KNOWN_ISSUES.md) to verify that your issue hasn't been reported yet. + +Please consult the [security policy](https://github.com/IBM/cloudant-python-sdk/security/policy) before opening security related issues. + +## Versioning and LTS support + +This SDK follows semantic versioning with respect to the definition of user facing APIs. +This means under some circumstances breaking changes may occur within a major or minor version +of the SDK related to changes in supported language platforms. + +The SDK is supported on the available LTS releases of the language platform. +The LTS language versions are listed in the prerequisites: +* [LTS versions currently supported by the SDK](https://github.com/IBM/cloudant-python-sdk/#prerequisites) +* [LTS versions for this release of the SDK](#prerequisites) + +Incompatible changes from new language versions are not added to the SDK +until they are available in the minimum supported language version. + +When language LTS versions move out of support the following will happen: +* Existing SDK releases will continue to run on obsolete language versions, but will no longer be supported. +* The minimum language version supported by the SDK will be updated to the next available LTS. +* New language features may be added in subsequent SDK releases that will cause breaking changes + if the new releases of the SDK are used with older, now unsupported, language levels. ## Open source at IBM -Find more open source projects on the [IBM Github](http://ibm.github.io/) page. +Find more open source projects on the [IBM GitHub](http://ibm.github.io/) page. ## Contributing -For more information, see [CONTRIBUTING](CONTRIBUTING.md). +For more information, see [CONTRIBUTING](https://github.com/IBM/cloudant-python-sdk/tree/v0.11.10/CONTRIBUTING.md). ## License -This SDK is released under the Apache 2.0 license. To read the full text of the license, see [LICENSE](LICENSE). +This SDK is released under the Apache 2.0 license. To read the full text of the license, see [LICENSE](https://github.com/IBM/cloudant-python-sdk/tree/v0.11.10/LICENSE). diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 00000000..eecdf578 --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,16 @@ +# Security Policy + +## Supported Versions + +| Version | Supported | +| ------- | ------------------ | +| latest | :white_check_mark: | + +## Reporting a Vulnerability + +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. + +## Vulnerabilities in dependencies + +The SDK is regularly scanned for known CVEs in dependencies and updates to versions with remediations are applied and released as soon as possible. +Reporting known CVEs in dependencies in this repository is not necessary and will not lead to a faster resolution. diff --git a/api-changes.md b/api-changes.md new file mode 100644 index 00000000..906e42ed --- /dev/null +++ b/api-changes.md @@ -0,0 +1,186 @@ +# `0.8.0` + +For versions earlier than `0.8.0` the model classes for document types: +* `Document` +* `DesignDocument` +* `ReplicationDocument` + +were generated with standard Python convention names for all fields. +By convention a leading `_` in Python implies internal use so leading `_` were +not used for the reserved CouchDB names in the Python model classes and +were added during serialization. + +This representation of the `Document` model did not allow for members with the +same names as the reserved (`_` prefixed) document metadata members. + +This meant that members named any of the following were removed by the `Document` +`from_dict` and `to_dict` functions: +* `attachments` +* `conflicts` +* `deleted` +* `deleted_conflicts` +* `id` +* `local_seq` +* `rev` +* `revisions` +* `revs_info` + +as described in [issue #490](https://github.com/IBM/cloudant-python-sdk/issues/490). + +To resolve this problem, starting from version `0.8.0` model classes that accept +user defined properties use the leading `_` CouchDB convention for +CouchDB metadata property names instead of using the Python convention. +This introduces breaking changes that require code updates for usages +of the model types `Document`, `DesignDocument` and `ReplicationDocument`. + +## Breaking changes + +The kwarg or attribute names that changed are: +| kwarg/attribute name (<`0.8.0`) | kwarg/attribute name (>=`0.8.0`) | +| --- | --- | +| `attachments`| `_attachments` | +| `conflicts`| `_conflicts` | +| `deleted`| `_deleted` | +| `deleted_conflicts`| `_deleted_conflicts` | +| `id`| `_id` | +| `local_seq`| `_local_seq` | +| `rev`| `_rev` | +| `revisions`| `_revisions` | +| `revs_info`| `_revs_info` | + +_Note:_ Dictionary literals always used the `_` prefixed form of the +name so there are no code changes in those cases. + +### Writing + +In the case of writing to the server the names are +kwarg parameters used to initialize these classes: +* `Document` +* `DesignDocument` +* `ReplicationDocument` + +The functions that impacted by these changes are: +1. Functions that accept `Document` in the `document` kwarg: + * `post_document` + * `put_document` + * `put_local_document` +1. Functions that accept `DesignDocument` in the `design_document` kwarg: + * `put_design_document` +1. Functions that accept `ReplicationDocument` in the `replication_document` kwarg: + * `put_replication_document` +1. Functions that accept `BulkDocs` in the `bulk_docs` kwarg. In this case the +changes are in the elements of the `List[Document]` in the `docs` kwarg: + * `post_bulk_docs` + +#### Example class initialization + +Before: +```python +# id is used in Document initializer +my_doc = Document( + id="small-appliances:1000042", + type="product", + productid="1000042", + name="Fidget toy") + +result = service.post_document(db='products', document=my_doc).get_result() +``` + +After: +```python +# Now _id is used in Document initializer +my_doc = Document( + _id="small-appliances:1000042", + type="product", + productid="1000042", + name="Fidget toy") + +result = service.post_document(db='products', document=my_doc).get_result() +``` + +#### Example dict literal + +Before & After (no changes): +```python +# _id is used in dict literal +my_doc = { + '_id': 'small-appliances:1000042', + 'type': 'product', + 'productid': '1000042', + 'name': 'Fidget toy' +} + +result = service.post_document(db='products', document=my_doc).get_result() +``` + +### Reading + +In the case of reading from the server the `_` prefixed names were always used in the raw +dictionaries returned from the `get_result` function. As such **no changes** are necessary +to the key names to read the values from these result dicts. However, renames are necessary +if the calling code uses the `from_dict` function to convert the result dict to a model class. + +The functions impacted in that case are: +1. Functions returning a `dict` that represents a `Document`: + * `get_document` + * `get_local_document` +1. Functions returning a `dict` that represents a `DesignDocument`: + * `get_design_document` +1. Functions returning a `dict` that represents a `ReplicationDocument`: + * `get_replication_document` +1. Functions returning a `dict` containing a `Document` representation: + * `post_bulk_get` (via `BulkGetResult` `results` > `BulkGetResultItem` `docs` >`BulkGetResultDocument` `ok`) +1. Functions returning a `dict` potentially containing a `Document` representation (for example if using `include_docs`): + * `post_all_docs` (via `AllDocsResult` `rows` > `DocsResultRow` `doc`) + * `post_changes` (via `ChangesResult` `results` > `ChangesResultItem` `doc`) + * `post_find` (via `FindResult` `docs`) + * `post_partition_find`(via `FindResult` `docs`) + * `post_search` (via `SearchResult` `rows` > `SearchResultRow` `doc` or `SearchResult` `groups` > `SearchResultProperties` `rows` > `SearchResultRow` `doc`) + * `post_partition_search` (via `SearchResult` `rows` > `SearchResultRow` `doc` or `SearchResult` `groups` > `SearchResultProperties` `rows` > `SearchResultRow` `doc`) + * `post_view` (via `ViewResult` `rows` > `ViewResultRow` `doc`) + * `post_partition_view` (via `ViewResult` `rows` > `ViewResultRow` `doc`) + +#### Example result + +Before & after (no changes): +```python +result = service.get_document( + db='products', + doc_id='small-appliances:1000042' +).get_result() + +# _id is used to access document id in result dict +print(result._id) +# prints: +# small-appliances:1000042 +``` + +#### Example `from_dict` + +Before: +```python +result = service.get_document( + db='products', + doc_id='small-appliances:1000042' +).get_result() + +doc = Document.from_dict(result) +# id is used to access document id in Document class +print(doc.id) +# prints: +# small-appliances:1000042 +``` + +After: +```python +result = service.get_document( + db='products', + doc_id='small-appliances:1000042' +).get_result() + +doc = Document.from_dict(result) +# Now _id is used to access document id in Document class +print(doc._id) +# prints: +# small-appliances:1000042 +``` diff --git a/docs/Authentication.md b/docs/Authentication.md new file mode 100644 index 00000000..d40a7142 --- /dev/null +++ b/docs/Authentication.md @@ -0,0 +1,161 @@ +# Authentication + + +
+Table of Contents + + +- [Authenticators](#authenticators) +- [Authentication with environment variables](#authentication-with-environment-variables) + * [IAM API key authentication](#iam-api-key-authentication) + * [IAM Trusted profile (container) authentication](#iam-trusted-profile-container-authentication) + * [IAM Trusted profile (VPC) authentication](#iam-trusted-profile-vpc-authentication) + * [IAM Trusted profile (assume identity) authentication](#iam-trusted-profile-assume-identity-authentication) + * [Session cookie authentication](#session-cookie-authentication) + * [Bearer token authentication](#bearer-token-authentication) + * [Basic authentication](#basic-authentication) +- [Authentication with external configuration](#authentication-with-external-configuration) +- [Programmatic authentication](#programmatic-authentication) +
+ +## Authenticators + +This library requires credentials to authenticate with IBM Cloudant. These credentials may be: +* IBM Cloud IAM credentials (with authentication types `CONTAINER`, `VPC`, `IAMASSUME` and `IAM`) + * [IBM Cloud account](https://cloud.ibm.com/docs/Cloudant?topic=Cloudant-managing-access-for-cloudant#introduction-iam-ai) user, service ID or trusted profile credentials + that have access granted to the IBM Cloud Cloudant resource instance. + * [IBM Cloudant service credentials](https://cloud.ibm.com/docs/Cloudant?topic=Cloudant-locating-your-service-credentials) generated by the IBM Cloud Cloudant resource instance. +* Username and password credentials (with authentication types `COUCHDB_SESSION` and `BASIC`) + * [IBM Cloudant service credentials](https://cloud.ibm.com/docs/Cloudant?topic=Cloudant-locating-your-service-credentials) generated for an IBM Cloud Cloudant resource instance not configured as `IAM only`. + * IBM Cloudant [legacy credentials](https://cloud.ibm.com/docs/Cloudant?topic=Cloudant-work-with-your-account#basic-authentication) (username and password) for instances not in IBM Cloud. + * IBM Cloudant [legacy API keys](https://cloud.ibm.com/docs/Cloudant?topic=Cloudant-work-with-your-account#api-keys). + +| Authentication type | Recommended for | `AUTH_TYPE` | Description | +| --- | --- | --- | --- | +| IAM Trusted Profiles (compute resource [container](https://github.com/IBM/python-sdk-core/blob/main/Authentication.md#container-authentication)) | Cloudant
(SDK running in IBM Cloud Containers, including Serverless) | `CONTAINER` | Obtains a compute resource (CR) token from the container.
Exchanges the CR token for an IAM `access_token`.
Adds an `Authorization` header to each HTTP request with the `access_token` bearer.
Automatically renews the access token when needed. | +| IAM Trusted Profiles (compute resource [VPC](https://github.com/IBM/python-sdk-core/blob/main/Authentication.md#vpc-instance-authentication)) | Cloudant
(SDK running in IBM Cloud VPC) | `VPC` | Obtains an identity token from the VPC instance metadata.
Exchanges the identity token for an IAM `access_token`.
Adds an `Authorization` header to each HTTP request with the `access_token` bearer.
Automatically renews the access token when needed. | +| IAM Trusted Profiles ([assume identity](https://github.com/IBM/python-sdk-core/blob/main/Authentication.md#identity-and-access-management-iam-authentication-grant-type-assume)) | Cloudant | `IAMASSUME` | Exchanges an IAM API key for an IAM `access_token` (same as `IAM` auth type).
Uses that initial token to obtain a second `access_token` from IAM with the assumed identity information.
Adds an `Authorization` header to each HTTP request with the `access_token` bearer.
Automatically renews the access token when needed. | +| [IAM API key](https://github.com/IBM/python-sdk-core/blob/main/Authentication.md#identity-and-access-management-iam-authentication-grant-type-apikey) | Cloudant | `IAM` | Exchanges an IAM API key for an IAM `access_token`.
Adds an `Authorization` header to each HTTP request with the `access_token` bearer.
Automatically renews the access token when needed. | +| [Session cookie](#session-cookie-authentication) | [Cloudant](https://cloud.ibm.com/docs/Cloudant?topic=Cloudant-work-with-your-account#cookie-authentication)
(legacy credentials & instances without IAM)

[Apache CouchDB](https://docs.couchdb.org/en/stable/api/server/authn.html#cookie-authentication) | `COUCHDB_SESSION` | Exchanges credentials with `/_session` endpoint to retrieve a cookie.
Adds `Cookie` header and content to each HTTP request.
Automatically renews session when needed. | +| [Bearer token](https://github.com/IBM/python-sdk-core/blob/main/Authentication.md#bearer-token-authentication) | [Apache CouchDB](https://docs.couchdb.org/en/stable/api/server/authn.html#jwt-authentication)
(using JWT authentication) | `BEARERTOKEN` | Adds an `Authorization` header to each HTTP request with the bearer token.
No token management or renewal.
Also compatible with IAM access tokens managed independently of the SDK. | +| [Basic](https://github.com/IBM/python-sdk-core/blob/main/Authentication.md#basic-authentication) | [Apache CouchDB](https://docs.couchdb.org/en/stable/api/server/authn.html#basic-authentication)
(if cookies are not enabled) | `BASIC` | Adds an `Authorization` header to each HTTP request with the base64 encoded basic credentials. | +| [None](https://github.com/IBM/python-sdk-core/blob/main/Authentication.md#no-auth-authentication) | - | `NOAUTH` | Note that this authentication type only works for operations against a database allowing access for unauthenticated users. | + +The default authentication type for the SDK is `CONTAINER` unless supplying `APIKEY` configuration, which changes the default authentication type to `IAM`. + +## Authentication with environment variables + +The default service name is `CLOUDANT` so these examples use `CLOUDANT_` prefixed names. + +Any custom service name prefix is valid, provided it matches the name used to instantiate the SDK client +and applied to all configuration options. + +### IAM API key authentication + +For Cloudant *IAM API key authentication*, set the following environmental variables by +amending the values with your own +[service credentials](https://cloud.ibm.com/docs/Cloudant?topic=Cloudant-locating-your-service-credentials). There is no need to set +`CLOUDANT_AUTH_TYPE` to `IAM` because it is the default when supplying an `APIKEY`. + +```sh +CLOUDANT_URL=https://~replace-with-cloudant-host~.cloudantnosqldb.appdomain.cloud # use your own Cloudant public or private URL +CLOUDANT_APIKEY=a1b2c3d4e5f6f1g4h7j3k6l9m2p5q8s1t4v7x0z3 # use your own IAM API key +``` + +### IAM Trusted profile (container) authentication + +For Cloudant *IAM Trusted profile compute resource container authentication*, set the following environmental variables, +amending with your own correct values. There is no need to set +`CLOUDANT_AUTH_TYPE` to `CONTAINER` because it is the default. + +```sh +CLOUDANT_URL=https://~replace-with-cloudant-host~.cloudantnosqldb.appdomain.cloud # use your own Cloudant public or private URL +CLOUDANT_IAM_PROFILE_ID=Profile-00000000-0000-0000-0000-000000000000 # use your own IAM Profile ID +``` + +Alternatives to `CLOUDANT_IAM_PROFILE_ID`: +* `CLOUDANT_IAM_PROFILE_NAME` + +### IAM Trusted profile (VPC) authentication + +For Cloudant *IAM Trusted profile compute resource vpc authentication*, set the following environmental variables, +amending with your own correct values. + +```sh +CLOUDANT_AUTH_TYPE=VPC +CLOUDANT_URL=https://~replace-with-cloudant-host~.cloudantnosqldb.appdomain.cloud # use your own Cloudant public or private URL +CLOUDANT_IAM_PROFILE_ID=Profile-00000000-0000-0000-0000-000000000000 # use your own IAM Profile ID +``` + +Alternatives to `CLOUDANT_IAM_PROFILE_ID`: +* `CLOUDANT_IAM_PROFILE_CRN` +* No profile information (uses the default trusted profile linked to the compute resource) + +### IAM Trusted profile (assume identity) authentication + +For Cloudant *IAM Trusted profile assume authentication*, set the following environmental variables, +amending with your own correct values. + +```sh +CLOUDANT_AUTH_TYPE=IAMASSUME +CLOUDANT_URL=https://~replace-with-cloudant-host~.cloudantnosqldb.appdomain.cloud # use your own Cloudant public or private URL +CLOUDANT_IAM_PROFILE_ID=Profile-00000000-0000-0000-0000-000000000000 # use your own IAM Profile ID +``` + +Alternatives to `CLOUDANT_IAM_PROFILE_ID`: +* `CLOUDANT_IAM_PROFILE_CRN` +* `CLOUDANT_IAM_PROFILE_NAME` *and* `CLOUDANT_IAM_ACCOUNT_ID` (ID of the account that contains the named trusted profile) + +### Session cookie authentication + +For `COUCHDB_SESSION` authentication, set the following environmental variables +amending with your own [service credentials](https://cloud.ibm.com/docs/Cloudant?topic=Cloudant-locating-your-service-credentials). + +```sh +CLOUDANT_AUTH_TYPE=COUCHDB_SESSION +CLOUDANT_URL=https://~replace-with-cloudant-host~.cloudantnosqldb.appdomain.cloud # use your own Cloudant public or private URL +CLOUDANT_USERNAME=username # replace with your Cloudant legacy username +CLOUDANT_PASSWORD=password # replace with your Cloudant legacy password or API key (not IAM) +``` + +### Bearer token authentication + +Preferably use IAM authentication methods to automatically manage bearer tokens. + +For *bearer token authentication*, set the following environmental variables, +amending with your own correct values. + +```sh +CLOUDANT_AUTH_TYPE=BEARERTOKEN +CLOUDANT_URL=https://~replace-with-cloudant-host~.cloudantnosqldb.appdomain.cloud # use your own Cloudant public or private URL +CLOUDANT_BEARER_TOKEN=A1b2C3QiOiIyMDE4MDgxNDAwMDAwMDAwMDAwMDBjNzYwNzY2YjYxYjYwYjYwIiwidHlwIjoiSldUIiwiYWxnIjoiUlMyNTYifQ.eyJzdWIiOiJ1c2VyQGdtYWlsLmNvbSIsImF1ZCI6Imh0dHBzOi8vaWF1LmNsb3VkLmlibS5jb20iLCJpYXQiOjE2ODg4ODg4ODgsImV4cCI6MTY4ODg5MjQ4OCwiaXNzIjoiaHR0cHM6Ly9pYXUuY2xvdWQuaWJtLmNvbSIsInNjb3BlIjpbImNsb3VkLnJlYWRlciJdfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c # replace with your bearer token +``` + +### Basic authentication + +Preferably use [session cookie authentication](#session-cookie-authentication) instead. + +To use *basic HTTP authentication* set the following environmental variables, +amending with your own correct values. + +```sh +CLOUDANT_AUTH_TYPE=BASIC +CLOUDANT_URL=https://~replace-with-cloudant-host~.cloudantnosqldb.appdomain.cloud # use your own Cloudant public or private URL +CLOUDANT_USERNAME=username # replace with your Cloudant legacy username +CLOUDANT_PASSWORD=password # replace with your Cloudant legacy password or API key (not IAM) +``` + +## Authentication with external configuration + +For more information about using an external configuration file, see the related documentation in +[Cloudant API docs](https://cloud.ibm.com/apidocs/cloudant?code=python#authentication-with-external-configuration), +or the +[general SDK usage information](https://github.com/IBM/ibm-cloud-sdk-common#using-external-configuration). + +## Programmatic authentication + +To learn more about how to use programmatic authentication, see the related +documentation in the +[Cloudant API docs](https://cloud.ibm.com/apidocs/cloudant?code=python#programmatic-authentication) +or in the +[Python SDK Core document](https://github.com/IBM/python-sdk-core/blob/main/Authentication.md) about authentication. diff --git a/docs/Changes_Follower.md b/docs/Changes_Follower.md new file mode 100644 index 00000000..47933be9 --- /dev/null +++ b/docs/Changes_Follower.md @@ -0,0 +1,281 @@ +# Changes feed follower + +
+Table of Contents + + +- [Introduction](#introduction) +- [Modes of operation](#modes-of-operation) +- [Configuring the changes follower](#configuring-the-changes-follower) +- [Error suppression](#error-suppression) +- [Follower operation](#follower-operation) +- [Checkpoints](#checkpoints) +- [Code examples](#code-examples) + * [Initializing a changes follower](#initializing-a-changes-follower) + * [Starting the changes follower](#starting-the-changes-follower) + + [Start mode for continuous listening](#start-mode-for-continuous-listening) + + [Start mode for one-off fetching](#start-mode-for-one-off-fetching) + * [Processing changes](#processing-changes) + + [Process continuous changes](#process-continuous-changes) + + [Process one-off changes](#process-one-off-changes) + * [Stopping the changes follower](#stopping-the-changes-follower) +
+ +## Introduction + +The SDK provides a changes feed follower utility. +This helper utility connects to the `_changes` endpoint and returns the individual change items. +It removes some complexity of using the `_changes` endpoint by setting some options automatically +and providing error suppression and retries. + +*Tip: the changes feed often does not meet user expectations or assumptions.* + +Consult the [Cloudant changes feed FAQ](https://cloud.ibm.com/docs/Cloudant?topic=Cloudant-faq-using-changes-feed) +to get a better understanding of the limitations and suitable use-cases before using the changes feed in your application. + +## Modes of operation + +There are two modes of operation: +* Start mode + * Fetches the changes from the supplied `since` sequence (in this mode follower defaults to reading the feed from `now`). + * Fetches all available changes and then continues listening for new changes indefinitely unless encountering an end condition. + * An example use case for this mode is event driven workloads. +* Start one-off mode + * Fetches the changes from the supplied `since` sequence (in this mode follower defaults to reading the feed from the beginning). + * Fetches all available changes and then stops when either there are no further changes pending or encountering an end condition. + * An example use case for this mode is ETL style workloads. + +## Configuring the changes follower + +The SDK's model of changes feed options is also used to configure the follower. +However, it is invalid to configure some options used internally by the follower implementation. +Supplying these options when instantiating the follower causes an error. +The invalid options are: +* `descending` +* `feed` +* `heartbeat` +* `lastEventId` - use `since` instead +* `timeout` +* Follower permits only the value of `_selector` for the `filter` option. This restriction is because selector + based filters perform better than JavaScript backed filters. Configuring a non-selector based filter + causes the follower to error. + +Note that the `limit` parameter terminates the follower at the given number of changes in either +operating mode. + +The changes follower requires the client to have HTTP timeouts of at least 1 minute and errors during +instantiation if it is insufficient. The default client configuration has sufficiently long timeouts. + +For use-cases where these configuration limitations are too restrictive then write code to use the SDK's +[POST `_changes` API](https://github.com/IBM/cloudant-python-sdk/tree/v0.11.10/examples#postchanges) instead of the follower. + +## Error suppression + +By default, the changes follower suppresses transient errors indefinitely and attempts to run to completion or listen forever as +dictated by the operating mode. +For applications where that is not desirable configure the optional error tolerance duration. This controls the time since +the last successful response that the follower suppresses transient errors. An example usage is an application grace period +before reporting an error and requiring intervention. + +There are some additional points to consider for error suppression: +* Errors considered terminal, for example, the database not existing or invalid credentials are never suppressed and error immediately. +* The error suppression duration is not guaranteed to fire immediately after lapsing and is a minimum suppression time. +* The changes follower backs-off between retries and as such may remain paused for a short while after the transient errors have resolved. +* If the underlying SDK client used to initialize the follower also has retries configured then suppression of errors may last + significantly longer than the follower's configured error tolerance duration depending on the specific options. + +## Follower operation + +For both modes: +* The end conditions are: + * A terminal error (HTTP codes `400`, `401`, `403` `404`). + * Transient errors occur for longer than the error tolerance duration. Transient errors are all other HTTP status codes and connection errors. + * The number of changes received reaches the configured `limit`. + * The application calls stop to terminate the feed early. + +As is true for the `_changes` endpoint change items have *at least once* delivery and callers may receive +an individual item multiple times. When using the follower change items may repeat even within a limited +number of changes (that is using the `limit` option) this is a minor difference from using `limit` on the HTTP native API. + +The follower is not optimized for some use cases and it is not recommended to use it in cases where: +* Setting `include_docs` and larger document sizes (for example > 10 kiB). +* The volume of changes is very high (if the rate of changes in the database exceeds the follower's rate of pulling them it can never catch-up). + +In these use-cases use the SDK's [POST `_changes` API](https://github.com/IBM/cloudant-python-sdk/tree/v0.11.10/examples#postchanges) +for specific control over the number of change requests made and the content size of the responses. + +## Checkpoints + +The changes follower does not checkpoint since it has no information about whether the consuming application +has processed a change item after delivery. It is the application developer's responsibility +to store the sequence IDs to have appropriate checkpoints and to re-initialize the follower with the required +`since` value after, for example, the application restarts. + +The frequency and conditions for checkpoints are application specific and some applications may be tolerant +of dropped changes. This section provides only general guidance on how to avoid missing changes. + +To guarantee processing of all changes do not persist the sequence ID from a change item until *after* +the processing of the change item by the application has completed. As indicated previously change item +delivery is *at least once* so application code must be able to handle repeated changes already. It is +preferable to restart from an older `since` value and receive changes again than risk missing them. + +The sequence IDs are available on each change item by default. However, the server omits sequence IDs from +some change items when using the `seq_interval` configuration option. +Infrequent sequence IDs may improve performance by reducing the amount of data transfer and server load, +but the tradeoff is repeating more changes if it is necessary to resume the changes follower. + +Take extreme care persisting sequences if choosing to process change items in parallel as there +is a considerable risk of missing changes on a restart if the recorded sequence is out of order. + +## Code examples + +### Initializing a changes follower +```py +import ChangesFollower +from ibmcloudant.cloudant_v1 import CloudantV1 + +client = CloudantV1.new_instance() + +cf_params = { + 'db': 'example', # Required: the database name. + 'limit': 100, # Optional: return only 100 changes (including duplicates). + 'since': '3-g1AG3...' # Optional: start from this sequence ID (e.g. with a value read from persistent storage). +} + +changes_follower = ChangesFollower( + service=client, # Required: the Cloudant service client instance. + error_tolerance=10000, # Optional: suppress transient errors for at least 10 seconds before terminating. + **cf_params # Required: changes feed configuration options dict. +) +``` + +### Starting the changes follower + +#### Start mode for continuous listening +```py +import Iterable + +from ibmcloudant import ChangesFollower +from ibmcloudant.cloudant_v1 import CloudantV1, ChangesResultItem + +client = CloudantV1.new_instance() + +changes_follower = ChangesFollower( + service=client, + **{'db': 'example'}) + +changes_items: Iterable[ChangesResultItem] = changes_follower.start() +# Note: iterable will not do anything until it is iterated +# Create a for loop to iterate over the flow of changes +# for changes_item in changes_items: ... +``` + +#### Start mode for one-off fetching +```py +import Iterable + +from ibmcloudant import ChangesFollower +from ibmcloudant.cloudant_v1 import CloudantV1, ChangesResultItem + +client = CloudantV1.new_instance() + +changes_follower = ChangesFollower( + service=client, + **{'db': 'example'}) + +changes_items: Iterable[ChangesResultItem] = changes_follower.start_one_off() +# Note: iterable will not do anything until it is iterated +# Create a for loop to iterate over the flow of changes +# for changes_item in changes_items: ... +``` + +### Processing changes + +#### Process continuous changes +```py +import ChangesFollower +from ibmcloudant.cloudant_v1 import CloudantV1 + +client = CloudantV1.new_instance() + +# Start from a previously persisted seq +# Normally this would be read by the app from persistent storage +# e.g. previously_persisted_seq = your_app_persistence_read_func() +previously_persisted_seq = '3-g1AG3...' +changes_follower = ChangesFollower( + service=client, + **{'db': 'example', 'since': previously_persisted_seq}) + +changes_items = changes_follower.start() +for changes_item in changes_items: + # do something with changes + print(changes_item.id) + for change in changes_item.changes: + print(change.rev) + # when change item processing is complete app can store seq + seq = changes_item.seq + # write seq to persistent storage for use as since if required to resume later + # e.g. your_app_persistence_write_func(seq) + # keep processing changes until the application is terminated or some other stop condition is reached + +# Note: iterator above is blocking, code here will be unreachable +# until the iteration is stopped or another stop condition is reached. +# For long running followers careful consideration should be made of where to call stop on the iterator. +``` + +#### Process one-off changes +```py +import ChangesFollower +from ibmcloudant.cloudant_v1 import CloudantV1 + +client = CloudantV1.new_instance() + +# Start from a previously persisted seq +# Normally this would be read by the app from persistent storage +# e.g. previously_persisted_seq = your_app_persistence_read_func() +previously_persisted_seq = '3-g1AG3...' +changes_follower = ChangesFollower( + service=client, + **{'db': 'example', 'since': previously_persisted_seq}) + +changes_items = changes_follower.start_one_off() +for changes_item in changes_items: + # do something with changes + print(changes_item.id) + for change in changes_item.changes: + print(change.rev) + # when change item processing is complete app can store seq + seq = changes_item.seq + # write seq to persistent storage for use as since if required to resume later + # e.g. your_app_persistence_write_func(seq) + +# Note: iterator above is blocking, code here will be unreachable +# until all changes are processed (or another stop condition is reached). +``` + +### Stopping the changes follower +```py +import ChangesFollower +from ibmcloudant.cloudant_v1 import CloudantV1 + +client = CloudantV1.new_instance() +changes_follower = ChangesFollower( + service=client, + **{'db': 'example'}) +changes_items = changes_follower.start() + +for changes_item in changes_items: + # Option 1: call stop after some condition + # Note that since the iterator is blocking at least one item + # must be returned from it to reach to this point. + # Additional changes may be processed before the iterator stops. + changes_follower.stop() + +# Option 2: call stop method when you want to end the continuous loop from +# outside the iterator. For example, you've put the changes follower in a +# separate thread and need to call stop on the main thread. +# Note: in this context the call must be made from a different thread because +# code immediately following the iterator is unreachable until the iterator +# has stopped. +changes_follower.stop() +``` diff --git a/docs/Examples.md b/docs/Examples.md new file mode 100644 index 00000000..8f0f31bd --- /dev/null +++ b/docs/Examples.md @@ -0,0 +1,325 @@ +# Code examples + +
+Table of Contents + + +- [1. Create a database and add a document](#1-create-a-database-and-add-a-document) +- [2. Retrieve information from an existing database](#2-retrieve-information-from-an-existing-database) +- [3. Update your previously created document](#3-update-your-previously-created-document) +- [4. Delete your previously created document](#4-delete-your-previously-created-document) +- [Further code examples](#further-code-examples) +
+ +The following code examples +[authenticate with the environment variables](Authentication.md#authentication-with-environment-variables). + +## 1. Create a database and add a document + +**Note:** This example code assumes that `orders` database does not exist in your account. + +This example code creates `orders` database and adds a new document "example" +into it. To connect, you must set your environment variables with +the *service url*, *authentication type* and *authentication credentials* +of your Cloudant service. + +Cloudant environment variable naming starts with a *service name* prefix that identifies your service. +By default, this is `CLOUDANT`, see the settings in the +[authentication with environment variables section](Authentication.md#authentication-with-environment-variables). + +If you would like to rename your Cloudant service from `CLOUDANT`, +you must use your defined service name as the prefix for all Cloudant related environment variables. + +Once the environment variables are set, you can try out the code examples. + +```py +from ibm_cloud_sdk_core import ApiException +from ibmcloudant.cloudant_v1 import CloudantV1, Document + +# 1. Create a client with `CLOUDANT` default service name ============= +client = CloudantV1.new_instance() + +# 2. Create a database ================================================ +example_db_name = "orders" + +# Try to create database if it doesn't exist +try: + put_database_result = client.put_database( + db=example_db_name + ).get_result() + if put_database_result["ok"]: + print(f'"{example_db_name}" database created.') +except ApiException as ae: + if ae.status_code == 412: + print(f'Cannot create "{example_db_name}" database, ' + + 'it already exists.') + +# 3. Create a document ================================================ +# Create a document object with "example" id +example_doc_id = "example" +# Setting `_id` for the document is optional when "post_document" +# function is used for CREATE. When `_id` is not provided the server +# will generate one for your document. +example_document: Document = Document(_id=example_doc_id) + +# Add "name" and "joined" fields to the document +example_document.name = "Bob Smith" +example_document.joined = "2019-01-24T10:42:59.000Z" + +# Save the document in the database with "post_document" function +create_document_response = client.post_document( + db=example_db_name, + document=example_document +).get_result() + +# ===================================================================== +# Note: saving the document can also be done with the "put_document" +# function. In this case `doc_id` is required for a CREATE operation: +""" +create_document_response = client.put_document( + db=example_db_name, + doc_id=example_doc_id, + document=example_document +).get_result() +""" +# ===================================================================== + +response_body = json.dumps(create_document_response, indent=2) +print(f'You have created the document. Response body:\n{response_body}') +``` + +When you run the code, you see a result similar to the following output. + +```text +"orders" database created. +You have created the document. Response body: +{ + "ok": true, + "id": "example", + "rev": "1-1b403633540686aa32d013fda9041a5d" +} +``` + +## 2. Retrieve information from an existing database + +**Note**: This example code assumes that you have created both the `orders` +database and the `example` document by +[running the previous example code](#1-create-a-database-and-add-a-document) +successfully. Otherwise, the following error message occurs, "Cannot delete document because either 'orders' +database or 'example' document was not found." + +
+Gather database information example + +```py +import json + +from ibmcloudant.cloudant_v1 import CloudantV1 + +# 1. Create a client with `CLOUDANT` default service name ============ +client = CloudantV1.new_instance() + +# 2. Get server information =========================================== +server_information = client.get_server_information( +).get_result() + +print(f'Server Version: {server_information["version"]}') + +# 3. Get database information for "orders" ========================== +db_name = "orders" + +db_information = client.get_database_information( + db=db_name +).get_result() + +# 4. Show document count in database ================================== +document_count = db_information["doc_count"] + +print(f'Document count in \"{db_information["db_name"]}\" ' + f'database is {document_count}.') + +# 5. Get "example" document out of the database by document id ============ +document_example = client.get_document( + db=db_name, + doc_id="example" +).get_result() + +print(f'Document retrieved from database:\n' + f'{json.dumps(document_example, indent=2)}') +``` + +
+When you run the code, you see a result similar to the following output. + +```text +Server Version: 3.2.1 +Document count in "orders" database is 1. +Document retrieved from database: +{ + "_id": "example", + "_rev": "1-1b403633540686aa32d013fda9041a5d", + "name": "Bob Smith", + "joined": "2019-01-24T10:42:59.000Z" +} +``` + +## 3. Update your previously created document + +**Note**: This example code assumes that you have created both the `orders` +database and the `example` document by +[running the previous example code](#1-create-a-database-and-add-a-document) +successfully. Otherwise, the following error message occurs, "Cannot update document because either 'orders' +database or 'example' document was not found." + +
+Update code example + +```py +import json + +from ibm_cloud_sdk_core import ApiException +from ibmcloudant.cloudant_v1 import CloudantV1 + +# 1. Create a client with `CLOUDANT` default service name ============= +client = CloudantV1.new_instance() + +# 2. Update the document ============================================== +example_db_name = "orders" +example_doc_id = "example" + +# Try to get the document if it previously existed in the database +try: + document = client.get_document( + db=example_db_name, + doc_id=example_doc_id + ).get_result() + + # ================================================================= + # Note: for response byte stream use: + """ + document_as_byte_stream = client.get_document_as_stream( + db=example_db_name, + doc_id=example_doc_id + ).get_result() + """ + # ================================================================= + + # Add Bob Smith's address to the document + document["address"] = "19 Front Street, Darlington, DL5 1TY" + + # Remove the joined property from document object + if "joined" in document: + document.pop("joined") + + # Update the document in the database + update_document_response = client.post_document( + db=example_db_name, + document=document + ).get_result() + + # ================================================================= + # Note 1: for request byte stream use: + """ + update_document_response = client.post_document( + db=example_db_name, + document=document_as_byte_stream + ).get_result() + """ + # ================================================================= + + # ================================================================= + # Note 2: updating the document can also be done with the + # "put_document" function. `doc_id` and `rev` are required for an + # UPDATE operation, but `rev` can be provided in the document + # object as `_rev` too: + """ + update_document_response = client.put_document( + db=example_db_name, + doc_id=example_doc_id, # doc_id is a required parameter + rev=document["_rev"], + document=document # _rev in the document object CAN replace above `rev` parameter + ).get_result() + """ + # ================================================================= + + # Keeping track of the latest revision number of the document + # object is necessary for further UPDATE/DELETE operations: + document["_rev"] = update_document_response["rev"] + print(f'You have updated the document:\n' + + json.dumps(document, indent=2)) + +except ApiException as ae: + if ae.status_code == 404: + print('Cannot update document because either ' + + f'"{example_db_name}" database or "{example_doc_id}" ' + + 'document was not found.') +``` + +
+When you run the code, you see a result similar to the following output. + +```text +You have updated the document: +{ + "_id": "example", + "_rev": "2-4e2178e85cffb32d38ba4e451f6ca376", + "name": "Bob Smith", + "address": "19 Front Street, Darlington, DL5 1TY" +} +``` + +## 4. Delete your previously created document + +**Note**: This example code assumes that you have created both the `orders` +database and the `example` document by +[running the previous example code](#1-create-a-database-and-add-a-document) +successfully. Otherwise, the following error message occurs, "Cannot delete document because either 'orders' +database or 'example' document was not found." + +
+Delete code example + +```py +from ibm_cloud_sdk_core import ApiException +from ibmcloudant.cloudant_v1 import CloudantV1 + +# 1. Create a client with `CLOUDANT` default service name ============= +client = CloudantV1.new_instance() + +# 2. Delete the document ============================================== +example_db_name = "orders" +example_doc_id = "example" + +# Try to get the document if it previously existed in the database +try: + document = client.get_document( + db=example_db_name, + doc_id=example_doc_id + ).get_result() + + delete_document_response = client.delete_document( + db=example_db_name, + doc_id=example_doc_id, # `doc_id` is required for DELETE + rev=document["_rev"] # `rev` is required for DELETE + ).get_result() + + if delete_document_response["ok"]: + print('You have deleted the document.') + +except ApiException as ae: + if ae.status_code == 404: + print('Cannot delete document because either ' + + f'"{example_db_name}" database or "{example_doc_id}" ' + + 'document was not found.') +``` + +
+When you run the code, you see the following output. + +```text +You have deleted the document. +``` + +## Further code examples + +For a complete list of code examples, see the [examples directory](https://github.com/IBM/cloudant-python-sdk/tree/v0.11.10/examples#examples-for-python). diff --git a/docs/Pagination.md b/docs/Pagination.md new file mode 100644 index 00000000..6a641173 --- /dev/null +++ b/docs/Pagination.md @@ -0,0 +1,267 @@ +# Pagination + +
+Table of Contents + + +- [Introduction](#introduction) +- [Limitations](#limitations) +- [Capacity considerations](#capacity-considerations) +- [Available operations](#available-operations) +- [Creating a pagination](#creating-a-pagination) + * [Initialize the service](#initialize-the-service) + * [Set the options](#set-the-options) + * [Create the pagination](#create-the-pagination) +- [Using pagination](#using-pagination) + * [Iterate pages](#iterate-pages) + * [Iterate rows](#iterate-rows) + * [Pager](#pager) + + [Get each page from a pager](#get-each-page-from-a-pager) + + [Get all results from a pager](#get-all-results-from-a-pager) +
+ +## Introduction + +The pagination feature accepts options for a single operation and automatically +creates the multiple requests to the server necessary to page through the results a fixed number at a time. + +Pagination is a best-practice to break apart large queries into multiple server requests. +This has a number of advantages: +* Keeping requests within server imposed limits, for example + * `200` max results for text search + * `2000` max results for partitioned queries +* Fetching only the necessary data, for example + * User finds required result on first page, no need to continue fetching results +* Reducing the duration of any individual query + * Reduce risk of query timing out on the server + * Reduce risk of network request timeouts + +## Limitations + +Limitations of pagination: +* Forward only, no backwards paging +* Limitations on `_all_docs` and `_design_docs` operations + * No pagination for `key` option. + There is no need to paginate as IDs are unique and this returns only a single row. + This is better achieved with a single document get request. + * No pagination for `keys` option. +* Limitations on `_view` operations + * No pagination for `key` option. Pass the same `key` as a start and end key instead. + * No pagination for `keys` option. + * Views that emit multiple identical keys (with the same or different values) + from the same document cannot paginate if those key rows with the same ID + span a page boundary. + The pagination feature detects this condition and an error occurs. + It may be possible to workaround using a different page size. +* Limitations on `_search` operations + * No pagination of grouped results. + * No pagination of faceted `counts` or `ranges` results. + +## Capacity considerations + +Pagination can make many requests rapidly from a single program call. + +For IBM Cloudant take care to ensure you have appropriate plan capacity +in place to avoid consuming all the permitted requests. +If there is no remaining plan allowance and retries are not enabled or insufficient +then a `429 Too Many Requests` error occurs. + +## Available operations + +Pagination is available for these operations: +* Query all documents [global](https://cloud.ibm.com/apidocs/cloudant?code=python#postalldocs) + and [partitioned](https://cloud.ibm.com/apidocs/cloudant?code=python#postpartitionalldocs) + * [Global all documents examples](https://github.com/IBM/cloudant-python-sdk/tree/v0.11.10/test/examples/src/features/pagination/all_docs_pagination.py) + * [Partitioned all documents examples](https://github.com/IBM/cloudant-python-sdk/tree/v0.11.10/test/examples/src/features/pagination/partition_all_docs_pagination.py) +* Query all [design documents](https://cloud.ibm.com/apidocs/cloudant?code=python#postdesigndocs) + * [Design documents examples](https://github.com/IBM/cloudant-python-sdk/tree/v0.11.10/test/examples/src/features/pagination/design_docs_pagination.py) +* Query with selector syntax [global](https://cloud.ibm.com/apidocs/cloudant?code=python#postfind) + and [partitioned](https://cloud.ibm.com/apidocs/cloudant?code=python#postpartitionfind) + * [Global find selector query examples](https://github.com/IBM/cloudant-python-sdk/tree/v0.11.10/test/examples/src/features/pagination/find_pagination.py) + * [Partitioned find selector query examples](https://github.com/IBM/cloudant-python-sdk/tree/v0.11.10/test/examples/src/features/pagination/partition_find_pagination.py) +* Query a search index [global](https://cloud.ibm.com/apidocs/cloudant?code=python#postsearch) + and [partitioned](https://cloud.ibm.com/apidocs/cloudant?code=python#postpartitionsearch) + * [Global search examples](https://github.com/IBM/cloudant-python-sdk/tree/v0.11.10/test/examples/src/features/pagination/search_pagination.py) + * [Partitioned search examples](https://github.com/IBM/cloudant-python-sdk/tree/v0.11.10/test/examples/src/features/pagination/partition_search_pagination.py) +* Query a MapReduce view [global](https://cloud.ibm.com/apidocs/cloudant?code=python#postview) + and [partitioned](https://cloud.ibm.com/apidocs/cloudant?code=python#postpartitionview) + * [Global view examples](https://github.com/IBM/cloudant-python-sdk/tree/v0.11.10/test/examples/src/features/pagination/view_pagination.py) + * [Partitioned view examples](https://github.com/IBM/cloudant-python-sdk/tree/v0.11.10/test/examples/src/features/pagination/partition_view_pagination.py) + +The examples presented in this `README` are for all documents in a partition. +The links in the list are to equivalent examples for each of the other available operations. + +## Creating a pagination + +Make a new pagination from a client, `PagerType` for the operation +and the options for the chosen operation. +Use the `limit` option to configure the page size (default and maximum `200`). + +Imports required for these examples: + +
+Python: + +```py +from ibmcloudant import Pager, Pagination, PagerType +from ibmcloudant.cloudant_v1 import CloudantV1 +``` + +
+ +### Initialize the service + +
+Python: + +```py +# Initialize service +service = CloudantV1.new_instance() +``` + +
+ +### Set the options + +
+Python: + +```py +# Setup options +opts = { + 'db': 'events', # example database name + 'limit': 50, # limit option sets the page size + 'partition_key': 'ns1HJS13AMkK', # query only this partition +} +``` + +
+ +### Create the pagination + +
+Python: + +```py +# Create pagination +pagination = Pagination.new_pagination( + service, PagerType.POST_PARTITION_ALL_DOCS, **opts) +# pagination can be reused without side-effects as a factory for iterables or pagers +# options are fixed at pagination creation time +``` + +
+ +## Using pagination + +Once you have a pagination factory there are multiple options available. + +* Iterate pages +* Iterate rows +* Get each page from a pager +* Get all results from a pager + +All the paging styles produce equivalent results and make identical page requests. +The style of paging to choose depends on the use case requirements +in particular whether to process a page at a time or a row at a time. + +The pagination factory is reusable and can repeatedly produce new instances +of the same or different pagination styles for the same operation options. + +Here are examples for each paging style. + +### Iterate pages + +Iterating pages is ideal for using an iterable for loop to process a page at a time. + +
+Python: + +```py +# Option: iterate pages +# Ideal for using a for loop with each page. +# Each call to pages() returns a fresh iterator that can be traversed once. +for page in pagination.pages(): + # Do something with page + pass +``` + +
+ +### Iterate rows + +Iterating rows is ideal for using an iterable for loop to process a result row at a time. + +
+Python: + +```py +# Option: iterate rows +# Ideal for using a for loop with each row. +# Each call to rows() returns a fresh iterator that can be traversed once. +for row in pagination.rows(): + # Do something with row + pass +``` + +
+ +### Pager + +The pager style is similar to other [IBM Cloud SDKs](https://github.com/IBM/ibm-cloud-sdk-common?tab=readme-ov-file#pagination). +Users familiar with that style of pagination may find using them preferable +to the native language style iterators. + +In the Cloudant SDKs these pagers are single use and traverse the complete set of pages once and only once. +After exhaustion they cannot be re-used, simply create a new one from the pagination factory if needed. + +Pagers are only valid for one of either page at a time or getting all results. +For example, calling for the next page then calling for all results causes an error. + +#### Get each page from a pager + +This is useful for calling to retrieve one page at a time, for example, +in a user interface with a "next page" interaction. + +If calling for the next page errors, it is valid to call for the next page again +to continue paging. + +
+Python: + +```py +# Option: use pager next page +# For retrieving one page at a time with a method call. +pager: Pager = pagination.pager() +if pager.has_next(): + page = pager.get_next() + # Do something with page +``` + +
+ +#### Get all results from a pager + +This is useful to retrieve all results in a single call. +However, this approach requires sufficient memory for the entire collection of results. +So although it may be convenient for small result sets generally prefer iterating pages +or rows with the other paging styles, especially for large result sets. + +If calling for all the results errors, then calling for all the results again restarts the pagination. + +
+Python: + +```py +# Option: use pager all results +# For retrieving all result rows in a single list +# Note: all result rows may be very large! +# Preferably use iterables instead of get_all for memory efficiency with large result sets. +all_pager: Pager = pagination.pager() +all_rows = all_pager.get_all() +for page in all_rows: + # Do something with row + pass +``` + +
diff --git a/docs/README.md b/docs/README.md new file mode 100644 index 00000000..9686b862 --- /dev/null +++ b/docs/README.md @@ -0,0 +1,11 @@ +# IBM Cloudant Python SDK Version 0.11.10 Detailed Documentation + +## Table of Contents + +### [Authentication](Authentication.md) + +### [Changes Follower](Changes_Follower.md) + +### [Examples](Examples.md) + +### [Pagination](Pagination.md) diff --git a/examples/README.md b/examples/README.md index 39e5efe8..58ac8e33 100644 --- a/examples/README.md +++ b/examples/README.md @@ -2,482 +2,2007 @@ ## getServerInformation -### get `/` +_GET `/`_ -- [Example request](./getServerInformation/example_request.py) +### [Example request](snippets/getServerInformation/example_request.py) + +[embedmd]:# (snippets/getServerInformation/example_request.py) +```py +# section: code +from ibmcloudant.cloudant_v1 import CloudantV1 + +service = CloudantV1.new_instance() + +response = service.get_server_information().get_result() + +print(response) +``` ## getActiveTasks -### get `/_active_tasks` +_GET `/_active_tasks`_ + +### [Example request](snippets/getActiveTasks/example_request.py) + +[embedmd]:# (snippets/getActiveTasks/example_request.py) +```py +# section: code +from ibmcloudant.cloudant_v1 import CloudantV1 -- [Example request](./getActiveTasks/example_request.py) +service = CloudantV1.new_instance() + +response = service.get_active_tasks().get_result() + +print(response) +``` ## getAllDbs -### get `/_all_dbs` +_GET `/_all_dbs`_ + +### [Example request](snippets/getAllDbs/example_request.py) + +[embedmd]:# (snippets/getAllDbs/example_request.py) +```py +# section: code +from ibmcloudant.cloudant_v1 import CloudantV1 + +service = CloudantV1.new_instance() -- [Example request](./getAllDbs/example_request.py) +response = service.get_all_dbs().get_result() + +print(response) +``` ## postApiKeys -### post `/_api/v2/api_keys` +_POST `/_api/v2/api_keys`_ + +### [Example request](snippets/postApiKeys/example_request.py) + +[embedmd]:# (snippets/postApiKeys/example_request.py) +```py +# section: code +from ibmcloudant.cloudant_v1 import CloudantV1 + +service = CloudantV1.new_instance() + +response = service.post_api_keys().get_result() -- [Example request](./postApiKeys/example_request.py) +print(response) +``` ## putCloudantSecurity -### put `/_api/v2/db/{db}/_security` +_PUT `/_api/v2/db/{db}/_security`_ -- [Example request](./putCloudantSecurity/example_request.py) +### [Example request](snippets/putCloudantSecurity/example_request.py) + +[embedmd]:# (snippets/putCloudantSecurity/example_request.py) +```py +# section: code +from ibmcloudant.cloudant_v1 import CloudantV1 + +service = CloudantV1.new_instance() + +security_object = {'nobody':['_reader']} +response = service.put_cloudant_security_configuration( + db='products', + cloudant=security_object +).get_result() + +print(response) +# section: markdown +# 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`. +# section: markdown +# 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. +``` ## getActivityTrackerEvents -### get `/_api/v2/user/activity_tracker/events` +_GET `/_api/v2/user/activity_tracker/events`_ + +### [Example request](snippets/getActivityTrackerEvents/example_request.py) -- [Example request](./getActivityTrackerEvents/example_request.py) +[embedmd]:# (snippets/getActivityTrackerEvents/example_request.py) +```py +# section: code +from ibmcloudant.cloudant_v1 import CloudantV1 + +service = CloudantV1.new_instance() + +response = service.get_activity_tracker_events().get_result() + +print(response) +``` ## postActivityTrackerEvents -### post `/_api/v2/user/activity_tracker/events` +_POST `/_api/v2/user/activity_tracker/events`_ + +### [Example request](snippets/postActivityTrackerEvents/example_request.py) + +[embedmd]:# (snippets/postActivityTrackerEvents/example_request.py) +```py +# section: code +from ibmcloudant.cloudant_v1 import CloudantV1, ActivityTrackerEvents -- [Example request](./postActivityTrackerEvents/example_request.py) +service = CloudantV1.new_instance() + +response = service.post_activity_tracker_events( + types=[ + ActivityTrackerEvents.TypesEnum.MANAGEMENT, + ActivityTrackerEvents.TypesEnum.DATA + ] +).get_result() + +print(response) +``` ## getCapacityThroughputInformation -### get `/_api/v2/user/capacity/throughput` +_GET `/_api/v2/user/capacity/throughput`_ + +### [Example request](snippets/getCapacityThroughputInformation/example_request.py) + +[embedmd]:# (snippets/getCapacityThroughputInformation/example_request.py) +```py +# section: code +from ibmcloudant.cloudant_v1 import CloudantV1 + +service = CloudantV1.new_instance() -- [Example request](./getCapacityThroughputInformation/example_request.py) +response = service.get_capacity_throughput_information().get_result() + +print(response) +``` ## putCapacityThroughputConfiguration -### put `/_api/v2/user/capacity/throughput` +_PUT `/_api/v2/user/capacity/throughput`_ + +### [Example request](snippets/putCapacityThroughputConfiguration/example_request.py) + +[embedmd]:# (snippets/putCapacityThroughputConfiguration/example_request.py) +```py +# section: code +from ibmcloudant.cloudant_v1 import CloudantV1 + +service = CloudantV1.new_instance() + +response = service.put_capacity_throughput_configuration( + blocks=1 +).get_result() -- [Example request](./putCapacityThroughputConfiguration/example_request.py) +print(response) +``` ## getCorsInformation -### get `/_api/v2/user/config/cors` +_GET `/_api/v2/user/config/cors`_ -- [Example request](./getCorsInformation/example_request.py) +### [Example request](snippets/getCorsInformation/example_request.py) + +[embedmd]:# (snippets/getCorsInformation/example_request.py) +```py +# section: code +from ibmcloudant.cloudant_v1 import CloudantV1 + +service = CloudantV1.new_instance() + +response = service.get_cors_information().get_result() + +print(response) +``` ## putCorsConfiguration -### put `/_api/v2/user/config/cors` +_PUT `/_api/v2/user/config/cors`_ + +### [Example request](snippets/putCorsConfiguration/example_request.py) -- [Example request](./putCorsConfiguration/example_request.py) +[embedmd]:# (snippets/putCorsConfiguration/example_request.py) +```py +# section: code +from ibmcloudant.cloudant_v1 import CloudantV1 + +service = CloudantV1.new_instance() + +response = service.put_cors_configuration( + enable_cors=True, + origins=['https://example.com'] +).get_result() + +print(response) +``` ## getCurrentThroughputInformation -### get `/_api/v2/user/current/throughput` +_GET `/_api/v2/user/current/throughput`_ + +### [Example request](snippets/getCurrentThroughputInformation/example_request.py) + +[embedmd]:# (snippets/getCurrentThroughputInformation/example_request.py) +```py +# section: code +from ibmcloudant.cloudant_v1 import CloudantV1 + +service = CloudantV1.new_instance() -- [Example request](./getCurrentThroughputInformation/example_request.py) +response = service.get_current_throughput_information().get_result() + +print(response) +``` ## getDbUpdates -### get `/_db_updates` +_GET `/_db_updates`_ + +### [Example request](snippets/getDbUpdates/example_request.py) + +[embedmd]:# (snippets/getDbUpdates/example_request.py) +```py +# section: code +from ibmcloudant.cloudant_v1 import CloudantV1 + +service = CloudantV1.new_instance() + +response = service.get_db_updates( + feed='normal', + since='now' +).get_result() -- [Example request](./getDbUpdates/example_request.py) +print(response) +# section: markdown +# This request requires `server_admin` access. +``` ## postDbsInfo -### post `/_dbs_info` +_POST `/_dbs_info`_ -- [Example request](./postDbsInfo/example_request.py) +### [Example request](snippets/postDbsInfo/example_request.py) + +[embedmd]:# (snippets/postDbsInfo/example_request.py) +```py +# section: code +from ibmcloudant.cloudant_v1 import CloudantV1 + +service = CloudantV1.new_instance() + +response = service.post_dbs_info( + keys=['products', 'users', 'orders'] +).get_result() + +print(response) +``` ## getMembershipInformation -### get `/_membership` +_GET `/_membership`_ + +### [Example request](snippets/getMembershipInformation/example_request.py) -- [Example request](./getMembershipInformation/example_request.py) +[embedmd]:# (snippets/getMembershipInformation/example_request.py) +```py +# section: code +from ibmcloudant.cloudant_v1 import CloudantV1 + +service = CloudantV1.new_instance() + +response = service.get_membership_information().get_result() + +print(response) +``` ## deleteReplicationDocument -### delete `/_replicator/{doc_id}` +_DELETE `/_replicator/{doc_id}`_ + +### [Example request](snippets/deleteReplicationDocument/example_request.py) + +[embedmd]:# (snippets/deleteReplicationDocument/example_request.py) +```py +# section: code +from ibmcloudant.cloudant_v1 import CloudantV1 -- [Example request](./deleteReplicationDocument/example_request.py) +service = CloudantV1.new_instance() + +response = service.delete_replication_document( +doc_id='repldoc-example', +rev='1-00000000000000000000000000000000' +).get_result() + +print(response) +``` ## getReplicationDocument -### get `/_replicator/{doc_id}` +_GET `/_replicator/{doc_id}`_ + +### [Example request](snippets/getReplicationDocument/example_request.py) + +[embedmd]:# (snippets/getReplicationDocument/example_request.py) +```py +# section: code +from ibmcloudant.cloudant_v1 import CloudantV1 + +service = CloudantV1.new_instance() -- [Example request](./getReplicationDocument/example_request.py) +response = service.get_replication_document( + doc_id='repldoc-example' +).get_result() + +print(response) +``` ## headReplicationDocument -### head `/_replicator/{doc_id}` +_HEAD `/_replicator/{doc_id}`_ + +### [Example request](snippets/headReplicationDocument/example_request.py) + +[embedmd]:# (snippets/headReplicationDocument/example_request.py) +```py +# section: code +from ibmcloudant.cloudant_v1 import CloudantV1 -- [Example request](./headReplicationDocument/example_request.py) +service = CloudantV1.new_instance() + +response = service.head_replication_document( + doc_id='repldoc-example' +) +print(response.get_status_code()) +print(response.get_headers()['ETag']) +``` ## putReplicationDocument -### put `/_replicator/{doc_id}` +_PUT `/_replicator/{doc_id}`_ + +### [Example request](snippets/putReplicationDocument/example_request.py) -- [Example request](./putReplicationDocument/example_request.py) +[embedmd]:# (snippets/putReplicationDocument/example_request.py) +```py +# section: code +from ibmcloudant.cloudant_v1 import CloudantV1, ReplicationDocument, ReplicationDatabase, ReplicationDatabaseAuthIam, ReplicationDatabaseAuth + +service = CloudantV1.new_instance() + +source_db = ReplicationDatabase( + url='https://~replace-with-source-host~.cloudantnosqldb.appdomain.cloud/animaldb' +) + +target_auth_iam = ReplicationDatabaseAuthIam( + api_key='a1b2c3d4e5f6f1g4h7j3k6l9m2p5q8s1t4v7x0z3' #use your own IAM API key +) +target_auth = ReplicationDatabaseAuth( + iam=target_auth_iam +) +target_db = ReplicationDatabase( + auth=target_auth, + url='https://~replace-with-target-host~.cloudantnosqldb.appdomain.cloud/animaldb-target' +) + +replication_document = ReplicationDocument( + _id='repldoc-example', + create_target=True, + source=source_db, + target=target_db +) + +response = service.put_replication_document( + doc_id='repldoc-example', + replication_document=replication_document +).get_result() + +print(response) +``` ## getSchedulerDocs -### get `/_scheduler/docs` +_GET `/_scheduler/docs`_ + +### [Example request](snippets/getSchedulerDocs/example_request.py) + +[embedmd]:# (snippets/getSchedulerDocs/example_request.py) +```py +# section: code +from ibmcloudant.cloudant_v1 import CloudantV1 + +service = CloudantV1.new_instance() + +response = service.get_scheduler_docs( + limit=100, + states=['completed'] +).get_result() -- [Example request](./getSchedulerDocs/example_request.py) +print(response) +``` ## getSchedulerDocument -### get `/_scheduler/docs/_replicator/{doc_id}` +_GET `/_scheduler/docs/_replicator/{doc_id}`_ -- [Example request](./getSchedulerDocument/example_request.py) +### [Example request](snippets/getSchedulerDocument/example_request.py) + +[embedmd]:# (snippets/getSchedulerDocument/example_request.py) +```py +# section: code +from ibmcloudant.cloudant_v1 import CloudantV1 + +service = CloudantV1.new_instance() + +response = service.get_scheduler_document(doc_id='repldoc-example').get_result() + +print(response) +``` ## getSchedulerJobs -### get `/_scheduler/jobs` +_GET `/_scheduler/jobs`_ + +### [Example request](snippets/getSchedulerJobs/example_request.py) + +[embedmd]:# (snippets/getSchedulerJobs/example_request.py) +```py +# section: code +from ibmcloudant.cloudant_v1 import CloudantV1 -- [Example request](./getSchedulerJobs/example_request.py) +service = CloudantV1.new_instance() + +response = service.get_scheduler_jobs( + limit=100 +).get_result() + +print(response) +``` ## getSchedulerJob -### get `/_scheduler/jobs/{job_id}` +_GET `/_scheduler/jobs/{job_id}`_ + +### [Example request](snippets/getSchedulerJob/example_request.py) + +[embedmd]:# (snippets/getSchedulerJob/example_request.py) +```py +# section: code +from ibmcloudant.cloudant_v1 import CloudantV1 + +service = CloudantV1.new_instance() -- [Example request](./getSchedulerJob/example_request.py) +response = service.get_scheduler_job( + job_id='7b94915cd8c4a0173c77c55cd0443939+continuous' +).get_result() + +print(response) +``` ## headSchedulerJob -### head `/_scheduler/jobs/{job_id}` +_HEAD `/_scheduler/jobs/{job_id}`_ + +### [Example request](snippets/headSchedulerJob/example_request.py) + +[embedmd]:# (snippets/headSchedulerJob/example_request.py) +```py +# section: code +from ibmcloudant.cloudant_v1 import CloudantV1 + +service = CloudantV1.new_instance() + +response = service.head_scheduler_job( + job_id='7b94915cd8c4a0173c77c55cd0443939+continuous' +).get_result() -- [Example request](./headSchedulerJob/example_request.py) +print(response.get_status_code()) +``` ## postSearchAnalyze -### post `/_search_analyze` +_POST `/_search_analyze`_ -- [Example request](./postSearchAnalyze/example_request.py) +### [Example request](snippets/postSearchAnalyze/example_request.py) + +[embedmd]:# (snippets/postSearchAnalyze/example_request.py) +```py +# section: code +from ibmcloudant.cloudant_v1 import CloudantV1 + +service = CloudantV1.new_instance() + +response = service.post_search_analyze( + analyzer='english', + text='running is fun' +).get_result() + +print(response) +``` ## getSessionInformation -### get `/_session` +_GET `/_session`_ + +### [Example request](snippets/getSessionInformation/example_request.py) -- [Example request](./getSessionInformation/example_request.py) +[embedmd]:# (snippets/getSessionInformation/example_request.py) +```py +# section: code +from ibmcloudant.cloudant_v1 import CloudantV1 + +service = CloudantV1.new_instance() + +response = service.get_session_information().get_result() + +print(response) +# section: markdown +# For more details on Session Authentication, see [Authentication.](#authentication) +``` ## getUpInformation -### get `/_up` +_GET `/_up`_ + +### [Example request](snippets/getUpInformation/example_request.py) + +[embedmd]:# (snippets/getUpInformation/example_request.py) +```py +# section: code +from ibmcloudant.cloudant_v1 import CloudantV1 +from ibm_cloud_sdk_core.api_exception import ApiException -- [Example request](./getUpInformation/example_request.py) +service = CloudantV1.new_instance() +try: + response = service.get_up_information() + + print("Service is up and healthy") + print(response.get_result()) +except ApiException as ae: + if ae.code == 503: + print(f"Service is unavailable, status code: {ae.code}") + else: + print(f"Issue performing health check, status code: {ae.code}, message: {ae.http_response.reason}") +``` ## getUuids -### get `/_uuids` +_GET `/_uuids`_ + +### [Example request](snippets/getUuids/example_request.py) + +[embedmd]:# (snippets/getUuids/example_request.py) +```py +# section: code +from ibmcloudant.cloudant_v1 import CloudantV1 + +service = CloudantV1.new_instance() + +response = service.get_uuids(count=10).get_result() -- [Example request](./getUuids/example_request.py) +print(response) +``` ## deleteDatabase -### delete `/{db}` +_DELETE `/{db}`_ -- [Example request](./deleteDatabase/example_request.py) +### [Example request](snippets/deleteDatabase/example_request.py) + +[embedmd]:# (snippets/deleteDatabase/example_request.py) +```py +# section: code +from ibmcloudant.cloudant_v1 import CloudantV1 + +service = CloudantV1.new_instance() + +response = service.delete_database(db='products').get_result() + +print(response) +``` ## getDatabaseInformation -### get `/{db}` +_GET `/{db}`_ + +### [Example request](snippets/getDatabaseInformation/example_request.py) -- [Example request](./getDatabaseInformation/example_request.py) +[embedmd]:# (snippets/getDatabaseInformation/example_request.py) +```py +# section: code +from ibmcloudant.cloudant_v1 import CloudantV1 + +service = CloudantV1.new_instance() + +response = service.get_database_information(db='products').get_result() + +print(response) +``` ## headDatabase -### head `/{db}` +_HEAD `/{db}`_ + +### [Example request](snippets/headDatabase/example_request.py) + +[embedmd]:# (snippets/headDatabase/example_request.py) +```py +# section: code +from ibmcloudant.cloudant_v1 import CloudantV1 -- [Example request](./headDatabase/example_request.py) +service = CloudantV1.new_instance() + +response = service.head_database(db='products') +print(response.get_status_code()) +``` ## postDocument -### post `/{db}` +_POST `/{db}`_ + +### [Example request](snippets/postDocument/example_request.py) + +[embedmd]:# (snippets/postDocument/example_request.py) +```py +# section: code +from ibmcloudant.cloudant_v1 import Document, CloudantV1 + +service = CloudantV1.new_instance() + +products_doc = Document( + _id="1000042", + type="product", + productId="1000042", + brand="Salter", + name="Digital Kitchen Scales", + description="Slim Colourful Design Electronic Cooking Appliance for Home / Kitchen, Weigh up to 5kg + Aquatronic for Liquids ml + fl. oz. 15Yr Guarantee - Green", + price=14.99, + image="assets/img/0gmsnghhew.jpg") + +response = service.post_document(db='products', document=products_doc).get_result() + +print(response) +``` + +### [Example request as a stream](snippets/postDocument/example_request_as_a_stream.py) + +[embedmd]:# (snippets/postDocument/example_request_as_a_stream.py) +```py +# section: code +from ibmcloudant.cloudant_v1 import Document, CloudantV1 + +service = CloudantV1.new_instance() -- [Example request](./postDocument/example_request.py) +with open("products_doc.json", "rb") as products_doc: + response = service.post_document( + db='products', + document=products_doc, + content_type="application/json" + ).get_result() + +print(response) +``` ## putDatabase -### put `/{db}` +_PUT `/{db}`_ + +### [Example request](snippets/putDatabase/example_request.py) + +[embedmd]:# (snippets/putDatabase/example_request.py) +```py +# section: code +from ibmcloudant.cloudant_v1 import CloudantV1 + +service = CloudantV1.new_instance() + +response = service.put_database(db='events', partitioned=True).get_result() -- [Example request](./putDatabase/example_request.py) +print(response) +``` ## postAllDocs -### post `/{db}/_all_docs` +_POST `/{db}/_all_docs`_ + +### [Example request](snippets/postAllDocs/example_request.py) + +[embedmd]:# (snippets/postAllDocs/example_request.py) +```py +# section: code +from ibmcloudant.cloudant_v1 import CloudantV1 + +service = CloudantV1.new_instance() + +response = service.post_all_docs( + db='orders', + include_docs=True, + start_key='abc', + limit=10 +).get_result() + +print(response) +``` -- [Example request](./postAllDocs/example_request.py) -- [Example request as a stream](./postAllDocs/example_request_as_a_stream.py) +### [Example request as a stream](snippets/postAllDocs/example_request_as_a_stream.py) + +[embedmd]:# (snippets/postAllDocs/example_request_as_a_stream.py) +```py +# section: code +from ibmcloudant.cloudant_v1 import CloudantV1 + +service = CloudantV1.new_instance() + +result = service.post_all_docs_as_stream( + db='orders', + include_docs=True, + start_key='abc', + limit=10 +).get_result() + +with open('result.json', 'wb') as f: + for chunk in result.iter_content(): + f.write(chunk) +``` ## postAllDocsQueries -### post `/{db}/_all_docs/queries` +_POST `/{db}/_all_docs/queries`_ -- [Example request](./postAllDocsQueries/example_request.py) +### [Example request](snippets/postAllDocsQueries/example_request.py) -## postBulkDocs +[embedmd]:# (snippets/postAllDocsQueries/example_request.py) +```py +# section: code +from ibmcloudant.cloudant_v1 import AllDocsQuery, CloudantV1 + +service = CloudantV1.new_instance() + +all_docs_query1 = AllDocsQuery( + keys=['1000042', '1000043'] +) + +all_docs_query2 = AllDocsQuery( + limit=3, + skip=2 +) + +response = service.post_all_docs_queries( + db='products', + queries=[all_docs_query1, all_docs_query2] +).get_result() + +print(response) +``` -### post `/{db}/_bulk_docs` +## postBulkDocs -- [Example request: create documents](./postBulkDocs/example_request_create_documents.py) -- [Example request: delete documents](./postBulkDocs/example_request_delete_documents.py) -- [Example request as a stream](./postBulkDocs/example_request_as_a_stream.py) +_POST `/{db}/_bulk_docs`_ + +### [Example request: create documents](snippets/postBulkDocs/example_request_create_documents.py) + +[embedmd]:# (snippets/postBulkDocs/example_request_create_documents.py) +```py +# section: code +from ibmcloudant.cloudant_v1 import Document, CloudantV1, BulkDocs + +service = CloudantV1.new_instance() + +event_doc_1 = Document( + _id="ns1HJS13AMkK:0007241142412418284", + type="event", + userId="abc123", + eventType="addedToBasket", + productId="1000042", + date="2019-01-28T10:44:22.000Z" +) +event_doc_2 = Document( + _id="H8tDIwfadxp9:0007241142412418285", + type="event", + userId="abc234", + eventType="addedToBasket", + productId="1000050", + date="2019-01-25T20:00:00.000Z" +) + +bulk_docs = BulkDocs(docs=[event_doc_1, event_doc_2]) + +response = service.post_bulk_docs( + db='events', + bulk_docs=bulk_docs +).get_result() + +print(response) +``` + +### [Example request: delete documents](snippets/postBulkDocs/example_request_delete_documents.py) + +[embedmd]:# (snippets/postBulkDocs/example_request_delete_documents.py) +```py +# section: code +from ibmcloudant.cloudant_v1 import Document, CloudantV1, BulkDocs + +service = CloudantV1.new_instance() + +event_doc_1 = Document( + _id="ns1HJS13AMkK:0007241142412418284", + _rev="1-00000000000000000000000000000000", + _deleted=True, +) +event_doc_2 = Document( + _id="H8tDIwfadxp9:0007241142412418285", + _rev="1-00000000000000000000000000000000", + _deleted=True, +) + +bulk_docs = BulkDocs(docs=[event_doc_1, event_doc_2]) + +response = service.post_bulk_docs( + db='events', + bulk_docs=bulk_docs +).get_result() + +print(response) +``` + +### [Example request as a stream](snippets/postBulkDocs/example_request_as_a_stream.py) + +[embedmd]:# (snippets/postBulkDocs/example_request_as_a_stream.py) +```py +# section: code +from ibmcloudant.cloudant_v1 import Document, CloudantV1 + +service = CloudantV1.new_instance() + +with open('upload.json', 'rb') as f: + response = service.post_bulk_docs( + db='events', + bulk_docs=f + ).get_result() + +print(response) +# section: markdown +# Content of upload.json +# section: code +{ + "docs": [ + { + "_id": "ns1HJS13AMkK:0007241142412418284", + "type": "event", + "userId": "abc123", + "eventType": "addedToBasket", + "productId": "1000042", + "date": "2019-01-28T10:44:22.000Z" + }, + { + "_id": "H8tDIwfadxp9:0007241142412418285", + "type": "event", + "userId": "abc234", + "eventType": "addedToBasket", + "productId": "1000050", + "date": "2019-01-25T20:00:00.000Z" + } + ] +} +``` ## postBulkGet -### post `/{db}/_bulk_get` +_POST `/{db}/_bulk_get`_ + +### [Example request](snippets/postBulkGet/example_request.py) + +[embedmd]:# (snippets/postBulkGet/example_request.py) +```py +# section: code +from ibmcloudant.cloudant_v1 import BulkGetQueryDocument, CloudantV1 + +service = CloudantV1.new_instance() + +doc_id = 'order00067' +bulk_get_doc_1 = BulkGetQueryDocument( + id=doc_id, + rev='3-22222222222222222222222222222222') +bulk_get_doc_2 = BulkGetQueryDocument( + id=doc_id, + rev='4-33333333333333333333333333333333') + +response = service.post_bulk_get( + db='orders', + docs=[bulk_get_doc_1, bulk_get_doc_2], +).get_result() + +print(response) +``` + +### [Alternative example request for `open_revs=all`](snippets/postBulkGet/alternative_example_request_for_open_revs_all.py) + +[embedmd]:# (snippets/postBulkGet/alternative_example_request_for_open_revs_all.py) +```py +# section: code +from ibmcloudant.cloudant_v1 import BulkGetQueryDocument, CloudantV1 + +service = CloudantV1.new_instance() -- [Example request](./postBulkGet/example_request.py) -- [Alternative example request for `open_revs=all`](./postBulkGet/alternative_example_request_for_open_revs_all.py) -- [Alternative example request for `atts_since`](./postBulkGet/alternative_example_request_for_atts_since.py) +bulk_get_doc = BulkGetQueryDocument(id='order00067') +response = service.post_bulk_get( + db='orders', + docs=[bulk_get_doc], +).get_result() + +print(response) +``` + +### [Alternative example request for `atts_since`](snippets/postBulkGet/alternative_example_request_for_atts_since.py) + +[embedmd]:# (snippets/postBulkGet/alternative_example_request_for_atts_since.py) +```py +# section: code +from ibmcloudant.cloudant_v1 import BulkGetQueryDocument, CloudantV1 + +service = CloudantV1.new_instance() + +bulk_get_doc = BulkGetQueryDocument( + id='order00058', + atts_since=['1-00000000000000000000000000000000']) +response = service.post_bulk_get( + db='orders', + docs=[bulk_get_doc] +).get_result() + +print(response) +``` ## postChanges -### post `/{db}/_changes` +_POST `/{db}/_changes`_ + +### [Example request](snippets/postChanges/example_request.py) + +[embedmd]:# (snippets/postChanges/example_request.py) +```py +# section: code +from ibmcloudant.cloudant_v1 import CloudantV1 + +service = CloudantV1.new_instance() +response = service.post_changes( + db='orders' +).get_result() + +print(response) +``` + +### [Example request as a stream](snippets/postChanges/example_request_as_a_stream.py) -- [Example request](./postChanges/example_request.py) -- [Example request as a stream](./postChanges/example_request_as_a_stream.py) +[embedmd]:# (snippets/postChanges/example_request_as_a_stream.py) +```py +# section: code +from ibmcloudant.cloudant_v1 import CloudantV1 + +service = CloudantV1.new_instance() + +result = service.post_changes_as_stream( + db='orders' +).get_result() + +with open('result.json', 'wb') as f: + for chunk in result.iter_content(): + f.write(chunk) +``` ## deleteDesignDocument -### delete `/{db}/_design/{ddoc}` +_DELETE `/{db}/_design/{ddoc}`_ + +### [Example request](snippets/deleteDesignDocument/example_request.py) + +[embedmd]:# (snippets/deleteDesignDocument/example_request.py) +```py +# section: code +from ibmcloudant.cloudant_v1 import CloudantV1 -- [Example request](./deleteDesignDocument/example_request.py) +service = CloudantV1.new_instance() + +response = service.delete_design_document( + db='products', + ddoc='appliances', + rev='1-00000000000000000000000000000000' +).get_result() + +print(response) +``` ## getDesignDocument -### get `/{db}/_design/{ddoc}` +_GET `/{db}/_design/{ddoc}`_ + +### [Example request](snippets/getDesignDocument/example_request.py) + +[embedmd]:# (snippets/getDesignDocument/example_request.py) +```py +# section: code +from ibmcloudant.cloudant_v1 import CloudantV1 + +service = CloudantV1.new_instance() -- [Example request](./getDesignDocument/example_request.py) +response = service.get_design_document( + db='products', + ddoc='appliances' +).get_result() + +print(response) +``` ## headDesignDocument -### head `/{db}/_design/{ddoc}` +_HEAD `/{db}/_design/{ddoc}`_ + +### [Example request](snippets/headDesignDocument/example_request.py) + +[embedmd]:# (snippets/headDesignDocument/example_request.py) +```py +# section: code +from ibmcloudant.cloudant_v1 import CloudantV1 -- [Example request](./headDesignDocument/example_request.py) +service = CloudantV1.new_instance() + +response = service.head_design_document( + db='events', + ddoc='checkout' +) +print(response.get_status_code()) +print(response.get_headers()['ETag']) +``` ## putDesignDocument -### put `/{db}/_design/{ddoc}` +_PUT `/{db}/_design/{ddoc}`_ + +### [Example request](snippets/putDesignDocument/example_request.py) + +[embedmd]:# (snippets/putDesignDocument/example_request.py) +```py +# section: code +from ibmcloudant.cloudant_v1 import Analyzer, AnalyzerConfiguration, CloudantV1, DesignDocument, DesignDocumentOptions, DesignDocumentViewsMapReduce, SearchIndexDefinition + +service = CloudantV1.new_instance() + +email_view_map_reduce = DesignDocumentViewsMapReduce( + map='function(doc) { if(doc.email_verified === true) { emit(doc.email, [doc.name, doc.email_verified, doc.joined]); }}' +) + +user_index = SearchIndexDefinition( + index='function(doc) { index("name", doc.name); index("active", doc.active); }', + analyzer=AnalyzerConfiguration(name="standard", fields={"email": Analyzer(name="email")})) + +design_document = DesignDocument( + views={'getVerifiedEmails': email_view_map_reduce}, + indexes={'activeUsers': user_index} +) -- [Example request](./putDesignDocument/example_request.py) +response = service.put_design_document( + db='users', + design_document=design_document, + ddoc='allusers' +).get_result() -## getGeo +print(response) -### get `/{db}/_design/{ddoc}/_geo/{index}` +# Partitioned DesignDocument Example -- [Example request](./getGeo/example_request.py) +product_map = DesignDocumentViewsMapReduce( + map='function(doc) { emit(doc.productId, [doc.date, doc.eventType, doc.userId]); }' +) -## getGeoIndexInformation +date_index = SearchIndexDefinition( + index='function(doc) { index("date", doc.date); }', + analyzer=AnalyzerConfiguration(name="classic", fields={"description": Analyzer(name="english")}) +) -### get `/{db}/_design/{ddoc}/_geo_info/{index}` +design_document_options = DesignDocumentOptions( + partitioned=True +) -- [Example request](./getGeoIndexInformation/example_request.py) +partitioned_design_doc = DesignDocument( + views={'byProductId': product_map}, + indexes={'findByDate': date_index}, + options=design_document_options +) + +response = service.put_design_document( + db='events', + design_document=partitioned_design_doc, + ddoc='checkout' +).get_result() + +print(response) +# section: markdown +# This example creates `allusers` design document in the `users` database and `checkout` design document in the partitioned `events` database. +``` ## getDesignDocumentInformation -### get `/{db}/_design/{ddoc}/_info` +_GET `/{db}/_design/{ddoc}/_info`_ + +### [Example request](snippets/getDesignDocumentInformation/example_request.py) + +[embedmd]:# (snippets/getDesignDocumentInformation/example_request.py) +```py +# section: code +from ibmcloudant.cloudant_v1 import CloudantV1 + +service = CloudantV1.new_instance() -- [Example request](./getDesignDocumentInformation/example_request.py) +response = service.get_design_document_information( + db='products', + ddoc='appliances' +).get_result() + +print(response) +``` ## postSearch -### post `/{db}/_design/{ddoc}/_search/{index}` +_POST `/{db}/_design/{ddoc}/_search/{index}`_ + +### [Example request](snippets/postSearch/example_request.py) + +[embedmd]:# (snippets/postSearch/example_request.py) +```py +# section: code +from ibmcloudant.cloudant_v1 import CloudantV1 + +service = CloudantV1.new_instance() + +response = service.post_search( + db='users', + ddoc='allusers', + index='activeUsers', + query='name:Jane* AND active:True' +).get_result() -- [Example request](./postSearch/example_request.py) +print(response) +# section: markdown +# 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) +``` ## getSearchInfo -### get `/{db}/_design/{ddoc}/_search_info/{index}` +_GET `/{db}/_design/{ddoc}/_search_info/{index}`_ -- [Example request](./getSearchInfo/example_request.py) +### [Example request](snippets/getSearchInfo/example_request.py) + +[embedmd]:# (snippets/getSearchInfo/example_request.py) +```py +# section: code +from ibmcloudant.cloudant_v1 import CloudantV1 + +service = CloudantV1.new_instance() + +response = service.get_search_info( + db='events', + ddoc='checkout', + index='findByDate' +).get_result() + +print(response) +# section: markdown +# 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) +``` ## postView -### post `/{db}/_design/{ddoc}/_view/{view}` +_POST `/{db}/_design/{ddoc}/_view/{view}`_ + +### [Example request](snippets/postView/example_request.py) + +[embedmd]:# (snippets/postView/example_request.py) +```py +# section: code +from ibmcloudant.cloudant_v1 import CloudantV1 -- [Example request](./postView/example_request.py) +service = CloudantV1.new_instance() + +response = service.post_view( + db='users', + ddoc='allusers', + view='getVerifiedEmails' +).get_result() + +print(response) +# section: markdown +# This example requires the `getVerifiedEmails` view to exist. To create the design document with this view, see [Create or modify a design document.](#putdesigndocument) +``` ## postViewQueries -### post `/{db}/_design/{ddoc}/_view/{view}/queries` +_POST `/{db}/_design/{ddoc}/_view/{view}/queries`_ + +### [Example request](snippets/postViewQueries/example_request.py) + +[embedmd]:# (snippets/postViewQueries/example_request.py) +```py +# section: code +from ibmcloudant.cloudant_v1 import CloudantV1, ViewQuery + +service = CloudantV1.new_instance() -- [Example request](./postViewQueries/example_request.py) +query1 = ViewQuery( + include_docs=True, + limit=5 +) +query2 = ViewQuery( + descending=True, + skip=1 +) + +response = service.post_view_queries( + db='users', + ddoc='allusers', + queries=[query1, query2], + view='getVerifiedEmails' +).get_result() + +print(response) +# section: markdown +# This example requires the `getVerifiedEmails` view to exist. To create the design document with this view, see [Create or modify a design document.](#putdesigndocument) +``` ## postDesignDocs -### post `/{db}/_design_docs` +_POST `/{db}/_design_docs`_ + +### [Example request](snippets/postDesignDocs/example_request.py) + +[embedmd]:# (snippets/postDesignDocs/example_request.py) +```py +# section: code +from ibmcloudant.cloudant_v1 import CloudantV1 + +service = CloudantV1.new_instance() -- [Example request](./postDesignDocs/example_request.py) +response = service.post_design_docs( + descending=True, + db='users' +).get_result() + +print(response) +``` ## postDesignDocsQueries -### post `/{db}/_design_docs/queries` +_POST `/{db}/_design_docs/queries`_ + +### [Example request](snippets/postDesignDocsQueries/example_request.py) + +[embedmd]:# (snippets/postDesignDocsQueries/example_request.py) +```py +# section: code +from ibmcloudant.cloudant_v1 import AllDocsQuery, CloudantV1 + +service = CloudantV1.new_instance() + +doc1 = AllDocsQuery( + descending=True, + include_docs=True, + limit=10 +) +doc2 = AllDocsQuery( + inclusive_end=True, + start_key='_design/allusers', + skip=1 +) -- [Example request](./postDesignDocsQueries/example_request.py) +response = service.post_design_docs_queries( + db='users', + queries=[doc1, doc2] +).get_result() + +print(response) +``` ## postExplain -### post `/{db}/_explain` +_POST `/{db}/_explain`_ -- [Example request](./postExplain/example_request.py) +### [Example request](snippets/postExplain/example_request.py) -## postFind +[embedmd]:# (snippets/postExplain/example_request.py) +```py +# section: code +from ibmcloudant.cloudant_v1 import CloudantV1 -### post `/{db}/_find` +service = CloudantV1.new_instance() -- [Example request for "json" index type](./postFind/example_request_for_json_index_type.py) -- [Example request for "text" index type](./postFind/example_request_for_text_index_type.py) +response = service.post_explain( + db='users', + execution_stats=True, + limit=10, + selector={'type': {"$eq": "user"}} +).get_result() -## postGeoCleanup +print(response) +``` -### post `/{db}/_geo_cleanup` +## postFind -- [Example request](./postGeoCleanup/example_request.py) +_POST `/{db}/_find`_ + +### [Example request for "json" index type](snippets/postFind/example_request_for_json_index_type.py) + +[embedmd]:# (snippets/postFind/example_request_for_json_index_type.py) +```py +# section: code +from ibmcloudant.cloudant_v1 import CloudantV1 + +service = CloudantV1.new_instance() + +response = service.post_find( + db='users', + selector={'email_verified': {'$eq': True}}, + fields=["_id", "type", "name", "email"], + sort=[{'email': 'desc'}], + limit=3 +).get_result() +print(response) +# section: markdown +# This example requires the `getUserByEmail` Cloudant Query "json" index to exist. To create the index, see [Create a new index on a database.](#postindex) +``` + +### [Example request for "text" index type](snippets/postFind/example_request_for_text_index_type.py) + +[embedmd]:# (snippets/postFind/example_request_for_text_index_type.py) +```py +# section: code +from ibmcloudant.cloudant_v1 import CloudantV1 + +service = CloudantV1.new_instance() + +response = service.post_find( + db='users', + selector={'address': {'$exists': True}}, + fields=["_id", "type", "name", "email", "address"], + limit=3 +).get_result() +print(response) +# section: markdown +# This example requires the `getUserByAddress` Cloudant Query "text" index to exist. To create the index, see [Create a new index on a database.](#postindex) +``` ## getIndexesInformation -### get `/{db}/_index` +_GET `/{db}/_index`_ -- [Example request](./getIndexesInformation/example_request.py) +### [Example request](snippets/getIndexesInformation/example_request.py) -## postIndex +[embedmd]:# (snippets/getIndexesInformation/example_request.py) +```py +# section: code +from ibmcloudant.cloudant_v1 import CloudantV1 + +service = CloudantV1.new_instance() -### post `/{db}/_index` +response = service.get_indexes_information( + db='users' +).get_result() -- [Example request using "json" type index](./postIndex/example_request_using_json_type_index.py) -- [Example request using "text" type index](./postIndex/example_request_using_text_type_index.py) +print(response) +``` + +## postIndex + +_POST `/{db}/_index`_ + +### [Example request using "json" type index](snippets/postIndex/example_request_using_json_type_index.py) + +[embedmd]:# (snippets/postIndex/example_request_using_json_type_index.py) +```py +# section: code +from ibmcloudant.cloudant_v1 import CloudantV1, IndexDefinition, IndexField + +service = CloudantV1.new_instance() + +# Type "json" index fields require an object that maps the name of a field to a sort direction. +index_field = IndexField( + email="asc" +) +index = IndexDefinition( + fields=[index_field] +) + +response = service.post_index( + db='users', + ddoc='json-index', + name='getUserByEmail', + index=index, + type='json' +).get_result() + +print(response) +``` + +### [Example request using "text" type index](snippets/postIndex/example_request_using_text_type_index.py) + +[embedmd]:# (snippets/postIndex/example_request_using_text_type_index.py) +```py +# section: code +from ibmcloudant.cloudant_v1 import CloudantV1, IndexDefinition, IndexField + +service = CloudantV1.new_instance() + +# Type "text" index fields require an object with a name and type properties for the field. +index_field = IndexField( + name="address", + type=IndexField.TypeEnum.STRING +) +index = IndexDefinition( + fields=[index_field] +) + +response = service.post_index( + db='users', + ddoc='text-index', + name='getUserByAddress', + index=index, + type='text' +).get_result() + +print(response) +``` ## deleteIndex -### delete `/{db}/_index/_design/{ddoc}/{type}/{index}` +_DELETE `/{db}/_index/_design/{ddoc}/{type}/{index}`_ + +### [Example request](snippets/deleteIndex/example_request.py) + +[embedmd]:# (snippets/deleteIndex/example_request.py) +```py +# section: code +from ibmcloudant.cloudant_v1 import CloudantV1 -- [Example request](./deleteIndex/example_request.py) +service = CloudantV1.new_instance() + +response = service.delete_index( + db='users', + ddoc='json-index', + index='getUserByName', + type='json' +).get_result() + +print(response) +# section: markdown +# This example will fail if `getUserByName` index doesn't exist. To create the index, see [Create a new index on a database.](#postindex) +``` ## deleteLocalDocument -### delete `/{db}/_local/{doc_id}` +_DELETE `/{db}/_local/{doc_id}`_ + +### [Example request](snippets/deleteLocalDocument/example_request.py) + +[embedmd]:# (snippets/deleteLocalDocument/example_request.py) +```py +# section: code +from ibmcloudant.cloudant_v1 import CloudantV1 + +service = CloudantV1.new_instance() -- [Example request](./deleteLocalDocument/example_request.py) +response = service.delete_local_document( + db='orders', + doc_id='local-0007741142412418284' +).get_result() + +print(response) +``` ## getLocalDocument -### get `/{db}/_local/{doc_id}` +_GET `/{db}/_local/{doc_id}`_ + +### [Example request](snippets/getLocalDocument/example_request.py) + +[embedmd]:# (snippets/getLocalDocument/example_request.py) +```py +# section: code +from ibmcloudant.cloudant_v1 import CloudantV1 + +service = CloudantV1.new_instance() + +response = service.get_local_document( + db='orders', + doc_id='local-0007741142412418284' +).get_result() -- [Example request](./getLocalDocument/example_request.py) +print(response) +``` ## putLocalDocument -### put `/{db}/_local/{doc_id}` +_PUT `/{db}/_local/{doc_id}`_ -- [Example request](./putLocalDocument/example_request.py) +### [Example request](snippets/putLocalDocument/example_request.py) -## postMissingRevs +[embedmd]:# (snippets/putLocalDocument/example_request.py) +```py +# section: code +from ibmcloudant.cloudant_v1 import Document, CloudantV1 -### post `/{db}/_missing_revs` +service = CloudantV1.new_instance() -- [Example request](./postMissingRevs/example_request.py) +local_document = Document( + type='order', + user='Bob Smith', + orderId='0007741142412418284', + userId='abc123', + total=214.98, + deliveryAddress='19 Front Street, Darlington, DL5 1TY', + delivered='true', + courier='UPS', + courierId='15125425151261289', + date='2019-01-28T10:44:22.000Z' +) + +response = service.put_local_document( + db='orders', + doc_id='local-0007741142412418284', + document=local_document, +).get_result() + +print(response) +``` ## getPartitionInformation -### get `/{db}/_partition/{partition_key}` +_GET `/{db}/_partition/{partition_key}`_ + +### [Example request](snippets/getPartitionInformation/example_request.py) + +[embedmd]:# (snippets/getPartitionInformation/example_request.py) +```py +# section: code +from ibmcloudant.cloudant_v1 import CloudantV1 + +service = CloudantV1.new_instance() + +response = service.get_partition_information( + db='events', + partition_key='ns1HJS13AMkK' +).get_result() -- [Example request](./getPartitionInformation/example_request.py) +print(response) +``` ## postPartitionAllDocs -### post `/{db}/_partition/{partition_key}/_all_docs` +_POST `/{db}/_partition/{partition_key}/_all_docs`_ -- [Example request](./postPartitionAllDocs/example_request.py) +### [Example request](snippets/postPartitionAllDocs/example_request.py) + +[embedmd]:# (snippets/postPartitionAllDocs/example_request.py) +```py +# section: code +from ibmcloudant.cloudant_v1 import CloudantV1 + +service = CloudantV1.new_instance() + +response = service.post_partition_all_docs( + db='events', + partition_key='ns1HJS13AMkK', + include_docs=True +).get_result() + +print(response) +``` ## postPartitionSearch -### post `/{db}/_partition/{partition_key}/_design/{ddoc}/_search/{index}` +_POST `/{db}/_partition/{partition_key}/_design/{ddoc}/_search/{index}`_ + +### [Example request](snippets/postPartitionSearch/example_request.py) -- [Example request](./postPartitionSearch/example_request.py) +[embedmd]:# (snippets/postPartitionSearch/example_request.py) +```py +# section: code +from ibmcloudant.cloudant_v1 import CloudantV1 + +service = CloudantV1.new_instance() + +response = service.post_partition_search( + db='events', + partition_key='ns1HJS13AMkK', + ddoc='checkout', + index='findByDate', + query='date:[2019-01-01T12:00:00.000Z TO 2019-01-31T12:00:00.000Z]' +).get_result() + +print(response) +# section: markdown +# 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) +``` ## postPartitionView -### post `/{db}/_partition/{partition_key}/_design/{ddoc}/_view/{view}` +_POST `/{db}/_partition/{partition_key}/_design/{ddoc}/_view/{view}`_ + +### [Example request](snippets/postPartitionView/example_request.py) + +[embedmd]:# (snippets/postPartitionView/example_request.py) +```py +# section: code +from ibmcloudant.cloudant_v1 import CloudantV1 + +service = CloudantV1.new_instance() + +response = service.post_partition_view( + db='events', + ddoc='checkout', + include_docs=True, + limit=10, + partition_key='ns1HJS13AMkK', + view='byProductId' +).get_result() + +print(response) +# section: markdown +# 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) +``` + +## postPartitionExplain + +_POST `/{db}/_partition/{partition_key}/_explain`_ -- [Example request](./postPartitionView/example_request.py) +### [Example request](snippets/postPartitionExplain/example_request.py) + +[embedmd]:# (snippets/postPartitionFind/example_request.py) +```py +# section: code +from ibmcloudant.cloudant_v1 import CloudantV1 + +service = CloudantV1.new_instance() + +response = service.post_partition_find( + db='events', + partition_key='ns1HJS13AMkK', + fields=['productId', 'eventType', 'date'], + selector={'userId': {'$eq': 'abc123'}} +).get_result() + +print(response) +``` ## postPartitionFind -### post `/{db}/_partition/{partition_key}/_find` +_POST `/{db}/_partition/{partition_key}/_find`_ + +### [Example request](snippets/postPartitionFind/example_request.py) -- [Example request](./postPartitionFind/example_request.py) +[embedmd]:# (snippets/postPartitionFind/example_request.py) +```py +# section: code +from ibmcloudant.cloudant_v1 import CloudantV1 + +service = CloudantV1.new_instance() + +response = service.post_partition_find( + db='events', + partition_key='ns1HJS13AMkK', + fields=['productId', 'eventType', 'date'], + selector={'userId': {'$eq': 'abc123'}} +).get_result() + +print(response) +``` ## postRevsDiff -### post `/{db}/_revs_diff` +_POST `/{db}/_revs_diff`_ + +### [Example request](snippets/postRevsDiff/example_request.py) + +[embedmd]:# (snippets/postRevsDiff/example_request.py) +```py +# section: code +from ibmcloudant.cloudant_v1 import DocumentRevisions, CloudantV1 -- [Example request](./postRevsDiff/example_request.py) +service = CloudantV1.new_instance() + +revs_diff = DocumentRevisions( + order00077=[ + "1-00000000000000000000000000000000", # missing revision + "2-11111111111111111111111111111111", # missing revision + "3-22222222222222222222222222222222" # possible ancestor revision +] +) + +response = service.post_revs_diff( + db='orders', + revs_diff_request=revs_diff.to_dict() +).get_result() + +print(response) +// section: markdown +// This example requires the example revisions in the POST body to be replaced with valid revisions. +``` ## getSecurity -### get `/{db}/_security` +_GET `/{db}/_security`_ + +### [Example request](snippets/getSecurity/example_request.py) + +[embedmd]:# (snippets/getSecurity/example_request.py) +```py +# section: code +from ibmcloudant.cloudant_v1 import CloudantV1 -- [Example request](./getSecurity/example_request.py) +service = CloudantV1.new_instance() + +response = service.get_security( + db='products' +).get_result() + +print(response) +``` ## putSecurity -### put `/{db}/_security` +_PUT `/{db}/_security`_ + +### [Example request](snippets/putSecurity/example_request.py) + +[embedmd]:# (snippets/putSecurity/example_request.py) +```py +# section: code +from ibmcloudant.cloudant_v1 import CloudantV1, SecurityObject + +service = CloudantV1.new_instance() -- [Example request](./putSecurity/example_request.py) +members = SecurityObject( + names=['user1', 'user2'], + roles=['developers'] +) + +response = service.put_security( + db='products', + members=members +).get_result() + +print(response) +# section: markdown +# 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`. +# section: markdown +# 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. +``` ## getShardsInformation -### get `/{db}/_shards` +_GET `/{db}/_shards`_ + +### [Example request](snippets/getShardsInformation/example_request.py) + +[embedmd]:# (snippets/getShardsInformation/example_request.py) +```py +# section: code +from ibmcloudant.cloudant_v1 import CloudantV1 + +service = CloudantV1.new_instance() -- [Example request](./getShardsInformation/example_request.py) +response = service.get_shards_information( + db='products' +).get_result() + +print(response) +``` ## getDocumentShardsInfo -### get `/{db}/_shards/{doc_id}` +_GET `/{db}/_shards/{doc_id}`_ + +### [Example request](snippets/getDocumentShardsInfo/example_request.py) + +[embedmd]:# (snippets/getDocumentShardsInfo/example_request.py) +```py +# section: code +from ibmcloudant.cloudant_v1 import CloudantV1 + +service = CloudantV1.new_instance() + +response = service.get_document_shards_info( + db='products', + doc_id='1000042' +).get_result() -- [Example request](./getDocumentShardsInfo/example_request.py) +print(response) +``` ## deleteDocument -### delete `/{db}/{doc_id}` +_DELETE `/{db}/{doc_id}`_ -- [Example request](./deleteDocument/example_request.py) +### [Example request](snippets/deleteDocument/example_request.py) + +[embedmd]:# (snippets/deleteDocument/example_request.py) +```py +# section: code +from ibmcloudant.cloudant_v1 import CloudantV1 + +service = CloudantV1.new_instance() + +response = service.delete_document( + db='orders', + doc_id='order00058', + rev='1-00000000000000000000000000000000' +).get_result() + +print(response) +``` ## getDocument -### get `/{db}/{doc_id}` +_GET `/{db}/{doc_id}`_ + +### [Example request](snippets/getDocument/example_request.py) -- [Example request](./getDocument/example_request.py) +[embedmd]:# (snippets/getDocument/example_request.py) +```py +# section: code +from ibmcloudant.cloudant_v1 import CloudantV1 + +service = CloudantV1.new_instance() + +response = service.get_document( + db='products', + doc_id='1000042' +).get_result() + +print(response) +``` ## headDocument -### head `/{db}/{doc_id}` +_HEAD `/{db}/{doc_id}`_ + +### [Example request](snippets/headDocument/example_request.py) + +[embedmd]:# (snippets/headDocument/example_request.py) +```py +# section: code +from ibmcloudant.cloudant_v1 import CloudantV1 -- [Example request](./headDocument/example_request.py) +service = CloudantV1.new_instance() + +response = service.head_document( + db='orders', + doc_id='order00058' +) +print(response.get_status_code()) +print(response.get_headers()['ETag']) +``` ## putDocument -### put `/{db}/{doc_id}` +_PUT `/{db}/{doc_id}`_ + +### [Example request](snippets/putDocument/example_request.py) + +[embedmd]:# (snippets/putDocument/example_request.py) +```py +# section: code +from ibmcloudant.cloudant_v1 import Document, CloudantV1 + +service = CloudantV1.new_instance() + +event_doc = Document( + type='event', + userId='abc123', + eventType='addedToBasket', + productId='1000042', + date='2019-01-28T10:44:22.000Z' +) +response = service.put_document( + db='events', + doc_id='ns1HJS13AMkK:0007241142412418284', + document=event_doc +).get_result() -- [Example request](./putDocument/example_request.py) +print(response) +``` ## deleteAttachment -### delete `/{db}/{doc_id}/{attachment_name}` +_DELETE `/{db}/{doc_id}/{attachment_name}`_ -- [Example request](./deleteAttachment/example_request.py) +### [Example request](snippets/deleteAttachment/example_request.py) + +[embedmd]:# (snippets/deleteAttachment/example_request.py) +```py +# section: code +from ibmcloudant.cloudant_v1 import CloudantV1 + +service = CloudantV1.new_instance() + +response = service.delete_attachment( + db='products', + doc_id='1000042', + attachment_name='product_details.txt', + rev='4-33333333333333333333333333333333' +).get_result() + +print(response) +# section: markdown +# This example requires the `product_details.txt` attachment in `1000042` document to exist. To create the attachment, see [Create or modify an attachment.](#putattachment) +``` ## getAttachment -### get `/{db}/{doc_id}/{attachment_name}` +_GET `/{db}/{doc_id}/{attachment_name}`_ + +### [Example request](snippets/getAttachment/example_request.py) -- [Example request](./getAttachment/example_request.py) +[embedmd]:# (snippets/getAttachment/example_request.py) +```py +# section: code +from ibmcloudant.cloudant_v1 import CloudantV1 + +service = CloudantV1.new_instance() + +response_attachment = service.get_attachment( + db='products', + doc_id='1000042', + attachment_name='product_details.txt' +).get_result().content + +print(response_attachment) +# section: markdown +# This example requires the `product_details.txt` attachment in `1000042` document to exist. To create the attachment, see [Create or modify an attachment.](#putattachment) +``` ## headAttachment -### head `/{db}/{doc_id}/{attachment_name}` +_HEAD `/{db}/{doc_id}/{attachment_name}`_ + +### [Example request](snippets/headAttachment/example_request.py) + +[embedmd]:# (snippets/headAttachment/example_request.py) +```py +# section: code +from ibmcloudant.cloudant_v1 import CloudantV1 -- [Example request](./headAttachment/example_request.py) +service = CloudantV1.new_instance() + +response = service.head_attachment( + db='products', + doc_id='1000042', + attachment_name='product_details.txt' +) +print(response.get_status_code()) +print(response.get_headers()['Content-Length']) +print(response.get_headers()['Content-Type']) +# section: markdown +# This example requires the `product_details.txt` attachment in `1000042` document to exist. To create the attachment, see [Create or modify an attachment.](#putattachment) +``` ## putAttachment -### put `/{db}/{doc_id}/{attachment_name}` +_PUT `/{db}/{doc_id}/{attachment_name}`_ + +### [Example request](snippets/putAttachment/example_request.py) + +[embedmd]:# (snippets/putAttachment/example_request.py) +```py +# section: code +from ibmcloudant.cloudant_v1 import CloudantV1 + +service = CloudantV1.new_instance() + +detailed_description = "This appliance includes..." +response = service.put_attachment( + db='products', + doc_id='1000042', + attachment_name='product_details.txt', + attachment=detailed_description, + content_type='text/plain' +).get_result() -- [Example request](./putAttachment/example_request.py) +print(response) +``` diff --git a/examples/deleteDatabase/example_request.py b/examples/deleteDatabase/example_request.py deleted file mode 100644 index 0ca7bc37..00000000 --- a/examples/deleteDatabase/example_request.py +++ /dev/null @@ -1,8 +0,0 @@ -# section: code -from ibmcloudant.cloudant_v1 import CloudantV1 - -service = CloudantV1.new_instance() - -response = service.delete_database(db='').get_result() - -print(response) diff --git a/examples/getGeo/example_request.py b/examples/getGeo/example_request.py deleted file mode 100644 index 0e59fc60..00000000 --- a/examples/getGeo/example_request.py +++ /dev/null @@ -1,15 +0,0 @@ -# section: code -from ibmcloudant.cloudant_v1 import CloudantV1 - -service = CloudantV1.new_instance() - -response = service.get_geo( - db='stores', - ddoc='places', - index='pointsInEngland', - bbox='-50.52,-4.46,54.59,1.45', - include_docs=True, - nearest=True -).get_result() - -print(response) diff --git a/examples/getGeoIndexInformation/example_request.py b/examples/getGeoIndexInformation/example_request.py deleted file mode 100644 index 45436c6c..00000000 --- a/examples/getGeoIndexInformation/example_request.py +++ /dev/null @@ -1,12 +0,0 @@ -# section: code -from ibmcloudant.cloudant_v1 import CloudantV1 - -service = CloudantV1.new_instance() - -response = service.get_geo_index_information( - db='stores', - ddoc='places', - index='pointsInEngland' -).get_result() - -print(response) diff --git a/examples/getSearchInfo/example_request.py b/examples/getSearchInfo/example_request.py deleted file mode 100644 index 71c97d98..00000000 --- a/examples/getSearchInfo/example_request.py +++ /dev/null @@ -1,14 +0,0 @@ -# section: code -from ibmcloudant.cloudant_v1 import CloudantV1 - -service = CloudantV1.new_instance() - -response = service.get_search_info( - db='products', - ddoc='appliances', - index='findByPrice' -).get_result() - -print(response) -# section: markdown -# This example requires the `findByPrice` Cloudant Search partitioned index to exist. To create the design document with this index, see [Create or modify a design document.](#putdesigndocument) diff --git a/examples/postActivityTrackerEvents/example_request.py b/examples/postActivityTrackerEvents/example_request.py deleted file mode 100644 index a4af6f0a..00000000 --- a/examples/postActivityTrackerEvents/example_request.py +++ /dev/null @@ -1,10 +0,0 @@ -# section: code -from ibmcloudant.cloudant_v1 import CloudantV1 - -service = CloudantV1.new_instance() - -response = service.post_activity_tracker_events( - types=['management'] -).get_result() - -print(response) diff --git a/examples/postMissingRevs/example_request.py b/examples/postMissingRevs/example_request.py deleted file mode 100644 index 6550e8d0..00000000 --- a/examples/postMissingRevs/example_request.py +++ /dev/null @@ -1,12 +0,0 @@ -# section: code -from ibmcloudant.cloudant_v1 import DocumentRevisions, CloudantV1 - -service = CloudantV1.new_instance() - -revs = DocumentRevisions(order00077=['', '<2-missing-revision>']) -response = service.post_missing_revs( - db='orders', - missing_revs=revs.to_dict() -).get_result() - -print(response) diff --git a/examples/postPartitionSearch/example_request.py b/examples/postPartitionSearch/example_request.py deleted file mode 100644 index 34eb160c..00000000 --- a/examples/postPartitionSearch/example_request.py +++ /dev/null @@ -1,16 +0,0 @@ -# section: code -from ibmcloudant.cloudant_v1 import CloudantV1 - -service = CloudantV1.new_instance() - -response = service.post_partition_search( - db='products', - partition_key='small-appliances', - ddoc='appliances', - index='findByPrice', - query='price:[14 TO 20]' -).get_result() - -print(response) -# section: markdown -# This example requires the `findByPrice` Cloudant Search partitioned index to exist. To create the design document with this index, see [Create or modify a design document.](#putdesigndocument) diff --git a/examples/postPartitionView/example_request.py b/examples/postPartitionView/example_request.py deleted file mode 100644 index 27251002..00000000 --- a/examples/postPartitionView/example_request.py +++ /dev/null @@ -1,16 +0,0 @@ -# section: code -from ibmcloudant.cloudant_v1 import CloudantV1 - -service = CloudantV1.new_instance() - -response = service.post_partition_view( - db='products', - ddoc='appliances', - limit=10, - partition_key='small-appliances', - view='byApplianceProdId' -).get_result() - -print(response) -# section: markdown -# This example requires the `byApplianceProdId` partitioned view to exist. To create the design document with this view, see [Create or modify a design document.](#putdesigndocument) diff --git a/examples/postRevsDiff/example_request.py b/examples/postRevsDiff/example_request.py deleted file mode 100644 index 85cc5b75..00000000 --- a/examples/postRevsDiff/example_request.py +++ /dev/null @@ -1,19 +0,0 @@ -# section: code -from ibmcloudant.cloudant_v1 import DocumentRevisions, CloudantV1 - -service = CloudantV1.new_instance() - -revs_diff = DocumentRevisions( - order00077=[ - "<1-missing-revision>", - "<2-missing-revision>", - "<3-possible-ancestor-revision>" -] -) - -response = service.post_revs_diff( - db='orders', - revs_diff_request=revs_diff.to_dict() -).get_result() - -print(response) diff --git a/examples/putDatabase/example_request.py b/examples/putDatabase/example_request.py deleted file mode 100644 index d3424b70..00000000 --- a/examples/putDatabase/example_request.py +++ /dev/null @@ -1,8 +0,0 @@ -# section: code -from ibmcloudant.cloudant_v1 import CloudantV1 - -service = CloudantV1.new_instance() - -response = service.put_database(db='products', partitioned=True).get_result() - -print(response) diff --git a/examples/deleteAttachment/example_request.py b/examples/snippets/deleteAttachment/example_request.py similarity index 60% rename from examples/deleteAttachment/example_request.py rename to examples/snippets/deleteAttachment/example_request.py index f51d4e52..1d4edd2b 100644 --- a/examples/deleteAttachment/example_request.py +++ b/examples/snippets/deleteAttachment/example_request.py @@ -5,11 +5,11 @@ response = service.delete_attachment( db='products', - doc_id='small-appliances:100001', + doc_id='1000042', attachment_name='product_details.txt', - rev='4-1a0d1cd6f40472509e9aac646183736a' + rev='4-33333333333333333333333333333333' ).get_result() print(response) # section: markdown -# This example requires the `product_details.txt` attachment in `small-appliances:100001` document to exist. To create the attachment, see [Create or modify an attachment.](#putattachment) +# This example requires the `product_details.txt` attachment in `1000042` document to exist. To create the attachment, see [Create or modify an attachment.](#putattachment) diff --git a/examples/getUpInformation/example_request.py b/examples/snippets/deleteDatabase/example_request.py similarity index 65% rename from examples/getUpInformation/example_request.py rename to examples/snippets/deleteDatabase/example_request.py index 4e6be59e..77f7c021 100644 --- a/examples/getUpInformation/example_request.py +++ b/examples/snippets/deleteDatabase/example_request.py @@ -3,6 +3,6 @@ service = CloudantV1.new_instance() -response = service.get_up_information().get_result() +response = service.delete_database(db='products').get_result() print(response) diff --git a/examples/deleteDesignDocument/example_request.py b/examples/snippets/deleteDesignDocument/example_request.py similarity index 83% rename from examples/deleteDesignDocument/example_request.py rename to examples/snippets/deleteDesignDocument/example_request.py index bc175eb4..af0aa41f 100644 --- a/examples/deleteDesignDocument/example_request.py +++ b/examples/snippets/deleteDesignDocument/example_request.py @@ -6,7 +6,7 @@ response = service.delete_design_document( db='products', ddoc='appliances', - rev='1-98e6a25b3b45df62e7d47095ac15b16a' + rev='1-00000000000000000000000000000000' ).get_result() print(response) diff --git a/examples/deleteDocument/example_request.py b/examples/snippets/deleteDocument/example_request.py similarity index 65% rename from examples/deleteDocument/example_request.py rename to examples/snippets/deleteDocument/example_request.py index a51d55c1..e3ccea00 100644 --- a/examples/deleteDocument/example_request.py +++ b/examples/snippets/deleteDocument/example_request.py @@ -4,9 +4,9 @@ service = CloudantV1.new_instance() response = service.delete_document( - db='events', - doc_id='0007241142412418284', - rev='2-9a0d1cd9f40472509e9aac6461837367' + db='orders', + doc_id='order00058', + rev='1-00000000000000000000000000000000' ).get_result() print(response) diff --git a/examples/deleteIndex/example_request.py b/examples/snippets/deleteIndex/example_request.py similarity index 100% rename from examples/deleteIndex/example_request.py rename to examples/snippets/deleteIndex/example_request.py diff --git a/examples/deleteLocalDocument/example_request.py b/examples/snippets/deleteLocalDocument/example_request.py similarity index 100% rename from examples/deleteLocalDocument/example_request.py rename to examples/snippets/deleteLocalDocument/example_request.py diff --git a/examples/deleteReplicationDocument/example_request.py b/examples/snippets/deleteReplicationDocument/example_request.py similarity index 83% rename from examples/deleteReplicationDocument/example_request.py rename to examples/snippets/deleteReplicationDocument/example_request.py index 9e2d91ff..a3325376 100644 --- a/examples/deleteReplicationDocument/example_request.py +++ b/examples/snippets/deleteReplicationDocument/example_request.py @@ -5,7 +5,7 @@ response = service.delete_replication_document( doc_id='repldoc-example', -rev='3-a0ccbdc6fe95b4184f9031d086034d85' +rev='1-00000000000000000000000000000000' ).get_result() print(response) diff --git a/examples/getActiveTasks/example_request.py b/examples/snippets/getActiveTasks/example_request.py similarity index 100% rename from examples/getActiveTasks/example_request.py rename to examples/snippets/getActiveTasks/example_request.py diff --git a/examples/getActivityTrackerEvents/example_request.py b/examples/snippets/getActivityTrackerEvents/example_request.py similarity index 100% rename from examples/getActivityTrackerEvents/example_request.py rename to examples/snippets/getActivityTrackerEvents/example_request.py diff --git a/examples/getAllDbs/example_request.py b/examples/snippets/getAllDbs/example_request.py similarity index 100% rename from examples/getAllDbs/example_request.py rename to examples/snippets/getAllDbs/example_request.py diff --git a/examples/getAttachment/example_request.py b/examples/snippets/getAttachment/example_request.py similarity index 67% rename from examples/getAttachment/example_request.py rename to examples/snippets/getAttachment/example_request.py index da91599e..ab1d3044 100644 --- a/examples/getAttachment/example_request.py +++ b/examples/snippets/getAttachment/example_request.py @@ -5,10 +5,10 @@ response_attachment = service.get_attachment( db='products', - doc_id='small-appliances:100001', + doc_id='1000042', attachment_name='product_details.txt' ).get_result().content print(response_attachment) # section: markdown -# This example requires the `product_details.txt` attachment in `small-appliances:100001` document to exist. To create the attachment, see [Create or modify an attachment.](#putattachment) +# This example requires the `product_details.txt` attachment in `1000042` document to exist. To create the attachment, see [Create or modify an attachment.](#putattachment) diff --git a/examples/getCapacityThroughputInformation/example_request.py b/examples/snippets/getCapacityThroughputInformation/example_request.py similarity index 100% rename from examples/getCapacityThroughputInformation/example_request.py rename to examples/snippets/getCapacityThroughputInformation/example_request.py diff --git a/examples/getCorsInformation/example_request.py b/examples/snippets/getCorsInformation/example_request.py similarity index 100% rename from examples/getCorsInformation/example_request.py rename to examples/snippets/getCorsInformation/example_request.py diff --git a/examples/getCurrentThroughputInformation/example_request.py b/examples/snippets/getCurrentThroughputInformation/example_request.py similarity index 100% rename from examples/getCurrentThroughputInformation/example_request.py rename to examples/snippets/getCurrentThroughputInformation/example_request.py diff --git a/examples/getDatabaseInformation/example_request.py b/examples/snippets/getDatabaseInformation/example_request.py similarity index 100% rename from examples/getDatabaseInformation/example_request.py rename to examples/snippets/getDatabaseInformation/example_request.py diff --git a/examples/getDbUpdates/example_request.py b/examples/snippets/getDbUpdates/example_request.py similarity index 93% rename from examples/getDbUpdates/example_request.py rename to examples/snippets/getDbUpdates/example_request.py index 8c99878f..ea8ce344 100644 --- a/examples/getDbUpdates/example_request.py +++ b/examples/snippets/getDbUpdates/example_request.py @@ -5,7 +5,6 @@ response = service.get_db_updates( feed='normal', - heartbeat=10000, since='now' ).get_result() diff --git a/examples/getDesignDocument/example_request.py b/examples/snippets/getDesignDocument/example_request.py similarity index 84% rename from examples/getDesignDocument/example_request.py rename to examples/snippets/getDesignDocument/example_request.py index 7330e77c..97fd1080 100644 --- a/examples/getDesignDocument/example_request.py +++ b/examples/snippets/getDesignDocument/example_request.py @@ -5,8 +5,7 @@ response = service.get_design_document( db='products', - ddoc='appliances', - latest=True + ddoc='appliances' ).get_result() print(response) diff --git a/examples/getDesignDocumentInformation/example_request.py b/examples/snippets/getDesignDocumentInformation/example_request.py similarity index 100% rename from examples/getDesignDocumentInformation/example_request.py rename to examples/snippets/getDesignDocumentInformation/example_request.py diff --git a/examples/getDocument/example_request.py b/examples/snippets/getDocument/example_request.py similarity index 83% rename from examples/getDocument/example_request.py rename to examples/snippets/getDocument/example_request.py index 888454fe..49e43fbd 100644 --- a/examples/getDocument/example_request.py +++ b/examples/snippets/getDocument/example_request.py @@ -5,7 +5,7 @@ response = service.get_document( db='products', - doc_id='small-appliances:1000042' + doc_id='1000042' ).get_result() print(response) diff --git a/examples/getDocumentShardsInfo/example_request.py b/examples/snippets/getDocumentShardsInfo/example_request.py similarity index 84% rename from examples/getDocumentShardsInfo/example_request.py rename to examples/snippets/getDocumentShardsInfo/example_request.py index 4891a04c..52855494 100644 --- a/examples/getDocumentShardsInfo/example_request.py +++ b/examples/snippets/getDocumentShardsInfo/example_request.py @@ -5,7 +5,7 @@ response = service.get_document_shards_info( db='products', - doc_id='small-appliances:1000042' + doc_id='1000042' ).get_result() print(response) diff --git a/examples/getIndexesInformation/example_request.py b/examples/snippets/getIndexesInformation/example_request.py similarity index 100% rename from examples/getIndexesInformation/example_request.py rename to examples/snippets/getIndexesInformation/example_request.py diff --git a/examples/getLocalDocument/example_request.py b/examples/snippets/getLocalDocument/example_request.py similarity index 100% rename from examples/getLocalDocument/example_request.py rename to examples/snippets/getLocalDocument/example_request.py diff --git a/examples/getMembershipInformation/example_request.py b/examples/snippets/getMembershipInformation/example_request.py similarity index 100% rename from examples/getMembershipInformation/example_request.py rename to examples/snippets/getMembershipInformation/example_request.py diff --git a/examples/getPartitionInformation/example_request.py b/examples/snippets/getPartitionInformation/example_request.py similarity index 77% rename from examples/getPartitionInformation/example_request.py rename to examples/snippets/getPartitionInformation/example_request.py index 4fded20d..e5a8b0b9 100644 --- a/examples/getPartitionInformation/example_request.py +++ b/examples/snippets/getPartitionInformation/example_request.py @@ -4,8 +4,8 @@ service = CloudantV1.new_instance() response = service.get_partition_information( - db='products', - partition_key='small-appliances' + db='events', + partition_key='ns1HJS13AMkK' ).get_result() print(response) diff --git a/examples/getReplicationDocument/example_request.py b/examples/snippets/getReplicationDocument/example_request.py similarity index 100% rename from examples/getReplicationDocument/example_request.py rename to examples/snippets/getReplicationDocument/example_request.py diff --git a/examples/getSchedulerDocs/example_request.py b/examples/snippets/getSchedulerDocs/example_request.py similarity index 100% rename from examples/getSchedulerDocs/example_request.py rename to examples/snippets/getSchedulerDocs/example_request.py diff --git a/examples/getSchedulerDocument/example_request.py b/examples/snippets/getSchedulerDocument/example_request.py similarity index 100% rename from examples/getSchedulerDocument/example_request.py rename to examples/snippets/getSchedulerDocument/example_request.py diff --git a/examples/getSchedulerJob/example_request.py b/examples/snippets/getSchedulerJob/example_request.py similarity index 100% rename from examples/getSchedulerJob/example_request.py rename to examples/snippets/getSchedulerJob/example_request.py diff --git a/examples/getSchedulerJobs/example_request.py b/examples/snippets/getSchedulerJobs/example_request.py similarity index 100% rename from examples/getSchedulerJobs/example_request.py rename to examples/snippets/getSchedulerJobs/example_request.py diff --git a/examples/snippets/getSearchInfo/example_request.py b/examples/snippets/getSearchInfo/example_request.py new file mode 100644 index 00000000..b24b7c29 --- /dev/null +++ b/examples/snippets/getSearchInfo/example_request.py @@ -0,0 +1,14 @@ +# section: code +from ibmcloudant.cloudant_v1 import CloudantV1 + +service = CloudantV1.new_instance() + +response = service.get_search_info( + db='events', + ddoc='checkout', + index='findByDate' +).get_result() + +print(response) +# section: markdown +# 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) diff --git a/examples/getSecurity/example_request.py b/examples/snippets/getSecurity/example_request.py similarity index 100% rename from examples/getSecurity/example_request.py rename to examples/snippets/getSecurity/example_request.py diff --git a/examples/getServerInformation/example_request.py b/examples/snippets/getServerInformation/example_request.py similarity index 100% rename from examples/getServerInformation/example_request.py rename to examples/snippets/getServerInformation/example_request.py diff --git a/examples/getSessionInformation/example_request.py b/examples/snippets/getSessionInformation/example_request.py similarity index 100% rename from examples/getSessionInformation/example_request.py rename to examples/snippets/getSessionInformation/example_request.py diff --git a/examples/getShardsInformation/example_request.py b/examples/snippets/getShardsInformation/example_request.py similarity index 100% rename from examples/getShardsInformation/example_request.py rename to examples/snippets/getShardsInformation/example_request.py diff --git a/examples/snippets/getUpInformation/example_request.py b/examples/snippets/getUpInformation/example_request.py new file mode 100644 index 00000000..215a2208 --- /dev/null +++ b/examples/snippets/getUpInformation/example_request.py @@ -0,0 +1,15 @@ +# section: code +from ibmcloudant.cloudant_v1 import CloudantV1 +from ibm_cloud_sdk_core.api_exception import ApiException + +service = CloudantV1.new_instance() +try: + response = service.get_up_information() + + print("Service is up and healthy") + print(response.get_result()) +except ApiException as ae: + if ae.code == 503: + print(f"Service is unavailable, status code: {ae.code}") + else: + print(f"Issue performing health check, status code: {ae.code}, message: {ae.http_response.reason}") diff --git a/examples/getUuids/example_request.py b/examples/snippets/getUuids/example_request.py similarity index 100% rename from examples/getUuids/example_request.py rename to examples/snippets/getUuids/example_request.py diff --git a/examples/headAttachment/example_request.py b/examples/snippets/headAttachment/example_request.py similarity index 71% rename from examples/headAttachment/example_request.py rename to examples/snippets/headAttachment/example_request.py index 8fde6d97..137e002c 100644 --- a/examples/headAttachment/example_request.py +++ b/examples/snippets/headAttachment/example_request.py @@ -5,11 +5,11 @@ response = service.head_attachment( db='products', - doc_id='small-appliances:100001', + doc_id='1000042', attachment_name='product_details.txt' ) print(response.get_status_code()) print(response.get_headers()['Content-Length']) print(response.get_headers()['Content-Type']) # section: markdown -# This example requires the `product_details.txt` attachment in `small-appliances:100001` document to exist. To create the attachment, see [Create or modify an attachment.](#putattachment) +# This example requires the `product_details.txt` attachment in `1000042` document to exist. To create the attachment, see [Create or modify an attachment.](#putattachment) diff --git a/examples/headDatabase/example_request.py b/examples/snippets/headDatabase/example_request.py similarity index 100% rename from examples/headDatabase/example_request.py rename to examples/snippets/headDatabase/example_request.py diff --git a/examples/headDesignDocument/example_request.py b/examples/snippets/headDesignDocument/example_request.py similarity index 85% rename from examples/headDesignDocument/example_request.py rename to examples/snippets/headDesignDocument/example_request.py index c1ecf015..ed2c1a20 100644 --- a/examples/headDesignDocument/example_request.py +++ b/examples/snippets/headDesignDocument/example_request.py @@ -4,8 +4,8 @@ service = CloudantV1.new_instance() response = service.head_design_document( - db='products', - ddoc='appliances' + db='events', + ddoc='checkout' ) print(response.get_status_code()) print(response.get_headers()['ETag']) diff --git a/examples/headDocument/example_request.py b/examples/snippets/headDocument/example_request.py similarity index 81% rename from examples/headDocument/example_request.py rename to examples/snippets/headDocument/example_request.py index 71692421..6141844e 100644 --- a/examples/headDocument/example_request.py +++ b/examples/snippets/headDocument/example_request.py @@ -4,8 +4,8 @@ service = CloudantV1.new_instance() response = service.head_document( - db='events', - doc_id='0007241142412418284' + db='orders', + doc_id='order00058' ) print(response.get_status_code()) print(response.get_headers()['ETag']) diff --git a/examples/headReplicationDocument/example_request.py b/examples/snippets/headReplicationDocument/example_request.py similarity index 100% rename from examples/headReplicationDocument/example_request.py rename to examples/snippets/headReplicationDocument/example_request.py diff --git a/examples/headSchedulerJob/example_request.py b/examples/snippets/headSchedulerJob/example_request.py similarity index 100% rename from examples/headSchedulerJob/example_request.py rename to examples/snippets/headSchedulerJob/example_request.py diff --git a/examples/snippets/headUpInformation/example_request.py b/examples/snippets/headUpInformation/example_request.py new file mode 100644 index 00000000..9942e6d6 --- /dev/null +++ b/examples/snippets/headUpInformation/example_request.py @@ -0,0 +1,14 @@ +# section: code +from ibmcloudant.cloudant_v1 import CloudantV1 +from ibm_cloud_sdk_core.api_exception import ApiException + +service = CloudantV1.new_instance() +try: + response = service.head_up_information() + + print("Service is up and healthy") +except ApiException as ae: + if ae.code == 503: + print(f"Service is unavailable, status code: {ae.code}") + else: + print(f"Issue performing health check, status code: {ae.code}, message: {ae.http_response.reason}") diff --git a/examples/snippets/postActivityTrackerEvents/example_request.py b/examples/snippets/postActivityTrackerEvents/example_request.py new file mode 100644 index 00000000..8460282f --- /dev/null +++ b/examples/snippets/postActivityTrackerEvents/example_request.py @@ -0,0 +1,13 @@ +# section: code +from ibmcloudant.cloudant_v1 import CloudantV1, ActivityTrackerEvents + +service = CloudantV1.new_instance() + +response = service.post_activity_tracker_events( + types=[ + ActivityTrackerEvents.TypesEnum.MANAGEMENT, + ActivityTrackerEvents.TypesEnum.DATA + ] +).get_result() + +print(response) diff --git a/examples/postAllDocs/example_request.py b/examples/snippets/postAllDocs/example_request.py similarity index 100% rename from examples/postAllDocs/example_request.py rename to examples/snippets/postAllDocs/example_request.py diff --git a/examples/postAllDocs/example_request_as_a_stream.py b/examples/snippets/postAllDocs/example_request_as_a_stream.py similarity index 100% rename from examples/postAllDocs/example_request_as_a_stream.py rename to examples/snippets/postAllDocs/example_request_as_a_stream.py diff --git a/examples/postAllDocsQueries/example_request.py b/examples/snippets/postAllDocsQueries/example_request.py similarity index 84% rename from examples/postAllDocsQueries/example_request.py rename to examples/snippets/postAllDocsQueries/example_request.py index cc333a43..d625e9c9 100644 --- a/examples/postAllDocsQueries/example_request.py +++ b/examples/snippets/postAllDocsQueries/example_request.py @@ -4,7 +4,7 @@ service = CloudantV1.new_instance() all_docs_query1 = AllDocsQuery( - keys=['small-appliances:1000042', 'small-appliances:1000043'] + keys=['1000042', '1000043'] ) all_docs_query2 = AllDocsQuery( diff --git a/examples/postApiKeys/example_request.py b/examples/snippets/postApiKeys/example_request.py similarity index 100% rename from examples/postApiKeys/example_request.py rename to examples/snippets/postApiKeys/example_request.py diff --git a/examples/postBulkDocs/example_request_as_a_stream.py b/examples/snippets/postBulkDocs/example_request_as_a_stream.py similarity index 80% rename from examples/postBulkDocs/example_request_as_a_stream.py rename to examples/snippets/postBulkDocs/example_request_as_a_stream.py index e8891bfc..c3a6d7d3 100644 --- a/examples/postBulkDocs/example_request_as_a_stream.py +++ b/examples/snippets/postBulkDocs/example_request_as_a_stream.py @@ -16,17 +16,17 @@ { "docs": [ { - "_id": "0007241142412418284", + "_id": "ns1HJS13AMkK:0007241142412418284", "type": "event", - "userid": "abc123", + "userId": "abc123", "eventType": "addedToBasket", "productId": "1000042", "date": "2019-01-28T10:44:22.000Z" }, { - "_id": "0007241142412418285", + "_id": "H8tDIwfadxp9:0007241142412418285", "type": "event", - "userid": "abc123", + "userId": "abc234", "eventType": "addedToBasket", "productId": "1000050", "date": "2019-01-25T20:00:00.000Z" diff --git a/examples/postBulkDocs/example_request_create_documents.py b/examples/snippets/postBulkDocs/example_request_create_documents.py similarity index 81% rename from examples/postBulkDocs/example_request_create_documents.py rename to examples/snippets/postBulkDocs/example_request_create_documents.py index 2151bedf..172125e2 100644 --- a/examples/postBulkDocs/example_request_create_documents.py +++ b/examples/snippets/postBulkDocs/example_request_create_documents.py @@ -4,17 +4,17 @@ service = CloudantV1.new_instance() event_doc_1 = Document( - id="0007241142412418284", + _id="ns1HJS13AMkK:0007241142412418284", type="event", - userid="abc123", + userId="abc123", eventType="addedToBasket", productId="1000042", date="2019-01-28T10:44:22.000Z" ) event_doc_2 = Document( - id="0007241142412418285", + _id="H8tDIwfadxp9:0007241142412418285", type="event", - userid="abc234", + userId="abc234", eventType="addedToBasket", productId="1000050", date="2019-01-25T20:00:00.000Z" diff --git a/examples/postBulkDocs/example_request_delete_documents.py b/examples/snippets/postBulkDocs/example_request_delete_documents.py similarity index 61% rename from examples/postBulkDocs/example_request_delete_documents.py rename to examples/snippets/postBulkDocs/example_request_delete_documents.py index bc1a23df..09ffeece 100644 --- a/examples/postBulkDocs/example_request_delete_documents.py +++ b/examples/snippets/postBulkDocs/example_request_delete_documents.py @@ -4,14 +4,14 @@ service = CloudantV1.new_instance() event_doc_1 = Document( - id="0007241142412418284", - rev="1-5005d65514fe9e90f8eccf174af5dd64", - deleted=True, + _id="ns1HJS13AMkK:0007241142412418284", + _rev="1-00000000000000000000000000000000", + _deleted=True, ) event_doc_2 = Document( - id="0007241142412418285", - rev="1-2d7810b054babeda4812b3924428d6d6", - deleted=True, + _id="H8tDIwfadxp9:0007241142412418285", + _rev="1-00000000000000000000000000000000", + _deleted=True, ) bulk_docs = BulkDocs(docs=[event_doc_1, event_doc_2]) diff --git a/examples/postBulkGet/alternative_example_request_for_atts_since.py b/examples/snippets/postBulkGet/alternative_example_request_for_atts_since.py similarity index 83% rename from examples/postBulkGet/alternative_example_request_for_atts_since.py rename to examples/snippets/postBulkGet/alternative_example_request_for_atts_since.py index b9d77083..0811db87 100644 --- a/examples/postBulkGet/alternative_example_request_for_atts_since.py +++ b/examples/snippets/postBulkGet/alternative_example_request_for_atts_since.py @@ -5,7 +5,7 @@ bulk_get_doc = BulkGetQueryDocument( id='order00058', - atts_since=['1-99b02e08da151943c2dcb40090160bb8']) + atts_since=['1-00000000000000000000000000000000']) response = service.post_bulk_get( db='orders', docs=[bulk_get_doc] diff --git a/examples/postBulkGet/alternative_example_request_for_open_revs_all.py b/examples/snippets/postBulkGet/alternative_example_request_for_open_revs_all.py similarity index 100% rename from examples/postBulkGet/alternative_example_request_for_open_revs_all.py rename to examples/snippets/postBulkGet/alternative_example_request_for_open_revs_all.py diff --git a/examples/postBulkGet/example_request.py b/examples/snippets/postBulkGet/example_request.py similarity index 80% rename from examples/postBulkGet/example_request.py rename to examples/snippets/postBulkGet/example_request.py index e161518a..ad88c649 100644 --- a/examples/postBulkGet/example_request.py +++ b/examples/snippets/postBulkGet/example_request.py @@ -6,10 +6,10 @@ doc_id = 'order00067' bulk_get_doc_1 = BulkGetQueryDocument( id=doc_id, - rev='3-917fa2381192822767f010b95b45325b') + rev='3-22222222222222222222222222222222') bulk_get_doc_2 = BulkGetQueryDocument( id=doc_id, - rev='4-a5be949eeb7296747cc271766e9a498b') + rev='4-33333333333333333333333333333333') response = service.post_bulk_get( db='orders', diff --git a/examples/postChanges/example_request.py b/examples/snippets/postChanges/example_request.py similarity index 100% rename from examples/postChanges/example_request.py rename to examples/snippets/postChanges/example_request.py diff --git a/examples/postChanges/example_request_as_a_stream.py b/examples/snippets/postChanges/example_request_as_a_stream.py similarity index 100% rename from examples/postChanges/example_request_as_a_stream.py rename to examples/snippets/postChanges/example_request_as_a_stream.py diff --git a/examples/postDbsInfo/example_request.py b/examples/snippets/postDbsInfo/example_request.py similarity index 100% rename from examples/postDbsInfo/example_request.py rename to examples/snippets/postDbsInfo/example_request.py diff --git a/examples/postDesignDocs/example_request.py b/examples/snippets/postDesignDocs/example_request.py similarity index 90% rename from examples/postDesignDocs/example_request.py rename to examples/snippets/postDesignDocs/example_request.py index 92373308..6fe6331c 100644 --- a/examples/postDesignDocs/example_request.py +++ b/examples/snippets/postDesignDocs/example_request.py @@ -4,7 +4,7 @@ service = CloudantV1.new_instance() response = service.post_design_docs( - attachments=True, + descending=True, db='users' ).get_result() diff --git a/examples/postDesignDocsQueries/example_request.py b/examples/snippets/postDesignDocsQueries/example_request.py similarity index 91% rename from examples/postDesignDocsQueries/example_request.py rename to examples/snippets/postDesignDocsQueries/example_request.py index ce1a089f..c562af36 100644 --- a/examples/postDesignDocsQueries/example_request.py +++ b/examples/snippets/postDesignDocsQueries/example_request.py @@ -10,7 +10,7 @@ ) doc2 = AllDocsQuery( inclusive_end=True, - key='_design/allusers', + start_key='_design/allusers', skip=1 ) diff --git a/examples/postDocument/example_request.py b/examples/snippets/postDocument/example_request.py similarity index 90% rename from examples/postDocument/example_request.py rename to examples/snippets/postDocument/example_request.py index d2eb426c..a18fe7cb 100644 --- a/examples/postDocument/example_request.py +++ b/examples/snippets/postDocument/example_request.py @@ -4,9 +4,9 @@ service = CloudantV1.new_instance() products_doc = Document( - id="small-appliances:1000042", + _id="1000042", type="product", - productid="1000042", + productId="1000042", brand="Salter", name="Digital Kitchen Scales", description="Slim Colourful Design Electronic Cooking Appliance for Home / Kitchen, Weigh up to 5kg + Aquatronic for Liquids ml + fl. oz. 15Yr Guarantee - Green", diff --git a/examples/snippets/postDocument/example_request_as_a_stream.py b/examples/snippets/postDocument/example_request_as_a_stream.py new file mode 100644 index 00000000..965177ba --- /dev/null +++ b/examples/snippets/postDocument/example_request_as_a_stream.py @@ -0,0 +1,13 @@ +# section: code +from ibmcloudant.cloudant_v1 import Document, CloudantV1 + +service = CloudantV1.new_instance() + +with open("products_doc.json", "rb") as products_doc: + response = service.post_document( + db='products', + document=products_doc, + content_type="application/json" + ).get_result() + +print(response) diff --git a/examples/postExplain/example_request.py b/examples/snippets/postExplain/example_request.py similarity index 100% rename from examples/postExplain/example_request.py rename to examples/snippets/postExplain/example_request.py diff --git a/examples/postFind/example_request_for_json_index_type.py b/examples/snippets/postFind/example_request_for_json_index_type.py similarity index 67% rename from examples/postFind/example_request_for_json_index_type.py rename to examples/snippets/postFind/example_request_for_json_index_type.py index b2393326..1e4544e7 100644 --- a/examples/postFind/example_request_for_json_index_type.py +++ b/examples/snippets/postFind/example_request_for_json_index_type.py @@ -12,4 +12,4 @@ ).get_result() print(response) # section: markdown -# This example requires the `getUserByAddress` Cloudant Query "json" index to exist. To create the index, see [Create a new index on a database.](#postindex) +# This example requires the `getUserByEmail` Cloudant Query "json" index to exist. To create the index, see [Create a new index on a database.](#postindex) diff --git a/examples/postFind/example_request_for_text_index_type.py b/examples/snippets/postFind/example_request_for_text_index_type.py similarity index 55% rename from examples/postFind/example_request_for_text_index_type.py rename to examples/snippets/postFind/example_request_for_text_index_type.py index a6f74f9b..c704b7de 100644 --- a/examples/postFind/example_request_for_text_index_type.py +++ b/examples/snippets/postFind/example_request_for_text_index_type.py @@ -5,10 +5,10 @@ response = service.post_find( db='users', - selector={'address': {'$regex': 'Street'}}, + selector={'address': {'$exists': True}}, fields=["_id", "type", "name", "email", "address"], limit=3 ).get_result() print(response) # section: markdown -# This example requires the `getUserByVerifiedEmail` Cloudant Query "text" index to exist. To create the index, see [Create a new index on a database.](#postindex) +# This example requires the `getUserByAddress` Cloudant Query "text" index to exist. To create the index, see [Create a new index on a database.](#postindex) diff --git a/examples/postIndex/example_request_using_json_type_index.py b/examples/snippets/postIndex/example_request_using_json_type_index.py similarity index 100% rename from examples/postIndex/example_request_using_json_type_index.py rename to examples/snippets/postIndex/example_request_using_json_type_index.py diff --git a/examples/postIndex/example_request_using_text_type_index.py b/examples/snippets/postIndex/example_request_using_text_type_index.py similarity index 93% rename from examples/postIndex/example_request_using_text_type_index.py rename to examples/snippets/postIndex/example_request_using_text_type_index.py index 0d085675..6c3e21f8 100644 --- a/examples/postIndex/example_request_using_text_type_index.py +++ b/examples/snippets/postIndex/example_request_using_text_type_index.py @@ -6,7 +6,7 @@ # Type "text" index fields require an object with a name and type properties for the field. index_field = IndexField( name="address", - type="string" + type=IndexField.TypeEnum.STRING ) index = IndexDefinition( fields=[index_field] diff --git a/examples/postPartitionAllDocs/example_request.py b/examples/snippets/postPartitionAllDocs/example_request.py similarity index 78% rename from examples/postPartitionAllDocs/example_request.py rename to examples/snippets/postPartitionAllDocs/example_request.py index a01cc841..4ac774a6 100644 --- a/examples/postPartitionAllDocs/example_request.py +++ b/examples/snippets/postPartitionAllDocs/example_request.py @@ -4,8 +4,8 @@ service = CloudantV1.new_instance() response = service.post_partition_all_docs( - db='products', - partition_key='small-appliances', + db='events', + partition_key='ns1HJS13AMkK', include_docs=True ).get_result() diff --git a/examples/snippets/postPartitionExplain/example_request.py b/examples/snippets/postPartitionExplain/example_request.py new file mode 100644 index 00000000..48b8c477 --- /dev/null +++ b/examples/snippets/postPartitionExplain/example_request.py @@ -0,0 +1,14 @@ +# section: code +from ibmcloudant.cloudant_v1 import CloudantV1 + +service = CloudantV1.new_instance() + +response = service.post_partition_explain( + db='events', + execution_stats=True, + limit=10, + partition_key='ns1HJS13AMkK', + selector={'userId': {'$eq': 'abc123'}} +).get_result() + +print(response) diff --git a/examples/postPartitionFind/example_request.py b/examples/snippets/postPartitionFind/example_request.py similarity index 55% rename from examples/postPartitionFind/example_request.py rename to examples/snippets/postPartitionFind/example_request.py index 74dbab5c..95095789 100644 --- a/examples/postPartitionFind/example_request.py +++ b/examples/snippets/postPartitionFind/example_request.py @@ -4,10 +4,10 @@ service = CloudantV1.new_instance() response = service.post_partition_find( - db='products', - partition_key='small-appliances', - fields=['productid', 'name', 'description'], - selector={'type': {'$eq': 'product'}} + db='events', + partition_key='ns1HJS13AMkK', + fields=['productId', 'eventType', 'date'], + selector={'userId': {'$eq': 'abc123'}} ).get_result() print(response) diff --git a/examples/snippets/postPartitionSearch/example_request.py b/examples/snippets/postPartitionSearch/example_request.py new file mode 100644 index 00000000..d1a1aa1e --- /dev/null +++ b/examples/snippets/postPartitionSearch/example_request.py @@ -0,0 +1,16 @@ +# section: code +from ibmcloudant.cloudant_v1 import CloudantV1 + +service = CloudantV1.new_instance() + +response = service.post_partition_search( + db='events', + partition_key='ns1HJS13AMkK', + ddoc='checkout', + index='findByDate', + query='date:[2019-01-01T12:00:00.000Z TO 2019-01-31T12:00:00.000Z]' +).get_result() + +print(response) +# section: markdown +# 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) diff --git a/examples/snippets/postPartitionView/example_request.py b/examples/snippets/postPartitionView/example_request.py new file mode 100644 index 00000000..5f3c31b4 --- /dev/null +++ b/examples/snippets/postPartitionView/example_request.py @@ -0,0 +1,17 @@ +# section: code +from ibmcloudant.cloudant_v1 import CloudantV1 + +service = CloudantV1.new_instance() + +response = service.post_partition_view( + db='events', + ddoc='checkout', + include_docs=True, + limit=10, + partition_key='ns1HJS13AMkK', + view='byProductId' +).get_result() + +print(response) +# section: markdown +# 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) diff --git a/examples/snippets/postRevsDiff/example_request.py b/examples/snippets/postRevsDiff/example_request.py new file mode 100644 index 00000000..5dd6092f --- /dev/null +++ b/examples/snippets/postRevsDiff/example_request.py @@ -0,0 +1,21 @@ +# section: code +from ibmcloudant.cloudant_v1 import DocumentRevisions, CloudantV1 + +service = CloudantV1.new_instance() + +revs_diff = DocumentRevisions( + order00077=[ + "1-00000000000000000000000000000000", # missing revision + "2-11111111111111111111111111111111", # missing revision + "3-22222222222222222222222222222222" # possible ancestor revision +] +) + +response = service.post_revs_diff( + db='orders', + revs_diff_request=revs_diff.to_dict() +).get_result() + +print(response) +// section: markdown +// This example requires the example revisions in the POST body to be replaced with valid revisions. diff --git a/examples/postSearch/example_request.py b/examples/snippets/postSearch/example_request.py similarity index 100% rename from examples/postSearch/example_request.py rename to examples/snippets/postSearch/example_request.py diff --git a/examples/postSearchAnalyze/example_request.py b/examples/snippets/postSearchAnalyze/example_request.py similarity index 100% rename from examples/postSearchAnalyze/example_request.py rename to examples/snippets/postSearchAnalyze/example_request.py diff --git a/examples/postView/example_request.py b/examples/snippets/postView/example_request.py similarity index 100% rename from examples/postView/example_request.py rename to examples/snippets/postView/example_request.py diff --git a/examples/postViewQueries/example_request.py b/examples/snippets/postViewQueries/example_request.py similarity index 100% rename from examples/postViewQueries/example_request.py rename to examples/snippets/postViewQueries/example_request.py diff --git a/examples/putAttachment/example_request.py b/examples/snippets/putAttachment/example_request.py similarity index 90% rename from examples/putAttachment/example_request.py rename to examples/snippets/putAttachment/example_request.py index 6ddaed4f..2e6edf80 100644 --- a/examples/putAttachment/example_request.py +++ b/examples/snippets/putAttachment/example_request.py @@ -6,7 +6,7 @@ detailed_description = "This appliance includes..." response = service.put_attachment( db='products', - doc_id='small-appliances:100001', + doc_id='1000042', attachment_name='product_details.txt', attachment=detailed_description, content_type='text/plain' diff --git a/examples/putCapacityThroughputConfiguration/example_request.py b/examples/snippets/putCapacityThroughputConfiguration/example_request.py similarity index 100% rename from examples/putCapacityThroughputConfiguration/example_request.py rename to examples/snippets/putCapacityThroughputConfiguration/example_request.py diff --git a/examples/putCloudantSecurity/example_request.py b/examples/snippets/putCloudantSecurity/example_request.py similarity index 100% rename from examples/putCloudantSecurity/example_request.py rename to examples/snippets/putCloudantSecurity/example_request.py diff --git a/examples/putCorsConfiguration/example_request.py b/examples/snippets/putCorsConfiguration/example_request.py similarity index 100% rename from examples/putCorsConfiguration/example_request.py rename to examples/snippets/putCorsConfiguration/example_request.py diff --git a/examples/postGeoCleanup/example_request.py b/examples/snippets/putDatabase/example_request.py similarity index 60% rename from examples/postGeoCleanup/example_request.py rename to examples/snippets/putDatabase/example_request.py index 6f067877..fa6b8b4f 100644 --- a/examples/postGeoCleanup/example_request.py +++ b/examples/snippets/putDatabase/example_request.py @@ -3,8 +3,6 @@ service = CloudantV1.new_instance() -response = service.post_geo_cleanup( - db='stores' -).get_result() +response = service.put_database(db='events', partitioned=True).get_result() print(response) diff --git a/examples/putDesignDocument/example_request.py b/examples/snippets/putDesignDocument/example_request.py similarity index 67% rename from examples/putDesignDocument/example_request.py rename to examples/snippets/putDesignDocument/example_request.py index 37d5ca5b..135dcbbc 100644 --- a/examples/putDesignDocument/example_request.py +++ b/examples/snippets/putDesignDocument/example_request.py @@ -4,11 +4,11 @@ service = CloudantV1.new_instance() email_view_map_reduce = DesignDocumentViewsMapReduce( - map='function(doc) { if(doc.email_verified === true){\n emit(doc.email, [doc.name, doc.email_verified, doc.joined]) }}' + map='function(doc) { if(doc.email_verified === true) { emit(doc.email, [doc.name, doc.email_verified, doc.joined]); }}' ) user_index = SearchIndexDefinition( - index='function (doc) { index("name", doc.name); index("active", doc.active); }', + index='function(doc) { index("name", doc.name); index("active", doc.active); }', analyzer=AnalyzerConfiguration(name="standard", fields={"email": Analyzer(name="email")})) design_document = DesignDocument( @@ -27,11 +27,11 @@ # Partitioned DesignDocument Example product_map = DesignDocumentViewsMapReduce( - map='function(doc) { emit(doc.productId, [doc.brand, doc.name, doc.description]) }' + map='function(doc) { emit(doc.productId, [doc.date, doc.eventType, doc.userId]); }' ) -price_index = SearchIndexDefinition( - index='function (doc) { index("price", doc.price);}', +date_index = SearchIndexDefinition( + index='function(doc) { index("date", doc.date); }', analyzer=AnalyzerConfiguration(name="classic", fields={"description": Analyzer(name="english")}) ) @@ -40,17 +40,17 @@ ) partitioned_design_doc = DesignDocument( - views={'byApplianceProdId': product_map}, - indexes={'findByPrice': price_index}, + views={'byProductId': product_map}, + indexes={'findByDate': date_index}, options=design_document_options ) response = service.put_design_document( - db='products', + db='events', design_document=partitioned_design_doc, - ddoc='appliances' + ddoc='checkout' ).get_result() print(response) # section: markdown -# This example creates `allusers` design document in the `users` database and `appliances` design document in the partitioned `products` database. +# This example creates `allusers` design document in the `users` database and `checkout` design document in the partitioned `events` database. diff --git a/examples/putDocument/example_request.py b/examples/snippets/putDocument/example_request.py similarity index 84% rename from examples/putDocument/example_request.py rename to examples/snippets/putDocument/example_request.py index 6b442d9f..98a384bc 100644 --- a/examples/putDocument/example_request.py +++ b/examples/snippets/putDocument/example_request.py @@ -5,14 +5,14 @@ event_doc = Document( type='event', - userid='abc123', + userId='abc123', eventType='addedToBasket', productId='1000042', date='2019-01-28T10:44:22.000Z' ) response = service.put_document( db='events', - doc_id='0007241142412418284', + doc_id='ns1HJS13AMkK:0007241142412418284', document=event_doc ).get_result() diff --git a/examples/putLocalDocument/example_request.py b/examples/snippets/putLocalDocument/example_request.py similarity index 84% rename from examples/putLocalDocument/example_request.py rename to examples/snippets/putLocalDocument/example_request.py index 412e4912..cf0fb203 100644 --- a/examples/putLocalDocument/example_request.py +++ b/examples/snippets/putLocalDocument/example_request.py @@ -6,13 +6,13 @@ local_document = Document( type='order', user='Bob Smith', - orderid='0007741142412418284', - userid='abc123', + orderId='0007741142412418284', + userId='abc123', total=214.98, deliveryAddress='19 Front Street, Darlington, DL5 1TY', delivered='true', courier='UPS', - courierid='15125425151261289', + courierId='15125425151261289', date='2019-01-28T10:44:22.000Z' ) diff --git a/examples/putReplicationDocument/example_request.py b/examples/snippets/putReplicationDocument/example_request.py similarity index 70% rename from examples/putReplicationDocument/example_request.py rename to examples/snippets/putReplicationDocument/example_request.py index 1a06e3b6..0c2d92dc 100644 --- a/examples/putReplicationDocument/example_request.py +++ b/examples/snippets/putReplicationDocument/example_request.py @@ -4,22 +4,22 @@ service = CloudantV1.new_instance() source_db = ReplicationDatabase( - url='https://examples.cloudant.com/animaldb' + url='https://~replace-with-source-host~.cloudantnosqldb.appdomain.cloud/animaldb' ) target_auth_iam = ReplicationDatabaseAuthIam( - api_key='' + api_key='a1b2c3d4e5f6f1g4h7j3k6l9m2p5q8s1t4v7x0z3' #use your own IAM API key ) target_auth = ReplicationDatabaseAuth( iam=target_auth_iam ) target_db = ReplicationDatabase( auth=target_auth, - url='/animaldb-target' + url='https://~replace-with-target-host~.cloudantnosqldb.appdomain.cloud/animaldb-target' ) replication_document = ReplicationDocument( - id='repldoc-example', + _id='repldoc-example', create_target=True, source=source_db, target=target_db diff --git a/examples/putSecurity/example_request.py b/examples/snippets/putSecurity/example_request.py similarity index 100% rename from examples/putSecurity/example_request.py rename to examples/snippets/putSecurity/example_request.py diff --git a/ibmcloudant/__init__.py b/ibmcloudant/__init__.py index c212bfa1..c385b4f3 100644 --- a/ibmcloudant/__init__.py +++ b/ibmcloudant/__init__.py @@ -1,5 +1,5 @@ # coding: utf-8 -# © Copyright IBM Corporation 2020, 2021. +# © Copyright IBM Corporation 2020, 2025. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -19,17 +19,10 @@ from ibm_cloud_sdk_core import IAMTokenManager, DetailedResponse, BaseService, ApiException, get_authenticator from .couchdb_session_authenticator import CouchDbSessionAuthenticator from .couchdb_session_get_authenticator_patch import new_construct_authenticator -from .cloudant_base_service import new_init, new_prepare_request, new_set_service_url, new_set_default_headers from .couchdb_session_token_manager import CouchDbSessionTokenManager from .cloudant_v1 import CloudantV1 +from .features.changes_follower import ChangesFollower +from .features.pagination import Pager, PagerType, Pagination # sdk-core's __construct_authenticator works with a long switch-case so monkey-patching is required get_authenticator.__construct_authenticator = new_construct_authenticator - -CloudantV1.__init__ = new_init - -CloudantV1.set_service_url = new_set_service_url - -CloudantV1.set_default_headers = new_set_default_headers - -CloudantV1.prepare_request = new_prepare_request diff --git a/ibmcloudant/cloudant_base_service.py b/ibmcloudant/cloudant_base_service.py index 4c73073f..3884671b 100644 --- a/ibmcloudant/cloudant_base_service.py +++ b/ibmcloudant/cloudant_base_service.py @@ -1,6 +1,6 @@ # coding: utf-8 -# © Copyright IBM Corporation 2020, 2021. +# © Copyright IBM Corporation 2020, 2025. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -15,16 +15,20 @@ # limitations under the License. """ Module to patch sdk core base service for session authentication +and other helpful features. """ from collections import namedtuple from typing import Dict, Optional, Union, Tuple, List from urllib.parse import urlsplit, unquote +from json import dumps +from json.decoder import JSONDecodeError +from io import BytesIO +from ibm_cloud_sdk_core import BaseService from ibm_cloud_sdk_core.authenticators import Authenticator -from requests.cookies import RequestsCookieJar +from requests import Response, Session from .common import get_sdk_headers -from .cloudant_v1 import CloudantV1 from .couchdb_session_authenticator import CouchDbSessionAuthenticator # pylint: disable=missing-docstring @@ -43,6 +47,9 @@ def __hash__(self): doc_id_rule = ValidationRule(path_segment_index=1, error_parameter_name='Document ID', operation_ids= [ 'delete_document', 'get_document', + 'get_document_as_mixed', + 'get_document_as_related', + 'get_document_as_stream', 'head_document', 'put_document', 'delete_attachment', @@ -64,96 +71,169 @@ def __hash__(self): # Since Py3.6 dict is ordered so use a key only dict for our set rules_by_operation.setdefault(operation_id, dict()).setdefault(rule) +class CloudantBaseService(BaseService): + """ + The base class for service classes. + """ + def __init__( + self, + service_url: str = None, + authenticator: Authenticator = None, + ) -> None: + """ + Construct a new client for the Cloudant service. + + :param Authenticator authenticator: The authenticator specifies the authentication mechanism. + Get up to date information from https://github.com/IBM/python-sdk-core/blob/main/README.md + about initializing the authenticator of your choice. + """ + BaseService.__init__(self, service_url=service_url, authenticator=authenticator) + # Overwrite default read timeout to 2.5 minutes + if not ('timeout' in self.http_config): + new_http_config = self.http_config.copy() + new_http_config['timeout'] = (CONNECT_TIMEOUT, READ_TIMEOUT) + self.set_http_config(new_http_config) + # Custom actions for CouchDbSessionAuthenticator + if isinstance(authenticator, CouchDbSessionAuthenticator): + # Make token manager of CouchDbSessionAuthenticator to use the same http client as main service + self.authenticator._set_http_client(self.get_http_client(), self.jar) + add_hooks(self) + + def set_service_url(self, service_url: str): + super().set_service_url(service_url) + try: + if isinstance(self.authenticator, CouchDbSessionAuthenticator): + self.authenticator.token_manager.set_service_url(service_url) + except AttributeError: + pass # in case no authenticator is configured yet, pass + + def set_default_headers(self, headers: Dict[str, str]): + super().set_default_headers(headers) + if isinstance(self.authenticator, CouchDbSessionAuthenticator): + combined_headers = {} + combined_headers.update(headers) + combined_headers.update(get_sdk_headers( + service_name=self.DEFAULT_SERVICE_NAME, + service_version='V1', + operation_id='authenticator_post_session') + ) + self.authenticator.token_manager.set_default_headers(combined_headers) + + def set_disable_ssl_verification(self, status: bool = False) -> None: + super().set_disable_ssl_verification(status) + if isinstance(self.authenticator, CouchDbSessionAuthenticator): + self.authenticator.token_manager.set_disable_ssl_verification(status) -old_init = CloudantV1.__init__ - - -def new_init(self, authenticator: Authenticator = None): - old_init(self, authenticator) - # Overwrite default read timeout to 2.5 minutes - if not ('timeout' in self.http_config): - new_http_config = self.http_config.copy() - new_http_config['timeout'] = (CONNECT_TIMEOUT, READ_TIMEOUT) - self.set_http_config(new_http_config) - # Custom actions for CouchDbSessionAuthenticator - if isinstance(authenticator, CouchDbSessionAuthenticator): - # Replacing BaseService's http.cookiejar.CookieJar as RequestsCookieJar supports update(CookieJar) - self.jar = RequestsCookieJar(self.jar) - self.authenticator.set_jar(self.jar) # Authenticators don't have access to cookie jars by default - - -old_set_service_url = CloudantV1.set_service_url - - -def new_set_service_url(self, service_url: str): - old_set_service_url(self, service_url) - try: + def set_http_client(self, http_client: Session) -> None: + super().set_http_client(http_client) if isinstance(self.authenticator, CouchDbSessionAuthenticator): - self.authenticator.token_manager.set_service_url(service_url) - except AttributeError: - pass # in case no authenticator is configured yet, pass - - -old_set_default_headers = CloudantV1.set_default_headers - - -def new_set_default_headers(self, headers: Dict[str, str]): - old_set_default_headers(self, headers) - if isinstance(self.authenticator, CouchDbSessionAuthenticator): - combined_headers = {} - combined_headers.update(headers) - combined_headers.update(get_sdk_headers( - service_name=self.DEFAULT_SERVICE_NAME, - service_version='V1', - operation_id='authenticator_post_session') - ) - self.authenticator.token_manager.set_default_headers(combined_headers) - - -old_set_disable_ssl_verification = CloudantV1.set_disable_ssl_verification - - -def new_set_disable_ssl_verification(self, status: bool = False) -> None: - old_set_disable_ssl_verification(self, status) - if isinstance(self.authenticator, CouchDbSessionAuthenticator): - self.authenticator.token_manager.set_disable_ssl_verification(status) - - -old_prepare_request = CloudantV1.prepare_request - - -def new_prepare_request(self, - method: str, - url: str, - *args, - headers: Optional[dict] = None, - params: Optional[dict] = None, - data: Optional[Union[str, dict]] = None, - files: Optional[Union[Dict[str, Tuple[str]], - List[Tuple[str, - Tuple[str, - ...]]]]] = None, - **kwargs) -> dict: - # Extract the operation ID from the request headers. - operation_id = None - header = headers.get('X-IBMCloud-SDK-Analytics') - if header is not None: - for element in header.split(';'): - if element.startswith('operation_id'): - operation_id = element.split('=')[1] - break - if operation_id is not None: - # Check each validation rule that applies to the operation. - # Until the request URL is passed to old_prepare_request it does not include the - # service URL and is relative to it - request_url_path_segments = urlsplit(url).path.strip('/').split('/') - if len(request_url_path_segments) == 1 and request_url_path_segments[0] == '': - request_url_path_segments = [] - # Note the get returns a value-less dict, we are iterating only the keys - for rule in rules_by_operation.get(operation_id, {}): - if len(request_url_path_segments) > rule.path_segment_index: - segment_to_validate = request_url_path_segments[rule.path_segment_index] - if segment_to_validate.startswith('_'): - raise ValueError('{0} {1} starts with the invalid _ character.'.format(rule.error_parameter_name, - unquote(segment_to_validate))) - return old_prepare_request(self, method, url, *args, headers=headers, params=params, data=data, files=files, **kwargs) + self.authenticator._set_http_client(self.get_http_client(), self.jar) + add_hooks(self) + + def prepare_request(self, + method: str, + url: str, + *args, + headers: Optional[dict] = None, + params: Optional[dict] = None, + data: Optional[Union[str, dict]] = None, + files: Optional[Union[Dict[str, Tuple[str]], + List[Tuple[str, + Tuple[str, + ...]]]]] = None, + **kwargs) -> dict: + # Extract the operation ID from the request headers. + operation_id = None + header = headers.get('X-IBMCloud-SDK-Analytics') + if header is not None: + for element in header.split(';'): + if element.startswith('operation_id'): + operation_id = element.split('=')[1] + break + if operation_id is not None: + # Check each validation rule that applies to the operation. + # Until the request URL is passed to old_prepare_request it does not include the + # service URL and is relative to it + request_url_path_segments = urlsplit(url).path.strip('/').split('/') + if len(request_url_path_segments) == 1 and request_url_path_segments[0] == '': + request_url_path_segments = [] + # Note the get returns a value-less dict, we are iterating only the keys + for rule in rules_by_operation.get(operation_id, {}): + if len(request_url_path_segments) > rule.path_segment_index: + segment_to_validate = request_url_path_segments[rule.path_segment_index] + if segment_to_validate.startswith('_'): + raise ValueError('{0} {1} starts with the invalid _ character.'.format(rule.error_parameter_name, + unquote(segment_to_validate))) + return super().prepare_request(method, url, *args, headers=headers, params=params, data=data, files=files, **kwargs) + +def _error_response_hook(response:Response, *args, **kwargs) -> Optional[Response]: + # pylint: disable=W0613 + # unused args and kwargs required by requests event hook interface + """Function for augmenting error responses. + Converts the Cloudant response to better match the + standard error response formats including adding a + trace ID and appending the Cloudant/CouchDB error + reason to the message. + + Follows the requests event hook pattern. + + :param response: the requests Response object + :type response: Response + + :return: A new response object, defaults to the existing response + :rtype: Response,optional + """ + # Only hook into error responses + # Ignore HEAD request responses because there is no body to read + if not response.ok and response.request.method != 'HEAD': + content_type = response.headers.get('content-type') + # If it isn't JSON don't mess with it! + if content_type is not None and content_type.startswith('application/json'): + try: + error_json: dict = response.json() + # Only augment if there isn't a trace or errors already + send_augmented_response = False + if 'trace' not in error_json: + if 'errors' not in error_json: + error = error_json.get('error') + reason = error_json.get('reason') + if error is not None: + error_model: dict = {'code': error, 'message': f'{error}'} + if reason: + error_model['message'] += f': {reason}' + error_json['errors'] = [error_model] + send_augmented_response = True + if 'errors' in error_json: + # Get the x-request-id header if available + # otherwise try the x-couch-request-id header + trace = response.headers.get('x-request-id', + response.headers.get('x-couch-request-id')) + if trace is not None: + # Augment trace if there was a value + error_json['trace'] = trace + send_augmented_response = True + if send_augmented_response: + # It'd be nice to just change content on response, but it's internal. + # Instead copy the named attributes to a new Response and then set + # the encoding and bytes of the modified error body. + error_response = Response() + error_response.status_code = response.status_code + error_response.headers = response.headers + error_response.url = response.url + error_response.history = response.history + error_response.reason = response.reason + error_response.cookies = response.cookies + error_response.elapsed = response.elapsed + error_response.request = response.request + error_response.encoding = 'utf-8' + error_response.raw = BytesIO(dumps(error_json).encode('utf-8')) + return error_response + except JSONDecodeError: + # If we couldn't read the JSON we just return the response as-is + # so the exception can surface elsewhere. + pass + return response +def add_hooks(self): + response_hooks = self.get_http_client().hooks['response'] + if _error_response_hook not in response_hooks: + response_hooks.append(_error_response_hook) diff --git a/ibmcloudant/cloudant_v1.py b/ibmcloudant/cloudant_v1.py index e6a86258..aeb6ecf8 100644 --- a/ibmcloudant/cloudant_v1.py +++ b/ibmcloudant/cloudant_v1.py @@ -1,6 +1,6 @@ # coding: utf-8 -# (C) Copyright IBM Corp. 2022. +# (C) Copyright IBM Corp. 2026. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -17,36 +17,40 @@ """ NoSQL database based on Apache CouchDB -See: https://cloud.ibm.com/docs/services/Cloudant/ +See: https://cloud.ibm.com/docs/Cloudant """ from datetime import datetime from enum import Enum -from typing import BinaryIO, Dict, List, Union +from typing import BinaryIO, Dict, List, Optional, Union import base64 import json +import logging -from ibm_cloud_sdk_core import BaseService, DetailedResponse +from ibm_cloud_sdk_core import DetailedResponse from ibm_cloud_sdk_core.authenticators.authenticator import Authenticator from ibm_cloud_sdk_core.get_authenticator import get_authenticator_from_environment from ibm_cloud_sdk_core.utils import convert_list, convert_model, datetime_to_string, string_to_datetime +from .cloudant_base_service import CloudantBaseService from .common import get_sdk_headers ############################################################################## # Service ############################################################################## -class CloudantV1(BaseService): + +class CloudantV1(CloudantBaseService): """The Cloudant V1 service.""" - DEFAULT_SERVICE_URL = 'http://localhost:5984' + DEFAULT_SERVICE_URL = 'https://~replace-with-cloudant-host~.cloudantnosqldb.appdomain.cloud' DEFAULT_SERVICE_NAME = 'cloudant' @classmethod - def new_instance(cls, - service_name: str = DEFAULT_SERVICE_NAME, - ) -> 'CloudantV1': + def new_instance( + cls, + service_name: str = DEFAULT_SERVICE_NAME, + ) -> 'CloudantV1': """ Return a new client for the Cloudant service using the specified parameters and external configuration. @@ -58,9 +62,10 @@ def new_instance(cls, service.configure_service(service_name) return service - def __init__(self, - authenticator: Authenticator = None, - ) -> None: + def __init__( + self, + authenticator: Authenticator = None, + ) -> None: """ Construct a new client for the Cloudant service. @@ -68,20 +73,17 @@ def __init__(self, Get up to date information from https://github.com/IBM/python-sdk-core/blob/main/README.md about initializing the authenticator of your choice. """ - BaseService.__init__(self, - service_url=self.DEFAULT_SERVICE_URL, - authenticator=authenticator) + CloudantBaseService.__init__(self, service_url=self.DEFAULT_SERVICE_URL, authenticator=authenticator) # enable gzip compression of request bodies self.set_enable_gzip_compression(True) - ######################### # Server ######################### - - def get_server_information(self, - **kwargs + def get_server_information( + self, + **kwargs, ) -> DetailedResponse: """ Retrieve server instance information. @@ -90,6 +92,7 @@ def get_server_information(self, about the instance. The response includes a JSON structure that contains information about the server, including a welcome message and the server's version. + **Tip:** The authentication for this endpoint is only enforced when using IAM. :param dict headers: A `dict` containing the request headers :return: A `DetailedResponse` containing the result, headers and HTTP status code. @@ -97,9 +100,11 @@ def get_server_information(self, """ headers = {} - sdk_headers = get_sdk_headers(service_name=self.DEFAULT_SERVICE_NAME, - service_version='V1', - operation_id='get_server_information') + sdk_headers = get_sdk_headers( + service_name=self.DEFAULT_SERVICE_NAME, + service_version='V1', + operation_id='get_server_information', + ) headers.update(sdk_headers) if 'headers' in kwargs: @@ -108,33 +113,36 @@ def get_server_information(self, headers['Accept'] = 'application/json' url = '/' - request = self.prepare_request(method='GET', - url=url, - headers=headers) + request = self.prepare_request( + method='GET', + url=url, + headers=headers, + ) response = self.send(request, **kwargs) return response - - def get_membership_information(self, - **kwargs + def get_capacity_throughput_information( + self, + **kwargs, ) -> DetailedResponse: """ - Retrieve cluster membership information. + Retrieve provisioned throughput capacity information. - Displays the nodes that are part of the cluster as `cluster_nodes`. The field, - `all_nodes`, displays all nodes this node knows about, including the ones that are - part of the cluster. This endpoint is useful when you set up a cluster. + View the amount of provisioned throughput capacity that is allocated to an IBM + Cloudant instance and what is the target provisioned throughput capacity. :param dict headers: A `dict` containing the request headers :return: A `DetailedResponse` containing the result, headers and HTTP status code. - :rtype: DetailedResponse with `dict` result representing a `MembershipInformation` object + :rtype: DetailedResponse with `dict` result representing a `CapacityThroughputInformation` object """ headers = {} - sdk_headers = get_sdk_headers(service_name=self.DEFAULT_SERVICE_NAME, - service_version='V1', - operation_id='get_membership_information') + sdk_headers = get_sdk_headers( + service_name=self.DEFAULT_SERVICE_NAME, + service_version='V1', + operation_id='get_capacity_throughput_information', + ) headers.update(sdk_headers) if 'headers' in kwargs: @@ -142,77 +150,52 @@ def get_membership_information(self, del kwargs['headers'] headers['Accept'] = 'application/json' - url = '/_membership' - request = self.prepare_request(method='GET', - url=url, - headers=headers) + url = '/_api/v2/user/capacity/throughput' + request = self.prepare_request( + method='GET', + url=url, + headers=headers, + ) response = self.send(request, **kwargs) return response - - def get_uuids(self, - *, - count: int = None, - **kwargs + def put_capacity_throughput_configuration( + self, + blocks: int, + **kwargs, ) -> DetailedResponse: """ - Retrieve one or more UUIDs. + Update the target provisioned throughput capacity. - Requests one or more Universally Unique Identifiers (UUIDs) from the instance. The - response is a JSON object that provides a list of UUIDs. + Sets the target provisioned throughput capacity for an IBM Cloudant instance. When + target capacity is changed, the current capacity asynchronously changes to meet + the target capacity. - :param int count: (optional) Query parameter to specify the number of UUIDs - to return. + :param int blocks: A number of blocks of throughput units. A block consists + of 100 reads/sec, 50 writes/sec, and 5 global queries/sec of provisioned + throughput capacity. Not available for some plans. :param dict headers: A `dict` containing the request headers :return: A `DetailedResponse` containing the result, headers and HTTP status code. - :rtype: DetailedResponse with `dict` result representing a `UuidsResult` object + :rtype: DetailedResponse with `dict` result representing a `CapacityThroughputInformation` object """ + if blocks is None: + raise ValueError('blocks must be provided') headers = {} - sdk_headers = get_sdk_headers(service_name=self.DEFAULT_SERVICE_NAME, - service_version='V1', - operation_id='get_uuids') + sdk_headers = get_sdk_headers( + service_name=self.DEFAULT_SERVICE_NAME, + service_version='V1', + operation_id='put_capacity_throughput_configuration', + ) headers.update(sdk_headers) - params = { - 'count': count + data = { + 'blocks': blocks, } - - if 'headers' in kwargs: - headers.update(kwargs.get('headers')) - del kwargs['headers'] - headers['Accept'] = 'application/json' - - url = '/_uuids' - request = self.prepare_request(method='GET', - url=url, - headers=headers, - params=params) - - response = self.send(request, **kwargs) - return response - - - def get_capacity_throughput_information(self, - **kwargs - ) -> DetailedResponse: - """ - Retrieve provisioned throughput capacity information. - - View the amount of provisioned throughput capacity that is allocated to an IBM - Cloudant instance and what is the target provisioned throughput capacity. - - :param dict headers: A `dict` containing the request headers - :return: A `DetailedResponse` containing the result, headers and HTTP status code. - :rtype: DetailedResponse with `dict` result representing a `CapacityThroughputInformation` object - """ - - headers = {} - sdk_headers = get_sdk_headers(service_name=self.DEFAULT_SERVICE_NAME, - service_version='V1', - operation_id='get_capacity_throughput_information') - headers.update(sdk_headers) + data = {k: v for (k, v) in data.items() if v is not None} + data = json.dumps(data) + headers['content-type'] = 'application/json' if 'headers' in kwargs: headers.update(kwargs.get('headers')) @@ -220,58 +203,60 @@ def get_capacity_throughput_information(self, headers['Accept'] = 'application/json' url = '/_api/v2/user/capacity/throughput' - request = self.prepare_request(method='GET', - url=url, - headers=headers) + request = self.prepare_request( + method='PUT', + url=url, + headers=headers, + data=data, + ) response = self.send(request, **kwargs) return response - - def put_capacity_throughput_configuration(self, - blocks: int, - **kwargs + def get_uuids( + self, + *, + count: Optional[int] = None, + **kwargs, ) -> DetailedResponse: """ - Update the target provisioned throughput capacity. + Retrieve one or more UUIDs. - Sets the target provisioned throughput capacity for an IBM Cloudant instance. When - target capacity is changed, the current capacity asynchronously changes to meet - the target capacity. + Requests one or more Universally Unique Identifiers (UUIDs) from the instance. The + response is a JSON object that provides a list of UUIDs. + **Tip:** The authentication for this endpoint is only enforced when using IAM. - :param int blocks: A number of blocks of throughput units. A block consists - of 100 reads/sec, 50 writes/sec, and 5 global queries/sec of provisioned - throughput capacity. + :param int count: (optional) Query parameter to specify the number of UUIDs + to return. :param dict headers: A `dict` containing the request headers :return: A `DetailedResponse` containing the result, headers and HTTP status code. - :rtype: DetailedResponse with `dict` result representing a `CapacityThroughputInformation` object + :rtype: DetailedResponse with `dict` result representing a `UuidsResult` object """ - if blocks is None: - raise ValueError('blocks must be provided') headers = {} - sdk_headers = get_sdk_headers(service_name=self.DEFAULT_SERVICE_NAME, - service_version='V1', - operation_id='put_capacity_throughput_configuration') + sdk_headers = get_sdk_headers( + service_name=self.DEFAULT_SERVICE_NAME, + service_version='V1', + operation_id='get_uuids', + ) headers.update(sdk_headers) - data = { - 'blocks': blocks + params = { + 'count': count, } - data = {k: v for (k, v) in data.items() if v is not None} - data = json.dumps(data) - headers['content-type'] = 'application/json' if 'headers' in kwargs: headers.update(kwargs.get('headers')) del kwargs['headers'] headers['Accept'] = 'application/json' - url = '/_api/v2/user/capacity/throughput' - request = self.prepare_request(method='PUT', - url=url, - headers=headers, - data=data) + url = '/_uuids' + request = self.prepare_request( + method='GET', + url=url, + headers=headers, + params=params, + ) response = self.send(request, **kwargs) return response @@ -280,33 +265,47 @@ def put_capacity_throughput_configuration(self, # Databases ######################### - - def get_db_updates(self, + def get_db_updates( + self, *, - feed: str = None, - heartbeat: int = None, - timeout: int = None, - since: str = None, - **kwargs + descending: Optional[bool] = None, + feed: Optional[str] = None, + heartbeat: Optional[int] = None, + limit: Optional[int] = None, + timeout: Optional[int] = None, + since: Optional[str] = None, + **kwargs, ) -> DetailedResponse: """ Retrieve change events for all databases. + **This endpoint is not available in IBM Cloudant.** Lists changes to databases, like a global changes feed. Types of changes include updating the database and creating or deleting a database. Like the changes feed, the feed is not guaranteed to return changes in the correct order and might repeat changes. Polling modes for this method work like polling modes for the changes feed. - **Note: This endpoint requires _admin or _db_updates role and is only available on - dedicated clusters.**. + :param bool descending: (optional) Query parameter to specify whether to + return rows in descending by key order. :param str feed: (optional) Query parameter to specify the changes feed type. :param int heartbeat: (optional) Query parameter to specify the period in - milliseconds after which an empty line is sent in the results. Only - applicable for longpoll, continuous, and eventsource feeds. Overrides any - timeout to keep the feed alive indefinitely. May also be `true` to use - default value of 60000. + milliseconds after which an empty line is sent in the results. Off by + default and only applicable for `continuous` and `eventsource` feeds. + Overrides any timeout to keep the feed alive indefinitely. May also be + `true` to use a value of `60000`. + **Note:** Delivery of heartbeats cannot be relied on at specific intervals. + If your application runs in an environment where idle network connections + may break, `heartbeat` is not suitable as a keepalive mechanism. Instead, + consider one of the following options: + * Use the `timeout` parameter with a value that is compatible with + your network environment. + * Switch to scheduled usage of one of the non-continuous changes feed + types (`normal` or `longpoll`). + * Use TCP keepalive. + :param int limit: (optional) Query parameter to specify the number of + returned documents to limit the result to. :param int timeout: (optional) Query parameter to specify the maximum period in milliseconds to wait for a change before the response is sent, even if there are no results. Only applicable for `longpoll` or @@ -314,24 +313,36 @@ def get_db_updates(self, configuration option. Note that `60000` value is also the default maximum timeout to prevent undetected dead connections. :param str since: (optional) Query parameter to specify to start the - results from the change immediately after the given update sequence. Can be - a valid update sequence or `now` value. Default is `0` i.e. all changes. + results from the change immediately after the given update sequence. + Possible values are: + * `0` for all available changes (default). + * `now` for future changes. + * A valid update sequence, for example, from the `seq` value of a + change received before. :param dict headers: A `dict` containing the request headers :return: A `DetailedResponse` containing the result, headers and HTTP status code. :rtype: DetailedResponse with `dict` result representing a `DbUpdates` object + + Deprecated: this method is deprecated and may be removed in a future release. """ + logging.warning('A deprecated operation has been invoked: get_db_updates') + headers = {} - sdk_headers = get_sdk_headers(service_name=self.DEFAULT_SERVICE_NAME, - service_version='V1', - operation_id='get_db_updates') + sdk_headers = get_sdk_headers( + service_name=self.DEFAULT_SERVICE_NAME, + service_version='V1', + operation_id='get_db_updates', + ) headers.update(sdk_headers) params = { + 'descending': descending, 'feed': feed, 'heartbeat': heartbeat, + 'limit': limit, 'timeout': timeout, - 'since': since + 'since': since, } if 'headers' in kwargs: @@ -340,37 +351,39 @@ def get_db_updates(self, headers['Accept'] = 'application/json' url = '/_db_updates' - request = self.prepare_request(method='GET', - url=url, - headers=headers, - params=params) + request = self.prepare_request( + method='GET', + url=url, + headers=headers, + params=params, + ) response = self.send(request, **kwargs) return response - - def post_changes(self, + def post_changes( + self, db: str, *, - doc_ids: List[str] = None, - fields: List[str] = None, - selector: dict = None, - last_event_id: str = None, - att_encoding_info: bool = None, - attachments: bool = None, - conflicts: bool = None, - descending: bool = None, - feed: str = None, - filter: str = None, - heartbeat: int = None, - include_docs: bool = None, - limit: int = None, - seq_interval: int = None, - since: str = None, - style: str = None, - timeout: int = None, - view: str = None, - **kwargs + doc_ids: Optional[List[str]] = None, + fields: Optional[List[str]] = None, + selector: Optional[dict] = None, + last_event_id: Optional[str] = None, + att_encoding_info: Optional[bool] = None, + attachments: Optional[bool] = None, + conflicts: Optional[bool] = None, + descending: Optional[bool] = None, + feed: Optional[str] = None, + filter: Optional[str] = None, + heartbeat: Optional[int] = None, + include_docs: Optional[bool] = None, + limit: Optional[int] = None, + seq_interval: Optional[int] = None, + since: Optional[str] = None, + style: Optional[str] = None, + timeout: Optional[int] = None, + view: Optional[str] = None, + **kwargs, ) -> DetailedResponse: """ Query the database document changes feed. @@ -379,15 +392,19 @@ def post_changes(self, It is widely used with the `filter` query parameter because it allows one to pass more information to the filter. ### Note - Before using the changes feed we recommend reading the + Before using the changes feed read the [FAQs](https://cloud.ibm.com/docs/Cloudant?topic=Cloudant-faq-using-changes-feed) - to understand the limitations and appropriate use cases.". + to understand the limitations and appropriate use cases. + If you need to pass parameters to dynamically change the filtered content use the + `_selector` filter type for better performance and compatibility. The SDKs have + full support for change requests using selector filters, but don't support passing + parameters to design document filters. :param str db: Path parameter to specify the database name. :param List[str] doc_ids: (optional) Schema for a list of document IDs. :param List[str] fields: (optional) JSON array that uses the field syntax. Use this parameter to specify which fields of a document must be returned. - If it is omitted, the entire document is returned. + If it is omitted or empty, the entire document is returned. :param dict selector: (optional) JSON object describing criteria used to select documents. The selector specifies fields in the document, and provides an expression to evaluate with the field content or other data. @@ -404,54 +421,77 @@ def post_changes(self, field. There are two core types of operators in the selector syntax: * Combination operators: applied at the topmost level of selection. They - are used to combine selectors. In addition to the common boolean operators - (`$and`, `$or`, `$not`, `$nor`) there are three combination operators: - `$all`, `$elemMatch`, and `$allMatch`. A combination operator takes a - single argument. The argument is either another selector, or an array of + are used to combine selectors. A combination operator takes a single + argument. The argument is either another selector, or an array of selectors. * Condition operators: are specific to a field, and are used to evaluate the value stored in that field. For instance, the basic `$eq` operator matches when the specified field contains a value that is equal to the supplied argument. - * Only equality operators such as `$eq`, `$gt`, `$gte`, `$lt`, and `$lte` - (but not `$ne`) can be used as the basis of a query. You should include at - least one of these in a selector. - For further reference see - [selector - syntax](https://cloud.ibm.com/docs/Cloudant?topic=Cloudant-query#selector-syntax). + It is important for query performance to use appropriate selectors: + * Only equality operators such as `$eq`, `$gt`, `$gte`, `$lt`, and + `$lte` (but not `$ne`) can be used as the basis of a query. You should + include at least one of these in a selector. + * Some operators such as `$not`, `$or`, `$in`, and `$regex` cannot be + answered from an index. For query selectors use these operators in + conjunction with equality operators or create and use a partial index to + reduce the number of documents that will need to be scanned. + See [the Cloudant + Docs](https://cloud.ibm.com/docs/Cloudant?topic=Cloudant-operators) for a + list of all available combination and conditional operators. + For further reference see [selector + syntax](https://cloud.ibm.com/docs/Cloudant?topic=Cloudant-selector-syntax). :param str last_event_id: (optional) Header parameter to specify the ID of the last events received by the server on a previous connection. Overrides `since` query parameter. :param bool att_encoding_info: (optional) Query parameter to specify - whether to include the encoding information in attachment stubs if the - particular attachment is compressed. + whether to include the encoding information for compressed attachments. + Note that when used with a view-style query or changes feed this only + applies when requesting documents in the response. :param bool attachments: (optional) Query parameter to specify whether to - include attachments bodies in a response. + include attachment content in the response. Note that when used with a + view-style query or changes feed this only applies when requesting + documents in the response. :param bool conflicts: (optional) Query parameter to specify whether to - include a list of conflicted revisions in each returned document. Active - only when `include_docs` is `true`. + include a list of conflicted revisions in each returned document. Note that + when used with a view-style query or changes feed this only applies when + requesting documents in the response. :param bool descending: (optional) Query parameter to specify whether to - return the documents in descending by key order. + return changes in the descending order with most recent change first. The + `since` parameter has no effect when using descending order. :param str feed: (optional) Query parameter to specify the changes feed type. - :param str filter: (optional) Query parameter to specify a filter function - from a design document that will filter the changes stream emitting only - filtered events. For example: `design_doc/filtername`. - Additionally, some keywords are reserved for built-in filters: + :param str filter: (optional) Query parameter to specify a filter to emit + only specific events from the changes stream. + The built-in filter types are: * `_design` - Returns only changes to design documents. * `_doc_ids` - Returns changes for documents with an ID matching one - specified in - `doc_ids` request body parameter. - * `_selector` - Returns changes for documents that match the `selector` - request body parameter. The selector syntax is the same as used for - `_find`. + specified in `doc_ids` request body parameter. (`POST` only) + * `_selector` - Returns changes for documents that match the + `selector` request body parameter. The selector syntax is the same + as used for `_find`. (`POST` only) * `_view` - Returns changes for documents that match an existing map function in the view specified by the query parameter `view`. + Additionally, the value can be the name of a JS filter function from a + design document. For example: `design_doc/filtername`. + **Note:** For better performance use the built-in `_selector`, `_design` or + `_doc_ids` filters rather than JS based `_view` or design document filters. + If you need to pass values to change the filtered content use the + `_selector` filter type. :param int heartbeat: (optional) Query parameter to specify the period in - milliseconds after which an empty line is sent in the results. Only - applicable for longpoll, continuous, and eventsource feeds. Overrides any - timeout to keep the feed alive indefinitely. May also be `true` to use - default value of 60000. + milliseconds after which an empty line is sent in the results. Off by + default and only applicable for `continuous` and `eventsource` feeds. + Overrides any timeout to keep the feed alive indefinitely. May also be + `true` to use a value of `60000`. + **Note:** Delivery of heartbeats cannot be relied on at specific intervals. + If your application runs in an environment where idle network connections + may break, `heartbeat` is not suitable as a keepalive mechanism. Instead, + consider one of the following options: + * Use the `timeout` parameter with a value that is compatible with + your network environment. + * Switch to scheduled usage of one of the non-continuous changes feed + types (`normal` or `longpoll`). + * Use TCP keepalive. :param bool include_docs: (optional) Query parameter to specify whether to include the full content of the documents in the response. :param int limit: (optional) Query parameter to specify the number of @@ -464,8 +504,12 @@ def post_changes(self, computing the seq value across many shards (especially in highly-sharded databases) is expensive. :param str since: (optional) Query parameter to specify to start the - results from the change immediately after the given update sequence. Can be - a valid update sequence or `now` value. Default is `0` i.e. all changes. + results from the change immediately after the given update sequence. + Possible values are: + * `0` for all available changes (default). + * `now` for future changes. + * A valid update sequence, for example, from the `seq` value of a + change received before. :param str style: (optional) Query parameter to specify how many revisions are returned in the changes array. The default, `main_only`, will only return the current "winning" revision; all_docs will return all leaf @@ -484,14 +528,16 @@ def post_changes(self, :rtype: DetailedResponse with `dict` result representing a `ChangesResult` object """ - if db is None: + if not db: raise ValueError('db must be provided') headers = { - 'Last-Event-ID': last_event_id + 'Last-Event-ID': last_event_id, } - sdk_headers = get_sdk_headers(service_name=self.DEFAULT_SERVICE_NAME, - service_version='V1', - operation_id='post_changes') + sdk_headers = get_sdk_headers( + service_name=self.DEFAULT_SERVICE_NAME, + service_version='V1', + operation_id='post_changes', + ) headers.update(sdk_headers) params = { @@ -508,13 +554,13 @@ def post_changes(self, 'since': since, 'style': style, 'timeout': timeout, - 'view': view + 'view': view, } data = { 'doc_ids': doc_ids, 'fields': fields, - 'selector': selector + 'selector': selector, } data = {k: v for (k, v) in data.items() if v is not None} data = json.dumps(data) @@ -529,38 +575,40 @@ def post_changes(self, path_param_values = self.encode_path_vars(db) path_param_dict = dict(zip(path_param_keys, path_param_values)) url = '/{db}/_changes'.format(**path_param_dict) - request = self.prepare_request(method='POST', - url=url, - headers=headers, - params=params, - data=data) + request = self.prepare_request( + method='POST', + url=url, + headers=headers, + params=params, + data=data, + ) response = self.send(request, **kwargs) return response - - def post_changes_as_stream(self, + def post_changes_as_stream( + self, db: str, *, - doc_ids: List[str] = None, - fields: List[str] = None, - selector: dict = None, - last_event_id: str = None, - att_encoding_info: bool = None, - attachments: bool = None, - conflicts: bool = None, - descending: bool = None, - feed: str = None, - filter: str = None, - heartbeat: int = None, - include_docs: bool = None, - limit: int = None, - seq_interval: int = None, - since: str = None, - style: str = None, - timeout: int = None, - view: str = None, - **kwargs + doc_ids: Optional[List[str]] = None, + fields: Optional[List[str]] = None, + selector: Optional[dict] = None, + last_event_id: Optional[str] = None, + att_encoding_info: Optional[bool] = None, + attachments: Optional[bool] = None, + conflicts: Optional[bool] = None, + descending: Optional[bool] = None, + feed: Optional[str] = None, + filter: Optional[str] = None, + heartbeat: Optional[int] = None, + include_docs: Optional[bool] = None, + limit: Optional[int] = None, + seq_interval: Optional[int] = None, + since: Optional[str] = None, + style: Optional[str] = None, + timeout: Optional[int] = None, + view: Optional[str] = None, + **kwargs, ) -> DetailedResponse: """ Query the database document changes feed as stream. @@ -569,15 +617,19 @@ def post_changes_as_stream(self, It is widely used with the `filter` query parameter because it allows one to pass more information to the filter. ### Note - Before using the changes feed we recommend reading the + Before using the changes feed read the [FAQs](https://cloud.ibm.com/docs/Cloudant?topic=Cloudant-faq-using-changes-feed) - to understand the limitations and appropriate use cases.". + to understand the limitations and appropriate use cases. + If you need to pass parameters to dynamically change the filtered content use the + `_selector` filter type for better performance and compatibility. The SDKs have + full support for change requests using selector filters, but don't support passing + parameters to design document filters. :param str db: Path parameter to specify the database name. :param List[str] doc_ids: (optional) Schema for a list of document IDs. :param List[str] fields: (optional) JSON array that uses the field syntax. Use this parameter to specify which fields of a document must be returned. - If it is omitted, the entire document is returned. + If it is omitted or empty, the entire document is returned. :param dict selector: (optional) JSON object describing criteria used to select documents. The selector specifies fields in the document, and provides an expression to evaluate with the field content or other data. @@ -594,54 +646,77 @@ def post_changes_as_stream(self, field. There are two core types of operators in the selector syntax: * Combination operators: applied at the topmost level of selection. They - are used to combine selectors. In addition to the common boolean operators - (`$and`, `$or`, `$not`, `$nor`) there are three combination operators: - `$all`, `$elemMatch`, and `$allMatch`. A combination operator takes a - single argument. The argument is either another selector, or an array of + are used to combine selectors. A combination operator takes a single + argument. The argument is either another selector, or an array of selectors. * Condition operators: are specific to a field, and are used to evaluate the value stored in that field. For instance, the basic `$eq` operator matches when the specified field contains a value that is equal to the supplied argument. - * Only equality operators such as `$eq`, `$gt`, `$gte`, `$lt`, and `$lte` - (but not `$ne`) can be used as the basis of a query. You should include at - least one of these in a selector. - For further reference see - [selector - syntax](https://cloud.ibm.com/docs/Cloudant?topic=Cloudant-query#selector-syntax). + It is important for query performance to use appropriate selectors: + * Only equality operators such as `$eq`, `$gt`, `$gte`, `$lt`, and + `$lte` (but not `$ne`) can be used as the basis of a query. You should + include at least one of these in a selector. + * Some operators such as `$not`, `$or`, `$in`, and `$regex` cannot be + answered from an index. For query selectors use these operators in + conjunction with equality operators or create and use a partial index to + reduce the number of documents that will need to be scanned. + See [the Cloudant + Docs](https://cloud.ibm.com/docs/Cloudant?topic=Cloudant-operators) for a + list of all available combination and conditional operators. + For further reference see [selector + syntax](https://cloud.ibm.com/docs/Cloudant?topic=Cloudant-selector-syntax). :param str last_event_id: (optional) Header parameter to specify the ID of the last events received by the server on a previous connection. Overrides `since` query parameter. :param bool att_encoding_info: (optional) Query parameter to specify - whether to include the encoding information in attachment stubs if the - particular attachment is compressed. + whether to include the encoding information for compressed attachments. + Note that when used with a view-style query or changes feed this only + applies when requesting documents in the response. :param bool attachments: (optional) Query parameter to specify whether to - include attachments bodies in a response. + include attachment content in the response. Note that when used with a + view-style query or changes feed this only applies when requesting + documents in the response. :param bool conflicts: (optional) Query parameter to specify whether to - include a list of conflicted revisions in each returned document. Active - only when `include_docs` is `true`. + include a list of conflicted revisions in each returned document. Note that + when used with a view-style query or changes feed this only applies when + requesting documents in the response. :param bool descending: (optional) Query parameter to specify whether to - return the documents in descending by key order. + return changes in the descending order with most recent change first. The + `since` parameter has no effect when using descending order. :param str feed: (optional) Query parameter to specify the changes feed type. - :param str filter: (optional) Query parameter to specify a filter function - from a design document that will filter the changes stream emitting only - filtered events. For example: `design_doc/filtername`. - Additionally, some keywords are reserved for built-in filters: + :param str filter: (optional) Query parameter to specify a filter to emit + only specific events from the changes stream. + The built-in filter types are: * `_design` - Returns only changes to design documents. * `_doc_ids` - Returns changes for documents with an ID matching one - specified in - `doc_ids` request body parameter. - * `_selector` - Returns changes for documents that match the `selector` - request body parameter. The selector syntax is the same as used for - `_find`. + specified in `doc_ids` request body parameter. (`POST` only) + * `_selector` - Returns changes for documents that match the + `selector` request body parameter. The selector syntax is the same + as used for `_find`. (`POST` only) * `_view` - Returns changes for documents that match an existing map function in the view specified by the query parameter `view`. + Additionally, the value can be the name of a JS filter function from a + design document. For example: `design_doc/filtername`. + **Note:** For better performance use the built-in `_selector`, `_design` or + `_doc_ids` filters rather than JS based `_view` or design document filters. + If you need to pass values to change the filtered content use the + `_selector` filter type. :param int heartbeat: (optional) Query parameter to specify the period in - milliseconds after which an empty line is sent in the results. Only - applicable for longpoll, continuous, and eventsource feeds. Overrides any - timeout to keep the feed alive indefinitely. May also be `true` to use - default value of 60000. + milliseconds after which an empty line is sent in the results. Off by + default and only applicable for `continuous` and `eventsource` feeds. + Overrides any timeout to keep the feed alive indefinitely. May also be + `true` to use a value of `60000`. + **Note:** Delivery of heartbeats cannot be relied on at specific intervals. + If your application runs in an environment where idle network connections + may break, `heartbeat` is not suitable as a keepalive mechanism. Instead, + consider one of the following options: + * Use the `timeout` parameter with a value that is compatible with + your network environment. + * Switch to scheduled usage of one of the non-continuous changes feed + types (`normal` or `longpoll`). + * Use TCP keepalive. :param bool include_docs: (optional) Query parameter to specify whether to include the full content of the documents in the response. :param int limit: (optional) Query parameter to specify the number of @@ -654,8 +729,12 @@ def post_changes_as_stream(self, computing the seq value across many shards (especially in highly-sharded databases) is expensive. :param str since: (optional) Query parameter to specify to start the - results from the change immediately after the given update sequence. Can be - a valid update sequence or `now` value. Default is `0` i.e. all changes. + results from the change immediately after the given update sequence. + Possible values are: + * `0` for all available changes (default). + * `now` for future changes. + * A valid update sequence, for example, from the `seq` value of a + change received before. :param str style: (optional) Query parameter to specify how many revisions are returned in the changes array. The default, `main_only`, will only return the current "winning" revision; all_docs will return all leaf @@ -674,14 +753,16 @@ def post_changes_as_stream(self, :rtype: DetailedResponse with `BinaryIO` result """ - if db is None: + if not db: raise ValueError('db must be provided') headers = { - 'Last-Event-ID': last_event_id + 'Last-Event-ID': last_event_id, } - sdk_headers = get_sdk_headers(service_name=self.DEFAULT_SERVICE_NAME, - service_version='V1', - operation_id='post_changes_as_stream') + sdk_headers = get_sdk_headers( + service_name=self.DEFAULT_SERVICE_NAME, + service_version='V1', + operation_id='post_changes_as_stream', + ) headers.update(sdk_headers) params = { @@ -698,13 +779,13 @@ def post_changes_as_stream(self, 'since': since, 'style': style, 'timeout': timeout, - 'view': view + 'view': view, } data = { 'doc_ids': doc_ids, 'fields': fields, - 'selector': selector + 'selector': selector, } data = {k: v for (k, v) in data.items() if v is not None} data = json.dumps(data) @@ -719,11 +800,13 @@ def post_changes_as_stream(self, path_param_values = self.encode_path_vars(db) path_param_dict = dict(zip(path_param_keys, path_param_values)) url = '/{db}/_changes'.format(**path_param_dict) - request = self.prepare_request(method='POST', - url=url, - headers=headers, - params=params, - data=data) + request = self.prepare_request( + method='POST', + url=url, + headers=headers, + params=params, + data=data, + ) response = self.send(request, stream=True, **kwargs) return response @@ -732,10 +815,10 @@ def post_changes_as_stream(self, # Databases ######################### - - def head_database(self, + def head_database( + self, db: str, - **kwargs + **kwargs, ) -> DetailedResponse: """ Retrieve the HTTP headers for a database. @@ -750,12 +833,14 @@ def head_database(self, :rtype: DetailedResponse """ - if db is None: + if not db: raise ValueError('db must be provided') headers = {} - sdk_headers = get_sdk_headers(service_name=self.DEFAULT_SERVICE_NAME, - service_version='V1', - operation_id='head_database') + sdk_headers = get_sdk_headers( + service_name=self.DEFAULT_SERVICE_NAME, + service_version='V1', + operation_id='head_database', + ) headers.update(sdk_headers) if 'headers' in kwargs: @@ -766,28 +851,32 @@ def head_database(self, path_param_values = self.encode_path_vars(db) path_param_dict = dict(zip(path_param_keys, path_param_values)) url = '/{db}'.format(**path_param_dict) - request = self.prepare_request(method='HEAD', - url=url, - headers=headers) + request = self.prepare_request( + method='HEAD', + url=url, + headers=headers, + ) response = self.send(request, **kwargs) return response - - def get_all_dbs(self, + def get_all_dbs( + self, *, - descending: bool = None, - end_key: str = None, - limit: int = None, - skip: int = None, - start_key: str = None, - **kwargs + descending: Optional[bool] = None, + end_key: Optional[str] = None, + limit: Optional[int] = None, + skip: Optional[int] = None, + start_key: Optional[str] = None, + **kwargs, ) -> DetailedResponse: """ Query a list of all database names in the instance. + Query to retrieve a list of database names from the instance. + :param bool descending: (optional) Query parameter to specify whether to - return the documents in descending by key order. + return rows in descending by key order. :param str end_key: (optional) Query parameter to specify to stop returning records when the specified key is reached. String representation of any JSON type that matches the key type emitted by the view function. @@ -804,9 +893,11 @@ def get_all_dbs(self, """ headers = {} - sdk_headers = get_sdk_headers(service_name=self.DEFAULT_SERVICE_NAME, - service_version='V1', - operation_id='get_all_dbs') + sdk_headers = get_sdk_headers( + service_name=self.DEFAULT_SERVICE_NAME, + service_version='V1', + operation_id='get_all_dbs', + ) headers.update(sdk_headers) params = { @@ -814,7 +905,7 @@ def get_all_dbs(self, 'end_key': end_key, 'limit': limit, 'skip': skip, - 'start_key': start_key + 'start_key': start_key, } if 'headers' in kwargs: @@ -823,18 +914,20 @@ def get_all_dbs(self, headers['Accept'] = 'application/json' url = '/_all_dbs' - request = self.prepare_request(method='GET', - url=url, - headers=headers, - params=params) + request = self.prepare_request( + method='GET', + url=url, + headers=headers, + params=params, + ) response = self.send(request, **kwargs) return response - - def post_dbs_info(self, + def post_dbs_info( + self, keys: List[str], - **kwargs + **kwargs, ) -> DetailedResponse: """ Query information about multiple databases. @@ -853,13 +946,15 @@ def post_dbs_info(self, if keys is None: raise ValueError('keys must be provided') headers = {} - sdk_headers = get_sdk_headers(service_name=self.DEFAULT_SERVICE_NAME, - service_version='V1', - operation_id='post_dbs_info') + sdk_headers = get_sdk_headers( + service_name=self.DEFAULT_SERVICE_NAME, + service_version='V1', + operation_id='post_dbs_info', + ) headers.update(sdk_headers) data = { - 'keys': keys + 'keys': keys, } data = {k: v for (k, v) in data.items() if v is not None} data = json.dumps(data) @@ -871,18 +966,20 @@ def post_dbs_info(self, headers['Accept'] = 'application/json' url = '/_dbs_info' - request = self.prepare_request(method='POST', - url=url, - headers=headers, - data=data) + request = self.prepare_request( + method='POST', + url=url, + headers=headers, + data=data, + ) response = self.send(request, **kwargs) return response - - def delete_database(self, + def delete_database( + self, db: str, - **kwargs + **kwargs, ) -> DetailedResponse: """ Delete a database. @@ -898,12 +995,14 @@ def delete_database(self, :rtype: DetailedResponse with `dict` result representing a `Ok` object """ - if db is None: + if not db: raise ValueError('db must be provided') headers = {} - sdk_headers = get_sdk_headers(service_name=self.DEFAULT_SERVICE_NAME, - service_version='V1', - operation_id='delete_database') + sdk_headers = get_sdk_headers( + service_name=self.DEFAULT_SERVICE_NAME, + service_version='V1', + operation_id='delete_database', + ) headers.update(sdk_headers) if 'headers' in kwargs: @@ -915,33 +1014,39 @@ def delete_database(self, path_param_values = self.encode_path_vars(db) path_param_dict = dict(zip(path_param_keys, path_param_values)) url = '/{db}'.format(**path_param_dict) - request = self.prepare_request(method='DELETE', - url=url, - headers=headers) + request = self.prepare_request( + method='DELETE', + url=url, + headers=headers, + ) response = self.send(request, **kwargs) return response - - def get_database_information(self, + def get_database_information( + self, db: str, - **kwargs + **kwargs, ) -> DetailedResponse: """ Retrieve information about a database. + Retrieve detailed information about the database. + :param str db: Path parameter to specify the database name. :param dict headers: A `dict` containing the request headers :return: A `DetailedResponse` containing the result, headers and HTTP status code. :rtype: DetailedResponse with `dict` result representing a `DatabaseInformation` object """ - if db is None: + if not db: raise ValueError('db must be provided') headers = {} - sdk_headers = get_sdk_headers(service_name=self.DEFAULT_SERVICE_NAME, - service_version='V1', - operation_id='get_database_information') + sdk_headers = get_sdk_headers( + service_name=self.DEFAULT_SERVICE_NAME, + service_version='V1', + operation_id='get_database_information', + ) headers.update(sdk_headers) if 'headers' in kwargs: @@ -953,47 +1058,58 @@ def get_database_information(self, path_param_values = self.encode_path_vars(db) path_param_dict = dict(zip(path_param_keys, path_param_values)) url = '/{db}'.format(**path_param_dict) - request = self.prepare_request(method='GET', - url=url, - headers=headers) + request = self.prepare_request( + method='GET', + url=url, + headers=headers, + ) response = self.send(request, **kwargs) return response - - def put_database(self, + def put_database( + self, db: str, *, - partitioned: bool = None, - q: int = None, - **kwargs + partitioned: Optional[bool] = None, + q: Optional[int] = None, + **kwargs, ) -> DetailedResponse: """ Create a database. + Create a new database with the requested properties. + :param str db: Path parameter to specify the database name. :param bool partitioned: (optional) Query parameter to specify whether to enable database partitions when creating a database. + Before using read the + [FAQs](https://cloud.ibm.com/docs/Cloudant?topic=Cloudant-database-partitioning#partitioned-databases-database-partitioning) + to understand the limitations and appropriate use cases. :param int q: (optional) The number of shards in the database. Each shard - is a partition of the hash value range. Its value is set by the service. - For more information about modifying database configuration, contact IBM - Cloudant support. + is a partition of the hash value range. Cloudant recommends using the + default value for most databases. However, if your database is expected to + be larger than 250 GB or have a lot of indexes, you may need to adjust the + settings. In these cases, it's best to reach out to IBM Cloudant customer + support for guidance on how to meet your specific needs and requirements. :param dict headers: A `dict` containing the request headers :return: A `DetailedResponse` containing the result, headers and HTTP status code. :rtype: DetailedResponse with `dict` result representing a `Ok` object """ - if db is None: + if not db: raise ValueError('db must be provided') headers = {} - sdk_headers = get_sdk_headers(service_name=self.DEFAULT_SERVICE_NAME, - service_version='V1', - operation_id='put_database') + sdk_headers = get_sdk_headers( + service_name=self.DEFAULT_SERVICE_NAME, + service_version='V1', + operation_id='put_database', + ) headers.update(sdk_headers) params = { 'partitioned': partitioned, - 'q': q + 'q': q, } if 'headers' in kwargs: @@ -1005,10 +1121,12 @@ def put_database(self, path_param_values = self.encode_path_vars(db) path_param_dict = dict(zip(path_param_keys, path_param_values)) url = '/{db}'.format(**path_param_dict) - request = self.prepare_request(method='PUT', - url=url, - headers=headers, - params=params) + request = self.prepare_request( + method='PUT', + url=url, + headers=headers, + params=params, + ) response = self.send(request, **kwargs) return response @@ -1017,15 +1135,15 @@ def put_database(self, # Documents ######################### - - def head_document(self, + def head_document( + self, db: str, doc_id: str, *, - if_none_match: str = None, - latest: bool = None, - rev: str = None, - **kwargs + if_none_match: Optional[str] = None, + latest: Optional[bool] = None, + rev: Optional[str] = None, + **kwargs, ) -> DetailedResponse: """ Retrieve the HTTP headers for the document. @@ -1039,8 +1157,8 @@ def head_document(self, :param str db: Path parameter to specify the database name. :param str doc_id: Path parameter to specify the document ID. - :param str if_none_match: (optional) Header parameter to specify a double - quoted document revision token for cache control. + :param str if_none_match: (optional) Header parameter for a conditional + HTTP request not matching an ETag. :param bool latest: (optional) Query parameter to specify whether to force retrieving latest leaf revision, no matter what rev was requested. :param str rev: (optional) Query parameter to specify a document revision. @@ -1049,21 +1167,23 @@ def head_document(self, :rtype: DetailedResponse """ - if db is None: + if not db: raise ValueError('db must be provided') - if doc_id is None: + if not doc_id: raise ValueError('doc_id must be provided') headers = { - 'If-None-Match': if_none_match + 'If-None-Match': if_none_match, } - sdk_headers = get_sdk_headers(service_name=self.DEFAULT_SERVICE_NAME, - service_version='V1', - operation_id='head_document') + sdk_headers = get_sdk_headers( + service_name=self.DEFAULT_SERVICE_NAME, + service_version='V1', + operation_id='head_document', + ) headers.update(sdk_headers) params = { 'latest': latest, - 'rev': rev + 'rev': rev, } if 'headers' in kwargs: @@ -1074,22 +1194,24 @@ def head_document(self, path_param_values = self.encode_path_vars(db, doc_id) path_param_dict = dict(zip(path_param_keys, path_param_values)) url = '/{db}/{doc_id}'.format(**path_param_dict) - request = self.prepare_request(method='HEAD', - url=url, - headers=headers, - params=params) + request = self.prepare_request( + method='HEAD', + url=url, + headers=headers, + params=params, + ) response = self.send(request, **kwargs) return response - - def post_document(self, + def post_document( + self, db: str, document: Union['Document', BinaryIO], *, - content_type: str = None, - batch: str = None, - **kwargs + content_type: Optional[str] = None, + batch: Optional[str] = None, + **kwargs, ) -> DetailedResponse: """ Create or modify a document in a database. @@ -1115,7 +1237,7 @@ def post_document(self, :rtype: DetailedResponse with `dict` result representing a `DocumentResult` object """ - if db is None: + if not db: raise ValueError('db must be provided') if document is None: raise ValueError('document must be provided') @@ -1123,15 +1245,17 @@ def post_document(self, document = convert_model(document) content_type = content_type or 'application/json' headers = { - 'Content-Type': content_type + 'Content-Type': content_type, } - sdk_headers = get_sdk_headers(service_name=self.DEFAULT_SERVICE_NAME, - service_version='V1', - operation_id='post_document') + sdk_headers = get_sdk_headers( + service_name=self.DEFAULT_SERVICE_NAME, + service_version='V1', + operation_id='post_document', + ) headers.update(sdk_headers) params = { - 'batch': batch + 'batch': batch, } if isinstance(document, dict): @@ -1150,33 +1274,35 @@ def post_document(self, path_param_values = self.encode_path_vars(db) path_param_dict = dict(zip(path_param_keys, path_param_values)) url = '/{db}'.format(**path_param_dict) - request = self.prepare_request(method='POST', - url=url, - headers=headers, - params=params, - data=data) + request = self.prepare_request( + method='POST', + url=url, + headers=headers, + params=params, + data=data, + ) response = self.send(request, **kwargs) return response - - def post_all_docs(self, + def post_all_docs( + self, db: str, *, - att_encoding_info: bool = None, - attachments: bool = None, - conflicts: bool = None, - descending: bool = None, - include_docs: bool = None, - inclusive_end: bool = None, - limit: int = None, - skip: int = None, - update_seq: bool = None, - end_key: str = None, - key: str = None, - keys: List[str] = None, - start_key: str = None, - **kwargs + att_encoding_info: Optional[bool] = None, + attachments: Optional[bool] = None, + conflicts: Optional[bool] = None, + descending: Optional[bool] = None, + include_docs: Optional[bool] = None, + inclusive_end: Optional[bool] = None, + limit: Optional[int] = None, + skip: Optional[int] = None, + update_seq: Optional[bool] = None, + end_key: Optional[str] = None, + key: Optional[str] = None, + keys: Optional[List[str]] = None, + start_key: Optional[str] = None, + **kwargs, ) -> DetailedResponse: """ Query a list of all documents in a database. @@ -1190,19 +1316,20 @@ def post_all_docs(self, :param str db: Path parameter to specify the database name. :param bool att_encoding_info: (optional) Parameter to specify whether to - include the encoding information in attachment stubs if the particular - attachment is compressed. + include the encoding information for compressed attachments. This only + applies when requesting documents in the response. :param bool attachments: (optional) Parameter to specify whether to include - attachments bodies in a response. + attachment content in included document content or only the attachment + metadata. This only applies when requesting documents in the response. :param bool conflicts: (optional) Parameter to specify whether to include a - list of conflicted revisions in each returned document. Active only when - `include_docs` is `true`. + list of conflicted revisions in each returned document. This only applies + when requesting documents in the response. :param bool descending: (optional) Parameter to specify whether to return the documents in descending by key order. :param bool include_docs: (optional) Parameter to specify whether to include the full content of the documents in the response. - :param bool inclusive_end: (optional) Parameter to specify whether the - specified end key should be included in the result. + :param bool inclusive_end: (optional) Parameter to specify whether to + include the specified end key in the result. :param int limit: (optional) Parameter to specify the number of returned documents to limit the result to. :param int skip: (optional) Parameter to specify the number of records @@ -1219,12 +1346,14 @@ def post_all_docs(self, :rtype: DetailedResponse with `dict` result representing a `AllDocsResult` object """ - if db is None: + if not db: raise ValueError('db must be provided') headers = {} - sdk_headers = get_sdk_headers(service_name=self.DEFAULT_SERVICE_NAME, - service_version='V1', - operation_id='post_all_docs') + sdk_headers = get_sdk_headers( + service_name=self.DEFAULT_SERVICE_NAME, + service_version='V1', + operation_id='post_all_docs', + ) headers.update(sdk_headers) data = { @@ -1240,7 +1369,7 @@ def post_all_docs(self, 'end_key': end_key, 'key': key, 'keys': keys, - 'start_key': start_key + 'start_key': start_key, } data = {k: v for (k, v) in data.items() if v is not None} data = json.dumps(data) @@ -1255,32 +1384,34 @@ def post_all_docs(self, path_param_values = self.encode_path_vars(db) path_param_dict = dict(zip(path_param_keys, path_param_values)) url = '/{db}/_all_docs'.format(**path_param_dict) - request = self.prepare_request(method='POST', - url=url, - headers=headers, - data=data) + request = self.prepare_request( + method='POST', + url=url, + headers=headers, + data=data, + ) response = self.send(request, **kwargs) return response - - def post_all_docs_as_stream(self, + def post_all_docs_as_stream( + self, db: str, *, - att_encoding_info: bool = None, - attachments: bool = None, - conflicts: bool = None, - descending: bool = None, - include_docs: bool = None, - inclusive_end: bool = None, - limit: int = None, - skip: int = None, - update_seq: bool = None, - end_key: str = None, - key: str = None, - keys: List[str] = None, - start_key: str = None, - **kwargs + att_encoding_info: Optional[bool] = None, + attachments: Optional[bool] = None, + conflicts: Optional[bool] = None, + descending: Optional[bool] = None, + include_docs: Optional[bool] = None, + inclusive_end: Optional[bool] = None, + limit: Optional[int] = None, + skip: Optional[int] = None, + update_seq: Optional[bool] = None, + end_key: Optional[str] = None, + key: Optional[str] = None, + keys: Optional[List[str]] = None, + start_key: Optional[str] = None, + **kwargs, ) -> DetailedResponse: """ Query a list of all documents in a database as stream. @@ -1294,19 +1425,20 @@ def post_all_docs_as_stream(self, :param str db: Path parameter to specify the database name. :param bool att_encoding_info: (optional) Parameter to specify whether to - include the encoding information in attachment stubs if the particular - attachment is compressed. + include the encoding information for compressed attachments. This only + applies when requesting documents in the response. :param bool attachments: (optional) Parameter to specify whether to include - attachments bodies in a response. + attachment content in included document content or only the attachment + metadata. This only applies when requesting documents in the response. :param bool conflicts: (optional) Parameter to specify whether to include a - list of conflicted revisions in each returned document. Active only when - `include_docs` is `true`. + list of conflicted revisions in each returned document. This only applies + when requesting documents in the response. :param bool descending: (optional) Parameter to specify whether to return the documents in descending by key order. :param bool include_docs: (optional) Parameter to specify whether to include the full content of the documents in the response. - :param bool inclusive_end: (optional) Parameter to specify whether the - specified end key should be included in the result. + :param bool inclusive_end: (optional) Parameter to specify whether to + include the specified end key in the result. :param int limit: (optional) Parameter to specify the number of returned documents to limit the result to. :param int skip: (optional) Parameter to specify the number of records @@ -1323,12 +1455,14 @@ def post_all_docs_as_stream(self, :rtype: DetailedResponse with `BinaryIO` result """ - if db is None: + if not db: raise ValueError('db must be provided') headers = {} - sdk_headers = get_sdk_headers(service_name=self.DEFAULT_SERVICE_NAME, - service_version='V1', - operation_id='post_all_docs_as_stream') + sdk_headers = get_sdk_headers( + service_name=self.DEFAULT_SERVICE_NAME, + service_version='V1', + operation_id='post_all_docs_as_stream', + ) headers.update(sdk_headers) data = { @@ -1344,7 +1478,7 @@ def post_all_docs_as_stream(self, 'end_key': end_key, 'key': key, 'keys': keys, - 'start_key': start_key + 'start_key': start_key, } data = {k: v for (k, v) in data.items() if v is not None} data = json.dumps(data) @@ -1359,19 +1493,21 @@ def post_all_docs_as_stream(self, path_param_values = self.encode_path_vars(db) path_param_dict = dict(zip(path_param_keys, path_param_values)) url = '/{db}/_all_docs'.format(**path_param_dict) - request = self.prepare_request(method='POST', - url=url, - headers=headers, - data=data) + request = self.prepare_request( + method='POST', + url=url, + headers=headers, + data=data, + ) response = self.send(request, stream=True, **kwargs) return response - - def post_all_docs_queries(self, + def post_all_docs_queries( + self, db: str, queries: List['AllDocsQuery'], - **kwargs + **kwargs, ) -> DetailedResponse: """ Multi-query the list of all documents in a database. @@ -1392,19 +1528,21 @@ def post_all_docs_queries(self, :rtype: DetailedResponse with `dict` result representing a `AllDocsQueriesResult` object """ - if db is None: + if not db: raise ValueError('db must be provided') if queries is None: raise ValueError('queries must be provided') queries = [convert_model(x) for x in queries] headers = {} - sdk_headers = get_sdk_headers(service_name=self.DEFAULT_SERVICE_NAME, - service_version='V1', - operation_id='post_all_docs_queries') + sdk_headers = get_sdk_headers( + service_name=self.DEFAULT_SERVICE_NAME, + service_version='V1', + operation_id='post_all_docs_queries', + ) headers.update(sdk_headers) data = { - 'queries': queries + 'queries': queries, } data = {k: v for (k, v) in data.items() if v is not None} data = json.dumps(data) @@ -1419,19 +1557,21 @@ def post_all_docs_queries(self, path_param_values = self.encode_path_vars(db) path_param_dict = dict(zip(path_param_keys, path_param_values)) url = '/{db}/_all_docs/queries'.format(**path_param_dict) - request = self.prepare_request(method='POST', - url=url, - headers=headers, - data=data) + request = self.prepare_request( + method='POST', + url=url, + headers=headers, + data=data, + ) response = self.send(request, **kwargs) return response - - def post_all_docs_queries_as_stream(self, + def post_all_docs_queries_as_stream( + self, db: str, queries: List['AllDocsQuery'], - **kwargs + **kwargs, ) -> DetailedResponse: """ Multi-query the list of all documents in a database as stream. @@ -1452,19 +1592,21 @@ def post_all_docs_queries_as_stream(self, :rtype: DetailedResponse with `BinaryIO` result """ - if db is None: + if not db: raise ValueError('db must be provided') if queries is None: raise ValueError('queries must be provided') queries = [convert_model(x) for x in queries] headers = {} - sdk_headers = get_sdk_headers(service_name=self.DEFAULT_SERVICE_NAME, - service_version='V1', - operation_id='post_all_docs_queries_as_stream') + sdk_headers = get_sdk_headers( + service_name=self.DEFAULT_SERVICE_NAME, + service_version='V1', + operation_id='post_all_docs_queries_as_stream', + ) headers.update(sdk_headers) data = { - 'queries': queries + 'queries': queries, } data = {k: v for (k, v) in data.items() if v is not None} data = json.dumps(data) @@ -1479,19 +1621,21 @@ def post_all_docs_queries_as_stream(self, path_param_values = self.encode_path_vars(db) path_param_dict = dict(zip(path_param_keys, path_param_values)) url = '/{db}/_all_docs/queries'.format(**path_param_dict) - request = self.prepare_request(method='POST', - url=url, - headers=headers, - data=data) + request = self.prepare_request( + method='POST', + url=url, + headers=headers, + data=data, + ) response = self.send(request, stream=True, **kwargs) return response - - def post_bulk_docs(self, + def post_bulk_docs( + self, db: str, bulk_docs: Union['BulkDocs', BinaryIO], - **kwargs + **kwargs, ) -> DetailedResponse: """ Bulk modify multiple documents in a database. @@ -1508,16 +1652,18 @@ def post_bulk_docs(self, :rtype: DetailedResponse with `List[DocumentResult]` result """ - if db is None: + if not db: raise ValueError('db must be provided') if bulk_docs is None: raise ValueError('bulk_docs must be provided') if isinstance(bulk_docs, BulkDocs): bulk_docs = convert_model(bulk_docs) headers = {} - sdk_headers = get_sdk_headers(service_name=self.DEFAULT_SERVICE_NAME, - service_version='V1', - operation_id='post_bulk_docs') + sdk_headers = get_sdk_headers( + service_name=self.DEFAULT_SERVICE_NAME, + service_version='V1', + operation_id='post_bulk_docs', + ) headers.update(sdk_headers) if isinstance(bulk_docs, dict): @@ -1535,24 +1681,26 @@ def post_bulk_docs(self, path_param_values = self.encode_path_vars(db) path_param_dict = dict(zip(path_param_keys, path_param_values)) url = '/{db}/_bulk_docs'.format(**path_param_dict) - request = self.prepare_request(method='POST', - url=url, - headers=headers, - data=data) + request = self.prepare_request( + method='POST', + url=url, + headers=headers, + data=data, + ) response = self.send(request, **kwargs) return response - - def post_bulk_get(self, + def post_bulk_get( + self, db: str, docs: List['BulkGetQueryDocument'], *, - attachments: bool = None, - att_encoding_info: bool = None, - latest: bool = None, - revs: bool = None, - **kwargs + attachments: Optional[bool] = None, + att_encoding_info: Optional[bool] = None, + latest: Optional[bool] = None, + revs: Optional[bool] = None, + **kwargs, ) -> DetailedResponse: """ Bulk query revision information for multiple documents. @@ -1564,10 +1712,13 @@ def post_bulk_get(self, :param List[BulkGetQueryDocument] docs: List of document items to get in bulk. :param bool attachments: (optional) Query parameter to specify whether to - include attachments bodies in a response. + include attachment content in the response. Note that when used with a + view-style query or changes feed this only applies when requesting + documents in the response. :param bool att_encoding_info: (optional) Query parameter to specify - whether to include the encoding information in attachment stubs if the - particular attachment is compressed. + whether to include the encoding information for compressed attachments. + Note that when used with a view-style query or changes feed this only + applies when requesting documents in the response. :param bool latest: (optional) Query parameter to specify whether to force retrieving latest leaf revision, no matter what rev was requested. :param bool revs: (optional) Query parameter to specify whether to include @@ -1577,26 +1728,28 @@ def post_bulk_get(self, :rtype: DetailedResponse with `dict` result representing a `BulkGetResult` object """ - if db is None: + if not db: raise ValueError('db must be provided') if docs is None: raise ValueError('docs must be provided') docs = [convert_model(x) for x in docs] headers = {} - sdk_headers = get_sdk_headers(service_name=self.DEFAULT_SERVICE_NAME, - service_version='V1', - operation_id='post_bulk_get') + sdk_headers = get_sdk_headers( + service_name=self.DEFAULT_SERVICE_NAME, + service_version='V1', + operation_id='post_bulk_get', + ) headers.update(sdk_headers) params = { 'attachments': attachments, 'att_encoding_info': att_encoding_info, 'latest': latest, - 'revs': revs + 'revs': revs, } data = { - 'docs': docs + 'docs': docs, } data = {k: v for (k, v) in data.items() if v is not None} data = json.dumps(data) @@ -1611,25 +1764,27 @@ def post_bulk_get(self, path_param_values = self.encode_path_vars(db) path_param_dict = dict(zip(path_param_keys, path_param_values)) url = '/{db}/_bulk_get'.format(**path_param_dict) - request = self.prepare_request(method='POST', - url=url, - headers=headers, - params=params, - data=data) + request = self.prepare_request( + method='POST', + url=url, + headers=headers, + params=params, + data=data, + ) response = self.send(request, **kwargs) return response - - def post_bulk_get_as_mixed(self, + def post_bulk_get_as_mixed( + self, db: str, docs: List['BulkGetQueryDocument'], *, - attachments: bool = None, - att_encoding_info: bool = None, - latest: bool = None, - revs: bool = None, - **kwargs + attachments: Optional[bool] = None, + att_encoding_info: Optional[bool] = None, + latest: Optional[bool] = None, + revs: Optional[bool] = None, + **kwargs, ) -> DetailedResponse: """ Bulk query revision information for multiple documents as mixed. @@ -1641,10 +1796,13 @@ def post_bulk_get_as_mixed(self, :param List[BulkGetQueryDocument] docs: List of document items to get in bulk. :param bool attachments: (optional) Query parameter to specify whether to - include attachments bodies in a response. + include attachment content in the response. Note that when used with a + view-style query or changes feed this only applies when requesting + documents in the response. :param bool att_encoding_info: (optional) Query parameter to specify - whether to include the encoding information in attachment stubs if the - particular attachment is compressed. + whether to include the encoding information for compressed attachments. + Note that when used with a view-style query or changes feed this only + applies when requesting documents in the response. :param bool latest: (optional) Query parameter to specify whether to force retrieving latest leaf revision, no matter what rev was requested. :param bool revs: (optional) Query parameter to specify whether to include @@ -1654,26 +1812,28 @@ def post_bulk_get_as_mixed(self, :rtype: DetailedResponse with `BinaryIO` result """ - if db is None: + if not db: raise ValueError('db must be provided') if docs is None: raise ValueError('docs must be provided') docs = [convert_model(x) for x in docs] headers = {} - sdk_headers = get_sdk_headers(service_name=self.DEFAULT_SERVICE_NAME, - service_version='V1', - operation_id='post_bulk_get_as_mixed') + sdk_headers = get_sdk_headers( + service_name=self.DEFAULT_SERVICE_NAME, + service_version='V1', + operation_id='post_bulk_get_as_mixed', + ) headers.update(sdk_headers) params = { 'attachments': attachments, 'att_encoding_info': att_encoding_info, 'latest': latest, - 'revs': revs + 'revs': revs, } data = { - 'docs': docs + 'docs': docs, } data = {k: v for (k, v) in data.items() if v is not None} data = json.dumps(data) @@ -1688,25 +1848,27 @@ def post_bulk_get_as_mixed(self, path_param_values = self.encode_path_vars(db) path_param_dict = dict(zip(path_param_keys, path_param_values)) url = '/{db}/_bulk_get'.format(**path_param_dict) - request = self.prepare_request(method='POST', - url=url, - headers=headers, - params=params, - data=data) + request = self.prepare_request( + method='POST', + url=url, + headers=headers, + params=params, + data=data, + ) response = self.send(request, **kwargs) return response - - def post_bulk_get_as_related(self, + def post_bulk_get_as_related( + self, db: str, docs: List['BulkGetQueryDocument'], *, - attachments: bool = None, - att_encoding_info: bool = None, - latest: bool = None, - revs: bool = None, - **kwargs + attachments: Optional[bool] = None, + att_encoding_info: Optional[bool] = None, + latest: Optional[bool] = None, + revs: Optional[bool] = None, + **kwargs, ) -> DetailedResponse: """ Bulk query revision information for multiple documents as related. @@ -1718,10 +1880,13 @@ def post_bulk_get_as_related(self, :param List[BulkGetQueryDocument] docs: List of document items to get in bulk. :param bool attachments: (optional) Query parameter to specify whether to - include attachments bodies in a response. + include attachment content in the response. Note that when used with a + view-style query or changes feed this only applies when requesting + documents in the response. :param bool att_encoding_info: (optional) Query parameter to specify - whether to include the encoding information in attachment stubs if the - particular attachment is compressed. + whether to include the encoding information for compressed attachments. + Note that when used with a view-style query or changes feed this only + applies when requesting documents in the response. :param bool latest: (optional) Query parameter to specify whether to force retrieving latest leaf revision, no matter what rev was requested. :param bool revs: (optional) Query parameter to specify whether to include @@ -1731,26 +1896,28 @@ def post_bulk_get_as_related(self, :rtype: DetailedResponse with `BinaryIO` result """ - if db is None: + if not db: raise ValueError('db must be provided') if docs is None: raise ValueError('docs must be provided') docs = [convert_model(x) for x in docs] headers = {} - sdk_headers = get_sdk_headers(service_name=self.DEFAULT_SERVICE_NAME, - service_version='V1', - operation_id='post_bulk_get_as_related') + sdk_headers = get_sdk_headers( + service_name=self.DEFAULT_SERVICE_NAME, + service_version='V1', + operation_id='post_bulk_get_as_related', + ) headers.update(sdk_headers) params = { 'attachments': attachments, 'att_encoding_info': att_encoding_info, 'latest': latest, - 'revs': revs + 'revs': revs, } data = { - 'docs': docs + 'docs': docs, } data = {k: v for (k, v) in data.items() if v is not None} data = json.dumps(data) @@ -1765,25 +1932,27 @@ def post_bulk_get_as_related(self, path_param_values = self.encode_path_vars(db) path_param_dict = dict(zip(path_param_keys, path_param_values)) url = '/{db}/_bulk_get'.format(**path_param_dict) - request = self.prepare_request(method='POST', - url=url, - headers=headers, - params=params, - data=data) + request = self.prepare_request( + method='POST', + url=url, + headers=headers, + params=params, + data=data, + ) response = self.send(request, **kwargs) return response - - def post_bulk_get_as_stream(self, + def post_bulk_get_as_stream( + self, db: str, docs: List['BulkGetQueryDocument'], *, - attachments: bool = None, - att_encoding_info: bool = None, - latest: bool = None, - revs: bool = None, - **kwargs + attachments: Optional[bool] = None, + att_encoding_info: Optional[bool] = None, + latest: Optional[bool] = None, + revs: Optional[bool] = None, + **kwargs, ) -> DetailedResponse: """ Bulk query revision information for multiple documents as stream. @@ -1795,10 +1964,13 @@ def post_bulk_get_as_stream(self, :param List[BulkGetQueryDocument] docs: List of document items to get in bulk. :param bool attachments: (optional) Query parameter to specify whether to - include attachments bodies in a response. + include attachment content in the response. Note that when used with a + view-style query or changes feed this only applies when requesting + documents in the response. :param bool att_encoding_info: (optional) Query parameter to specify - whether to include the encoding information in attachment stubs if the - particular attachment is compressed. + whether to include the encoding information for compressed attachments. + Note that when used with a view-style query or changes feed this only + applies when requesting documents in the response. :param bool latest: (optional) Query parameter to specify whether to force retrieving latest leaf revision, no matter what rev was requested. :param bool revs: (optional) Query parameter to specify whether to include @@ -1808,26 +1980,28 @@ def post_bulk_get_as_stream(self, :rtype: DetailedResponse with `BinaryIO` result """ - if db is None: + if not db: raise ValueError('db must be provided') if docs is None: raise ValueError('docs must be provided') docs = [convert_model(x) for x in docs] headers = {} - sdk_headers = get_sdk_headers(service_name=self.DEFAULT_SERVICE_NAME, - service_version='V1', - operation_id='post_bulk_get_as_stream') + sdk_headers = get_sdk_headers( + service_name=self.DEFAULT_SERVICE_NAME, + service_version='V1', + operation_id='post_bulk_get_as_stream', + ) headers.update(sdk_headers) params = { 'attachments': attachments, 'att_encoding_info': att_encoding_info, 'latest': latest, - 'revs': revs + 'revs': revs, } data = { - 'docs': docs + 'docs': docs, } data = {k: v for (k, v) in data.items() if v is not None} data = json.dumps(data) @@ -1842,24 +2016,26 @@ def post_bulk_get_as_stream(self, path_param_values = self.encode_path_vars(db) path_param_dict = dict(zip(path_param_keys, path_param_values)) url = '/{db}/_bulk_get'.format(**path_param_dict) - request = self.prepare_request(method='POST', - url=url, - headers=headers, - params=params, - data=data) + request = self.prepare_request( + method='POST', + url=url, + headers=headers, + params=params, + data=data, + ) response = self.send(request, stream=True, **kwargs) return response - - def delete_document(self, + def delete_document( + self, db: str, doc_id: str, *, - if_match: str = None, - batch: str = None, - rev: str = None, - **kwargs + if_match: Optional[str] = None, + batch: Optional[str] = None, + rev: Optional[str] = None, + **kwargs, ) -> DetailedResponse: """ Delete a document. @@ -1872,8 +2048,8 @@ def delete_document(self, :param str db: Path parameter to specify the database name. :param str doc_id: Path parameter to specify the document ID. - :param str if_match: (optional) Header parameter to specify the document - revision. Alternative to rev query parameter. + :param str if_match: (optional) Header parameter for a conditional HTTP + request matching an ETag. :param str batch: (optional) Query parameter to specify whether to store in batch mode. The server will respond with a HTTP 202 Accepted response code immediately. @@ -1883,21 +2059,23 @@ def delete_document(self, :rtype: DetailedResponse with `dict` result representing a `DocumentResult` object """ - if db is None: + if not db: raise ValueError('db must be provided') - if doc_id is None: + if not doc_id: raise ValueError('doc_id must be provided') headers = { - 'If-Match': if_match + 'If-Match': if_match, } - sdk_headers = get_sdk_headers(service_name=self.DEFAULT_SERVICE_NAME, - service_version='V1', - operation_id='delete_document') + sdk_headers = get_sdk_headers( + service_name=self.DEFAULT_SERVICE_NAME, + service_version='V1', + operation_id='delete_document', + ) headers.update(sdk_headers) params = { 'batch': batch, - 'rev': rev + 'rev': rev, } if 'headers' in kwargs: @@ -1909,31 +2087,33 @@ def delete_document(self, path_param_values = self.encode_path_vars(db, doc_id) path_param_dict = dict(zip(path_param_keys, path_param_values)) url = '/{db}/{doc_id}'.format(**path_param_dict) - request = self.prepare_request(method='DELETE', - url=url, - headers=headers, - params=params) + request = self.prepare_request( + method='DELETE', + url=url, + headers=headers, + params=params, + ) response = self.send(request, **kwargs) return response - - def get_document(self, + def get_document( + self, db: str, doc_id: str, *, - if_none_match: str = None, - attachments: bool = None, - att_encoding_info: bool = None, - conflicts: bool = None, - deleted_conflicts: bool = None, - latest: bool = None, - local_seq: bool = None, - meta: bool = None, - rev: str = None, - revs: bool = None, - revs_info: bool = None, - **kwargs + if_none_match: Optional[str] = None, + attachments: Optional[bool] = None, + att_encoding_info: Optional[bool] = None, + conflicts: Optional[bool] = None, + deleted_conflicts: Optional[bool] = None, + latest: Optional[bool] = None, + local_seq: Optional[bool] = None, + meta: Optional[bool] = None, + rev: Optional[str] = None, + revs: Optional[bool] = None, + revs_info: Optional[bool] = None, + **kwargs, ) -> DetailedResponse: """ Retrieve a document. @@ -1944,16 +2124,20 @@ def get_document(self, :param str db: Path parameter to specify the database name. :param str doc_id: Path parameter to specify the document ID. - :param str if_none_match: (optional) Header parameter to specify a double - quoted document revision token for cache control. + :param str if_none_match: (optional) Header parameter for a conditional + HTTP request not matching an ETag. :param bool attachments: (optional) Query parameter to specify whether to - include attachments bodies in a response. + include attachment content in the response. Note that when used with a + view-style query or changes feed this only applies when requesting + documents in the response. :param bool att_encoding_info: (optional) Query parameter to specify - whether to include the encoding information in attachment stubs if the - particular attachment is compressed. + whether to include the encoding information for compressed attachments. + Note that when used with a view-style query or changes feed this only + applies when requesting documents in the response. :param bool conflicts: (optional) Query parameter to specify whether to - include a list of conflicted revisions in each returned document. Active - only when `include_docs` is `true`. + include a list of conflicted revisions in each returned document. Note that + when used with a view-style query or changes feed this only applies when + requesting documents in the response. :param bool deleted_conflicts: (optional) Query parameter to specify whether to include a list of deleted conflicted revisions in the `_deleted_conflicts` property of the returned document. @@ -1974,16 +2158,18 @@ def get_document(self, :rtype: DetailedResponse with `dict` result representing a `Document` object """ - if db is None: + if not db: raise ValueError('db must be provided') - if doc_id is None: + if not doc_id: raise ValueError('doc_id must be provided') headers = { - 'If-None-Match': if_none_match + 'If-None-Match': if_none_match, } - sdk_headers = get_sdk_headers(service_name=self.DEFAULT_SERVICE_NAME, - service_version='V1', - operation_id='get_document') + sdk_headers = get_sdk_headers( + service_name=self.DEFAULT_SERVICE_NAME, + service_version='V1', + operation_id='get_document', + ) headers.update(sdk_headers) params = { @@ -1996,7 +2182,7 @@ def get_document(self, 'meta': meta, 'rev': rev, 'revs': revs, - 'revs_info': revs_info + 'revs_info': revs_info, } if 'headers' in kwargs: @@ -2008,31 +2194,33 @@ def get_document(self, path_param_values = self.encode_path_vars(db, doc_id) path_param_dict = dict(zip(path_param_keys, path_param_values)) url = '/{db}/{doc_id}'.format(**path_param_dict) - request = self.prepare_request(method='GET', - url=url, - headers=headers, - params=params) + request = self.prepare_request( + method='GET', + url=url, + headers=headers, + params=params, + ) response = self.send(request, **kwargs) return response - - def get_document_as_mixed(self, + def get_document_as_mixed( + self, db: str, doc_id: str, *, - if_none_match: str = None, - attachments: bool = None, - att_encoding_info: bool = None, - conflicts: bool = None, - deleted_conflicts: bool = None, - latest: bool = None, - local_seq: bool = None, - meta: bool = None, - rev: str = None, - revs: bool = None, - revs_info: bool = None, - **kwargs + if_none_match: Optional[str] = None, + attachments: Optional[bool] = None, + att_encoding_info: Optional[bool] = None, + conflicts: Optional[bool] = None, + deleted_conflicts: Optional[bool] = None, + latest: Optional[bool] = None, + local_seq: Optional[bool] = None, + meta: Optional[bool] = None, + rev: Optional[str] = None, + revs: Optional[bool] = None, + revs_info: Optional[bool] = None, + **kwargs, ) -> DetailedResponse: """ Retrieve a document as mixed. @@ -2043,16 +2231,20 @@ def get_document_as_mixed(self, :param str db: Path parameter to specify the database name. :param str doc_id: Path parameter to specify the document ID. - :param str if_none_match: (optional) Header parameter to specify a double - quoted document revision token for cache control. + :param str if_none_match: (optional) Header parameter for a conditional + HTTP request not matching an ETag. :param bool attachments: (optional) Query parameter to specify whether to - include attachments bodies in a response. + include attachment content in the response. Note that when used with a + view-style query or changes feed this only applies when requesting + documents in the response. :param bool att_encoding_info: (optional) Query parameter to specify - whether to include the encoding information in attachment stubs if the - particular attachment is compressed. + whether to include the encoding information for compressed attachments. + Note that when used with a view-style query or changes feed this only + applies when requesting documents in the response. :param bool conflicts: (optional) Query parameter to specify whether to - include a list of conflicted revisions in each returned document. Active - only when `include_docs` is `true`. + include a list of conflicted revisions in each returned document. Note that + when used with a view-style query or changes feed this only applies when + requesting documents in the response. :param bool deleted_conflicts: (optional) Query parameter to specify whether to include a list of deleted conflicted revisions in the `_deleted_conflicts` property of the returned document. @@ -2073,16 +2265,18 @@ def get_document_as_mixed(self, :rtype: DetailedResponse with `BinaryIO` result """ - if db is None: + if not db: raise ValueError('db must be provided') - if doc_id is None: + if not doc_id: raise ValueError('doc_id must be provided') headers = { - 'If-None-Match': if_none_match + 'If-None-Match': if_none_match, } - sdk_headers = get_sdk_headers(service_name=self.DEFAULT_SERVICE_NAME, - service_version='V1', - operation_id='get_document_as_mixed') + sdk_headers = get_sdk_headers( + service_name=self.DEFAULT_SERVICE_NAME, + service_version='V1', + operation_id='get_document_as_mixed', + ) headers.update(sdk_headers) params = { @@ -2095,7 +2289,7 @@ def get_document_as_mixed(self, 'meta': meta, 'rev': rev, 'revs': revs, - 'revs_info': revs_info + 'revs_info': revs_info, } if 'headers' in kwargs: @@ -2107,31 +2301,33 @@ def get_document_as_mixed(self, path_param_values = self.encode_path_vars(db, doc_id) path_param_dict = dict(zip(path_param_keys, path_param_values)) url = '/{db}/{doc_id}'.format(**path_param_dict) - request = self.prepare_request(method='GET', - url=url, - headers=headers, - params=params) + request = self.prepare_request( + method='GET', + url=url, + headers=headers, + params=params, + ) response = self.send(request, **kwargs) return response - - def get_document_as_related(self, + def get_document_as_related( + self, db: str, doc_id: str, *, - if_none_match: str = None, - attachments: bool = None, - att_encoding_info: bool = None, - conflicts: bool = None, - deleted_conflicts: bool = None, - latest: bool = None, - local_seq: bool = None, - meta: bool = None, - rev: str = None, - revs: bool = None, - revs_info: bool = None, - **kwargs + if_none_match: Optional[str] = None, + attachments: Optional[bool] = None, + att_encoding_info: Optional[bool] = None, + conflicts: Optional[bool] = None, + deleted_conflicts: Optional[bool] = None, + latest: Optional[bool] = None, + local_seq: Optional[bool] = None, + meta: Optional[bool] = None, + rev: Optional[str] = None, + revs: Optional[bool] = None, + revs_info: Optional[bool] = None, + **kwargs, ) -> DetailedResponse: """ Retrieve a document as related. @@ -2142,16 +2338,20 @@ def get_document_as_related(self, :param str db: Path parameter to specify the database name. :param str doc_id: Path parameter to specify the document ID. - :param str if_none_match: (optional) Header parameter to specify a double - quoted document revision token for cache control. + :param str if_none_match: (optional) Header parameter for a conditional + HTTP request not matching an ETag. :param bool attachments: (optional) Query parameter to specify whether to - include attachments bodies in a response. + include attachment content in the response. Note that when used with a + view-style query or changes feed this only applies when requesting + documents in the response. :param bool att_encoding_info: (optional) Query parameter to specify - whether to include the encoding information in attachment stubs if the - particular attachment is compressed. + whether to include the encoding information for compressed attachments. + Note that when used with a view-style query or changes feed this only + applies when requesting documents in the response. :param bool conflicts: (optional) Query parameter to specify whether to - include a list of conflicted revisions in each returned document. Active - only when `include_docs` is `true`. + include a list of conflicted revisions in each returned document. Note that + when used with a view-style query or changes feed this only applies when + requesting documents in the response. :param bool deleted_conflicts: (optional) Query parameter to specify whether to include a list of deleted conflicted revisions in the `_deleted_conflicts` property of the returned document. @@ -2172,16 +2372,18 @@ def get_document_as_related(self, :rtype: DetailedResponse with `BinaryIO` result """ - if db is None: + if not db: raise ValueError('db must be provided') - if doc_id is None: + if not doc_id: raise ValueError('doc_id must be provided') headers = { - 'If-None-Match': if_none_match + 'If-None-Match': if_none_match, } - sdk_headers = get_sdk_headers(service_name=self.DEFAULT_SERVICE_NAME, - service_version='V1', - operation_id='get_document_as_related') + sdk_headers = get_sdk_headers( + service_name=self.DEFAULT_SERVICE_NAME, + service_version='V1', + operation_id='get_document_as_related', + ) headers.update(sdk_headers) params = { @@ -2194,7 +2396,7 @@ def get_document_as_related(self, 'meta': meta, 'rev': rev, 'revs': revs, - 'revs_info': revs_info + 'revs_info': revs_info, } if 'headers' in kwargs: @@ -2206,31 +2408,33 @@ def get_document_as_related(self, path_param_values = self.encode_path_vars(db, doc_id) path_param_dict = dict(zip(path_param_keys, path_param_values)) url = '/{db}/{doc_id}'.format(**path_param_dict) - request = self.prepare_request(method='GET', - url=url, - headers=headers, - params=params) + request = self.prepare_request( + method='GET', + url=url, + headers=headers, + params=params, + ) response = self.send(request, **kwargs) return response - - def get_document_as_stream(self, + def get_document_as_stream( + self, db: str, doc_id: str, *, - if_none_match: str = None, - attachments: bool = None, - att_encoding_info: bool = None, - conflicts: bool = None, - deleted_conflicts: bool = None, - latest: bool = None, - local_seq: bool = None, - meta: bool = None, - rev: str = None, - revs: bool = None, - revs_info: bool = None, - **kwargs + if_none_match: Optional[str] = None, + attachments: Optional[bool] = None, + att_encoding_info: Optional[bool] = None, + conflicts: Optional[bool] = None, + deleted_conflicts: Optional[bool] = None, + latest: Optional[bool] = None, + local_seq: Optional[bool] = None, + meta: Optional[bool] = None, + rev: Optional[str] = None, + revs: Optional[bool] = None, + revs_info: Optional[bool] = None, + **kwargs, ) -> DetailedResponse: """ Retrieve a document as stream. @@ -2241,16 +2445,20 @@ def get_document_as_stream(self, :param str db: Path parameter to specify the database name. :param str doc_id: Path parameter to specify the document ID. - :param str if_none_match: (optional) Header parameter to specify a double - quoted document revision token for cache control. + :param str if_none_match: (optional) Header parameter for a conditional + HTTP request not matching an ETag. :param bool attachments: (optional) Query parameter to specify whether to - include attachments bodies in a response. + include attachment content in the response. Note that when used with a + view-style query or changes feed this only applies when requesting + documents in the response. :param bool att_encoding_info: (optional) Query parameter to specify - whether to include the encoding information in attachment stubs if the - particular attachment is compressed. + whether to include the encoding information for compressed attachments. + Note that when used with a view-style query or changes feed this only + applies when requesting documents in the response. :param bool conflicts: (optional) Query parameter to specify whether to - include a list of conflicted revisions in each returned document. Active - only when `include_docs` is `true`. + include a list of conflicted revisions in each returned document. Note that + when used with a view-style query or changes feed this only applies when + requesting documents in the response. :param bool deleted_conflicts: (optional) Query parameter to specify whether to include a list of deleted conflicted revisions in the `_deleted_conflicts` property of the returned document. @@ -2271,16 +2479,18 @@ def get_document_as_stream(self, :rtype: DetailedResponse with `BinaryIO` result """ - if db is None: + if not db: raise ValueError('db must be provided') - if doc_id is None: + if not doc_id: raise ValueError('doc_id must be provided') headers = { - 'If-None-Match': if_none_match + 'If-None-Match': if_none_match, } - sdk_headers = get_sdk_headers(service_name=self.DEFAULT_SERVICE_NAME, - service_version='V1', - operation_id='get_document_as_stream') + sdk_headers = get_sdk_headers( + service_name=self.DEFAULT_SERVICE_NAME, + service_version='V1', + operation_id='get_document_as_stream', + ) headers.update(sdk_headers) params = { @@ -2293,7 +2503,7 @@ def get_document_as_stream(self, 'meta': meta, 'rev': rev, 'revs': revs, - 'revs_info': revs_info + 'revs_info': revs_info, } if 'headers' in kwargs: @@ -2305,26 +2515,28 @@ def get_document_as_stream(self, path_param_values = self.encode_path_vars(db, doc_id) path_param_dict = dict(zip(path_param_keys, path_param_values)) url = '/{db}/{doc_id}'.format(**path_param_dict) - request = self.prepare_request(method='GET', - url=url, - headers=headers, - params=params) + request = self.prepare_request( + method='GET', + url=url, + headers=headers, + params=params, + ) response = self.send(request, stream=True, **kwargs) return response - - def put_document(self, + def put_document( + self, db: str, doc_id: str, document: Union['Document', BinaryIO], *, - content_type: str = None, - if_match: str = None, - batch: str = None, - new_edits: bool = None, - rev: str = None, - **kwargs + content_type: Optional[str] = None, + if_match: Optional[str] = None, + batch: Optional[str] = None, + new_edits: Optional[bool] = None, + rev: Optional[str] = None, + **kwargs, ) -> DetailedResponse: """ Create or modify a document. @@ -2332,14 +2544,14 @@ def put_document(self, Creates or modifies a document in the specified database. For creation, you must specify the document ID but you should not specify the revision. - For modification, you must specify the document ID and a revision identifier. + For modification, you must specify the document ID and a revision identifier. :param str db: Path parameter to specify the database name. :param str doc_id: Path parameter to specify the document ID. :param Document document: HTTP request body for Document operations. :param str content_type: (optional) The type of the input. - :param str if_match: (optional) Header parameter to specify the document - revision. Alternative to rev query parameter. + :param str if_match: (optional) Header parameter for a conditional HTTP + request matching an ETag. :param str batch: (optional) Query parameter to specify whether to store in batch mode. The server will respond with a HTTP 202 Accepted response code immediately. @@ -2348,15 +2560,18 @@ def put_document(self, well-formed _rev must be included in the document. False is used by the replicator to insert documents into the target database even if that leads to the creation of conflicts. + Avoid using this parameter, since this option applies document revisions + without checking for conflicts, so it is very easy to accidentally end up + with a large number of conflicts. :param str rev: (optional) Query parameter to specify a document revision. :param dict headers: A `dict` containing the request headers :return: A `DetailedResponse` containing the result, headers and HTTP status code. :rtype: DetailedResponse with `dict` result representing a `DocumentResult` object """ - if db is None: + if not db: raise ValueError('db must be provided') - if doc_id is None: + if not doc_id: raise ValueError('doc_id must be provided') if document is None: raise ValueError('document must be provided') @@ -2365,17 +2580,19 @@ def put_document(self, content_type = content_type or 'application/json' headers = { 'Content-Type': content_type, - 'If-Match': if_match + 'If-Match': if_match, } - sdk_headers = get_sdk_headers(service_name=self.DEFAULT_SERVICE_NAME, - service_version='V1', - operation_id='put_document') + sdk_headers = get_sdk_headers( + service_name=self.DEFAULT_SERVICE_NAME, + service_version='V1', + operation_id='put_document', + ) headers.update(sdk_headers) params = { 'batch': batch, 'new_edits': new_edits, - 'rev': rev + 'rev': rev, } if isinstance(document, dict): @@ -2394,11 +2611,13 @@ def put_document(self, path_param_values = self.encode_path_vars(db, doc_id) path_param_dict = dict(zip(path_param_keys, path_param_values)) url = '/{db}/{doc_id}'.format(**path_param_dict) - request = self.prepare_request(method='PUT', - url=url, - headers=headers, - params=params, - data=data) + request = self.prepare_request( + method='PUT', + url=url, + headers=headers, + params=params, + data=data, + ) response = self.send(request, **kwargs) return response @@ -2407,13 +2626,13 @@ def put_document(self, # Design Documents ######################### - - def head_design_document(self, + def head_design_document( + self, db: str, ddoc: str, *, - if_none_match: str = None, - **kwargs + if_none_match: Optional[str] = None, + **kwargs, ) -> DetailedResponse: """ Retrieve the HTTP headers for a design document. @@ -2430,23 +2649,25 @@ def head_design_document(self, :param str ddoc: Path parameter to specify the design document name. The design document name is the design document ID excluding the `_design/` prefix. - :param str if_none_match: (optional) Header parameter to specify a double - quoted document revision token for cache control. + :param str if_none_match: (optional) Header parameter for a conditional + HTTP request not matching an ETag. :param dict headers: A `dict` containing the request headers :return: A `DetailedResponse` containing the result, headers and HTTP status code. :rtype: DetailedResponse """ - if db is None: + if not db: raise ValueError('db must be provided') - if ddoc is None: + if not ddoc: raise ValueError('ddoc must be provided') headers = { - 'If-None-Match': if_none_match + 'If-None-Match': if_none_match, } - sdk_headers = get_sdk_headers(service_name=self.DEFAULT_SERVICE_NAME, - service_version='V1', - operation_id='head_design_document') + sdk_headers = get_sdk_headers( + service_name=self.DEFAULT_SERVICE_NAME, + service_version='V1', + operation_id='head_design_document', + ) headers.update(sdk_headers) if 'headers' in kwargs: @@ -2457,22 +2678,24 @@ def head_design_document(self, path_param_values = self.encode_path_vars(db, ddoc) path_param_dict = dict(zip(path_param_keys, path_param_values)) url = '/{db}/_design/{ddoc}'.format(**path_param_dict) - request = self.prepare_request(method='HEAD', - url=url, - headers=headers) + request = self.prepare_request( + method='HEAD', + url=url, + headers=headers, + ) response = self.send(request, **kwargs) return response - - def delete_design_document(self, + def delete_design_document( + self, db: str, ddoc: str, *, - if_match: str = None, - batch: str = None, - rev: str = None, - **kwargs + if_match: Optional[str] = None, + batch: Optional[str] = None, + rev: Optional[str] = None, + **kwargs, ) -> DetailedResponse: """ Delete a design document. @@ -2487,8 +2710,8 @@ def delete_design_document(self, :param str ddoc: Path parameter to specify the design document name. The design document name is the design document ID excluding the `_design/` prefix. - :param str if_match: (optional) Header parameter to specify the document - revision. Alternative to rev query parameter. + :param str if_match: (optional) Header parameter for a conditional HTTP + request matching an ETag. :param str batch: (optional) Query parameter to specify whether to store in batch mode. The server will respond with a HTTP 202 Accepted response code immediately. @@ -2498,21 +2721,23 @@ def delete_design_document(self, :rtype: DetailedResponse with `dict` result representing a `DocumentResult` object """ - if db is None: + if not db: raise ValueError('db must be provided') - if ddoc is None: + if not ddoc: raise ValueError('ddoc must be provided') headers = { - 'If-Match': if_match + 'If-Match': if_match, } - sdk_headers = get_sdk_headers(service_name=self.DEFAULT_SERVICE_NAME, - service_version='V1', - operation_id='delete_design_document') + sdk_headers = get_sdk_headers( + service_name=self.DEFAULT_SERVICE_NAME, + service_version='V1', + operation_id='delete_design_document', + ) headers.update(sdk_headers) params = { 'batch': batch, - 'rev': rev + 'rev': rev, } if 'headers' in kwargs: @@ -2524,31 +2749,33 @@ def delete_design_document(self, path_param_values = self.encode_path_vars(db, ddoc) path_param_dict = dict(zip(path_param_keys, path_param_values)) url = '/{db}/_design/{ddoc}'.format(**path_param_dict) - request = self.prepare_request(method='DELETE', - url=url, - headers=headers, - params=params) + request = self.prepare_request( + method='DELETE', + url=url, + headers=headers, + params=params, + ) response = self.send(request, **kwargs) return response - - def get_design_document(self, + def get_design_document( + self, db: str, ddoc: str, *, - if_none_match: str = None, - attachments: bool = None, - att_encoding_info: bool = None, - conflicts: bool = None, - deleted_conflicts: bool = None, - latest: bool = None, - local_seq: bool = None, - meta: bool = None, - rev: str = None, - revs: bool = None, - revs_info: bool = None, - **kwargs + if_none_match: Optional[str] = None, + attachments: Optional[bool] = None, + att_encoding_info: Optional[bool] = None, + conflicts: Optional[bool] = None, + deleted_conflicts: Optional[bool] = None, + latest: Optional[bool] = None, + local_seq: Optional[bool] = None, + meta: Optional[bool] = None, + rev: Optional[str] = None, + revs: Optional[bool] = None, + revs_info: Optional[bool] = None, + **kwargs, ) -> DetailedResponse: """ Retrieve a design document. @@ -2561,16 +2788,20 @@ def get_design_document(self, :param str ddoc: Path parameter to specify the design document name. The design document name is the design document ID excluding the `_design/` prefix. - :param str if_none_match: (optional) Header parameter to specify a double - quoted document revision token for cache control. + :param str if_none_match: (optional) Header parameter for a conditional + HTTP request not matching an ETag. :param bool attachments: (optional) Query parameter to specify whether to - include attachments bodies in a response. + include attachment content in the response. Note that when used with a + view-style query or changes feed this only applies when requesting + documents in the response. :param bool att_encoding_info: (optional) Query parameter to specify - whether to include the encoding information in attachment stubs if the - particular attachment is compressed. + whether to include the encoding information for compressed attachments. + Note that when used with a view-style query or changes feed this only + applies when requesting documents in the response. :param bool conflicts: (optional) Query parameter to specify whether to - include a list of conflicted revisions in each returned document. Active - only when `include_docs` is `true`. + include a list of conflicted revisions in each returned document. Note that + when used with a view-style query or changes feed this only applies when + requesting documents in the response. :param bool deleted_conflicts: (optional) Query parameter to specify whether to include a list of deleted conflicted revisions in the `_deleted_conflicts` property of the returned document. @@ -2591,16 +2822,18 @@ def get_design_document(self, :rtype: DetailedResponse with `dict` result representing a `DesignDocument` object """ - if db is None: + if not db: raise ValueError('db must be provided') - if ddoc is None: + if not ddoc: raise ValueError('ddoc must be provided') headers = { - 'If-None-Match': if_none_match + 'If-None-Match': if_none_match, } - sdk_headers = get_sdk_headers(service_name=self.DEFAULT_SERVICE_NAME, - service_version='V1', - operation_id='get_design_document') + sdk_headers = get_sdk_headers( + service_name=self.DEFAULT_SERVICE_NAME, + service_version='V1', + operation_id='get_design_document', + ) headers.update(sdk_headers) params = { @@ -2613,7 +2846,7 @@ def get_design_document(self, 'meta': meta, 'rev': rev, 'revs': revs, - 'revs_info': revs_info + 'revs_info': revs_info, } if 'headers' in kwargs: @@ -2625,25 +2858,27 @@ def get_design_document(self, path_param_values = self.encode_path_vars(db, ddoc) path_param_dict = dict(zip(path_param_keys, path_param_values)) url = '/{db}/_design/{ddoc}'.format(**path_param_dict) - request = self.prepare_request(method='GET', - url=url, - headers=headers, - params=params) + request = self.prepare_request( + method='GET', + url=url, + headers=headers, + params=params, + ) response = self.send(request, **kwargs) return response - - def put_design_document(self, + def put_design_document( + self, db: str, ddoc: str, design_document: 'DesignDocument', *, - if_match: str = None, - batch: str = None, - new_edits: bool = None, - rev: str = None, - **kwargs + if_match: Optional[str] = None, + batch: Optional[str] = None, + new_edits: Optional[bool] = None, + rev: Optional[str] = None, + **kwargs, ) -> DetailedResponse: """ Create or modify a design document. @@ -2657,8 +2892,8 @@ def put_design_document(self, prefix. :param DesignDocument design_document: HTTP request body for DesignDocument operations. - :param str if_match: (optional) Header parameter to specify the document - revision. Alternative to rev query parameter. + :param str if_match: (optional) Header parameter for a conditional HTTP + request matching an ETag. :param str batch: (optional) Query parameter to specify whether to store in batch mode. The server will respond with a HTTP 202 Accepted response code immediately. @@ -2667,32 +2902,37 @@ def put_design_document(self, well-formed _rev must be included in the document. False is used by the replicator to insert documents into the target database even if that leads to the creation of conflicts. + Avoid using this parameter, since this option applies document revisions + without checking for conflicts, so it is very easy to accidentally end up + with a large number of conflicts. :param str rev: (optional) Query parameter to specify a document revision. :param dict headers: A `dict` containing the request headers :return: A `DetailedResponse` containing the result, headers and HTTP status code. :rtype: DetailedResponse with `dict` result representing a `DocumentResult` object """ - if db is None: + if not db: raise ValueError('db must be provided') - if ddoc is None: + if not ddoc: raise ValueError('ddoc must be provided') if design_document is None: raise ValueError('design_document must be provided') if isinstance(design_document, DesignDocument): design_document = convert_model(design_document) headers = { - 'If-Match': if_match + 'If-Match': if_match, } - sdk_headers = get_sdk_headers(service_name=self.DEFAULT_SERVICE_NAME, - service_version='V1', - operation_id='put_design_document') + sdk_headers = get_sdk_headers( + service_name=self.DEFAULT_SERVICE_NAME, + service_version='V1', + operation_id='put_design_document', + ) headers.update(sdk_headers) params = { 'batch': batch, 'new_edits': new_edits, - 'rev': rev + 'rev': rev, } data = json.dumps(design_document) @@ -2707,20 +2947,22 @@ def put_design_document(self, path_param_values = self.encode_path_vars(db, ddoc) path_param_dict = dict(zip(path_param_keys, path_param_values)) url = '/{db}/_design/{ddoc}'.format(**path_param_dict) - request = self.prepare_request(method='PUT', - url=url, - headers=headers, - params=params, - data=data) + request = self.prepare_request( + method='PUT', + url=url, + headers=headers, + params=params, + data=data, + ) response = self.send(request, **kwargs) return response - - def get_design_document_information(self, + def get_design_document_information( + self, db: str, ddoc: str, - **kwargs + **kwargs, ) -> DetailedResponse: """ Retrieve information about a design document. @@ -2738,14 +2980,16 @@ def get_design_document_information(self, :rtype: DetailedResponse with `dict` result representing a `DesignDocumentInformation` object """ - if db is None: + if not db: raise ValueError('db must be provided') - if ddoc is None: + if not ddoc: raise ValueError('ddoc must be provided') headers = {} - sdk_headers = get_sdk_headers(service_name=self.DEFAULT_SERVICE_NAME, - service_version='V1', - operation_id='get_design_document_information') + sdk_headers = get_sdk_headers( + service_name=self.DEFAULT_SERVICE_NAME, + service_version='V1', + operation_id='get_design_document_information', + ) headers.update(sdk_headers) if 'headers' in kwargs: @@ -2757,32 +3001,33 @@ def get_design_document_information(self, path_param_values = self.encode_path_vars(db, ddoc) path_param_dict = dict(zip(path_param_keys, path_param_values)) url = '/{db}/_design/{ddoc}/_info'.format(**path_param_dict) - request = self.prepare_request(method='GET', - url=url, - headers=headers) + request = self.prepare_request( + method='GET', + url=url, + headers=headers, + ) response = self.send(request, **kwargs) return response - - def post_design_docs(self, + def post_design_docs( + self, db: str, *, - att_encoding_info: bool = None, - attachments: bool = None, - conflicts: bool = None, - descending: bool = None, - include_docs: bool = None, - inclusive_end: bool = None, - limit: int = None, - skip: int = None, - update_seq: bool = None, - end_key: str = None, - key: str = None, - keys: List[str] = None, - start_key: str = None, - accept: str = None, - **kwargs + att_encoding_info: Optional[bool] = None, + attachments: Optional[bool] = None, + conflicts: Optional[bool] = None, + descending: Optional[bool] = None, + include_docs: Optional[bool] = None, + inclusive_end: Optional[bool] = None, + limit: Optional[int] = None, + skip: Optional[int] = None, + update_seq: Optional[bool] = None, + end_key: Optional[str] = None, + key: Optional[str] = None, + keys: Optional[List[str]] = None, + start_key: Optional[str] = None, + **kwargs, ) -> DetailedResponse: """ Query a list of all design documents in a database. @@ -2796,19 +3041,20 @@ def post_design_docs(self, :param str db: Path parameter to specify the database name. :param bool att_encoding_info: (optional) Parameter to specify whether to - include the encoding information in attachment stubs if the particular - attachment is compressed. + include the encoding information for compressed attachments. This only + applies when requesting documents in the response. :param bool attachments: (optional) Parameter to specify whether to include - attachments bodies in a response. + attachment content in included document content or only the attachment + metadata. This only applies when requesting documents in the response. :param bool conflicts: (optional) Parameter to specify whether to include a - list of conflicted revisions in each returned document. Active only when - `include_docs` is `true`. + list of conflicted revisions in each returned document. This only applies + when requesting documents in the response. :param bool descending: (optional) Parameter to specify whether to return the documents in descending by key order. :param bool include_docs: (optional) Parameter to specify whether to include the full content of the documents in the response. - :param bool inclusive_end: (optional) Parameter to specify whether the - specified end key should be included in the result. + :param bool inclusive_end: (optional) Parameter to specify whether to + include the specified end key in the result. :param int limit: (optional) Parameter to specify the number of returned documents to limit the result to. :param int skip: (optional) Parameter to specify the number of records @@ -2820,21 +3066,19 @@ def post_design_docs(self, :param str key: (optional) Schema for a document ID. :param List[str] keys: (optional) Schema for a list of document IDs. :param str start_key: (optional) Schema for a document ID. - :param str accept: (optional) The type of the response: application/json or - application/octet-stream. :param dict headers: A `dict` containing the request headers :return: A `DetailedResponse` containing the result, headers and HTTP status code. :rtype: DetailedResponse with `dict` result representing a `AllDocsResult` object """ - if db is None: + if not db: raise ValueError('db must be provided') - headers = { - 'Accept': accept - } - sdk_headers = get_sdk_headers(service_name=self.DEFAULT_SERVICE_NAME, - service_version='V1', - operation_id='post_design_docs') + headers = {} + sdk_headers = get_sdk_headers( + service_name=self.DEFAULT_SERVICE_NAME, + service_version='V1', + operation_id='post_design_docs', + ) headers.update(sdk_headers) data = { @@ -2850,7 +3094,7 @@ def post_design_docs(self, 'end_key': end_key, 'key': key, 'keys': keys, - 'start_key': start_key + 'start_key': start_key, } data = {k: v for (k, v) in data.items() if v is not None} data = json.dumps(data) @@ -2859,26 +3103,29 @@ def post_design_docs(self, if 'headers' in kwargs: headers.update(kwargs.get('headers')) del kwargs['headers'] + headers['Accept'] = 'application/json' path_param_keys = ['db'] path_param_values = self.encode_path_vars(db) path_param_dict = dict(zip(path_param_keys, path_param_values)) url = '/{db}/_design_docs'.format(**path_param_dict) - request = self.prepare_request(method='POST', - url=url, - headers=headers, - data=data) + request = self.prepare_request( + method='POST', + url=url, + headers=headers, + data=data, + ) response = self.send(request, **kwargs) return response - - def post_design_docs_queries(self, + def post_design_docs_queries( + self, db: str, queries: List['AllDocsQuery'], *, - accept: str = None, - **kwargs + accept: Optional[str] = None, + **kwargs, ) -> DetailedResponse: """ Multi-query the list of all design documents. @@ -2899,21 +3146,23 @@ def post_design_docs_queries(self, :rtype: DetailedResponse with `dict` result representing a `AllDocsQueriesResult` object """ - if db is None: + if not db: raise ValueError('db must be provided') if queries is None: raise ValueError('queries must be provided') queries = [convert_model(x) for x in queries] headers = { - 'Accept': accept + 'Accept': accept, } - sdk_headers = get_sdk_headers(service_name=self.DEFAULT_SERVICE_NAME, - service_version='V1', - operation_id='post_design_docs_queries') + sdk_headers = get_sdk_headers( + service_name=self.DEFAULT_SERVICE_NAME, + service_version='V1', + operation_id='post_design_docs_queries', + ) headers.update(sdk_headers) data = { - 'queries': queries + 'queries': queries, } data = {k: v for (k, v) in data.items() if v is not None} data = json.dumps(data) @@ -2927,10 +3176,12 @@ def post_design_docs_queries(self, path_param_values = self.encode_path_vars(db) path_param_dict = dict(zip(path_param_keys, path_param_values)) url = '/{db}/_design_docs/queries'.format(**path_param_dict) - request = self.prepare_request(method='POST', - url=url, - headers=headers, - data=data) + request = self.prepare_request( + method='POST', + url=url, + headers=headers, + data=data, + ) response = self.send(request, **kwargs) return response @@ -2939,33 +3190,33 @@ def post_design_docs_queries(self, # Views ######################### - - def post_view(self, + def post_view( + self, db: str, ddoc: str, view: str, *, - att_encoding_info: bool = None, - attachments: bool = None, - conflicts: bool = None, - descending: bool = None, - include_docs: bool = None, - inclusive_end: bool = None, - limit: int = None, - skip: int = None, - update_seq: bool = None, - end_key: object = None, - end_key_doc_id: str = None, - group: bool = None, - group_level: int = None, - key: object = None, - keys: List[object] = None, - reduce: bool = None, - stable: bool = None, - start_key: object = None, - start_key_doc_id: str = None, - update: str = None, - **kwargs + att_encoding_info: Optional[bool] = None, + attachments: Optional[bool] = None, + conflicts: Optional[bool] = None, + descending: Optional[bool] = None, + include_docs: Optional[bool] = None, + inclusive_end: Optional[bool] = None, + limit: Optional[int] = None, + skip: Optional[int] = None, + update_seq: Optional[bool] = None, + end_key: Optional[object] = None, + end_key_doc_id: Optional[str] = None, + group: Optional[bool] = None, + group_level: Optional[int] = None, + key: Optional[object] = None, + keys: Optional[List[object]] = None, + reduce: Optional[bool] = None, + stable: Optional[bool] = None, + start_key: Optional[object] = None, + start_key_doc_id: Optional[str] = None, + update: Optional[str] = None, + **kwargs, ) -> DetailedResponse: """ Query a MapReduce view. @@ -2984,19 +3235,20 @@ def post_view(self, :param str view: Path parameter to specify the map reduce view function name. :param bool att_encoding_info: (optional) Parameter to specify whether to - include the encoding information in attachment stubs if the particular - attachment is compressed. + include the encoding information for compressed attachments. This only + applies when requesting documents in the response. :param bool attachments: (optional) Parameter to specify whether to include - attachments bodies in a response. + attachment content in included document content or only the attachment + metadata. This only applies when requesting documents in the response. :param bool conflicts: (optional) Parameter to specify whether to include a - list of conflicted revisions in each returned document. Active only when - `include_docs` is `true`. + list of conflicted revisions in each returned document. This only applies + when requesting documents in the response. :param bool descending: (optional) Parameter to specify whether to return the documents in descending by key order. :param bool include_docs: (optional) Parameter to specify whether to include the full content of the documents in the response. - :param bool inclusive_end: (optional) Parameter to specify whether the - specified end key should be included in the result. + :param bool inclusive_end: (optional) Parameter to specify whether to + include the specified end key in the result. :param int limit: (optional) Parameter to specify the number of returned documents to limit the result to. :param int skip: (optional) Parameter to specify the number of records @@ -3005,7 +3257,8 @@ def post_view(self, in the response an update_seq value indicating the sequence id of the database the view reflects. :param object end_key: (optional) Schema for any JSON type. - :param str end_key_doc_id: (optional) Schema for a document ID. + :param str end_key_doc_id: (optional) Stop returning rows at the specified + document ID. No effect if using `group` or not providing end key. :param bool group: (optional) Parameter to specify whether to group reduced results by key. Valid only if a reduce function defined in the view. If the view emits key in JSON array format, then it is possible to reduce groups @@ -3016,24 +3269,32 @@ def post_view(self, group is `true`. Group level groups the reduced results by the specified number of array elements. If unset, results are grouped by the entire array key, returning a reduced value for each complete key. - :param object key: (optional) Schema for any JSON type. + :param object key: (optional) Parameter to specify to return only rows that + match the specified key. String representation of any JSON type that + matches the key type emitted by the view function. :param List[object] keys: (optional) Parameter to specify returning only - documents that match any of the specified keys. A JSON array of keys that - match the key type emitted by the view function. + rows that match any of the specified keys. A JSON array of keys that match + the key type emitted by the view function. :param bool reduce: (optional) Parameter to specify whether to use the reduce function in a map-reduce view. Default is true when a reduce function is defined. + A default `reduce` view type can be disabled to behave like a + `map` by setting `reduce=false` explicitly. + Be aware that `include_docs=true` is only for `map` views. Reduced views do + not include document IDs in view rows. :param bool stable: (optional) Query parameter to specify whether use the - same replica of the index on each request. The default value `false` - contacts all replicas and returns the result from the first, fastest, + same replica of the index on each request. The default value `false` + contacts all replicas and returns the result from the first, fastest, responder. Setting it to `true` when used in conjunction with - `update=false` may improve consistency at the expense of increased latency + `update=false` may improve consistency at the expense of increased latency and decreased throughput if the selected replica is not the fastest of the - available replicas. + available replicas. **Note:** In general setting `true` is discouraged and is strictly not recommended when using `update=true`. :param object start_key: (optional) Schema for any JSON type. - :param str start_key_doc_id: (optional) Schema for a document ID. + :param str start_key_doc_id: (optional) Start returning rows at the + specified document ID. No effect if using `group` or not providing start + key. :param str update: (optional) Parameter to specify whether or not the view in question should be updated prior to responding to the user. * `true` - Return results after the view is updated. @@ -3045,16 +3306,18 @@ def post_view(self, :rtype: DetailedResponse with `dict` result representing a `ViewResult` object """ - if db is None: + if not db: raise ValueError('db must be provided') - if ddoc is None: + if not ddoc: raise ValueError('ddoc must be provided') - if view is None: + if not view: raise ValueError('view must be provided') headers = {} - sdk_headers = get_sdk_headers(service_name=self.DEFAULT_SERVICE_NAME, - service_version='V1', - operation_id='post_view') + sdk_headers = get_sdk_headers( + service_name=self.DEFAULT_SERVICE_NAME, + service_version='V1', + operation_id='post_view', + ) headers.update(sdk_headers) data = { @@ -3077,7 +3340,7 @@ def post_view(self, 'stable': stable, 'start_key': start_key, 'start_key_doc_id': start_key_doc_id, - 'update': update + 'update': update, } data = {k: v for (k, v) in data.items() if v is not None} data = json.dumps(data) @@ -3092,41 +3355,43 @@ def post_view(self, path_param_values = self.encode_path_vars(db, ddoc, view) path_param_dict = dict(zip(path_param_keys, path_param_values)) url = '/{db}/_design/{ddoc}/_view/{view}'.format(**path_param_dict) - request = self.prepare_request(method='POST', - url=url, - headers=headers, - data=data) + request = self.prepare_request( + method='POST', + url=url, + headers=headers, + data=data, + ) response = self.send(request, **kwargs) return response - - def post_view_as_stream(self, + def post_view_as_stream( + self, db: str, ddoc: str, view: str, *, - att_encoding_info: bool = None, - attachments: bool = None, - conflicts: bool = None, - descending: bool = None, - include_docs: bool = None, - inclusive_end: bool = None, - limit: int = None, - skip: int = None, - update_seq: bool = None, - end_key: object = None, - end_key_doc_id: str = None, - group: bool = None, - group_level: int = None, - key: object = None, - keys: List[object] = None, - reduce: bool = None, - stable: bool = None, - start_key: object = None, - start_key_doc_id: str = None, - update: str = None, - **kwargs + att_encoding_info: Optional[bool] = None, + attachments: Optional[bool] = None, + conflicts: Optional[bool] = None, + descending: Optional[bool] = None, + include_docs: Optional[bool] = None, + inclusive_end: Optional[bool] = None, + limit: Optional[int] = None, + skip: Optional[int] = None, + update_seq: Optional[bool] = None, + end_key: Optional[object] = None, + end_key_doc_id: Optional[str] = None, + group: Optional[bool] = None, + group_level: Optional[int] = None, + key: Optional[object] = None, + keys: Optional[List[object]] = None, + reduce: Optional[bool] = None, + stable: Optional[bool] = None, + start_key: Optional[object] = None, + start_key_doc_id: Optional[str] = None, + update: Optional[str] = None, + **kwargs, ) -> DetailedResponse: """ Query a MapReduce view as stream. @@ -3145,19 +3410,20 @@ def post_view_as_stream(self, :param str view: Path parameter to specify the map reduce view function name. :param bool att_encoding_info: (optional) Parameter to specify whether to - include the encoding information in attachment stubs if the particular - attachment is compressed. + include the encoding information for compressed attachments. This only + applies when requesting documents in the response. :param bool attachments: (optional) Parameter to specify whether to include - attachments bodies in a response. + attachment content in included document content or only the attachment + metadata. This only applies when requesting documents in the response. :param bool conflicts: (optional) Parameter to specify whether to include a - list of conflicted revisions in each returned document. Active only when - `include_docs` is `true`. + list of conflicted revisions in each returned document. This only applies + when requesting documents in the response. :param bool descending: (optional) Parameter to specify whether to return the documents in descending by key order. :param bool include_docs: (optional) Parameter to specify whether to include the full content of the documents in the response. - :param bool inclusive_end: (optional) Parameter to specify whether the - specified end key should be included in the result. + :param bool inclusive_end: (optional) Parameter to specify whether to + include the specified end key in the result. :param int limit: (optional) Parameter to specify the number of returned documents to limit the result to. :param int skip: (optional) Parameter to specify the number of records @@ -3166,7 +3432,8 @@ def post_view_as_stream(self, in the response an update_seq value indicating the sequence id of the database the view reflects. :param object end_key: (optional) Schema for any JSON type. - :param str end_key_doc_id: (optional) Schema for a document ID. + :param str end_key_doc_id: (optional) Stop returning rows at the specified + document ID. No effect if using `group` or not providing end key. :param bool group: (optional) Parameter to specify whether to group reduced results by key. Valid only if a reduce function defined in the view. If the view emits key in JSON array format, then it is possible to reduce groups @@ -3177,24 +3444,32 @@ def post_view_as_stream(self, group is `true`. Group level groups the reduced results by the specified number of array elements. If unset, results are grouped by the entire array key, returning a reduced value for each complete key. - :param object key: (optional) Schema for any JSON type. + :param object key: (optional) Parameter to specify to return only rows that + match the specified key. String representation of any JSON type that + matches the key type emitted by the view function. :param List[object] keys: (optional) Parameter to specify returning only - documents that match any of the specified keys. A JSON array of keys that - match the key type emitted by the view function. + rows that match any of the specified keys. A JSON array of keys that match + the key type emitted by the view function. :param bool reduce: (optional) Parameter to specify whether to use the reduce function in a map-reduce view. Default is true when a reduce function is defined. + A default `reduce` view type can be disabled to behave like a + `map` by setting `reduce=false` explicitly. + Be aware that `include_docs=true` is only for `map` views. Reduced views do + not include document IDs in view rows. :param bool stable: (optional) Query parameter to specify whether use the - same replica of the index on each request. The default value `false` - contacts all replicas and returns the result from the first, fastest, + same replica of the index on each request. The default value `false` + contacts all replicas and returns the result from the first, fastest, responder. Setting it to `true` when used in conjunction with - `update=false` may improve consistency at the expense of increased latency + `update=false` may improve consistency at the expense of increased latency and decreased throughput if the selected replica is not the fastest of the - available replicas. + available replicas. **Note:** In general setting `true` is discouraged and is strictly not recommended when using `update=true`. :param object start_key: (optional) Schema for any JSON type. - :param str start_key_doc_id: (optional) Schema for a document ID. + :param str start_key_doc_id: (optional) Start returning rows at the + specified document ID. No effect if using `group` or not providing start + key. :param str update: (optional) Parameter to specify whether or not the view in question should be updated prior to responding to the user. * `true` - Return results after the view is updated. @@ -3206,16 +3481,18 @@ def post_view_as_stream(self, :rtype: DetailedResponse with `BinaryIO` result """ - if db is None: + if not db: raise ValueError('db must be provided') - if ddoc is None: + if not ddoc: raise ValueError('ddoc must be provided') - if view is None: + if not view: raise ValueError('view must be provided') headers = {} - sdk_headers = get_sdk_headers(service_name=self.DEFAULT_SERVICE_NAME, - service_version='V1', - operation_id='post_view_as_stream') + sdk_headers = get_sdk_headers( + service_name=self.DEFAULT_SERVICE_NAME, + service_version='V1', + operation_id='post_view_as_stream', + ) headers.update(sdk_headers) data = { @@ -3238,7 +3515,7 @@ def post_view_as_stream(self, 'stable': stable, 'start_key': start_key, 'start_key_doc_id': start_key_doc_id, - 'update': update + 'update': update, } data = {k: v for (k, v) in data.items() if v is not None} data = json.dumps(data) @@ -3253,21 +3530,23 @@ def post_view_as_stream(self, path_param_values = self.encode_path_vars(db, ddoc, view) path_param_dict = dict(zip(path_param_keys, path_param_values)) url = '/{db}/_design/{ddoc}/_view/{view}'.format(**path_param_dict) - request = self.prepare_request(method='POST', - url=url, - headers=headers, - data=data) + request = self.prepare_request( + method='POST', + url=url, + headers=headers, + data=data, + ) response = self.send(request, stream=True, **kwargs) return response - - def post_view_queries(self, + def post_view_queries( + self, db: str, ddoc: str, view: str, queries: List['ViewQuery'], - **kwargs + **kwargs, ) -> DetailedResponse: """ Multi-query a MapReduce view. @@ -3290,23 +3569,25 @@ def post_view_queries(self, :rtype: DetailedResponse with `dict` result representing a `ViewQueriesResult` object """ - if db is None: + if not db: raise ValueError('db must be provided') - if ddoc is None: + if not ddoc: raise ValueError('ddoc must be provided') - if view is None: + if not view: raise ValueError('view must be provided') if queries is None: raise ValueError('queries must be provided') queries = [convert_model(x) for x in queries] headers = {} - sdk_headers = get_sdk_headers(service_name=self.DEFAULT_SERVICE_NAME, - service_version='V1', - operation_id='post_view_queries') + sdk_headers = get_sdk_headers( + service_name=self.DEFAULT_SERVICE_NAME, + service_version='V1', + operation_id='post_view_queries', + ) headers.update(sdk_headers) data = { - 'queries': queries + 'queries': queries, } data = {k: v for (k, v) in data.items() if v is not None} data = json.dumps(data) @@ -3321,21 +3602,23 @@ def post_view_queries(self, path_param_values = self.encode_path_vars(db, ddoc, view) path_param_dict = dict(zip(path_param_keys, path_param_values)) url = '/{db}/_design/{ddoc}/_view/{view}/queries'.format(**path_param_dict) - request = self.prepare_request(method='POST', - url=url, - headers=headers, - data=data) + request = self.prepare_request( + method='POST', + url=url, + headers=headers, + data=data, + ) response = self.send(request, **kwargs) return response - - def post_view_queries_as_stream(self, + def post_view_queries_as_stream( + self, db: str, ddoc: str, view: str, queries: List['ViewQuery'], - **kwargs + **kwargs, ) -> DetailedResponse: """ Multi-query a MapReduce view as stream. @@ -3358,23 +3641,25 @@ def post_view_queries_as_stream(self, :rtype: DetailedResponse with `BinaryIO` result """ - if db is None: + if not db: raise ValueError('db must be provided') - if ddoc is None: + if not ddoc: raise ValueError('ddoc must be provided') - if view is None: + if not view: raise ValueError('view must be provided') if queries is None: raise ValueError('queries must be provided') queries = [convert_model(x) for x in queries] headers = {} - sdk_headers = get_sdk_headers(service_name=self.DEFAULT_SERVICE_NAME, - service_version='V1', - operation_id='post_view_queries_as_stream') + sdk_headers = get_sdk_headers( + service_name=self.DEFAULT_SERVICE_NAME, + service_version='V1', + operation_id='post_view_queries_as_stream', + ) headers.update(sdk_headers) data = { - 'queries': queries + 'queries': queries, } data = {k: v for (k, v) in data.items() if v is not None} data = json.dumps(data) @@ -3389,10 +3674,12 @@ def post_view_queries_as_stream(self, path_param_values = self.encode_path_vars(db, ddoc, view) path_param_dict = dict(zip(path_param_keys, path_param_values)) url = '/{db}/_design/{ddoc}/_view/{view}/queries'.format(**path_param_dict) - request = self.prepare_request(method='POST', - url=url, - headers=headers, - data=data) + request = self.prepare_request( + method='POST', + url=url, + headers=headers, + data=data, + ) response = self.send(request, stream=True, **kwargs) return response @@ -3401,11 +3688,11 @@ def post_view_queries_as_stream(self, # Queries ######################### - - def get_partition_information(self, + def get_partition_information( + self, db: str, partition_key: str, - **kwargs + **kwargs, ) -> DetailedResponse: """ Retrieve information about a database partition. @@ -3421,14 +3708,16 @@ def get_partition_information(self, :rtype: DetailedResponse with `dict` result representing a `PartitionInformation` object """ - if db is None: + if not db: raise ValueError('db must be provided') - if partition_key is None: + if not partition_key: raise ValueError('partition_key must be provided') headers = {} - sdk_headers = get_sdk_headers(service_name=self.DEFAULT_SERVICE_NAME, - service_version='V1', - operation_id='get_partition_information') + sdk_headers = get_sdk_headers( + service_name=self.DEFAULT_SERVICE_NAME, + service_version='V1', + operation_id='get_partition_information', + ) headers.update(sdk_headers) if 'headers' in kwargs: @@ -3440,32 +3729,34 @@ def get_partition_information(self, path_param_values = self.encode_path_vars(db, partition_key) path_param_dict = dict(zip(path_param_keys, path_param_values)) url = '/{db}/_partition/{partition_key}'.format(**path_param_dict) - request = self.prepare_request(method='GET', - url=url, - headers=headers) + request = self.prepare_request( + method='GET', + url=url, + headers=headers, + ) response = self.send(request, **kwargs) return response - - def post_partition_all_docs(self, + def post_partition_all_docs( + self, db: str, partition_key: str, *, - att_encoding_info: bool = None, - attachments: bool = None, - conflicts: bool = None, - descending: bool = None, - include_docs: bool = None, - inclusive_end: bool = None, - limit: int = None, - skip: int = None, - update_seq: bool = None, - end_key: str = None, - key: str = None, - keys: List[str] = None, - start_key: str = None, - **kwargs + att_encoding_info: Optional[bool] = None, + attachments: Optional[bool] = None, + conflicts: Optional[bool] = None, + descending: Optional[bool] = None, + include_docs: Optional[bool] = None, + inclusive_end: Optional[bool] = None, + limit: Optional[int] = None, + skip: Optional[int] = None, + update_seq: Optional[bool] = None, + end_key: Optional[str] = None, + key: Optional[str] = None, + keys: Optional[List[str]] = None, + start_key: Optional[str] = None, + **kwargs, ) -> DetailedResponse: """ Query a list of all documents in a database partition. @@ -3481,19 +3772,20 @@ def post_partition_all_docs(self, :param str partition_key: Path parameter to specify the database partition key. :param bool att_encoding_info: (optional) Parameter to specify whether to - include the encoding information in attachment stubs if the particular - attachment is compressed. + include the encoding information for compressed attachments. This only + applies when requesting documents in the response. :param bool attachments: (optional) Parameter to specify whether to include - attachments bodies in a response. + attachment content in included document content or only the attachment + metadata. This only applies when requesting documents in the response. :param bool conflicts: (optional) Parameter to specify whether to include a - list of conflicted revisions in each returned document. Active only when - `include_docs` is `true`. + list of conflicted revisions in each returned document. This only applies + when requesting documents in the response. :param bool descending: (optional) Parameter to specify whether to return the documents in descending by key order. :param bool include_docs: (optional) Parameter to specify whether to include the full content of the documents in the response. - :param bool inclusive_end: (optional) Parameter to specify whether the - specified end key should be included in the result. + :param bool inclusive_end: (optional) Parameter to specify whether to + include the specified end key in the result. :param int limit: (optional) Parameter to specify the number of returned documents to limit the result to. :param int skip: (optional) Parameter to specify the number of records @@ -3510,14 +3802,16 @@ def post_partition_all_docs(self, :rtype: DetailedResponse with `dict` result representing a `AllDocsResult` object """ - if db is None: + if not db: raise ValueError('db must be provided') - if partition_key is None: + if not partition_key: raise ValueError('partition_key must be provided') headers = {} - sdk_headers = get_sdk_headers(service_name=self.DEFAULT_SERVICE_NAME, - service_version='V1', - operation_id='post_partition_all_docs') + sdk_headers = get_sdk_headers( + service_name=self.DEFAULT_SERVICE_NAME, + service_version='V1', + operation_id='post_partition_all_docs', + ) headers.update(sdk_headers) data = { @@ -3533,7 +3827,7 @@ def post_partition_all_docs(self, 'end_key': end_key, 'key': key, 'keys': keys, - 'start_key': start_key + 'start_key': start_key, } data = {k: v for (k, v) in data.items() if v is not None} data = json.dumps(data) @@ -3548,33 +3842,35 @@ def post_partition_all_docs(self, path_param_values = self.encode_path_vars(db, partition_key) path_param_dict = dict(zip(path_param_keys, path_param_values)) url = '/{db}/_partition/{partition_key}/_all_docs'.format(**path_param_dict) - request = self.prepare_request(method='POST', - url=url, - headers=headers, - data=data) + request = self.prepare_request( + method='POST', + url=url, + headers=headers, + data=data, + ) response = self.send(request, **kwargs) return response - - def post_partition_all_docs_as_stream(self, + def post_partition_all_docs_as_stream( + self, db: str, partition_key: str, *, - att_encoding_info: bool = None, - attachments: bool = None, - conflicts: bool = None, - descending: bool = None, - include_docs: bool = None, - inclusive_end: bool = None, - limit: int = None, - skip: int = None, - update_seq: bool = None, - end_key: str = None, - key: str = None, - keys: List[str] = None, - start_key: str = None, - **kwargs + att_encoding_info: Optional[bool] = None, + attachments: Optional[bool] = None, + conflicts: Optional[bool] = None, + descending: Optional[bool] = None, + include_docs: Optional[bool] = None, + inclusive_end: Optional[bool] = None, + limit: Optional[int] = None, + skip: Optional[int] = None, + update_seq: Optional[bool] = None, + end_key: Optional[str] = None, + key: Optional[str] = None, + keys: Optional[List[str]] = None, + start_key: Optional[str] = None, + **kwargs, ) -> DetailedResponse: """ Query a list of all documents in a database partition as stream. @@ -3590,19 +3886,20 @@ def post_partition_all_docs_as_stream(self, :param str partition_key: Path parameter to specify the database partition key. :param bool att_encoding_info: (optional) Parameter to specify whether to - include the encoding information in attachment stubs if the particular - attachment is compressed. + include the encoding information for compressed attachments. This only + applies when requesting documents in the response. :param bool attachments: (optional) Parameter to specify whether to include - attachments bodies in a response. + attachment content in included document content or only the attachment + metadata. This only applies when requesting documents in the response. :param bool conflicts: (optional) Parameter to specify whether to include a - list of conflicted revisions in each returned document. Active only when - `include_docs` is `true`. + list of conflicted revisions in each returned document. This only applies + when requesting documents in the response. :param bool descending: (optional) Parameter to specify whether to return the documents in descending by key order. :param bool include_docs: (optional) Parameter to specify whether to include the full content of the documents in the response. - :param bool inclusive_end: (optional) Parameter to specify whether the - specified end key should be included in the result. + :param bool inclusive_end: (optional) Parameter to specify whether to + include the specified end key in the result. :param int limit: (optional) Parameter to specify the number of returned documents to limit the result to. :param int skip: (optional) Parameter to specify the number of records @@ -3619,14 +3916,16 @@ def post_partition_all_docs_as_stream(self, :rtype: DetailedResponse with `BinaryIO` result """ - if db is None: + if not db: raise ValueError('db must be provided') - if partition_key is None: + if not partition_key: raise ValueError('partition_key must be provided') headers = {} - sdk_headers = get_sdk_headers(service_name=self.DEFAULT_SERVICE_NAME, - service_version='V1', - operation_id='post_partition_all_docs_as_stream') + sdk_headers = get_sdk_headers( + service_name=self.DEFAULT_SERVICE_NAME, + service_version='V1', + operation_id='post_partition_all_docs_as_stream', + ) headers.update(sdk_headers) data = { @@ -3642,7 +3941,7 @@ def post_partition_all_docs_as_stream(self, 'end_key': end_key, 'key': key, 'keys': keys, - 'start_key': start_key + 'start_key': start_key, } data = {k: v for (k, v) in data.items() if v is not None} data = json.dumps(data) @@ -3657,34 +3956,36 @@ def post_partition_all_docs_as_stream(self, path_param_values = self.encode_path_vars(db, partition_key) path_param_dict = dict(zip(path_param_keys, path_param_values)) url = '/{db}/_partition/{partition_key}/_all_docs'.format(**path_param_dict) - request = self.prepare_request(method='POST', - url=url, - headers=headers, - data=data) + request = self.prepare_request( + method='POST', + url=url, + headers=headers, + data=data, + ) response = self.send(request, stream=True, **kwargs) return response - - def post_partition_search(self, + def post_partition_search( + self, db: str, partition_key: str, ddoc: str, index: str, query: str, *, - bookmark: str = None, - highlight_fields: List[str] = None, - highlight_number: int = None, - highlight_post_tag: str = None, - highlight_pre_tag: str = None, - highlight_size: int = None, - include_docs: bool = None, - include_fields: List[str] = None, - limit: int = None, - sort: List[str] = None, - stale: str = None, - **kwargs + bookmark: Optional[str] = None, + highlight_fields: Optional[List[str]] = None, + highlight_number: Optional[int] = None, + highlight_post_tag: Optional[str] = None, + highlight_pre_tag: Optional[str] = None, + highlight_size: Optional[int] = None, + include_docs: Optional[bool] = None, + include_fields: Optional[List[str]] = None, + limit: Optional[int] = None, + sort: Optional[List[str]] = None, + stale: Optional[str] = None, + **kwargs, ) -> DetailedResponse: """ Query a database partition search index. @@ -3693,6 +3994,9 @@ def post_partition_search(self, databases to be queried by using Lucene Query Parser Syntax. Search indexes are defined by an index function, similar to a map function in MapReduce views. The index function decides what data to index and store in the index. + Before using read the + [FAQs](https://cloud.ibm.com/docs/Cloudant?topic=Cloudant-database-partitioning#partition-querying) + to understand the limitations and appropriate use cases. :param str db: Path parameter to specify the database name. :param str partition_key: Path parameter to specify the database partition @@ -3725,7 +4029,7 @@ def post_partition_search(self, the specified number. :param List[str] sort: (optional) Specifies the sort order of the results. In a grouped search (when group_field is used), this parameter specifies - the sort order within a group. The default sort order is relevance. A JSON + the sort order within a group. The default sort order is relevance. A JSON string of the form "fieldname<type>" or "-fieldname<type>" for descending order, where fieldname is the name of a string or number field, and type is either a number, a string, or a JSON array of strings. The type @@ -3741,20 +4045,22 @@ def post_partition_search(self, :rtype: DetailedResponse with `dict` result representing a `SearchResult` object """ - if db is None: + if not db: raise ValueError('db must be provided') - if partition_key is None: + if not partition_key: raise ValueError('partition_key must be provided') - if ddoc is None: + if not ddoc: raise ValueError('ddoc must be provided') - if index is None: + if not index: raise ValueError('index must be provided') if query is None: raise ValueError('query must be provided') headers = {} - sdk_headers = get_sdk_headers(service_name=self.DEFAULT_SERVICE_NAME, - service_version='V1', - operation_id='post_partition_search') + sdk_headers = get_sdk_headers( + service_name=self.DEFAULT_SERVICE_NAME, + service_version='V1', + operation_id='post_partition_search', + ) headers.update(sdk_headers) data = { @@ -3769,7 +4075,7 @@ def post_partition_search(self, 'include_fields': include_fields, 'limit': limit, 'sort': sort, - 'stale': stale + 'stale': stale, } data = {k: v for (k, v) in data.items() if v is not None} data = json.dumps(data) @@ -3784,34 +4090,36 @@ def post_partition_search(self, path_param_values = self.encode_path_vars(db, partition_key, ddoc, index) path_param_dict = dict(zip(path_param_keys, path_param_values)) url = '/{db}/_partition/{partition_key}/_design/{ddoc}/_search/{index}'.format(**path_param_dict) - request = self.prepare_request(method='POST', - url=url, - headers=headers, - data=data) + request = self.prepare_request( + method='POST', + url=url, + headers=headers, + data=data, + ) response = self.send(request, **kwargs) return response - - def post_partition_search_as_stream(self, + def post_partition_search_as_stream( + self, db: str, partition_key: str, ddoc: str, index: str, query: str, *, - bookmark: str = None, - highlight_fields: List[str] = None, - highlight_number: int = None, - highlight_post_tag: str = None, - highlight_pre_tag: str = None, - highlight_size: int = None, - include_docs: bool = None, - include_fields: List[str] = None, - limit: int = None, - sort: List[str] = None, - stale: str = None, - **kwargs + bookmark: Optional[str] = None, + highlight_fields: Optional[List[str]] = None, + highlight_number: Optional[int] = None, + highlight_post_tag: Optional[str] = None, + highlight_pre_tag: Optional[str] = None, + highlight_size: Optional[int] = None, + include_docs: Optional[bool] = None, + include_fields: Optional[List[str]] = None, + limit: Optional[int] = None, + sort: Optional[List[str]] = None, + stale: Optional[str] = None, + **kwargs, ) -> DetailedResponse: """ Query a database partition search index as stream. @@ -3820,6 +4128,9 @@ def post_partition_search_as_stream(self, databases to be queried by using Lucene Query Parser Syntax. Search indexes are defined by an index function, similar to a map function in MapReduce views. The index function decides what data to index and store in the index. + Before using read the + [FAQs](https://cloud.ibm.com/docs/Cloudant?topic=Cloudant-database-partitioning#partition-querying) + to understand the limitations and appropriate use cases. :param str db: Path parameter to specify the database name. :param str partition_key: Path parameter to specify the database partition @@ -3852,7 +4163,7 @@ def post_partition_search_as_stream(self, the specified number. :param List[str] sort: (optional) Specifies the sort order of the results. In a grouped search (when group_field is used), this parameter specifies - the sort order within a group. The default sort order is relevance. A JSON + the sort order within a group. The default sort order is relevance. A JSON string of the form "fieldname<type>" or "-fieldname<type>" for descending order, where fieldname is the name of a string or number field, and type is either a number, a string, or a JSON array of strings. The type @@ -3868,20 +4179,22 @@ def post_partition_search_as_stream(self, :rtype: DetailedResponse with `BinaryIO` result """ - if db is None: + if not db: raise ValueError('db must be provided') - if partition_key is None: + if not partition_key: raise ValueError('partition_key must be provided') - if ddoc is None: + if not ddoc: raise ValueError('ddoc must be provided') - if index is None: + if not index: raise ValueError('index must be provided') if query is None: raise ValueError('query must be provided') headers = {} - sdk_headers = get_sdk_headers(service_name=self.DEFAULT_SERVICE_NAME, - service_version='V1', - operation_id='post_partition_search_as_stream') + sdk_headers = get_sdk_headers( + service_name=self.DEFAULT_SERVICE_NAME, + service_version='V1', + operation_id='post_partition_search_as_stream', + ) headers.update(sdk_headers) data = { @@ -3896,7 +4209,7 @@ def post_partition_search_as_stream(self, 'include_fields': include_fields, 'limit': limit, 'sort': sort, - 'stale': stale + 'stale': stale, } data = {k: v for (k, v) in data.items() if v is not None} data = json.dumps(data) @@ -3911,42 +4224,43 @@ def post_partition_search_as_stream(self, path_param_values = self.encode_path_vars(db, partition_key, ddoc, index) path_param_dict = dict(zip(path_param_keys, path_param_values)) url = '/{db}/_partition/{partition_key}/_design/{ddoc}/_search/{index}'.format(**path_param_dict) - request = self.prepare_request(method='POST', - url=url, - headers=headers, - data=data) + request = self.prepare_request( + method='POST', + url=url, + headers=headers, + data=data, + ) response = self.send(request, stream=True, **kwargs) return response - - def post_partition_view(self, + def post_partition_view( + self, db: str, partition_key: str, ddoc: str, view: str, *, - att_encoding_info: bool = None, - attachments: bool = None, - conflicts: bool = None, - descending: bool = None, - include_docs: bool = None, - inclusive_end: bool = None, - limit: int = None, - skip: int = None, - update_seq: bool = None, - end_key: object = None, - end_key_doc_id: str = None, - group: bool = None, - group_level: int = None, - key: object = None, - keys: List[object] = None, - reduce: bool = None, - stable: bool = None, - start_key: object = None, - start_key_doc_id: str = None, - update: str = None, - **kwargs + att_encoding_info: Optional[bool] = None, + attachments: Optional[bool] = None, + conflicts: Optional[bool] = None, + descending: Optional[bool] = None, + include_docs: Optional[bool] = None, + inclusive_end: Optional[bool] = None, + limit: Optional[int] = None, + skip: Optional[int] = None, + update_seq: Optional[bool] = None, + end_key: Optional[object] = None, + end_key_doc_id: Optional[str] = None, + group: Optional[bool] = None, + group_level: Optional[int] = None, + key: Optional[object] = None, + keys: Optional[List[object]] = None, + reduce: Optional[bool] = None, + start_key: Optional[object] = None, + start_key_doc_id: Optional[str] = None, + update: Optional[str] = None, + **kwargs, ) -> DetailedResponse: """ Query a database partition MapReduce view function. @@ -3956,6 +4270,9 @@ def post_partition_view(self, the specification of explicit keys to be retrieved from the view results. The remainder of the POST view functionality is identical to the `GET /{db}/_design/{ddoc}/_view/{view}` API. + Before using read the + [FAQs](https://cloud.ibm.com/docs/Cloudant?topic=Cloudant-database-partitioning#partition-querying) + to understand the limitations and appropriate use cases. :param str db: Path parameter to specify the database name. :param str partition_key: Path parameter to specify the database partition @@ -3966,19 +4283,20 @@ def post_partition_view(self, :param str view: Path parameter to specify the map reduce view function name. :param bool att_encoding_info: (optional) Parameter to specify whether to - include the encoding information in attachment stubs if the particular - attachment is compressed. + include the encoding information for compressed attachments. This only + applies when requesting documents in the response. :param bool attachments: (optional) Parameter to specify whether to include - attachments bodies in a response. + attachment content in included document content or only the attachment + metadata. This only applies when requesting documents in the response. :param bool conflicts: (optional) Parameter to specify whether to include a - list of conflicted revisions in each returned document. Active only when - `include_docs` is `true`. + list of conflicted revisions in each returned document. This only applies + when requesting documents in the response. :param bool descending: (optional) Parameter to specify whether to return the documents in descending by key order. :param bool include_docs: (optional) Parameter to specify whether to include the full content of the documents in the response. - :param bool inclusive_end: (optional) Parameter to specify whether the - specified end key should be included in the result. + :param bool inclusive_end: (optional) Parameter to specify whether to + include the specified end key in the result. :param int limit: (optional) Parameter to specify the number of returned documents to limit the result to. :param int skip: (optional) Parameter to specify the number of records @@ -3987,7 +4305,8 @@ def post_partition_view(self, in the response an update_seq value indicating the sequence id of the database the view reflects. :param object end_key: (optional) Schema for any JSON type. - :param str end_key_doc_id: (optional) Schema for a document ID. + :param str end_key_doc_id: (optional) Stop returning rows at the specified + document ID. No effect if using `group` or not providing end key. :param bool group: (optional) Parameter to specify whether to group reduced results by key. Valid only if a reduce function defined in the view. If the view emits key in JSON array format, then it is possible to reduce groups @@ -3998,24 +4317,23 @@ def post_partition_view(self, group is `true`. Group level groups the reduced results by the specified number of array elements. If unset, results are grouped by the entire array key, returning a reduced value for each complete key. - :param object key: (optional) Schema for any JSON type. + :param object key: (optional) Parameter to specify to return only rows that + match the specified key. String representation of any JSON type that + matches the key type emitted by the view function. :param List[object] keys: (optional) Parameter to specify returning only - documents that match any of the specified keys. A JSON array of keys that - match the key type emitted by the view function. + rows that match any of the specified keys. A JSON array of keys that match + the key type emitted by the view function. :param bool reduce: (optional) Parameter to specify whether to use the reduce function in a map-reduce view. Default is true when a reduce function is defined. - :param bool stable: (optional) Query parameter to specify whether use the - same replica of the index on each request. The default value `false` - contacts all replicas and returns the result from the first, fastest, - responder. Setting it to `true` when used in conjunction with - `update=false` may improve consistency at the expense of increased latency - and decreased throughput if the selected replica is not the fastest of the - available replicas. - **Note:** In general setting `true` is discouraged and is strictly not - recommended when using `update=true`. + A default `reduce` view type can be disabled to behave like a + `map` by setting `reduce=false` explicitly. + Be aware that `include_docs=true` is only for `map` views. Reduced views do + not include document IDs in view rows. :param object start_key: (optional) Schema for any JSON type. - :param str start_key_doc_id: (optional) Schema for a document ID. + :param str start_key_doc_id: (optional) Start returning rows at the + specified document ID. No effect if using `group` or not providing start + key. :param str update: (optional) Parameter to specify whether or not the view in question should be updated prior to responding to the user. * `true` - Return results after the view is updated. @@ -4027,18 +4345,20 @@ def post_partition_view(self, :rtype: DetailedResponse with `dict` result representing a `ViewResult` object """ - if db is None: + if not db: raise ValueError('db must be provided') - if partition_key is None: + if not partition_key: raise ValueError('partition_key must be provided') - if ddoc is None: + if not ddoc: raise ValueError('ddoc must be provided') - if view is None: + if not view: raise ValueError('view must be provided') headers = {} - sdk_headers = get_sdk_headers(service_name=self.DEFAULT_SERVICE_NAME, - service_version='V1', - operation_id='post_partition_view') + sdk_headers = get_sdk_headers( + service_name=self.DEFAULT_SERVICE_NAME, + service_version='V1', + operation_id='post_partition_view', + ) headers.update(sdk_headers) data = { @@ -4058,10 +4378,9 @@ def post_partition_view(self, 'key': key, 'keys': keys, 'reduce': reduce, - 'stable': stable, 'start_key': start_key, 'start_key_doc_id': start_key_doc_id, - 'update': update + 'update': update, } data = {k: v for (k, v) in data.items() if v is not None} data = json.dumps(data) @@ -4076,42 +4395,43 @@ def post_partition_view(self, path_param_values = self.encode_path_vars(db, partition_key, ddoc, view) path_param_dict = dict(zip(path_param_keys, path_param_values)) url = '/{db}/_partition/{partition_key}/_design/{ddoc}/_view/{view}'.format(**path_param_dict) - request = self.prepare_request(method='POST', - url=url, - headers=headers, - data=data) + request = self.prepare_request( + method='POST', + url=url, + headers=headers, + data=data, + ) response = self.send(request, **kwargs) return response - - def post_partition_view_as_stream(self, + def post_partition_view_as_stream( + self, db: str, partition_key: str, ddoc: str, view: str, *, - att_encoding_info: bool = None, - attachments: bool = None, - conflicts: bool = None, - descending: bool = None, - include_docs: bool = None, - inclusive_end: bool = None, - limit: int = None, - skip: int = None, - update_seq: bool = None, - end_key: object = None, - end_key_doc_id: str = None, - group: bool = None, - group_level: int = None, - key: object = None, - keys: List[object] = None, - reduce: bool = None, - stable: bool = None, - start_key: object = None, - start_key_doc_id: str = None, - update: str = None, - **kwargs + att_encoding_info: Optional[bool] = None, + attachments: Optional[bool] = None, + conflicts: Optional[bool] = None, + descending: Optional[bool] = None, + include_docs: Optional[bool] = None, + inclusive_end: Optional[bool] = None, + limit: Optional[int] = None, + skip: Optional[int] = None, + update_seq: Optional[bool] = None, + end_key: Optional[object] = None, + end_key_doc_id: Optional[str] = None, + group: Optional[bool] = None, + group_level: Optional[int] = None, + key: Optional[object] = None, + keys: Optional[List[object]] = None, + reduce: Optional[bool] = None, + start_key: Optional[object] = None, + start_key_doc_id: Optional[str] = None, + update: Optional[str] = None, + **kwargs, ) -> DetailedResponse: """ Query a database partition MapReduce view function as stream. @@ -4121,6 +4441,9 @@ def post_partition_view_as_stream(self, the specification of explicit keys to be retrieved from the view results. The remainder of the POST view functionality is identical to the `GET /{db}/_design/{ddoc}/_view/{view}` API. + Before using read the + [FAQs](https://cloud.ibm.com/docs/Cloudant?topic=Cloudant-database-partitioning#partition-querying) + to understand the limitations and appropriate use cases. :param str db: Path parameter to specify the database name. :param str partition_key: Path parameter to specify the database partition @@ -4131,19 +4454,20 @@ def post_partition_view_as_stream(self, :param str view: Path parameter to specify the map reduce view function name. :param bool att_encoding_info: (optional) Parameter to specify whether to - include the encoding information in attachment stubs if the particular - attachment is compressed. + include the encoding information for compressed attachments. This only + applies when requesting documents in the response. :param bool attachments: (optional) Parameter to specify whether to include - attachments bodies in a response. + attachment content in included document content or only the attachment + metadata. This only applies when requesting documents in the response. :param bool conflicts: (optional) Parameter to specify whether to include a - list of conflicted revisions in each returned document. Active only when - `include_docs` is `true`. + list of conflicted revisions in each returned document. This only applies + when requesting documents in the response. :param bool descending: (optional) Parameter to specify whether to return the documents in descending by key order. :param bool include_docs: (optional) Parameter to specify whether to include the full content of the documents in the response. - :param bool inclusive_end: (optional) Parameter to specify whether the - specified end key should be included in the result. + :param bool inclusive_end: (optional) Parameter to specify whether to + include the specified end key in the result. :param int limit: (optional) Parameter to specify the number of returned documents to limit the result to. :param int skip: (optional) Parameter to specify the number of records @@ -4152,7 +4476,8 @@ def post_partition_view_as_stream(self, in the response an update_seq value indicating the sequence id of the database the view reflects. :param object end_key: (optional) Schema for any JSON type. - :param str end_key_doc_id: (optional) Schema for a document ID. + :param str end_key_doc_id: (optional) Stop returning rows at the specified + document ID. No effect if using `group` or not providing end key. :param bool group: (optional) Parameter to specify whether to group reduced results by key. Valid only if a reduce function defined in the view. If the view emits key in JSON array format, then it is possible to reduce groups @@ -4163,24 +4488,23 @@ def post_partition_view_as_stream(self, group is `true`. Group level groups the reduced results by the specified number of array elements. If unset, results are grouped by the entire array key, returning a reduced value for each complete key. - :param object key: (optional) Schema for any JSON type. + :param object key: (optional) Parameter to specify to return only rows that + match the specified key. String representation of any JSON type that + matches the key type emitted by the view function. :param List[object] keys: (optional) Parameter to specify returning only - documents that match any of the specified keys. A JSON array of keys that - match the key type emitted by the view function. + rows that match any of the specified keys. A JSON array of keys that match + the key type emitted by the view function. :param bool reduce: (optional) Parameter to specify whether to use the reduce function in a map-reduce view. Default is true when a reduce function is defined. - :param bool stable: (optional) Query parameter to specify whether use the - same replica of the index on each request. The default value `false` - contacts all replicas and returns the result from the first, fastest, - responder. Setting it to `true` when used in conjunction with - `update=false` may improve consistency at the expense of increased latency - and decreased throughput if the selected replica is not the fastest of the - available replicas. - **Note:** In general setting `true` is discouraged and is strictly not - recommended when using `update=true`. + A default `reduce` view type can be disabled to behave like a + `map` by setting `reduce=false` explicitly. + Be aware that `include_docs=true` is only for `map` views. Reduced views do + not include document IDs in view rows. :param object start_key: (optional) Schema for any JSON type. - :param str start_key_doc_id: (optional) Schema for a document ID. + :param str start_key_doc_id: (optional) Start returning rows at the + specified document ID. No effect if using `group` or not providing start + key. :param str update: (optional) Parameter to specify whether or not the view in question should be updated prior to responding to the user. * `true` - Return results after the view is updated. @@ -4192,18 +4516,20 @@ def post_partition_view_as_stream(self, :rtype: DetailedResponse with `BinaryIO` result """ - if db is None: + if not db: raise ValueError('db must be provided') - if partition_key is None: + if not partition_key: raise ValueError('partition_key must be provided') - if ddoc is None: + if not ddoc: raise ValueError('ddoc must be provided') - if view is None: + if not view: raise ValueError('view must be provided') headers = {} - sdk_headers = get_sdk_headers(service_name=self.DEFAULT_SERVICE_NAME, - service_version='V1', - operation_id='post_partition_view_as_stream') + sdk_headers = get_sdk_headers( + service_name=self.DEFAULT_SERVICE_NAME, + service_version='V1', + operation_id='post_partition_view_as_stream', + ) headers.update(sdk_headers) data = { @@ -4223,10 +4549,9 @@ def post_partition_view_as_stream(self, 'key': key, 'keys': keys, 'reduce': reduce, - 'stable': stable, 'start_key': start_key, 'start_key_doc_id': start_key_doc_id, - 'update': update + 'update': update, } data = {k: v for (k, v) in data.items() if v is not None} data = json.dumps(data) @@ -4241,42 +4566,40 @@ def post_partition_view_as_stream(self, path_param_values = self.encode_path_vars(db, partition_key, ddoc, view) path_param_dict = dict(zip(path_param_keys, path_param_values)) url = '/{db}/_partition/{partition_key}/_design/{ddoc}/_view/{view}'.format(**path_param_dict) - request = self.prepare_request(method='POST', - url=url, - headers=headers, - data=data) + request = self.prepare_request( + method='POST', + url=url, + headers=headers, + data=data, + ) response = self.send(request, stream=True, **kwargs) return response - - def post_partition_find(self, + def post_partition_explain( + self, db: str, partition_key: str, selector: dict, *, - bookmark: str = None, - conflicts: bool = None, - execution_stats: bool = None, - fields: List[str] = None, - limit: int = None, - skip: int = None, - sort: List[dict] = None, - stable: bool = None, - update: str = None, - use_index: List[str] = None, - **kwargs + allow_fallback: Optional[bool] = None, + bookmark: Optional[str] = None, + conflicts: Optional[bool] = None, + execution_stats: Optional[bool] = None, + fields: Optional[List[str]] = None, + limit: Optional[int] = None, + skip: Optional[int] = None, + sort: Optional[List[dict]] = None, + stable: Optional[bool] = None, + update: Optional[str] = None, + use_index: Optional[List[str]] = None, + **kwargs, ) -> DetailedResponse: """ - Query a database partition index by using selector syntax. + Retrieve information about which partition index is used for a query. - Query documents by using a declarative JSON querying syntax. It's best practice to - create an appropriate index for all fields in selector by using the `_index` - endpoint. - Queries without an appropriate backing index will fallback to using the built-in - `_all_docs` index. This is not recommended because it has a noticeable performance - impact causing a full scan of the partition with each request. In this case the - response body will include a warning field recommending that an index is created. + Shows which index is being used by the query. Parameters are the same as the + [`/{db}/_partition/{partition_key}/_find` endpoint](#postpartitionfind-queries). :param str db: Path parameter to specify the database name. :param str partition_key: Path parameter to specify the database partition @@ -4297,21 +4620,28 @@ def post_partition_find(self, field. There are two core types of operators in the selector syntax: * Combination operators: applied at the topmost level of selection. They - are used to combine selectors. In addition to the common boolean operators - (`$and`, `$or`, `$not`, `$nor`) there are three combination operators: - `$all`, `$elemMatch`, and `$allMatch`. A combination operator takes a - single argument. The argument is either another selector, or an array of + are used to combine selectors. A combination operator takes a single + argument. The argument is either another selector, or an array of selectors. * Condition operators: are specific to a field, and are used to evaluate the value stored in that field. For instance, the basic `$eq` operator matches when the specified field contains a value that is equal to the supplied argument. - * Only equality operators such as `$eq`, `$gt`, `$gte`, `$lt`, and `$lte` - (but not `$ne`) can be used as the basis of a query. You should include at - least one of these in a selector. - For further reference see - [selector - syntax](https://cloud.ibm.com/docs/Cloudant?topic=Cloudant-query#selector-syntax). + It is important for query performance to use appropriate selectors: + * Only equality operators such as `$eq`, `$gt`, `$gte`, `$lt`, and + `$lte` (but not `$ne`) can be used as the basis of a query. You should + include at least one of these in a selector. + * Some operators such as `$not`, `$or`, `$in`, and `$regex` cannot be + answered from an index. For query selectors use these operators in + conjunction with equality operators or create and use a partial index to + reduce the number of documents that will need to be scanned. + See [the Cloudant + Docs](https://cloud.ibm.com/docs/Cloudant?topic=Cloudant-operators) for a + list of all available combination and conditional operators. + For further reference see [selector + syntax](https://cloud.ibm.com/docs/Cloudant?topic=Cloudant-selector-syntax). + :param bool allow_fallback: (optional) Whether to allow fallback to other + indexes. Default is true. :param str bookmark: (optional) Opaque bookmark token used when paginating results. :param bool conflicts: (optional) A boolean value that indicates whether or @@ -4322,7 +4652,7 @@ def post_partition_find(self, document lookups (when each document replica is fetched). :param List[str] fields: (optional) JSON array that uses the field syntax. Use this parameter to specify which fields of a document must be returned. - If it is omitted, the entire document is returned. + If it is omitted or empty, the entire document is returned. :param int limit: (optional) Maximum number of results returned. The `type: text` indexes are limited to 200 results when queried. :param int skip: (optional) Skip the first 'n' results, where 'n' is the @@ -4332,8 +4662,8 @@ def post_partition_find(self, field name and direction pair is the topmost level of sort. The second pair, if provided, is the next level of sort. The field can be any field, using dotted notation if desired for sub-document fields. - For example in JSON: `[{"fieldName1": "desc"}, {"fieldName2.subFieldName1": - "desc"}]` + For example in JSON: + `[{"fieldName1": "desc"}, {"fieldName2.subFieldName1": "desc"}]` When sorting with multiple fields, ensure that there is an index already defined with all the sort fields in the same order and each object in the sort array has a single key or at least one of the sort fields is included @@ -4344,27 +4674,39 @@ def post_partition_find(self, :param str update: (optional) Whether to update the index prior to returning the result. :param List[str] use_index: (optional) Use this option to identify a - specific index for query to run against, rather than by using the IBM - Cloudant Query algorithm to find the best index. + specific index to answer the query, rather than letting the IBM Cloudant + query planner choose an index. Specified as a two element array of design + document id followed by index name, for example `["my_design_doc", + "my_index"]`. + It’s recommended to specify indexes explicitly in your queries to prevent + existing queries being affected by new indexes that might get added later. + If the specified index doesn't exist or can't answer the query then the + server ignores the value and answers using another index or a full scan of + all documents. To change this behavior set `allow_fallback` to `false` and + the server responds instead with a `400` status code if the requested index + is unsuitable to answer the query. :param dict headers: A `dict` containing the request headers :return: A `DetailedResponse` containing the result, headers and HTTP status code. - :rtype: DetailedResponse with `dict` result representing a `FindResult` object + :rtype: DetailedResponse with `dict` result representing a `ExplainResult` object """ - if db is None: + if not db: raise ValueError('db must be provided') - if partition_key is None: + if not partition_key: raise ValueError('partition_key must be provided') if selector is None: raise ValueError('selector must be provided') headers = {} - sdk_headers = get_sdk_headers(service_name=self.DEFAULT_SERVICE_NAME, - service_version='V1', - operation_id='post_partition_find') + sdk_headers = get_sdk_headers( + service_name=self.DEFAULT_SERVICE_NAME, + service_version='V1', + operation_id='post_partition_explain', + ) headers.update(sdk_headers) data = { 'selector': selector, + 'allow_fallback': allow_fallback, 'bookmark': bookmark, 'conflicts': conflicts, 'execution_stats': execution_stats, @@ -4374,7 +4716,7 @@ def post_partition_find(self, 'sort': sort, 'stable': stable, 'update': update, - 'use_index': use_index + 'use_index': use_index, } data = {k: v for (k, v) in data.items() if v is not None} data = json.dumps(data) @@ -4388,43 +4730,54 @@ def post_partition_find(self, path_param_keys = ['db', 'partition_key'] path_param_values = self.encode_path_vars(db, partition_key) path_param_dict = dict(zip(path_param_keys, path_param_values)) - url = '/{db}/_partition/{partition_key}/_find'.format(**path_param_dict) - request = self.prepare_request(method='POST', - url=url, - headers=headers, - data=data) + url = '/{db}/_partition/{partition_key}/_explain'.format(**path_param_dict) + request = self.prepare_request( + method='POST', + url=url, + headers=headers, + data=data, + ) response = self.send(request, **kwargs) return response - - def post_partition_find_as_stream(self, + def post_partition_find( + self, db: str, partition_key: str, selector: dict, *, - bookmark: str = None, - conflicts: bool = None, - execution_stats: bool = None, - fields: List[str] = None, - limit: int = None, - skip: int = None, - sort: List[dict] = None, - stable: bool = None, - update: str = None, - use_index: List[str] = None, - **kwargs + allow_fallback: Optional[bool] = None, + bookmark: Optional[str] = None, + conflicts: Optional[bool] = None, + execution_stats: Optional[bool] = None, + fields: Optional[List[str]] = None, + limit: Optional[int] = None, + skip: Optional[int] = None, + sort: Optional[List[dict]] = None, + stable: Optional[bool] = None, + update: Optional[str] = None, + use_index: Optional[List[str]] = None, + **kwargs, ) -> DetailedResponse: """ - Query a database partition index by using selector syntax as stream. + Query a database partition index by using selector syntax. Query documents by using a declarative JSON querying syntax. It's best practice to create an appropriate index for all fields in selector by using the `_index` endpoint. - Queries without an appropriate backing index will fallback to using the built-in - `_all_docs` index. This is not recommended because it has a noticeable performance - impact causing a full scan of the partition with each request. In this case the - response body will include a warning field recommending that an index is created. + Queries without an appropriate backing index by default fallback to using the + built-in `_all_docs` index. This isn't recommended because it has a significant + performance impact causing a full scan of the partition with each request. In this + case the response body includes a warning field recommending the creation of an + index. + To avoid the fallback behavior set the `allow_fallback` option to `false` and the + server responds with a `400` status code if no suitable index exists. If you want + to use only a specific index for your query set + `allow_fallback` to `false` and set the `use_index` option. + Before using read the + [FAQs](https://cloud.ibm.com/docs/Cloudant?topic=Cloudant-database-partitioning#partition-querying) + to understand the limitations and appropriate use cases. :param str db: Path parameter to specify the database name. :param str partition_key: Path parameter to specify the database partition @@ -4445,21 +4798,28 @@ def post_partition_find_as_stream(self, field. There are two core types of operators in the selector syntax: * Combination operators: applied at the topmost level of selection. They - are used to combine selectors. In addition to the common boolean operators - (`$and`, `$or`, `$not`, `$nor`) there are three combination operators: - `$all`, `$elemMatch`, and `$allMatch`. A combination operator takes a - single argument. The argument is either another selector, or an array of + are used to combine selectors. A combination operator takes a single + argument. The argument is either another selector, or an array of selectors. * Condition operators: are specific to a field, and are used to evaluate the value stored in that field. For instance, the basic `$eq` operator matches when the specified field contains a value that is equal to the supplied argument. - * Only equality operators such as `$eq`, `$gt`, `$gte`, `$lt`, and `$lte` - (but not `$ne`) can be used as the basis of a query. You should include at - least one of these in a selector. - For further reference see - [selector - syntax](https://cloud.ibm.com/docs/Cloudant?topic=Cloudant-query#selector-syntax). + It is important for query performance to use appropriate selectors: + * Only equality operators such as `$eq`, `$gt`, `$gte`, `$lt`, and + `$lte` (but not `$ne`) can be used as the basis of a query. You should + include at least one of these in a selector. + * Some operators such as `$not`, `$or`, `$in`, and `$regex` cannot be + answered from an index. For query selectors use these operators in + conjunction with equality operators or create and use a partial index to + reduce the number of documents that will need to be scanned. + See [the Cloudant + Docs](https://cloud.ibm.com/docs/Cloudant?topic=Cloudant-operators) for a + list of all available combination and conditional operators. + For further reference see [selector + syntax](https://cloud.ibm.com/docs/Cloudant?topic=Cloudant-selector-syntax). + :param bool allow_fallback: (optional) Whether to allow fallback to other + indexes. Default is true. :param str bookmark: (optional) Opaque bookmark token used when paginating results. :param bool conflicts: (optional) A boolean value that indicates whether or @@ -4470,7 +4830,7 @@ def post_partition_find_as_stream(self, document lookups (when each document replica is fetched). :param List[str] fields: (optional) JSON array that uses the field syntax. Use this parameter to specify which fields of a document must be returned. - If it is omitted, the entire document is returned. + If it is omitted or empty, the entire document is returned. :param int limit: (optional) Maximum number of results returned. The `type: text` indexes are limited to 200 results when queried. :param int skip: (optional) Skip the first 'n' results, where 'n' is the @@ -4480,8 +4840,8 @@ def post_partition_find_as_stream(self, field name and direction pair is the topmost level of sort. The second pair, if provided, is the next level of sort. The field can be any field, using dotted notation if desired for sub-document fields. - For example in JSON: `[{"fieldName1": "desc"}, {"fieldName2.subFieldName1": - "desc"}]` + For example in JSON: + `[{"fieldName1": "desc"}, {"fieldName2.subFieldName1": "desc"}]` When sorting with multiple fields, ensure that there is an index already defined with all the sort fields in the same order and each object in the sort array has a single key or at least one of the sort fields is included @@ -4492,27 +4852,39 @@ def post_partition_find_as_stream(self, :param str update: (optional) Whether to update the index prior to returning the result. :param List[str] use_index: (optional) Use this option to identify a - specific index for query to run against, rather than by using the IBM - Cloudant Query algorithm to find the best index. + specific index to answer the query, rather than letting the IBM Cloudant + query planner choose an index. Specified as a two element array of design + document id followed by index name, for example `["my_design_doc", + "my_index"]`. + It’s recommended to specify indexes explicitly in your queries to prevent + existing queries being affected by new indexes that might get added later. + If the specified index doesn't exist or can't answer the query then the + server ignores the value and answers using another index or a full scan of + all documents. To change this behavior set `allow_fallback` to `false` and + the server responds instead with a `400` status code if the requested index + is unsuitable to answer the query. :param dict headers: A `dict` containing the request headers :return: A `DetailedResponse` containing the result, headers and HTTP status code. - :rtype: DetailedResponse with `BinaryIO` result + :rtype: DetailedResponse with `dict` result representing a `FindResult` object """ - if db is None: + if not db: raise ValueError('db must be provided') - if partition_key is None: + if not partition_key: raise ValueError('partition_key must be provided') if selector is None: raise ValueError('selector must be provided') headers = {} - sdk_headers = get_sdk_headers(service_name=self.DEFAULT_SERVICE_NAME, - service_version='V1', - operation_id='post_partition_find_as_stream') + sdk_headers = get_sdk_headers( + service_name=self.DEFAULT_SERVICE_NAME, + service_version='V1', + operation_id='post_partition_find', + ) headers.update(sdk_headers) data = { 'selector': selector, + 'allow_fallback': allow_fallback, 'bookmark': bookmark, 'conflicts': conflicts, 'execution_stats': execution_stats, @@ -4522,7 +4894,7 @@ def post_partition_find_as_stream(self, 'sort': sort, 'stable': stable, 'update': update, - 'use_index': use_index + 'use_index': use_index, } data = {k: v for (k, v) in data.items() if v is not None} data = json.dumps(data) @@ -4537,43 +4909,57 @@ def post_partition_find_as_stream(self, path_param_values = self.encode_path_vars(db, partition_key) path_param_dict = dict(zip(path_param_keys, path_param_values)) url = '/{db}/_partition/{partition_key}/_find'.format(**path_param_dict) - request = self.prepare_request(method='POST', - url=url, - headers=headers, - data=data) + request = self.prepare_request( + method='POST', + url=url, + headers=headers, + data=data, + ) - response = self.send(request, stream=True, **kwargs) + response = self.send(request, **kwargs) return response - ######################### - # Queries - ######################### - - - def post_explain(self, + def post_partition_find_as_stream( + self, db: str, + partition_key: str, selector: dict, *, - bookmark: str = None, - conflicts: bool = None, - execution_stats: bool = None, - fields: List[str] = None, - limit: int = None, - skip: int = None, - sort: List[dict] = None, - stable: bool = None, - update: str = None, - use_index: List[str] = None, - r: int = None, - **kwargs + allow_fallback: Optional[bool] = None, + bookmark: Optional[str] = None, + conflicts: Optional[bool] = None, + execution_stats: Optional[bool] = None, + fields: Optional[List[str]] = None, + limit: Optional[int] = None, + skip: Optional[int] = None, + sort: Optional[List[dict]] = None, + stable: Optional[bool] = None, + update: Optional[str] = None, + use_index: Optional[List[str]] = None, + **kwargs, ) -> DetailedResponse: """ - Retrieve information about which index is used for a query. + Query a database partition index by using selector syntax as stream. - Shows which index is being used by the query. Parameters are the same as the - [`_find` endpoint](#query-an-index-by-using-selector-syntax). + Query documents by using a declarative JSON querying syntax. It's best practice to + create an appropriate index for all fields in selector by using the `_index` + endpoint. + Queries without an appropriate backing index by default fallback to using the + built-in `_all_docs` index. This isn't recommended because it has a significant + performance impact causing a full scan of the partition with each request. In this + case the response body includes a warning field recommending the creation of an + index. + To avoid the fallback behavior set the `allow_fallback` option to `false` and the + server responds with a `400` status code if no suitable index exists. If you want + to use only a specific index for your query set + `allow_fallback` to `false` and set the `use_index` option. + Before using read the + [FAQs](https://cloud.ibm.com/docs/Cloudant?topic=Cloudant-database-partitioning#partition-querying) + to understand the limitations and appropriate use cases. :param str db: Path parameter to specify the database name. + :param str partition_key: Path parameter to specify the database partition + key. :param dict selector: JSON object describing criteria used to select documents. The selector specifies fields in the document, and provides an expression to evaluate with the field content or other data. @@ -4590,21 +4976,28 @@ def post_explain(self, field. There are two core types of operators in the selector syntax: * Combination operators: applied at the topmost level of selection. They - are used to combine selectors. In addition to the common boolean operators - (`$and`, `$or`, `$not`, `$nor`) there are three combination operators: - `$all`, `$elemMatch`, and `$allMatch`. A combination operator takes a - single argument. The argument is either another selector, or an array of + are used to combine selectors. A combination operator takes a single + argument. The argument is either another selector, or an array of selectors. * Condition operators: are specific to a field, and are used to evaluate the value stored in that field. For instance, the basic `$eq` operator matches when the specified field contains a value that is equal to the supplied argument. - * Only equality operators such as `$eq`, `$gt`, `$gte`, `$lt`, and `$lte` - (but not `$ne`) can be used as the basis of a query. You should include at - least one of these in a selector. - For further reference see - [selector - syntax](https://cloud.ibm.com/docs/Cloudant?topic=Cloudant-query#selector-syntax). + It is important for query performance to use appropriate selectors: + * Only equality operators such as `$eq`, `$gt`, `$gte`, `$lt`, and + `$lte` (but not `$ne`) can be used as the basis of a query. You should + include at least one of these in a selector. + * Some operators such as `$not`, `$or`, `$in`, and `$regex` cannot be + answered from an index. For query selectors use these operators in + conjunction with equality operators or create and use a partial index to + reduce the number of documents that will need to be scanned. + See [the Cloudant + Docs](https://cloud.ibm.com/docs/Cloudant?topic=Cloudant-operators) for a + list of all available combination and conditional operators. + For further reference see [selector + syntax](https://cloud.ibm.com/docs/Cloudant?topic=Cloudant-selector-syntax). + :param bool allow_fallback: (optional) Whether to allow fallback to other + indexes. Default is true. :param str bookmark: (optional) Opaque bookmark token used when paginating results. :param bool conflicts: (optional) A boolean value that indicates whether or @@ -4615,7 +5008,7 @@ def post_explain(self, document lookups (when each document replica is fetched). :param List[str] fields: (optional) JSON array that uses the field syntax. Use this parameter to specify which fields of a document must be returned. - If it is omitted, the entire document is returned. + If it is omitted or empty, the entire document is returned. :param int limit: (optional) Maximum number of results returned. The `type: text` indexes are limited to 200 results when queried. :param int skip: (optional) Skip the first 'n' results, where 'n' is the @@ -4625,8 +5018,8 @@ def post_explain(self, field name and direction pair is the topmost level of sort. The second pair, if provided, is the next level of sort. The field can be any field, using dotted notation if desired for sub-document fields. - For example in JSON: `[{"fieldName1": "desc"}, {"fieldName2.subFieldName1": - "desc"}]` + For example in JSON: + `[{"fieldName1": "desc"}, {"fieldName2.subFieldName1": "desc"}]` When sorting with multiple fields, ensure that there is an index already defined with all the sort fields in the same order and each object in the sort array has a single key or at least one of the sort fields is included @@ -4637,31 +5030,39 @@ def post_explain(self, :param str update: (optional) Whether to update the index prior to returning the result. :param List[str] use_index: (optional) Use this option to identify a - specific index for query to run against, rather than by using the IBM - Cloudant Query algorithm to find the best index. - :param int r: (optional) The read quorum that is needed for the result. The - value defaults to 1, in which case the document that was found in the index - is returned. If set to a higher value, each document is read from at least - that many replicas before it is returned in the results. The request will - take more time than using only the document that is stored locally with the - index. + specific index to answer the query, rather than letting the IBM Cloudant + query planner choose an index. Specified as a two element array of design + document id followed by index name, for example `["my_design_doc", + "my_index"]`. + It’s recommended to specify indexes explicitly in your queries to prevent + existing queries being affected by new indexes that might get added later. + If the specified index doesn't exist or can't answer the query then the + server ignores the value and answers using another index or a full scan of + all documents. To change this behavior set `allow_fallback` to `false` and + the server responds instead with a `400` status code if the requested index + is unsuitable to answer the query. :param dict headers: A `dict` containing the request headers :return: A `DetailedResponse` containing the result, headers and HTTP status code. - :rtype: DetailedResponse with `dict` result representing a `ExplainResult` object + :rtype: DetailedResponse with `BinaryIO` result """ - if db is None: + if not db: raise ValueError('db must be provided') + if not partition_key: + raise ValueError('partition_key must be provided') if selector is None: raise ValueError('selector must be provided') headers = {} - sdk_headers = get_sdk_headers(service_name=self.DEFAULT_SERVICE_NAME, - service_version='V1', - operation_id='post_explain') + sdk_headers = get_sdk_headers( + service_name=self.DEFAULT_SERVICE_NAME, + service_version='V1', + operation_id='post_partition_find_as_stream', + ) headers.update(sdk_headers) data = { 'selector': selector, + 'allow_fallback': allow_fallback, 'bookmark': bookmark, 'conflicts': conflicts, 'execution_stats': execution_stats, @@ -4672,7 +5073,6 @@ def post_explain(self, 'stable': stable, 'update': update, 'use_index': use_index, - 'r': r } data = {k: v for (k, v) in data.items() if v is not None} data = json.dumps(data) @@ -4683,35 +5083,210 @@ def post_explain(self, del kwargs['headers'] headers['Accept'] = 'application/json' - path_param_keys = ['db'] - path_param_values = self.encode_path_vars(db) + path_param_keys = ['db', 'partition_key'] + path_param_values = self.encode_path_vars(db, partition_key) path_param_dict = dict(zip(path_param_keys, path_param_values)) - url = '/{db}/_explain'.format(**path_param_dict) - request = self.prepare_request(method='POST', - url=url, - headers=headers, - data=data) + url = '/{db}/_partition/{partition_key}/_find'.format(**path_param_dict) + request = self.prepare_request( + method='POST', + url=url, + headers=headers, + data=data, + ) - response = self.send(request, **kwargs) + response = self.send(request, stream=True, **kwargs) return response - - def post_find(self, + ######################### + # Queries + ######################### + + def post_explain( + self, + db: str, + selector: dict, + *, + allow_fallback: Optional[bool] = None, + bookmark: Optional[str] = None, + conflicts: Optional[bool] = None, + execution_stats: Optional[bool] = None, + fields: Optional[List[str]] = None, + limit: Optional[int] = None, + skip: Optional[int] = None, + sort: Optional[List[dict]] = None, + stable: Optional[bool] = None, + update: Optional[str] = None, + use_index: Optional[List[str]] = None, + r: Optional[int] = None, + **kwargs, + ) -> DetailedResponse: + """ + Retrieve information about which index is used for a query. + + Shows which index is being used by the query. Parameters are the same as the + [`_find` endpoint](#postfind). + + :param str db: Path parameter to specify the database name. + :param dict selector: JSON object describing criteria used to select + documents. The selector specifies fields in the document, and provides an + expression to evaluate with the field content or other data. + The selector object must: + * Be structured as valid JSON. + * Contain a valid query expression. + Using a selector is significantly more efficient than using a JavaScript + filter function, and is the recommended option if filtering on document + attributes only. + Elementary selector syntax requires you to specify one or more fields, and + the corresponding values required for those fields. You can create more + complex selector expressions by combining operators. + Operators are identified by the use of a dollar sign `$` prefix in the name + field. + There are two core types of operators in the selector syntax: + * Combination operators: applied at the topmost level of selection. They + are used to combine selectors. A combination operator takes a single + argument. The argument is either another selector, or an array of + selectors. + * Condition operators: are specific to a field, and are used to evaluate + the value stored in that field. For instance, the basic `$eq` operator + matches when the specified field contains a value that is equal to the + supplied argument. + It is important for query performance to use appropriate selectors: + * Only equality operators such as `$eq`, `$gt`, `$gte`, `$lt`, and + `$lte` (but not `$ne`) can be used as the basis of a query. You should + include at least one of these in a selector. + * Some operators such as `$not`, `$or`, `$in`, and `$regex` cannot be + answered from an index. For query selectors use these operators in + conjunction with equality operators or create and use a partial index to + reduce the number of documents that will need to be scanned. + See [the Cloudant + Docs](https://cloud.ibm.com/docs/Cloudant?topic=Cloudant-operators) for a + list of all available combination and conditional operators. + For further reference see [selector + syntax](https://cloud.ibm.com/docs/Cloudant?topic=Cloudant-selector-syntax). + :param bool allow_fallback: (optional) Whether to allow fallback to other + indexes. Default is true. + :param str bookmark: (optional) Opaque bookmark token used when paginating + results. + :param bool conflicts: (optional) A boolean value that indicates whether or + not to include information about existing conflicts in the document. + :param bool execution_stats: (optional) Use this option to find information + about the query that was run. This information includes total key lookups, + total document lookups (when `include_docs=true` is used), and total quorum + document lookups (when each document replica is fetched). + :param List[str] fields: (optional) JSON array that uses the field syntax. + Use this parameter to specify which fields of a document must be returned. + If it is omitted or empty, the entire document is returned. + :param int limit: (optional) Maximum number of results returned. The `type: + text` indexes are limited to 200 results when queried. + :param int skip: (optional) Skip the first 'n' results, where 'n' is the + value that is specified. + :param List[dict] sort: (optional) The sort field contains a list of pairs, + each mapping a field name to a sort direction (asc or desc). The first + field name and direction pair is the topmost level of sort. The second + pair, if provided, is the next level of sort. The field can be any field, + using dotted notation if desired for sub-document fields. + For example in JSON: + `[{"fieldName1": "desc"}, {"fieldName2.subFieldName1": "desc"}]` + When sorting with multiple fields, ensure that there is an index already + defined with all the sort fields in the same order and each object in the + sort array has a single key or at least one of the sort fields is included + in the selector. All sorting fields must use the same sort direction, + either all ascending or all descending. + :param bool stable: (optional) Whether or not the view results should be + returned from a "stable" set of shards. + :param str update: (optional) Whether to update the index prior to + returning the result. + :param List[str] use_index: (optional) Use this option to identify a + specific index to answer the query, rather than letting the IBM Cloudant + query planner choose an index. Specified as a two element array of design + document id followed by index name, for example `["my_design_doc", + "my_index"]`. + It’s recommended to specify indexes explicitly in your queries to prevent + existing queries being affected by new indexes that might get added later. + If the specified index doesn't exist or can't answer the query then the + server ignores the value and answers using another index or a full scan of + all documents. To change this behavior set `allow_fallback` to `false` and + the server responds instead with a `400` status code if the requested index + is unsuitable to answer the query. + :param int r: (optional) The read quorum that is needed for the result. The + value defaults to 1, in which case the document that was found in the index + is returned. If set to a higher value, each document is read from at least + that many replicas before it is returned in the results. The request will + take more time than using only the document that is stored locally with the + index. + :param dict headers: A `dict` containing the request headers + :return: A `DetailedResponse` containing the result, headers and HTTP status code. + :rtype: DetailedResponse with `dict` result representing a `ExplainResult` object + """ + + if not db: + raise ValueError('db must be provided') + if selector is None: + raise ValueError('selector must be provided') + headers = {} + sdk_headers = get_sdk_headers( + service_name=self.DEFAULT_SERVICE_NAME, + service_version='V1', + operation_id='post_explain', + ) + headers.update(sdk_headers) + + data = { + 'selector': selector, + 'allow_fallback': allow_fallback, + 'bookmark': bookmark, + 'conflicts': conflicts, + 'execution_stats': execution_stats, + 'fields': fields, + 'limit': limit, + 'skip': skip, + 'sort': sort, + 'stable': stable, + 'update': update, + 'use_index': use_index, + 'r': r, + } + data = {k: v for (k, v) in data.items() if v is not None} + data = json.dumps(data) + headers['content-type'] = 'application/json' + + if 'headers' in kwargs: + headers.update(kwargs.get('headers')) + del kwargs['headers'] + headers['Accept'] = 'application/json' + + path_param_keys = ['db'] + path_param_values = self.encode_path_vars(db) + path_param_dict = dict(zip(path_param_keys, path_param_values)) + url = '/{db}/_explain'.format(**path_param_dict) + request = self.prepare_request( + method='POST', + url=url, + headers=headers, + data=data, + ) + + response = self.send(request, **kwargs) + return response + + def post_find( + self, db: str, selector: dict, *, - bookmark: str = None, - conflicts: bool = None, - execution_stats: bool = None, - fields: List[str] = None, - limit: int = None, - skip: int = None, - sort: List[dict] = None, - stable: bool = None, - update: str = None, - use_index: List[str] = None, - r: int = None, - **kwargs + allow_fallback: Optional[bool] = None, + bookmark: Optional[str] = None, + conflicts: Optional[bool] = None, + execution_stats: Optional[bool] = None, + fields: Optional[List[str]] = None, + limit: Optional[int] = None, + skip: Optional[int] = None, + sort: Optional[List[dict]] = None, + stable: Optional[bool] = None, + update: Optional[str] = None, + use_index: Optional[List[str]] = None, + r: Optional[int] = None, + **kwargs, ) -> DetailedResponse: """ Query an index by using selector syntax. @@ -4719,11 +5294,15 @@ def post_find(self, Query documents by using a declarative JSON querying syntax. It's best practice to create an appropriate index for all fields in selector by using the `_index` endpoint. - Queries without an appropriate backing index will fallback to using the built-in - `_all_docs` index. This is not recommended because it has a significant + Queries without an appropriate backing index by default fallback to using the + built-in `_all_docs` index. This isn't recommended because it has a significant performance impact causing a full scan of the database with each request. In this - case the response body will include a warning field recommending that an index is - created. + case the response body includes a warning field recommending the creation of an + index. + To avoid the fallback behavior set the `allow_fallback` option to `false` and the + server responds with a `400` status code if no suitable index exists. If you want + to use only a specific index for your query set + `allow_fallback` to `false` and set the `use_index` option. :param str db: Path parameter to specify the database name. :param dict selector: JSON object describing criteria used to select @@ -4742,21 +5321,28 @@ def post_find(self, field. There are two core types of operators in the selector syntax: * Combination operators: applied at the topmost level of selection. They - are used to combine selectors. In addition to the common boolean operators - (`$and`, `$or`, `$not`, `$nor`) there are three combination operators: - `$all`, `$elemMatch`, and `$allMatch`. A combination operator takes a - single argument. The argument is either another selector, or an array of + are used to combine selectors. A combination operator takes a single + argument. The argument is either another selector, or an array of selectors. * Condition operators: are specific to a field, and are used to evaluate the value stored in that field. For instance, the basic `$eq` operator matches when the specified field contains a value that is equal to the supplied argument. - * Only equality operators such as `$eq`, `$gt`, `$gte`, `$lt`, and `$lte` - (but not `$ne`) can be used as the basis of a query. You should include at - least one of these in a selector. - For further reference see - [selector - syntax](https://cloud.ibm.com/docs/Cloudant?topic=Cloudant-query#selector-syntax). + It is important for query performance to use appropriate selectors: + * Only equality operators such as `$eq`, `$gt`, `$gte`, `$lt`, and + `$lte` (but not `$ne`) can be used as the basis of a query. You should + include at least one of these in a selector. + * Some operators such as `$not`, `$or`, `$in`, and `$regex` cannot be + answered from an index. For query selectors use these operators in + conjunction with equality operators or create and use a partial index to + reduce the number of documents that will need to be scanned. + See [the Cloudant + Docs](https://cloud.ibm.com/docs/Cloudant?topic=Cloudant-operators) for a + list of all available combination and conditional operators. + For further reference see [selector + syntax](https://cloud.ibm.com/docs/Cloudant?topic=Cloudant-selector-syntax). + :param bool allow_fallback: (optional) Whether to allow fallback to other + indexes. Default is true. :param str bookmark: (optional) Opaque bookmark token used when paginating results. :param bool conflicts: (optional) A boolean value that indicates whether or @@ -4767,7 +5353,7 @@ def post_find(self, document lookups (when each document replica is fetched). :param List[str] fields: (optional) JSON array that uses the field syntax. Use this parameter to specify which fields of a document must be returned. - If it is omitted, the entire document is returned. + If it is omitted or empty, the entire document is returned. :param int limit: (optional) Maximum number of results returned. The `type: text` indexes are limited to 200 results when queried. :param int skip: (optional) Skip the first 'n' results, where 'n' is the @@ -4777,8 +5363,8 @@ def post_find(self, field name and direction pair is the topmost level of sort. The second pair, if provided, is the next level of sort. The field can be any field, using dotted notation if desired for sub-document fields. - For example in JSON: `[{"fieldName1": "desc"}, {"fieldName2.subFieldName1": - "desc"}]` + For example in JSON: + `[{"fieldName1": "desc"}, {"fieldName2.subFieldName1": "desc"}]` When sorting with multiple fields, ensure that there is an index already defined with all the sort fields in the same order and each object in the sort array has a single key or at least one of the sort fields is included @@ -4789,8 +5375,17 @@ def post_find(self, :param str update: (optional) Whether to update the index prior to returning the result. :param List[str] use_index: (optional) Use this option to identify a - specific index for query to run against, rather than by using the IBM - Cloudant Query algorithm to find the best index. + specific index to answer the query, rather than letting the IBM Cloudant + query planner choose an index. Specified as a two element array of design + document id followed by index name, for example `["my_design_doc", + "my_index"]`. + It’s recommended to specify indexes explicitly in your queries to prevent + existing queries being affected by new indexes that might get added later. + If the specified index doesn't exist or can't answer the query then the + server ignores the value and answers using another index or a full scan of + all documents. To change this behavior set `allow_fallback` to `false` and + the server responds instead with a `400` status code if the requested index + is unsuitable to answer the query. :param int r: (optional) The read quorum that is needed for the result. The value defaults to 1, in which case the document that was found in the index is returned. If set to a higher value, each document is read from at least @@ -4802,18 +5397,21 @@ def post_find(self, :rtype: DetailedResponse with `dict` result representing a `FindResult` object """ - if db is None: + if not db: raise ValueError('db must be provided') if selector is None: raise ValueError('selector must be provided') headers = {} - sdk_headers = get_sdk_headers(service_name=self.DEFAULT_SERVICE_NAME, - service_version='V1', - operation_id='post_find') + sdk_headers = get_sdk_headers( + service_name=self.DEFAULT_SERVICE_NAME, + service_version='V1', + operation_id='post_find', + ) headers.update(sdk_headers) data = { 'selector': selector, + 'allow_fallback': allow_fallback, 'bookmark': bookmark, 'conflicts': conflicts, 'execution_stats': execution_stats, @@ -4824,7 +5422,7 @@ def post_find(self, 'stable': stable, 'update': update, 'use_index': use_index, - 'r': r + 'r': r, } data = {k: v for (k, v) in data.items() if v is not None} data = json.dumps(data) @@ -4839,31 +5437,34 @@ def post_find(self, path_param_values = self.encode_path_vars(db) path_param_dict = dict(zip(path_param_keys, path_param_values)) url = '/{db}/_find'.format(**path_param_dict) - request = self.prepare_request(method='POST', - url=url, - headers=headers, - data=data) + request = self.prepare_request( + method='POST', + url=url, + headers=headers, + data=data, + ) response = self.send(request, **kwargs) return response - - def post_find_as_stream(self, + def post_find_as_stream( + self, db: str, selector: dict, *, - bookmark: str = None, - conflicts: bool = None, - execution_stats: bool = None, - fields: List[str] = None, - limit: int = None, - skip: int = None, - sort: List[dict] = None, - stable: bool = None, - update: str = None, - use_index: List[str] = None, - r: int = None, - **kwargs + allow_fallback: Optional[bool] = None, + bookmark: Optional[str] = None, + conflicts: Optional[bool] = None, + execution_stats: Optional[bool] = None, + fields: Optional[List[str]] = None, + limit: Optional[int] = None, + skip: Optional[int] = None, + sort: Optional[List[dict]] = None, + stable: Optional[bool] = None, + update: Optional[str] = None, + use_index: Optional[List[str]] = None, + r: Optional[int] = None, + **kwargs, ) -> DetailedResponse: """ Query an index by using selector syntax as stream. @@ -4871,11 +5472,15 @@ def post_find_as_stream(self, Query documents by using a declarative JSON querying syntax. It's best practice to create an appropriate index for all fields in selector by using the `_index` endpoint. - Queries without an appropriate backing index will fallback to using the built-in - `_all_docs` index. This is not recommended because it has a significant + Queries without an appropriate backing index by default fallback to using the + built-in `_all_docs` index. This isn't recommended because it has a significant performance impact causing a full scan of the database with each request. In this - case the response body will include a warning field recommending that an index is - created. + case the response body includes a warning field recommending the creation of an + index. + To avoid the fallback behavior set the `allow_fallback` option to `false` and the + server responds with a `400` status code if no suitable index exists. If you want + to use only a specific index for your query set + `allow_fallback` to `false` and set the `use_index` option. :param str db: Path parameter to specify the database name. :param dict selector: JSON object describing criteria used to select @@ -4894,21 +5499,28 @@ def post_find_as_stream(self, field. There are two core types of operators in the selector syntax: * Combination operators: applied at the topmost level of selection. They - are used to combine selectors. In addition to the common boolean operators - (`$and`, `$or`, `$not`, `$nor`) there are three combination operators: - `$all`, `$elemMatch`, and `$allMatch`. A combination operator takes a - single argument. The argument is either another selector, or an array of + are used to combine selectors. A combination operator takes a single + argument. The argument is either another selector, or an array of selectors. * Condition operators: are specific to a field, and are used to evaluate the value stored in that field. For instance, the basic `$eq` operator matches when the specified field contains a value that is equal to the supplied argument. - * Only equality operators such as `$eq`, `$gt`, `$gte`, `$lt`, and `$lte` - (but not `$ne`) can be used as the basis of a query. You should include at - least one of these in a selector. - For further reference see - [selector - syntax](https://cloud.ibm.com/docs/Cloudant?topic=Cloudant-query#selector-syntax). + It is important for query performance to use appropriate selectors: + * Only equality operators such as `$eq`, `$gt`, `$gte`, `$lt`, and + `$lte` (but not `$ne`) can be used as the basis of a query. You should + include at least one of these in a selector. + * Some operators such as `$not`, `$or`, `$in`, and `$regex` cannot be + answered from an index. For query selectors use these operators in + conjunction with equality operators or create and use a partial index to + reduce the number of documents that will need to be scanned. + See [the Cloudant + Docs](https://cloud.ibm.com/docs/Cloudant?topic=Cloudant-operators) for a + list of all available combination and conditional operators. + For further reference see [selector + syntax](https://cloud.ibm.com/docs/Cloudant?topic=Cloudant-selector-syntax). + :param bool allow_fallback: (optional) Whether to allow fallback to other + indexes. Default is true. :param str bookmark: (optional) Opaque bookmark token used when paginating results. :param bool conflicts: (optional) A boolean value that indicates whether or @@ -4919,7 +5531,7 @@ def post_find_as_stream(self, document lookups (when each document replica is fetched). :param List[str] fields: (optional) JSON array that uses the field syntax. Use this parameter to specify which fields of a document must be returned. - If it is omitted, the entire document is returned. + If it is omitted or empty, the entire document is returned. :param int limit: (optional) Maximum number of results returned. The `type: text` indexes are limited to 200 results when queried. :param int skip: (optional) Skip the first 'n' results, where 'n' is the @@ -4929,8 +5541,8 @@ def post_find_as_stream(self, field name and direction pair is the topmost level of sort. The second pair, if provided, is the next level of sort. The field can be any field, using dotted notation if desired for sub-document fields. - For example in JSON: `[{"fieldName1": "desc"}, {"fieldName2.subFieldName1": - "desc"}]` + For example in JSON: + `[{"fieldName1": "desc"}, {"fieldName2.subFieldName1": "desc"}]` When sorting with multiple fields, ensure that there is an index already defined with all the sort fields in the same order and each object in the sort array has a single key or at least one of the sort fields is included @@ -4941,8 +5553,17 @@ def post_find_as_stream(self, :param str update: (optional) Whether to update the index prior to returning the result. :param List[str] use_index: (optional) Use this option to identify a - specific index for query to run against, rather than by using the IBM - Cloudant Query algorithm to find the best index. + specific index to answer the query, rather than letting the IBM Cloudant + query planner choose an index. Specified as a two element array of design + document id followed by index name, for example `["my_design_doc", + "my_index"]`. + It’s recommended to specify indexes explicitly in your queries to prevent + existing queries being affected by new indexes that might get added later. + If the specified index doesn't exist or can't answer the query then the + server ignores the value and answers using another index or a full scan of + all documents. To change this behavior set `allow_fallback` to `false` and + the server responds instead with a `400` status code if the requested index + is unsuitable to answer the query. :param int r: (optional) The read quorum that is needed for the result. The value defaults to 1, in which case the document that was found in the index is returned. If set to a higher value, each document is read from at least @@ -4954,18 +5575,21 @@ def post_find_as_stream(self, :rtype: DetailedResponse with `BinaryIO` result """ - if db is None: + if not db: raise ValueError('db must be provided') if selector is None: raise ValueError('selector must be provided') headers = {} - sdk_headers = get_sdk_headers(service_name=self.DEFAULT_SERVICE_NAME, - service_version='V1', - operation_id='post_find_as_stream') + sdk_headers = get_sdk_headers( + service_name=self.DEFAULT_SERVICE_NAME, + service_version='V1', + operation_id='post_find_as_stream', + ) headers.update(sdk_headers) data = { 'selector': selector, + 'allow_fallback': allow_fallback, 'bookmark': bookmark, 'conflicts': conflicts, 'execution_stats': execution_stats, @@ -4976,7 +5600,7 @@ def post_find_as_stream(self, 'stable': stable, 'update': update, 'use_index': use_index, - 'r': r + 'r': r, } data = {k: v for (k, v) in data.items() if v is not None} data = json.dumps(data) @@ -4991,26 +5615,28 @@ def post_find_as_stream(self, path_param_values = self.encode_path_vars(db) path_param_dict = dict(zip(path_param_keys, path_param_values)) url = '/{db}/_find'.format(**path_param_dict) - request = self.prepare_request(method='POST', - url=url, - headers=headers, - data=data) + request = self.prepare_request( + method='POST', + url=url, + headers=headers, + data=data, + ) response = self.send(request, stream=True, **kwargs) return response - - def get_indexes_information(self, + def get_indexes_information( + self, db: str, - **kwargs + **kwargs, ) -> DetailedResponse: """ Retrieve information about all indexes. - When you make a GET request to `/db/_index`, you get a list of all indexes used by - Cloudant Query in the database, including the primary index. In addition to the - information available through this API, indexes are also stored in the `indexes` - property of design documents. + When you make a GET request to `/db/_index`, you get a list of all the indexes + using `"language":"query"` in the database and the primary index. In addition to + the information available through this API, the indexes are stored in the + `indexes` property of their respective design documents. :param str db: Path parameter to specify the database name. :param dict headers: A `dict` containing the request headers @@ -5018,12 +5644,14 @@ def get_indexes_information(self, :rtype: DetailedResponse with `dict` result representing a `IndexesInformation` object """ - if db is None: + if not db: raise ValueError('db must be provided') headers = {} - sdk_headers = get_sdk_headers(service_name=self.DEFAULT_SERVICE_NAME, - service_version='V1', - operation_id='get_indexes_information') + sdk_headers = get_sdk_headers( + service_name=self.DEFAULT_SERVICE_NAME, + service_version='V1', + operation_id='get_indexes_information', + ) headers.update(sdk_headers) if 'headers' in kwargs: @@ -5035,24 +5663,25 @@ def get_indexes_information(self, path_param_values = self.encode_path_vars(db) path_param_dict = dict(zip(path_param_keys, path_param_values)) url = '/{db}/_index'.format(**path_param_dict) - request = self.prepare_request(method='GET', - url=url, - headers=headers) + request = self.prepare_request( + method='GET', + url=url, + headers=headers, + ) response = self.send(request, **kwargs) return response - - def post_index(self, + def post_index( + self, db: str, index: 'IndexDefinition', *, - ddoc: str = None, - def_: 'IndexDefinition' = None, - name: str = None, - partitioned: bool = None, - type: str = None, - **kwargs + ddoc: Optional[str] = None, + name: Optional[str] = None, + partitioned: Optional[bool] = None, + type: Optional[str] = None, + **kwargs, ) -> DetailedResponse: """ Create a new index on a database. @@ -5063,17 +5692,12 @@ def post_index(self, :param IndexDefinition index: Schema for a `json` or `text` query index definition. Indexes of type `text` have additional configuration properties that do not apply to `json` indexes, these are: - * `default_analyzer` - the default text analyzer to use * `default_field` - - whether to index the text in all document fields and what analyzer to use - for that purpose. - :param str ddoc: (optional) Name of the design document in which the index - will be created. - :param IndexDefinition def_: (optional) Schema for a `json` or `text` query - index definition. Indexes of type `text` have additional configuration - properties that do not apply to `json` indexes, these are: - * `default_analyzer` - the default text analyzer to use * `default_field` - - whether to index the text in all document fields and what analyzer to use - for that purpose. + * `default_analyzer` - the default text analyzer to use + * `default_field` - whether to index the text in all document fields and + what analyzer to use for that purpose. + :param str ddoc: (optional) Specifies the design document name in which the + index will be created. The design document name is the design document ID + excluding the `_design/` prefix. :param str name: (optional) name. :param bool partitioned: (optional) The default value is `true` for databases with `partitioned: true` and `false` otherwise. For databases @@ -5085,26 +5709,25 @@ def post_index(self, :rtype: DetailedResponse with `dict` result representing a `IndexResult` object """ - if db is None: + if not db: raise ValueError('db must be provided') if index is None: raise ValueError('index must be provided') index = convert_model(index) - if def_ is not None: - def_ = convert_model(def_) headers = {} - sdk_headers = get_sdk_headers(service_name=self.DEFAULT_SERVICE_NAME, - service_version='V1', - operation_id='post_index') + sdk_headers = get_sdk_headers( + service_name=self.DEFAULT_SERVICE_NAME, + service_version='V1', + operation_id='post_index', + ) headers.update(sdk_headers) data = { 'index': index, 'ddoc': ddoc, - 'def': def_, 'name': name, 'partitioned': partitioned, - 'type': type + 'type': type, } data = {k: v for (k, v) in data.items() if v is not None} data = json.dumps(data) @@ -5119,25 +5742,29 @@ def post_index(self, path_param_values = self.encode_path_vars(db) path_param_dict = dict(zip(path_param_keys, path_param_values)) url = '/{db}/_index'.format(**path_param_dict) - request = self.prepare_request(method='POST', - url=url, - headers=headers, - data=data) + request = self.prepare_request( + method='POST', + url=url, + headers=headers, + data=data, + ) response = self.send(request, **kwargs) return response - - def delete_index(self, + def delete_index( + self, db: str, ddoc: str, type: str, index: str, - **kwargs + **kwargs, ) -> DetailedResponse: """ Delete an index. + Delete the index functions from the design document and index files on the server. + :param str db: Path parameter to specify the database name. :param str ddoc: Path parameter to specify the design document name. The design document name is the design document ID excluding the `_design/` @@ -5149,18 +5776,20 @@ def delete_index(self, :rtype: DetailedResponse with `dict` result representing a `Ok` object """ - if db is None: + if not db: raise ValueError('db must be provided') - if ddoc is None: + if not ddoc: raise ValueError('ddoc must be provided') - if type is None: + if not type: raise ValueError('type must be provided') - if index is None: + if not index: raise ValueError('index must be provided') headers = {} - sdk_headers = get_sdk_headers(service_name=self.DEFAULT_SERVICE_NAME, - service_version='V1', - operation_id='delete_index') + sdk_headers = get_sdk_headers( + service_name=self.DEFAULT_SERVICE_NAME, + service_version='V1', + operation_id='delete_index', + ) headers.update(sdk_headers) if 'headers' in kwargs: @@ -5172,9 +5801,11 @@ def delete_index(self, path_param_values = self.encode_path_vars(db, ddoc, type, index) path_param_dict = dict(zip(path_param_keys, path_param_values)) url = '/{db}/_index/_design/{ddoc}/{type}/{index}'.format(**path_param_dict) - request = self.prepare_request(method='DELETE', - url=url, - headers=headers) + request = self.prepare_request( + method='DELETE', + url=url, + headers=headers, + ) response = self.send(request, **kwargs) return response @@ -5183,11 +5814,11 @@ def delete_index(self, # Searches ######################### - - def post_search_analyze(self, + def post_search_analyze( + self, analyzer: str, text: str, - **kwargs + **kwargs, ) -> DetailedResponse: """ Query tokenization of sample text. @@ -5208,14 +5839,16 @@ def post_search_analyze(self, if text is None: raise ValueError('text must be provided') headers = {} - sdk_headers = get_sdk_headers(service_name=self.DEFAULT_SERVICE_NAME, - service_version='V1', - operation_id='post_search_analyze') + sdk_headers = get_sdk_headers( + service_name=self.DEFAULT_SERVICE_NAME, + service_version='V1', + operation_id='post_search_analyze', + ) headers.update(sdk_headers) data = { 'analyzer': analyzer, - 'text': text + 'text': text, } data = {k: v for (k, v) in data.items() if v is not None} data = json.dumps(data) @@ -5227,39 +5860,41 @@ def post_search_analyze(self, headers['Accept'] = 'application/json' url = '/_search_analyze' - request = self.prepare_request(method='POST', - url=url, - headers=headers, - data=data) + request = self.prepare_request( + method='POST', + url=url, + headers=headers, + data=data, + ) response = self.send(request, **kwargs) return response - - def post_search(self, + def post_search( + self, db: str, ddoc: str, index: str, query: str, *, - bookmark: str = None, - highlight_fields: List[str] = None, - highlight_number: int = None, - highlight_post_tag: str = None, - highlight_pre_tag: str = None, - highlight_size: int = None, - include_docs: bool = None, - include_fields: List[str] = None, - limit: int = None, - sort: List[str] = None, - stale: str = None, - counts: List[str] = None, - drilldown: List[List[str]] = None, - group_field: str = None, - group_limit: int = None, - group_sort: List[str] = None, - ranges: dict = None, - **kwargs + bookmark: Optional[str] = None, + highlight_fields: Optional[List[str]] = None, + highlight_number: Optional[int] = None, + highlight_post_tag: Optional[str] = None, + highlight_pre_tag: Optional[str] = None, + highlight_size: Optional[int] = None, + include_docs: Optional[bool] = None, + include_fields: Optional[List[str]] = None, + limit: Optional[int] = None, + sort: Optional[List[str]] = None, + stale: Optional[str] = None, + counts: Optional[List[str]] = None, + drilldown: Optional[List[List[str]]] = None, + group_field: Optional[str] = None, + group_limit: Optional[int] = None, + group_sort: Optional[List[str]] = None, + ranges: Optional[dict] = None, + **kwargs, ) -> DetailedResponse: """ Query a search index. @@ -5301,7 +5936,7 @@ def post_search(self, the specified number. :param List[str] sort: (optional) Specifies the sort order of the results. In a grouped search (when group_field is used), this parameter specifies - the sort order within a group. The default sort order is relevance. A JSON + the sort order within a group. The default sort order is relevance. A JSON string of the form "fieldname<type>" or "-fieldname<type>" for descending order, where fieldname is the name of a string or number field, and type is either a number, a string, or a JSON array of strings. The type @@ -5335,29 +5970,30 @@ def post_search(self, relevance. This field can have the same values as the sort field, so single fields and arrays of fields are supported. This option is only available when making global queries. - :param dict ranges: (optional) This field defines ranges for faceted, - numeric search fields. The value is a JSON object where the fields names - are faceted numeric search fields, and the values of the fields are JSON - objects. The field names of the JSON objects are names for ranges. The - values are strings that describe the range, for example "[0 TO 10]". This - option is only available when making global queries. + :param dict ranges: (optional) Object mapping faceted, numeric search field + names to the required ranges. Each key is a field name and each value is + another object defining the ranges by mapping range name keys to string + values describing the numeric ranges, for example "[0 TO 10]". This option + is only available when making global queries. :param dict headers: A `dict` containing the request headers :return: A `DetailedResponse` containing the result, headers and HTTP status code. :rtype: DetailedResponse with `dict` result representing a `SearchResult` object """ - if db is None: + if not db: raise ValueError('db must be provided') - if ddoc is None: + if not ddoc: raise ValueError('ddoc must be provided') - if index is None: + if not index: raise ValueError('index must be provided') if query is None: raise ValueError('query must be provided') headers = {} - sdk_headers = get_sdk_headers(service_name=self.DEFAULT_SERVICE_NAME, - service_version='V1', - operation_id='post_search') + sdk_headers = get_sdk_headers( + service_name=self.DEFAULT_SERVICE_NAME, + service_version='V1', + operation_id='post_search', + ) headers.update(sdk_headers) data = { @@ -5378,7 +6014,7 @@ def post_search(self, 'group_field': group_field, 'group_limit': group_limit, 'group_sort': group_sort, - 'ranges': ranges + 'ranges': ranges, } data = {k: v for (k, v) in data.items() if v is not None} data = json.dumps(data) @@ -5393,39 +6029,41 @@ def post_search(self, path_param_values = self.encode_path_vars(db, ddoc, index) path_param_dict = dict(zip(path_param_keys, path_param_values)) url = '/{db}/_design/{ddoc}/_search/{index}'.format(**path_param_dict) - request = self.prepare_request(method='POST', - url=url, - headers=headers, - data=data) + request = self.prepare_request( + method='POST', + url=url, + headers=headers, + data=data, + ) response = self.send(request, **kwargs) return response - - def post_search_as_stream(self, + def post_search_as_stream( + self, db: str, ddoc: str, index: str, query: str, *, - bookmark: str = None, - highlight_fields: List[str] = None, - highlight_number: int = None, - highlight_post_tag: str = None, - highlight_pre_tag: str = None, - highlight_size: int = None, - include_docs: bool = None, - include_fields: List[str] = None, - limit: int = None, - sort: List[str] = None, - stale: str = None, - counts: List[str] = None, - drilldown: List[List[str]] = None, - group_field: str = None, - group_limit: int = None, - group_sort: List[str] = None, - ranges: dict = None, - **kwargs + bookmark: Optional[str] = None, + highlight_fields: Optional[List[str]] = None, + highlight_number: Optional[int] = None, + highlight_post_tag: Optional[str] = None, + highlight_pre_tag: Optional[str] = None, + highlight_size: Optional[int] = None, + include_docs: Optional[bool] = None, + include_fields: Optional[List[str]] = None, + limit: Optional[int] = None, + sort: Optional[List[str]] = None, + stale: Optional[str] = None, + counts: Optional[List[str]] = None, + drilldown: Optional[List[List[str]]] = None, + group_field: Optional[str] = None, + group_limit: Optional[int] = None, + group_sort: Optional[List[str]] = None, + ranges: Optional[dict] = None, + **kwargs, ) -> DetailedResponse: """ Query a search index as stream. @@ -5467,7 +6105,7 @@ def post_search_as_stream(self, the specified number. :param List[str] sort: (optional) Specifies the sort order of the results. In a grouped search (when group_field is used), this parameter specifies - the sort order within a group. The default sort order is relevance. A JSON + the sort order within a group. The default sort order is relevance. A JSON string of the form "fieldname<type>" or "-fieldname<type>" for descending order, where fieldname is the name of a string or number field, and type is either a number, a string, or a JSON array of strings. The type @@ -5501,29 +6139,30 @@ def post_search_as_stream(self, relevance. This field can have the same values as the sort field, so single fields and arrays of fields are supported. This option is only available when making global queries. - :param dict ranges: (optional) This field defines ranges for faceted, - numeric search fields. The value is a JSON object where the fields names - are faceted numeric search fields, and the values of the fields are JSON - objects. The field names of the JSON objects are names for ranges. The - values are strings that describe the range, for example "[0 TO 10]". This - option is only available when making global queries. + :param dict ranges: (optional) Object mapping faceted, numeric search field + names to the required ranges. Each key is a field name and each value is + another object defining the ranges by mapping range name keys to string + values describing the numeric ranges, for example "[0 TO 10]". This option + is only available when making global queries. :param dict headers: A `dict` containing the request headers :return: A `DetailedResponse` containing the result, headers and HTTP status code. :rtype: DetailedResponse with `BinaryIO` result """ - if db is None: + if not db: raise ValueError('db must be provided') - if ddoc is None: + if not ddoc: raise ValueError('ddoc must be provided') - if index is None: + if not index: raise ValueError('index must be provided') if query is None: raise ValueError('query must be provided') headers = {} - sdk_headers = get_sdk_headers(service_name=self.DEFAULT_SERVICE_NAME, - service_version='V1', - operation_id='post_search_as_stream') + sdk_headers = get_sdk_headers( + service_name=self.DEFAULT_SERVICE_NAME, + service_version='V1', + operation_id='post_search_as_stream', + ) headers.update(sdk_headers) data = { @@ -5544,7 +6183,7 @@ def post_search_as_stream(self, 'group_field': group_field, 'group_limit': group_limit, 'group_sort': group_sort, - 'ranges': ranges + 'ranges': ranges, } data = {k: v for (k, v) in data.items() if v is not None} data = json.dumps(data) @@ -5559,182 +6198,52 @@ def post_search_as_stream(self, path_param_values = self.encode_path_vars(db, ddoc, index) path_param_dict = dict(zip(path_param_keys, path_param_values)) url = '/{db}/_design/{ddoc}/_search/{index}'.format(**path_param_dict) - request = self.prepare_request(method='POST', - url=url, - headers=headers, - data=data) + request = self.prepare_request( + method='POST', + url=url, + headers=headers, + data=data, + ) response = self.send(request, stream=True, **kwargs) return response - - def get_search_info(self, - db: str, - ddoc: str, - index: str, - **kwargs - ) -> DetailedResponse: - """ - Retrieve information about a search index. - - Retrieve search index metadata information, such as the size of the index on disk. - - :param str db: Path parameter to specify the database name. - :param str ddoc: Path parameter to specify the design document name. The - design document name is the design document ID excluding the `_design/` - prefix. - :param str index: Path parameter to specify the index name. - :param dict headers: A `dict` containing the request headers - :return: A `DetailedResponse` containing the result, headers and HTTP status code. - :rtype: DetailedResponse with `dict` result representing a `SearchInfoResult` object - """ - - if db is None: - raise ValueError('db must be provided') - if ddoc is None: - raise ValueError('ddoc must be provided') - if index is None: - raise ValueError('index must be provided') - headers = {} - sdk_headers = get_sdk_headers(service_name=self.DEFAULT_SERVICE_NAME, - service_version='V1', - operation_id='get_search_info') - headers.update(sdk_headers) - - if 'headers' in kwargs: - headers.update(kwargs.get('headers')) - del kwargs['headers'] - headers['Accept'] = 'application/json' - - path_param_keys = ['db', 'ddoc', 'index'] - path_param_values = self.encode_path_vars(db, ddoc, index) - path_param_dict = dict(zip(path_param_keys, path_param_values)) - url = '/{db}/_design/{ddoc}/_search_info/{index}'.format(**path_param_dict) - request = self.prepare_request(method='GET', - url=url, - headers=headers) - - response = self.send(request, **kwargs) - return response - - ######################### - # Geospatial - ######################### - - - def get_geo(self, + def get_search_disk_size( + self, db: str, ddoc: str, index: str, - *, - bbox: str = None, - bookmark: str = None, - format: str = None, - g: str = None, - include_docs: bool = None, - lat: float = None, - limit: int = None, - lon: float = None, - nearest: bool = None, - radius: float = None, - rangex: float = None, - rangey: float = None, - relation: str = None, - skip: int = None, - stale: str = None, - **kwargs + **kwargs, ) -> DetailedResponse: """ - Query a geospatial index. + Retrieve information about the search index disk size. - Executes a query against the requested geospatial index from the specified design - document. + Retrieve size of the search index on disk. :param str db: Path parameter to specify the database name. :param str ddoc: Path parameter to specify the design document name. The design document name is the design document ID excluding the `_design/` prefix. :param str index: Path parameter to specify the index name. - :param str bbox: (optional) Query parameter to specify a geospatial query - bounding box with two latitude,longitude coordinates for the lower-left and - upper-right corners. An example is - `-11.05987446,12.28339928,-101.05987446,62.28339928`. - :param str bookmark: (optional) Query parameter to specify a bookmark that - was received from a previous request. This parameter enables paging through - the results. If there are no more results after the bookmark, you get a - response containing no further results and the same bookmark, confirming - the end of the result list. - :param str format: (optional) Query parameter that causes the geospatial - query output to be returned in the specified format. - :param str g: (optional) Query parameter to specify a Well Known Text (WKT) - representation of a geospatial query geometry. The valid values for the WKT - parameter include `Point`, `LineString`, `Polygon`, `MultiPoint`, - `MultiLineString`, `MultiPolygon`, and `GeometryCollection`. - :param bool include_docs: (optional) Query parameter to specify whether to - include the full content of the documents in the response. - :param float lat: (optional) Query parameter to specify a latitude - coordinate for use with radius or ellipse geospatial queries. - :param int limit: (optional) Query parameter to specify the number of - returned documents to limit the result to. - :param float lon: (optional) Query parameter to specify a longitude - coordinate for use with radius or ellipse geospatial queries. - :param bool nearest: (optional) Query parameter to specify whether to - perform a nearest neighbour (NN) search. If provided, the `nearest=true` - search returns all results by sorting their distances to the center of the - query geometry. NN search can be used alone or with any of the supported - DE-9IM (Dimensionally Extended nine-Intersection Model) specification - geometric relations documented. - :param float radius: (optional) Query parameter to specify the radius, in - meters, to search from a lat,lon coordinate point in a circular geospatial - query. - :param float rangex: (optional) Query parameter to specify the first - radius, in meters, to search from a lat,lon coordinate point in an ellipse - geospatial query. - :param float rangey: (optional) Query parameter to specify the second - radius, in meters, to search from a lat,lon coordinate point in an ellipse - geospatial query. - :param str relation: (optional) Query parameter to specify the DE-9IM - (Dimensionally Extended nine-Intersection Model)geospatial relationship - between the query geometry and the result documents. - :param int skip: (optional) Query parameter to specify the number of - records before starting to return the results. - :param str stale: (optional) Query parameter to specify to not wait for the - index to finish building before returning results. :param dict headers: A `dict` containing the request headers :return: A `DetailedResponse` containing the result, headers and HTTP status code. - :rtype: DetailedResponse with `dict` result representing a `GeoResult` object + :rtype: DetailedResponse with `dict` result representing a `SearchDiskSizeInformation` object """ - if db is None: + if not db: raise ValueError('db must be provided') - if ddoc is None: + if not ddoc: raise ValueError('ddoc must be provided') - if index is None: + if not index: raise ValueError('index must be provided') headers = {} - sdk_headers = get_sdk_headers(service_name=self.DEFAULT_SERVICE_NAME, - service_version='V1', - operation_id='get_geo') + sdk_headers = get_sdk_headers( + service_name=self.DEFAULT_SERVICE_NAME, + service_version='V1', + operation_id='get_search_disk_size', + ) headers.update(sdk_headers) - params = { - 'bbox': bbox, - 'bookmark': bookmark, - 'format': format, - 'g': g, - 'include_docs': include_docs, - 'lat': lat, - 'limit': limit, - 'lon': lon, - 'nearest': nearest, - 'radius': radius, - 'rangex': rangex, - 'rangey': rangey, - 'relation': relation, - 'skip': skip, - 'stale': stale - } - if 'headers' in kwargs: headers.update(kwargs.get('headers')) del kwargs['headers'] @@ -5743,196 +6252,27 @@ def get_geo(self, path_param_keys = ['db', 'ddoc', 'index'] path_param_values = self.encode_path_vars(db, ddoc, index) path_param_dict = dict(zip(path_param_keys, path_param_values)) - url = '/{db}/_design/{ddoc}/_geo/{index}'.format(**path_param_dict) - request = self.prepare_request(method='GET', - url=url, - headers=headers, - params=params) + url = '/{db}/_design/{ddoc}/_search_disk_size/{index}'.format(**path_param_dict) + request = self.prepare_request( + method='GET', + url=url, + headers=headers, + ) response = self.send(request, **kwargs) return response - - def get_geo_as_stream(self, + def get_search_info( + self, db: str, ddoc: str, index: str, - *, - bbox: str = None, - bookmark: str = None, - format: str = None, - g: str = None, - include_docs: bool = None, - lat: float = None, - limit: int = None, - lon: float = None, - nearest: bool = None, - radius: float = None, - rangex: float = None, - rangey: float = None, - relation: str = None, - skip: int = None, - stale: str = None, - **kwargs + **kwargs, ) -> DetailedResponse: """ - Query a geospatial index as stream. - - Executes a query against the requested geospatial index from the specified design - document. - - :param str db: Path parameter to specify the database name. - :param str ddoc: Path parameter to specify the design document name. The - design document name is the design document ID excluding the `_design/` - prefix. - :param str index: Path parameter to specify the index name. - :param str bbox: (optional) Query parameter to specify a geospatial query - bounding box with two latitude,longitude coordinates for the lower-left and - upper-right corners. An example is - `-11.05987446,12.28339928,-101.05987446,62.28339928`. - :param str bookmark: (optional) Query parameter to specify a bookmark that - was received from a previous request. This parameter enables paging through - the results. If there are no more results after the bookmark, you get a - response containing no further results and the same bookmark, confirming - the end of the result list. - :param str format: (optional) Query parameter that causes the geospatial - query output to be returned in the specified format. - :param str g: (optional) Query parameter to specify a Well Known Text (WKT) - representation of a geospatial query geometry. The valid values for the WKT - parameter include `Point`, `LineString`, `Polygon`, `MultiPoint`, - `MultiLineString`, `MultiPolygon`, and `GeometryCollection`. - :param bool include_docs: (optional) Query parameter to specify whether to - include the full content of the documents in the response. - :param float lat: (optional) Query parameter to specify a latitude - coordinate for use with radius or ellipse geospatial queries. - :param int limit: (optional) Query parameter to specify the number of - returned documents to limit the result to. - :param float lon: (optional) Query parameter to specify a longitude - coordinate for use with radius or ellipse geospatial queries. - :param bool nearest: (optional) Query parameter to specify whether to - perform a nearest neighbour (NN) search. If provided, the `nearest=true` - search returns all results by sorting their distances to the center of the - query geometry. NN search can be used alone or with any of the supported - DE-9IM (Dimensionally Extended nine-Intersection Model) specification - geometric relations documented. - :param float radius: (optional) Query parameter to specify the radius, in - meters, to search from a lat,lon coordinate point in a circular geospatial - query. - :param float rangex: (optional) Query parameter to specify the first - radius, in meters, to search from a lat,lon coordinate point in an ellipse - geospatial query. - :param float rangey: (optional) Query parameter to specify the second - radius, in meters, to search from a lat,lon coordinate point in an ellipse - geospatial query. - :param str relation: (optional) Query parameter to specify the DE-9IM - (Dimensionally Extended nine-Intersection Model)geospatial relationship - between the query geometry and the result documents. - :param int skip: (optional) Query parameter to specify the number of - records before starting to return the results. - :param str stale: (optional) Query parameter to specify to not wait for the - index to finish building before returning results. - :param dict headers: A `dict` containing the request headers - :return: A `DetailedResponse` containing the result, headers and HTTP status code. - :rtype: DetailedResponse with `BinaryIO` result - """ - - if db is None: - raise ValueError('db must be provided') - if ddoc is None: - raise ValueError('ddoc must be provided') - if index is None: - raise ValueError('index must be provided') - headers = {} - sdk_headers = get_sdk_headers(service_name=self.DEFAULT_SERVICE_NAME, - service_version='V1', - operation_id='get_geo_as_stream') - headers.update(sdk_headers) - - params = { - 'bbox': bbox, - 'bookmark': bookmark, - 'format': format, - 'g': g, - 'include_docs': include_docs, - 'lat': lat, - 'limit': limit, - 'lon': lon, - 'nearest': nearest, - 'radius': radius, - 'rangex': rangex, - 'rangey': rangey, - 'relation': relation, - 'skip': skip, - 'stale': stale - } - - if 'headers' in kwargs: - headers.update(kwargs.get('headers')) - del kwargs['headers'] - headers['Accept'] = 'application/json' - - path_param_keys = ['db', 'ddoc', 'index'] - path_param_values = self.encode_path_vars(db, ddoc, index) - path_param_dict = dict(zip(path_param_keys, path_param_values)) - url = '/{db}/_design/{ddoc}/_geo/{index}'.format(**path_param_dict) - request = self.prepare_request(method='GET', - url=url, - headers=headers, - params=params) - - response = self.send(request, stream=True, **kwargs) - return response - - - def post_geo_cleanup(self, - db: str, - **kwargs - ) -> DetailedResponse: - """ - Cleanup old geospatial indexes. - - Cleanup old geospatial indexes from disk that have been superseded by newer index - builds. - - :param str db: Path parameter to specify the database name. - :param dict headers: A `dict` containing the request headers - :return: A `DetailedResponse` containing the result, headers and HTTP status code. - :rtype: DetailedResponse with `dict` result representing a `Ok` object - """ - - if db is None: - raise ValueError('db must be provided') - headers = {} - sdk_headers = get_sdk_headers(service_name=self.DEFAULT_SERVICE_NAME, - service_version='V1', - operation_id='post_geo_cleanup') - headers.update(sdk_headers) - - if 'headers' in kwargs: - headers.update(kwargs.get('headers')) - del kwargs['headers'] - headers['Accept'] = 'application/json' - - path_param_keys = ['db'] - path_param_values = self.encode_path_vars(db) - path_param_dict = dict(zip(path_param_keys, path_param_values)) - url = '/{db}/_geo_cleanup'.format(**path_param_dict) - request = self.prepare_request(method='POST', - url=url, - headers=headers) - - response = self.send(request, **kwargs) - return response - + Retrieve information about a search index. - def get_geo_index_information(self, - db: str, - ddoc: str, - index: str, - **kwargs - ) -> DetailedResponse: - """ - Retrieve information about a geospatial index. + Retrieve search index metadata information, such as the size of the index on disk. :param str db: Path parameter to specify the database name. :param str ddoc: Path parameter to specify the design document name. The @@ -5941,19 +6281,21 @@ def get_geo_index_information(self, :param str index: Path parameter to specify the index name. :param dict headers: A `dict` containing the request headers :return: A `DetailedResponse` containing the result, headers and HTTP status code. - :rtype: DetailedResponse with `dict` result representing a `GeoIndexInformation` object + :rtype: DetailedResponse with `dict` result representing a `SearchInfoResult` object """ - if db is None: + if not db: raise ValueError('db must be provided') - if ddoc is None: + if not ddoc: raise ValueError('ddoc must be provided') - if index is None: + if not index: raise ValueError('index must be provided') headers = {} - sdk_headers = get_sdk_headers(service_name=self.DEFAULT_SERVICE_NAME, - service_version='V1', - operation_id='get_geo_index_information') + sdk_headers = get_sdk_headers( + service_name=self.DEFAULT_SERVICE_NAME, + service_version='V1', + operation_id='get_search_info', + ) headers.update(sdk_headers) if 'headers' in kwargs: @@ -5964,10 +6306,12 @@ def get_geo_index_information(self, path_param_keys = ['db', 'ddoc', 'index'] path_param_values = self.encode_path_vars(db, ddoc, index) path_param_dict = dict(zip(path_param_keys, path_param_values)) - url = '/{db}/_design/{ddoc}/_geo_info/{index}'.format(**path_param_dict) - request = self.prepare_request(method='GET', - url=url, - headers=headers) + url = '/{db}/_design/{ddoc}/_search_info/{index}'.format(**path_param_dict) + request = self.prepare_request( + method='GET', + url=url, + headers=headers, + ) response = self.send(request, **kwargs) return response @@ -5976,37 +6320,40 @@ def get_geo_index_information(self, # Replication ######################### - - def head_replication_document(self, + def head_replication_document( + self, doc_id: str, *, - if_none_match: str = None, - **kwargs + if_none_match: Optional[str] = None, + **kwargs, ) -> DetailedResponse: """ - Retrieve the HTTP headers for a replication document. + Retrieve the HTTP headers for a persistent replication. Retrieves the HTTP headers containing minimal amount of information about the specified replication document from the `_replicator` database. The method supports the same query arguments as the `GET /_replicator/{doc_id}` method, but only headers like content length and the revision (ETag header) are returned. - :param str doc_id: Path parameter to specify the document ID. - :param str if_none_match: (optional) Header parameter to specify a double - quoted document revision token for cache control. + :param str doc_id: Path parameter to specify the ID of the stored + replication configuration in the `_replicator` database. + :param str if_none_match: (optional) Header parameter for a conditional + HTTP request not matching an ETag. :param dict headers: A `dict` containing the request headers :return: A `DetailedResponse` containing the result, headers and HTTP status code. :rtype: DetailedResponse """ - if doc_id is None: + if not doc_id: raise ValueError('doc_id must be provided') headers = { - 'If-None-Match': if_none_match + 'If-None-Match': if_none_match, } - sdk_headers = get_sdk_headers(service_name=self.DEFAULT_SERVICE_NAME, - service_version='V1', - operation_id='head_replication_document') + sdk_headers = get_sdk_headers( + service_name=self.DEFAULT_SERVICE_NAME, + service_version='V1', + operation_id='head_replication_document', + ) headers.update(sdk_headers) if 'headers' in kwargs: @@ -6017,17 +6364,19 @@ def head_replication_document(self, path_param_values = self.encode_path_vars(doc_id) path_param_dict = dict(zip(path_param_keys, path_param_values)) url = '/_replicator/{doc_id}'.format(**path_param_dict) - request = self.prepare_request(method='HEAD', - url=url, - headers=headers) + request = self.prepare_request( + method='HEAD', + url=url, + headers=headers, + ) response = self.send(request, **kwargs) return response - - def head_scheduler_document(self, + def head_scheduler_document( + self, doc_id: str, - **kwargs + **kwargs, ) -> DetailedResponse: """ Retrieve HTTP headers for a replication scheduler document. @@ -6043,12 +6392,14 @@ def head_scheduler_document(self, :rtype: DetailedResponse """ - if doc_id is None: + if not doc_id: raise ValueError('doc_id must be provided') headers = {} - sdk_headers = get_sdk_headers(service_name=self.DEFAULT_SERVICE_NAME, - service_version='V1', - operation_id='head_scheduler_document') + sdk_headers = get_sdk_headers( + service_name=self.DEFAULT_SERVICE_NAME, + service_version='V1', + operation_id='head_scheduler_document', + ) headers.update(sdk_headers) if 'headers' in kwargs: @@ -6059,17 +6410,19 @@ def head_scheduler_document(self, path_param_values = self.encode_path_vars(doc_id) path_param_dict = dict(zip(path_param_keys, path_param_values)) url = '/_scheduler/docs/_replicator/{doc_id}'.format(**path_param_dict) - request = self.prepare_request(method='HEAD', - url=url, - headers=headers) + request = self.prepare_request( + method='HEAD', + url=url, + headers=headers, + ) response = self.send(request, **kwargs) return response - - def head_scheduler_job(self, + def head_scheduler_job( + self, job_id: str, - **kwargs + **kwargs, ) -> DetailedResponse: """ Retrieve the HTTP headers for a replication scheduler job. @@ -6083,12 +6436,14 @@ def head_scheduler_job(self, :rtype: DetailedResponse """ - if job_id is None: + if not job_id: raise ValueError('job_id must be provided') headers = {} - sdk_headers = get_sdk_headers(service_name=self.DEFAULT_SERVICE_NAME, - service_version='V1', - operation_id='head_scheduler_job') + sdk_headers = get_sdk_headers( + service_name=self.DEFAULT_SERVICE_NAME, + service_version='V1', + operation_id='head_scheduler_job', + ) headers.update(sdk_headers) if 'headers' in kwargs: @@ -6099,31 +6454,93 @@ def head_scheduler_job(self, path_param_values = self.encode_path_vars(job_id) path_param_dict = dict(zip(path_param_keys, path_param_values)) url = '/_scheduler/jobs/{job_id}'.format(**path_param_dict) - request = self.prepare_request(method='HEAD', - url=url, - headers=headers) + request = self.prepare_request( + method='HEAD', + url=url, + headers=headers, + ) response = self.send(request, **kwargs) return response + def post_replicator( + self, + replication_document: 'ReplicationDocument', + *, + batch: Optional[str] = None, + **kwargs, + ) -> DetailedResponse: + """ + Create a persistent replication with a generated ID. + + Creates or modifies a document in the `_replicator` database to start a new + replication or to edit an existing replication. + + :param ReplicationDocument replication_document: HTTP request body for + replication operations. + :param str batch: (optional) Query parameter to specify whether to store in + batch mode. The server will respond with a HTTP 202 Accepted response code + immediately. + :param dict headers: A `dict` containing the request headers + :return: A `DetailedResponse` containing the result, headers and HTTP status code. + :rtype: DetailedResponse with `dict` result representing a `DocumentResult` object + """ + + if replication_document is None: + raise ValueError('replication_document must be provided') + if isinstance(replication_document, ReplicationDocument): + replication_document = convert_model(replication_document) + headers = {} + sdk_headers = get_sdk_headers( + service_name=self.DEFAULT_SERVICE_NAME, + service_version='V1', + operation_id='post_replicator', + ) + headers.update(sdk_headers) + + params = { + 'batch': batch, + } + + data = json.dumps(replication_document) + headers['content-type'] = 'application/json' + + if 'headers' in kwargs: + headers.update(kwargs.get('headers')) + del kwargs['headers'] + headers['Accept'] = 'application/json' + + url = '/_replicator' + request = self.prepare_request( + method='POST', + url=url, + headers=headers, + params=params, + data=data, + ) - def delete_replication_document(self, + response = self.send(request, **kwargs) + return response + + def delete_replication_document( + self, doc_id: str, *, - if_match: str = None, - batch: str = None, - rev: str = None, - **kwargs + if_match: Optional[str] = None, + batch: Optional[str] = None, + rev: Optional[str] = None, + **kwargs, ) -> DetailedResponse: """ - Cancel a replication. + Cancel a persistent replication. Cancels a replication by deleting the document that describes it from the `_replicator` database. - :param str doc_id: Path parameter to specify the document ID. - :param str if_match: (optional) Header parameter to specify the document - revision. Alternative to rev query parameter. + :param str doc_id: Path parameter to specify the ID of the stored + replication configuration in the `_replicator` database. + :param str if_match: (optional) Header parameter for a conditional HTTP + request matching an ETag. :param str batch: (optional) Query parameter to specify whether to store in batch mode. The server will respond with a HTTP 202 Accepted response code immediately. @@ -6133,19 +6550,21 @@ def delete_replication_document(self, :rtype: DetailedResponse with `dict` result representing a `DocumentResult` object """ - if doc_id is None: + if not doc_id: raise ValueError('doc_id must be provided') headers = { - 'If-Match': if_match + 'If-Match': if_match, } - sdk_headers = get_sdk_headers(service_name=self.DEFAULT_SERVICE_NAME, - service_version='V1', - operation_id='delete_replication_document') + sdk_headers = get_sdk_headers( + service_name=self.DEFAULT_SERVICE_NAME, + service_version='V1', + operation_id='delete_replication_document', + ) headers.update(sdk_headers) params = { 'batch': batch, - 'rev': rev + 'rev': rev, } if 'headers' in kwargs: @@ -6157,49 +6576,56 @@ def delete_replication_document(self, path_param_values = self.encode_path_vars(doc_id) path_param_dict = dict(zip(path_param_keys, path_param_values)) url = '/_replicator/{doc_id}'.format(**path_param_dict) - request = self.prepare_request(method='DELETE', - url=url, - headers=headers, - params=params) + request = self.prepare_request( + method='DELETE', + url=url, + headers=headers, + params=params, + ) response = self.send(request, **kwargs) return response - - def get_replication_document(self, + def get_replication_document( + self, doc_id: str, *, - if_none_match: str = None, - attachments: bool = None, - att_encoding_info: bool = None, - conflicts: bool = None, - deleted_conflicts: bool = None, - latest: bool = None, - local_seq: bool = None, - meta: bool = None, - rev: str = None, - revs: bool = None, - revs_info: bool = None, - **kwargs + if_none_match: Optional[str] = None, + attachments: Optional[bool] = None, + att_encoding_info: Optional[bool] = None, + conflicts: Optional[bool] = None, + deleted_conflicts: Optional[bool] = None, + latest: Optional[bool] = None, + local_seq: Optional[bool] = None, + meta: Optional[bool] = None, + rev: Optional[str] = None, + revs: Optional[bool] = None, + revs_info: Optional[bool] = None, + **kwargs, ) -> DetailedResponse: """ - Retrieve a replication document. + Retrieve the configuration for a persistent replication. Retrieves a replication document from the `_replicator` database to view the configuration of the replication. The status of the replication is no longer recorded in the document but can be checked via the replication scheduler. - :param str doc_id: Path parameter to specify the document ID. - :param str if_none_match: (optional) Header parameter to specify a double - quoted document revision token for cache control. + :param str doc_id: Path parameter to specify the ID of the stored + replication configuration in the `_replicator` database. + :param str if_none_match: (optional) Header parameter for a conditional + HTTP request not matching an ETag. :param bool attachments: (optional) Query parameter to specify whether to - include attachments bodies in a response. + include attachment content in the response. Note that when used with a + view-style query or changes feed this only applies when requesting + documents in the response. :param bool att_encoding_info: (optional) Query parameter to specify - whether to include the encoding information in attachment stubs if the - particular attachment is compressed. + whether to include the encoding information for compressed attachments. + Note that when used with a view-style query or changes feed this only + applies when requesting documents in the response. :param bool conflicts: (optional) Query parameter to specify whether to - include a list of conflicted revisions in each returned document. Active - only when `include_docs` is `true`. + include a list of conflicted revisions in each returned document. Note that + when used with a view-style query or changes feed this only applies when + requesting documents in the response. :param bool deleted_conflicts: (optional) Query parameter to specify whether to include a list of deleted conflicted revisions in the `_deleted_conflicts` property of the returned document. @@ -6220,14 +6646,16 @@ def get_replication_document(self, :rtype: DetailedResponse with `dict` result representing a `ReplicationDocument` object """ - if doc_id is None: + if not doc_id: raise ValueError('doc_id must be provided') headers = { - 'If-None-Match': if_none_match + 'If-None-Match': if_none_match, } - sdk_headers = get_sdk_headers(service_name=self.DEFAULT_SERVICE_NAME, - service_version='V1', - operation_id='get_replication_document') + sdk_headers = get_sdk_headers( + service_name=self.DEFAULT_SERVICE_NAME, + service_version='V1', + operation_id='get_replication_document', + ) headers.update(sdk_headers) params = { @@ -6240,7 +6668,7 @@ def get_replication_document(self, 'meta': meta, 'rev': rev, 'revs': revs, - 'revs_info': revs_info + 'revs_info': revs_info, } if 'headers' in kwargs: @@ -6252,36 +6680,39 @@ def get_replication_document(self, path_param_values = self.encode_path_vars(doc_id) path_param_dict = dict(zip(path_param_keys, path_param_values)) url = '/_replicator/{doc_id}'.format(**path_param_dict) - request = self.prepare_request(method='GET', - url=url, - headers=headers, - params=params) + request = self.prepare_request( + method='GET', + url=url, + headers=headers, + params=params, + ) response = self.send(request, **kwargs) return response - - def put_replication_document(self, + def put_replication_document( + self, doc_id: str, replication_document: 'ReplicationDocument', *, - if_match: str = None, - batch: str = None, - new_edits: bool = None, - rev: str = None, - **kwargs + if_match: Optional[str] = None, + batch: Optional[str] = None, + new_edits: Optional[bool] = None, + rev: Optional[str] = None, + **kwargs, ) -> DetailedResponse: """ - Create or modify a replication using a replication document. + Create or modify a persistent replication. Creates or modifies a document in the `_replicator` database to start a new replication or to edit an existing replication. - :param str doc_id: Path parameter to specify the document ID. + :param str doc_id: Path parameter to specify the ID of the stored + replication configuration in the `_replicator` database. :param ReplicationDocument replication_document: HTTP request body for replication operations. - :param str if_match: (optional) Header parameter to specify the document - revision. Alternative to rev query parameter. + :param str if_match: (optional) Header parameter for a conditional HTTP + request matching an ETag. :param str batch: (optional) Query parameter to specify whether to store in batch mode. The server will respond with a HTTP 202 Accepted response code immediately. @@ -6290,30 +6721,35 @@ def put_replication_document(self, well-formed _rev must be included in the document. False is used by the replicator to insert documents into the target database even if that leads to the creation of conflicts. + Avoid using this parameter, since this option applies document revisions + without checking for conflicts, so it is very easy to accidentally end up + with a large number of conflicts. :param str rev: (optional) Query parameter to specify a document revision. :param dict headers: A `dict` containing the request headers :return: A `DetailedResponse` containing the result, headers and HTTP status code. :rtype: DetailedResponse with `dict` result representing a `DocumentResult` object """ - if doc_id is None: + if not doc_id: raise ValueError('doc_id must be provided') if replication_document is None: raise ValueError('replication_document must be provided') if isinstance(replication_document, ReplicationDocument): replication_document = convert_model(replication_document) headers = { - 'If-Match': if_match + 'If-Match': if_match, } - sdk_headers = get_sdk_headers(service_name=self.DEFAULT_SERVICE_NAME, - service_version='V1', - operation_id='put_replication_document') + sdk_headers = get_sdk_headers( + service_name=self.DEFAULT_SERVICE_NAME, + service_version='V1', + operation_id='put_replication_document', + ) headers.update(sdk_headers) params = { 'batch': batch, 'new_edits': new_edits, - 'rev': rev + 'rev': rev, } data = json.dumps(replication_document) @@ -6328,22 +6764,24 @@ def put_replication_document(self, path_param_values = self.encode_path_vars(doc_id) path_param_dict = dict(zip(path_param_keys, path_param_values)) url = '/_replicator/{doc_id}'.format(**path_param_dict) - request = self.prepare_request(method='PUT', - url=url, - headers=headers, - params=params, - data=data) + request = self.prepare_request( + method='PUT', + url=url, + headers=headers, + params=params, + data=data, + ) response = self.send(request, **kwargs) return response - - def get_scheduler_docs(self, + def get_scheduler_docs( + self, *, - limit: int = None, - skip: int = None, - states: List[str] = None, - **kwargs + limit: Optional[int] = None, + skip: Optional[int] = None, + states: Optional[List[str]] = None, + **kwargs, ) -> DetailedResponse: """ Retrieve replication scheduler documents. @@ -6365,15 +6803,17 @@ def get_scheduler_docs(self, """ headers = {} - sdk_headers = get_sdk_headers(service_name=self.DEFAULT_SERVICE_NAME, - service_version='V1', - operation_id='get_scheduler_docs') + sdk_headers = get_sdk_headers( + service_name=self.DEFAULT_SERVICE_NAME, + service_version='V1', + operation_id='get_scheduler_docs', + ) headers.update(sdk_headers) params = { 'limit': limit, 'skip': skip, - 'states': convert_list(states) + 'states': convert_list(states), } if 'headers' in kwargs: @@ -6382,18 +6822,20 @@ def get_scheduler_docs(self, headers['Accept'] = 'application/json' url = '/_scheduler/docs' - request = self.prepare_request(method='GET', - url=url, - headers=headers, - params=params) + request = self.prepare_request( + method='GET', + url=url, + headers=headers, + params=params, + ) response = self.send(request, **kwargs) return response - - def get_scheduler_document(self, + def get_scheduler_document( + self, doc_id: str, - **kwargs + **kwargs, ) -> DetailedResponse: """ Retrieve a replication scheduler document. @@ -6408,12 +6850,14 @@ def get_scheduler_document(self, :rtype: DetailedResponse with `dict` result representing a `SchedulerDocument` object """ - if doc_id is None: + if not doc_id: raise ValueError('doc_id must be provided') headers = {} - sdk_headers = get_sdk_headers(service_name=self.DEFAULT_SERVICE_NAME, - service_version='V1', - operation_id='get_scheduler_document') + sdk_headers = get_sdk_headers( + service_name=self.DEFAULT_SERVICE_NAME, + service_version='V1', + operation_id='get_scheduler_document', + ) headers.update(sdk_headers) if 'headers' in kwargs: @@ -6425,19 +6869,21 @@ def get_scheduler_document(self, path_param_values = self.encode_path_vars(doc_id) path_param_dict = dict(zip(path_param_keys, path_param_values)) url = '/_scheduler/docs/_replicator/{doc_id}'.format(**path_param_dict) - request = self.prepare_request(method='GET', - url=url, - headers=headers) + request = self.prepare_request( + method='GET', + url=url, + headers=headers, + ) response = self.send(request, **kwargs) return response - - def get_scheduler_jobs(self, + def get_scheduler_jobs( + self, *, - limit: int = None, - skip: int = None, - **kwargs + limit: Optional[int] = None, + skip: Optional[int] = None, + **kwargs, ) -> DetailedResponse: """ Retrieve replication scheduler jobs. @@ -6449,7 +6895,7 @@ def get_scheduler_jobs(self, replication ID, history of recent events, and other information. :param int limit: (optional) Query parameter to specify the number of - returned documents to limit the result to. + returned jobs to limit the result to. :param int skip: (optional) Query parameter to specify the number of records before starting to return the results. :param dict headers: A `dict` containing the request headers @@ -6458,14 +6904,16 @@ def get_scheduler_jobs(self, """ headers = {} - sdk_headers = get_sdk_headers(service_name=self.DEFAULT_SERVICE_NAME, - service_version='V1', - operation_id='get_scheduler_jobs') + sdk_headers = get_sdk_headers( + service_name=self.DEFAULT_SERVICE_NAME, + service_version='V1', + operation_id='get_scheduler_jobs', + ) headers.update(sdk_headers) params = { 'limit': limit, - 'skip': skip + 'skip': skip, } if 'headers' in kwargs: @@ -6474,18 +6922,20 @@ def get_scheduler_jobs(self, headers['Accept'] = 'application/json' url = '/_scheduler/jobs' - request = self.prepare_request(method='GET', - url=url, - headers=headers, - params=params) + request = self.prepare_request( + method='GET', + url=url, + headers=headers, + params=params, + ) response = self.send(request, **kwargs) return response - - def get_scheduler_job(self, + def get_scheduler_job( + self, job_id: str, - **kwargs + **kwargs, ) -> DetailedResponse: """ Retrieve a replication scheduler job. @@ -6498,12 +6948,14 @@ def get_scheduler_job(self, :rtype: DetailedResponse with `dict` result representing a `SchedulerJob` object """ - if job_id is None: + if not job_id: raise ValueError('job_id must be provided') headers = {} - sdk_headers = get_sdk_headers(service_name=self.DEFAULT_SERVICE_NAME, - service_version='V1', - operation_id='get_scheduler_job') + sdk_headers = get_sdk_headers( + service_name=self.DEFAULT_SERVICE_NAME, + service_version='V1', + operation_id='get_scheduler_job', + ) headers.update(sdk_headers) if 'headers' in kwargs: @@ -6515,9 +6967,11 @@ def get_scheduler_job(self, path_param_values = self.encode_path_vars(job_id) path_param_dict = dict(zip(path_param_keys, path_param_values)) url = '/_scheduler/jobs/{job_id}'.format(**path_param_dict) - request = self.prepare_request(method='GET', - url=url, - headers=headers) + request = self.prepare_request( + method='GET', + url=url, + headers=headers, + ) response = self.send(request, **kwargs) return response @@ -6526,9 +6980,9 @@ def get_scheduler_job(self, # Authentication ######################### - - def get_session_information(self, - **kwargs + def get_session_information( + self, + **kwargs, ) -> DetailedResponse: """ Retrieve current session cookie information. @@ -6541,9 +6995,11 @@ def get_session_information(self, """ headers = {} - sdk_headers = get_sdk_headers(service_name=self.DEFAULT_SERVICE_NAME, - service_version='V1', - operation_id='get_session_information') + sdk_headers = get_sdk_headers( + service_name=self.DEFAULT_SERVICE_NAME, + service_version='V1', + operation_id='get_session_information', + ) headers.update(sdk_headers) if 'headers' in kwargs: @@ -6552,9 +7008,11 @@ def get_session_information(self, headers['Accept'] = 'application/json' url = '/_session' - request = self.prepare_request(method='GET', - url=url, - headers=headers) + request = self.prepare_request( + method='GET', + url=url, + headers=headers, + ) response = self.send(request, **kwargs) return response @@ -6563,32 +7021,29 @@ def get_session_information(self, # Authorization ######################### - - def get_security(self, - db: str, - **kwargs + def post_api_keys( + self, + **kwargs, ) -> DetailedResponse: """ - Retrieve database permissions information. + Generates API keys for apps or persons to enable database access. - See who has permission to read, write, and manage the database. The credentials - you use to log in to the dashboard automatically include `_admin` permissions to - all databases you create. Everyone and everything else, including users you share - databases with and API keys you create, must be given a permission level - explicitly. + Generates API keys to enable database access for a person or application, but + without creating a new IBM Cloudant account for that person or application. An API + key is a randomly generated username and password. The key is given the wanted + access permissions for a database. - :param str db: Path parameter to specify the database name. :param dict headers: A `dict` containing the request headers :return: A `DetailedResponse` containing the result, headers and HTTP status code. - :rtype: DetailedResponse with `dict` result representing a `Security` object + :rtype: DetailedResponse with `dict` result representing a `ApiKeysResult` object """ - if db is None: - raise ValueError('db must be provided') headers = {} - sdk_headers = get_sdk_headers(service_name=self.DEFAULT_SERVICE_NAME, - service_version='V1', - operation_id='get_security') + sdk_headers = get_sdk_headers( + service_name=self.DEFAULT_SERVICE_NAME, + service_version='V1', + operation_id='post_api_keys', + ) headers.update(sdk_headers) if 'headers' in kwargs: @@ -6596,35 +7051,32 @@ def get_security(self, del kwargs['headers'] headers['Accept'] = 'application/json' - path_param_keys = ['db'] - path_param_values = self.encode_path_vars(db) - path_param_dict = dict(zip(path_param_keys, path_param_values)) - url = '/{db}/_security'.format(**path_param_dict) - request = self.prepare_request(method='GET', - url=url, - headers=headers) + url = '/_api/v2/api_keys' + request = self.prepare_request( + method='POST', + url=url, + headers=headers, + ) response = self.send(request, **kwargs) return response - - def put_security(self, + def put_cloudant_security_configuration( + self, db: str, + cloudant: dict, *, - admins: 'SecurityObject' = None, - members: 'SecurityObject' = None, - cloudant: dict = None, - couchdb_auth_only: bool = None, - **kwargs + admins: Optional['SecurityObject'] = None, + couchdb_auth_only: Optional[bool] = None, + members: Optional['SecurityObject'] = None, + **kwargs, ) -> DetailedResponse: """ - Modify database permissions. + Modify only Cloudant related database permissions. - Modify who has permission to read, write, or manage a database. This endpoint can - be used to modify both Cloudant and CouchDB related permissions. Be careful: by - removing a Cloudant API key, a member or an admin from the list of users that have - access permissions, you remove it from the list of users that have access to the - database. + Modify only Cloudant related permissions to database. Be careful: by removing an + API key from the list, you remove the API key from the list of users that have + access to the database. ### Note about nobody role The `nobody` username applies to all unauthenticated connection attempts. For example, if an application tries to read data from a database, but did not @@ -6632,36 +7084,40 @@ def put_security(self, `_reader`. :param str db: Path parameter to specify the database name. + :param dict cloudant: Database permissions for Cloudant users, API keys or + both. :param SecurityObject admins: (optional) Schema for names and roles to map to a database permission. - :param SecurityObject members: (optional) Schema for names and roles to map - to a database permission. - :param dict cloudant: (optional) Database permissions for Cloudant users - and/or API keys. :param bool couchdb_auth_only: (optional) Manage permissions using the `_users` database only. + :param SecurityObject members: (optional) Schema for names and roles to map + to a database permission. :param dict headers: A `dict` containing the request headers :return: A `DetailedResponse` containing the result, headers and HTTP status code. :rtype: DetailedResponse with `dict` result representing a `Ok` object """ - if db is None: + if not db: raise ValueError('db must be provided') + if cloudant is None: + raise ValueError('cloudant must be provided') if admins is not None: admins = convert_model(admins) if members is not None: members = convert_model(members) headers = {} - sdk_headers = get_sdk_headers(service_name=self.DEFAULT_SERVICE_NAME, - service_version='V1', - operation_id='put_security') + sdk_headers = get_sdk_headers( + service_name=self.DEFAULT_SERVICE_NAME, + service_version='V1', + operation_id='put_cloudant_security_configuration', + ) headers.update(sdk_headers) data = { + 'cloudant': cloudant, 'admins': admins, + 'couchdb_auth_only': couchdb_auth_only, 'members': members, - 'cloudant': cloudant, - 'couchdb_auth_only': couchdb_auth_only } data = {k: v for (k, v) in data.items() if v is not None} data = json.dumps(data) @@ -6675,36 +7131,45 @@ def put_security(self, path_param_keys = ['db'] path_param_values = self.encode_path_vars(db) path_param_dict = dict(zip(path_param_keys, path_param_values)) - url = '/{db}/_security'.format(**path_param_dict) - request = self.prepare_request(method='PUT', - url=url, - headers=headers, - data=data) + url = '/_api/v2/db/{db}/_security'.format(**path_param_dict) + request = self.prepare_request( + method='PUT', + url=url, + headers=headers, + data=data, + ) response = self.send(request, **kwargs) return response - - def post_api_keys(self, - **kwargs + def get_security( + self, + db: str, + **kwargs, ) -> DetailedResponse: """ - Generates API keys for apps or persons to enable database access. + Retrieve database permissions information. - Generates API keys to enable database access for a person or application, but - without creating a new IBM Cloudant account for that person or application. An API - key is a randomly generated username and password. The key is given the wanted - access permissions for a database. + See who has permission to read, write, and manage the database. The credentials + you use to log in to the dashboard automatically include `_admin` permissions to + all databases you create. Everyone and everything else, including users you share + databases with and API keys you create, must be given a permission level + explicitly. + :param str db: Path parameter to specify the database name. :param dict headers: A `dict` containing the request headers :return: A `DetailedResponse` containing the result, headers and HTTP status code. - :rtype: DetailedResponse with `dict` result representing a `ApiKeysResult` object + :rtype: DetailedResponse with `dict` result representing a `Security` object """ + if not db: + raise ValueError('db must be provided') headers = {} - sdk_headers = get_sdk_headers(service_name=self.DEFAULT_SERVICE_NAME, - service_version='V1', - operation_id='post_api_keys') + sdk_headers = get_sdk_headers( + service_name=self.DEFAULT_SERVICE_NAME, + service_version='V1', + operation_id='get_security', + ) headers.update(sdk_headers) if 'headers' in kwargs: @@ -6712,30 +7177,37 @@ def post_api_keys(self, del kwargs['headers'] headers['Accept'] = 'application/json' - url = '/_api/v2/api_keys' - request = self.prepare_request(method='POST', - url=url, - headers=headers) + path_param_keys = ['db'] + path_param_values = self.encode_path_vars(db) + path_param_dict = dict(zip(path_param_keys, path_param_values)) + url = '/{db}/_security'.format(**path_param_dict) + request = self.prepare_request( + method='GET', + url=url, + headers=headers, + ) response = self.send(request, **kwargs) return response - - def put_cloudant_security_configuration(self, + def put_security( + self, db: str, - cloudant: dict, *, - admins: 'SecurityObject' = None, - members: 'SecurityObject' = None, - couchdb_auth_only: bool = None, - **kwargs + admins: Optional['SecurityObject'] = None, + cloudant: Optional[dict] = None, + couchdb_auth_only: Optional[bool] = None, + members: Optional['SecurityObject'] = None, + **kwargs, ) -> DetailedResponse: """ - Modify only Cloudant related database permissions. + Modify database permissions. - Modify only Cloudant related permissions to database. Be careful: by removing an - API key from the list, you remove the API key from the list of users that have - access to the database. + Modify who has permission to read, write, or manage a database. This endpoint can + be used to modify both Cloudant and CouchDB related permissions. Be careful: by + removing a Cloudant API key, a member or an admin from the list of users that have + access permissions, you remove it from the list of users that have access to the + database. ### Note about nobody role The `nobody` username applies to all unauthenticated connection attempts. For example, if an application tries to read data from a database, but did not @@ -6743,38 +7215,38 @@ def put_cloudant_security_configuration(self, `_reader`. :param str db: Path parameter to specify the database name. - :param dict cloudant: Database permissions for Cloudant users and/or API - keys. :param SecurityObject admins: (optional) Schema for names and roles to map to a database permission. - :param SecurityObject members: (optional) Schema for names and roles to map - to a database permission. + :param dict cloudant: (optional) Database permissions for Cloudant users, + API keys or both. :param bool couchdb_auth_only: (optional) Manage permissions using the `_users` database only. + :param SecurityObject members: (optional) Schema for names and roles to map + to a database permission. :param dict headers: A `dict` containing the request headers :return: A `DetailedResponse` containing the result, headers and HTTP status code. :rtype: DetailedResponse with `dict` result representing a `Ok` object """ - if db is None: + if not db: raise ValueError('db must be provided') - if cloudant is None: - raise ValueError('cloudant must be provided') if admins is not None: admins = convert_model(admins) if members is not None: members = convert_model(members) headers = {} - sdk_headers = get_sdk_headers(service_name=self.DEFAULT_SERVICE_NAME, - service_version='V1', - operation_id='put_cloudant_security_configuration') + sdk_headers = get_sdk_headers( + service_name=self.DEFAULT_SERVICE_NAME, + service_version='V1', + operation_id='put_security', + ) headers.update(sdk_headers) data = { - 'cloudant': cloudant, 'admins': admins, + 'cloudant': cloudant, + 'couchdb_auth_only': couchdb_auth_only, 'members': members, - 'couchdb_auth_only': couchdb_auth_only } data = {k: v for (k, v) in data.items() if v is not None} data = json.dumps(data) @@ -6788,11 +7260,13 @@ def put_cloudant_security_configuration(self, path_param_keys = ['db'] path_param_values = self.encode_path_vars(db) path_param_dict = dict(zip(path_param_keys, path_param_values)) - url = '/_api/v2/db/{db}/_security'.format(**path_param_dict) - request = self.prepare_request(method='PUT', - url=url, - headers=headers, - data=data) + url = '/{db}/_security'.format(**path_param_dict) + request = self.prepare_request( + method='PUT', + url=url, + headers=headers, + data=data, + ) response = self.send(request, **kwargs) return response @@ -6801,9 +7275,9 @@ def put_cloudant_security_configuration(self, # CORS ######################### - - def get_cors_information(self, - **kwargs + def get_cors_information( + self, + **kwargs, ) -> DetailedResponse: """ Retrieve CORS configuration information. @@ -6818,9 +7292,11 @@ def get_cors_information(self, """ headers = {} - sdk_headers = get_sdk_headers(service_name=self.DEFAULT_SERVICE_NAME, - service_version='V1', - operation_id='get_cors_information') + sdk_headers = get_sdk_headers( + service_name=self.DEFAULT_SERVICE_NAME, + service_version='V1', + operation_id='get_cors_information', + ) headers.update(sdk_headers) if 'headers' in kwargs: @@ -6829,20 +7305,22 @@ def get_cors_information(self, headers['Accept'] = 'application/json' url = '/_api/v2/user/config/cors' - request = self.prepare_request(method='GET', - url=url, - headers=headers) + request = self.prepare_request( + method='GET', + url=url, + headers=headers, + ) response = self.send(request, **kwargs) return response - - def put_cors_configuration(self, + def put_cors_configuration( + self, origins: List[str], *, - allow_credentials: bool = None, - enable_cors: bool = None, - **kwargs + allow_credentials: Optional[bool] = None, + enable_cors: Optional[bool] = None, + **kwargs, ) -> DetailedResponse: """ Modify CORS configuration. @@ -6866,15 +7344,17 @@ def put_cors_configuration(self, if origins is None: raise ValueError('origins must be provided') headers = {} - sdk_headers = get_sdk_headers(service_name=self.DEFAULT_SERVICE_NAME, - service_version='V1', - operation_id='put_cors_configuration') + sdk_headers = get_sdk_headers( + service_name=self.DEFAULT_SERVICE_NAME, + service_version='V1', + operation_id='put_cors_configuration', + ) headers.update(sdk_headers) data = { 'origins': origins, 'allow_credentials': allow_credentials, - 'enable_cors': enable_cors + 'enable_cors': enable_cors, } data = {k: v for (k, v) in data.items() if v is not None} data = json.dumps(data) @@ -6886,10 +7366,12 @@ def put_cors_configuration(self, headers['Accept'] = 'application/json' url = '/_api/v2/user/config/cors' - request = self.prepare_request(method='PUT', - url=url, - headers=headers, - data=data) + request = self.prepare_request( + method='PUT', + url=url, + headers=headers, + data=data, + ) response = self.send(request, **kwargs) return response @@ -6898,16 +7380,16 @@ def put_cors_configuration(self, # Attachments ######################### - - def head_attachment(self, + def head_attachment( + self, db: str, doc_id: str, attachment_name: str, *, - if_match: str = None, - if_none_match: str = None, - rev: str = None, - **kwargs + if_match: Optional[str] = None, + if_none_match: Optional[str] = None, + rev: Optional[str] = None, + **kwargs, ) -> DetailedResponse: """ Retrieve the HTTP headers for an attachment. @@ -6920,33 +7402,35 @@ def head_attachment(self, :param str db: Path parameter to specify the database name. :param str doc_id: Path parameter to specify the document ID. :param str attachment_name: Path parameter to specify the attachment name. - :param str if_match: (optional) Header parameter to specify the document - revision. Alternative to rev query parameter. - :param str if_none_match: (optional) Header parameter to specify a double - quoted document revision token for cache control. + :param str if_match: (optional) Header parameter for a conditional HTTP + request matching an ETag. + :param str if_none_match: (optional) Header parameter for a conditional + HTTP request not matching an ETag. :param str rev: (optional) Query parameter to specify a document revision. :param dict headers: A `dict` containing the request headers :return: A `DetailedResponse` containing the result, headers and HTTP status code. :rtype: DetailedResponse """ - if db is None: + if not db: raise ValueError('db must be provided') - if doc_id is None: + if not doc_id: raise ValueError('doc_id must be provided') - if attachment_name is None: + if not attachment_name: raise ValueError('attachment_name must be provided') headers = { 'If-Match': if_match, - 'If-None-Match': if_none_match + 'If-None-Match': if_none_match, } - sdk_headers = get_sdk_headers(service_name=self.DEFAULT_SERVICE_NAME, - service_version='V1', - operation_id='head_attachment') + sdk_headers = get_sdk_headers( + service_name=self.DEFAULT_SERVICE_NAME, + service_version='V1', + operation_id='head_attachment', + ) headers.update(sdk_headers) params = { - 'rev': rev + 'rev': rev, } if 'headers' in kwargs: @@ -6957,24 +7441,26 @@ def head_attachment(self, path_param_values = self.encode_path_vars(db, doc_id, attachment_name) path_param_dict = dict(zip(path_param_keys, path_param_values)) url = '/{db}/{doc_id}/{attachment_name}'.format(**path_param_dict) - request = self.prepare_request(method='HEAD', - url=url, - headers=headers, - params=params) + request = self.prepare_request( + method='HEAD', + url=url, + headers=headers, + params=params, + ) response = self.send(request, **kwargs) return response - - def delete_attachment(self, + def delete_attachment( + self, db: str, doc_id: str, attachment_name: str, *, - if_match: str = None, - rev: str = None, - batch: str = None, - **kwargs + if_match: Optional[str] = None, + rev: Optional[str] = None, + batch: Optional[str] = None, + **kwargs, ) -> DetailedResponse: """ Delete an attachment. @@ -6986,8 +7472,8 @@ def delete_attachment(self, :param str db: Path parameter to specify the database name. :param str doc_id: Path parameter to specify the document ID. :param str attachment_name: Path parameter to specify the attachment name. - :param str if_match: (optional) Header parameter to specify the document - revision. Alternative to rev query parameter. + :param str if_match: (optional) Header parameter for a conditional HTTP + request matching an ETag. :param str rev: (optional) Query parameter to specify a document revision. :param str batch: (optional) Query parameter to specify whether to store in batch mode. The server will respond with a HTTP 202 Accepted response code @@ -6997,23 +7483,25 @@ def delete_attachment(self, :rtype: DetailedResponse with `dict` result representing a `DocumentResult` object """ - if db is None: + if not db: raise ValueError('db must be provided') - if doc_id is None: + if not doc_id: raise ValueError('doc_id must be provided') - if attachment_name is None: + if not attachment_name: raise ValueError('attachment_name must be provided') headers = { - 'If-Match': if_match + 'If-Match': if_match, } - sdk_headers = get_sdk_headers(service_name=self.DEFAULT_SERVICE_NAME, - service_version='V1', - operation_id='delete_attachment') + sdk_headers = get_sdk_headers( + service_name=self.DEFAULT_SERVICE_NAME, + service_version='V1', + operation_id='delete_attachment', + ) headers.update(sdk_headers) params = { 'rev': rev, - 'batch': batch + 'batch': batch, } if 'headers' in kwargs: @@ -7025,25 +7513,27 @@ def delete_attachment(self, path_param_values = self.encode_path_vars(db, doc_id, attachment_name) path_param_dict = dict(zip(path_param_keys, path_param_values)) url = '/{db}/{doc_id}/{attachment_name}'.format(**path_param_dict) - request = self.prepare_request(method='DELETE', - url=url, - headers=headers, - params=params) + request = self.prepare_request( + method='DELETE', + url=url, + headers=headers, + params=params, + ) response = self.send(request, **kwargs) return response - - def get_attachment(self, + def get_attachment( + self, db: str, doc_id: str, attachment_name: str, *, - if_match: str = None, - if_none_match: str = None, - range: str = None, - rev: str = None, - **kwargs + if_match: Optional[str] = None, + if_none_match: Optional[str] = None, + range: Optional[str] = None, + rev: Optional[str] = None, + **kwargs, ) -> DetailedResponse: """ Retrieve an attachment. @@ -7056,10 +7546,10 @@ def get_attachment(self, :param str db: Path parameter to specify the database name. :param str doc_id: Path parameter to specify the document ID. :param str attachment_name: Path parameter to specify the attachment name. - :param str if_match: (optional) Header parameter to specify the document - revision. Alternative to rev query parameter. - :param str if_none_match: (optional) Header parameter to specify a double - quoted document revision token for cache control. + :param str if_match: (optional) Header parameter for a conditional HTTP + request matching an ETag. + :param str if_none_match: (optional) Header parameter for a conditional + HTTP request not matching an ETag. :param str range: (optional) Header parameter to specify the byte range for a request. This allows the implementation of resumable downloads and skippable streams. This is available for all attachments inside CouchDB. @@ -7069,24 +7559,26 @@ def get_attachment(self, :rtype: DetailedResponse with `BinaryIO` result """ - if db is None: + if not db: raise ValueError('db must be provided') - if doc_id is None: + if not doc_id: raise ValueError('doc_id must be provided') - if attachment_name is None: + if not attachment_name: raise ValueError('attachment_name must be provided') headers = { 'If-Match': if_match, 'If-None-Match': if_none_match, - 'Range': range + 'Range': range, } - sdk_headers = get_sdk_headers(service_name=self.DEFAULT_SERVICE_NAME, - service_version='V1', - operation_id='get_attachment') + sdk_headers = get_sdk_headers( + service_name=self.DEFAULT_SERVICE_NAME, + service_version='V1', + operation_id='get_attachment', + ) headers.update(sdk_headers) params = { - 'rev': rev + 'rev': rev, } if 'headers' in kwargs: @@ -7098,25 +7590,27 @@ def get_attachment(self, path_param_values = self.encode_path_vars(db, doc_id, attachment_name) path_param_dict = dict(zip(path_param_keys, path_param_values)) url = '/{db}/{doc_id}/{attachment_name}'.format(**path_param_dict) - request = self.prepare_request(method='GET', - url=url, - headers=headers, - params=params) + request = self.prepare_request( + method='GET', + url=url, + headers=headers, + params=params, + ) response = self.send(request, **kwargs) return response - - def put_attachment(self, + def put_attachment( + self, db: str, doc_id: str, attachment_name: str, attachment: BinaryIO, content_type: str, *, - if_match: str = None, - rev: str = None, - **kwargs + if_match: Optional[str] = None, + rev: Optional[str] = None, + **kwargs, ) -> DetailedResponse: """ Create or modify an attachment. @@ -7136,35 +7630,37 @@ def put_attachment(self, :param str attachment_name: Path parameter to specify the attachment name. :param BinaryIO attachment: HTTP request body for attachment operations. :param str content_type: Content-Type of the attachment. - :param str if_match: (optional) Header parameter to specify the document - revision. Alternative to rev query parameter. + :param str if_match: (optional) Header parameter for a conditional HTTP + request matching an ETag. :param str rev: (optional) Query parameter to specify a document revision. :param dict headers: A `dict` containing the request headers :return: A `DetailedResponse` containing the result, headers and HTTP status code. :rtype: DetailedResponse with `dict` result representing a `DocumentResult` object """ - if db is None: + if not db: raise ValueError('db must be provided') - if doc_id is None: + if not doc_id: raise ValueError('doc_id must be provided') - if attachment_name is None: + if not attachment_name: raise ValueError('attachment_name must be provided') if attachment is None: raise ValueError('attachment must be provided') - if content_type is None: + if not content_type: raise ValueError('content_type must be provided') headers = { 'Content-Type': content_type, - 'If-Match': if_match + 'If-Match': if_match, } - sdk_headers = get_sdk_headers(service_name=self.DEFAULT_SERVICE_NAME, - service_version='V1', - operation_id='put_attachment') + sdk_headers = get_sdk_headers( + service_name=self.DEFAULT_SERVICE_NAME, + service_version='V1', + operation_id='put_attachment', + ) headers.update(sdk_headers) params = { - 'rev': rev + 'rev': rev, } data = attachment @@ -7178,11 +7674,13 @@ def put_attachment(self, path_param_values = self.encode_path_vars(db, doc_id, attachment_name) path_param_dict = dict(zip(path_param_keys, path_param_values)) url = '/{db}/{doc_id}/{attachment_name}'.format(**path_param_dict) - request = self.prepare_request(method='PUT', - url=url, - headers=headers, - params=params, - data=data) + request = self.prepare_request( + method='PUT', + url=url, + headers=headers, + params=params, + data=data, + ) response = self.send(request, **kwargs) return response @@ -7191,13 +7689,13 @@ def put_attachment(self, # Local Documents ######################### - - def head_local_document(self, + def head_local_document( + self, db: str, doc_id: str, *, - if_none_match: str = None, - **kwargs + if_none_match: Optional[str] = None, + **kwargs, ) -> DetailedResponse: """ Retrieve HTTP headers for a local document. @@ -7208,23 +7706,25 @@ def head_local_document(self, :param str db: Path parameter to specify the database name. :param str doc_id: Path parameter to specify the document ID. - :param str if_none_match: (optional) Header parameter to specify a double - quoted document revision token for cache control. + :param str if_none_match: (optional) Header parameter for a conditional + HTTP request not matching an ETag. :param dict headers: A `dict` containing the request headers :return: A `DetailedResponse` containing the result, headers and HTTP status code. :rtype: DetailedResponse """ - if db is None: + if not db: raise ValueError('db must be provided') - if doc_id is None: + if not doc_id: raise ValueError('doc_id must be provided') headers = { - 'If-None-Match': if_none_match + 'If-None-Match': if_none_match, } - sdk_headers = get_sdk_headers(service_name=self.DEFAULT_SERVICE_NAME, - service_version='V1', - operation_id='head_local_document') + sdk_headers = get_sdk_headers( + service_name=self.DEFAULT_SERVICE_NAME, + service_version='V1', + operation_id='head_local_document', + ) headers.update(sdk_headers) if 'headers' in kwargs: @@ -7235,20 +7735,22 @@ def head_local_document(self, path_param_values = self.encode_path_vars(db, doc_id) path_param_dict = dict(zip(path_param_keys, path_param_values)) url = '/{db}/_local/{doc_id}'.format(**path_param_dict) - request = self.prepare_request(method='HEAD', - url=url, - headers=headers) + request = self.prepare_request( + method='HEAD', + url=url, + headers=headers, + ) response = self.send(request, **kwargs) return response - - def delete_local_document(self, + def delete_local_document( + self, db: str, doc_id: str, *, - batch: str = None, - **kwargs + batch: Optional[str] = None, + **kwargs, ) -> DetailedResponse: """ Delete a local document. @@ -7267,18 +7769,20 @@ def delete_local_document(self, :rtype: DetailedResponse with `dict` result representing a `DocumentResult` object """ - if db is None: + if not db: raise ValueError('db must be provided') - if doc_id is None: + if not doc_id: raise ValueError('doc_id must be provided') headers = {} - sdk_headers = get_sdk_headers(service_name=self.DEFAULT_SERVICE_NAME, - service_version='V1', - operation_id='delete_local_document') + sdk_headers = get_sdk_headers( + service_name=self.DEFAULT_SERVICE_NAME, + service_version='V1', + operation_id='delete_local_document', + ) headers.update(sdk_headers) params = { - 'batch': batch + 'batch': batch, } if 'headers' in kwargs: @@ -7290,25 +7794,27 @@ def delete_local_document(self, path_param_values = self.encode_path_vars(db, doc_id) path_param_dict = dict(zip(path_param_keys, path_param_values)) url = '/{db}/_local/{doc_id}'.format(**path_param_dict) - request = self.prepare_request(method='DELETE', - url=url, - headers=headers, - params=params) + request = self.prepare_request( + method='DELETE', + url=url, + headers=headers, + params=params, + ) response = self.send(request, **kwargs) return response - - def get_local_document(self, + def get_local_document( + self, db: str, doc_id: str, *, - accept: str = None, - if_none_match: str = None, - attachments: bool = None, - att_encoding_info: bool = None, - local_seq: bool = None, - **kwargs + accept: Optional[str] = None, + if_none_match: Optional[str] = None, + attachments: Optional[bool] = None, + att_encoding_info: Optional[bool] = None, + local_seq: Optional[bool] = None, + **kwargs, ) -> DetailedResponse: """ Retrieve a local document. @@ -7321,13 +7827,16 @@ def get_local_document(self, :param str doc_id: Path parameter to specify the document ID. :param str accept: (optional) The type of the response: application/json, multipart/mixed, multipart/related, or application/octet-stream. - :param str if_none_match: (optional) Header parameter to specify a double - quoted document revision token for cache control. + :param str if_none_match: (optional) Header parameter for a conditional + HTTP request not matching an ETag. :param bool attachments: (optional) Query parameter to specify whether to - include attachments bodies in a response. + include attachment content in the response. Note that when used with a + view-style query or changes feed this only applies when requesting + documents in the response. :param bool att_encoding_info: (optional) Query parameter to specify - whether to include the encoding information in attachment stubs if the - particular attachment is compressed. + whether to include the encoding information for compressed attachments. + Note that when used with a view-style query or changes feed this only + applies when requesting documents in the response. :param bool local_seq: (optional) Query parameter to specify whether to include the last update sequence for the document. :param dict headers: A `dict` containing the request headers @@ -7335,23 +7844,25 @@ def get_local_document(self, :rtype: DetailedResponse with `dict` result representing a `Document` object """ - if db is None: + if not db: raise ValueError('db must be provided') - if doc_id is None: + if not doc_id: raise ValueError('doc_id must be provided') headers = { 'Accept': accept, - 'If-None-Match': if_none_match + 'If-None-Match': if_none_match, } - sdk_headers = get_sdk_headers(service_name=self.DEFAULT_SERVICE_NAME, - service_version='V1', - operation_id='get_local_document') + sdk_headers = get_sdk_headers( + service_name=self.DEFAULT_SERVICE_NAME, + service_version='V1', + operation_id='get_local_document', + ) headers.update(sdk_headers) params = { 'attachments': attachments, 'att_encoding_info': att_encoding_info, - 'local_seq': local_seq + 'local_seq': local_seq, } if 'headers' in kwargs: @@ -7362,23 +7873,25 @@ def get_local_document(self, path_param_values = self.encode_path_vars(db, doc_id) path_param_dict = dict(zip(path_param_keys, path_param_values)) url = '/{db}/_local/{doc_id}'.format(**path_param_dict) - request = self.prepare_request(method='GET', - url=url, - headers=headers, - params=params) + request = self.prepare_request( + method='GET', + url=url, + headers=headers, + params=params, + ) response = self.send(request, **kwargs) return response - - def put_local_document(self, + def put_local_document( + self, db: str, doc_id: str, document: Union['Document', BinaryIO], *, - content_type: str = None, - batch: str = None, - **kwargs + content_type: Optional[str] = None, + batch: Optional[str] = None, + **kwargs, ) -> DetailedResponse: """ Create or modify a local document. @@ -7399,9 +7912,9 @@ def put_local_document(self, :rtype: DetailedResponse with `dict` result representing a `DocumentResult` object """ - if db is None: + if not db: raise ValueError('db must be provided') - if doc_id is None: + if not doc_id: raise ValueError('doc_id must be provided') if document is None: raise ValueError('document must be provided') @@ -7409,15 +7922,17 @@ def put_local_document(self, document = convert_model(document) content_type = content_type or 'application/json' headers = { - 'Content-Type': content_type + 'Content-Type': content_type, } - sdk_headers = get_sdk_headers(service_name=self.DEFAULT_SERVICE_NAME, - service_version='V1', - operation_id='put_local_document') + sdk_headers = get_sdk_headers( + service_name=self.DEFAULT_SERVICE_NAME, + service_version='V1', + operation_id='put_local_document', + ) headers.update(sdk_headers) params = { - 'batch': batch + 'batch': batch, } if isinstance(document, dict): @@ -7436,11 +7951,13 @@ def put_local_document(self, path_param_values = self.encode_path_vars(db, doc_id) path_param_dict = dict(zip(path_param_keys, path_param_values)) url = '/{db}/_local/{doc_id}'.format(**path_param_dict) - request = self.prepare_request(method='PUT', - url=url, - headers=headers, - params=params, - data=data) + request = self.prepare_request( + method='PUT', + url=url, + headers=headers, + params=params, + data=data, + ) response = self.send(request, **kwargs) return response @@ -7449,11 +7966,11 @@ def put_local_document(self, # Database Details ######################### - - def post_revs_diff(self, + def post_revs_diff( + self, db: str, document_revisions: dict, - **kwargs + **kwargs, ) -> DetailedResponse: """ Query the document revisions and possible ancestors missing from the database. @@ -7471,14 +7988,16 @@ def post_revs_diff(self, :rtype: DetailedResponse with `dict` result representing a `dict` object """ - if db is None: + if not db: raise ValueError('db must be provided') if document_revisions is None: raise ValueError('document_revisions must be provided') headers = {} - sdk_headers = get_sdk_headers(service_name=self.DEFAULT_SERVICE_NAME, - service_version='V1', - operation_id='post_revs_diff') + sdk_headers = get_sdk_headers( + service_name=self.DEFAULT_SERVICE_NAME, + service_version='V1', + operation_id='post_revs_diff', + ) headers.update(sdk_headers) data = json.dumps(document_revisions) @@ -7493,18 +8012,20 @@ def post_revs_diff(self, path_param_values = self.encode_path_vars(db) path_param_dict = dict(zip(path_param_keys, path_param_values)) url = '/{db}/_revs_diff'.format(**path_param_dict) - request = self.prepare_request(method='POST', - url=url, - headers=headers, - data=data) + request = self.prepare_request( + method='POST', + url=url, + headers=headers, + data=data, + ) response = self.send(request, **kwargs) return response - - def get_shards_information(self, + def get_shards_information( + self, db: str, - **kwargs + **kwargs, ) -> DetailedResponse: """ Retrieve shard information. @@ -7517,12 +8038,14 @@ def get_shards_information(self, :rtype: DetailedResponse with `dict` result representing a `ShardsInformation` object """ - if db is None: + if not db: raise ValueError('db must be provided') headers = {} - sdk_headers = get_sdk_headers(service_name=self.DEFAULT_SERVICE_NAME, - service_version='V1', - operation_id='get_shards_information') + sdk_headers = get_sdk_headers( + service_name=self.DEFAULT_SERVICE_NAME, + service_version='V1', + operation_id='get_shards_information', + ) headers.update(sdk_headers) if 'headers' in kwargs: @@ -7534,18 +8057,20 @@ def get_shards_information(self, path_param_values = self.encode_path_vars(db) path_param_dict = dict(zip(path_param_keys, path_param_values)) url = '/{db}/_shards'.format(**path_param_dict) - request = self.prepare_request(method='GET', - url=url, - headers=headers) + request = self.prepare_request( + method='GET', + url=url, + headers=headers, + ) response = self.send(request, **kwargs) return response - - def get_document_shards_info(self, + def get_document_shards_info( + self, db: str, doc_id: str, - **kwargs + **kwargs, ) -> DetailedResponse: """ Retrieve shard information for a specific document. @@ -7560,14 +8085,16 @@ def get_document_shards_info(self, :rtype: DetailedResponse with `dict` result representing a `DocumentShardInfo` object """ - if db is None: + if not db: raise ValueError('db must be provided') - if doc_id is None: + if not doc_id: raise ValueError('doc_id must be provided') headers = {} - sdk_headers = get_sdk_headers(service_name=self.DEFAULT_SERVICE_NAME, - service_version='V1', - operation_id='get_document_shards_info') + sdk_headers = get_sdk_headers( + service_name=self.DEFAULT_SERVICE_NAME, + service_version='V1', + operation_id='get_document_shards_info', + ) headers.update(sdk_headers) if 'headers' in kwargs: @@ -7579,9 +8106,11 @@ def get_document_shards_info(self, path_param_values = self.encode_path_vars(db, doc_id) path_param_dict = dict(zip(path_param_keys, path_param_values)) url = '/{db}/_shards/{doc_id}'.format(**path_param_dict) - request = self.prepare_request(method='GET', - url=url, - headers=headers) + request = self.prepare_request( + method='GET', + url=url, + headers=headers, + ) response = self.send(request, **kwargs) return response @@ -7590,9 +8119,9 @@ def get_document_shards_info(self, # Monitoring ######################### - - def head_up_information(self, - **kwargs + def head_up_information( + self, + **kwargs, ) -> DetailedResponse: """ Retrieve HTTP headers about whether the server is up. @@ -7605,9 +8134,11 @@ def head_up_information(self, """ headers = {} - sdk_headers = get_sdk_headers(service_name=self.DEFAULT_SERVICE_NAME, - service_version='V1', - operation_id='head_up_information') + sdk_headers = get_sdk_headers( + service_name=self.DEFAULT_SERVICE_NAME, + service_version='V1', + operation_id='head_up_information', + ) headers.update(sdk_headers) if 'headers' in kwargs: @@ -7615,16 +8146,18 @@ def head_up_information(self, del kwargs['headers'] url = '/_up' - request = self.prepare_request(method='HEAD', - url=url, - headers=headers) + request = self.prepare_request( + method='HEAD', + url=url, + headers=headers, + ) response = self.send(request, **kwargs) return response - - def get_active_tasks(self, - **kwargs + def get_active_tasks( + self, + **kwargs, ) -> DetailedResponse: """ Retrieve list of running tasks. @@ -7640,9 +8173,11 @@ def get_active_tasks(self, """ headers = {} - sdk_headers = get_sdk_headers(service_name=self.DEFAULT_SERVICE_NAME, - service_version='V1', - operation_id='get_active_tasks') + sdk_headers = get_sdk_headers( + service_name=self.DEFAULT_SERVICE_NAME, + service_version='V1', + operation_id='get_active_tasks', + ) headers.update(sdk_headers) if 'headers' in kwargs: @@ -7651,32 +8186,36 @@ def get_active_tasks(self, headers['Accept'] = 'application/json' url = '/_active_tasks' - request = self.prepare_request(method='GET', - url=url, - headers=headers) + request = self.prepare_request( + method='GET', + url=url, + headers=headers, + ) response = self.send(request, **kwargs) return response - - def get_up_information(self, - **kwargs + def get_activity_tracker_events( + self, + **kwargs, ) -> DetailedResponse: """ - Retrieve information about whether the server is up. + Retrieve activity tracking events information. - Confirms that the server is up, running, and ready to respond to requests. If - `maintenance_mode` is `true` or `nolb`, the endpoint returns a 404 response. + Check event types sent to IBM Cloud Activity Tracker Event Routing for the IBM + Cloudant instance. :param dict headers: A `dict` containing the request headers :return: A `DetailedResponse` containing the result, headers and HTTP status code. - :rtype: DetailedResponse with `dict` result representing a `UpInformation` object + :rtype: DetailedResponse with `dict` result representing a `ActivityTrackerEvents` object """ headers = {} - sdk_headers = get_sdk_headers(service_name=self.DEFAULT_SERVICE_NAME, - service_version='V1', - operation_id='get_up_information') + sdk_headers = get_sdk_headers( + service_name=self.DEFAULT_SERVICE_NAME, + service_version='V1', + operation_id='get_activity_tracker_events', + ) headers.update(sdk_headers) if 'headers' in kwargs: @@ -7684,99 +8223,145 @@ def get_up_information(self, del kwargs['headers'] headers['Accept'] = 'application/json' - url = '/_up' - request = self.prepare_request(method='GET', - url=url, - headers=headers) + url = '/_api/v2/user/activity_tracker/events' + request = self.prepare_request( + method='GET', + url=url, + headers=headers, + ) response = self.send(request, **kwargs) return response - - def get_activity_tracker_events(self, - **kwargs + def post_activity_tracker_events( + self, + types: List[str], + **kwargs, ) -> DetailedResponse: """ - Retrieve Activity Tracker events information. + Modify activity tracking events configuration. - Check event types that are being sent to IBM Cloud Activity Tracker for the IBM + Configure event types sent to IBM Cloud Activity Tracker Event Routing for the IBM Cloudant instance. + :param List[str] types: An array of event types sent to IBM Cloud Activity + Tracker Event Routing for the IBM Cloudant instance. "management" is a + required element of this array. :param dict headers: A `dict` containing the request headers :return: A `DetailedResponse` containing the result, headers and HTTP status code. - :rtype: DetailedResponse with `dict` result representing a `ActivityTrackerEvents` object + :rtype: DetailedResponse with `dict` result representing a `Ok` object """ + if types is None: + raise ValueError('types must be provided') headers = {} - sdk_headers = get_sdk_headers(service_name=self.DEFAULT_SERVICE_NAME, - service_version='V1', - operation_id='get_activity_tracker_events') + sdk_headers = get_sdk_headers( + service_name=self.DEFAULT_SERVICE_NAME, + service_version='V1', + operation_id='post_activity_tracker_events', + ) headers.update(sdk_headers) + data = { + 'types': types, + } + data = {k: v for (k, v) in data.items() if v is not None} + data = json.dumps(data) + headers['content-type'] = 'application/json' + if 'headers' in kwargs: headers.update(kwargs.get('headers')) del kwargs['headers'] headers['Accept'] = 'application/json' url = '/_api/v2/user/activity_tracker/events' - request = self.prepare_request(method='GET', - url=url, - headers=headers) + request = self.prepare_request( + method='POST', + url=url, + headers=headers, + data=data, + ) response = self.send(request, **kwargs) return response - - def post_activity_tracker_events(self, - types: List[str], - **kwargs + def get_capacity_databases_information( + self, + **kwargs, ) -> DetailedResponse: """ - Modify Activity Tracker events configuration. + Retrieve maximum allowed database count. - Configure event types that are being sent to IBM Cloud Activity Tracker for the - IBM Cloudant instance. + Retrieves the maximum number of databases currently allowed in the instance. - :param List[str] types: An array of event types that are being sent to IBM - Cloud Activity Tracker for the IBM Cloudant instance. "management" is a - required element of this array. :param dict headers: A `dict` containing the request headers :return: A `DetailedResponse` containing the result, headers and HTTP status code. - :rtype: DetailedResponse with `dict` result representing a `Ok` object + :rtype: DetailedResponse with `dict` result representing a `CapacityDatabasesInformation` object """ - if types is None: - raise ValueError('types must be provided') headers = {} - sdk_headers = get_sdk_headers(service_name=self.DEFAULT_SERVICE_NAME, - service_version='V1', - operation_id='post_activity_tracker_events') + sdk_headers = get_sdk_headers( + service_name=self.DEFAULT_SERVICE_NAME, + service_version='V1', + operation_id='get_capacity_databases_information', + ) headers.update(sdk_headers) - data = { - 'types': types - } - data = {k: v for (k, v) in data.items() if v is not None} - data = json.dumps(data) - headers['content-type'] = 'application/json' - if 'headers' in kwargs: headers.update(kwargs.get('headers')) del kwargs['headers'] headers['Accept'] = 'application/json' - url = '/_api/v2/user/activity_tracker/events' - request = self.prepare_request(method='POST', - url=url, - headers=headers, - data=data) + url = '/_api/v2/user/capacity/databases' + request = self.prepare_request( + method='GET', + url=url, + headers=headers, + ) response = self.send(request, **kwargs) return response + def get_current_databases_information( + self, + **kwargs, + ) -> DetailedResponse: + """ + Retrieve current database count. + + Retrieves the current number of databases that exist in the instance. + + :param dict headers: A `dict` containing the request headers + :return: A `DetailedResponse` containing the result, headers and HTTP status code. + :rtype: DetailedResponse with `dict` result representing a `CurrentDatabasesInformation` object + """ + + headers = {} + sdk_headers = get_sdk_headers( + service_name=self.DEFAULT_SERVICE_NAME, + service_version='V1', + operation_id='get_current_databases_information', + ) + headers.update(sdk_headers) + + if 'headers' in kwargs: + headers.update(kwargs.get('headers')) + del kwargs['headers'] + headers['Accept'] = 'application/json' + + url = '/_api/v2/user/current/databases' + request = self.prepare_request( + method='GET', + url=url, + headers=headers, + ) + + response = self.send(request, **kwargs) + return response - def get_current_throughput_information(self, - **kwargs + def get_current_throughput_information( + self, + **kwargs, ) -> DetailedResponse: """ Retrieve the current provisioned throughput capacity consumption. @@ -7791,9 +8376,11 @@ def get_current_throughput_information(self, """ headers = {} - sdk_headers = get_sdk_headers(service_name=self.DEFAULT_SERVICE_NAME, - service_version='V1', - operation_id='get_current_throughput_information') + sdk_headers = get_sdk_headers( + service_name=self.DEFAULT_SERVICE_NAME, + service_version='V1', + operation_id='get_current_throughput_information', + ) headers.update(sdk_headers) if 'headers' in kwargs: @@ -7802,9 +8389,89 @@ def get_current_throughput_information(self, headers['Accept'] = 'application/json' url = '/_api/v2/user/current/throughput' - request = self.prepare_request(method='GET', - url=url, - headers=headers) + request = self.prepare_request( + method='GET', + url=url, + headers=headers, + ) + + response = self.send(request, **kwargs) + return response + + def get_membership_information( + self, + **kwargs, + ) -> DetailedResponse: + """ + Retrieve cluster membership information. + + Displays the nodes that are part of the cluster as `cluster_nodes`. The field, + `all_nodes`, displays all nodes this node knows about, including the ones that are + part of the cluster. This endpoint is useful when you set up a cluster. + + :param dict headers: A `dict` containing the request headers + :return: A `DetailedResponse` containing the result, headers and HTTP status code. + :rtype: DetailedResponse with `dict` result representing a `MembershipInformation` object + """ + + headers = {} + sdk_headers = get_sdk_headers( + service_name=self.DEFAULT_SERVICE_NAME, + service_version='V1', + operation_id='get_membership_information', + ) + headers.update(sdk_headers) + + if 'headers' in kwargs: + headers.update(kwargs.get('headers')) + del kwargs['headers'] + headers['Accept'] = 'application/json' + + url = '/_membership' + request = self.prepare_request( + method='GET', + url=url, + headers=headers, + ) + + response = self.send(request, **kwargs) + return response + + def get_up_information( + self, + **kwargs, + ) -> DetailedResponse: + """ + Retrieve information about whether the server is up. + + Confirms that the server is up, running, and ready to respond to requests. If the + cluster is unavailable the endpoint returns a `503` response code. + **Tip:** The authentication for this endpoint is only enforced when using IAM. + + :param dict headers: A `dict` containing the request headers + :return: A `DetailedResponse` containing the result, headers and HTTP status code. + :rtype: DetailedResponse with `dict` result representing a `UpInformation` object + """ + + headers = {} + sdk_headers = get_sdk_headers( + service_name=self.DEFAULT_SERVICE_NAME, + service_version='V1', + operation_id='get_up_information', + ) + headers.update(sdk_headers) + + if 'headers' in kwargs: + headers.update(kwargs.get('headers')) + del kwargs['headers'] + headers['Accept'] = 'application/json' + + url = '/_up' + request = self.prepare_request( + method='GET', + url=url, + headers=headers, + ) response = self.send(request, **kwargs) return response @@ -7819,6 +8486,7 @@ class Feed(str, Enum): """ Query parameter to specify the changes feed type. """ + CONTINUOUS = 'continuous' EVENTSOURCE = 'eventsource' LONGPOLL = 'longpoll' @@ -7834,13 +8502,24 @@ class Feed(str, Enum): """ Query parameter to specify the changes feed type. """ + CONTINUOUS = 'continuous' EVENTSOURCE = 'eventsource' LONGPOLL = 'longpoll' NORMAL = 'normal' + class Style(str, Enum): + """ + Query parameter to specify how many revisions are returned in the changes array. + The default, `main_only`, will only return the current "winning" revision; + all_docs will return all leaf revisions (including conflicts and deleted former + conflicts). + """ + MAIN_ONLY = 'main_only' + ALL_DOCS = 'all_docs' -class PostChangesEnums: + +class PostChangesAsStreamEnums: """ Enums for post_changes_as_stream parameters. """ @@ -7849,10 +8528,21 @@ class Feed(str, Enum): """ Query parameter to specify the changes feed type. """ + CONTINUOUS = 'continuous' EVENTSOURCE = 'eventsource' LONGPOLL = 'longpoll' NORMAL = 'normal' + class Style(str, Enum): + """ + Query parameter to specify how many revisions are returned in the changes array. + The default, `main_only`, will only return the current "winning" revision; + all_docs will return all leaf revisions (including conflicts and deleted former + conflicts). + """ + + MAIN_ONLY = 'main_only' + ALL_DOCS = 'all_docs' class PostDocumentEnums: @@ -7864,6 +8554,7 @@ class ContentType(str, Enum): """ The type of the input. """ + APPLICATION_JSON = 'application/json' MULTIPART_MIXED = 'multipart/mixed' MULTIPART_RELATED = 'multipart/related' @@ -7873,6 +8564,7 @@ class Batch(str, Enum): Query parameter to specify whether to store in batch mode. The server will respond with a HTTP 202 Accepted response code immediately. """ + OK = 'ok' @@ -7886,6 +8578,7 @@ class Batch(str, Enum): Query parameter to specify whether to store in batch mode. The server will respond with a HTTP 202 Accepted response code immediately. """ + OK = 'ok' @@ -7898,6 +8591,7 @@ class ContentType(str, Enum): """ The type of the input. """ + APPLICATION_JSON = 'application/json' MULTIPART_MIXED = 'multipart/mixed' MULTIPART_RELATED = 'multipart/related' @@ -7907,6 +8601,7 @@ class Batch(str, Enum): Query parameter to specify whether to store in batch mode. The server will respond with a HTTP 202 Accepted response code immediately. """ + OK = 'ok' @@ -7920,6 +8615,7 @@ class Batch(str, Enum): Query parameter to specify whether to store in batch mode. The server will respond with a HTTP 202 Accepted response code immediately. """ + OK = 'ok' @@ -7933,20 +8629,8 @@ class Batch(str, Enum): Query parameter to specify whether to store in batch mode. The server will respond with a HTTP 202 Accepted response code immediately. """ - OK = 'ok' - - -class PostDesignDocsEnums: - """ - Enums for post_design_docs parameters. - """ - class Accept(str, Enum): - """ - The type of the response: application/json or application/octet-stream. - """ - APPLICATION_JSON = 'application/json' - APPLICATION_OCTET_STREAM = 'application/octet-stream' + OK = 'ok' class PostDesignDocsQueriesEnums: @@ -7958,6 +8642,7 @@ class Accept(str, Enum): """ The type of the response: application/json or application/octet-stream. """ + APPLICATION_JSON = 'application/json' APPLICATION_OCTET_STREAM = 'application/octet-stream' @@ -7971,82 +8656,23 @@ class Type(str, Enum): """ Path parameter to specify the index type. """ + JSON = 'json' SPECIAL = 'special' TEXT = 'text' -class GetGeoEnums: +class PostReplicatorEnums: """ - Enums for get_geo parameters. + Enums for post_replicator parameters. """ - class Format(str, Enum): - """ - Query parameter that causes the geospatial query output to be returned in the - specified format. + class Batch(str, Enum): """ - LEGACY = 'legacy' - GEOJSON = 'geojson' - VIEW = 'view' - APPLICATION_VND_GEO_JSON = 'application/vnd.geo+json' - class Relation(str, Enum): - """ - Query parameter to specify the DE-9IM (Dimensionally Extended nine-Intersection - Model)geospatial relationship between the query geometry and the result documents. - """ - CONTAINS = 'contains' - CONTAINS_PROPERLY = 'contains_properly' - COVERED_BY = 'covered_by' - COVERS = 'covers' - CROSSES = 'crosses' - DISJOINT = 'disjoint' - INTERSECTS = 'intersects' - OVERLAPS = 'overlaps' - TOUCHES = 'touches' - WITHIN = 'within' - class Stale(str, Enum): - """ - Query parameter to specify to not wait for the index to finish building before - returning results. + Query parameter to specify whether to store in batch mode. The server will respond + with a HTTP 202 Accepted response code immediately. """ - OK = 'ok' - - -class GetGeoEnums: - """ - Enums for get_geo_as_stream parameters. - """ - class Format(str, Enum): - """ - Query parameter that causes the geospatial query output to be returned in the - specified format. - """ - LEGACY = 'legacy' - GEOJSON = 'geojson' - VIEW = 'view' - APPLICATION_VND_GEO_JSON = 'application/vnd.geo+json' - class Relation(str, Enum): - """ - Query parameter to specify the DE-9IM (Dimensionally Extended nine-Intersection - Model)geospatial relationship between the query geometry and the result documents. - """ - CONTAINS = 'contains' - CONTAINS_PROPERLY = 'contains_properly' - COVERED_BY = 'covered_by' - COVERS = 'covers' - CROSSES = 'crosses' - DISJOINT = 'disjoint' - INTERSECTS = 'intersects' - OVERLAPS = 'overlaps' - TOUCHES = 'touches' - WITHIN = 'within' - class Stale(str, Enum): - """ - Query parameter to specify to not wait for the index to finish building before - returning results. - """ OK = 'ok' @@ -8060,6 +8686,7 @@ class Batch(str, Enum): Query parameter to specify whether to store in batch mode. The server will respond with a HTTP 202 Accepted response code immediately. """ + OK = 'ok' @@ -8073,6 +8700,7 @@ class Batch(str, Enum): Query parameter to specify whether to store in batch mode. The server will respond with a HTTP 202 Accepted response code immediately. """ + OK = 'ok' @@ -8086,6 +8714,7 @@ class States(str, Enum): Query parameter to include only replication documents in the specified states. String must be a comma-delimited string. """ + INITIALIZING = 'initializing' ERROR = 'error' PENDING = 'pending' @@ -8105,6 +8734,7 @@ class Batch(str, Enum): Query parameter to specify whether to store in batch mode. The server will respond with a HTTP 202 Accepted response code immediately. """ + OK = 'ok' @@ -8118,6 +8748,7 @@ class Batch(str, Enum): Query parameter to specify whether to store in batch mode. The server will respond with a HTTP 202 Accepted response code immediately. """ + OK = 'ok' @@ -8131,6 +8762,7 @@ class Accept(str, Enum): The type of the response: application/json, multipart/mixed, multipart/related, or application/octet-stream. """ + APPLICATION_JSON = 'application/json' MULTIPART_MIXED = 'multipart/mixed' MULTIPART_RELATED = 'multipart/related' @@ -8146,6 +8778,7 @@ class ContentType(str, Enum): """ The type of the input. """ + APPLICATION_JSON = 'application/json' MULTIPART_MIXED = 'multipart/mixed' MULTIPART_RELATED = 'multipart/related' @@ -8155,6 +8788,7 @@ class Batch(str, Enum): Query parameter to specify whether to store in batch mode. The server will respond with a HTTP 202 Accepted response code immediately. """ + OK = 'ok' @@ -8163,36 +8797,114 @@ class Batch(str, Enum): ############################################################################## -class ActiveTask(): +class ActiveTask: """ Schema for information about a running task. - :attr int changes_done: (optional) Processed changes. - :attr str database: Source database. - :attr str node: Cluster node where the task is running. - :attr str pid: Process ID. - :attr int progress: (optional) Current percentage progress. - :attr int started_on: Schema for a Unix epoch timestamp. - :attr str status: (optional) Task status message. - :attr str task: (optional) Task name. - :attr int total_changes: (optional) Total changes to process. - :attr str type: Operation type. - :attr int updated_on: Schema for a Unix epoch timestamp. + :param int bulk_get_attempts: (optional) The total count of attempted doc + revisions fetched with `_bulk_get`. Available for `replication` type tasks. + :param int bulk_get_docs: (optional) The total count of successful docs fetched + with `_bulk_get`. Available for `replication` type tasks. + :param int changes_done: (optional) Processed changes. Available for + `database_compaction`, `indexer`, `search_indexer`, `view_compaction` type + tasks. + :param int changes_pending: (optional) The count of changes not yet replicated. + Available for `replication` type tasks. + :param int checkpoint_interval: (optional) Specifies the checkpoint interval in + ms. Available for `replication` type tasks. + :param str checkpointed_source_seq: (optional) The source sequence id which was + last successfully replicated. Available for `replication` type tasks. + :param bool continuous: (optional) The replication configured to be continuous. + Available for `replication` type tasks. + :param str database: Source database. + :param str design_document: (optional) The design document that belongs to this + task. Available for `indexer`, `search_indexer`, `view_compaction` type tasks. + :param str doc_id: (optional) Replication document ID. Available for + `replication` type tasks. + :param int doc_write_failures: (optional) Number of document write failures. + Available for `replication` type tasks. + :param int docs_read: (optional) Number of documents read. Available for + `replication` type tasks. + :param int docs_written: (optional) Number of documents written to target. + Available for `replication` type tasks. + :param str index: (optional) The search index that belongs to this task. + Available for `search_indexer` type tasks. + :param str indexer_pid: (optional) Indexer process ID. Available for `indexer` + type tasks. + :param int missing_revisions_found: (optional) The count of docs which have been + read from the source. Available for `replication` type tasks. + :param str node: Cluster node where the task is running. + :param str phase: (optional) The phase the active task is in. `docid_sort`, + `docid_copy`, `document_copy` phases are available for `database_compaction`, + while `ids` and `view` phases are available for `view_compaction` type tasks. + :param str pid: Process ID. + :param str process_status: (optional) Process status. + :param int progress: (optional) Current percentage progress. Available for + `database_compaction`, `indexer`, `search_indexer`, `view_compaction` type + tasks. + :param str replication_id: (optional) Replication ID. Available for + `replication` type tasks. + :param bool retry: (optional) Indicates whether a compaction retry is currently + running on the database. Available for `database_compaction` type tasks. + :param int revisions_checked: (optional) The count of revisions which have been + checked since this replication began. Available for `replication` type tasks. + :param str source: (optional) Replication source. Available for `replication` + type tasks. + :param str source_seq: (optional) The last sequence number obtained from the + source database changes feed. Available for `replication` type tasks. + :param int started_on: Schema for a Unix epoch timestamp. + :param str target: (optional) Replication target. Available for `replication` + type tasks. + :param str through_seq: (optional) The last sequence number processed by the + replicator. Available for `replication` type tasks. + :param int total_changes: (optional) Total changes to process. Available for + `database_compaction`, `indexer`, `search_indexer`, `view_compaction` type + tasks. + :param str type: Operation type. + :param int updated_on: Schema for a Unix epoch timestamp. + :param str user: (optional) Name of user running the process. + :param int view: (optional) Number of view indexes. Available for + `view_compaction` type tasks. """ - def __init__(self, - database: str, - node: str, - pid: str, - started_on: int, - type: str, - updated_on: int, - *, - changes_done: int = None, - progress: int = None, - status: str = None, - task: str = None, - total_changes: int = None) -> None: + def __init__( + self, + database: str, + node: str, + pid: str, + started_on: int, + type: str, + updated_on: int, + *, + bulk_get_attempts: Optional[int] = None, + bulk_get_docs: Optional[int] = None, + changes_done: Optional[int] = None, + changes_pending: Optional[int] = None, + checkpoint_interval: Optional[int] = None, + checkpointed_source_seq: Optional[str] = None, + continuous: Optional[bool] = None, + design_document: Optional[str] = None, + doc_id: Optional[str] = None, + doc_write_failures: Optional[int] = None, + docs_read: Optional[int] = None, + docs_written: Optional[int] = None, + index: Optional[str] = None, + indexer_pid: Optional[str] = None, + missing_revisions_found: Optional[int] = None, + phase: Optional[str] = None, + process_status: Optional[str] = None, + progress: Optional[int] = None, + replication_id: Optional[str] = None, + retry: Optional[bool] = None, + revisions_checked: Optional[int] = None, + source: Optional[str] = None, + source_seq: Optional[str] = None, + target: Optional[str] = None, + through_seq: Optional[str] = None, + total_changes: Optional[int] = None, + user: Optional[str] = None, + view: Optional[int] = None, + ) -> None: """ Initialize a ActiveTask object. @@ -8202,62 +8914,188 @@ def __init__(self, :param int started_on: Schema for a Unix epoch timestamp. :param str type: Operation type. :param int updated_on: Schema for a Unix epoch timestamp. - :param int changes_done: (optional) Processed changes. - :param int progress: (optional) Current percentage progress. - :param str status: (optional) Task status message. - :param str task: (optional) Task name. - :param int total_changes: (optional) Total changes to process. - """ + :param int bulk_get_attempts: (optional) The total count of attempted doc + revisions fetched with `_bulk_get`. Available for `replication` type tasks. + :param int bulk_get_docs: (optional) The total count of successful docs + fetched with `_bulk_get`. Available for `replication` type tasks. + :param int changes_done: (optional) Processed changes. Available for + `database_compaction`, `indexer`, `search_indexer`, `view_compaction` type + tasks. + :param int changes_pending: (optional) The count of changes not yet + replicated. Available for `replication` type tasks. + :param int checkpoint_interval: (optional) Specifies the checkpoint + interval in ms. Available for `replication` type tasks. + :param str checkpointed_source_seq: (optional) The source sequence id which + was last successfully replicated. Available for `replication` type tasks. + :param bool continuous: (optional) The replication configured to be + continuous. Available for `replication` type tasks. + :param str design_document: (optional) The design document that belongs to + this task. Available for `indexer`, `search_indexer`, `view_compaction` + type tasks. + :param str doc_id: (optional) Replication document ID. Available for + `replication` type tasks. + :param int doc_write_failures: (optional) Number of document write + failures. Available for `replication` type tasks. + :param int docs_read: (optional) Number of documents read. Available for + `replication` type tasks. + :param int docs_written: (optional) Number of documents written to target. + Available for `replication` type tasks. + :param str index: (optional) The search index that belongs to this task. + Available for `search_indexer` type tasks. + :param str indexer_pid: (optional) Indexer process ID. Available for + `indexer` type tasks. + :param int missing_revisions_found: (optional) The count of docs which have + been read from the source. Available for `replication` type tasks. + :param str phase: (optional) The phase the active task is in. `docid_sort`, + `docid_copy`, `document_copy` phases are available for + `database_compaction`, while `ids` and `view` phases are available for + `view_compaction` type tasks. + :param str process_status: (optional) Process status. + :param int progress: (optional) Current percentage progress. Available for + `database_compaction`, `indexer`, `search_indexer`, `view_compaction` type + tasks. + :param str replication_id: (optional) Replication ID. Available for + `replication` type tasks. + :param bool retry: (optional) Indicates whether a compaction retry is + currently running on the database. Available for `database_compaction` type + tasks. + :param int revisions_checked: (optional) The count of revisions which have + been checked since this replication began. Available for `replication` type + tasks. + :param str source: (optional) Replication source. Available for + `replication` type tasks. + :param str source_seq: (optional) The last sequence number obtained from + the source database changes feed. Available for `replication` type tasks. + :param str target: (optional) Replication target. Available for + `replication` type tasks. + :param str through_seq: (optional) The last sequence number processed by + the replicator. Available for `replication` type tasks. + :param int total_changes: (optional) Total changes to process. Available + for `database_compaction`, `indexer`, `search_indexer`, `view_compaction` + type tasks. + :param str user: (optional) Name of user running the process. + :param int view: (optional) Number of view indexes. Available for + `view_compaction` type tasks. + """ + self.bulk_get_attempts = bulk_get_attempts + self.bulk_get_docs = bulk_get_docs self.changes_done = changes_done + self.changes_pending = changes_pending + self.checkpoint_interval = checkpoint_interval + self.checkpointed_source_seq = checkpointed_source_seq + self.continuous = continuous self.database = database + self.design_document = design_document + self.doc_id = doc_id + self.doc_write_failures = doc_write_failures + self.docs_read = docs_read + self.docs_written = docs_written + self.index = index + self.indexer_pid = indexer_pid + self.missing_revisions_found = missing_revisions_found self.node = node + self.phase = phase self.pid = pid + self.process_status = process_status self.progress = progress + self.replication_id = replication_id + self.retry = retry + self.revisions_checked = revisions_checked + self.source = source + self.source_seq = source_seq self.started_on = started_on - self.status = status - self.task = task + self.target = target + self.through_seq = through_seq self.total_changes = total_changes self.type = type self.updated_on = updated_on + self.user = user + self.view = view @classmethod def from_dict(cls, _dict: Dict) -> 'ActiveTask': """Initialize a ActiveTask object from a json dictionary.""" args = {} - if 'changes_done' in _dict: - args['changes_done'] = _dict.get('changes_done') - if 'database' in _dict: - args['database'] = _dict.get('database') + if (bulk_get_attempts := _dict.get('bulk_get_attempts')) is not None: + args['bulk_get_attempts'] = bulk_get_attempts + if (bulk_get_docs := _dict.get('bulk_get_docs')) is not None: + args['bulk_get_docs'] = bulk_get_docs + if (changes_done := _dict.get('changes_done')) is not None: + args['changes_done'] = changes_done + if (changes_pending := _dict.get('changes_pending')) is not None: + args['changes_pending'] = changes_pending + if (checkpoint_interval := _dict.get('checkpoint_interval')) is not None: + args['checkpoint_interval'] = checkpoint_interval + if (checkpointed_source_seq := _dict.get('checkpointed_source_seq')) is not None: + args['checkpointed_source_seq'] = checkpointed_source_seq + if (continuous := _dict.get('continuous')) is not None: + args['continuous'] = continuous + if (database := _dict.get('database')) is not None: + args['database'] = database else: raise ValueError('Required property \'database\' not present in ActiveTask JSON') - if 'node' in _dict: - args['node'] = _dict.get('node') + if (design_document := _dict.get('design_document')) is not None: + args['design_document'] = design_document + if (doc_id := _dict.get('doc_id')) is not None: + args['doc_id'] = doc_id + if (doc_write_failures := _dict.get('doc_write_failures')) is not None: + args['doc_write_failures'] = doc_write_failures + if (docs_read := _dict.get('docs_read')) is not None: + args['docs_read'] = docs_read + if (docs_written := _dict.get('docs_written')) is not None: + args['docs_written'] = docs_written + if (index := _dict.get('index')) is not None: + args['index'] = index + if (indexer_pid := _dict.get('indexer_pid')) is not None: + args['indexer_pid'] = indexer_pid + if (missing_revisions_found := _dict.get('missing_revisions_found')) is not None: + args['missing_revisions_found'] = missing_revisions_found + if (node := _dict.get('node')) is not None: + args['node'] = node else: raise ValueError('Required property \'node\' not present in ActiveTask JSON') - if 'pid' in _dict: - args['pid'] = _dict.get('pid') + if (phase := _dict.get('phase')) is not None: + args['phase'] = phase + if (pid := _dict.get('pid')) is not None: + args['pid'] = pid else: raise ValueError('Required property \'pid\' not present in ActiveTask JSON') - if 'progress' in _dict: - args['progress'] = _dict.get('progress') - if 'started_on' in _dict: - args['started_on'] = _dict.get('started_on') + if (process_status := _dict.get('process_status')) is not None: + args['process_status'] = process_status + if (progress := _dict.get('progress')) is not None: + args['progress'] = progress + if (replication_id := _dict.get('replication_id')) is not None: + args['replication_id'] = replication_id + if (retry := _dict.get('retry')) is not None: + args['retry'] = retry + if (revisions_checked := _dict.get('revisions_checked')) is not None: + args['revisions_checked'] = revisions_checked + if (source := _dict.get('source')) is not None: + args['source'] = source + if (source_seq := _dict.get('source_seq')) is not None: + args['source_seq'] = source_seq + if (started_on := _dict.get('started_on')) is not None: + args['started_on'] = started_on else: raise ValueError('Required property \'started_on\' not present in ActiveTask JSON') - if 'status' in _dict: - args['status'] = _dict.get('status') - if 'task' in _dict: - args['task'] = _dict.get('task') - if 'total_changes' in _dict: - args['total_changes'] = _dict.get('total_changes') - if 'type' in _dict: - args['type'] = _dict.get('type') + if (target := _dict.get('target')) is not None: + args['target'] = target + if (through_seq := _dict.get('through_seq')) is not None: + args['through_seq'] = through_seq + if (total_changes := _dict.get('total_changes')) is not None: + args['total_changes'] = total_changes + if (type := _dict.get('type')) is not None: + args['type'] = type else: raise ValueError('Required property \'type\' not present in ActiveTask JSON') - if 'updated_on' in _dict: - args['updated_on'] = _dict.get('updated_on') + if (updated_on := _dict.get('updated_on')) is not None: + args['updated_on'] = updated_on else: raise ValueError('Required property \'updated_on\' not present in ActiveTask JSON') + if (user := _dict.get('user')) is not None: + args['user'] = user + if (view := _dict.get('view')) is not None: + args['view'] = view return cls(**args) @classmethod @@ -8268,28 +9106,74 @@ def _from_dict(cls, _dict): def to_dict(self) -> Dict: """Return a json dictionary representing this model.""" _dict = {} + if hasattr(self, 'bulk_get_attempts') and self.bulk_get_attempts is not None: + _dict['bulk_get_attempts'] = self.bulk_get_attempts + if hasattr(self, 'bulk_get_docs') and self.bulk_get_docs is not None: + _dict['bulk_get_docs'] = self.bulk_get_docs if hasattr(self, 'changes_done') and self.changes_done is not None: _dict['changes_done'] = self.changes_done + if hasattr(self, 'changes_pending') and self.changes_pending is not None: + _dict['changes_pending'] = self.changes_pending + if hasattr(self, 'checkpoint_interval') and self.checkpoint_interval is not None: + _dict['checkpoint_interval'] = self.checkpoint_interval + if hasattr(self, 'checkpointed_source_seq') and self.checkpointed_source_seq is not None: + _dict['checkpointed_source_seq'] = self.checkpointed_source_seq + if hasattr(self, 'continuous') and self.continuous is not None: + _dict['continuous'] = self.continuous if hasattr(self, 'database') and self.database is not None: _dict['database'] = self.database + if hasattr(self, 'design_document') and self.design_document is not None: + _dict['design_document'] = self.design_document + if hasattr(self, 'doc_id') and self.doc_id is not None: + _dict['doc_id'] = self.doc_id + if hasattr(self, 'doc_write_failures') and self.doc_write_failures is not None: + _dict['doc_write_failures'] = self.doc_write_failures + if hasattr(self, 'docs_read') and self.docs_read is not None: + _dict['docs_read'] = self.docs_read + if hasattr(self, 'docs_written') and self.docs_written is not None: + _dict['docs_written'] = self.docs_written + if hasattr(self, 'index') and self.index is not None: + _dict['index'] = self.index + if hasattr(self, 'indexer_pid') and self.indexer_pid is not None: + _dict['indexer_pid'] = self.indexer_pid + if hasattr(self, 'missing_revisions_found') and self.missing_revisions_found is not None: + _dict['missing_revisions_found'] = self.missing_revisions_found if hasattr(self, 'node') and self.node is not None: _dict['node'] = self.node + if hasattr(self, 'phase') and self.phase is not None: + _dict['phase'] = self.phase if hasattr(self, 'pid') and self.pid is not None: _dict['pid'] = self.pid + if hasattr(self, 'process_status') and self.process_status is not None: + _dict['process_status'] = self.process_status if hasattr(self, 'progress') and self.progress is not None: _dict['progress'] = self.progress + if hasattr(self, 'replication_id') and self.replication_id is not None: + _dict['replication_id'] = self.replication_id + if hasattr(self, 'retry') and self.retry is not None: + _dict['retry'] = self.retry + if hasattr(self, 'revisions_checked') and self.revisions_checked is not None: + _dict['revisions_checked'] = self.revisions_checked + if hasattr(self, 'source') and self.source is not None: + _dict['source'] = self.source + if hasattr(self, 'source_seq') and self.source_seq is not None: + _dict['source_seq'] = self.source_seq if hasattr(self, 'started_on') and self.started_on is not None: _dict['started_on'] = self.started_on - if hasattr(self, 'status') and self.status is not None: - _dict['status'] = self.status - if hasattr(self, 'task') and self.task is not None: - _dict['task'] = self.task + if hasattr(self, 'target') and self.target is not None: + _dict['target'] = self.target + if hasattr(self, 'through_seq') and self.through_seq is not None: + _dict['through_seq'] = self.through_seq if hasattr(self, 'total_changes') and self.total_changes is not None: _dict['total_changes'] = self.total_changes if hasattr(self, 'type') and self.type is not None: _dict['type'] = self.type if hasattr(self, 'updated_on') and self.updated_on is not None: _dict['updated_on'] = self.updated_on + if hasattr(self, 'user') and self.user is not None: + _dict['user'] = self.user + if hasattr(self, 'view') and self.view is not None: + _dict['view'] = self.view return _dict def _to_dict(self): @@ -8310,22 +9194,64 @@ def __ne__(self, other: 'ActiveTask') -> bool: """Return `true` when self and other are not equal, false otherwise.""" return not self == other -class ActivityTrackerEvents(): + class PhaseEnum(str, Enum): + """ + The phase the active task is in. `docid_sort`, `docid_copy`, `document_copy` + phases are available for `database_compaction`, while `ids` and `view` phases are + available for `view_compaction` type tasks. + """ + + DOCID_SORT = 'docid_sort' + DOCID_COPY = 'docid_copy' + DOCUMENT_COPY = 'document_copy' + IDS = 'ids' + VIEW = 'view' + + + class ProcessStatusEnum(str, Enum): + """ + Process status. + """ + + EXITING = 'exiting' + GARBAGE_COLLECTING = 'garbage_collecting' + RUNNABLE = 'runnable' + RUNNING = 'running' + SUSPENDED = 'suspended' + WAITING = 'waiting' + + + class TypeEnum(str, Enum): + """ + Operation type. + """ + + DATABASE_COMPACTION = 'database_compaction' + INDEXER = 'indexer' + REPLICATION = 'replication' + SEARCH_INDEXER = 'search_indexer' + VIEW_COMPACTION = 'view_compaction' + + + +class ActivityTrackerEvents: """ - Schema for Activity Tracker events. + Schema for activity tracking events. - :attr List[str] types: An array of event types that are being sent to IBM Cloud - Activity Tracker for the IBM Cloudant instance. "management" is a required + :param List[str] types: An array of event types sent to IBM Cloud Activity + Tracker Event Routing for the IBM Cloudant instance. "management" is a required element of this array. """ - def __init__(self, - types: List[str]) -> None: + def __init__( + self, + types: List[str], + ) -> None: """ Initialize a ActivityTrackerEvents object. - :param List[str] types: An array of event types that are being sent to IBM - Cloud Activity Tracker for the IBM Cloudant instance. "management" is a + :param List[str] types: An array of event types sent to IBM Cloud Activity + Tracker Event Routing for the IBM Cloudant instance. "management" is a required element of this array. """ self.types = types @@ -8334,8 +9260,8 @@ def __init__(self, def from_dict(cls, _dict: Dict) -> 'ActivityTrackerEvents': """Initialize a ActivityTrackerEvents object from a json dictionary.""" args = {} - if 'types' in _dict: - args['types'] = _dict.get('types') + if (types := _dict.get('types')) is not None: + args['types'] = types else: raise ValueError('Required property \'types\' not present in ActivityTrackerEvents JSON') return cls(**args) @@ -8374,21 +9300,25 @@ class TypesEnum(str, Enum): """ types. """ + MANAGEMENT = 'management' DATA = 'data' -class AllDocsQueriesResult(): + +class AllDocsQueriesResult: """ Schema for the result of an all documents queries operation. - :attr List[AllDocsResult] results: An array of result objects - one for each + :param List[AllDocsResult] results: An array of result objects - one for each query. Each result object contains the same fields as the response to a regular `/_all_docs` request. """ - def __init__(self, - results: List['AllDocsResult']) -> None: + def __init__( + self, + results: List['AllDocsResult'], + ) -> None: """ Initialize a AllDocsQueriesResult object. @@ -8402,8 +9332,8 @@ def __init__(self, def from_dict(cls, _dict: Dict) -> 'AllDocsQueriesResult': """Initialize a AllDocsQueriesResult object from a json dictionary.""" args = {} - if 'results' in _dict: - args['results'] = [AllDocsResult.from_dict(x) for x in _dict.get('results')] + if (results := _dict.get('results')) is not None: + args['results'] = [AllDocsResult.from_dict(v) for v in results] else: raise ValueError('Required property \'results\' not present in AllDocsQueriesResult JSON') return cls(**args) @@ -8417,7 +9347,13 @@ def to_dict(self) -> Dict: """Return a json dictionary representing this model.""" _dict = {} if hasattr(self, 'results') and self.results is not None: - _dict['results'] = [x.to_dict() for x in self.results] + results_list = [] + for v in self.results: + if isinstance(v, dict): + results_list.append(v) + else: + results_list.append(v.to_dict()) + _dict['results'] = results_list return _dict def _to_dict(self): @@ -8438,69 +9374,74 @@ def __ne__(self, other: 'AllDocsQueriesResult') -> bool: """Return `true` when self and other are not equal, false otherwise.""" return not self == other -class AllDocsQuery(): + +class AllDocsQuery: """ Schema for an all documents query operation. - :attr bool att_encoding_info: (optional) Parameter to specify whether to include - the encoding information in attachment stubs if the particular attachment is - compressed. - :attr bool attachments: (optional) Parameter to specify whether to include - attachments bodies in a response. - :attr bool conflicts: (optional) Parameter to specify whether to include a list - of conflicted revisions in each returned document. Active only when - `include_docs` is `true`. - :attr bool descending: (optional) Parameter to specify whether to return the + :param bool att_encoding_info: (optional) Parameter to specify whether to + include the encoding information for compressed attachments. This only applies + when requesting documents in the response. + :param bool attachments: (optional) Parameter to specify whether to include + attachment content in included document content or only the attachment metadata. + This only applies when requesting documents in the response. + :param bool conflicts: (optional) Parameter to specify whether to include a list + of conflicted revisions in each returned document. This only applies when + requesting documents in the response. + :param bool descending: (optional) Parameter to specify whether to return the documents in descending by key order. - :attr bool include_docs: (optional) Parameter to specify whether to include the + :param bool include_docs: (optional) Parameter to specify whether to include the full content of the documents in the response. - :attr bool inclusive_end: (optional) Parameter to specify whether the specified - end key should be included in the result. - :attr int limit: (optional) Parameter to specify the number of returned + :param bool inclusive_end: (optional) Parameter to specify whether to include + the specified end key in the result. + :param int limit: (optional) Parameter to specify the number of returned documents to limit the result to. - :attr int skip: (optional) Parameter to specify the number of records before + :param int skip: (optional) Parameter to specify the number of records before starting to return the results. - :attr bool update_seq: (optional) Parameter to specify whether to include in the - response an update_seq value indicating the sequence id of the database the view - reflects. - :attr str end_key: (optional) Schema for a document ID. - :attr str key: (optional) Schema for a document ID. - :attr List[str] keys: (optional) Schema for a list of document IDs. - :attr str start_key: (optional) Schema for a document ID. + :param bool update_seq: (optional) Parameter to specify whether to include in + the response an update_seq value indicating the sequence id of the database the + view reflects. + :param str end_key: (optional) Schema for a document ID. + :param str key: (optional) Schema for a document ID. + :param List[str] keys: (optional) Schema for a list of document IDs. + :param str start_key: (optional) Schema for a document ID. """ - def __init__(self, - *, - att_encoding_info: bool = None, - attachments: bool = None, - conflicts: bool = None, - descending: bool = None, - include_docs: bool = None, - inclusive_end: bool = None, - limit: int = None, - skip: int = None, - update_seq: bool = None, - end_key: str = None, - key: str = None, - keys: List[str] = None, - start_key: str = None) -> None: + def __init__( + self, + *, + att_encoding_info: Optional[bool] = None, + attachments: Optional[bool] = None, + conflicts: Optional[bool] = None, + descending: Optional[bool] = None, + include_docs: Optional[bool] = None, + inclusive_end: Optional[bool] = None, + limit: Optional[int] = None, + skip: Optional[int] = None, + update_seq: Optional[bool] = None, + end_key: Optional[str] = None, + key: Optional[str] = None, + keys: Optional[List[str]] = None, + start_key: Optional[str] = None, + ) -> None: """ Initialize a AllDocsQuery object. :param bool att_encoding_info: (optional) Parameter to specify whether to - include the encoding information in attachment stubs if the particular - attachment is compressed. + include the encoding information for compressed attachments. This only + applies when requesting documents in the response. :param bool attachments: (optional) Parameter to specify whether to include - attachments bodies in a response. + attachment content in included document content or only the attachment + metadata. This only applies when requesting documents in the response. :param bool conflicts: (optional) Parameter to specify whether to include a - list of conflicted revisions in each returned document. Active only when - `include_docs` is `true`. + list of conflicted revisions in each returned document. This only applies + when requesting documents in the response. :param bool descending: (optional) Parameter to specify whether to return the documents in descending by key order. :param bool include_docs: (optional) Parameter to specify whether to include the full content of the documents in the response. - :param bool inclusive_end: (optional) Parameter to specify whether the - specified end key should be included in the result. + :param bool inclusive_end: (optional) Parameter to specify whether to + include the specified end key in the result. :param int limit: (optional) Parameter to specify the number of returned documents to limit the result to. :param int skip: (optional) Parameter to specify the number of records @@ -8531,32 +9472,32 @@ def __init__(self, def from_dict(cls, _dict: Dict) -> 'AllDocsQuery': """Initialize a AllDocsQuery object from a json dictionary.""" args = {} - if 'att_encoding_info' in _dict: - args['att_encoding_info'] = _dict.get('att_encoding_info') - if 'attachments' in _dict: - args['attachments'] = _dict.get('attachments') - if 'conflicts' in _dict: - args['conflicts'] = _dict.get('conflicts') - if 'descending' in _dict: - args['descending'] = _dict.get('descending') - if 'include_docs' in _dict: - args['include_docs'] = _dict.get('include_docs') - if 'inclusive_end' in _dict: - args['inclusive_end'] = _dict.get('inclusive_end') - if 'limit' in _dict: - args['limit'] = _dict.get('limit') - if 'skip' in _dict: - args['skip'] = _dict.get('skip') - if 'update_seq' in _dict: - args['update_seq'] = _dict.get('update_seq') - if 'end_key' in _dict: - args['end_key'] = _dict.get('end_key') - if 'key' in _dict: - args['key'] = _dict.get('key') - if 'keys' in _dict: - args['keys'] = _dict.get('keys') - if 'start_key' in _dict: - args['start_key'] = _dict.get('start_key') + if (att_encoding_info := _dict.get('att_encoding_info')) is not None: + args['att_encoding_info'] = att_encoding_info + if (attachments := _dict.get('attachments')) is not None: + args['attachments'] = attachments + if (conflicts := _dict.get('conflicts')) is not None: + args['conflicts'] = conflicts + if (descending := _dict.get('descending')) is not None: + args['descending'] = descending + if (include_docs := _dict.get('include_docs')) is not None: + args['include_docs'] = include_docs + if (inclusive_end := _dict.get('inclusive_end')) is not None: + args['inclusive_end'] = inclusive_end + if (limit := _dict.get('limit')) is not None: + args['limit'] = limit + if (skip := _dict.get('skip')) is not None: + args['skip'] = skip + if (update_seq := _dict.get('update_seq')) is not None: + args['update_seq'] = update_seq + if (end_key := _dict.get('end_key')) is not None: + args['end_key'] = end_key + if (key := _dict.get('key')) is not None: + args['key'] = key + if (keys := _dict.get('keys')) is not None: + args['keys'] = keys + if (start_key := _dict.get('start_key')) is not None: + args['start_key'] = start_key return cls(**args) @classmethod @@ -8613,24 +9554,27 @@ def __ne__(self, other: 'AllDocsQuery') -> bool: """Return `true` when self and other are not equal, false otherwise.""" return not self == other -class AllDocsResult(): + +class AllDocsResult: """ Schema for the result of an all documents operation. - :attr int total_rows: Number of total rows. - :attr List[DocsResultRow] rows: List of doc results. - :attr str update_seq: (optional) Current update sequence for the database. + :param int total_rows: Total number of document results. + :param List[DocsResultRow] rows: List of doc results. + :param str update_seq: (optional) Current update sequence for the database. """ - def __init__(self, - total_rows: int, - rows: List['DocsResultRow'], - *, - update_seq: str = None) -> None: + def __init__( + self, + total_rows: int, + rows: List['DocsResultRow'], + *, + update_seq: Optional[str] = None, + ) -> None: """ Initialize a AllDocsResult object. - :param int total_rows: Number of total rows. + :param int total_rows: Total number of document results. :param List[DocsResultRow] rows: List of doc results. :param str update_seq: (optional) Current update sequence for the database. """ @@ -8642,16 +9586,16 @@ def __init__(self, def from_dict(cls, _dict: Dict) -> 'AllDocsResult': """Initialize a AllDocsResult object from a json dictionary.""" args = {} - if 'total_rows' in _dict: - args['total_rows'] = _dict.get('total_rows') + if (total_rows := _dict.get('total_rows')) is not None: + args['total_rows'] = total_rows else: raise ValueError('Required property \'total_rows\' not present in AllDocsResult JSON') - if 'rows' in _dict: - args['rows'] = [DocsResultRow.from_dict(x) for x in _dict.get('rows')] + if (rows := _dict.get('rows')) is not None: + args['rows'] = [DocsResultRow.from_dict(v) for v in rows] else: raise ValueError('Required property \'rows\' not present in AllDocsResult JSON') - if 'update_seq' in _dict: - args['update_seq'] = _dict.get('update_seq') + if (update_seq := _dict.get('update_seq')) is not None: + args['update_seq'] = update_seq return cls(**args) @classmethod @@ -8665,7 +9609,13 @@ def to_dict(self) -> Dict: if hasattr(self, 'total_rows') and self.total_rows is not None: _dict['total_rows'] = self.total_rows if hasattr(self, 'rows') and self.rows is not None: - _dict['rows'] = [x.to_dict() for x in self.rows] + rows_list = [] + for v in self.rows: + if isinstance(v, dict): + rows_list.append(v) + else: + rows_list.append(v.to_dict()) + _dict['rows'] = rows_list if hasattr(self, 'update_seq') and self.update_seq is not None: _dict['update_seq'] = self.update_seq return _dict @@ -8688,34 +9638,41 @@ def __ne__(self, other: 'AllDocsResult') -> bool: """Return `true` when self and other are not equal, false otherwise.""" return not self == other -class Analyzer(): + +class Analyzer: """ Schema for a full text search analyzer. - :attr str name: (optional) Schema for the name of the Apache Lucene analyzer to - use for text indexing. The default value varies depending on the analyzer usage: - * For search indexes the default is `standard` * For query text indexes the - default is `keyword` * For a query text index default_field the default is - `standard`. - :attr List[str] stopwords: (optional) Custom stopwords to use with the named - analyzer. + :param str name: Schema for the name of the Apache Lucene analyzer to use for + text indexing. The default value varies depending on the analyzer usage: + * For search indexes the default is `standard` + * For query text indexes the default is `keyword` + * For a query text index default_field the default is `standard`. + :param List[str] stopwords: (optional) Custom stopwords to use with the named + analyzer where supported. Not used for `keyword`, `simple`, `whitespace`, + `simple_asciifolding` or `perfield` analyzers. Ensure stopwords match the folded + case for analyzers that perform folding. """ - def __init__(self, - *, - name: str = None, - stopwords: List[str] = None) -> None: + def __init__( + self, + name: str, + *, + stopwords: Optional[List[str]] = None, + ) -> None: """ Initialize a Analyzer object. - :param str name: (optional) Schema for the name of the Apache Lucene - analyzer to use for text indexing. The default value varies depending on - the analyzer usage: - * For search indexes the default is `standard` * For query text indexes the - default is `keyword` * For a query text index default_field the default is - `standard`. + :param str name: Schema for the name of the Apache Lucene analyzer to use + for text indexing. The default value varies depending on the analyzer + usage: + * For search indexes the default is `standard` + * For query text indexes the default is `keyword` + * For a query text index default_field the default is `standard`. :param List[str] stopwords: (optional) Custom stopwords to use with the - named analyzer. + named analyzer where supported. Not used for `keyword`, `simple`, + `whitespace`, `simple_asciifolding` or `perfield` analyzers. Ensure + stopwords match the folded case for analyzers that perform folding. """ self.name = name self.stopwords = stopwords @@ -8724,10 +9681,12 @@ def __init__(self, def from_dict(cls, _dict: Dict) -> 'Analyzer': """Initialize a Analyzer object from a json dictionary.""" args = {} - if 'name' in _dict: - args['name'] = _dict.get('name') - if 'stopwords' in _dict: - args['stopwords'] = _dict.get('stopwords') + if (name := _dict.get('name')) is not None: + args['name'] = name + else: + raise ValueError('Required property \'name\' not present in Analyzer JSON') + if (stopwords := _dict.get('stopwords')) is not None: + args['stopwords'] = stopwords return cls(**args) @classmethod @@ -8766,33 +9725,36 @@ class NameEnum(str, Enum): """ Schema for the name of the Apache Lucene analyzer to use for text indexing. The default value varies depending on the analyzer usage: - * For search indexes the default is `standard` * For query text indexes the - default is `keyword` * For a query text index default_field the default is - `standard`. + * For search indexes the default is `standard` + * For query text indexes the default is `keyword` + * For a query text index default_field the default is `standard`. """ + CLASSIC = 'classic' EMAIL = 'email' KEYWORD = 'keyword' + PERFIELD = 'perfield' SIMPLE = 'simple' + SIMPLE_ASCIIFOLDING = 'simple_asciifolding' STANDARD = 'standard' WHITESPACE = 'whitespace' ARABIC = 'arabic' ARMENIAN = 'armenian' BASQUE = 'basque' - BULGARIAN = 'bulgarian' BRAZILIAN = 'brazilian' + BULGARIAN = 'bulgarian' CATALAN = 'catalan' - CJK = 'cjk' CHINESE = 'chinese' + CJK = 'cjk' CZECH = 'czech' DANISH = 'danish' DUTCH = 'dutch' ENGLISH = 'english' FINNISH = 'finnish' FRENCH = 'french' + GALICIAN = 'galician' GERMAN = 'german' GREEK = 'greek' - GALICIAN = 'galician' HINDI = 'hindi' HUNGARIAN = 'hungarian' INDONESIAN = 'indonesian' @@ -8810,57 +9772,72 @@ class NameEnum(str, Enum): SWEDISH = 'swedish' THAI = 'thai' TURKISH = 'turkish' - PERFIELD = 'perfield' -class AnalyzerConfiguration(): + +class AnalyzerConfiguration: """ - Schema for a search analyzer configuration. - - :attr str name: (optional) Schema for the name of the Apache Lucene analyzer to - use for text indexing. The default value varies depending on the analyzer usage: - * For search indexes the default is `standard` * For query text indexes the - default is `keyword` * For a query text index default_field the default is - `standard`. - :attr List[str] stopwords: (optional) Custom stopwords to use with the named - analyzer. - :attr dict fields: (optional) Schema for mapping a field name to a per field + Analyzer configuration for search indexes. The default and fields properties are only + applicable for the `perfield` analyzer name. + + :param str name: Schema for the name of the Apache Lucene analyzer to use for + text indexing. The default value varies depending on the analyzer usage: + * For search indexes the default is `standard` + * For query text indexes the default is `keyword` + * For a query text index default_field the default is `standard`. + :param List[str] stopwords: (optional) Custom stopwords to use with the named + analyzer where supported. Not used for `keyword`, `simple`, `whitespace`, + `simple_asciifolding` or `perfield` analyzers. Ensure stopwords match the folded + case for analyzers that perform folding. + :param Analyzer default: (optional) Schema for a full text search analyzer. + :param dict fields: (optional) Schema for mapping a field name to a per field analyzer. """ - def __init__(self, - *, - name: str = None, - stopwords: List[str] = None, - fields: dict = None) -> None: + def __init__( + self, + name: str, + *, + stopwords: Optional[List[str]] = None, + default: Optional['Analyzer'] = None, + fields: Optional[dict] = None, + ) -> None: """ Initialize a AnalyzerConfiguration object. - :param str name: (optional) Schema for the name of the Apache Lucene - analyzer to use for text indexing. The default value varies depending on - the analyzer usage: - * For search indexes the default is `standard` * For query text indexes the - default is `keyword` * For a query text index default_field the default is - `standard`. + :param str name: Schema for the name of the Apache Lucene analyzer to use + for text indexing. The default value varies depending on the analyzer + usage: + * For search indexes the default is `standard` + * For query text indexes the default is `keyword` + * For a query text index default_field the default is `standard`. :param List[str] stopwords: (optional) Custom stopwords to use with the - named analyzer. + named analyzer where supported. Not used for `keyword`, `simple`, + `whitespace`, `simple_asciifolding` or `perfield` analyzers. Ensure + stopwords match the folded case for analyzers that perform folding. + :param Analyzer default: (optional) Schema for a full text search analyzer. :param dict fields: (optional) Schema for mapping a field name to a per field analyzer. """ self.name = name self.stopwords = stopwords + self.default = default self.fields = fields @classmethod def from_dict(cls, _dict: Dict) -> 'AnalyzerConfiguration': """Initialize a AnalyzerConfiguration object from a json dictionary.""" args = {} - if 'name' in _dict: - args['name'] = _dict.get('name') - if 'stopwords' in _dict: - args['stopwords'] = _dict.get('stopwords') - if 'fields' in _dict: - args['fields'] = {k : Analyzer.from_dict(v) for k, v in _dict.get('fields').items()} + if (name := _dict.get('name')) is not None: + args['name'] = name + else: + raise ValueError('Required property \'name\' not present in AnalyzerConfiguration JSON') + if (stopwords := _dict.get('stopwords')) is not None: + args['stopwords'] = stopwords + if (default := _dict.get('default')) is not None: + args['default'] = Analyzer.from_dict(default) + if (fields := _dict.get('fields')) is not None: + args['fields'] = {k: Analyzer.from_dict(v) for k, v in fields.items()} return cls(**args) @classmethod @@ -8875,8 +9852,19 @@ def to_dict(self) -> Dict: _dict['name'] = self.name if hasattr(self, 'stopwords') and self.stopwords is not None: _dict['stopwords'] = self.stopwords + if hasattr(self, 'default') and self.default is not None: + if isinstance(self.default, dict): + _dict['default'] = self.default + else: + _dict['default'] = self.default.to_dict() if hasattr(self, 'fields') and self.fields is not None: - _dict['fields'] = {k : v.to_dict() for k, v in self.fields.items()} + fields_map = {} + for k, v in self.fields.items(): + if isinstance(v, dict): + fields_map[k] = v + else: + fields_map[k] = v.to_dict() + _dict['fields'] = fields_map return _dict def _to_dict(self): @@ -8901,33 +9889,36 @@ class NameEnum(str, Enum): """ Schema for the name of the Apache Lucene analyzer to use for text indexing. The default value varies depending on the analyzer usage: - * For search indexes the default is `standard` * For query text indexes the - default is `keyword` * For a query text index default_field the default is - `standard`. + * For search indexes the default is `standard` + * For query text indexes the default is `keyword` + * For a query text index default_field the default is `standard`. """ + CLASSIC = 'classic' EMAIL = 'email' KEYWORD = 'keyword' + PERFIELD = 'perfield' SIMPLE = 'simple' + SIMPLE_ASCIIFOLDING = 'simple_asciifolding' STANDARD = 'standard' WHITESPACE = 'whitespace' ARABIC = 'arabic' ARMENIAN = 'armenian' BASQUE = 'basque' - BULGARIAN = 'bulgarian' BRAZILIAN = 'brazilian' + BULGARIAN = 'bulgarian' CATALAN = 'catalan' - CJK = 'cjk' CHINESE = 'chinese' + CJK = 'cjk' CZECH = 'czech' DANISH = 'danish' DUTCH = 'dutch' ENGLISH = 'english' FINNISH = 'finnish' FRENCH = 'french' + GALICIAN = 'galician' GERMAN = 'german' GREEK = 'greek' - GALICIAN = 'galician' HINDI = 'hindi' HUNGARIAN = 'hungarian' INDONESIAN = 'indonesian' @@ -8945,22 +9936,24 @@ class NameEnum(str, Enum): SWEDISH = 'swedish' THAI = 'thai' TURKISH = 'turkish' - PERFIELD = 'perfield' -class ApiKeysResult(): + +class ApiKeysResult: """ Schema for api keys. - :attr bool ok: ok. - :attr str key: The generated api key. - :attr str password: The password associated with the api key. + :param bool ok: ok. + :param str key: The generated api key. + :param str password: The password associated with the api key. """ - def __init__(self, - ok: bool, - key: str, - password: str) -> None: + def __init__( + self, + ok: bool, + key: str, + password: str, + ) -> None: """ Initialize a ApiKeysResult object. @@ -8976,16 +9969,16 @@ def __init__(self, def from_dict(cls, _dict: Dict) -> 'ApiKeysResult': """Initialize a ApiKeysResult object from a json dictionary.""" args = {} - if 'ok' in _dict: - args['ok'] = _dict.get('ok') + if (ok := _dict.get('ok')) is not None: + args['ok'] = ok else: raise ValueError('Required property \'ok\' not present in ApiKeysResult JSON') - if 'key' in _dict: - args['key'] = _dict.get('key') + if (key := _dict.get('key')) is not None: + args['key'] = key else: raise ValueError('Required property \'key\' not present in ApiKeysResult JSON') - if 'password' in _dict: - args['password'] = _dict.get('password') + if (password := _dict.get('password')) is not None: + args['password'] = password else: raise ValueError('Required property \'password\' not present in ApiKeysResult JSON') return cls(**args) @@ -9024,73 +10017,82 @@ def __ne__(self, other: 'ApiKeysResult') -> bool: """Return `true` when self and other are not equal, false otherwise.""" return not self == other -class Attachment(): + +class Attachment: """ Schema for an attachment. - :attr str content_type: (optional) Attachment MIME type. - :attr bytes data: (optional) Base64-encoded content. Available if attachment - content is requested by using the query parameters `attachments=true` or - `atts_since`. Note that when used with a view or changes feed `include_docs` - must also be `true`. - :attr str digest: (optional) Content hash digest. It starts with prefix which - announce hash type (e.g. `md5-`) and continues with Base64-encoded hash digest. - :attr int encoded_length: (optional) Compressed attachment size in bytes. - Available if content_type was in list of compressible types when the attachment - was added and the query parameter `att_encoding_info` is `true`. Note that when - used with a view or changes feed `include_docs` must also be `true`. - :attr str encoding: (optional) Compression codec. Available if content_type was - in list of compressible types when the attachment was added and the and the - query parameter `att_encoding_info` is `true`. Note that when used with a view - or changes feed `include_docs` must also be `true`. - :attr bool follows: (optional) True if the attachment follows in a multipart + :param str content_type: (optional) Attachment MIME type. + :param bytes data: (optional) Base64-encoded content. Available when requested + with `attachments=true` or `atts_since`. When retrieving attachments for a + single document this field is only avialable when accepting an application/json + response. For multipart responses each attachment is instead included in a + separate part of the response (see `follows`). + Note that SDK deserialization of documents with included attachments + automatically decodes the Base64 encoded attachment content string to bytes. + :param str digest: (optional) Content hash digest. It starts with prefix + declaring the hash type, `md5-` for example, and continues with the + Base64-encoded hash digest. + :param int encoded_length: (optional) Compressed attachment size in bytes. + Available for compressed attachments when requested with `att_encoding_info`. + The database compresses attachments if the content_type is in the list of + compressible types when added. + :param str encoding: (optional) Compression codec. Available for compressed + attachments when requested with `att_encoding_info`. The database compresses + attachments if the content_type is in the list of compressible types when added. + :param bool follows: (optional) True if the attachment follows in a multipart request or response. - :attr int length: (optional) Real attachment size in bytes. Not available if + :param int length: (optional) Real attachment size in bytes. Not available if inline attachment content requested. - :attr int revpos: (optional) Revision number when attachment was added. - :attr bool stub: (optional) Has `true` value if object contains stub info and no - content. Otherwise omitted in response. + :param int revpos: (optional) Revision number at attachment addition. + :param bool stub: (optional) Has `true` value if object has stub attachment + metadata, but not attachment content. Otherwise omitted in response. """ - def __init__(self, - *, - content_type: str = None, - data: bytes = None, - digest: str = None, - encoded_length: int = None, - encoding: str = None, - follows: bool = None, - length: int = None, - revpos: int = None, - stub: bool = None) -> None: + def __init__( + self, + *, + content_type: Optional[str] = None, + data: Optional[bytes] = None, + digest: Optional[str] = None, + encoded_length: Optional[int] = None, + encoding: Optional[str] = None, + follows: Optional[bool] = None, + length: Optional[int] = None, + revpos: Optional[int] = None, + stub: Optional[bool] = None, + ) -> None: """ Initialize a Attachment object. :param str content_type: (optional) Attachment MIME type. - :param bytes data: (optional) Base64-encoded content. Available if - attachment content is requested by using the query parameters - `attachments=true` or `atts_since`. Note that when used with a view or - changes feed `include_docs` must also be `true`. + :param bytes data: (optional) Base64-encoded content. Available when + requested with `attachments=true` or `atts_since`. When retrieving + attachments for a single document this field is only avialable when + accepting an application/json response. For multipart responses each + attachment is instead included in a separate part of the response (see + `follows`). + Note that SDK deserialization of documents with included attachments + automatically decodes the Base64 encoded attachment content string to + bytes. :param str digest: (optional) Content hash digest. It starts with prefix - which announce hash type (e.g. `md5-`) and continues with Base64-encoded - hash digest. + declaring the hash type, `md5-` for example, and continues with the + Base64-encoded hash digest. :param int encoded_length: (optional) Compressed attachment size in bytes. - Available if content_type was in list of compressible types when the - attachment was added and the query parameter `att_encoding_info` is `true`. - Note that when used with a view or changes feed `include_docs` must also be - `true`. - :param str encoding: (optional) Compression codec. Available if - content_type was in list of compressible types when the attachment was - added and the and the query parameter `att_encoding_info` is `true`. Note - that when used with a view or changes feed `include_docs` must also be - `true`. + Available for compressed attachments when requested with + `att_encoding_info`. The database compresses attachments if the + content_type is in the list of compressible types when added. + :param str encoding: (optional) Compression codec. Available for compressed + attachments when requested with `att_encoding_info`. The database + compresses attachments if the content_type is in the list of compressible + types when added. :param bool follows: (optional) True if the attachment follows in a multipart request or response. :param int length: (optional) Real attachment size in bytes. Not available if inline attachment content requested. - :param int revpos: (optional) Revision number when attachment was added. - :param bool stub: (optional) Has `true` value if object contains stub info - and no content. Otherwise omitted in response. + :param int revpos: (optional) Revision number at attachment addition. + :param bool stub: (optional) Has `true` value if object has stub attachment + metadata, but not attachment content. Otherwise omitted in response. """ self.content_type = content_type self.data = data @@ -9106,24 +10108,24 @@ def __init__(self, def from_dict(cls, _dict: Dict) -> 'Attachment': """Initialize a Attachment object from a json dictionary.""" args = {} - if 'content_type' in _dict: - args['content_type'] = _dict.get('content_type') - if 'data' in _dict: - args['data'] = base64.b64decode(_dict.get('data')) - if 'digest' in _dict: - args['digest'] = _dict.get('digest') - if 'encoded_length' in _dict: - args['encoded_length'] = _dict.get('encoded_length') - if 'encoding' in _dict: - args['encoding'] = _dict.get('encoding') - if 'follows' in _dict: - args['follows'] = _dict.get('follows') - if 'length' in _dict: - args['length'] = _dict.get('length') - if 'revpos' in _dict: - args['revpos'] = _dict.get('revpos') - if 'stub' in _dict: - args['stub'] = _dict.get('stub') + if (content_type := _dict.get('content_type')) is not None: + args['content_type'] = content_type + if (data := _dict.get('data')) is not None: + args['data'] = base64.b64decode(data) + if (digest := _dict.get('digest')) is not None: + args['digest'] = digest + if (encoded_length := _dict.get('encoded_length')) is not None: + args['encoded_length'] = encoded_length + if (encoding := _dict.get('encoding')) is not None: + args['encoding'] = encoding + if (follows := _dict.get('follows')) is not None: + args['follows'] = follows + if (length := _dict.get('length')) is not None: + args['length'] = length + if (revpos := _dict.get('revpos')) is not None: + args['revpos'] = revpos + if (stub := _dict.get('stub')) is not None: + args['stub'] = stub return cls(**args) @classmethod @@ -9172,25 +10174,34 @@ def __ne__(self, other: 'Attachment') -> bool: """Return `true` when self and other are not equal, false otherwise.""" return not self == other -class BulkDocs(): + +class BulkDocs: """ Schema for submitting documents for bulk modifications. - :attr List[Document] docs: Array of documents. - :attr bool new_edits: (optional) If `false`, prevents the database from + :param List[Document] docs: Array of documents. + :param bool new_edits: (optional) If `false`, prevents the database from assigning them new revision IDs. Default is `true`. + Avoid using this parameter, since this option applies document revisions without + checking for conflicts, so it is very easy to accidentally end up with a large + number of conflicts. """ - def __init__(self, - docs: List['Document'], - *, - new_edits: bool = None) -> None: + def __init__( + self, + docs: List['Document'], + *, + new_edits: Optional[bool] = None, + ) -> None: """ Initialize a BulkDocs object. :param List[Document] docs: Array of documents. :param bool new_edits: (optional) If `false`, prevents the database from assigning them new revision IDs. Default is `true`. + Avoid using this parameter, since this option applies document revisions + without checking for conflicts, so it is very easy to accidentally end up + with a large number of conflicts. """ self.docs = docs self.new_edits = new_edits @@ -9199,12 +10210,12 @@ def __init__(self, def from_dict(cls, _dict: Dict) -> 'BulkDocs': """Initialize a BulkDocs object from a json dictionary.""" args = {} - if 'docs' in _dict: - args['docs'] = [Document.from_dict(x) for x in _dict.get('docs')] + if (docs := _dict.get('docs')) is not None: + args['docs'] = [Document.from_dict(v) for v in docs] else: raise ValueError('Required property \'docs\' not present in BulkDocs JSON') - if 'new_edits' in _dict: - args['new_edits'] = _dict.get('new_edits') + if (new_edits := _dict.get('new_edits')) is not None: + args['new_edits'] = new_edits return cls(**args) @classmethod @@ -9216,7 +10227,13 @@ def to_dict(self) -> Dict: """Return a json dictionary representing this model.""" _dict = {} if hasattr(self, 'docs') and self.docs is not None: - _dict['docs'] = [x.to_dict() for x in self.docs] + docs_list = [] + for v in self.docs: + if isinstance(v, dict): + docs_list.append(v) + else: + docs_list.append(v.to_dict()) + _dict['docs'] = docs_list if hasattr(self, 'new_edits') and self.new_edits is not None: _dict['new_edits'] = self.new_edits return _dict @@ -9239,21 +10256,24 @@ def __ne__(self, other: 'BulkDocs') -> bool: """Return `true` when self and other are not equal, false otherwise.""" return not self == other -class BulkGetQueryDocument(): + +class BulkGetQueryDocument: """ Schema for a document item in a bulk get query. - :attr List[str] atts_since: (optional) Includes attachments only since specified - revisions. - :attr str id: Schema for a document ID. - :attr str rev: (optional) Schema for a document revision identifier. + :param List[str] atts_since: (optional) Includes attachments only since + specified revisions. + :param str id: Schema for a document ID. + :param str rev: (optional) Schema for a document revision identifier. """ - def __init__(self, - id: str, - *, - atts_since: List[str] = None, - rev: str = None) -> None: + def __init__( + self, + id: str, + *, + atts_since: Optional[List[str]] = None, + rev: Optional[str] = None, + ) -> None: """ Initialize a BulkGetQueryDocument object. @@ -9270,14 +10290,14 @@ def __init__(self, def from_dict(cls, _dict: Dict) -> 'BulkGetQueryDocument': """Initialize a BulkGetQueryDocument object from a json dictionary.""" args = {} - if 'atts_since' in _dict: - args['atts_since'] = _dict.get('atts_since') - if 'id' in _dict: - args['id'] = _dict.get('id') + if (atts_since := _dict.get('atts_since')) is not None: + args['atts_since'] = atts_since + if (id := _dict.get('id')) is not None: + args['id'] = id else: raise ValueError('Required property \'id\' not present in BulkGetQueryDocument JSON') - if 'rev' in _dict: - args['rev'] = _dict.get('rev') + if (rev := _dict.get('rev')) is not None: + args['rev'] = rev return cls(**args) @classmethod @@ -9314,15 +10334,18 @@ def __ne__(self, other: 'BulkGetQueryDocument') -> bool: """Return `true` when self and other are not equal, false otherwise.""" return not self == other -class BulkGetResult(): + +class BulkGetResult: """ Schema for the results object of a bulk get operation. - :attr List[BulkGetResultItem] results: Results. + :param List[BulkGetResultItem] results: Results. """ - def __init__(self, - results: List['BulkGetResultItem']) -> None: + def __init__( + self, + results: List['BulkGetResultItem'], + ) -> None: """ Initialize a BulkGetResult object. @@ -9334,8 +10357,8 @@ def __init__(self, def from_dict(cls, _dict: Dict) -> 'BulkGetResult': """Initialize a BulkGetResult object from a json dictionary.""" args = {} - if 'results' in _dict: - args['results'] = [BulkGetResultItem.from_dict(x) for x in _dict.get('results')] + if (results := _dict.get('results')) is not None: + args['results'] = [BulkGetResultItem.from_dict(v) for v in results] else: raise ValueError('Required property \'results\' not present in BulkGetResult JSON') return cls(**args) @@ -9349,7 +10372,13 @@ def to_dict(self) -> Dict: """Return a json dictionary representing this model.""" _dict = {} if hasattr(self, 'results') and self.results is not None: - _dict['results'] = [x.to_dict() for x in self.results] + results_list = [] + for v in self.results: + if isinstance(v, dict): + results_list.append(v) + else: + results_list.append(v.to_dict()) + _dict['results'] = results_list return _dict def _to_dict(self): @@ -9370,20 +10399,23 @@ def __ne__(self, other: 'BulkGetResult') -> bool: """Return `true` when self and other are not equal, false otherwise.""" return not self == other -class BulkGetResultDocument(): + +class BulkGetResultDocument: """ Schema for BulkGetResult object containing a successfully retrieved document or error information. - :attr DocumentResult error: (optional) Schema for the result of a document + :param DocumentResult error: (optional) Schema for the result of a document modification. - :attr Document ok: (optional) Schema for a document. + :param Document ok: (optional) Schema for a document. """ - def __init__(self, - *, - error: 'DocumentResult' = None, - ok: 'Document' = None) -> None: + def __init__( + self, + *, + error: Optional['DocumentResult'] = None, + ok: Optional['Document'] = None, + ) -> None: """ Initialize a BulkGetResultDocument object. @@ -9398,10 +10430,10 @@ def __init__(self, def from_dict(cls, _dict: Dict) -> 'BulkGetResultDocument': """Initialize a BulkGetResultDocument object from a json dictionary.""" args = {} - if 'error' in _dict: - args['error'] = DocumentResult.from_dict(_dict.get('error')) - if 'ok' in _dict: - args['ok'] = Document.from_dict(_dict.get('ok')) + if (error := _dict.get('error')) is not None: + args['error'] = DocumentResult.from_dict(error) + if (ok := _dict.get('ok')) is not None: + args['ok'] = Document.from_dict(ok) return cls(**args) @classmethod @@ -9413,9 +10445,15 @@ def to_dict(self) -> Dict: """Return a json dictionary representing this model.""" _dict = {} if hasattr(self, 'error') and self.error is not None: - _dict['error'] = self.error.to_dict() + if isinstance(self.error, dict): + _dict['error'] = self.error + else: + _dict['error'] = self.error.to_dict() if hasattr(self, 'ok') and self.ok is not None: - _dict['ok'] = self.ok.to_dict() + if isinstance(self.ok, dict): + _dict['ok'] = self.ok + else: + _dict['ok'] = self.ok.to_dict() return _dict def _to_dict(self): @@ -9436,18 +10474,21 @@ def __ne__(self, other: 'BulkGetResultDocument') -> bool: """Return `true` when self and other are not equal, false otherwise.""" return not self == other -class BulkGetResultItem(): + +class BulkGetResultItem: """ Schema for the document revisions information from a bulk get operation. - :attr List[BulkGetResultDocument] docs: Array of document revisions or error + :param List[BulkGetResultDocument] docs: Array of document revisions or error information. - :attr str id: Schema for a document ID. + :param str id: Schema for a document ID. """ - def __init__(self, - docs: List['BulkGetResultDocument'], - id: str) -> None: + def __init__( + self, + docs: List['BulkGetResultDocument'], + id: str, + ) -> None: """ Initialize a BulkGetResultItem object. @@ -9462,12 +10503,12 @@ def __init__(self, def from_dict(cls, _dict: Dict) -> 'BulkGetResultItem': """Initialize a BulkGetResultItem object from a json dictionary.""" args = {} - if 'docs' in _dict: - args['docs'] = [BulkGetResultDocument.from_dict(x) for x in _dict.get('docs')] + if (docs := _dict.get('docs')) is not None: + args['docs'] = [BulkGetResultDocument.from_dict(v) for v in docs] else: raise ValueError('Required property \'docs\' not present in BulkGetResultItem JSON') - if 'id' in _dict: - args['id'] = _dict.get('id') + if (id := _dict.get('id')) is not None: + args['id'] = id else: raise ValueError('Required property \'id\' not present in BulkGetResultItem JSON') return cls(**args) @@ -9481,7 +10522,13 @@ def to_dict(self) -> Dict: """Return a json dictionary representing this model.""" _dict = {} if hasattr(self, 'docs') and self.docs is not None: - _dict['docs'] = [x.to_dict() for x in self.docs] + docs_list = [] + for v in self.docs: + if isinstance(v, dict): + docs_list.append(v) + else: + docs_list.append(v.to_dict()) + _dict['docs'] = docs_list if hasattr(self, 'id') and self.id is not None: _dict['id'] = self.id return _dict @@ -9504,20 +10551,149 @@ def __ne__(self, other: 'BulkGetResultItem') -> bool: """Return `true` when self and other are not equal, false otherwise.""" return not self == other -class CapacityThroughputInformation(): + +class CapacityDatabasesInformation: + """ + Schema for information about maximum total database count. + + :param CapacityDatabasesInformationCurrent current: (optional) Schema for + information about the current database capacity. + """ + + def __init__( + self, + *, + current: Optional['CapacityDatabasesInformationCurrent'] = None, + ) -> None: + """ + Initialize a CapacityDatabasesInformation object. + + :param CapacityDatabasesInformationCurrent current: (optional) Schema for + information about the current database capacity. + """ + self.current = current + + @classmethod + def from_dict(cls, _dict: Dict) -> 'CapacityDatabasesInformation': + """Initialize a CapacityDatabasesInformation object from a json dictionary.""" + args = {} + if (current := _dict.get('current')) is not None: + args['current'] = CapacityDatabasesInformationCurrent.from_dict(current) + return cls(**args) + + @classmethod + def _from_dict(cls, _dict): + """Initialize a CapacityDatabasesInformation object from a json dictionary.""" + return cls.from_dict(_dict) + + def to_dict(self) -> Dict: + """Return a json dictionary representing this model.""" + _dict = {} + if hasattr(self, 'current') and self.current is not None: + if isinstance(self.current, dict): + _dict['current'] = self.current + else: + _dict['current'] = self.current.to_dict() + return _dict + + def _to_dict(self): + """Return a json dictionary representing this model.""" + return self.to_dict() + + def __str__(self) -> str: + """Return a `str` version of this CapacityDatabasesInformation object.""" + return json.dumps(self.to_dict(), indent=2) + + def __eq__(self, other: 'CapacityDatabasesInformation') -> bool: + """Return `true` when self and other are equal, false otherwise.""" + if not isinstance(other, self.__class__): + return False + return self.__dict__ == other.__dict__ + + def __ne__(self, other: 'CapacityDatabasesInformation') -> bool: + """Return `true` when self and other are not equal, false otherwise.""" + return not self == other + + +class CapacityDatabasesInformationCurrent: + """ + Schema for information about the current database capacity. + + :param DatabasesCountInformation databases: (optional) Schema for databases + count. + """ + + def __init__( + self, + *, + databases: Optional['DatabasesCountInformation'] = None, + ) -> None: + """ + Initialize a CapacityDatabasesInformationCurrent object. + + :param DatabasesCountInformation databases: (optional) Schema for databases + count. + """ + self.databases = databases + + @classmethod + def from_dict(cls, _dict: Dict) -> 'CapacityDatabasesInformationCurrent': + """Initialize a CapacityDatabasesInformationCurrent object from a json dictionary.""" + args = {} + if (databases := _dict.get('databases')) is not None: + args['databases'] = DatabasesCountInformation.from_dict(databases) + return cls(**args) + + @classmethod + def _from_dict(cls, _dict): + """Initialize a CapacityDatabasesInformationCurrent object from a json dictionary.""" + return cls.from_dict(_dict) + + def to_dict(self) -> Dict: + """Return a json dictionary representing this model.""" + _dict = {} + if hasattr(self, 'databases') and self.databases is not None: + if isinstance(self.databases, dict): + _dict['databases'] = self.databases + else: + _dict['databases'] = self.databases.to_dict() + return _dict + + def _to_dict(self): + """Return a json dictionary representing this model.""" + return self.to_dict() + + def __str__(self) -> str: + """Return a `str` version of this CapacityDatabasesInformationCurrent object.""" + return json.dumps(self.to_dict(), indent=2) + + def __eq__(self, other: 'CapacityDatabasesInformationCurrent') -> bool: + """Return `true` when self and other are equal, false otherwise.""" + if not isinstance(other, self.__class__): + return False + return self.__dict__ == other.__dict__ + + def __ne__(self, other: 'CapacityDatabasesInformationCurrent') -> bool: + """Return `true` when self and other are not equal, false otherwise.""" + return not self == other + + +class CapacityThroughputInformation: """ Schema for information about the currently provisioned and target throughput capacity. - :attr CapacityThroughputInformationCurrent current: Detailed information about + :param CapacityThroughputInformationCurrent current: Detailed information about provisioned throughput capacity. - :attr CapacityThroughputInformationTarget target: (optional) Detailed + :param CapacityThroughputInformationTarget target: (optional) Detailed information about target throughput capacity. """ - def __init__(self, - current: 'CapacityThroughputInformationCurrent', - *, - target: 'CapacityThroughputInformationTarget' = None) -> None: + def __init__( + self, + current: 'CapacityThroughputInformationCurrent', + *, + target: Optional['CapacityThroughputInformationTarget'] = None, + ) -> None: """ Initialize a CapacityThroughputInformation object. @@ -9533,12 +10709,12 @@ def __init__(self, def from_dict(cls, _dict: Dict) -> 'CapacityThroughputInformation': """Initialize a CapacityThroughputInformation object from a json dictionary.""" args = {} - if 'current' in _dict: - args['current'] = CapacityThroughputInformationCurrent.from_dict(_dict.get('current')) + if (current := _dict.get('current')) is not None: + args['current'] = CapacityThroughputInformationCurrent.from_dict(current) else: raise ValueError('Required property \'current\' not present in CapacityThroughputInformation JSON') - if 'target' in _dict: - args['target'] = CapacityThroughputInformationTarget.from_dict(_dict.get('target')) + if (target := _dict.get('target')) is not None: + args['target'] = CapacityThroughputInformationTarget.from_dict(target) return cls(**args) @classmethod @@ -9550,9 +10726,15 @@ def to_dict(self) -> Dict: """Return a json dictionary representing this model.""" _dict = {} if hasattr(self, 'current') and self.current is not None: - _dict['current'] = self.current.to_dict() + if isinstance(self.current, dict): + _dict['current'] = self.current + else: + _dict['current'] = self.current.to_dict() if hasattr(self, 'target') and self.target is not None: - _dict['target'] = self.target.to_dict() + if isinstance(self.target, dict): + _dict['target'] = self.target + else: + _dict['target'] = self.target.to_dict() return _dict def _to_dict(self): @@ -9573,16 +10755,19 @@ def __ne__(self, other: 'CapacityThroughputInformation') -> bool: """Return `true` when self and other are not equal, false otherwise.""" return not self == other -class CapacityThroughputInformationCurrent(): + +class CapacityThroughputInformationCurrent: """ Detailed information about provisioned throughput capacity. - :attr ThroughputInformation throughput: Schema for detailed information about + :param ThroughputInformation throughput: Schema for detailed information about throughput capacity with breakdown by specific throughput requests classes. """ - def __init__(self, - throughput: 'ThroughputInformation') -> None: + def __init__( + self, + throughput: 'ThroughputInformation', + ) -> None: """ Initialize a CapacityThroughputInformationCurrent object. @@ -9596,8 +10781,8 @@ def __init__(self, def from_dict(cls, _dict: Dict) -> 'CapacityThroughputInformationCurrent': """Initialize a CapacityThroughputInformationCurrent object from a json dictionary.""" args = {} - if 'throughput' in _dict: - args['throughput'] = ThroughputInformation.from_dict(_dict.get('throughput')) + if (throughput := _dict.get('throughput')) is not None: + args['throughput'] = ThroughputInformation.from_dict(throughput) else: raise ValueError('Required property \'throughput\' not present in CapacityThroughputInformationCurrent JSON') return cls(**args) @@ -9611,7 +10796,10 @@ def to_dict(self) -> Dict: """Return a json dictionary representing this model.""" _dict = {} if hasattr(self, 'throughput') and self.throughput is not None: - _dict['throughput'] = self.throughput.to_dict() + if isinstance(self.throughput, dict): + _dict['throughput'] = self.throughput + else: + _dict['throughput'] = self.throughput.to_dict() return _dict def _to_dict(self): @@ -9632,16 +10820,19 @@ def __ne__(self, other: 'CapacityThroughputInformationCurrent') -> bool: """Return `true` when self and other are not equal, false otherwise.""" return not self == other -class CapacityThroughputInformationTarget(): + +class CapacityThroughputInformationTarget: """ Detailed information about target throughput capacity. - :attr ThroughputInformation throughput: Schema for detailed information about + :param ThroughputInformation throughput: Schema for detailed information about throughput capacity with breakdown by specific throughput requests classes. """ - def __init__(self, - throughput: 'ThroughputInformation') -> None: + def __init__( + self, + throughput: 'ThroughputInformation', + ) -> None: """ Initialize a CapacityThroughputInformationTarget object. @@ -9655,8 +10846,8 @@ def __init__(self, def from_dict(cls, _dict: Dict) -> 'CapacityThroughputInformationTarget': """Initialize a CapacityThroughputInformationTarget object from a json dictionary.""" args = {} - if 'throughput' in _dict: - args['throughput'] = ThroughputInformation.from_dict(_dict.get('throughput')) + if (throughput := _dict.get('throughput')) is not None: + args['throughput'] = ThroughputInformation.from_dict(throughput) else: raise ValueError('Required property \'throughput\' not present in CapacityThroughputInformationTarget JSON') return cls(**args) @@ -9670,7 +10861,10 @@ def to_dict(self) -> Dict: """Return a json dictionary representing this model.""" _dict = {} if hasattr(self, 'throughput') and self.throughput is not None: - _dict['throughput'] = self.throughput.to_dict() + if isinstance(self.throughput, dict): + _dict['throughput'] = self.throughput + else: + _dict['throughput'] = self.throughput.to_dict() return _dict def _to_dict(self): @@ -9691,15 +10885,18 @@ def __ne__(self, other: 'CapacityThroughputInformationTarget') -> bool: """Return `true` when self and other are not equal, false otherwise.""" return not self == other -class Change(): + +class Change: """ Schema for a document leaf with single field rev. - :attr str rev: Schema for a document revision identifier. + :param str rev: Schema for a document revision identifier. """ - def __init__(self, - rev: str) -> None: + def __init__( + self, + rev: str, + ) -> None: """ Initialize a Change object. @@ -9711,8 +10908,8 @@ def __init__(self, def from_dict(cls, _dict: Dict) -> 'Change': """Initialize a Change object from a json dictionary.""" args = {} - if 'rev' in _dict: - args['rev'] = _dict.get('rev') + if (rev := _dict.get('rev')) is not None: + args['rev'] = rev else: raise ValueError('Required property \'rev\' not present in Change JSON') return cls(**args) @@ -9747,19 +10944,22 @@ def __ne__(self, other: 'Change') -> bool: """Return `true` when self and other are not equal, false otherwise.""" return not self == other -class ChangesResult(): + +class ChangesResult: """ Schema for normal changes feed result. - :attr str last_seq: last_seq. - :attr int pending: pending. - :attr List[ChangesResultItem] results: results. + :param str last_seq: last_seq. + :param int pending: pending. + :param List[ChangesResultItem] results: results. """ - def __init__(self, - last_seq: str, - pending: int, - results: List['ChangesResultItem']) -> None: + def __init__( + self, + last_seq: str, + pending: int, + results: List['ChangesResultItem'], + ) -> None: """ Initialize a ChangesResult object. @@ -9775,16 +10975,16 @@ def __init__(self, def from_dict(cls, _dict: Dict) -> 'ChangesResult': """Initialize a ChangesResult object from a json dictionary.""" args = {} - if 'last_seq' in _dict: - args['last_seq'] = _dict.get('last_seq') + if (last_seq := _dict.get('last_seq')) is not None: + args['last_seq'] = last_seq else: raise ValueError('Required property \'last_seq\' not present in ChangesResult JSON') - if 'pending' in _dict: - args['pending'] = _dict.get('pending') + if (pending := _dict.get('pending')) is not None: + args['pending'] = pending else: raise ValueError('Required property \'pending\' not present in ChangesResult JSON') - if 'results' in _dict: - args['results'] = [ChangesResultItem.from_dict(x) for x in _dict.get('results')] + if (results := _dict.get('results')) is not None: + args['results'] = [ChangesResultItem.from_dict(v) for v in results] else: raise ValueError('Required property \'results\' not present in ChangesResult JSON') return cls(**args) @@ -9802,7 +11002,13 @@ def to_dict(self) -> Dict: if hasattr(self, 'pending') and self.pending is not None: _dict['pending'] = self.pending if hasattr(self, 'results') and self.results is not None: - _dict['results'] = [x.to_dict() for x in self.results] + results_list = [] + for v in self.results: + if isinstance(v, dict): + results_list.append(v) + else: + results_list.append(v.to_dict()) + _dict['results'] = results_list return _dict def _to_dict(self): @@ -9823,24 +11029,27 @@ def __ne__(self, other: 'ChangesResult') -> bool: """Return `true` when self and other are not equal, false otherwise.""" return not self == other -class ChangesResultItem(): + +class ChangesResultItem: """ Schema for an item in the changes results array. - :attr List[Change] changes: List of document's leaves with single field rev. - :attr bool deleted: (optional) if `true` then the document is deleted. - :attr Document doc: (optional) Schema for a document. - :attr str id: Schema for a document ID. - :attr str seq: Update sequence. + :param List[Change] changes: List of document's leaves with single field rev. + :param bool deleted: (optional) if `true` then the document is deleted. + :param Document doc: (optional) Schema for a document. + :param str id: Schema for a document ID. + :param str seq: Update sequence. """ - def __init__(self, - changes: List['Change'], - id: str, - seq: str, - *, - deleted: bool = None, - doc: 'Document' = None) -> None: + def __init__( + self, + changes: List['Change'], + id: str, + seq: str, + *, + deleted: Optional[bool] = None, + doc: Optional['Document'] = None, + ) -> None: """ Initialize a ChangesResultItem object. @@ -9861,20 +11070,20 @@ def __init__(self, def from_dict(cls, _dict: Dict) -> 'ChangesResultItem': """Initialize a ChangesResultItem object from a json dictionary.""" args = {} - if 'changes' in _dict: - args['changes'] = [Change.from_dict(x) for x in _dict.get('changes')] + if (changes := _dict.get('changes')) is not None: + args['changes'] = [Change.from_dict(v) for v in changes] else: raise ValueError('Required property \'changes\' not present in ChangesResultItem JSON') - if 'deleted' in _dict: - args['deleted'] = _dict.get('deleted') - if 'doc' in _dict: - args['doc'] = Document.from_dict(_dict.get('doc')) - if 'id' in _dict: - args['id'] = _dict.get('id') + if (deleted := _dict.get('deleted')) is not None: + args['deleted'] = deleted + if (doc := _dict.get('doc')) is not None: + args['doc'] = Document.from_dict(doc) + if (id := _dict.get('id')) is not None: + args['id'] = id else: raise ValueError('Required property \'id\' not present in ChangesResultItem JSON') - if 'seq' in _dict: - args['seq'] = _dict.get('seq') + if (seq := _dict.get('seq')) is not None: + args['seq'] = seq else: raise ValueError('Required property \'seq\' not present in ChangesResultItem JSON') return cls(**args) @@ -9888,11 +11097,20 @@ def to_dict(self) -> Dict: """Return a json dictionary representing this model.""" _dict = {} if hasattr(self, 'changes') and self.changes is not None: - _dict['changes'] = [x.to_dict() for x in self.changes] + changes_list = [] + for v in self.changes: + if isinstance(v, dict): + changes_list.append(v) + else: + changes_list.append(v.to_dict()) + _dict['changes'] = changes_list if hasattr(self, 'deleted') and self.deleted is not None: _dict['deleted'] = self.deleted if hasattr(self, 'doc') and self.doc is not None: - _dict['doc'] = self.doc.to_dict() + if isinstance(self.doc, dict): + _dict['doc'] = self.doc + else: + _dict['doc'] = self.doc.to_dict() if hasattr(self, 'id') and self.id is not None: _dict['id'] = self.id if hasattr(self, 'seq') and self.seq is not None: @@ -9917,24 +11135,29 @@ def __ne__(self, other: 'ChangesResultItem') -> bool: """Return `true` when self and other are not equal, false otherwise.""" return not self == other -class ContentInformationSizes(): + +class ContentInformationSizes: """ Schema for size information of content. - :attr int active: The active size of the content, in bytes. - :attr int external: The total uncompressed size of the content, in bytes. - :attr int file: The total size of the content as stored on disk, in bytes. + :param int active: The active size of the content, in bytes. + :param int external: The total uncompressed size of the content, in bytes. + This is the value used for IBM Cloudant storage billing. + :param int file: The total size of the content as stored on disk, in bytes. """ - def __init__(self, - active: int, - external: int, - file: int) -> None: + def __init__( + self, + active: int, + external: int, + file: int, + ) -> None: """ Initialize a ContentInformationSizes object. :param int active: The active size of the content, in bytes. :param int external: The total uncompressed size of the content, in bytes. + This is the value used for IBM Cloudant storage billing. :param int file: The total size of the content as stored on disk, in bytes. """ self.active = active @@ -9945,16 +11168,16 @@ def __init__(self, def from_dict(cls, _dict: Dict) -> 'ContentInformationSizes': """Initialize a ContentInformationSizes object from a json dictionary.""" args = {} - if 'active' in _dict: - args['active'] = _dict.get('active') + if (active := _dict.get('active')) is not None: + args['active'] = active else: raise ValueError('Required property \'active\' not present in ContentInformationSizes JSON') - if 'external' in _dict: - args['external'] = _dict.get('external') + if (external := _dict.get('external')) is not None: + args['external'] = external else: raise ValueError('Required property \'external\' not present in ContentInformationSizes JSON') - if 'file' in _dict: - args['file'] = _dict.get('file') + if (file := _dict.get('file')) is not None: + args['file'] = file else: raise ValueError('Required property \'file\' not present in ContentInformationSizes JSON') return cls(**args) @@ -9993,23 +11216,27 @@ def __ne__(self, other: 'ContentInformationSizes') -> bool: """Return `true` when self and other are not equal, false otherwise.""" return not self == other -class CorsInformation(): + +class CorsInformation: """ Schema for information about the CORS configuration. - :attr bool allow_credentials: Boolean value to allow authentication credentials. - If set to true, browser requests must be done by using withCredentials = true. - :attr bool enable_cors: Boolean value to turn CORS on and off. - :attr List[str] origins: An array of strings that contain allowed origin + :param bool allow_credentials: Boolean value to allow authentication + credentials. If set to true, browser requests must be done by using + withCredentials = true. + :param bool enable_cors: Boolean value to turn CORS on and off. + :param List[str] origins: An array of strings that contain allowed origin domains. You have to specify the full URL including the protocol. It is recommended that only the HTTPS protocol is used. Subdomains count as separate domains, so you have to specify all subdomains used. """ - def __init__(self, - allow_credentials: bool, - enable_cors: bool, - origins: List[str]) -> None: + def __init__( + self, + allow_credentials: bool, + enable_cors: bool, + origins: List[str], + ) -> None: """ Initialize a CorsInformation object. @@ -10030,16 +11257,16 @@ def __init__(self, def from_dict(cls, _dict: Dict) -> 'CorsInformation': """Initialize a CorsInformation object from a json dictionary.""" args = {} - if 'allow_credentials' in _dict: - args['allow_credentials'] = _dict.get('allow_credentials') + if (allow_credentials := _dict.get('allow_credentials')) is not None: + args['allow_credentials'] = allow_credentials else: raise ValueError('Required property \'allow_credentials\' not present in CorsInformation JSON') - if 'enable_cors' in _dict: - args['enable_cors'] = _dict.get('enable_cors') + if (enable_cors := _dict.get('enable_cors')) is not None: + args['enable_cors'] = enable_cors else: raise ValueError('Required property \'enable_cors\' not present in CorsInformation JSON') - if 'origins' in _dict: - args['origins'] = _dict.get('origins') + if (origins := _dict.get('origins')) is not None: + args['origins'] = origins else: raise ValueError('Required property \'origins\' not present in CorsInformation JSON') return cls(**args) @@ -10078,16 +11305,82 @@ def __ne__(self, other: 'CorsInformation') -> bool: """Return `true` when self and other are not equal, false otherwise.""" return not self == other -class CurrentThroughputInformation(): + +class CurrentDatabasesInformation: + """ + Schema for information about the current database counts. + + :param DatabasesCountInformation databases: (optional) Schema for databases + count. + """ + + def __init__( + self, + *, + databases: Optional['DatabasesCountInformation'] = None, + ) -> None: + """ + Initialize a CurrentDatabasesInformation object. + + :param DatabasesCountInformation databases: (optional) Schema for databases + count. + """ + self.databases = databases + + @classmethod + def from_dict(cls, _dict: Dict) -> 'CurrentDatabasesInformation': + """Initialize a CurrentDatabasesInformation object from a json dictionary.""" + args = {} + if (databases := _dict.get('databases')) is not None: + args['databases'] = DatabasesCountInformation.from_dict(databases) + return cls(**args) + + @classmethod + def _from_dict(cls, _dict): + """Initialize a CurrentDatabasesInformation object from a json dictionary.""" + return cls.from_dict(_dict) + + def to_dict(self) -> Dict: + """Return a json dictionary representing this model.""" + _dict = {} + if hasattr(self, 'databases') and self.databases is not None: + if isinstance(self.databases, dict): + _dict['databases'] = self.databases + else: + _dict['databases'] = self.databases.to_dict() + return _dict + + def _to_dict(self): + """Return a json dictionary representing this model.""" + return self.to_dict() + + def __str__(self) -> str: + """Return a `str` version of this CurrentDatabasesInformation object.""" + return json.dumps(self.to_dict(), indent=2) + + def __eq__(self, other: 'CurrentDatabasesInformation') -> bool: + """Return `true` when self and other are equal, false otherwise.""" + if not isinstance(other, self.__class__): + return False + return self.__dict__ == other.__dict__ + + def __ne__(self, other: 'CurrentDatabasesInformation') -> bool: + """Return `true` when self and other are not equal, false otherwise.""" + return not self == other + + +class CurrentThroughputInformation: """ Schema for information about current consumption of a provisioned throughput capacity. - :attr CurrentThroughputInformationThroughput throughput: Detailed information + :param CurrentThroughputInformationThroughput throughput: Detailed information about current consumption. """ - def __init__(self, - throughput: 'CurrentThroughputInformationThroughput') -> None: + def __init__( + self, + throughput: 'CurrentThroughputInformationThroughput', + ) -> None: """ Initialize a CurrentThroughputInformation object. @@ -10100,8 +11393,8 @@ def __init__(self, def from_dict(cls, _dict: Dict) -> 'CurrentThroughputInformation': """Initialize a CurrentThroughputInformation object from a json dictionary.""" args = {} - if 'throughput' in _dict: - args['throughput'] = CurrentThroughputInformationThroughput.from_dict(_dict.get('throughput')) + if (throughput := _dict.get('throughput')) is not None: + args['throughput'] = CurrentThroughputInformationThroughput.from_dict(throughput) else: raise ValueError('Required property \'throughput\' not present in CurrentThroughputInformation JSON') return cls(**args) @@ -10115,7 +11408,10 @@ def to_dict(self) -> Dict: """Return a json dictionary representing this model.""" _dict = {} if hasattr(self, 'throughput') and self.throughput is not None: - _dict['throughput'] = self.throughput.to_dict() + if isinstance(self.throughput, dict): + _dict['throughput'] = self.throughput + else: + _dict['throughput'] = self.throughput.to_dict() return _dict def _to_dict(self): @@ -10136,22 +11432,25 @@ def __ne__(self, other: 'CurrentThroughputInformation') -> bool: """Return `true` when self and other are not equal, false otherwise.""" return not self == other -class CurrentThroughputInformationThroughput(): + +class CurrentThroughputInformationThroughput: """ Detailed information about current consumption. - :attr int query: Number of global queries conducted against the instance for a + :param int query: Number of global queries conducted against the instance for a given second. - :attr int read: Number of reads conducted against the instance for a given + :param int read: Number of reads conducted against the instance for a given second. - :attr int write: Number of writes conducted against the instance for a given + :param int write: Number of writes conducted against the instance for a given second. """ - def __init__(self, - query: int, - read: int, - write: int) -> None: + def __init__( + self, + query: int, + read: int, + write: int, + ) -> None: """ Initialize a CurrentThroughputInformationThroughput object. @@ -10170,16 +11469,16 @@ def __init__(self, def from_dict(cls, _dict: Dict) -> 'CurrentThroughputInformationThroughput': """Initialize a CurrentThroughputInformationThroughput object from a json dictionary.""" args = {} - if 'query' in _dict: - args['query'] = _dict.get('query') + if (query := _dict.get('query')) is not None: + args['query'] = query else: raise ValueError('Required property \'query\' not present in CurrentThroughputInformationThroughput JSON') - if 'read' in _dict: - args['read'] = _dict.get('read') + if (read := _dict.get('read')) is not None: + args['read'] = read else: raise ValueError('Required property \'read\' not present in CurrentThroughputInformationThroughput JSON') - if 'write' in _dict: - args['write'] = _dict.get('write') + if (write := _dict.get('write')) is not None: + args['write'] = write else: raise ValueError('Required property \'write\' not present in CurrentThroughputInformationThroughput JSON') return cls(**args) @@ -10218,46 +11517,57 @@ def __ne__(self, other: 'CurrentThroughputInformationThroughput') -> bool: """Return `true` when self and other are not equal, false otherwise.""" return not self == other -class DatabaseInformation(): + +class DatabaseInformation: """ Schema for information about a database. - :attr DatabaseInformationCluster cluster: Schema for database cluster + :param DatabaseInformationCluster cluster: Schema for database cluster information. - :attr str committed_update_seq: (optional) An opaque string that describes the + :param str committed_update_seq: (optional) An opaque string that describes the committed state of the database. - :attr bool compact_running: True if the database compaction routine is operating - on this database. - :attr str compacted_seq: (optional) An opaque string that describes the + :param bool compact_running: True if the database compaction routine is + operating on this database. + :param str compacted_seq: (optional) An opaque string that describes the compaction state of the database. - :attr str db_name: The name of the database. - :attr int disk_format_version: The version of the physical format used for the + :param str db_name: Schema for a database name. + :param int disk_format_version: The version of the physical format used for the data when it is stored on disk. - :attr int doc_count: A count of the documents in the specified database. - :attr int doc_del_count: Number of deleted documents. - :attr str engine: (optional) The engine used for the database. - :attr DatabaseInformationProps props: Schema for database properties. - :attr ContentInformationSizes sizes: Schema for size information of content. - :attr str update_seq: An opaque string that describes the state of the database. - Do not rely on this string for counting the number of updates. - :attr str uuid: (optional) The UUID of the database. + :param int doc_count: A count of the documents in the specified database. + :param int doc_del_count: Number of deleted documents. + :param str engine: (optional) The engine used for the database. + :param str instance_start_time: An opaque string to detect whether a database + has been recreated. The field name is for compatibility with old replicator + versions. Do not use the value to infer timing information. Typically only used + by replicators. + :param PartitionedIndexesInformation partitioned_indexes: (optional) Information + about database's partitioned indexes. + :param DatabaseInformationProps props: Schema for database properties. + :param ContentInformationSizes sizes: Schema for size information of content. + :param str update_seq: An opaque string that describes the state of the + database. Do not rely on this string for counting the number of updates. + :param str uuid: (optional) The UUID of the database. """ - def __init__(self, - cluster: 'DatabaseInformationCluster', - compact_running: bool, - db_name: str, - disk_format_version: int, - doc_count: int, - doc_del_count: int, - props: 'DatabaseInformationProps', - sizes: 'ContentInformationSizes', - update_seq: str, - *, - committed_update_seq: str = None, - compacted_seq: str = None, - engine: str = None, - uuid: str = None) -> None: + def __init__( + self, + cluster: 'DatabaseInformationCluster', + compact_running: bool, + db_name: str, + disk_format_version: int, + doc_count: int, + doc_del_count: int, + instance_start_time: str, + props: 'DatabaseInformationProps', + sizes: 'ContentInformationSizes', + update_seq: str, + *, + committed_update_seq: Optional[str] = None, + compacted_seq: Optional[str] = None, + engine: Optional[str] = None, + partitioned_indexes: Optional['PartitionedIndexesInformation'] = None, + uuid: Optional[str] = None, + ) -> None: """ Initialize a DatabaseInformation object. @@ -10265,11 +11575,15 @@ def __init__(self, information. :param bool compact_running: True if the database compaction routine is operating on this database. - :param str db_name: The name of the database. + :param str db_name: Schema for a database name. :param int disk_format_version: The version of the physical format used for the data when it is stored on disk. :param int doc_count: A count of the documents in the specified database. :param int doc_del_count: Number of deleted documents. + :param str instance_start_time: An opaque string to detect whether a + database has been recreated. The field name is for compatibility with old + replicator versions. Do not use the value to infer timing information. + Typically only used by replicators. :param DatabaseInformationProps props: Schema for database properties. :param ContentInformationSizes sizes: Schema for size information of content. @@ -10280,6 +11594,8 @@ def __init__(self, :param str compacted_seq: (optional) An opaque string that describes the compaction state of the database. :param str engine: (optional) The engine used for the database. + :param PartitionedIndexesInformation partitioned_indexes: (optional) + Information about database's partitioned indexes. :param str uuid: (optional) The UUID of the database. """ self.cluster = cluster @@ -10291,6 +11607,8 @@ def __init__(self, self.doc_count = doc_count self.doc_del_count = doc_del_count self.engine = engine + self.instance_start_time = instance_start_time + self.partitioned_indexes = partitioned_indexes self.props = props self.sizes = sizes self.update_seq = update_seq @@ -10300,50 +11618,56 @@ def __init__(self, def from_dict(cls, _dict: Dict) -> 'DatabaseInformation': """Initialize a DatabaseInformation object from a json dictionary.""" args = {} - if 'cluster' in _dict: - args['cluster'] = DatabaseInformationCluster.from_dict(_dict.get('cluster')) + if (cluster := _dict.get('cluster')) is not None: + args['cluster'] = DatabaseInformationCluster.from_dict(cluster) else: raise ValueError('Required property \'cluster\' not present in DatabaseInformation JSON') - if 'committed_update_seq' in _dict: - args['committed_update_seq'] = _dict.get('committed_update_seq') - if 'compact_running' in _dict: - args['compact_running'] = _dict.get('compact_running') + if (committed_update_seq := _dict.get('committed_update_seq')) is not None: + args['committed_update_seq'] = committed_update_seq + if (compact_running := _dict.get('compact_running')) is not None: + args['compact_running'] = compact_running else: raise ValueError('Required property \'compact_running\' not present in DatabaseInformation JSON') - if 'compacted_seq' in _dict: - args['compacted_seq'] = _dict.get('compacted_seq') - if 'db_name' in _dict: - args['db_name'] = _dict.get('db_name') + if (compacted_seq := _dict.get('compacted_seq')) is not None: + args['compacted_seq'] = compacted_seq + if (db_name := _dict.get('db_name')) is not None: + args['db_name'] = db_name else: raise ValueError('Required property \'db_name\' not present in DatabaseInformation JSON') - if 'disk_format_version' in _dict: - args['disk_format_version'] = _dict.get('disk_format_version') + if (disk_format_version := _dict.get('disk_format_version')) is not None: + args['disk_format_version'] = disk_format_version else: raise ValueError('Required property \'disk_format_version\' not present in DatabaseInformation JSON') - if 'doc_count' in _dict: - args['doc_count'] = _dict.get('doc_count') + if (doc_count := _dict.get('doc_count')) is not None: + args['doc_count'] = doc_count else: raise ValueError('Required property \'doc_count\' not present in DatabaseInformation JSON') - if 'doc_del_count' in _dict: - args['doc_del_count'] = _dict.get('doc_del_count') + if (doc_del_count := _dict.get('doc_del_count')) is not None: + args['doc_del_count'] = doc_del_count else: raise ValueError('Required property \'doc_del_count\' not present in DatabaseInformation JSON') - if 'engine' in _dict: - args['engine'] = _dict.get('engine') - if 'props' in _dict: - args['props'] = DatabaseInformationProps.from_dict(_dict.get('props')) + if (engine := _dict.get('engine')) is not None: + args['engine'] = engine + if (instance_start_time := _dict.get('instance_start_time')) is not None: + args['instance_start_time'] = instance_start_time + else: + raise ValueError('Required property \'instance_start_time\' not present in DatabaseInformation JSON') + if (partitioned_indexes := _dict.get('partitioned_indexes')) is not None: + args['partitioned_indexes'] = PartitionedIndexesInformation.from_dict(partitioned_indexes) + if (props := _dict.get('props')) is not None: + args['props'] = DatabaseInformationProps.from_dict(props) else: raise ValueError('Required property \'props\' not present in DatabaseInformation JSON') - if 'sizes' in _dict: - args['sizes'] = ContentInformationSizes.from_dict(_dict.get('sizes')) + if (sizes := _dict.get('sizes')) is not None: + args['sizes'] = ContentInformationSizes.from_dict(sizes) else: raise ValueError('Required property \'sizes\' not present in DatabaseInformation JSON') - if 'update_seq' in _dict: - args['update_seq'] = _dict.get('update_seq') + if (update_seq := _dict.get('update_seq')) is not None: + args['update_seq'] = update_seq else: raise ValueError('Required property \'update_seq\' not present in DatabaseInformation JSON') - if 'uuid' in _dict: - args['uuid'] = _dict.get('uuid') + if (uuid := _dict.get('uuid')) is not None: + args['uuid'] = uuid return cls(**args) @classmethod @@ -10355,7 +11679,10 @@ def to_dict(self) -> Dict: """Return a json dictionary representing this model.""" _dict = {} if hasattr(self, 'cluster') and self.cluster is not None: - _dict['cluster'] = self.cluster.to_dict() + if isinstance(self.cluster, dict): + _dict['cluster'] = self.cluster + else: + _dict['cluster'] = self.cluster.to_dict() if hasattr(self, 'committed_update_seq') and self.committed_update_seq is not None: _dict['committed_update_seq'] = self.committed_update_seq if hasattr(self, 'compact_running') and self.compact_running is not None: @@ -10372,10 +11699,23 @@ def to_dict(self) -> Dict: _dict['doc_del_count'] = self.doc_del_count if hasattr(self, 'engine') and self.engine is not None: _dict['engine'] = self.engine + if hasattr(self, 'instance_start_time') and self.instance_start_time is not None: + _dict['instance_start_time'] = self.instance_start_time + if hasattr(self, 'partitioned_indexes') and self.partitioned_indexes is not None: + if isinstance(self.partitioned_indexes, dict): + _dict['partitioned_indexes'] = self.partitioned_indexes + else: + _dict['partitioned_indexes'] = self.partitioned_indexes.to_dict() if hasattr(self, 'props') and self.props is not None: - _dict['props'] = self.props.to_dict() + if isinstance(self.props, dict): + _dict['props'] = self.props + else: + _dict['props'] = self.props.to_dict() if hasattr(self, 'sizes') and self.sizes is not None: - _dict['sizes'] = self.sizes.to_dict() + if isinstance(self.sizes, dict): + _dict['sizes'] = self.sizes + else: + _dict['sizes'] = self.sizes.to_dict() if hasattr(self, 'update_seq') and self.update_seq is not None: _dict['update_seq'] = self.update_seq if hasattr(self, 'uuid') and self.uuid is not None: @@ -10400,28 +11740,34 @@ def __ne__(self, other: 'DatabaseInformation') -> bool: """Return `true` when self and other are not equal, false otherwise.""" return not self == other -class DatabaseInformationCluster(): + +class DatabaseInformationCluster: """ Schema for database cluster information. - :attr int n: Schema for the number of replicas of a database in a cluster. - :attr int q: Schema for the number of shards in a database. Each shard is a + :param int n: Schema for the number of replicas of a database in a cluster. The + cluster is using the default value and it cannot be changed by the user. + :param int q: Schema for the number of shards in a database. Each shard is a partition of the hash value range. - :attr int r: Read quorum. The number of consistent copies of a document that + :param int r: Read quorum. The number of consistent copies of a document that need to be read before a successful reply. - :attr int w: Write quorum. The number of copies of a document that need to be + :param int w: Write quorum. The number of copies of a document that need to be written before a successful reply. """ - def __init__(self, - n: int, - q: int, - r: int, - w: int) -> None: + def __init__( + self, + n: int, + q: int, + r: int, + w: int, + ) -> None: """ Initialize a DatabaseInformationCluster object. :param int n: Schema for the number of replicas of a database in a cluster. + The cluster is using the default value and it cannot be changed by the + user. :param int q: Schema for the number of shards in a database. Each shard is a partition of the hash value range. :param int r: Read quorum. The number of consistent copies of a document @@ -10438,20 +11784,20 @@ def __init__(self, def from_dict(cls, _dict: Dict) -> 'DatabaseInformationCluster': """Initialize a DatabaseInformationCluster object from a json dictionary.""" args = {} - if 'n' in _dict: - args['n'] = _dict.get('n') + if (n := _dict.get('n')) is not None: + args['n'] = n else: raise ValueError('Required property \'n\' not present in DatabaseInformationCluster JSON') - if 'q' in _dict: - args['q'] = _dict.get('q') + if (q := _dict.get('q')) is not None: + args['q'] = q else: raise ValueError('Required property \'q\' not present in DatabaseInformationCluster JSON') - if 'r' in _dict: - args['r'] = _dict.get('r') + if (r := _dict.get('r')) is not None: + args['r'] = r else: raise ValueError('Required property \'r\' not present in DatabaseInformationCluster JSON') - if 'w' in _dict: - args['w'] = _dict.get('w') + if (w := _dict.get('w')) is not None: + args['w'] = w else: raise ValueError('Required property \'w\' not present in DatabaseInformationCluster JSON') return cls(**args) @@ -10492,17 +11838,20 @@ def __ne__(self, other: 'DatabaseInformationCluster') -> bool: """Return `true` when self and other are not equal, false otherwise.""" return not self == other -class DatabaseInformationProps(): + +class DatabaseInformationProps: """ Schema for database properties. - :attr bool partitioned: (optional) The value is `true` for a partitioned + :param bool partitioned: (optional) The value is `true` for a partitioned database. """ - def __init__(self, - *, - partitioned: bool = None) -> None: + def __init__( + self, + *, + partitioned: Optional[bool] = None, + ) -> None: """ Initialize a DatabaseInformationProps object. @@ -10515,8 +11864,8 @@ def __init__(self, def from_dict(cls, _dict: Dict) -> 'DatabaseInformationProps': """Initialize a DatabaseInformationProps object from a json dictionary.""" args = {} - if 'partitioned' in _dict: - args['partitioned'] = _dict.get('partitioned') + if (partitioned := _dict.get('partitioned')) is not None: + args['partitioned'] = partitioned return cls(**args) @classmethod @@ -10549,23 +11898,84 @@ def __ne__(self, other: 'DatabaseInformationProps') -> bool: """Return `true` when self and other are not equal, false otherwise.""" return not self == other -class DbEvent(): + +class DatabasesCountInformation: + """ + Schema for databases count. + + :param int total: (optional) The total number of databases. + """ + + def __init__( + self, + *, + total: Optional[int] = None, + ) -> None: + """ + Initialize a DatabasesCountInformation object. + + :param int total: (optional) The total number of databases. + """ + self.total = total + + @classmethod + def from_dict(cls, _dict: Dict) -> 'DatabasesCountInformation': + """Initialize a DatabasesCountInformation object from a json dictionary.""" + args = {} + if (total := _dict.get('total')) is not None: + args['total'] = total + return cls(**args) + + @classmethod + def _from_dict(cls, _dict): + """Initialize a DatabasesCountInformation object from a json dictionary.""" + return cls.from_dict(_dict) + + def to_dict(self) -> Dict: + """Return a json dictionary representing this model.""" + _dict = {} + if hasattr(self, 'total') and self.total is not None: + _dict['total'] = self.total + return _dict + + def _to_dict(self): + """Return a json dictionary representing this model.""" + return self.to_dict() + + def __str__(self) -> str: + """Return a `str` version of this DatabasesCountInformation object.""" + return json.dumps(self.to_dict(), indent=2) + + def __eq__(self, other: 'DatabasesCountInformation') -> bool: + """Return `true` when self and other are equal, false otherwise.""" + if not isinstance(other, self.__class__): + return False + return self.__dict__ == other.__dict__ + + def __ne__(self, other: 'DatabasesCountInformation') -> bool: + """Return `true` when self and other are not equal, false otherwise.""" + return not self == other + + +class DbEvent: """ Schema for a database change event. - :attr str db_name: Database name. - :attr str seq: Sequence number. - :attr str type: A database event. + :param str db_name: Schema for a database name. + :param str seq: Sequence number. + :param str type: A database event. """ - def __init__(self, - db_name: str, - seq: str, - type: str) -> None: + def __init__( + self, + db_name: str, + seq: str, + type: str, + ) -> None: """ Initialize a DbEvent object. - :param str db_name: Database name. + :param str db_name: Schema for a database name. :param str seq: Sequence number. :param str type: A database event. """ @@ -10577,16 +11987,16 @@ def __init__(self, def from_dict(cls, _dict: Dict) -> 'DbEvent': """Initialize a DbEvent object from a json dictionary.""" args = {} - if 'db_name' in _dict: - args['db_name'] = _dict.get('db_name') + if (db_name := _dict.get('db_name')) is not None: + args['db_name'] = db_name else: raise ValueError('Required property \'db_name\' not present in DbEvent JSON') - if 'seq' in _dict: - args['seq'] = _dict.get('seq') + if (seq := _dict.get('seq')) is not None: + args['seq'] = seq else: raise ValueError('Required property \'seq\' not present in DbEvent JSON') - if 'type' in _dict: - args['type'] = _dict.get('type') + if (type := _dict.get('type')) is not None: + args['type'] = type else: raise ValueError('Required property \'type\' not present in DbEvent JSON') return cls(**args) @@ -10629,22 +12039,26 @@ class TypeEnum(str, Enum): """ A database event. """ + CREATED = 'created' DELETED = 'deleted' UPDATED = 'updated' -class DbUpdates(): + +class DbUpdates: """ Schema for database updates. - :attr str last_seq: Last sequence number. - :attr List[DbEvent] results: results. + :param str last_seq: Last sequence number. + :param List[DbEvent] results: results. """ - def __init__(self, - last_seq: str, - results: List['DbEvent']) -> None: + def __init__( + self, + last_seq: str, + results: List['DbEvent'], + ) -> None: """ Initialize a DbUpdates object. @@ -10658,12 +12072,12 @@ def __init__(self, def from_dict(cls, _dict: Dict) -> 'DbUpdates': """Initialize a DbUpdates object from a json dictionary.""" args = {} - if 'last_seq' in _dict: - args['last_seq'] = _dict.get('last_seq') + if (last_seq := _dict.get('last_seq')) is not None: + args['last_seq'] = last_seq else: raise ValueError('Required property \'last_seq\' not present in DbUpdates JSON') - if 'results' in _dict: - args['results'] = [DbEvent.from_dict(x) for x in _dict.get('results')] + if (results := _dict.get('results')) is not None: + args['results'] = [DbEvent.from_dict(v) for v in results] else: raise ValueError('Required property \'results\' not present in DbUpdates JSON') return cls(**args) @@ -10679,7 +12093,13 @@ def to_dict(self) -> Dict: if hasattr(self, 'last_seq') and self.last_seq is not None: _dict['last_seq'] = self.last_seq if hasattr(self, 'results') and self.results is not None: - _dict['results'] = [x.to_dict() for x in self.results] + results_list = [] + for v in self.results: + if isinstance(v, dict): + results_list.append(v) + else: + results_list.append(v.to_dict()) + _dict['results'] = results_list return _dict def _to_dict(self): @@ -10700,25 +12120,28 @@ def __ne__(self, other: 'DbUpdates') -> bool: """Return `true` when self and other are not equal, false otherwise.""" return not self == other -class DbsInfoResult(): + +class DbsInfoResult: """ Schema for database information keyed by database name. - :attr str error: (optional) The name of the error. - :attr DatabaseInformation info: (optional) Schema for information about a + :param str error: (optional) The name of the error. + :param DatabaseInformation info: (optional) Schema for information about a database. - :attr str key: Database name. + :param str key: Schema for a database name. """ - def __init__(self, - key: str, - *, - error: str = None, - info: 'DatabaseInformation' = None) -> None: + def __init__( + self, + key: str, + *, + error: Optional[str] = None, + info: Optional['DatabaseInformation'] = None, + ) -> None: """ Initialize a DbsInfoResult object. - :param str key: Database name. + :param str key: Schema for a database name. :param str error: (optional) The name of the error. :param DatabaseInformation info: (optional) Schema for information about a database. @@ -10731,12 +12154,12 @@ def __init__(self, def from_dict(cls, _dict: Dict) -> 'DbsInfoResult': """Initialize a DbsInfoResult object from a json dictionary.""" args = {} - if 'error' in _dict: - args['error'] = _dict.get('error') - if 'info' in _dict: - args['info'] = DatabaseInformation.from_dict(_dict.get('info')) - if 'key' in _dict: - args['key'] = _dict.get('key') + if (error := _dict.get('error')) is not None: + args['error'] = error + if (info := _dict.get('info')) is not None: + args['info'] = DatabaseInformation.from_dict(info) + if (key := _dict.get('key')) is not None: + args['key'] = key else: raise ValueError('Required property \'key\' not present in DbsInfoResult JSON') return cls(**args) @@ -10752,7 +12175,10 @@ def to_dict(self) -> Dict: if hasattr(self, 'error') and self.error is not None: _dict['error'] = self.error if hasattr(self, 'info') and self.info is not None: - _dict['info'] = self.info.to_dict() + if isinstance(self.info, dict): + _dict['info'] = self.info + else: + _dict['info'] = self.info.to_dict() if hasattr(self, 'key') and self.key is not None: _dict['key'] = self.key return _dict @@ -10775,27 +12201,30 @@ def __ne__(self, other: 'DbsInfoResult') -> bool: """Return `true` when self and other are not equal, false otherwise.""" return not self == other -class DesignDocument(): + +class DesignDocument: """ Schema for a design document. - :attr dict attachments: (optional) Schema for a map of attachment name to + :param dict _attachments: (optional) Schema for a map of attachment name to attachment metadata. - :attr List[str] conflicts: (optional) Schema for a list of document revision + :param List[str] _conflicts: (optional) Schema for a list of document revision identifiers. - :attr bool deleted: (optional) Deletion flag. Available if document was removed. - :attr List[str] deleted_conflicts: (optional) Schema for a list of document + :param bool _deleted: (optional) Deletion flag. Available if document was + removed. + :param List[str] _deleted_conflicts: (optional) Schema for a list of document revision identifiers. - :attr str id: (optional) Document ID. - :attr str local_seq: (optional) Document's update sequence in current database. - Available if requested with local_seq=true query parameter. - :attr str rev: (optional) Schema for a document revision identifier. - :attr Revisions revisions: (optional) Schema for list of revision information. - :attr List[DocumentRevisionStatus] revs_info: (optional) Schema for a list of + :param str _id: (optional) Schema for a design document ID including a + `_design/` prefix. + :param str _local_seq: (optional) Document's update sequence in current + database. Available if requested with local_seq=true query parameter. + :param str _rev: (optional) Schema for a document revision identifier. + :param Revisions _revisions: (optional) Schema for list of revision information. + :param List[DocumentRevisionStatus] _revs_info: (optional) Schema for a list of objects with information about local revisions and their status. - :attr bool autoupdate: (optional) Indicates whether to automatically build + :param bool autoupdate: (optional) Indicates whether to automatically build indexes defined in this design document. - :attr dict filters: (optional) Schema for filter functions definition. This + :param dict filters: (optional) Schema for filter functions definition. This schema is a map where keys are the names of the filter functions and values are the function definition in string format. Filter function formats, or filters the changes feed that pass filter rules. The @@ -10834,12 +12263,12 @@ class DesignDocument(): * `uuid` - string, generated UUID by a specified algorithm in the config file. Filter functions must return true if a document passed all the rules. - :attr dict indexes: (optional) Search (text) index function definitions. - :attr str language: (optional) Defines Query Server key to process design + :param dict indexes: (optional) Search (text) index function definitions. + :param str language: (optional) Defines Query Server key to process design document functions. - :attr DesignDocumentOptions options: (optional) Schema for design document + :param DesignDocumentOptions options: (optional) Schema for design document options. - :attr str validate_doc_update: (optional) Validate document update function can + :param str validate_doc_update: (optional) Validate document update function can be used to prevent invalid or unauthorized document update requests from being stored. Validation functions typically examine the structure of the new document to ensure that required fields are present and to verify that the requesting @@ -10850,65 +12279,69 @@ class DesignDocument(): The validation function can abort the pending document write by throwing one of two error objects: ``` - // user is not authorized to make the change but may re-authenticate throw({ - unauthorized: 'Error message here.' }); + // user is not authorized to make the change but may + // re-authenticate throw({ unauthorized: 'Error message here.' }); // change is not allowed throw({ forbidden: 'Error message here.' }); ``` The function takes 4 parameters: * `newDoc` - New version of document that will be stored from the update request. - * `oldDoc` - Previous version of document that is already stored. - * `userCtx` - User Context Object, containing information about the - user writing the document (if present), see the `UserContext`. - * `secObj` - Security Object, with lists of database security roles, - see the `SecurityObject`. - :attr dict views: (optional) Schema for design document views. - :attr dict st_indexes: (optional) Schema for geospatial index function - definitions. + * `oldDoc` - Previous version of document that is already + stored. + * `userCtx` - User Context Object, containing information + about the user writing the document (if present), see the + `UserContext`. + * `secObj` - Security Object, with lists of database security + roles, see the `SecurityObject`. + :param dict views: (optional) Schema for design document views. + + This type supports additional properties of type object. """ # The set of defined properties for the class - _properties = frozenset(['attachments', '_attachments', 'conflicts', '_conflicts', 'deleted', '_deleted', 'deleted_conflicts', '_deleted_conflicts', 'id', '_id', 'local_seq', '_local_seq', 'rev', '_rev', 'revisions', '_revisions', 'revs_info', '_revs_info', 'autoupdate', 'filters', 'indexes', 'language', 'options', 'validate_doc_update', 'views', 'st_indexes']) - - def __init__(self, - *, - attachments: dict = None, - conflicts: List[str] = None, - deleted: bool = None, - deleted_conflicts: List[str] = None, - id: str = None, - local_seq: str = None, - rev: str = None, - revisions: 'Revisions' = None, - revs_info: List['DocumentRevisionStatus'] = None, - autoupdate: bool = None, - filters: dict = None, - indexes: dict = None, - language: str = None, - options: 'DesignDocumentOptions' = None, - validate_doc_update: str = None, - views: dict = None, - st_indexes: dict = None, - **kwargs) -> None: + _properties = frozenset(['_attachments', '_conflicts', '_deleted', '_deleted_conflicts', '_id', '_local_seq', '_rev', '_revisions', '_revs_info', 'autoupdate', 'filters', 'indexes', 'language', 'options', 'validate_doc_update', 'views']) + + def __init__( + self, + *, + _attachments: Optional[dict] = None, + _conflicts: Optional[List[str]] = None, + _deleted: Optional[bool] = None, + _deleted_conflicts: Optional[List[str]] = None, + _id: Optional[str] = None, + _local_seq: Optional[str] = None, + _rev: Optional[str] = None, + _revisions: Optional['Revisions'] = None, + _revs_info: Optional[List['DocumentRevisionStatus']] = None, + autoupdate: Optional[bool] = None, + filters: Optional[dict] = None, + indexes: Optional[dict] = None, + language: Optional[str] = None, + options: Optional['DesignDocumentOptions'] = None, + validate_doc_update: Optional[str] = None, + views: Optional[dict] = None, + **kwargs: Optional[object], + ) -> None: """ Initialize a DesignDocument object. - :param dict attachments: (optional) Schema for a map of attachment name to + :param dict _attachments: (optional) Schema for a map of attachment name to attachment metadata. - :param List[str] conflicts: (optional) Schema for a list of document + :param List[str] _conflicts: (optional) Schema for a list of document revision identifiers. - :param bool deleted: (optional) Deletion flag. Available if document was + :param bool _deleted: (optional) Deletion flag. Available if document was removed. - :param List[str] deleted_conflicts: (optional) Schema for a list of + :param List[str] _deleted_conflicts: (optional) Schema for a list of document revision identifiers. - :param str id: (optional) Document ID. - :param str local_seq: (optional) Document's update sequence in current + :param str _id: (optional) Schema for a design document ID including a + `_design/` prefix. + :param str _local_seq: (optional) Document's update sequence in current database. Available if requested with local_seq=true query parameter. - :param str rev: (optional) Schema for a document revision identifier. - :param Revisions revisions: (optional) Schema for list of revision + :param str _rev: (optional) Schema for a document revision identifier. + :param Revisions _revisions: (optional) Schema for list of revision information. - :param List[DocumentRevisionStatus] revs_info: (optional) Schema for a list - of objects with information about local revisions and their status. + :param List[DocumentRevisionStatus] _revs_info: (optional) Schema for a + list of objects with information about local revisions and their status. :param bool autoupdate: (optional) Indicates whether to automatically build indexes defined in this design document. :param dict filters: (optional) Schema for filter functions definition. @@ -10967,32 +12400,32 @@ def __init__(self, The validation function can abort the pending document write by throwing one of two error objects: ``` - // user is not authorized to make the change but may re-authenticate - throw({ unauthorized: 'Error message here.' }); + // user is not authorized to make the change but may + // re-authenticate throw({ unauthorized: 'Error message here.' }); // change is not allowed throw({ forbidden: 'Error message here.' }); ``` The function takes 4 parameters: * `newDoc` - New version of document that will be stored from the update request. - * `oldDoc` - Previous version of document that is already stored. - * `userCtx` - User Context Object, containing information about the - user writing the document (if present), see the `UserContext`. - * `secObj` - Security Object, with lists of database security roles, - see the `SecurityObject`. + * `oldDoc` - Previous version of document that is already + stored. + * `userCtx` - User Context Object, containing information + about the user writing the document (if present), see the + `UserContext`. + * `secObj` - Security Object, with lists of database security + roles, see the `SecurityObject`. :param dict views: (optional) Schema for design document views. - :param dict st_indexes: (optional) Schema for geospatial index function - definitions. - :param **kwargs: (optional) Any additional properties. - """ - self.attachments = attachments - self.conflicts = conflicts - self.deleted = deleted - self.deleted_conflicts = deleted_conflicts - self.id = id - self.local_seq = local_seq - self.rev = rev - self.revisions = revisions - self.revs_info = revs_info + :param object **kwargs: (optional) Additional properties of type object + """ + self._attachments = _attachments + self._conflicts = _conflicts + self._deleted = _deleted + self._deleted_conflicts = _deleted_conflicts + self._id = _id + self._local_seq = _local_seq + self._rev = _rev + self._revisions = _revisions + self._revs_info = _revs_info self.autoupdate = autoupdate self.filters = filters self.indexes = indexes @@ -11000,49 +12433,55 @@ def __init__(self, self.options = options self.validate_doc_update = validate_doc_update self.views = views - self.st_indexes = st_indexes - for _key, _value in kwargs.items(): - setattr(self, _key, _value) + for k, v in kwargs.items(): + if k not in DesignDocument._properties: + if not isinstance(v, object): + raise ValueError('Value for additional property {} must be of type object'.format(k)) + setattr(self, k, v) + else: + raise ValueError('Property {} cannot be specified as an additional property'.format(k)) @classmethod def from_dict(cls, _dict: Dict) -> 'DesignDocument': """Initialize a DesignDocument object from a json dictionary.""" args = {} - if '_attachments' in _dict: - args['attachments'] = {k : Attachment.from_dict(v) for k, v in _dict.get('_attachments').items()} - if '_conflicts' in _dict: - args['conflicts'] = _dict.get('_conflicts') - if '_deleted' in _dict: - args['deleted'] = _dict.get('_deleted') - if '_deleted_conflicts' in _dict: - args['deleted_conflicts'] = _dict.get('_deleted_conflicts') - if '_id' in _dict: - args['id'] = _dict.get('_id') - if '_local_seq' in _dict: - args['local_seq'] = _dict.get('_local_seq') - if '_rev' in _dict: - args['rev'] = _dict.get('_rev') - if '_revisions' in _dict: - args['revisions'] = Revisions.from_dict(_dict.get('_revisions')) - if '_revs_info' in _dict: - args['revs_info'] = [DocumentRevisionStatus.from_dict(x) for x in _dict.get('_revs_info')] - if 'autoupdate' in _dict: - args['autoupdate'] = _dict.get('autoupdate') - if 'filters' in _dict: - args['filters'] = _dict.get('filters') - if 'indexes' in _dict: - args['indexes'] = {k : SearchIndexDefinition.from_dict(v) for k, v in _dict.get('indexes').items()} - if 'language' in _dict: - args['language'] = _dict.get('language') - if 'options' in _dict: - args['options'] = DesignDocumentOptions.from_dict(_dict.get('options')) - if 'validate_doc_update' in _dict: - args['validate_doc_update'] = _dict.get('validate_doc_update') - if 'views' in _dict: - args['views'] = {k : DesignDocumentViewsMapReduce.from_dict(v) for k, v in _dict.get('views').items()} - if 'st_indexes' in _dict: - args['st_indexes'] = {k : GeoIndexDefinition.from_dict(v) for k, v in _dict.get('st_indexes').items()} - args.update({k:v for (k, v) in _dict.items() if k not in cls._properties}) + if (attachments := _dict.get('_attachments')) is not None: + args['_attachments'] = {k: Attachment.from_dict(v) for k, v in attachments.items()} + if (conflicts := _dict.get('_conflicts')) is not None: + args['_conflicts'] = conflicts + if (deleted := _dict.get('_deleted')) is not None: + args['_deleted'] = deleted + if (deleted_conflicts := _dict.get('_deleted_conflicts')) is not None: + args['_deleted_conflicts'] = deleted_conflicts + if (id := _dict.get('_id')) is not None: + args['_id'] = id + if (local_seq := _dict.get('_local_seq')) is not None: + args['_local_seq'] = local_seq + if (rev := _dict.get('_rev')) is not None: + args['_rev'] = rev + if (revisions := _dict.get('_revisions')) is not None: + args['_revisions'] = Revisions.from_dict(revisions) + if (revs_info := _dict.get('_revs_info')) is not None: + args['_revs_info'] = [DocumentRevisionStatus.from_dict(v) for v in revs_info] + if (autoupdate := _dict.get('autoupdate')) is not None: + args['autoupdate'] = autoupdate + if (filters := _dict.get('filters')) is not None: + args['filters'] = filters + if (indexes := _dict.get('indexes')) is not None: + args['indexes'] = {k: SearchIndexDefinition.from_dict(v) for k, v in indexes.items()} + if (language := _dict.get('language')) is not None: + args['language'] = language + if (options := _dict.get('options')) is not None: + args['options'] = DesignDocumentOptions.from_dict(options) + if (validate_doc_update := _dict.get('validate_doc_update')) is not None: + args['validate_doc_update'] = validate_doc_update + if (views := _dict.get('views')) is not None: + args['views'] = {k: DesignDocumentViewsMapReduce.from_dict(v) for k, v in views.items()} + for k, v in _dict.items(): + if k not in cls._properties: + if not isinstance(v, object): + raise ValueError('Value for additional property {} must be of type object'.format(k)) + args[k] = v return cls(**args) @classmethod @@ -11053,43 +12492,70 @@ def _from_dict(cls, _dict): def to_dict(self) -> Dict: """Return a json dictionary representing this model.""" _dict = {} - if hasattr(self, 'attachments') and self.attachments is not None: - _dict['_attachments'] = {k : v.to_dict() for k, v in self.attachments.items()} - if hasattr(self, 'conflicts') and self.conflicts is not None: - _dict['_conflicts'] = self.conflicts - if hasattr(self, 'deleted') and self.deleted is not None: - _dict['_deleted'] = self.deleted - if hasattr(self, 'deleted_conflicts') and self.deleted_conflicts is not None: - _dict['_deleted_conflicts'] = self.deleted_conflicts - if hasattr(self, 'id') and self.id is not None: - _dict['_id'] = self.id - if hasattr(self, 'local_seq') and self.local_seq is not None: - _dict['_local_seq'] = self.local_seq - if hasattr(self, 'rev') and self.rev is not None: - _dict['_rev'] = self.rev - if hasattr(self, 'revisions') and self.revisions is not None: - _dict['_revisions'] = self.revisions.to_dict() - if hasattr(self, 'revs_info') and self.revs_info is not None: - _dict['_revs_info'] = [x.to_dict() for x in self.revs_info] + if hasattr(self, '_attachments') and self._attachments is not None: + _attachments_map = {} + for k, v in self._attachments.items(): + if isinstance(v, dict): + _attachments_map[k] = v + else: + _attachments_map[k] = v.to_dict() + _dict['_attachments'] = _attachments_map + if hasattr(self, '_conflicts') and self._conflicts is not None: + _dict['_conflicts'] = self._conflicts + if hasattr(self, '_deleted') and self._deleted is not None: + _dict['_deleted'] = self._deleted + if hasattr(self, '_deleted_conflicts') and self._deleted_conflicts is not None: + _dict['_deleted_conflicts'] = self._deleted_conflicts + if hasattr(self, '_id') and self._id is not None: + _dict['_id'] = self._id + if hasattr(self, '_local_seq') and self._local_seq is not None: + _dict['_local_seq'] = self._local_seq + if hasattr(self, '_rev') and self._rev is not None: + _dict['_rev'] = self._rev + if hasattr(self, '_revisions') and self._revisions is not None: + if isinstance(self._revisions, dict): + _dict['_revisions'] = self._revisions + else: + _dict['_revisions'] = self._revisions.to_dict() + if hasattr(self, '_revs_info') and self._revs_info is not None: + _revs_info_list = [] + for v in self._revs_info: + if isinstance(v, dict): + _revs_info_list.append(v) + else: + _revs_info_list.append(v.to_dict()) + _dict['_revs_info'] = _revs_info_list if hasattr(self, 'autoupdate') and self.autoupdate is not None: _dict['autoupdate'] = self.autoupdate if hasattr(self, 'filters') and self.filters is not None: _dict['filters'] = self.filters if hasattr(self, 'indexes') and self.indexes is not None: - _dict['indexes'] = {k : v.to_dict() for k, v in self.indexes.items()} + indexes_map = {} + for k, v in self.indexes.items(): + if isinstance(v, dict): + indexes_map[k] = v + else: + indexes_map[k] = v.to_dict() + _dict['indexes'] = indexes_map if hasattr(self, 'language') and self.language is not None: _dict['language'] = self.language if hasattr(self, 'options') and self.options is not None: - _dict['options'] = self.options.to_dict() + if isinstance(self.options, dict): + _dict['options'] = self.options + else: + _dict['options'] = self.options.to_dict() if hasattr(self, 'validate_doc_update') and self.validate_doc_update is not None: _dict['validate_doc_update'] = self.validate_doc_update if hasattr(self, 'views') and self.views is not None: - _dict['views'] = {k : v.to_dict() for k, v in self.views.items()} - if hasattr(self, 'st_indexes') and self.st_indexes is not None: - _dict['st_indexes'] = {k : v.to_dict() for k, v in self.st_indexes.items()} - for _key in [k for k in vars(self).keys() if k not in DesignDocument._properties]: - if getattr(self, _key, None) is not None: - _dict[_key] = getattr(self, _key) + views_map = {} + for k, v in self.views.items(): + if isinstance(v, dict): + views_map[k] = v + else: + views_map[k] = v.to_dict() + _dict['views'] = views_map + for k in [_k for _k in vars(self).keys() if _k not in DesignDocument._properties]: + _dict[k] = getattr(self, k) return _dict def _to_dict(self): @@ -11097,21 +12563,23 @@ def _to_dict(self): return self.to_dict() def get_properties(self) -> Dict: - """Return a dictionary of arbitrary properties from this instance of DesignDocument""" + """Return the additional properties from this instance of DesignDocument in the form of a dict.""" _dict = {} - - for _key in [k for k in vars(self).keys() if k not in DesignDocument._properties]: - _dict[_key] = getattr(self, _key) + for k in [_k for _k in vars(self).keys() if _k not in DesignDocument._properties]: + _dict[k] = getattr(self, k) return _dict def set_properties(self, _dict: dict): - """Set a dictionary of arbitrary properties to this instance of DesignDocument""" - for _key in [k for k in vars(self).keys() if k not in DesignDocument._properties]: - delattr(self, _key) - - for _key, _value in _dict.items(): - if _key not in DesignDocument._properties: - setattr(self, _key, _value) + """Set a dictionary of additional properties in this instance of DesignDocument""" + for k in [_k for _k in vars(self).keys() if _k not in DesignDocument._properties]: + delattr(self, k) + for k, v in _dict.items(): + if k not in DesignDocument._properties: + if not isinstance(v, object): + raise ValueError('Value for additional property {} must be of type object'.format(k)) + setattr(self, k, v) + else: + raise ValueError('Property {} cannot be specified as an additional property'.format(k)) def __str__(self) -> str: """Return a `str` version of this DesignDocument object.""" @@ -11127,17 +12595,20 @@ def __ne__(self, other: 'DesignDocument') -> bool: """Return `true` when self and other are not equal, false otherwise.""" return not self == other -class DesignDocumentInformation(): + +class DesignDocumentInformation: """ Schema for information about a design document. - :attr str name: name. - :attr DesignDocumentViewIndex view_index: View index information. + :param str name: name. + :param DesignDocumentViewIndex view_index: View index information. """ - def __init__(self, - name: str, - view_index: 'DesignDocumentViewIndex') -> None: + def __init__( + self, + name: str, + view_index: 'DesignDocumentViewIndex', + ) -> None: """ Initialize a DesignDocumentInformation object. @@ -11151,12 +12622,12 @@ def __init__(self, def from_dict(cls, _dict: Dict) -> 'DesignDocumentInformation': """Initialize a DesignDocumentInformation object from a json dictionary.""" args = {} - if 'name' in _dict: - args['name'] = _dict.get('name') + if (name := _dict.get('name')) is not None: + args['name'] = name else: raise ValueError('Required property \'name\' not present in DesignDocumentInformation JSON') - if 'view_index' in _dict: - args['view_index'] = DesignDocumentViewIndex.from_dict(_dict.get('view_index')) + if (view_index := _dict.get('view_index')) is not None: + args['view_index'] = DesignDocumentViewIndex.from_dict(view_index) else: raise ValueError('Required property \'view_index\' not present in DesignDocumentInformation JSON') return cls(**args) @@ -11172,7 +12643,10 @@ def to_dict(self) -> Dict: if hasattr(self, 'name') and self.name is not None: _dict['name'] = self.name if hasattr(self, 'view_index') and self.view_index is not None: - _dict['view_index'] = self.view_index.to_dict() + if isinstance(self.view_index, dict): + _dict['view_index'] = self.view_index + else: + _dict['view_index'] = self.view_index.to_dict() return _dict def _to_dict(self): @@ -11193,22 +12667,41 @@ def __ne__(self, other: 'DesignDocumentInformation') -> bool: """Return `true` when self and other are not equal, false otherwise.""" return not self == other -class DesignDocumentOptions(): + +class DesignDocumentOptions: """ Schema for design document options. - :attr bool partitioned: (optional) Whether this design document describes - partitioned or global indexes. + :param bool partitioned: (optional) Whether this design document describes + partitioned or global indexes. Set this option to `false` for a design document + that describes global indexes in a partitioned database. A design document + describes either global or partitioned indexes, but not both. By default, for a + partitioned database this option is `true` and the design document describes + partitioned indexes for queries on a single partition at a time. When set to + `false` this option allows creating global indexes in this design document for + queries spanning many partitions. For non-partitioned databases, the default is + `false` and design documents default to global. Only partitioned databases can + have partitioned indexes. """ - def __init__(self, - *, - partitioned: bool = None) -> None: + def __init__( + self, + *, + partitioned: Optional[bool] = None, + ) -> None: """ Initialize a DesignDocumentOptions object. :param bool partitioned: (optional) Whether this design document describes - partitioned or global indexes. + partitioned or global indexes. Set this option to `false` for a design + document that describes global indexes in a partitioned database. A design + document describes either global or partitioned indexes, but not both. By + default, for a partitioned database this option is `true` and the design + document describes partitioned indexes for queries on a single partition at + a time. When set to `false` this option allows creating global indexes in + this design document for queries spanning many partitions. For + non-partitioned databases, the default is `false` and design documents + default to global. Only partitioned databases can have partitioned indexes. """ self.partitioned = partitioned @@ -11216,8 +12709,8 @@ def __init__(self, def from_dict(cls, _dict: Dict) -> 'DesignDocumentOptions': """Initialize a DesignDocumentOptions object from a json dictionary.""" args = {} - if 'partitioned' in _dict: - args['partitioned'] = _dict.get('partitioned') + if (partitioned := _dict.get('partitioned')) is not None: + args['partitioned'] = partitioned return cls(**args) @classmethod @@ -11250,34 +12743,40 @@ def __ne__(self, other: 'DesignDocumentOptions') -> bool: """Return `true` when self and other are not equal, false otherwise.""" return not self == other -class DesignDocumentViewIndex(): + +class DesignDocumentViewIndex: """ View index information. - :attr List[str] collator_versions: List of collator versions. If there are + :param List[str] collator_versions: List of collator versions. If there are multiple entries this implies a libicu upgrade has occurred but compaction has not run yet. - :attr bool compact_running: Indicates whether a compaction routine is currently + :param bool compact_running: Indicates whether a compaction routine is currently running on the view. - :attr str language: Language for the defined views. - :attr str signature: MD5 signature of the views for the design document. - :attr ContentInformationSizes sizes: Schema for size information of content. - :attr bool updater_running: Indicates if the view is currently being updated. - :attr int waiting_clients: Number of clients waiting on views from this design + :param str language: Language for the defined views. + :param str signature: MD5 signature of the views for the design document. + :param ContentInformationSizes sizes: Schema for size information of content. + :param bool updater_running: Indicates if the view is currently being updated. + :param UpdatesPending updates_pending: Schema for an ability to tell if view is + up-to-date without querying it. + :param int waiting_clients: Number of clients waiting on views from this design document. - :attr bool waiting_commit: Indicates if there are outstanding commits to the + :param bool waiting_commit: Indicates if there are outstanding commits to the underlying database that need to processed. """ - def __init__(self, - collator_versions: List[str], - compact_running: bool, - language: str, - signature: str, - sizes: 'ContentInformationSizes', - updater_running: bool, - waiting_clients: int, - waiting_commit: bool) -> None: + def __init__( + self, + collator_versions: List[str], + compact_running: bool, + language: str, + signature: str, + sizes: 'ContentInformationSizes', + updater_running: bool, + updates_pending: 'UpdatesPending', + waiting_clients: int, + waiting_commit: bool, + ) -> None: """ Initialize a DesignDocumentViewIndex object. @@ -11292,6 +12791,8 @@ def __init__(self, content. :param bool updater_running: Indicates if the view is currently being updated. + :param UpdatesPending updates_pending: Schema for an ability to tell if + view is up-to-date without querying it. :param int waiting_clients: Number of clients waiting on views from this design document. :param bool waiting_commit: Indicates if there are outstanding commits to @@ -11303,6 +12804,7 @@ def __init__(self, self.signature = signature self.sizes = sizes self.updater_running = updater_running + self.updates_pending = updates_pending self.waiting_clients = waiting_clients self.waiting_commit = waiting_commit @@ -11310,36 +12812,40 @@ def __init__(self, def from_dict(cls, _dict: Dict) -> 'DesignDocumentViewIndex': """Initialize a DesignDocumentViewIndex object from a json dictionary.""" args = {} - if 'collator_versions' in _dict: - args['collator_versions'] = _dict.get('collator_versions') + if (collator_versions := _dict.get('collator_versions')) is not None: + args['collator_versions'] = collator_versions else: raise ValueError('Required property \'collator_versions\' not present in DesignDocumentViewIndex JSON') - if 'compact_running' in _dict: - args['compact_running'] = _dict.get('compact_running') + if (compact_running := _dict.get('compact_running')) is not None: + args['compact_running'] = compact_running else: raise ValueError('Required property \'compact_running\' not present in DesignDocumentViewIndex JSON') - if 'language' in _dict: - args['language'] = _dict.get('language') + if (language := _dict.get('language')) is not None: + args['language'] = language else: raise ValueError('Required property \'language\' not present in DesignDocumentViewIndex JSON') - if 'signature' in _dict: - args['signature'] = _dict.get('signature') + if (signature := _dict.get('signature')) is not None: + args['signature'] = signature else: raise ValueError('Required property \'signature\' not present in DesignDocumentViewIndex JSON') - if 'sizes' in _dict: - args['sizes'] = ContentInformationSizes.from_dict(_dict.get('sizes')) + if (sizes := _dict.get('sizes')) is not None: + args['sizes'] = ContentInformationSizes.from_dict(sizes) else: raise ValueError('Required property \'sizes\' not present in DesignDocumentViewIndex JSON') - if 'updater_running' in _dict: - args['updater_running'] = _dict.get('updater_running') + if (updater_running := _dict.get('updater_running')) is not None: + args['updater_running'] = updater_running else: raise ValueError('Required property \'updater_running\' not present in DesignDocumentViewIndex JSON') - if 'waiting_clients' in _dict: - args['waiting_clients'] = _dict.get('waiting_clients') + if (updates_pending := _dict.get('updates_pending')) is not None: + args['updates_pending'] = UpdatesPending.from_dict(updates_pending) + else: + raise ValueError('Required property \'updates_pending\' not present in DesignDocumentViewIndex JSON') + if (waiting_clients := _dict.get('waiting_clients')) is not None: + args['waiting_clients'] = waiting_clients else: raise ValueError('Required property \'waiting_clients\' not present in DesignDocumentViewIndex JSON') - if 'waiting_commit' in _dict: - args['waiting_commit'] = _dict.get('waiting_commit') + if (waiting_commit := _dict.get('waiting_commit')) is not None: + args['waiting_commit'] = waiting_commit else: raise ValueError('Required property \'waiting_commit\' not present in DesignDocumentViewIndex JSON') return cls(**args) @@ -11361,9 +12867,17 @@ def to_dict(self) -> Dict: if hasattr(self, 'signature') and self.signature is not None: _dict['signature'] = self.signature if hasattr(self, 'sizes') and self.sizes is not None: - _dict['sizes'] = self.sizes.to_dict() + if isinstance(self.sizes, dict): + _dict['sizes'] = self.sizes + else: + _dict['sizes'] = self.sizes.to_dict() if hasattr(self, 'updater_running') and self.updater_running is not None: _dict['updater_running'] = self.updater_running + if hasattr(self, 'updates_pending') and self.updates_pending is not None: + if isinstance(self.updates_pending, dict): + _dict['updates_pending'] = self.updates_pending + else: + _dict['updates_pending'] = self.updates_pending.to_dict() if hasattr(self, 'waiting_clients') and self.waiting_clients is not None: _dict['waiting_clients'] = self.waiting_clients if hasattr(self, 'waiting_commit') and self.waiting_commit is not None: @@ -11388,18 +12902,21 @@ def __ne__(self, other: 'DesignDocumentViewIndex') -> bool: """Return `true` when self and other are not equal, false otherwise.""" return not self == other -class DesignDocumentViewsMapReduce(): + +class DesignDocumentViewsMapReduce: """ Schema for view functions definition. - :attr str map: JavaScript map function as a string. - :attr str reduce: (optional) JavaScript reduce function as a string. + :param str map: JavaScript map function as a string. + :param str reduce: (optional) JavaScript reduce function as a string. """ - def __init__(self, - map: str, - *, - reduce: str = None) -> None: + def __init__( + self, + map: str, + *, + reduce: Optional[str] = None, + ) -> None: """ Initialize a DesignDocumentViewsMapReduce object. @@ -11413,12 +12930,12 @@ def __init__(self, def from_dict(cls, _dict: Dict) -> 'DesignDocumentViewsMapReduce': """Initialize a DesignDocumentViewsMapReduce object from a json dictionary.""" args = {} - if 'map' in _dict: - args['map'] = _dict.get('map') + if (map := _dict.get('map')) is not None: + args['map'] = map else: raise ValueError('Required property \'map\' not present in DesignDocumentViewsMapReduce JSON') - if 'reduce' in _dict: - args['reduce'] = _dict.get('reduce') + if (reduce := _dict.get('reduce')) is not None: + args['reduce'] = reduce return cls(**args) @classmethod @@ -11453,44 +12970,51 @@ def __ne__(self, other: 'DesignDocumentViewsMapReduce') -> bool: """Return `true` when self and other are not equal, false otherwise.""" return not self == other -class DocsResultRow(): + +class DocsResultRow: """ Schema for a row of document information in a DocsResult. - :attr str caused_by: (optional) The cause of the error (if available). - :attr str error: (optional) The name of the error. - :attr str reason: (optional) The reason the error occurred (if available). - :attr Document doc: (optional) Schema for a document. - :attr str id: (optional) id. - :attr str key: Document ID. - :attr DocsResultRowValue value: (optional) Value of built-in `/_all_docs` style + :param str caused_by: (optional) The cause of the error (if available). + :param str error: (optional) The name of the error. + :param str reason: (optional) The reason the error occurred (if available). + :param int ref: (optional) An internal error reference (if available). + :param Document doc: (optional) Schema for a document. + :param str id: (optional) Schema for a document ID. + :param str key: Schema for a document ID. + :param DocsResultRowValue value: (optional) Value of built-in `/_all_docs` style view. """ - def __init__(self, - key: str, - *, - caused_by: str = None, - error: str = None, - reason: str = None, - doc: 'Document' = None, - id: str = None, - value: 'DocsResultRowValue' = None) -> None: + def __init__( + self, + key: str, + *, + caused_by: Optional[str] = None, + error: Optional[str] = None, + reason: Optional[str] = None, + ref: Optional[int] = None, + doc: Optional['Document'] = None, + id: Optional[str] = None, + value: Optional['DocsResultRowValue'] = None, + ) -> None: """ Initialize a DocsResultRow object. - :param str key: Document ID. + :param str key: Schema for a document ID. :param str caused_by: (optional) The cause of the error (if available). :param str error: (optional) The name of the error. :param str reason: (optional) The reason the error occurred (if available). + :param int ref: (optional) An internal error reference (if available). :param Document doc: (optional) Schema for a document. - :param str id: (optional) id. + :param str id: (optional) Schema for a document ID. :param DocsResultRowValue value: (optional) Value of built-in `/_all_docs` style view. """ self.caused_by = caused_by self.error = error self.reason = reason + self.ref = ref self.doc = doc self.id = id self.key = key @@ -11500,22 +13024,24 @@ def __init__(self, def from_dict(cls, _dict: Dict) -> 'DocsResultRow': """Initialize a DocsResultRow object from a json dictionary.""" args = {} - if 'caused_by' in _dict: - args['caused_by'] = _dict.get('caused_by') - if 'error' in _dict: - args['error'] = _dict.get('error') - if 'reason' in _dict: - args['reason'] = _dict.get('reason') - if 'doc' in _dict: - args['doc'] = Document.from_dict(_dict.get('doc')) - if 'id' in _dict: - args['id'] = _dict.get('id') - if 'key' in _dict: - args['key'] = _dict.get('key') + if (caused_by := _dict.get('caused_by')) is not None: + args['caused_by'] = caused_by + if (error := _dict.get('error')) is not None: + args['error'] = error + if (reason := _dict.get('reason')) is not None: + args['reason'] = reason + if (ref := _dict.get('ref')) is not None: + args['ref'] = ref + if (doc := _dict.get('doc')) is not None: + args['doc'] = Document.from_dict(doc) + if (id := _dict.get('id')) is not None: + args['id'] = id + if (key := _dict.get('key')) is not None: + args['key'] = key else: raise ValueError('Required property \'key\' not present in DocsResultRow JSON') - if 'value' in _dict: - args['value'] = DocsResultRowValue.from_dict(_dict.get('value')) + if (value := _dict.get('value')) is not None: + args['value'] = DocsResultRowValue.from_dict(value) return cls(**args) @classmethod @@ -11532,14 +13058,22 @@ def to_dict(self) -> Dict: _dict['error'] = self.error if hasattr(self, 'reason') and self.reason is not None: _dict['reason'] = self.reason + if hasattr(self, 'ref') and self.ref is not None: + _dict['ref'] = self.ref if hasattr(self, 'doc') and self.doc is not None: - _dict['doc'] = self.doc.to_dict() + if isinstance(self.doc, dict): + _dict['doc'] = self.doc + else: + _dict['doc'] = self.doc.to_dict() if hasattr(self, 'id') and self.id is not None: _dict['id'] = self.id if hasattr(self, 'key') and self.key is not None: _dict['key'] = self.key if hasattr(self, 'value') and self.value is not None: - _dict['value'] = self.value.to_dict() + if isinstance(self.value, dict): + _dict['value'] = self.value + else: + _dict['value'] = self.value.to_dict() return _dict def _to_dict(self): @@ -11560,28 +13094,40 @@ def __ne__(self, other: 'DocsResultRow') -> bool: """Return `true` when self and other are not equal, false otherwise.""" return not self == other -class DocsResultRowValue(): + +class DocsResultRowValue: """ Value of built-in `/_all_docs` style view. - :attr str rev: Schema for a document revision identifier. + :param bool deleted: (optional) If `true` then the document is deleted. Not + present for undeleted documents. + :param str rev: Schema for a document revision identifier. """ - def __init__(self, - rev: str) -> None: + def __init__( + self, + rev: str, + *, + deleted: Optional[bool] = None, + ) -> None: """ Initialize a DocsResultRowValue object. :param str rev: Schema for a document revision identifier. + :param bool deleted: (optional) If `true` then the document is deleted. Not + present for undeleted documents. """ + self.deleted = deleted self.rev = rev @classmethod def from_dict(cls, _dict: Dict) -> 'DocsResultRowValue': """Initialize a DocsResultRowValue object from a json dictionary.""" args = {} - if 'rev' in _dict: - args['rev'] = _dict.get('rev') + if (deleted := _dict.get('deleted')) is not None: + args['deleted'] = deleted + if (rev := _dict.get('rev')) is not None: + args['rev'] = rev else: raise ValueError('Required property \'rev\' not present in DocsResultRowValue JSON') return cls(**args) @@ -11594,6 +13140,8 @@ def _from_dict(cls, _dict): def to_dict(self) -> Dict: """Return a json dictionary representing this model.""" _dict = {} + if hasattr(self, 'deleted') and self.deleted is not None: + _dict['deleted'] = self.deleted if hasattr(self, 'rev') and self.rev is not None: _dict['rev'] = self.rev return _dict @@ -11616,97 +13164,112 @@ def __ne__(self, other: 'DocsResultRowValue') -> bool: """Return `true` when self and other are not equal, false otherwise.""" return not self == other -class Document(): + +class Document: """ Schema for a document. - :attr dict attachments: (optional) Schema for a map of attachment name to + :param dict _attachments: (optional) Schema for a map of attachment name to attachment metadata. - :attr List[str] conflicts: (optional) Schema for a list of document revision + :param List[str] _conflicts: (optional) Schema for a list of document revision identifiers. - :attr bool deleted: (optional) Deletion flag. Available if document was removed. - :attr List[str] deleted_conflicts: (optional) Schema for a list of document + :param bool _deleted: (optional) Deletion flag. Available if document was + removed. + :param List[str] _deleted_conflicts: (optional) Schema for a list of document revision identifiers. - :attr str id: (optional) Document ID. - :attr str local_seq: (optional) Document's update sequence in current database. - Available if requested with local_seq=true query parameter. - :attr str rev: (optional) Schema for a document revision identifier. - :attr Revisions revisions: (optional) Schema for list of revision information. - :attr List[DocumentRevisionStatus] revs_info: (optional) Schema for a list of + :param str _id: (optional) Schema for a document ID. + :param str _local_seq: (optional) Document's update sequence in current + database. Available if requested with local_seq=true query parameter. + :param str _rev: (optional) Schema for a document revision identifier. + :param Revisions _revisions: (optional) Schema for list of revision information. + :param List[DocumentRevisionStatus] _revs_info: (optional) Schema for a list of objects with information about local revisions and their status. + + This type supports additional properties of type object. """ # The set of defined properties for the class - _properties = frozenset(['attachments', '_attachments', 'conflicts', '_conflicts', 'deleted', '_deleted', 'deleted_conflicts', '_deleted_conflicts', 'id', '_id', 'local_seq', '_local_seq', 'rev', '_rev', 'revisions', '_revisions', 'revs_info', '_revs_info']) - - def __init__(self, - *, - attachments: dict = None, - conflicts: List[str] = None, - deleted: bool = None, - deleted_conflicts: List[str] = None, - id: str = None, - local_seq: str = None, - rev: str = None, - revisions: 'Revisions' = None, - revs_info: List['DocumentRevisionStatus'] = None, - **kwargs) -> None: + _properties = frozenset(['_attachments', '_conflicts', '_deleted', '_deleted_conflicts', '_id', '_local_seq', '_rev', '_revisions', '_revs_info']) + + def __init__( + self, + *, + _attachments: Optional[dict] = None, + _conflicts: Optional[List[str]] = None, + _deleted: Optional[bool] = None, + _deleted_conflicts: Optional[List[str]] = None, + _id: Optional[str] = None, + _local_seq: Optional[str] = None, + _rev: Optional[str] = None, + _revisions: Optional['Revisions'] = None, + _revs_info: Optional[List['DocumentRevisionStatus']] = None, + **kwargs: Optional[object], + ) -> None: """ Initialize a Document object. - :param dict attachments: (optional) Schema for a map of attachment name to + :param dict _attachments: (optional) Schema for a map of attachment name to attachment metadata. - :param List[str] conflicts: (optional) Schema for a list of document + :param List[str] _conflicts: (optional) Schema for a list of document revision identifiers. - :param bool deleted: (optional) Deletion flag. Available if document was + :param bool _deleted: (optional) Deletion flag. Available if document was removed. - :param List[str] deleted_conflicts: (optional) Schema for a list of + :param List[str] _deleted_conflicts: (optional) Schema for a list of document revision identifiers. - :param str id: (optional) Document ID. - :param str local_seq: (optional) Document's update sequence in current + :param str _id: (optional) Schema for a document ID. + :param str _local_seq: (optional) Document's update sequence in current database. Available if requested with local_seq=true query parameter. - :param str rev: (optional) Schema for a document revision identifier. - :param Revisions revisions: (optional) Schema for list of revision + :param str _rev: (optional) Schema for a document revision identifier. + :param Revisions _revisions: (optional) Schema for list of revision information. - :param List[DocumentRevisionStatus] revs_info: (optional) Schema for a list - of objects with information about local revisions and their status. - :param **kwargs: (optional) Any additional properties. - """ - self.attachments = attachments - self.conflicts = conflicts - self.deleted = deleted - self.deleted_conflicts = deleted_conflicts - self.id = id - self.local_seq = local_seq - self.rev = rev - self.revisions = revisions - self.revs_info = revs_info - for _key, _value in kwargs.items(): - setattr(self, _key, _value) + :param List[DocumentRevisionStatus] _revs_info: (optional) Schema for a + list of objects with information about local revisions and their status. + :param object **kwargs: (optional) Additional properties of type object + """ + self._attachments = _attachments + self._conflicts = _conflicts + self._deleted = _deleted + self._deleted_conflicts = _deleted_conflicts + self._id = _id + self._local_seq = _local_seq + self._rev = _rev + self._revisions = _revisions + self._revs_info = _revs_info + for k, v in kwargs.items(): + if k not in Document._properties: + if not isinstance(v, object): + raise ValueError('Value for additional property {} must be of type object'.format(k)) + setattr(self, k, v) + else: + raise ValueError('Property {} cannot be specified as an additional property'.format(k)) @classmethod def from_dict(cls, _dict: Dict) -> 'Document': """Initialize a Document object from a json dictionary.""" args = {} - if '_attachments' in _dict: - args['attachments'] = {k : Attachment.from_dict(v) for k, v in _dict.get('_attachments').items()} - if '_conflicts' in _dict: - args['conflicts'] = _dict.get('_conflicts') - if '_deleted' in _dict: - args['deleted'] = _dict.get('_deleted') - if '_deleted_conflicts' in _dict: - args['deleted_conflicts'] = _dict.get('_deleted_conflicts') - if '_id' in _dict: - args['id'] = _dict.get('_id') - if '_local_seq' in _dict: - args['local_seq'] = _dict.get('_local_seq') - if '_rev' in _dict: - args['rev'] = _dict.get('_rev') - if '_revisions' in _dict: - args['revisions'] = Revisions.from_dict(_dict.get('_revisions')) - if '_revs_info' in _dict: - args['revs_info'] = [DocumentRevisionStatus.from_dict(x) for x in _dict.get('_revs_info')] - args.update({k:v for (k, v) in _dict.items() if k not in cls._properties}) + if (attachments := _dict.get('_attachments')) is not None: + args['_attachments'] = {k: Attachment.from_dict(v) for k, v in attachments.items()} + if (conflicts := _dict.get('_conflicts')) is not None: + args['_conflicts'] = conflicts + if (deleted := _dict.get('_deleted')) is not None: + args['_deleted'] = deleted + if (deleted_conflicts := _dict.get('_deleted_conflicts')) is not None: + args['_deleted_conflicts'] = deleted_conflicts + if (id := _dict.get('_id')) is not None: + args['_id'] = id + if (local_seq := _dict.get('_local_seq')) is not None: + args['_local_seq'] = local_seq + if (rev := _dict.get('_rev')) is not None: + args['_rev'] = rev + if (revisions := _dict.get('_revisions')) is not None: + args['_revisions'] = Revisions.from_dict(revisions) + if (revs_info := _dict.get('_revs_info')) is not None: + args['_revs_info'] = [DocumentRevisionStatus.from_dict(v) for v in revs_info] + for k, v in _dict.items(): + if k not in cls._properties: + if not isinstance(v, object): + raise ValueError('Value for additional property {} must be of type object'.format(k)) + args[k] = v return cls(**args) @classmethod @@ -11717,27 +13280,41 @@ def _from_dict(cls, _dict): def to_dict(self) -> Dict: """Return a json dictionary representing this model.""" _dict = {} - if hasattr(self, 'attachments') and self.attachments is not None: - _dict['_attachments'] = {k : v.to_dict() for k, v in self.attachments.items()} - if hasattr(self, 'conflicts') and self.conflicts is not None: - _dict['_conflicts'] = self.conflicts - if hasattr(self, 'deleted') and self.deleted is not None: - _dict['_deleted'] = self.deleted - if hasattr(self, 'deleted_conflicts') and self.deleted_conflicts is not None: - _dict['_deleted_conflicts'] = self.deleted_conflicts - if hasattr(self, 'id') and self.id is not None: - _dict['_id'] = self.id - if hasattr(self, 'local_seq') and self.local_seq is not None: - _dict['_local_seq'] = self.local_seq - if hasattr(self, 'rev') and self.rev is not None: - _dict['_rev'] = self.rev - if hasattr(self, 'revisions') and self.revisions is not None: - _dict['_revisions'] = self.revisions.to_dict() - if hasattr(self, 'revs_info') and self.revs_info is not None: - _dict['_revs_info'] = [x.to_dict() for x in self.revs_info] - for _key in [k for k in vars(self).keys() if k not in Document._properties]: - if getattr(self, _key, None) is not None: - _dict[_key] = getattr(self, _key) + if hasattr(self, '_attachments') and self._attachments is not None: + _attachments_map = {} + for k, v in self._attachments.items(): + if isinstance(v, dict): + _attachments_map[k] = v + else: + _attachments_map[k] = v.to_dict() + _dict['_attachments'] = _attachments_map + if hasattr(self, '_conflicts') and self._conflicts is not None: + _dict['_conflicts'] = self._conflicts + if hasattr(self, '_deleted') and self._deleted is not None: + _dict['_deleted'] = self._deleted + if hasattr(self, '_deleted_conflicts') and self._deleted_conflicts is not None: + _dict['_deleted_conflicts'] = self._deleted_conflicts + if hasattr(self, '_id') and self._id is not None: + _dict['_id'] = self._id + if hasattr(self, '_local_seq') and self._local_seq is not None: + _dict['_local_seq'] = self._local_seq + if hasattr(self, '_rev') and self._rev is not None: + _dict['_rev'] = self._rev + if hasattr(self, '_revisions') and self._revisions is not None: + if isinstance(self._revisions, dict): + _dict['_revisions'] = self._revisions + else: + _dict['_revisions'] = self._revisions.to_dict() + if hasattr(self, '_revs_info') and self._revs_info is not None: + _revs_info_list = [] + for v in self._revs_info: + if isinstance(v, dict): + _revs_info_list.append(v) + else: + _revs_info_list.append(v.to_dict()) + _dict['_revs_info'] = _revs_info_list + for k in [_k for _k in vars(self).keys() if _k not in Document._properties]: + _dict[k] = getattr(self, k) return _dict def _to_dict(self): @@ -11745,21 +13322,23 @@ def _to_dict(self): return self.to_dict() def get_properties(self) -> Dict: - """Return a dictionary of arbitrary properties from this instance of Document""" + """Return the additional properties from this instance of Document in the form of a dict.""" _dict = {} - - for _key in [k for k in vars(self).keys() if k not in Document._properties]: - _dict[_key] = getattr(self, _key) + for k in [_k for _k in vars(self).keys() if _k not in Document._properties]: + _dict[k] = getattr(self, k) return _dict def set_properties(self, _dict: dict): - """Set a dictionary of arbitrary properties to this instance of Document""" - for _key in [k for k in vars(self).keys() if k not in Document._properties]: - delattr(self, _key) - - for _key, _value in _dict.items(): - if _key not in Document._properties: - setattr(self, _key, _value) + """Set a dictionary of additional properties in this instance of Document""" + for k in [_k for _k in vars(self).keys() if _k not in Document._properties]: + delattr(self, k) + for k, v in _dict.items(): + if k not in Document._properties: + if not isinstance(v, object): + raise ValueError('Value for additional property {} must be of type object'.format(k)) + setattr(self, k, v) + else: + raise ValueError('Property {} cannot be specified as an additional property'.format(k)) def __str__(self) -> str: """Return a `str` version of this Document object.""" @@ -11775,26 +13354,31 @@ def __ne__(self, other: 'Document') -> bool: """Return `true` when self and other are not equal, false otherwise.""" return not self == other -class DocumentResult(): + +class DocumentResult: """ Schema for the result of a document modification. - :attr str id: Schema for a document ID. - :attr str rev: (optional) Schema for a document revision identifier. - :attr bool ok: (optional) ok. - :attr str caused_by: (optional) The cause of the error (if available). - :attr str error: (optional) The name of the error. - :attr str reason: (optional) The reason the error occurred (if available). + :param str id: Schema for a document ID. + :param str rev: (optional) Schema for a document revision identifier. + :param bool ok: (optional) ok. + :param str caused_by: (optional) The cause of the error (if available). + :param str error: (optional) The name of the error. + :param str reason: (optional) The reason the error occurred (if available). + :param int ref: (optional) An internal error reference (if available). """ - def __init__(self, - id: str, - *, - rev: str = None, - ok: bool = None, - caused_by: str = None, - error: str = None, - reason: str = None) -> None: + def __init__( + self, + id: str, + *, + rev: Optional[str] = None, + ok: Optional[bool] = None, + caused_by: Optional[str] = None, + error: Optional[str] = None, + reason: Optional[str] = None, + ref: Optional[int] = None, + ) -> None: """ Initialize a DocumentResult object. @@ -11804,6 +13388,7 @@ def __init__(self, :param str caused_by: (optional) The cause of the error (if available). :param str error: (optional) The name of the error. :param str reason: (optional) The reason the error occurred (if available). + :param int ref: (optional) An internal error reference (if available). """ self.id = id self.rev = rev @@ -11811,25 +13396,28 @@ def __init__(self, self.caused_by = caused_by self.error = error self.reason = reason + self.ref = ref @classmethod def from_dict(cls, _dict: Dict) -> 'DocumentResult': """Initialize a DocumentResult object from a json dictionary.""" args = {} - if 'id' in _dict: - args['id'] = _dict.get('id') + if (id := _dict.get('id')) is not None: + args['id'] = id else: raise ValueError('Required property \'id\' not present in DocumentResult JSON') - if 'rev' in _dict: - args['rev'] = _dict.get('rev') - if 'ok' in _dict: - args['ok'] = _dict.get('ok') - if 'caused_by' in _dict: - args['caused_by'] = _dict.get('caused_by') - if 'error' in _dict: - args['error'] = _dict.get('error') - if 'reason' in _dict: - args['reason'] = _dict.get('reason') + if (rev := _dict.get('rev')) is not None: + args['rev'] = rev + if (ok := _dict.get('ok')) is not None: + args['ok'] = ok + if (caused_by := _dict.get('caused_by')) is not None: + args['caused_by'] = caused_by + if (error := _dict.get('error')) is not None: + args['error'] = error + if (reason := _dict.get('reason')) is not None: + args['reason'] = reason + if (ref := _dict.get('ref')) is not None: + args['ref'] = ref return cls(**args) @classmethod @@ -11852,6 +13440,8 @@ def to_dict(self) -> Dict: _dict['error'] = self.error if hasattr(self, 'reason') and self.reason is not None: _dict['reason'] = self.reason + if hasattr(self, 'ref') and self.ref is not None: + _dict['ref'] = self.ref return _dict def _to_dict(self): @@ -11872,19 +13462,22 @@ def __ne__(self, other: 'DocumentResult') -> bool: """Return `true` when self and other are not equal, false otherwise.""" return not self == other -class DocumentRevisionStatus(): + +class DocumentRevisionStatus: """ Schema for information about revisions and their status. - :attr str rev: Schema for a document revision identifier. - :attr str status: Status of the revision. May be one of: - `available`: Revision - is available for retrieving with rev query parameter - `missing`: Revision is - not available - `deleted`: Revision belongs to deleted document. + :param str rev: Schema for a document revision identifier. + :param str status: Status of the revision. May be one of: - `available`: + Revision is available for retrieving with rev query parameter - `missing`: + Revision is not available - `deleted`: Revision belongs to deleted document. """ - def __init__(self, - rev: str, - status: str) -> None: + def __init__( + self, + rev: str, + status: str, + ) -> None: """ Initialize a DocumentRevisionStatus object. @@ -11901,12 +13494,12 @@ def __init__(self, def from_dict(cls, _dict: Dict) -> 'DocumentRevisionStatus': """Initialize a DocumentRevisionStatus object from a json dictionary.""" args = {} - if 'rev' in _dict: - args['rev'] = _dict.get('rev') + if (rev := _dict.get('rev')) is not None: + args['rev'] = rev else: raise ValueError('Required property \'rev\' not present in DocumentRevisionStatus JSON') - if 'status' in _dict: - args['status'] = _dict.get('status') + if (status := _dict.get('status')) is not None: + args['status'] = status else: raise ValueError('Required property \'status\' not present in DocumentRevisionStatus JSON') return cls(**args) @@ -11949,22 +13542,26 @@ class StatusEnum(str, Enum): retrieving with rev query parameter - `missing`: Revision is not available - `deleted`: Revision belongs to deleted document. """ + AVAILABLE = 'available' MISSING = 'missing' DELETED = 'deleted' -class DocumentShardInfo(): + +class DocumentShardInfo: """ Schema for document shard information. - :attr List[str] nodes: List of nodes serving a replica of the shard. - :attr str range: The shard range in which the document is stored. + :param List[str] nodes: List of nodes serving a replica of the shard. + :param str range: The shard range in which the document is stored. """ - def __init__(self, - nodes: List[str], - range: str) -> None: + def __init__( + self, + nodes: List[str], + range: str, + ) -> None: """ Initialize a DocumentShardInfo object. @@ -11978,12 +13575,12 @@ def __init__(self, def from_dict(cls, _dict: Dict) -> 'DocumentShardInfo': """Initialize a DocumentShardInfo object from a json dictionary.""" args = {} - if 'nodes' in _dict: - args['nodes'] = _dict.get('nodes') + if (nodes := _dict.get('nodes')) is not None: + args['nodes'] = nodes else: raise ValueError('Required property \'nodes\' not present in DocumentShardInfo JSON') - if 'range' in _dict: - args['range'] = _dict.get('range') + if (range := _dict.get('range')) is not None: + args['range'] = range else: raise ValueError('Required property \'range\' not present in DocumentShardInfo JSON') return cls(**args) @@ -12020,24 +13617,27 @@ def __ne__(self, other: 'DocumentShardInfo') -> bool: """Return `true` when self and other are not equal, false otherwise.""" return not self == other -class ExecutionStats(): + +class ExecutionStats: """ Schema for find query execution statistics. - :attr float execution_time_ms: Time to execute the query. - :attr int results_returned: Number of results returned. - :attr int total_docs_examined: Number of documents fetched from the index. - :attr int total_keys_examined: Number of rows scanned in the index. - :attr int total_quorum_docs_examined: Number of documents fetched from the + :param float execution_time_ms: Time to execute the query. + :param int results_returned: Number of results returned. + :param int total_docs_examined: Number of documents fetched from the index. + :param int total_keys_examined: Number of rows scanned in the index. + :param int total_quorum_docs_examined: Number of documents fetched from the primary index with the specified read quorum. """ - def __init__(self, - execution_time_ms: float, - results_returned: int, - total_docs_examined: int, - total_keys_examined: int, - total_quorum_docs_examined: int) -> None: + def __init__( + self, + execution_time_ms: float, + results_returned: int, + total_docs_examined: int, + total_keys_examined: int, + total_quorum_docs_examined: int, + ) -> None: """ Initialize a ExecutionStats object. @@ -12058,24 +13658,24 @@ def __init__(self, def from_dict(cls, _dict: Dict) -> 'ExecutionStats': """Initialize a ExecutionStats object from a json dictionary.""" args = {} - if 'execution_time_ms' in _dict: - args['execution_time_ms'] = _dict.get('execution_time_ms') + if (execution_time_ms := _dict.get('execution_time_ms')) is not None: + args['execution_time_ms'] = execution_time_ms else: raise ValueError('Required property \'execution_time_ms\' not present in ExecutionStats JSON') - if 'results_returned' in _dict: - args['results_returned'] = _dict.get('results_returned') + if (results_returned := _dict.get('results_returned')) is not None: + args['results_returned'] = results_returned else: raise ValueError('Required property \'results_returned\' not present in ExecutionStats JSON') - if 'total_docs_examined' in _dict: - args['total_docs_examined'] = _dict.get('total_docs_examined') + if (total_docs_examined := _dict.get('total_docs_examined')) is not None: + args['total_docs_examined'] = total_docs_examined else: raise ValueError('Required property \'total_docs_examined\' not present in ExecutionStats JSON') - if 'total_keys_examined' in _dict: - args['total_keys_examined'] = _dict.get('total_keys_examined') + if (total_keys_examined := _dict.get('total_keys_examined')) is not None: + args['total_keys_examined'] = total_keys_examined else: raise ValueError('Required property \'total_keys_examined\' not present in ExecutionStats JSON') - if 'total_quorum_docs_examined' in _dict: - args['total_quorum_docs_examined'] = _dict.get('total_quorum_docs_examined') + if (total_quorum_docs_examined := _dict.get('total_quorum_docs_examined')) is not None: + args['total_quorum_docs_examined'] = total_quorum_docs_examined else: raise ValueError('Required property \'total_quorum_docs_examined\' not present in ExecutionStats JSON') return cls(**args) @@ -12118,17 +13718,26 @@ def __ne__(self, other: 'ExecutionStats') -> bool: """Return `true` when self and other are not equal, false otherwise.""" return not self == other -class ExplainResult(): + +class ExplainResult: """ Schema for information about the index used for a find query. - :attr str dbname: dbname. - :attr List[str] fields: fields. - :attr IndexInformation index: Schema for information about an index. - :attr int limit: limit. - :attr dict opts: opts. - :attr ExplainResultRange range: (optional) range. - :attr dict selector: JSON object describing criteria used to select documents. + :param bool covering: When `true`, the query is answered using the index only + and no documents are fetched. + :param str dbname: Schema for a database name. + :param List[str] fields: Fields that were requested to be projected from the + document. If no fields were requested to be projected this will be empty and all + fields will be returned. + :param IndexInformation index: Schema for information about an index. + :param List[IndexCandidate] index_candidates: Schema for the list of all the + other indexes that were not chosen for serving the query. + :param int limit: The used maximum number of results returned. + :param ExplainResultMrArgs mrargs: (optional) Arguments passed to the underlying + view. + :param ExplainResultOpts opts: Options used for the request. + :param object partitioned: (optional) Schema for any JSON type. + :param dict selector: JSON object describing criteria used to select documents. The selector specifies fields in the document, and provides an expression to evaluate with the field content or other data. The selector object must: @@ -12144,40 +13753,59 @@ class ExplainResult(): field. There are two core types of operators in the selector syntax: * Combination operators: applied at the topmost level of selection. They are - used to combine selectors. In addition to the common boolean operators (`$and`, - `$or`, `$not`, `$nor`) there are three combination operators: `$all`, - `$elemMatch`, and `$allMatch`. A combination operator takes a single argument. - The argument is either another selector, or an array of selectors. + used to combine selectors. A combination operator takes a single argument. The + argument is either another selector, or an array of selectors. * Condition operators: are specific to a field, and are used to evaluate the value stored in that field. For instance, the basic `$eq` operator matches when the specified field contains a value that is equal to the supplied argument. - * Only equality operators such as `$eq`, `$gt`, `$gte`, `$lt`, and `$lte` (but - not `$ne`) can be used as the basis of a query. You should include at least one - of these in a selector. - For further reference see - [selector - syntax](https://cloud.ibm.com/docs/Cloudant?topic=Cloudant-query#selector-syntax). - :attr int skip: skip. + It is important for query performance to use appropriate selectors: + * Only equality operators such as `$eq`, `$gt`, `$gte`, `$lt`, and + `$lte` (but not `$ne`) can be used as the basis of a query. You should include + at least one of these in a selector. + * Some operators such as `$not`, `$or`, `$in`, and `$regex` cannot be answered + from an index. For query selectors use these operators in conjunction with + equality operators or create and use a partial index to reduce the number of + documents that will need to be scanned. + See [the Cloudant + Docs](https://cloud.ibm.com/docs/Cloudant?topic=Cloudant-operators) for a list + of all available combination and conditional operators. + For further reference see [selector + syntax](https://cloud.ibm.com/docs/Cloudant?topic=Cloudant-selector-syntax). + :param List[SelectorHint] selector_hints: Schema for a list of objects with + extra information on the selector to provide insights about its usability. + :param int skip: Skip parameter used. """ - def __init__(self, - dbname: str, - fields: List[str], - index: 'IndexInformation', - limit: int, - opts: dict, - selector: dict, - skip: int, - *, - range: 'ExplainResultRange' = None) -> None: + def __init__( + self, + covering: bool, + dbname: str, + fields: List[str], + index: 'IndexInformation', + index_candidates: List['IndexCandidate'], + limit: int, + opts: 'ExplainResultOpts', + selector: dict, + selector_hints: List['SelectorHint'], + skip: int, + *, + mrargs: Optional['ExplainResultMrArgs'] = None, + partitioned: Optional[object] = None, + ) -> None: """ Initialize a ExplainResult object. - :param str dbname: dbname. - :param List[str] fields: fields. + :param bool covering: When `true`, the query is answered using the index + only and no documents are fetched. + :param str dbname: Schema for a database name. + :param List[str] fields: Fields that were requested to be projected from + the document. If no fields were requested to be projected this will be + empty and all fields will be returned. :param IndexInformation index: Schema for information about an index. - :param int limit: limit. - :param dict opts: opts. + :param List[IndexCandidate] index_candidates: Schema for the list of all + the other indexes that were not chosen for serving the query. + :param int limit: The used maximum number of results returned. + :param ExplainResultOpts opts: Options used for the request. :param dict selector: JSON object describing criteria used to select documents. The selector specifies fields in the document, and provides an expression to evaluate with the field content or other data. @@ -12194,65 +13822,92 @@ def __init__(self, field. There are two core types of operators in the selector syntax: * Combination operators: applied at the topmost level of selection. They - are used to combine selectors. In addition to the common boolean operators - (`$and`, `$or`, `$not`, `$nor`) there are three combination operators: - `$all`, `$elemMatch`, and `$allMatch`. A combination operator takes a - single argument. The argument is either another selector, or an array of + are used to combine selectors. A combination operator takes a single + argument. The argument is either another selector, or an array of selectors. * Condition operators: are specific to a field, and are used to evaluate the value stored in that field. For instance, the basic `$eq` operator matches when the specified field contains a value that is equal to the supplied argument. - * Only equality operators such as `$eq`, `$gt`, `$gte`, `$lt`, and `$lte` - (but not `$ne`) can be used as the basis of a query. You should include at - least one of these in a selector. - For further reference see - [selector - syntax](https://cloud.ibm.com/docs/Cloudant?topic=Cloudant-query#selector-syntax). - :param int skip: skip. - :param ExplainResultRange range: (optional) range. - """ + It is important for query performance to use appropriate selectors: + * Only equality operators such as `$eq`, `$gt`, `$gte`, `$lt`, and + `$lte` (but not `$ne`) can be used as the basis of a query. You should + include at least one of these in a selector. + * Some operators such as `$not`, `$or`, `$in`, and `$regex` cannot be + answered from an index. For query selectors use these operators in + conjunction with equality operators or create and use a partial index to + reduce the number of documents that will need to be scanned. + See [the Cloudant + Docs](https://cloud.ibm.com/docs/Cloudant?topic=Cloudant-operators) for a + list of all available combination and conditional operators. + For further reference see [selector + syntax](https://cloud.ibm.com/docs/Cloudant?topic=Cloudant-selector-syntax). + :param List[SelectorHint] selector_hints: Schema for a list of objects with + extra information on the selector to provide insights about its usability. + :param int skip: Skip parameter used. + :param ExplainResultMrArgs mrargs: (optional) Arguments passed to the + underlying view. + :param object partitioned: (optional) Schema for any JSON type. + """ + self.covering = covering self.dbname = dbname self.fields = fields self.index = index + self.index_candidates = index_candidates self.limit = limit + self.mrargs = mrargs self.opts = opts - self.range = range + self.partitioned = partitioned self.selector = selector + self.selector_hints = selector_hints self.skip = skip @classmethod def from_dict(cls, _dict: Dict) -> 'ExplainResult': """Initialize a ExplainResult object from a json dictionary.""" args = {} - if 'dbname' in _dict: - args['dbname'] = _dict.get('dbname') + if (covering := _dict.get('covering')) is not None: + args['covering'] = covering + else: + raise ValueError('Required property \'covering\' not present in ExplainResult JSON') + if (dbname := _dict.get('dbname')) is not None: + args['dbname'] = dbname else: raise ValueError('Required property \'dbname\' not present in ExplainResult JSON') - if 'fields' in _dict: - args['fields'] = _dict.get('fields') + if (fields := _dict.get('fields')) is not None: + args['fields'] = fields else: raise ValueError('Required property \'fields\' not present in ExplainResult JSON') - if 'index' in _dict: - args['index'] = IndexInformation.from_dict(_dict.get('index')) + if (index := _dict.get('index')) is not None: + args['index'] = IndexInformation.from_dict(index) else: raise ValueError('Required property \'index\' not present in ExplainResult JSON') - if 'limit' in _dict: - args['limit'] = _dict.get('limit') + if (index_candidates := _dict.get('index_candidates')) is not None: + args['index_candidates'] = [IndexCandidate.from_dict(v) for v in index_candidates] + else: + raise ValueError('Required property \'index_candidates\' not present in ExplainResult JSON') + if (limit := _dict.get('limit')) is not None: + args['limit'] = limit else: raise ValueError('Required property \'limit\' not present in ExplainResult JSON') - if 'opts' in _dict: - args['opts'] = _dict.get('opts') + if (mrargs := _dict.get('mrargs')) is not None: + args['mrargs'] = ExplainResultMrArgs.from_dict(mrargs) + if (opts := _dict.get('opts')) is not None: + args['opts'] = ExplainResultOpts.from_dict(opts) else: raise ValueError('Required property \'opts\' not present in ExplainResult JSON') - if 'range' in _dict: - args['range'] = ExplainResultRange.from_dict(_dict.get('range')) - if 'selector' in _dict: - args['selector'] = _dict.get('selector') + if (partitioned := _dict.get('partitioned')) is not None: + args['partitioned'] = partitioned + if (selector := _dict.get('selector')) is not None: + args['selector'] = selector else: raise ValueError('Required property \'selector\' not present in ExplainResult JSON') - if 'skip' in _dict: - args['skip'] = _dict.get('skip') + if (selector_hints := _dict.get('selector_hints')) is not None: + args['selector_hints'] = [SelectorHint.from_dict(v) for v in selector_hints] + else: + raise ValueError('Required property \'selector_hints\' not present in ExplainResult JSON') + if (skip := _dict.get('skip')) is not None: + args['skip'] = skip else: raise ValueError('Required property \'skip\' not present in ExplainResult JSON') return cls(**args) @@ -12265,20 +13920,49 @@ def _from_dict(cls, _dict): def to_dict(self) -> Dict: """Return a json dictionary representing this model.""" _dict = {} + if hasattr(self, 'covering') and self.covering is not None: + _dict['covering'] = self.covering if hasattr(self, 'dbname') and self.dbname is not None: _dict['dbname'] = self.dbname if hasattr(self, 'fields') and self.fields is not None: _dict['fields'] = self.fields if hasattr(self, 'index') and self.index is not None: - _dict['index'] = self.index.to_dict() + if isinstance(self.index, dict): + _dict['index'] = self.index + else: + _dict['index'] = self.index.to_dict() + if hasattr(self, 'index_candidates') and self.index_candidates is not None: + index_candidates_list = [] + for v in self.index_candidates: + if isinstance(v, dict): + index_candidates_list.append(v) + else: + index_candidates_list.append(v.to_dict()) + _dict['index_candidates'] = index_candidates_list if hasattr(self, 'limit') and self.limit is not None: _dict['limit'] = self.limit + if hasattr(self, 'mrargs') and self.mrargs is not None: + if isinstance(self.mrargs, dict): + _dict['mrargs'] = self.mrargs + else: + _dict['mrargs'] = self.mrargs.to_dict() if hasattr(self, 'opts') and self.opts is not None: - _dict['opts'] = self.opts - if hasattr(self, 'range') and self.range is not None: - _dict['range'] = self.range.to_dict() + if isinstance(self.opts, dict): + _dict['opts'] = self.opts + else: + _dict['opts'] = self.opts.to_dict() + if hasattr(self, 'partitioned') and self.partitioned is not None: + _dict['partitioned'] = self.partitioned if hasattr(self, 'selector') and self.selector is not None: _dict['selector'] = self.selector + if hasattr(self, 'selector_hints') and self.selector_hints is not None: + selector_hints_list = [] + for v in self.selector_hints: + if isinstance(v, dict): + selector_hints_list.append(v) + else: + selector_hints_list.append(v.to_dict()) + _dict['selector_hints'] = selector_hints_list if hasattr(self, 'skip') and self.skip is not None: _dict['skip'] = self.skip return _dict @@ -12301,49 +13985,141 @@ def __ne__(self, other: 'ExplainResult') -> bool: """Return `true` when self and other are not equal, false otherwise.""" return not self == other -class ExplainResultRange(): - """ - range. - :attr List[object] end_key: (optional) end_key. - :attr List[object] start_key: (optional) start_key. +class ExplainResultMrArgs: + """ + Arguments passed to the underlying view. + + :param object conflicts: Schema for any JSON type. + :param str direction: Direction parameter passed to the underlying view. + :param object end_key: Schema for any JSON type. + :param bool include_docs: A parameter that specifies whether to include the full + content of the documents in the response in the underlying view. + :param str partition: Partition parameter passed to the underlying view. + :param bool reduce: A parameter that specifies returning only documents that + match any of the specified keys in the underlying view. + :param bool stable: A parameter that specifies whether the view results should + be returned form a "stable" set of shards passed to the underlying view. + :param object start_key: (optional) Schema for any JSON type. + :param object update: Schema for any JSON type. + :param str view_type: The type of the underlying view. """ - def __init__(self, - *, - end_key: List[object] = None, - start_key: List[object] = None) -> None: - """ - Initialize a ExplainResultRange object. - - :param List[object] end_key: (optional) end_key. - :param List[object] start_key: (optional) start_key. + def __init__( + self, + conflicts: object, + direction: str, + end_key: object, + include_docs: bool, + partition: str, + reduce: bool, + stable: bool, + update: object, + view_type: str, + *, + start_key: Optional[object] = None, + ) -> None: + """ + Initialize a ExplainResultMrArgs object. + + :param object conflicts: Schema for any JSON type. + :param str direction: Direction parameter passed to the underlying view. + :param object end_key: Schema for any JSON type. + :param bool include_docs: A parameter that specifies whether to include the + full content of the documents in the response in the underlying view. + :param str partition: Partition parameter passed to the underlying view. + :param bool reduce: A parameter that specifies returning only documents + that match any of the specified keys in the underlying view. + :param bool stable: A parameter that specifies whether the view results + should be returned form a "stable" set of shards passed to the underlying + view. + :param object update: Schema for any JSON type. + :param str view_type: The type of the underlying view. + :param object start_key: (optional) Schema for any JSON type. """ + self.conflicts = conflicts + self.direction = direction self.end_key = end_key + self.include_docs = include_docs + self.partition = partition + self.reduce = reduce + self.stable = stable self.start_key = start_key + self.update = update + self.view_type = view_type @classmethod - def from_dict(cls, _dict: Dict) -> 'ExplainResultRange': - """Initialize a ExplainResultRange object from a json dictionary.""" + def from_dict(cls, _dict: Dict) -> 'ExplainResultMrArgs': + """Initialize a ExplainResultMrArgs object from a json dictionary.""" args = {} - if 'end_key' in _dict: - args['end_key'] = _dict.get('end_key') - if 'start_key' in _dict: - args['start_key'] = _dict.get('start_key') + if (conflicts := _dict.get('conflicts')) is not None: + args['conflicts'] = conflicts + else: + args['conflicts'] = None + if (direction := _dict.get('direction')) is not None: + args['direction'] = direction + else: + raise ValueError('Required property \'direction\' not present in ExplainResultMrArgs JSON') + if (end_key := _dict.get('end_key')) is not None: + args['end_key'] = end_key + else: + args['end_key'] = None + if (include_docs := _dict.get('include_docs')) is not None: + args['include_docs'] = include_docs + else: + raise ValueError('Required property \'include_docs\' not present in ExplainResultMrArgs JSON') + if (partition := _dict.get('partition')) is not None: + args['partition'] = partition + else: + args['partition'] = None + if (reduce := _dict.get('reduce')) is not None: + args['reduce'] = reduce + else: + raise ValueError('Required property \'reduce\' not present in ExplainResultMrArgs JSON') + if (stable := _dict.get('stable')) is not None: + args['stable'] = stable + else: + raise ValueError('Required property \'stable\' not present in ExplainResultMrArgs JSON') + if (start_key := _dict.get('start_key')) is not None: + args['start_key'] = start_key + if (update := _dict.get('update')) is not None: + args['update'] = update + else: + args['update'] = None + if (view_type := _dict.get('view_type')) is not None: + args['view_type'] = view_type + else: + raise ValueError('Required property \'view_type\' not present in ExplainResultMrArgs JSON') return cls(**args) @classmethod def _from_dict(cls, _dict): - """Initialize a ExplainResultRange object from a json dictionary.""" + """Initialize a ExplainResultMrArgs object from a json dictionary.""" return cls.from_dict(_dict) def to_dict(self) -> Dict: """Return a json dictionary representing this model.""" _dict = {} + if hasattr(self, 'conflicts') and self.conflicts is not None: + _dict['conflicts'] = self.conflicts + if hasattr(self, 'direction') and self.direction is not None: + _dict['direction'] = self.direction if hasattr(self, 'end_key') and self.end_key is not None: _dict['end_key'] = self.end_key + if hasattr(self, 'include_docs') and self.include_docs is not None: + _dict['include_docs'] = self.include_docs + if hasattr(self, 'partition') and self.partition is not None: + _dict['partition'] = self.partition + if hasattr(self, 'reduce') and self.reduce is not None: + _dict['reduce'] = self.reduce + if hasattr(self, 'stable') and self.stable is not None: + _dict['stable'] = self.stable if hasattr(self, 'start_key') and self.start_key is not None: _dict['start_key'] = self.start_key + if hasattr(self, 'update') and self.update is not None: + _dict['update'] = self.update + if hasattr(self, 'view_type') and self.view_type is not None: + _dict['view_type'] = self.view_type return _dict def _to_dict(self): @@ -12351,213 +14127,203 @@ def _to_dict(self): return self.to_dict() def __str__(self) -> str: - """Return a `str` version of this ExplainResultRange object.""" + """Return a `str` version of this ExplainResultMrArgs object.""" return json.dumps(self.to_dict(), indent=2) - def __eq__(self, other: 'ExplainResultRange') -> bool: + def __eq__(self, other: 'ExplainResultMrArgs') -> bool: """Return `true` when self and other are equal, false otherwise.""" if not isinstance(other, self.__class__): return False return self.__dict__ == other.__dict__ - def __ne__(self, other: 'ExplainResultRange') -> bool: + def __ne__(self, other: 'ExplainResultMrArgs') -> bool: """Return `true` when self and other are not equal, false otherwise.""" return not self == other -class FindResult(): - """ - Schema for the result of a query find operation. - - :attr str bookmark: Opaque bookmark token used when paginating results. - :attr List[Document] docs: Documents matching the selector. - :attr ExecutionStats execution_stats: (optional) Schema for find query execution - statistics. - :attr str warning: (optional) warning. - """ - - def __init__(self, - bookmark: str, - docs: List['Document'], - *, - execution_stats: 'ExecutionStats' = None, - warning: str = None) -> None: + class DirectionEnum(str, Enum): """ - Initialize a FindResult object. - - :param str bookmark: Opaque bookmark token used when paginating results. - :param List[Document] docs: Documents matching the selector. - :param ExecutionStats execution_stats: (optional) Schema for find query - execution statistics. - :param str warning: (optional) warning. + Direction parameter passed to the underlying view. """ - self.bookmark = bookmark - self.docs = docs - self.execution_stats = execution_stats - self.warning = warning - - @classmethod - def from_dict(cls, _dict: Dict) -> 'FindResult': - """Initialize a FindResult object from a json dictionary.""" - args = {} - if 'bookmark' in _dict: - args['bookmark'] = _dict.get('bookmark') - else: - raise ValueError('Required property \'bookmark\' not present in FindResult JSON') - if 'docs' in _dict: - args['docs'] = [Document.from_dict(x) for x in _dict.get('docs')] - else: - raise ValueError('Required property \'docs\' not present in FindResult JSON') - if 'execution_stats' in _dict: - args['execution_stats'] = ExecutionStats.from_dict(_dict.get('execution_stats')) - if 'warning' in _dict: - args['warning'] = _dict.get('warning') - return cls(**args) - - @classmethod - def _from_dict(cls, _dict): - """Initialize a FindResult object from a json dictionary.""" - return cls.from_dict(_dict) - - def to_dict(self) -> Dict: - """Return a json dictionary representing this model.""" - _dict = {} - if hasattr(self, 'bookmark') and self.bookmark is not None: - _dict['bookmark'] = self.bookmark - if hasattr(self, 'docs') and self.docs is not None: - _dict['docs'] = [x.to_dict() for x in self.docs] - if hasattr(self, 'execution_stats') and self.execution_stats is not None: - _dict['execution_stats'] = self.execution_stats.to_dict() - if hasattr(self, 'warning') and self.warning is not None: - _dict['warning'] = self.warning - return _dict - - def _to_dict(self): - """Return a json dictionary representing this model.""" - return self.to_dict() - - def __str__(self) -> str: - """Return a `str` version of this FindResult object.""" - return json.dumps(self.to_dict(), indent=2) - - def __eq__(self, other: 'FindResult') -> bool: - """Return `true` when self and other are equal, false otherwise.""" - if not isinstance(other, self.__class__): - return False - return self.__dict__ == other.__dict__ - - def __ne__(self, other: 'FindResult') -> bool: - """Return `true` when self and other are not equal, false otherwise.""" - return not self == other -class GeoIndexDefinition(): - """ - Schema for a geospatial index definition. + FWD = 'fwd' + REV = 'rev' - :attr str index: String form of a JavaScript function that is called for each - document in the database. The function takes the document as a parameter, - extracts some geospatial data from it, and then calls the `st_index` function to - index that data. The `st_index` takes a GeoJSON geometry as a parameter. - """ - def __init__(self, - index: str) -> None: + class ViewTypeEnum(str, Enum): """ - Initialize a GeoIndexDefinition object. - - :param str index: String form of a JavaScript function that is called for - each document in the database. The function takes the document as a - parameter, extracts some geospatial data from it, and then calls the - `st_index` function to index that data. The `st_index` takes a GeoJSON - geometry as a parameter. + The type of the underlying view. """ - self.index = index - - @classmethod - def from_dict(cls, _dict: Dict) -> 'GeoIndexDefinition': - """Initialize a GeoIndexDefinition object from a json dictionary.""" - args = {} - if 'index' in _dict: - args['index'] = _dict.get('index') - else: - raise ValueError('Required property \'index\' not present in GeoIndexDefinition JSON') - return cls(**args) - - @classmethod - def _from_dict(cls, _dict): - """Initialize a GeoIndexDefinition object from a json dictionary.""" - return cls.from_dict(_dict) - def to_dict(self) -> Dict: - """Return a json dictionary representing this model.""" - _dict = {} - if hasattr(self, 'index') and self.index is not None: - _dict['index'] = self.index - return _dict - - def _to_dict(self): - """Return a json dictionary representing this model.""" - return self.to_dict() - - def __str__(self) -> str: - """Return a `str` version of this GeoIndexDefinition object.""" - return json.dumps(self.to_dict(), indent=2) + MAP = 'map' + REDUCE = 'reduce' - def __eq__(self, other: 'GeoIndexDefinition') -> bool: - """Return `true` when self and other are equal, false otherwise.""" - if not isinstance(other, self.__class__): - return False - return self.__dict__ == other.__dict__ - def __ne__(self, other: 'GeoIndexDefinition') -> bool: - """Return `true` when self and other are not equal, false otherwise.""" - return not self == other -class GeoIndexInformation(): +class ExplainResultOpts: """ - Schema for information about a geospatial index. - - :attr GeoIndexStats geo_index: Schema for geospatial index statistics. - :attr str name: The name of the geospatial index design document. + Options used for the request. + + :param str bookmark: Opaque bookmark token used when paginating results. + :param bool conflicts: Conflicts used in the request query. + :param bool execution_stats: Execution statistics used in the request query. + :param List[str] fields: JSON array that uses the field syntax. Use this + parameter to specify which fields of a document must be returned. If it is + omitted or empty, the entire document is returned. + :param int limit: Limit used in the request query. + :param str partition: On which database partition the request was used. If it + was not used on a database partition, it returns with `""`. + :param int r: The read quorum that is needed for the result. + :param int skip: Skip used in the request query. + :param object sort: Schema for any JSON type. + :param bool stable: Stable used in the request query. + :param bool stale: Deprecated: Stale used in the request query. + :param bool update: Update used in the request query. + :param List[str] use_index: Use index used in the request query. """ - def __init__(self, - geo_index: 'GeoIndexStats', - name: str) -> None: - """ - Initialize a GeoIndexInformation object. + def __init__( + self, + bookmark: str, + conflicts: bool, + execution_stats: bool, + fields: List[str], + limit: int, + partition: str, + r: int, + skip: int, + sort: object, + stable: bool, + stale: bool, + update: bool, + use_index: List[str], + ) -> None: + """ + Initialize a ExplainResultOpts object. - :param GeoIndexStats geo_index: Schema for geospatial index statistics. - :param str name: The name of the geospatial index design document. + :param str bookmark: Opaque bookmark token used when paginating results. + :param bool conflicts: Conflicts used in the request query. + :param bool execution_stats: Execution statistics used in the request + query. + :param List[str] fields: JSON array that uses the field syntax. Use this + parameter to specify which fields of a document must be returned. If it is + omitted or empty, the entire document is returned. + :param int limit: Limit used in the request query. + :param str partition: On which database partition the request was used. If + it was not used on a database partition, it returns with `""`. + :param int r: The read quorum that is needed for the result. + :param int skip: Skip used in the request query. + :param object sort: Schema for any JSON type. + :param bool stable: Stable used in the request query. + :param bool stale: Deprecated: Stale used in the request query. + :param bool update: Update used in the request query. + :param List[str] use_index: Use index used in the request query. """ - self.geo_index = geo_index - self.name = name + self.bookmark = bookmark + self.conflicts = conflicts + self.execution_stats = execution_stats + self.fields = fields + self.limit = limit + self.partition = partition + self.r = r + self.skip = skip + self.sort = sort + self.stable = stable + self.stale = stale + self.update = update + self.use_index = use_index @classmethod - def from_dict(cls, _dict: Dict) -> 'GeoIndexInformation': - """Initialize a GeoIndexInformation object from a json dictionary.""" + def from_dict(cls, _dict: Dict) -> 'ExplainResultOpts': + """Initialize a ExplainResultOpts object from a json dictionary.""" args = {} - if 'geo_index' in _dict: - args['geo_index'] = GeoIndexStats.from_dict(_dict.get('geo_index')) + if (bookmark := _dict.get('bookmark')) is not None: + args['bookmark'] = bookmark + else: + raise ValueError('Required property \'bookmark\' not present in ExplainResultOpts JSON') + if (conflicts := _dict.get('conflicts')) is not None: + args['conflicts'] = conflicts + else: + raise ValueError('Required property \'conflicts\' not present in ExplainResultOpts JSON') + if (execution_stats := _dict.get('execution_stats')) is not None: + args['execution_stats'] = execution_stats + else: + raise ValueError('Required property \'execution_stats\' not present in ExplainResultOpts JSON') + if (fields := _dict.get('fields')) is not None: + args['fields'] = fields + else: + raise ValueError('Required property \'fields\' not present in ExplainResultOpts JSON') + if (limit := _dict.get('limit')) is not None: + args['limit'] = limit + else: + raise ValueError('Required property \'limit\' not present in ExplainResultOpts JSON') + if (partition := _dict.get('partition')) is not None: + args['partition'] = partition + else: + raise ValueError('Required property \'partition\' not present in ExplainResultOpts JSON') + if (r := _dict.get('r')) is not None: + args['r'] = r else: - raise ValueError('Required property \'geo_index\' not present in GeoIndexInformation JSON') - if 'name' in _dict: - args['name'] = _dict.get('name') + raise ValueError('Required property \'r\' not present in ExplainResultOpts JSON') + if (skip := _dict.get('skip')) is not None: + args['skip'] = skip else: - raise ValueError('Required property \'name\' not present in GeoIndexInformation JSON') + raise ValueError('Required property \'skip\' not present in ExplainResultOpts JSON') + if (sort := _dict.get('sort')) is not None: + args['sort'] = sort + else: + args['sort'] = None + if (stable := _dict.get('stable')) is not None: + args['stable'] = stable + else: + raise ValueError('Required property \'stable\' not present in ExplainResultOpts JSON') + if (stale := _dict.get('stale')) is not None: + args['stale'] = stale + else: + raise ValueError('Required property \'stale\' not present in ExplainResultOpts JSON') + if (update := _dict.get('update')) is not None: + args['update'] = update + else: + raise ValueError('Required property \'update\' not present in ExplainResultOpts JSON') + if (use_index := _dict.get('use_index')) is not None: + args['use_index'] = use_index + else: + raise ValueError('Required property \'use_index\' not present in ExplainResultOpts JSON') return cls(**args) @classmethod def _from_dict(cls, _dict): - """Initialize a GeoIndexInformation object from a json dictionary.""" + """Initialize a ExplainResultOpts object from a json dictionary.""" return cls.from_dict(_dict) def to_dict(self) -> Dict: """Return a json dictionary representing this model.""" _dict = {} - if hasattr(self, 'geo_index') and self.geo_index is not None: - _dict['geo_index'] = self.geo_index.to_dict() - if hasattr(self, 'name') and self.name is not None: - _dict['name'] = self.name + if hasattr(self, 'bookmark') and self.bookmark is not None: + _dict['bookmark'] = self.bookmark + if hasattr(self, 'conflicts') and self.conflicts is not None: + _dict['conflicts'] = self.conflicts + if hasattr(self, 'execution_stats') and self.execution_stats is not None: + _dict['execution_stats'] = self.execution_stats + if hasattr(self, 'fields') and self.fields is not None: + _dict['fields'] = self.fields + if hasattr(self, 'limit') and self.limit is not None: + _dict['limit'] = self.limit + if hasattr(self, 'partition') and self.partition is not None: + _dict['partition'] = self.partition + if hasattr(self, 'r') and self.r is not None: + _dict['r'] = self.r + if hasattr(self, 'skip') and self.skip is not None: + _dict['skip'] = self.skip + if hasattr(self, 'sort') and self.sort is not None: + _dict['sort'] = self.sort + if hasattr(self, 'stable') and self.stable is not None: + _dict['stable'] = self.stable + if hasattr(self, 'stale') and self.stale is not None: + _dict['stale'] = self.stale + if hasattr(self, 'update') and self.update is not None: + _dict['update'] = self.update + if hasattr(self, 'use_index') and self.use_index is not None: + _dict['use_index'] = self.use_index return _dict def _to_dict(self): @@ -12565,77 +14331,96 @@ def _to_dict(self): return self.to_dict() def __str__(self) -> str: - """Return a `str` version of this GeoIndexInformation object.""" + """Return a `str` version of this ExplainResultOpts object.""" return json.dumps(self.to_dict(), indent=2) - def __eq__(self, other: 'GeoIndexInformation') -> bool: + def __eq__(self, other: 'ExplainResultOpts') -> bool: """Return `true` when self and other are equal, false otherwise.""" if not isinstance(other, self.__class__): return False return self.__dict__ == other.__dict__ - def __ne__(self, other: 'GeoIndexInformation') -> bool: + def __ne__(self, other: 'ExplainResultOpts') -> bool: """Return `true` when self and other are not equal, false otherwise.""" return not self == other -class GeoIndexStats(): + +class FindResult: """ - Schema for geospatial index statistics. + Schema for the result of a query find operation. - :attr int data_size: The size of the geospatial index, in bytes. - :attr int disk_size: The size of the geospatial index, as stored on disk, in - bytes. - :attr int doc_count: Number of documents in the geospatial index. + :param str bookmark: Opaque bookmark token used when paginating results. + :param List[Document] docs: Documents matching the selector. + :param ExecutionStats execution_stats: (optional) Schema for find query + execution statistics. + :param str warning: (optional) warning. """ - def __init__(self, - data_size: int, - disk_size: int, - doc_count: int) -> None: + def __init__( + self, + bookmark: str, + docs: List['Document'], + *, + execution_stats: Optional['ExecutionStats'] = None, + warning: Optional[str] = None, + ) -> None: """ - Initialize a GeoIndexStats object. + Initialize a FindResult object. - :param int data_size: The size of the geospatial index, in bytes. - :param int disk_size: The size of the geospatial index, as stored on disk, - in bytes. - :param int doc_count: Number of documents in the geospatial index. + :param str bookmark: Opaque bookmark token used when paginating results. + :param List[Document] docs: Documents matching the selector. + :param ExecutionStats execution_stats: (optional) Schema for find query + execution statistics. + :param str warning: (optional) warning. """ - self.data_size = data_size - self.disk_size = disk_size - self.doc_count = doc_count + self.bookmark = bookmark + self.docs = docs + self.execution_stats = execution_stats + self.warning = warning @classmethod - def from_dict(cls, _dict: Dict) -> 'GeoIndexStats': - """Initialize a GeoIndexStats object from a json dictionary.""" + def from_dict(cls, _dict: Dict) -> 'FindResult': + """Initialize a FindResult object from a json dictionary.""" args = {} - if 'data_size' in _dict: - args['data_size'] = _dict.get('data_size') - else: - raise ValueError('Required property \'data_size\' not present in GeoIndexStats JSON') - if 'disk_size' in _dict: - args['disk_size'] = _dict.get('disk_size') + if (bookmark := _dict.get('bookmark')) is not None: + args['bookmark'] = bookmark else: - raise ValueError('Required property \'disk_size\' not present in GeoIndexStats JSON') - if 'doc_count' in _dict: - args['doc_count'] = _dict.get('doc_count') + raise ValueError('Required property \'bookmark\' not present in FindResult JSON') + if (docs := _dict.get('docs')) is not None: + args['docs'] = [Document.from_dict(v) for v in docs] else: - raise ValueError('Required property \'doc_count\' not present in GeoIndexStats JSON') + raise ValueError('Required property \'docs\' not present in FindResult JSON') + if (execution_stats := _dict.get('execution_stats')) is not None: + args['execution_stats'] = ExecutionStats.from_dict(execution_stats) + if (warning := _dict.get('warning')) is not None: + args['warning'] = warning return cls(**args) @classmethod def _from_dict(cls, _dict): - """Initialize a GeoIndexStats object from a json dictionary.""" + """Initialize a FindResult object from a json dictionary.""" return cls.from_dict(_dict) def to_dict(self) -> Dict: """Return a json dictionary representing this model.""" _dict = {} - if hasattr(self, 'data_size') and self.data_size is not None: - _dict['data_size'] = self.data_size - if hasattr(self, 'disk_size') and self.disk_size is not None: - _dict['disk_size'] = self.disk_size - if hasattr(self, 'doc_count') and self.doc_count is not None: - _dict['doc_count'] = self.doc_count + if hasattr(self, 'bookmark') and self.bookmark is not None: + _dict['bookmark'] = self.bookmark + if hasattr(self, 'docs') and self.docs is not None: + docs_list = [] + for v in self.docs: + if isinstance(v, dict): + docs_list.append(v) + else: + docs_list.append(v.to_dict()) + _dict['docs'] = docs_list + if hasattr(self, 'execution_stats') and self.execution_stats is not None: + if isinstance(self.execution_stats, dict): + _dict['execution_stats'] = self.execution_stats + else: + _dict['execution_stats'] = self.execution_stats.to_dict() + if hasattr(self, 'warning') and self.warning is not None: + _dict['warning'] = self.warning return _dict def _to_dict(self): @@ -12643,247 +14428,199 @@ def _to_dict(self): return self.to_dict() def __str__(self) -> str: - """Return a `str` version of this GeoIndexStats object.""" + """Return a `str` version of this FindResult object.""" return json.dumps(self.to_dict(), indent=2) - def __eq__(self, other: 'GeoIndexStats') -> bool: + def __eq__(self, other: 'FindResult') -> bool: """Return `true` when self and other are equal, false otherwise.""" if not isinstance(other, self.__class__): return False return self.__dict__ == other.__dict__ - def __ne__(self, other: 'GeoIndexStats') -> bool: + def __ne__(self, other: 'FindResult') -> bool: """Return `true` when self and other are not equal, false otherwise.""" return not self == other -class GeoJsonFeature(): + +class IndexAnalysis: """ - Schema for a GeoJSON feature object. Note that the content of the feature objects - varies depending on the response format chosen and whether the `include_docs` - parameter is `true`. - - :attr str id: (optional) Schema for a document ID. - :attr str rev: (optional) Schema for a document revision identifier. - :attr List[float] bbox: (optional) Schema for a GeoJSON bounding box. - :attr GeoJsonGeometryObject geometry: Schema for a GeoJSON geometry object. - :attr dict properties: (optional) Schema for the properties of a GeoJSON feature - object. - :attr str type: Declaration of the GeoJSON type: Feature Object. + Schema for detailed explanation of why the specific index was excluded by the query + planner. + + :param bool covering: When `true`, the query is answered using the index only + and no documents are fetched. + :param int ranking: A position of the unused index based on its potential + relevance to the query. + :param List[IndexAnalysisExclusionReason] reasons: A list of reasons explaining + why index was not chosen for the query. + :param bool usable: Indicates whether an index can still be used for the query. """ - # The set of defined properties for the class - _properties = frozenset(['id', '_id', 'rev', '_rev', 'bbox', 'geometry', 'properties', 'type']) - - def __init__(self, - geometry: 'GeoJsonGeometryObject', - type: str, - *, - id: str = None, - rev: str = None, - bbox: List[float] = None, - properties: dict = None, - **kwargs) -> None: - """ - Initialize a GeoJsonFeature object. - - :param GeoJsonGeometryObject geometry: Schema for a GeoJSON geometry - object. - :param str type: Declaration of the GeoJSON type: Feature Object. - :param str id: (optional) Schema for a document ID. - :param str rev: (optional) Schema for a document revision identifier. - :param List[float] bbox: (optional) Schema for a GeoJSON bounding box. - :param dict properties: (optional) Schema for the properties of a GeoJSON - feature object. - :param **kwargs: (optional) Any additional properties. + def __init__( + self, + covering: bool, + ranking: int, + reasons: List['IndexAnalysisExclusionReason'], + usable: bool, + ) -> None: + """ + Initialize a IndexAnalysis object. + + :param bool covering: When `true`, the query is answered using the index + only and no documents are fetched. + :param int ranking: A position of the unused index based on its potential + relevance to the query. + :param List[IndexAnalysisExclusionReason] reasons: A list of reasons + explaining why index was not chosen for the query. + :param bool usable: Indicates whether an index can still be used for the + query. """ - self.id = id - self.rev = rev - self.bbox = bbox - self.geometry = geometry - self.properties = properties - self.type = type - for _key, _value in kwargs.items(): - setattr(self, _key, _value) + self.covering = covering + self.ranking = ranking + self.reasons = reasons + self.usable = usable @classmethod - def from_dict(cls, _dict: Dict) -> 'GeoJsonFeature': - """Initialize a GeoJsonFeature object from a json dictionary.""" + def from_dict(cls, _dict: Dict) -> 'IndexAnalysis': + """Initialize a IndexAnalysis object from a json dictionary.""" args = {} - if '_id' in _dict: - args['id'] = _dict.get('_id') - if '_rev' in _dict: - args['rev'] = _dict.get('_rev') - if 'bbox' in _dict: - args['bbox'] = _dict.get('bbox') - if 'geometry' in _dict: - args['geometry'] = _dict.get('geometry') + if (covering := _dict.get('covering')) is not None: + args['covering'] = covering + else: + args['covering'] = None + if (ranking := _dict.get('ranking')) is not None: + args['ranking'] = ranking else: - raise ValueError('Required property \'geometry\' not present in GeoJsonFeature JSON') - if 'properties' in _dict: - args['properties'] = _dict.get('properties') - if 'type' in _dict: - args['type'] = _dict.get('type') + raise ValueError('Required property \'ranking\' not present in IndexAnalysis JSON') + if (reasons := _dict.get('reasons')) is not None: + args['reasons'] = [IndexAnalysisExclusionReason.from_dict(v) for v in reasons] else: - raise ValueError('Required property \'type\' not present in GeoJsonFeature JSON') - args.update({k:v for (k, v) in _dict.items() if k not in cls._properties}) + raise ValueError('Required property \'reasons\' not present in IndexAnalysis JSON') + if (usable := _dict.get('usable')) is not None: + args['usable'] = usable + else: + raise ValueError('Required property \'usable\' not present in IndexAnalysis JSON') return cls(**args) @classmethod def _from_dict(cls, _dict): - """Initialize a GeoJsonFeature object from a json dictionary.""" + """Initialize a IndexAnalysis object from a json dictionary.""" return cls.from_dict(_dict) def to_dict(self) -> Dict: """Return a json dictionary representing this model.""" _dict = {} - if hasattr(self, 'id') and self.id is not None: - _dict['_id'] = self.id - if hasattr(self, 'rev') and self.rev is not None: - _dict['_rev'] = self.rev - if hasattr(self, 'bbox') and self.bbox is not None: - _dict['bbox'] = self.bbox - if hasattr(self, 'geometry') and self.geometry is not None: - if isinstance(self.geometry, dict): - _dict['geometry'] = self.geometry - else: - _dict['geometry'] = self.geometry.to_dict() - if hasattr(self, 'properties') and self.properties is not None: - _dict['properties'] = self.properties - if hasattr(self, 'type') and self.type is not None: - _dict['type'] = self.type - for _key in [k for k in vars(self).keys() if k not in GeoJsonFeature._properties]: - if getattr(self, _key, None) is not None: - _dict[_key] = getattr(self, _key) + if hasattr(self, 'covering') and self.covering is not None: + _dict['covering'] = self.covering + if hasattr(self, 'ranking') and self.ranking is not None: + _dict['ranking'] = self.ranking + if hasattr(self, 'reasons') and self.reasons is not None: + reasons_list = [] + for v in self.reasons: + if isinstance(v, dict): + reasons_list.append(v) + else: + reasons_list.append(v.to_dict()) + _dict['reasons'] = reasons_list + if hasattr(self, 'usable') and self.usable is not None: + _dict['usable'] = self.usable return _dict def _to_dict(self): """Return a json dictionary representing this model.""" return self.to_dict() - def get_properties(self) -> Dict: - """Return a dictionary of arbitrary properties from this instance of GeoJsonFeature""" - _dict = {} - - for _key in [k for k in vars(self).keys() if k not in GeoJsonFeature._properties]: - _dict[_key] = getattr(self, _key) - return _dict - - def set_properties(self, _dict: dict): - """Set a dictionary of arbitrary properties to this instance of GeoJsonFeature""" - for _key in [k for k in vars(self).keys() if k not in GeoJsonFeature._properties]: - delattr(self, _key) - - for _key, _value in _dict.items(): - if _key not in GeoJsonFeature._properties: - setattr(self, _key, _value) - def __str__(self) -> str: - """Return a `str` version of this GeoJsonFeature object.""" + """Return a `str` version of this IndexAnalysis object.""" return json.dumps(self.to_dict(), indent=2) - def __eq__(self, other: 'GeoJsonFeature') -> bool: + def __eq__(self, other: 'IndexAnalysis') -> bool: """Return `true` when self and other are equal, false otherwise.""" if not isinstance(other, self.__class__): return False return self.__dict__ == other.__dict__ - def __ne__(self, other: 'GeoJsonFeature') -> bool: + def __ne__(self, other: 'IndexAnalysis') -> bool: """Return `true` when self and other are not equal, false otherwise.""" return not self == other - class TypeEnum(str, Enum): - """ - Declaration of the GeoJSON type: Feature Object. - """ - FEATURE = 'Feature' - - -class GeoJsonGeometryObject(): - """ - Schema for a GeoJSON geometry object. - - """ - - def __init__(self) -> None: - """ - Initialize a GeoJsonGeometryObject object. - - """ - msg = "Cannot instantiate base class. Instead, instantiate one of the defined subclasses: {0}".format( - ", ".join(['GeoJsonGeometry', 'GeoJsonGeometryCollection'])) - raise Exception(msg) -class GeoResult(): +class IndexAnalysisExclusionReason: """ - Schema for the result of a geospatial query operation. For the `legacy`, `geojson`, or - `application/vnd.geo+json` format this is a GeoJson FeatureCollection with additional - metadata in foreign members. - - :attr str bookmark: Opaque bookmark token used when paginating results. - :attr List[GeoJsonFeature] features: (optional) The array of GeoJSON Feature - Objects matching the geospatial query. - :attr List[GeoResultRow] rows: The array of rows matching the geospatial query. - Present only when using `view` format. - :attr str type: (optional) Declaration of the GeoJSON type: FeatureCollection - Object. + A reason for index's exclusion. + + :param str name: A reason code for index's exclusion. + The full list of possible reason codes is following: + * alphabetically_comes_after: json + There is another suitable index whose name comes before that of + this index. + * empty_selector: text + "text" indexes do not support queries with empty selectors. + * excluded_by_user: any use_index was used to manually specify the index. + * field_mismatch: any Fields in "selector" of the query do match with the fields + available in the index. + * is_partial: json, text Partial indexes can be selected only manually. + * less_overlap: json There is a better match of fields available within the + indexes for the query. + * needs_text_search: json The use of the $text operator requires a "text" index. + * scope_mismatch: json The scope of the query and the index is not the same. + * sort_order_mismatch: json, special Fields in "sort" of the query do not match + with the fields available in the index. + * too_many_fields: json The index has more fields than the chosen one. + * unfavored_type: any The type of the index is not preferred. """ - def __init__(self, - bookmark: str, - rows: List['GeoResultRow'], - *, - features: List['GeoJsonFeature'] = None, - type: str = None) -> None: - """ - Initialize a GeoResult object. - - :param str bookmark: Opaque bookmark token used when paginating results. - :param List[GeoResultRow] rows: The array of rows matching the geospatial - query. Present only when using `view` format. - :param List[GeoJsonFeature] features: (optional) The array of GeoJSON - Feature Objects matching the geospatial query. - :param str type: (optional) Declaration of the GeoJSON type: - FeatureCollection Object. + def __init__( + self, + name: str, + ) -> None: + """ + Initialize a IndexAnalysisExclusionReason object. + + :param str name: A reason code for index's exclusion. + The full list of possible reason codes is following: + * alphabetically_comes_after: json + There is another suitable index whose name comes before that of + this index. + * empty_selector: text + "text" indexes do not support queries with empty selectors. + * excluded_by_user: any use_index was used to manually specify the index. + * field_mismatch: any Fields in "selector" of the query do match with the + fields available in the index. + * is_partial: json, text Partial indexes can be selected only manually. + * less_overlap: json There is a better match of fields available within the + indexes for the query. + * needs_text_search: json The use of the $text operator requires a "text" + index. + * scope_mismatch: json The scope of the query and the index is not the + same. + * sort_order_mismatch: json, special Fields in "sort" of the query do not + match with the fields available in the index. + * too_many_fields: json The index has more fields than the chosen one. + * unfavored_type: any The type of the index is not preferred. """ - self.bookmark = bookmark - self.features = features - self.rows = rows - self.type = type + self.name = name @classmethod - def from_dict(cls, _dict: Dict) -> 'GeoResult': - """Initialize a GeoResult object from a json dictionary.""" + def from_dict(cls, _dict: Dict) -> 'IndexAnalysisExclusionReason': + """Initialize a IndexAnalysisExclusionReason object from a json dictionary.""" args = {} - if 'bookmark' in _dict: - args['bookmark'] = _dict.get('bookmark') - else: - raise ValueError('Required property \'bookmark\' not present in GeoResult JSON') - if 'features' in _dict: - args['features'] = [GeoJsonFeature.from_dict(x) for x in _dict.get('features')] - if 'rows' in _dict: - args['rows'] = [GeoResultRow.from_dict(x) for x in _dict.get('rows')] + if (name := _dict.get('name')) is not None: + args['name'] = name else: - raise ValueError('Required property \'rows\' not present in GeoResult JSON') - if 'type' in _dict: - args['type'] = _dict.get('type') + raise ValueError('Required property \'name\' not present in IndexAnalysisExclusionReason JSON') return cls(**args) @classmethod def _from_dict(cls, _dict): - """Initialize a GeoResult object from a json dictionary.""" + """Initialize a IndexAnalysisExclusionReason object from a json dictionary.""" return cls.from_dict(_dict) def to_dict(self) -> Dict: """Return a json dictionary representing this model.""" _dict = {} - if hasattr(self, 'bookmark') and self.bookmark is not None: - _dict['bookmark'] = self.bookmark - if hasattr(self, 'features') and self.features is not None: - _dict['features'] = [x.to_dict() for x in self.features] - if hasattr(self, 'rows') and self.rows is not None: - _dict['rows'] = [x.to_dict() for x in self.rows] - if hasattr(self, 'type') and self.type is not None: - _dict['type'] = self.type + if hasattr(self, 'name') and self.name is not None: + _dict['name'] = self.name return _dict def _to_dict(self): @@ -12891,85 +14628,113 @@ def _to_dict(self): return self.to_dict() def __str__(self) -> str: - """Return a `str` version of this GeoResult object.""" + """Return a `str` version of this IndexAnalysisExclusionReason object.""" return json.dumps(self.to_dict(), indent=2) - def __eq__(self, other: 'GeoResult') -> bool: + def __eq__(self, other: 'IndexAnalysisExclusionReason') -> bool: """Return `true` when self and other are equal, false otherwise.""" if not isinstance(other, self.__class__): return False return self.__dict__ == other.__dict__ - def __ne__(self, other: 'GeoResult') -> bool: + def __ne__(self, other: 'IndexAnalysisExclusionReason') -> bool: """Return `true` when self and other are not equal, false otherwise.""" return not self == other - class TypeEnum(str, Enum): - """ - Declaration of the GeoJSON type: FeatureCollection Object. + class NameEnum(str, Enum): """ - FEATURECOLLECTION = 'FeatureCollection' - - -class GeoResultRow(): + A reason code for index's exclusion. + The full list of possible reason codes is following: + * alphabetically_comes_after: json + There is another suitable index whose name comes before that of + this index. + * empty_selector: text + "text" indexes do not support queries with empty selectors. + * excluded_by_user: any use_index was used to manually specify the index. + * field_mismatch: any Fields in "selector" of the query do match with the fields + available in the index. + * is_partial: json, text Partial indexes can be selected only manually. + * less_overlap: json There is a better match of fields available within the + indexes for the query. + * needs_text_search: json The use of the $text operator requires a "text" index. + * scope_mismatch: json The scope of the query and the index is not the same. + * sort_order_mismatch: json, special Fields in "sort" of the query do not match + with the fields available in the index. + * too_many_fields: json The index has more fields than the chosen one. + * unfavored_type: any The type of the index is not preferred. + """ + + ALPHABETICALLY_COMES_AFTER = 'alphabetically_comes_after' + EMPTY_SELECTOR = 'empty_selector' + EXCLUDED_BY_USER = 'excluded_by_user' + FIELD_MISMATCH = 'field_mismatch' + IS_PARTIAL = 'is_partial' + LESS_OVERLAP = 'less_overlap' + NEEDS_TEXT_SEARCH = 'needs_text_search' + SCOPE_MISMATCH = 'scope_mismatch' + SORT_ORDER_MISMATCH = 'sort_order_mismatch' + TOO_MANY_FIELDS = 'too_many_fields' + UNFAVORED_TYPE = 'unfavored_type' + + + +class IndexCandidate: """ - Schema for a row of a geospatial result using view format. + Schema for an index that was not chosen for serving the query with the reason for the + exclusion. - :attr Document doc: (optional) Schema for a document. - :attr GeoJsonGeometry geometry: (optional) Schema for a GeoJSON geometry. - :attr str id: (optional) Schema for a document ID. - :attr str rev: (optional) Schema for a document revision identifier. + :param IndexAnalysis analysis: Schema for detailed explanation of why the + specific index was excluded by the query planner. + :param IndexInformation index: Schema for information about an index. """ - def __init__(self, - *, - doc: 'Document' = None, - geometry: 'GeoJsonGeometry' = None, - id: str = None, - rev: str = None) -> None: + def __init__( + self, + analysis: 'IndexAnalysis', + index: 'IndexInformation', + ) -> None: """ - Initialize a GeoResultRow object. + Initialize a IndexCandidate object. - :param Document doc: (optional) Schema for a document. - :param GeoJsonGeometry geometry: (optional) Schema for a GeoJSON geometry. - :param str id: (optional) Schema for a document ID. - :param str rev: (optional) Schema for a document revision identifier. + :param IndexAnalysis analysis: Schema for detailed explanation of why the + specific index was excluded by the query planner. + :param IndexInformation index: Schema for information about an index. """ - self.doc = doc - self.geometry = geometry - self.id = id - self.rev = rev + self.analysis = analysis + self.index = index @classmethod - def from_dict(cls, _dict: Dict) -> 'GeoResultRow': - """Initialize a GeoResultRow object from a json dictionary.""" + def from_dict(cls, _dict: Dict) -> 'IndexCandidate': + """Initialize a IndexCandidate object from a json dictionary.""" args = {} - if 'doc' in _dict: - args['doc'] = Document.from_dict(_dict.get('doc')) - if 'geometry' in _dict: - args['geometry'] = GeoJsonGeometry.from_dict(_dict.get('geometry')) - if 'id' in _dict: - args['id'] = _dict.get('id') - if 'rev' in _dict: - args['rev'] = _dict.get('rev') + if (analysis := _dict.get('analysis')) is not None: + args['analysis'] = IndexAnalysis.from_dict(analysis) + else: + raise ValueError('Required property \'analysis\' not present in IndexCandidate JSON') + if (index := _dict.get('index')) is not None: + args['index'] = IndexInformation.from_dict(index) + else: + raise ValueError('Required property \'index\' not present in IndexCandidate JSON') return cls(**args) @classmethod def _from_dict(cls, _dict): - """Initialize a GeoResultRow object from a json dictionary.""" + """Initialize a IndexCandidate object from a json dictionary.""" return cls.from_dict(_dict) def to_dict(self) -> Dict: """Return a json dictionary representing this model.""" _dict = {} - if hasattr(self, 'doc') and self.doc is not None: - _dict['doc'] = self.doc.to_dict() - if hasattr(self, 'geometry') and self.geometry is not None: - _dict['geometry'] = self.geometry.to_dict() - if hasattr(self, 'id') and self.id is not None: - _dict['id'] = self.id - if hasattr(self, 'rev') and self.rev is not None: - _dict['rev'] = self.rev + if hasattr(self, 'analysis') and self.analysis is not None: + if isinstance(self.analysis, dict): + _dict['analysis'] = self.analysis + else: + _dict['analysis'] = self.analysis.to_dict() + if hasattr(self, 'index') and self.index is not None: + if isinstance(self.index, dict): + _dict['index'] = self.index + else: + _dict['index'] = self.index.to_dict() return _dict def _to_dict(self): @@ -12977,45 +14742,48 @@ def _to_dict(self): return self.to_dict() def __str__(self) -> str: - """Return a `str` version of this GeoResultRow object.""" + """Return a `str` version of this IndexCandidate object.""" return json.dumps(self.to_dict(), indent=2) - def __eq__(self, other: 'GeoResultRow') -> bool: + def __eq__(self, other: 'IndexCandidate') -> bool: """Return `true` when self and other are equal, false otherwise.""" if not isinstance(other, self.__class__): return False return self.__dict__ == other.__dict__ - def __ne__(self, other: 'GeoResultRow') -> bool: + def __ne__(self, other: 'IndexCandidate') -> bool: """Return `true` when self and other are not equal, false otherwise.""" return not self == other -class IndexDefinition(): + +class IndexDefinition: """ Schema for a `json` or `text` query index definition. Indexes of type `text` have additional configuration properties that do not apply to `json` indexes, these are: - * `default_analyzer` - the default text analyzer to use * `default_field` - whether to - index the text in all document fields and what analyzer to use for that purpose. + * `default_analyzer` - the default text analyzer to use + * `default_field` - whether to index the text in all document fields and what analyzer + to use for that purpose. - :attr Analyzer default_analyzer: (optional) Schema for a full text search + :param Analyzer default_analyzer: (optional) Schema for a full text search analyzer. - :attr IndexTextOperatorDefaultField default_field: (optional) Schema for the + :param IndexTextOperatorDefaultField default_field: (optional) Schema for the text index default field configuration. The default field is used to index the text of all fields within a document for use with the `$text` operator. - :attr List[IndexField] fields: (optional) List of field objects to index. - Nested fields are also allowed, e.g. `person.name`. + :param List[IndexField] fields: List of field objects to index. Nested fields + are also allowed, e.g. `person.name`. For "json" type indexes each object is a mapping of field name to sort direction (asc or desc). For "text" type indexes each object has a `name` property of the field name and a `type` property of the field type (string, number, or boolean). - :attr bool index_array_lengths: (optional) Whether to scan every document for + :param bool index_array_lengths: (optional) Whether to scan every document for arrays and store the length for each array found. Set the index_array_lengths field to false if: - * You do not need to know the length of an array. * You do not use the `$size` - operator. * The documents in your database are complex, or not completely under - your control. As a result, it is difficult to estimate the impact of the extra + * You do not need to know the length of an array. + * You do not use the `$size` operator. + * The documents in your database are complex, or not completely under your + control. As a result, it is difficult to estimate the impact of the extra processing that is needed to determine and store the arrays lengths. - :attr dict partial_filter_selector: (optional) JSON object describing criteria + :param dict partial_filter_selector: (optional) JSON object describing criteria used to select documents. The selector specifies fields in the document, and provides an expression to evaluate with the field content or other data. The selector object must: @@ -13031,51 +14799,58 @@ class IndexDefinition(): field. There are two core types of operators in the selector syntax: * Combination operators: applied at the topmost level of selection. They are - used to combine selectors. In addition to the common boolean operators (`$and`, - `$or`, `$not`, `$nor`) there are three combination operators: `$all`, - `$elemMatch`, and `$allMatch`. A combination operator takes a single argument. - The argument is either another selector, or an array of selectors. + used to combine selectors. A combination operator takes a single argument. The + argument is either another selector, or an array of selectors. * Condition operators: are specific to a field, and are used to evaluate the value stored in that field. For instance, the basic `$eq` operator matches when the specified field contains a value that is equal to the supplied argument. - * Only equality operators such as `$eq`, `$gt`, `$gte`, `$lt`, and `$lte` (but - not `$ne`) can be used as the basis of a query. You should include at least one - of these in a selector. - For further reference see - [selector - syntax](https://cloud.ibm.com/docs/Cloudant?topic=Cloudant-query#selector-syntax). + It is important for query performance to use appropriate selectors: + * Only equality operators such as `$eq`, `$gt`, `$gte`, `$lt`, and + `$lte` (but not `$ne`) can be used as the basis of a query. You should include + at least one of these in a selector. + * Some operators such as `$not`, `$or`, `$in`, and `$regex` cannot be answered + from an index. For query selectors use these operators in conjunction with + equality operators or create and use a partial index to reduce the number of + documents that will need to be scanned. + See [the Cloudant + Docs](https://cloud.ibm.com/docs/Cloudant?topic=Cloudant-operators) for a list + of all available combination and conditional operators. + For further reference see [selector + syntax](https://cloud.ibm.com/docs/Cloudant?topic=Cloudant-selector-syntax). """ - def __init__(self, - *, - default_analyzer: 'Analyzer' = None, - default_field: 'IndexTextOperatorDefaultField' = None, - fields: List['IndexField'] = None, - index_array_lengths: bool = None, - partial_filter_selector: dict = None) -> None: + def __init__( + self, + fields: List['IndexField'], + *, + default_analyzer: Optional['Analyzer'] = None, + default_field: Optional['IndexTextOperatorDefaultField'] = None, + index_array_lengths: Optional[bool] = None, + partial_filter_selector: Optional[dict] = None, + ) -> None: """ Initialize a IndexDefinition object. + :param List[IndexField] fields: List of field objects to index. Nested + fields are also allowed, e.g. `person.name`. + For "json" type indexes each object is a mapping of field name to sort + direction (asc or desc). + For "text" type indexes each object has a `name` property of the field name + and a `type` property of the field type (string, number, or boolean). :param Analyzer default_analyzer: (optional) Schema for a full text search analyzer. :param IndexTextOperatorDefaultField default_field: (optional) Schema for the text index default field configuration. The default field is used to index the text of all fields within a document for use with the `$text` operator. - :param List[IndexField] fields: (optional) List of field objects to index. - Nested fields are also allowed, e.g. `person.name`. - For "json" type indexes each object is a mapping of field name to sort - direction (asc or desc). - For "text" type indexes each object has a `name` property of the field name - and a `type` property of the field type (string, number, or boolean). :param bool index_array_lengths: (optional) Whether to scan every document for arrays and store the length for each array found. Set the index_array_lengths field to false if: - * You do not need to know the length of an array. * You do not use the - `$size` operator. * The documents in your database are complex, or not - completely under your control. As a result, it is difficult to estimate the - impact of the extra processing that is needed to determine and store the - arrays lengths. + * You do not need to know the length of an array. + * You do not use the `$size` operator. + * The documents in your database are complex, or not completely under your + control. As a result, it is difficult to estimate the impact of the extra + processing that is needed to determine and store the arrays lengths. :param dict partial_filter_selector: (optional) JSON object describing criteria used to select documents. The selector specifies fields in the document, and provides an expression to evaluate with the field content or @@ -13093,21 +14868,26 @@ def __init__(self, field. There are two core types of operators in the selector syntax: * Combination operators: applied at the topmost level of selection. They - are used to combine selectors. In addition to the common boolean operators - (`$and`, `$or`, `$not`, `$nor`) there are three combination operators: - `$all`, `$elemMatch`, and `$allMatch`. A combination operator takes a - single argument. The argument is either another selector, or an array of + are used to combine selectors. A combination operator takes a single + argument. The argument is either another selector, or an array of selectors. * Condition operators: are specific to a field, and are used to evaluate the value stored in that field. For instance, the basic `$eq` operator matches when the specified field contains a value that is equal to the supplied argument. - * Only equality operators such as `$eq`, `$gt`, `$gte`, `$lt`, and `$lte` - (but not `$ne`) can be used as the basis of a query. You should include at - least one of these in a selector. - For further reference see - [selector - syntax](https://cloud.ibm.com/docs/Cloudant?topic=Cloudant-query#selector-syntax). + It is important for query performance to use appropriate selectors: + * Only equality operators such as `$eq`, `$gt`, `$gte`, `$lt`, and + `$lte` (but not `$ne`) can be used as the basis of a query. You should + include at least one of these in a selector. + * Some operators such as `$not`, `$or`, `$in`, and `$regex` cannot be + answered from an index. For query selectors use these operators in + conjunction with equality operators or create and use a partial index to + reduce the number of documents that will need to be scanned. + See [the Cloudant + Docs](https://cloud.ibm.com/docs/Cloudant?topic=Cloudant-operators) for a + list of all available combination and conditional operators. + For further reference see [selector + syntax](https://cloud.ibm.com/docs/Cloudant?topic=Cloudant-selector-syntax). """ self.default_analyzer = default_analyzer self.default_field = default_field @@ -13119,16 +14899,18 @@ def __init__(self, def from_dict(cls, _dict: Dict) -> 'IndexDefinition': """Initialize a IndexDefinition object from a json dictionary.""" args = {} - if 'default_analyzer' in _dict: - args['default_analyzer'] = Analyzer.from_dict(_dict.get('default_analyzer')) - if 'default_field' in _dict: - args['default_field'] = IndexTextOperatorDefaultField.from_dict(_dict.get('default_field')) - if 'fields' in _dict: - args['fields'] = [IndexField.from_dict(x) for x in _dict.get('fields')] - if 'index_array_lengths' in _dict: - args['index_array_lengths'] = _dict.get('index_array_lengths') - if 'partial_filter_selector' in _dict: - args['partial_filter_selector'] = _dict.get('partial_filter_selector') + if (default_analyzer := _dict.get('default_analyzer')) is not None: + args['default_analyzer'] = Analyzer.from_dict(default_analyzer) + if (default_field := _dict.get('default_field')) is not None: + args['default_field'] = IndexTextOperatorDefaultField.from_dict(default_field) + if (fields := _dict.get('fields')) is not None: + args['fields'] = [IndexField.from_dict(v) for v in fields] + else: + raise ValueError('Required property \'fields\' not present in IndexDefinition JSON') + if (index_array_lengths := _dict.get('index_array_lengths')) is not None: + args['index_array_lengths'] = index_array_lengths + if (partial_filter_selector := _dict.get('partial_filter_selector')) is not None: + args['partial_filter_selector'] = partial_filter_selector return cls(**args) @classmethod @@ -13140,11 +14922,23 @@ def to_dict(self) -> Dict: """Return a json dictionary representing this model.""" _dict = {} if hasattr(self, 'default_analyzer') and self.default_analyzer is not None: - _dict['default_analyzer'] = self.default_analyzer.to_dict() + if isinstance(self.default_analyzer, dict): + _dict['default_analyzer'] = self.default_analyzer + else: + _dict['default_analyzer'] = self.default_analyzer.to_dict() if hasattr(self, 'default_field') and self.default_field is not None: - _dict['default_field'] = self.default_field.to_dict() + if isinstance(self.default_field, dict): + _dict['default_field'] = self.default_field + else: + _dict['default_field'] = self.default_field.to_dict() if hasattr(self, 'fields') and self.fields is not None: - _dict['fields'] = [x.to_dict() for x in self.fields] + fields_list = [] + for v in self.fields: + if isinstance(v, dict): + fields_list.append(v) + else: + fields_list.append(v.to_dict()) + _dict['fields'] = fields_list if hasattr(self, 'index_array_lengths') and self.index_array_lengths is not None: _dict['index_array_lengths'] = self.index_array_lengths if hasattr(self, 'partial_filter_selector') and self.partial_filter_selector is not None: @@ -13169,43 +14963,57 @@ def __ne__(self, other: 'IndexDefinition') -> bool: """Return `true` when self and other are not equal, false otherwise.""" return not self == other -class IndexField(): + +class IndexField: """ Schema for indexed fields for use with declarative JSON query. - :attr str name: (optional) Name of the field. - :attr str type: (optional) The type of the named field. + :param str name: (optional) Name of the field. + :param str type: (optional) The type of the named field. + + This type supports additional properties of type str. Schema for sort direction. """ # The set of defined properties for the class _properties = frozenset(['name', 'type']) - def __init__(self, - *, - name: str = None, - type: str = None, - **kwargs) -> None: + def __init__( + self, + *, + name: Optional[str] = None, + type: Optional[str] = None, + **kwargs: Optional[str], + ) -> None: """ Initialize a IndexField object. :param str name: (optional) Name of the field. :param str type: (optional) The type of the named field. - :param **kwargs: (optional) Any additional properties. + :param str **kwargs: (optional) Schema for sort direction. """ self.name = name self.type = type - for _key, _value in kwargs.items(): - setattr(self, _key, _value) + for k, v in kwargs.items(): + if k not in IndexField._properties: + if not isinstance(v, str): + raise ValueError('Value for additional property {} must be of type str'.format(k)) + setattr(self, k, v) + else: + raise ValueError('Property {} cannot be specified as an additional property'.format(k)) @classmethod def from_dict(cls, _dict: Dict) -> 'IndexField': """Initialize a IndexField object from a json dictionary.""" args = {} - if 'name' in _dict: - args['name'] = _dict.get('name') - if 'type' in _dict: - args['type'] = _dict.get('type') - args.update({k:v for (k, v) in _dict.items() if k not in cls._properties}) + if (name := _dict.get('name')) is not None: + args['name'] = name + if (type := _dict.get('type')) is not None: + args['type'] = type + for k, v in _dict.items(): + if k not in cls._properties: + if not isinstance(v, str): + raise ValueError('Value for additional property {} must be of type str'.format(k)) + args[k] = v return cls(**args) @classmethod @@ -13220,9 +15028,8 @@ def to_dict(self) -> Dict: _dict['name'] = self.name if hasattr(self, 'type') and self.type is not None: _dict['type'] = self.type - for _key in [k for k in vars(self).keys() if k not in IndexField._properties]: - if getattr(self, _key, None) is not None: - _dict[_key] = getattr(self, _key) + for k in [_k for _k in vars(self).keys() if _k not in IndexField._properties]: + _dict[k] = getattr(self, k) return _dict def _to_dict(self): @@ -13230,21 +15037,23 @@ def _to_dict(self): return self.to_dict() def get_properties(self) -> Dict: - """Return a dictionary of arbitrary properties from this instance of IndexField""" + """Return the additional properties from this instance of IndexField in the form of a dict.""" _dict = {} - - for _key in [k for k in vars(self).keys() if k not in IndexField._properties]: - _dict[_key] = getattr(self, _key) + for k in [_k for _k in vars(self).keys() if _k not in IndexField._properties]: + _dict[k] = getattr(self, k) return _dict def set_properties(self, _dict: dict): - """Set a dictionary of arbitrary properties to this instance of IndexField""" - for _key in [k for k in vars(self).keys() if k not in IndexField._properties]: - delattr(self, _key) - - for _key, _value in _dict.items(): - if _key not in IndexField._properties: - setattr(self, _key, _value) + """Set a dictionary of additional properties in this instance of IndexField""" + for k in [_k for _k in vars(self).keys() if _k not in IndexField._properties]: + delattr(self, k) + for k, v in _dict.items(): + if k not in IndexField._properties: + if not isinstance(v, str): + raise ValueError('Value for additional property {} must be of type str'.format(k)) + setattr(self, k, v) + else: + raise ValueError('Property {} cannot be specified as an additional property'.format(k)) def __str__(self) -> str: """Return a `str` version of this IndexField object.""" @@ -13264,67 +15073,80 @@ class TypeEnum(str, Enum): """ The type of the named field. """ + BOOLEAN = 'boolean' NUMBER = 'number' STRING = 'string' -class IndexInformation(): + +class IndexInformation: """ Schema for information about an index. - :attr str ddoc: Design document ID. - :attr IndexDefinition def_: Schema for a `json` or `text` query index + :param str ddoc: Schema for a nullable design document ID including a `_design/` + prefix. + :param IndexDefinition def_: Schema for a `json` or `text` query index definition. Indexes of type `text` have additional configuration properties that do not apply to `json` indexes, these are: - * `default_analyzer` - the default text analyzer to use * `default_field` - - whether to index the text in all document fields and what analyzer to use for - that purpose. - :attr str name: Index name. - :attr str type: Schema for the type of an index. + * `default_analyzer` - the default text analyzer to use + * `default_field` - whether to index the text in all document fields and what + analyzer to use for that purpose. + :param str name: Index name. + :param bool partitioned: (optional) Indicates if index is partitioned. + :param str type: Schema for the type of an index. """ - def __init__(self, - ddoc: str, - def_: 'IndexDefinition', - name: str, - type: str) -> None: + def __init__( + self, + ddoc: str, + def_: 'IndexDefinition', + name: str, + type: str, + *, + partitioned: Optional[bool] = None, + ) -> None: """ Initialize a IndexInformation object. - :param str ddoc: Design document ID. + :param str ddoc: Schema for a nullable design document ID including a + `_design/` prefix. :param IndexDefinition def_: Schema for a `json` or `text` query index definition. Indexes of type `text` have additional configuration properties that do not apply to `json` indexes, these are: - * `default_analyzer` - the default text analyzer to use * `default_field` - - whether to index the text in all document fields and what analyzer to use - for that purpose. + * `default_analyzer` - the default text analyzer to use + * `default_field` - whether to index the text in all document fields and + what analyzer to use for that purpose. :param str name: Index name. :param str type: Schema for the type of an index. + :param bool partitioned: (optional) Indicates if index is partitioned. """ self.ddoc = ddoc self.def_ = def_ self.name = name + self.partitioned = partitioned self.type = type @classmethod def from_dict(cls, _dict: Dict) -> 'IndexInformation': """Initialize a IndexInformation object from a json dictionary.""" args = {} - if 'ddoc' in _dict: - args['ddoc'] = _dict.get('ddoc') + if (ddoc := _dict.get('ddoc')) is not None: + args['ddoc'] = ddoc else: - raise ValueError('Required property \'ddoc\' not present in IndexInformation JSON') - if 'def' in _dict: - args['def_'] = IndexDefinition.from_dict(_dict.get('def')) + args['ddoc'] = None + if (def_ := _dict.get('def')) is not None: + args['def_'] = IndexDefinition.from_dict(def_) else: raise ValueError('Required property \'def\' not present in IndexInformation JSON') - if 'name' in _dict: - args['name'] = _dict.get('name') + if (name := _dict.get('name')) is not None: + args['name'] = name else: raise ValueError('Required property \'name\' not present in IndexInformation JSON') - if 'type' in _dict: - args['type'] = _dict.get('type') + if (partitioned := _dict.get('partitioned')) is not None: + args['partitioned'] = partitioned + if (type := _dict.get('type')) is not None: + args['type'] = type else: raise ValueError('Required property \'type\' not present in IndexInformation JSON') return cls(**args) @@ -13340,9 +15162,14 @@ def to_dict(self) -> Dict: if hasattr(self, 'ddoc') and self.ddoc is not None: _dict['ddoc'] = self.ddoc if hasattr(self, 'def_') and self.def_ is not None: - _dict['def'] = self.def_.to_dict() + if isinstance(self.def_, dict): + _dict['def'] = self.def_ + else: + _dict['def'] = self.def_.to_dict() if hasattr(self, 'name') and self.name is not None: _dict['name'] = self.name + if hasattr(self, 'partitioned') and self.partitioned is not None: + _dict['partitioned'] = self.partitioned if hasattr(self, 'type') and self.type is not None: _dict['type'] = self.type return _dict @@ -13369,25 +15196,29 @@ class TypeEnum(str, Enum): """ Schema for the type of an index. """ + JSON = 'json' SPECIAL = 'special' TEXT = 'text' -class IndexResult(): + +class IndexResult: """ Schema for the result of creating an index. - :attr str id: Id of the design document the index was created in. - :attr str name: Name of the index created. - :attr str result: Flag to show whether the index was created or one already + :param str id: Id of the design document the index was created in. + :param str name: Name of the index created. + :param str result: Flag to show whether the index was created or one already exists. """ - def __init__(self, - id: str, - name: str, - result: str) -> None: + def __init__( + self, + id: str, + name: str, + result: str, + ) -> None: """ Initialize a IndexResult object. @@ -13404,16 +15235,16 @@ def __init__(self, def from_dict(cls, _dict: Dict) -> 'IndexResult': """Initialize a IndexResult object from a json dictionary.""" args = {} - if 'id' in _dict: - args['id'] = _dict.get('id') + if (id := _dict.get('id')) is not None: + args['id'] = id else: raise ValueError('Required property \'id\' not present in IndexResult JSON') - if 'name' in _dict: - args['name'] = _dict.get('name') + if (name := _dict.get('name')) is not None: + args['name'] = name else: raise ValueError('Required property \'name\' not present in IndexResult JSON') - if 'result' in _dict: - args['result'] = _dict.get('result') + if (result := _dict.get('result')) is not None: + args['result'] = result else: raise ValueError('Required property \'result\' not present in IndexResult JSON') return cls(**args) @@ -13456,23 +15287,27 @@ class ResultEnum(str, Enum): """ Flag to show whether the index was created or one already exists. """ + CREATED = 'created' EXISTS = 'exists' -class IndexTextOperatorDefaultField(): + +class IndexTextOperatorDefaultField: """ Schema for the text index default field configuration. The default field is used to index the text of all fields within a document for use with the `$text` operator. - :attr Analyzer analyzer: (optional) Schema for a full text search analyzer. - :attr bool enabled: (optional) Whether or not the default_field is enabled. + :param Analyzer analyzer: (optional) Schema for a full text search analyzer. + :param bool enabled: (optional) Whether or not the default_field is enabled. """ - def __init__(self, - *, - analyzer: 'Analyzer' = None, - enabled: bool = None) -> None: + def __init__( + self, + *, + analyzer: Optional['Analyzer'] = None, + enabled: Optional[bool] = None, + ) -> None: """ Initialize a IndexTextOperatorDefaultField object. @@ -13488,10 +15323,10 @@ def __init__(self, def from_dict(cls, _dict: Dict) -> 'IndexTextOperatorDefaultField': """Initialize a IndexTextOperatorDefaultField object from a json dictionary.""" args = {} - if 'analyzer' in _dict: - args['analyzer'] = Analyzer.from_dict(_dict.get('analyzer')) - if 'enabled' in _dict: - args['enabled'] = _dict.get('enabled') + if (analyzer := _dict.get('analyzer')) is not None: + args['analyzer'] = Analyzer.from_dict(analyzer) + if (enabled := _dict.get('enabled')) is not None: + args['enabled'] = enabled return cls(**args) @classmethod @@ -13503,7 +15338,10 @@ def to_dict(self) -> Dict: """Return a json dictionary representing this model.""" _dict = {} if hasattr(self, 'analyzer') and self.analyzer is not None: - _dict['analyzer'] = self.analyzer.to_dict() + if isinstance(self.analyzer, dict): + _dict['analyzer'] = self.analyzer + else: + _dict['analyzer'] = self.analyzer.to_dict() if hasattr(self, 'enabled') and self.enabled is not None: _dict['enabled'] = self.enabled return _dict @@ -13526,21 +15364,24 @@ def __ne__(self, other: 'IndexTextOperatorDefaultField') -> bool: """Return `true` when self and other are not equal, false otherwise.""" return not self == other -class IndexesInformation(): + +class IndexesInformation: """ Schema for information about the indexes in a database. - :attr int total_rows: Number of total rows. - :attr List[IndexInformation] indexes: Indexes. + :param int total_rows: Total number of query indexes in the database. + :param List[IndexInformation] indexes: Indexes. """ - def __init__(self, - total_rows: int, - indexes: List['IndexInformation']) -> None: + def __init__( + self, + total_rows: int, + indexes: List['IndexInformation'], + ) -> None: """ Initialize a IndexesInformation object. - :param int total_rows: Number of total rows. + :param int total_rows: Total number of query indexes in the database. :param List[IndexInformation] indexes: Indexes. """ self.total_rows = total_rows @@ -13550,12 +15391,12 @@ def __init__(self, def from_dict(cls, _dict: Dict) -> 'IndexesInformation': """Initialize a IndexesInformation object from a json dictionary.""" args = {} - if 'total_rows' in _dict: - args['total_rows'] = _dict.get('total_rows') + if (total_rows := _dict.get('total_rows')) is not None: + args['total_rows'] = total_rows else: raise ValueError('Required property \'total_rows\' not present in IndexesInformation JSON') - if 'indexes' in _dict: - args['indexes'] = [IndexInformation.from_dict(x) for x in _dict.get('indexes')] + if (indexes := _dict.get('indexes')) is not None: + args['indexes'] = [IndexInformation.from_dict(v) for v in indexes] else: raise ValueError('Required property \'indexes\' not present in IndexesInformation JSON') return cls(**args) @@ -13571,7 +15412,13 @@ def to_dict(self) -> Dict: if hasattr(self, 'total_rows') and self.total_rows is not None: _dict['total_rows'] = self.total_rows if hasattr(self, 'indexes') and self.indexes is not None: - _dict['indexes'] = [x.to_dict() for x in self.indexes] + indexes_list = [] + for v in self.indexes: + if isinstance(v, dict): + indexes_list.append(v) + else: + indexes_list.append(v.to_dict()) + _dict['indexes'] = indexes_list return _dict def _to_dict(self): @@ -13592,18 +15439,21 @@ def __ne__(self, other: 'IndexesInformation') -> bool: """Return `true` when self and other are not equal, false otherwise.""" return not self == other -class MembershipInformation(): + +class MembershipInformation: """ Schema for information about known nodes and cluster membership. - :attr List[str] all_nodes: List of nodes this node knows about, including the + :param List[str] all_nodes: List of nodes this node knows about, including the ones that are part of the cluster. - :attr List[str] cluster_nodes: All cluster nodes. + :param List[str] cluster_nodes: All cluster nodes. """ - def __init__(self, - all_nodes: List[str], - cluster_nodes: List[str]) -> None: + def __init__( + self, + all_nodes: List[str], + cluster_nodes: List[str], + ) -> None: """ Initialize a MembershipInformation object. @@ -13618,12 +15468,12 @@ def __init__(self, def from_dict(cls, _dict: Dict) -> 'MembershipInformation': """Initialize a MembershipInformation object from a json dictionary.""" args = {} - if 'all_nodes' in _dict: - args['all_nodes'] = _dict.get('all_nodes') + if (all_nodes := _dict.get('all_nodes')) is not None: + args['all_nodes'] = all_nodes else: raise ValueError('Required property \'all_nodes\' not present in MembershipInformation JSON') - if 'cluster_nodes' in _dict: - args['cluster_nodes'] = _dict.get('cluster_nodes') + if (cluster_nodes := _dict.get('cluster_nodes')) is not None: + args['cluster_nodes'] = cluster_nodes else: raise ValueError('Required property \'cluster_nodes\' not present in MembershipInformation JSON') return cls(**args) @@ -13660,16 +15510,19 @@ def __ne__(self, other: 'MembershipInformation') -> bool: """Return `true` when self and other are not equal, false otherwise.""" return not self == other -class Ok(): + +class Ok: """ Schema for an OK result. - :attr bool ok: (optional) ok. + :param bool ok: (optional) ok. """ - def __init__(self, - *, - ok: bool = None) -> None: + def __init__( + self, + *, + ok: Optional[bool] = None, + ) -> None: """ Initialize a Ok object. @@ -13681,8 +15534,8 @@ def __init__(self, def from_dict(cls, _dict: Dict) -> 'Ok': """Initialize a Ok object from a json dictionary.""" args = {} - if 'ok' in _dict: - args['ok'] = _dict.get('ok') + if (ok := _dict.get('ok')) is not None: + args['ok'] = ok return cls(**args) @classmethod @@ -13715,37 +15568,40 @@ def __ne__(self, other: 'Ok') -> bool: """Return `true` when self and other are not equal, false otherwise.""" return not self == other -class PartitionInformation(): + +class PartitionInformation: """ Schema for information about a database partition. - :attr str db_name: The name of the database. - :attr int doc_count: A count of the documents in the specified database + :param str db_name: Schema for a database name. + :param int doc_count: A count of the documents in the specified database partition. - :attr int doc_del_count: Number of deleted documents. - :attr str partition: The name of the partition in the database. - :attr PartitionInformationIndexes partitioned_indexes: (optional) Schema for + :param int doc_del_count: Number of deleted documents. + :param str partition: Schema for a partition key. + :param PartitionInformationIndexes partitioned_indexes: (optional) Schema for information about the partition index count and limit in a database. - :attr PartitionInformationSizes sizes: The size of active and external data, in + :param PartitionInformationSizes sizes: The size of active and external data, in bytes. """ - def __init__(self, - db_name: str, - doc_count: int, - doc_del_count: int, - partition: str, - sizes: 'PartitionInformationSizes', - *, - partitioned_indexes: 'PartitionInformationIndexes' = None) -> None: + def __init__( + self, + db_name: str, + doc_count: int, + doc_del_count: int, + partition: str, + sizes: 'PartitionInformationSizes', + *, + partitioned_indexes: Optional['PartitionInformationIndexes'] = None, + ) -> None: """ Initialize a PartitionInformation object. - :param str db_name: The name of the database. + :param str db_name: Schema for a database name. :param int doc_count: A count of the documents in the specified database partition. :param int doc_del_count: Number of deleted documents. - :param str partition: The name of the partition in the database. + :param str partition: Schema for a partition key. :param PartitionInformationSizes sizes: The size of active and external data, in bytes. :param PartitionInformationIndexes partitioned_indexes: (optional) Schema @@ -13762,26 +15618,26 @@ def __init__(self, def from_dict(cls, _dict: Dict) -> 'PartitionInformation': """Initialize a PartitionInformation object from a json dictionary.""" args = {} - if 'db_name' in _dict: - args['db_name'] = _dict.get('db_name') + if (db_name := _dict.get('db_name')) is not None: + args['db_name'] = db_name else: raise ValueError('Required property \'db_name\' not present in PartitionInformation JSON') - if 'doc_count' in _dict: - args['doc_count'] = _dict.get('doc_count') + if (doc_count := _dict.get('doc_count')) is not None: + args['doc_count'] = doc_count else: raise ValueError('Required property \'doc_count\' not present in PartitionInformation JSON') - if 'doc_del_count' in _dict: - args['doc_del_count'] = _dict.get('doc_del_count') + if (doc_del_count := _dict.get('doc_del_count')) is not None: + args['doc_del_count'] = doc_del_count else: raise ValueError('Required property \'doc_del_count\' not present in PartitionInformation JSON') - if 'partition' in _dict: - args['partition'] = _dict.get('partition') + if (partition := _dict.get('partition')) is not None: + args['partition'] = partition else: raise ValueError('Required property \'partition\' not present in PartitionInformation JSON') - if 'partitioned_indexes' in _dict: - args['partitioned_indexes'] = PartitionInformationIndexes.from_dict(_dict.get('partitioned_indexes')) - if 'sizes' in _dict: - args['sizes'] = PartitionInformationSizes.from_dict(_dict.get('sizes')) + if (partitioned_indexes := _dict.get('partitioned_indexes')) is not None: + args['partitioned_indexes'] = PartitionInformationIndexes.from_dict(partitioned_indexes) + if (sizes := _dict.get('sizes')) is not None: + args['sizes'] = PartitionInformationSizes.from_dict(sizes) else: raise ValueError('Required property \'sizes\' not present in PartitionInformation JSON') return cls(**args) @@ -13803,9 +15659,15 @@ def to_dict(self) -> Dict: if hasattr(self, 'partition') and self.partition is not None: _dict['partition'] = self.partition if hasattr(self, 'partitioned_indexes') and self.partitioned_indexes is not None: - _dict['partitioned_indexes'] = self.partitioned_indexes.to_dict() + if isinstance(self.partitioned_indexes, dict): + _dict['partitioned_indexes'] = self.partitioned_indexes + else: + _dict['partitioned_indexes'] = self.partitioned_indexes.to_dict() if hasattr(self, 'sizes') and self.sizes is not None: - _dict['sizes'] = self.sizes.to_dict() + if isinstance(self.sizes, dict): + _dict['sizes'] = self.sizes + else: + _dict['sizes'] = self.sizes.to_dict() return _dict def _to_dict(self): @@ -13826,59 +15688,201 @@ def __ne__(self, other: 'PartitionInformation') -> bool: """Return `true` when self and other are not equal, false otherwise.""" return not self == other -class PartitionInformationIndexes(): + +class PartitionInformationIndexes: """ Schema for information about the partition index count and limit in a database. - :attr int count: (optional) Total count of the partitioned indexes. - :attr PartitionInformationIndexesIndexes indexes: (optional) The count breakdown - of partitioned indexes. - :attr int limit: (optional) The partitioned index limit. + :param int count: (optional) Total count of the partitioned indexes. + :param PartitionInformationIndexesIndexes indexes: (optional) The count + breakdown of partitioned indexes. + :param int limit: (optional) The partitioned index limit. + """ + + def __init__( + self, + *, + count: Optional[int] = None, + indexes: Optional['PartitionInformationIndexesIndexes'] = None, + limit: Optional[int] = None, + ) -> None: + """ + Initialize a PartitionInformationIndexes object. + + :param int count: (optional) Total count of the partitioned indexes. + :param PartitionInformationIndexesIndexes indexes: (optional) The count + breakdown of partitioned indexes. + :param int limit: (optional) The partitioned index limit. + """ + self.count = count + self.indexes = indexes + self.limit = limit + + @classmethod + def from_dict(cls, _dict: Dict) -> 'PartitionInformationIndexes': + """Initialize a PartitionInformationIndexes object from a json dictionary.""" + args = {} + if (count := _dict.get('count')) is not None: + args['count'] = count + if (indexes := _dict.get('indexes')) is not None: + args['indexes'] = PartitionInformationIndexesIndexes.from_dict(indexes) + if (limit := _dict.get('limit')) is not None: + args['limit'] = limit + return cls(**args) + + @classmethod + def _from_dict(cls, _dict): + """Initialize a PartitionInformationIndexes object from a json dictionary.""" + return cls.from_dict(_dict) + + def to_dict(self) -> Dict: + """Return a json dictionary representing this model.""" + _dict = {} + if hasattr(self, 'count') and self.count is not None: + _dict['count'] = self.count + if hasattr(self, 'indexes') and self.indexes is not None: + if isinstance(self.indexes, dict): + _dict['indexes'] = self.indexes + else: + _dict['indexes'] = self.indexes.to_dict() + if hasattr(self, 'limit') and self.limit is not None: + _dict['limit'] = self.limit + return _dict + + def _to_dict(self): + """Return a json dictionary representing this model.""" + return self.to_dict() + + def __str__(self) -> str: + """Return a `str` version of this PartitionInformationIndexes object.""" + return json.dumps(self.to_dict(), indent=2) + + def __eq__(self, other: 'PartitionInformationIndexes') -> bool: + """Return `true` when self and other are equal, false otherwise.""" + if not isinstance(other, self.__class__): + return False + return self.__dict__ == other.__dict__ + + def __ne__(self, other: 'PartitionInformationIndexes') -> bool: + """Return `true` when self and other are not equal, false otherwise.""" + return not self == other + + +class PartitionInformationIndexesIndexes: + """ + The count breakdown of partitioned indexes. + + :param int search: (optional) Number of partitioned search indexes. + :param int view: (optional) Number of partitioned view indexes. + """ + + def __init__( + self, + *, + search: Optional[int] = None, + view: Optional[int] = None, + ) -> None: + """ + Initialize a PartitionInformationIndexesIndexes object. + + :param int search: (optional) Number of partitioned search indexes. + :param int view: (optional) Number of partitioned view indexes. + """ + self.search = search + self.view = view + + @classmethod + def from_dict(cls, _dict: Dict) -> 'PartitionInformationIndexesIndexes': + """Initialize a PartitionInformationIndexesIndexes object from a json dictionary.""" + args = {} + if (search := _dict.get('search')) is not None: + args['search'] = search + if (view := _dict.get('view')) is not None: + args['view'] = view + return cls(**args) + + @classmethod + def _from_dict(cls, _dict): + """Initialize a PartitionInformationIndexesIndexes object from a json dictionary.""" + return cls.from_dict(_dict) + + def to_dict(self) -> Dict: + """Return a json dictionary representing this model.""" + _dict = {} + if hasattr(self, 'search') and self.search is not None: + _dict['search'] = self.search + if hasattr(self, 'view') and self.view is not None: + _dict['view'] = self.view + return _dict + + def _to_dict(self): + """Return a json dictionary representing this model.""" + return self.to_dict() + + def __str__(self) -> str: + """Return a `str` version of this PartitionInformationIndexesIndexes object.""" + return json.dumps(self.to_dict(), indent=2) + + def __eq__(self, other: 'PartitionInformationIndexesIndexes') -> bool: + """Return `true` when self and other are equal, false otherwise.""" + if not isinstance(other, self.__class__): + return False + return self.__dict__ == other.__dict__ + + def __ne__(self, other: 'PartitionInformationIndexesIndexes') -> bool: + """Return `true` when self and other are not equal, false otherwise.""" + return not self == other + + +class PartitionInformationSizes: + """ + The size of active and external data, in bytes. + + :param int active: (optional) The size of live data inside the database, in + bytes. + :param int external: (optional) The uncompressed size of database contents in + bytes. """ - def __init__(self, - *, - count: int = None, - indexes: 'PartitionInformationIndexesIndexes' = None, - limit: int = None) -> None: + def __init__( + self, + *, + active: Optional[int] = None, + external: Optional[int] = None, + ) -> None: """ - Initialize a PartitionInformationIndexes object. + Initialize a PartitionInformationSizes object. - :param int count: (optional) Total count of the partitioned indexes. - :param PartitionInformationIndexesIndexes indexes: (optional) The count - breakdown of partitioned indexes. - :param int limit: (optional) The partitioned index limit. + :param int active: (optional) The size of live data inside the database, in + bytes. + :param int external: (optional) The uncompressed size of database contents + in bytes. """ - self.count = count - self.indexes = indexes - self.limit = limit + self.active = active + self.external = external @classmethod - def from_dict(cls, _dict: Dict) -> 'PartitionInformationIndexes': - """Initialize a PartitionInformationIndexes object from a json dictionary.""" + def from_dict(cls, _dict: Dict) -> 'PartitionInformationSizes': + """Initialize a PartitionInformationSizes object from a json dictionary.""" args = {} - if 'count' in _dict: - args['count'] = _dict.get('count') - if 'indexes' in _dict: - args['indexes'] = PartitionInformationIndexesIndexes.from_dict(_dict.get('indexes')) - if 'limit' in _dict: - args['limit'] = _dict.get('limit') + if (active := _dict.get('active')) is not None: + args['active'] = active + if (external := _dict.get('external')) is not None: + args['external'] = external return cls(**args) @classmethod def _from_dict(cls, _dict): - """Initialize a PartitionInformationIndexes object from a json dictionary.""" + """Initialize a PartitionInformationSizes object from a json dictionary.""" return cls.from_dict(_dict) def to_dict(self) -> Dict: """Return a json dictionary representing this model.""" _dict = {} - if hasattr(self, 'count') and self.count is not None: - _dict['count'] = self.count - if hasattr(self, 'indexes') and self.indexes is not None: - _dict['indexes'] = self.indexes.to_dict() - if hasattr(self, 'limit') and self.limit is not None: - _dict['limit'] = self.limit + if hasattr(self, 'active') and self.active is not None: + _dict['active'] = self.active + if hasattr(self, 'external') and self.external is not None: + _dict['external'] = self.external return _dict def _to_dict(self): @@ -13886,53 +15890,56 @@ def _to_dict(self): return self.to_dict() def __str__(self) -> str: - """Return a `str` version of this PartitionInformationIndexes object.""" + """Return a `str` version of this PartitionInformationSizes object.""" return json.dumps(self.to_dict(), indent=2) - def __eq__(self, other: 'PartitionInformationIndexes') -> bool: + def __eq__(self, other: 'PartitionInformationSizes') -> bool: """Return `true` when self and other are equal, false otherwise.""" if not isinstance(other, self.__class__): return False return self.__dict__ == other.__dict__ - def __ne__(self, other: 'PartitionInformationIndexes') -> bool: + def __ne__(self, other: 'PartitionInformationSizes') -> bool: """Return `true` when self and other are not equal, false otherwise.""" return not self == other -class PartitionInformationIndexesIndexes(): + +class PartitionedIndexesDetailedInformation: """ - The count breakdown of partitioned indexes. + Number of partitioned indexes by type. - :attr int search: (optional) Number of partitioned search indexes. - :attr int view: (optional) Number of partitioned view indexes. + :param int search: (optional) Number of partitioned indexes of search type. + :param int view: (optional) Number of partitioned indexes of view type. """ - def __init__(self, - *, - search: int = None, - view: int = None) -> None: + def __init__( + self, + *, + search: Optional[int] = None, + view: Optional[int] = None, + ) -> None: """ - Initialize a PartitionInformationIndexesIndexes object. + Initialize a PartitionedIndexesDetailedInformation object. - :param int search: (optional) Number of partitioned search indexes. - :param int view: (optional) Number of partitioned view indexes. + :param int search: (optional) Number of partitioned indexes of search type. + :param int view: (optional) Number of partitioned indexes of view type. """ self.search = search self.view = view @classmethod - def from_dict(cls, _dict: Dict) -> 'PartitionInformationIndexesIndexes': - """Initialize a PartitionInformationIndexesIndexes object from a json dictionary.""" + def from_dict(cls, _dict: Dict) -> 'PartitionedIndexesDetailedInformation': + """Initialize a PartitionedIndexesDetailedInformation object from a json dictionary.""" args = {} - if 'search' in _dict: - args['search'] = _dict.get('search') - if 'view' in _dict: - args['view'] = _dict.get('view') + if (search := _dict.get('search')) is not None: + args['search'] = search + if (view := _dict.get('view')) is not None: + args['view'] = view return cls(**args) @classmethod def _from_dict(cls, _dict): - """Initialize a PartitionInformationIndexesIndexes object from a json dictionary.""" + """Initialize a PartitionedIndexesDetailedInformation object from a json dictionary.""" return cls.from_dict(_dict) def to_dict(self) -> Dict: @@ -13949,66 +15956,82 @@ def _to_dict(self): return self.to_dict() def __str__(self) -> str: - """Return a `str` version of this PartitionInformationIndexesIndexes object.""" + """Return a `str` version of this PartitionedIndexesDetailedInformation object.""" return json.dumps(self.to_dict(), indent=2) - def __eq__(self, other: 'PartitionInformationIndexesIndexes') -> bool: + def __eq__(self, other: 'PartitionedIndexesDetailedInformation') -> bool: """Return `true` when self and other are equal, false otherwise.""" if not isinstance(other, self.__class__): return False return self.__dict__ == other.__dict__ - def __ne__(self, other: 'PartitionInformationIndexesIndexes') -> bool: + def __ne__(self, other: 'PartitionedIndexesDetailedInformation') -> bool: """Return `true` when self and other are not equal, false otherwise.""" return not self == other -class PartitionInformationSizes(): + +class PartitionedIndexesInformation: """ - The size of active and external data, in bytes. + Information about database's partitioned indexes. - :attr int active: (optional) The size of live data inside the database, in - bytes. - :attr int external: (optional) The uncompressed size of database contents in - bytes. + :param int count: (optional) Total number of partitioned indexes in the + database. + :param PartitionedIndexesDetailedInformation indexes: (optional) Number of + partitioned indexes by type. + :param int limit: (optional) Maximum allowed number of partitioned indexes in + the database. """ - def __init__(self, - *, - active: int = None, - external: int = None) -> None: + def __init__( + self, + *, + count: Optional[int] = None, + indexes: Optional['PartitionedIndexesDetailedInformation'] = None, + limit: Optional[int] = None, + ) -> None: """ - Initialize a PartitionInformationSizes object. + Initialize a PartitionedIndexesInformation object. - :param int active: (optional) The size of live data inside the database, in - bytes. - :param int external: (optional) The uncompressed size of database contents - in bytes. + :param int count: (optional) Total number of partitioned indexes in the + database. + :param PartitionedIndexesDetailedInformation indexes: (optional) Number of + partitioned indexes by type. + :param int limit: (optional) Maximum allowed number of partitioned indexes + in the database. """ - self.active = active - self.external = external + self.count = count + self.indexes = indexes + self.limit = limit @classmethod - def from_dict(cls, _dict: Dict) -> 'PartitionInformationSizes': - """Initialize a PartitionInformationSizes object from a json dictionary.""" + def from_dict(cls, _dict: Dict) -> 'PartitionedIndexesInformation': + """Initialize a PartitionedIndexesInformation object from a json dictionary.""" args = {} - if 'active' in _dict: - args['active'] = _dict.get('active') - if 'external' in _dict: - args['external'] = _dict.get('external') + if (count := _dict.get('count')) is not None: + args['count'] = count + if (indexes := _dict.get('indexes')) is not None: + args['indexes'] = PartitionedIndexesDetailedInformation.from_dict(indexes) + if (limit := _dict.get('limit')) is not None: + args['limit'] = limit return cls(**args) @classmethod def _from_dict(cls, _dict): - """Initialize a PartitionInformationSizes object from a json dictionary.""" + """Initialize a PartitionedIndexesInformation object from a json dictionary.""" return cls.from_dict(_dict) def to_dict(self) -> Dict: """Return a json dictionary representing this model.""" _dict = {} - if hasattr(self, 'active') and self.active is not None: - _dict['active'] = self.active - if hasattr(self, 'external') and self.external is not None: - _dict['external'] = self.external + if hasattr(self, 'count') and self.count is not None: + _dict['count'] = self.count + if hasattr(self, 'indexes') and self.indexes is not None: + if isinstance(self.indexes, dict): + _dict['indexes'] = self.indexes + else: + _dict['indexes'] = self.indexes.to_dict() + if hasattr(self, 'limit') and self.limit is not None: + _dict['limit'] = self.limit return _dict def _to_dict(self): @@ -14016,41 +16039,46 @@ def _to_dict(self): return self.to_dict() def __str__(self) -> str: - """Return a `str` version of this PartitionInformationSizes object.""" + """Return a `str` version of this PartitionedIndexesInformation object.""" return json.dumps(self.to_dict(), indent=2) - def __eq__(self, other: 'PartitionInformationSizes') -> bool: + def __eq__(self, other: 'PartitionedIndexesInformation') -> bool: """Return `true` when self and other are equal, false otherwise.""" if not isinstance(other, self.__class__): return False return self.__dict__ == other.__dict__ - def __ne__(self, other: 'PartitionInformationSizes') -> bool: + def __ne__(self, other: 'PartitionedIndexesInformation') -> bool: """Return `true` when self and other are not equal, false otherwise.""" return not self == other -class ReplicationCreateTargetParameters(): + +class ReplicationCreateTargetParameters: """ Request parameters to use during target database creation. - :attr int n: (optional) Schema for the number of replicas of a database in a - cluster. - :attr bool partitioned: (optional) Parameter to specify whether to enable + :param int n: (optional) Schema for the number of replicas of a database in a + cluster. The cluster is using the default value and it cannot be changed by the + user. + :param bool partitioned: (optional) Parameter to specify whether to enable database partitions when creating the target database. - :attr int q: (optional) Schema for the number of shards in a database. Each + :param int q: (optional) Schema for the number of shards in a database. Each shard is a partition of the hash value range. """ - def __init__(self, - *, - n: int = None, - partitioned: bool = None, - q: int = None) -> None: + def __init__( + self, + *, + n: Optional[int] = None, + partitioned: Optional[bool] = None, + q: Optional[int] = None, + ) -> None: """ Initialize a ReplicationCreateTargetParameters object. :param int n: (optional) Schema for the number of replicas of a database in - a cluster. + a cluster. The cluster is using the default value and it cannot be changed + by the user. :param bool partitioned: (optional) Parameter to specify whether to enable database partitions when creating the target database. :param int q: (optional) Schema for the number of shards in a database. @@ -14064,12 +16092,12 @@ def __init__(self, def from_dict(cls, _dict: Dict) -> 'ReplicationCreateTargetParameters': """Initialize a ReplicationCreateTargetParameters object from a json dictionary.""" args = {} - if 'n' in _dict: - args['n'] = _dict.get('n') - if 'partitioned' in _dict: - args['partitioned'] = _dict.get('partitioned') - if 'q' in _dict: - args['q'] = _dict.get('q') + if (n := _dict.get('n')) is not None: + args['n'] = n + if (partitioned := _dict.get('partitioned')) is not None: + args['partitioned'] = partitioned + if (q := _dict.get('q')) is not None: + args['q'] = q return cls(**args) @classmethod @@ -14106,21 +16134,24 @@ def __ne__(self, other: 'ReplicationCreateTargetParameters') -> bool: """Return `true` when self and other are not equal, false otherwise.""" return not self == other -class ReplicationDatabase(): + +class ReplicationDatabase: """ Schema for a replication source or target database. - :attr ReplicationDatabaseAuth auth: (optional) Schema for replication source or + :param ReplicationDatabaseAuth auth: (optional) Schema for replication source or target database authentication. - :attr dict headers_: (optional) Replication request headers. - :attr str url: Replication database URL. + :param dict headers_: (optional) Replication request headers. + :param str url: Replication database URL. """ - def __init__(self, - url: str, - *, - auth: 'ReplicationDatabaseAuth' = None, - headers_: dict = None) -> None: + def __init__( + self, + url: str, + *, + auth: Optional['ReplicationDatabaseAuth'] = None, + headers_: Optional[dict] = None, + ) -> None: """ Initialize a ReplicationDatabase object. @@ -14137,12 +16168,12 @@ def __init__(self, def from_dict(cls, _dict: Dict) -> 'ReplicationDatabase': """Initialize a ReplicationDatabase object from a json dictionary.""" args = {} - if 'auth' in _dict: - args['auth'] = ReplicationDatabaseAuth.from_dict(_dict.get('auth')) - if 'headers' in _dict: - args['headers_'] = _dict.get('headers') - if 'url' in _dict: - args['url'] = _dict.get('url') + if (auth := _dict.get('auth')) is not None: + args['auth'] = ReplicationDatabaseAuth.from_dict(auth) + if (headers_ := _dict.get('headers')) is not None: + args['headers_'] = headers_ + if (url := _dict.get('url')) is not None: + args['url'] = url else: raise ValueError('Required property \'url\' not present in ReplicationDatabase JSON') return cls(**args) @@ -14156,7 +16187,10 @@ def to_dict(self) -> Dict: """Return a json dictionary representing this model.""" _dict = {} if hasattr(self, 'auth') and self.auth is not None: - _dict['auth'] = self.auth.to_dict() + if isinstance(self.auth, dict): + _dict['auth'] = self.auth + else: + _dict['auth'] = self.auth.to_dict() if hasattr(self, 'headers_') and self.headers_ is not None: _dict['headers'] = self.headers_ if hasattr(self, 'url') and self.url is not None: @@ -14181,20 +16215,23 @@ def __ne__(self, other: 'ReplicationDatabase') -> bool: """Return `true` when self and other are not equal, false otherwise.""" return not self == other -class ReplicationDatabaseAuth(): + +class ReplicationDatabaseAuth: """ Schema for replication source or target database authentication. - :attr ReplicationDatabaseAuthBasic basic: (optional) Schema for basic + :param ReplicationDatabaseAuthBasic basic: (optional) Schema for basic authentication of replication source or target database. - :attr ReplicationDatabaseAuthIam iam: (optional) Schema for an IAM API key for + :param ReplicationDatabaseAuthIam iam: (optional) Schema for an IAM API key for replication database authentication. """ - def __init__(self, - *, - basic: 'ReplicationDatabaseAuthBasic' = None, - iam: 'ReplicationDatabaseAuthIam' = None) -> None: + def __init__( + self, + *, + basic: Optional['ReplicationDatabaseAuthBasic'] = None, + iam: Optional['ReplicationDatabaseAuthIam'] = None, + ) -> None: """ Initialize a ReplicationDatabaseAuth object. @@ -14210,10 +16247,10 @@ def __init__(self, def from_dict(cls, _dict: Dict) -> 'ReplicationDatabaseAuth': """Initialize a ReplicationDatabaseAuth object from a json dictionary.""" args = {} - if 'basic' in _dict: - args['basic'] = ReplicationDatabaseAuthBasic.from_dict(_dict.get('basic')) - if 'iam' in _dict: - args['iam'] = ReplicationDatabaseAuthIam.from_dict(_dict.get('iam')) + if (basic := _dict.get('basic')) is not None: + args['basic'] = ReplicationDatabaseAuthBasic.from_dict(basic) + if (iam := _dict.get('iam')) is not None: + args['iam'] = ReplicationDatabaseAuthIam.from_dict(iam) return cls(**args) @classmethod @@ -14225,9 +16262,15 @@ def to_dict(self) -> Dict: """Return a json dictionary representing this model.""" _dict = {} if hasattr(self, 'basic') and self.basic is not None: - _dict['basic'] = self.basic.to_dict() + if isinstance(self.basic, dict): + _dict['basic'] = self.basic + else: + _dict['basic'] = self.basic.to_dict() if hasattr(self, 'iam') and self.iam is not None: - _dict['iam'] = self.iam.to_dict() + if isinstance(self.iam, dict): + _dict['iam'] = self.iam + else: + _dict['iam'] = self.iam.to_dict() return _dict def _to_dict(self): @@ -14248,22 +16291,25 @@ def __ne__(self, other: 'ReplicationDatabaseAuth') -> bool: """Return `true` when self and other are not equal, false otherwise.""" return not self == other -class ReplicationDatabaseAuthBasic(): + +class ReplicationDatabaseAuthBasic: """ Schema for basic authentication of replication source or target database. - :attr str password: The password associated with the username. - :attr str username: The username. + :param str password: The password associated with the username. + :param str username: Schema for a username. """ - def __init__(self, - password: str, - username: str) -> None: + def __init__( + self, + password: str, + username: str, + ) -> None: """ Initialize a ReplicationDatabaseAuthBasic object. :param str password: The password associated with the username. - :param str username: The username. + :param str username: Schema for a username. """ self.password = password self.username = username @@ -14272,12 +16318,12 @@ def __init__(self, def from_dict(cls, _dict: Dict) -> 'ReplicationDatabaseAuthBasic': """Initialize a ReplicationDatabaseAuthBasic object from a json dictionary.""" args = {} - if 'password' in _dict: - args['password'] = _dict.get('password') + if (password := _dict.get('password')) is not None: + args['password'] = password else: raise ValueError('Required property \'password\' not present in ReplicationDatabaseAuthBasic JSON') - if 'username' in _dict: - args['username'] = _dict.get('username') + if (username := _dict.get('username')) is not None: + args['username'] = username else: raise ValueError('Required property \'username\' not present in ReplicationDatabaseAuthBasic JSON') return cls(**args) @@ -14314,15 +16360,18 @@ def __ne__(self, other: 'ReplicationDatabaseAuthBasic') -> bool: """Return `true` when self and other are not equal, false otherwise.""" return not self == other -class ReplicationDatabaseAuthIam(): + +class ReplicationDatabaseAuthIam: """ Schema for an IAM API key for replication database authentication. - :attr str api_key: IAM API key. + :param str api_key: IAM API key. """ - def __init__(self, - api_key: str) -> None: + def __init__( + self, + api_key: str, + ) -> None: """ Initialize a ReplicationDatabaseAuthIam object. @@ -14334,8 +16383,8 @@ def __init__(self, def from_dict(cls, _dict: Dict) -> 'ReplicationDatabaseAuthIam': """Initialize a ReplicationDatabaseAuthIam object from a json dictionary.""" args = {} - if 'api_key' in _dict: - args['api_key'] = _dict.get('api_key') + if (api_key := _dict.get('api_key')) is not None: + args['api_key'] = api_key else: raise ValueError('Required property \'api_key\' not present in ReplicationDatabaseAuthIam JSON') return cls(**args) @@ -14370,50 +16419,55 @@ def __ne__(self, other: 'ReplicationDatabaseAuthIam') -> bool: """Return `true` when self and other are not equal, false otherwise.""" return not self == other -class ReplicationDocument(): + +class ReplicationDocument: """ Schema for a replication document. Note that `selector`, `doc_ids`, and `filter` are incompatible with each other. - :attr dict attachments: (optional) Schema for a map of attachment name to + :param dict _attachments: (optional) Schema for a map of attachment name to attachment metadata. - :attr List[str] conflicts: (optional) Schema for a list of document revision + :param List[str] _conflicts: (optional) Schema for a list of document revision identifiers. - :attr bool deleted: (optional) Deletion flag. Available if document was removed. - :attr List[str] deleted_conflicts: (optional) Schema for a list of document + :param bool _deleted: (optional) Deletion flag. Available if document was + removed. + :param List[str] _deleted_conflicts: (optional) Schema for a list of document revision identifiers. - :attr str id: (optional) Document ID. - :attr str local_seq: (optional) Document's update sequence in current database. - Available if requested with local_seq=true query parameter. - :attr str rev: (optional) Schema for a document revision identifier. - :attr Revisions revisions: (optional) Schema for list of revision information. - :attr List[DocumentRevisionStatus] revs_info: (optional) Schema for a list of + :param str _id: (optional) Schema for a document ID. + :param str _local_seq: (optional) Document's update sequence in current + database. Available if requested with local_seq=true query parameter. + :param str _rev: (optional) Schema for a document revision identifier. + :param Revisions _revisions: (optional) Schema for list of revision information. + :param List[DocumentRevisionStatus] _revs_info: (optional) Schema for a list of objects with information about local revisions and their status. - :attr bool cancel: (optional) Cancels the replication. - :attr int checkpoint_interval: (optional) Defines replication checkpoint + :param bool cancel: (optional) Cancels the replication. + :param int checkpoint_interval: (optional) Defines replication checkpoint interval in milliseconds. - :attr int connection_timeout: (optional) HTTP connection timeout per + :param int connection_timeout: (optional) HTTP connection timeout per replication. Even for very fast/reliable networks it might need to be increased if a remote database is too busy. - :attr bool continuous: (optional) Configure the replication to be continuous. - :attr bool create_target: (optional) Creates the target database. Requires + :param bool continuous: (optional) Configure the replication to be continuous. + :param bool create_target: (optional) Creates the target database. Requires administrator privileges on target server. - :attr ReplicationCreateTargetParameters create_target_params: (optional) Request - parameters to use during target database creation. - :attr List[str] doc_ids: (optional) Schema for a list of document IDs. - :attr str filter: (optional) The name of a filter function which is defined in a - design document in the source database in {ddoc_id}/{filter} format. It + :param ReplicationCreateTargetParameters create_target_params: (optional) + Request parameters to use during target database creation. + :param List[str] doc_ids: (optional) Schema for a list of document IDs. + :param str filter: (optional) The name of a filter function which is defined in + a design document in the source database in {ddoc_id}/{filter} format. It determines which documents get replicated. Using the selector option provides performance benefits when compared with using the filter option. Use the selector option when possible. - :attr int http_connections: (optional) Maximum number of HTTP connections per + :param int http_connections: (optional) Maximum number of HTTP connections per replication. - :attr dict query_params: (optional) Schema for a map of string key value pairs, + :param str owner: (optional) The replication document owner. The server sets an + appropriate value if the field is unset when writing a replication document. + Only administrators can modify the value to an owner other than themselves. + :param dict query_params: (optional) Schema for a map of string key value pairs, such as query parameters. - :attr int retries_per_request: (optional) Number of times a replication request + :param int retries_per_request: (optional) Number of times a replication request is retried. The requests are retried with a doubling exponential backoff starting at 0.25 seconds, with a cap at 5 minutes. - :attr dict selector: (optional) JSON object describing criteria used to select + :param dict selector: (optional) JSON object describing criteria used to select documents. The selector specifies fields in the document, and provides an expression to evaluate with the field content or other data. The selector object must: @@ -14429,79 +16483,106 @@ class ReplicationDocument(): field. There are two core types of operators in the selector syntax: * Combination operators: applied at the topmost level of selection. They are - used to combine selectors. In addition to the common boolean operators (`$and`, - `$or`, `$not`, `$nor`) there are three combination operators: `$all`, - `$elemMatch`, and `$allMatch`. A combination operator takes a single argument. - The argument is either another selector, or an array of selectors. + used to combine selectors. A combination operator takes a single argument. The + argument is either another selector, or an array of selectors. * Condition operators: are specific to a field, and are used to evaluate the value stored in that field. For instance, the basic `$eq` operator matches when the specified field contains a value that is equal to the supplied argument. - * Only equality operators such as `$eq`, `$gt`, `$gte`, `$lt`, and `$lte` (but - not `$ne`) can be used as the basis of a query. You should include at least one - of these in a selector. - For further reference see - [selector - syntax](https://cloud.ibm.com/docs/Cloudant?topic=Cloudant-query#selector-syntax). - :attr str since_seq: (optional) Start the replication at a specific sequence + It is important for query performance to use appropriate selectors: + * Only equality operators such as `$eq`, `$gt`, `$gte`, `$lt`, and + `$lte` (but not `$ne`) can be used as the basis of a query. You should include + at least one of these in a selector. + * Some operators such as `$not`, `$or`, `$in`, and `$regex` cannot be answered + from an index. For query selectors use these operators in conjunction with + equality operators or create and use a partial index to reduce the number of + documents that will need to be scanned. + See [the Cloudant + Docs](https://cloud.ibm.com/docs/Cloudant?topic=Cloudant-operators) for a list + of all available combination and conditional operators. + For further reference see [selector + syntax](https://cloud.ibm.com/docs/Cloudant?topic=Cloudant-selector-syntax). + :param str since_seq: (optional) Start the replication at a specific sequence value. - :attr str socket_options: (optional) Replication socket options. - :attr ReplicationDatabase source: Schema for a replication source or target + :param str socket_options: (optional) Replication socket options. + :param ReplicationDatabase source: Schema for a replication source or target database. - :attr str source_proxy: (optional) Address of a (http or socks5 protocol) proxy - server through which replication with the source database should occur. - :attr ReplicationDatabase target: Schema for a replication source or target + :param str source_proxy: (optional) Deprecated: This setting is forbidden in IBM + Cloudant replication documents. This setting may be used with alternative + replication mediators. + Address of a (http or socks5 protocol) proxy server through which replication + with the source database should occur. + :param ReplicationDatabase target: Schema for a replication source or target database. - :attr str target_proxy: (optional) Address of a (http or socks5 protocol) proxy - server through which replication with the target database should occur. - :attr bool use_checkpoints: (optional) Specify if checkpoints should be saved + :param str target_proxy: (optional) Deprecated: This setting is forbidden in IBM + Cloudant replication documents. This setting may be used with alternative + replication mediators. + Address of a (http or socks5 protocol) proxy server through which replication + with the target database should occur. + :param bool use_bulk_get: (optional) Specify whether to use _bulk_get for + fetching documents from the source. If unset, the server configured default will + be used. + :param bool use_checkpoints: (optional) Specify if checkpoints should be saved during replication. Using checkpoints means a replication can be efficiently resumed. - :attr UserContext user_ctx: (optional) Schema for the user context of a session. - :attr int worker_batch_size: (optional) Controls how many documents are + :param UserContext user_ctx: (optional) Schema for the user context of a + session. + :param bool winning_revs_only: (optional) Replicate only the winning revisions. + Replication with this mode discards conflicting revisions. Replication IDs and + checkpoints generated by this mode are different to those generated by default, + so it is possible to first replicate the winning revisions then later backfill + remaining revisions with a regular replication job. + :param int worker_batch_size: (optional) Controls how many documents are processed. After each batch a checkpoint is written so this controls how frequently checkpointing occurs. - :attr int worker_processes: (optional) Controls how many separate processes will - read from the changes manager and write to the target. A higher number can + :param int worker_processes: (optional) Controls how many separate processes + will read from the changes manager and write to the target. A higher number can improve throughput. + + This type supports additional properties of type object. """ # The set of defined properties for the class - _properties = frozenset(['attachments', '_attachments', 'conflicts', '_conflicts', 'deleted', '_deleted', 'deleted_conflicts', '_deleted_conflicts', 'id', '_id', 'local_seq', '_local_seq', 'rev', '_rev', 'revisions', '_revisions', 'revs_info', '_revs_info', 'cancel', 'checkpoint_interval', 'connection_timeout', 'continuous', 'create_target', 'create_target_params', 'doc_ids', 'filter', 'http_connections', 'query_params', 'retries_per_request', 'selector', 'since_seq', 'socket_options', 'source', 'source_proxy', 'target', 'target_proxy', 'use_checkpoints', 'user_ctx', 'worker_batch_size', 'worker_processes']) - - def __init__(self, - source: 'ReplicationDatabase', - target: 'ReplicationDatabase', - *, - attachments: dict = None, - conflicts: List[str] = None, - deleted: bool = None, - deleted_conflicts: List[str] = None, - id: str = None, - local_seq: str = None, - rev: str = None, - revisions: 'Revisions' = None, - revs_info: List['DocumentRevisionStatus'] = None, - cancel: bool = None, - checkpoint_interval: int = None, - connection_timeout: int = None, - continuous: bool = None, - create_target: bool = None, - create_target_params: 'ReplicationCreateTargetParameters' = None, - doc_ids: List[str] = None, - filter: str = None, - http_connections: int = None, - query_params: dict = None, - retries_per_request: int = None, - selector: dict = None, - since_seq: str = None, - socket_options: str = None, - source_proxy: str = None, - target_proxy: str = None, - use_checkpoints: bool = None, - user_ctx: 'UserContext' = None, - worker_batch_size: int = None, - worker_processes: int = None, - **kwargs) -> None: + _properties = frozenset(['_attachments', '_conflicts', '_deleted', '_deleted_conflicts', '_id', '_local_seq', '_rev', '_revisions', '_revs_info', 'cancel', 'checkpoint_interval', 'connection_timeout', 'continuous', 'create_target', 'create_target_params', 'doc_ids', 'filter', 'http_connections', 'owner', 'query_params', 'retries_per_request', 'selector', 'since_seq', 'socket_options', 'source', 'source_proxy', 'target', 'target_proxy', 'use_bulk_get', 'use_checkpoints', 'user_ctx', 'winning_revs_only', 'worker_batch_size', 'worker_processes']) + + def __init__( + self, + source: 'ReplicationDatabase', + target: 'ReplicationDatabase', + *, + _attachments: Optional[dict] = None, + _conflicts: Optional[List[str]] = None, + _deleted: Optional[bool] = None, + _deleted_conflicts: Optional[List[str]] = None, + _id: Optional[str] = None, + _local_seq: Optional[str] = None, + _rev: Optional[str] = None, + _revisions: Optional['Revisions'] = None, + _revs_info: Optional[List['DocumentRevisionStatus']] = None, + cancel: Optional[bool] = None, + checkpoint_interval: Optional[int] = None, + connection_timeout: Optional[int] = None, + continuous: Optional[bool] = None, + create_target: Optional[bool] = None, + create_target_params: Optional['ReplicationCreateTargetParameters'] = None, + doc_ids: Optional[List[str]] = None, + filter: Optional[str] = None, + http_connections: Optional[int] = None, + owner: Optional[str] = None, + query_params: Optional[dict] = None, + retries_per_request: Optional[int] = None, + selector: Optional[dict] = None, + since_seq: Optional[str] = None, + socket_options: Optional[str] = None, + source_proxy: Optional[str] = None, + target_proxy: Optional[str] = None, + use_bulk_get: Optional[bool] = None, + use_checkpoints: Optional[bool] = None, + user_ctx: Optional['UserContext'] = None, + winning_revs_only: Optional[bool] = None, + worker_batch_size: Optional[int] = None, + worker_processes: Optional[int] = None, + **kwargs: Optional[object], + ) -> None: """ Initialize a ReplicationDocument object. @@ -14509,22 +16590,22 @@ def __init__(self, target database. :param ReplicationDatabase target: Schema for a replication source or target database. - :param dict attachments: (optional) Schema for a map of attachment name to + :param dict _attachments: (optional) Schema for a map of attachment name to attachment metadata. - :param List[str] conflicts: (optional) Schema for a list of document + :param List[str] _conflicts: (optional) Schema for a list of document revision identifiers. - :param bool deleted: (optional) Deletion flag. Available if document was + :param bool _deleted: (optional) Deletion flag. Available if document was removed. - :param List[str] deleted_conflicts: (optional) Schema for a list of + :param List[str] _deleted_conflicts: (optional) Schema for a list of document revision identifiers. - :param str id: (optional) Document ID. - :param str local_seq: (optional) Document's update sequence in current + :param str _id: (optional) Schema for a document ID. + :param str _local_seq: (optional) Document's update sequence in current database. Available if requested with local_seq=true query parameter. - :param str rev: (optional) Schema for a document revision identifier. - :param Revisions revisions: (optional) Schema for list of revision + :param str _rev: (optional) Schema for a document revision identifier. + :param Revisions _revisions: (optional) Schema for list of revision information. - :param List[DocumentRevisionStatus] revs_info: (optional) Schema for a list - of objects with information about local revisions and their status. + :param List[DocumentRevisionStatus] _revs_info: (optional) Schema for a + list of objects with information about local revisions and their status. :param bool cancel: (optional) Cancels the replication. :param int checkpoint_interval: (optional) Defines replication checkpoint interval in milliseconds. @@ -14545,6 +16626,10 @@ def __init__(self, option. Use the selector option when possible. :param int http_connections: (optional) Maximum number of HTTP connections per replication. + :param str owner: (optional) The replication document owner. The server + sets an appropriate value if the field is unset when writing a replication + document. Only administrators can modify the value to an owner other than + themselves. :param dict query_params: (optional) Schema for a map of string key value pairs, such as query parameters. :param int retries_per_request: (optional) Number of times a replication @@ -14566,52 +16651,70 @@ def __init__(self, field. There are two core types of operators in the selector syntax: * Combination operators: applied at the topmost level of selection. They - are used to combine selectors. In addition to the common boolean operators - (`$and`, `$or`, `$not`, `$nor`) there are three combination operators: - `$all`, `$elemMatch`, and `$allMatch`. A combination operator takes a - single argument. The argument is either another selector, or an array of + are used to combine selectors. A combination operator takes a single + argument. The argument is either another selector, or an array of selectors. * Condition operators: are specific to a field, and are used to evaluate the value stored in that field. For instance, the basic `$eq` operator matches when the specified field contains a value that is equal to the supplied argument. - * Only equality operators such as `$eq`, `$gt`, `$gte`, `$lt`, and `$lte` - (but not `$ne`) can be used as the basis of a query. You should include at - least one of these in a selector. - For further reference see - [selector - syntax](https://cloud.ibm.com/docs/Cloudant?topic=Cloudant-query#selector-syntax). + It is important for query performance to use appropriate selectors: + * Only equality operators such as `$eq`, `$gt`, `$gte`, `$lt`, and + `$lte` (but not `$ne`) can be used as the basis of a query. You should + include at least one of these in a selector. + * Some operators such as `$not`, `$or`, `$in`, and `$regex` cannot be + answered from an index. For query selectors use these operators in + conjunction with equality operators or create and use a partial index to + reduce the number of documents that will need to be scanned. + See [the Cloudant + Docs](https://cloud.ibm.com/docs/Cloudant?topic=Cloudant-operators) for a + list of all available combination and conditional operators. + For further reference see [selector + syntax](https://cloud.ibm.com/docs/Cloudant?topic=Cloudant-selector-syntax). :param str since_seq: (optional) Start the replication at a specific sequence value. :param str socket_options: (optional) Replication socket options. - :param str source_proxy: (optional) Address of a (http or socks5 protocol) - proxy server through which replication with the source database should - occur. - :param str target_proxy: (optional) Address of a (http or socks5 protocol) - proxy server through which replication with the target database should - occur. + :param str source_proxy: (optional) Deprecated: This setting is forbidden + in IBM Cloudant replication documents. This setting may be used with + alternative replication mediators. + Address of a (http or socks5 protocol) proxy server through which + replication with the source database should occur. + :param str target_proxy: (optional) Deprecated: This setting is forbidden + in IBM Cloudant replication documents. This setting may be used with + alternative replication mediators. + Address of a (http or socks5 protocol) proxy server through which + replication with the target database should occur. + :param bool use_bulk_get: (optional) Specify whether to use _bulk_get for + fetching documents from the source. If unset, the server configured default + will be used. :param bool use_checkpoints: (optional) Specify if checkpoints should be saved during replication. Using checkpoints means a replication can be efficiently resumed. :param UserContext user_ctx: (optional) Schema for the user context of a session. + :param bool winning_revs_only: (optional) Replicate only the winning + revisions. Replication with this mode discards conflicting revisions. + Replication IDs and checkpoints generated by this mode are different to + those generated by default, so it is possible to first replicate the + winning revisions then later backfill remaining revisions with a regular + replication job. :param int worker_batch_size: (optional) Controls how many documents are processed. After each batch a checkpoint is written so this controls how frequently checkpointing occurs. :param int worker_processes: (optional) Controls how many separate processes will read from the changes manager and write to the target. A higher number can improve throughput. - :param **kwargs: (optional) Any additional properties. - """ - self.attachments = attachments - self.conflicts = conflicts - self.deleted = deleted - self.deleted_conflicts = deleted_conflicts - self.id = id - self.local_seq = local_seq - self.rev = rev - self.revisions = revisions - self.revs_info = revs_info + :param object **kwargs: (optional) Additional properties of type object + """ + self._attachments = _attachments + self._conflicts = _conflicts + self._deleted = _deleted + self._deleted_conflicts = _deleted_conflicts + self._id = _id + self._local_seq = _local_seq + self._rev = _rev + self._revisions = _revisions + self._revs_info = _revs_info self.cancel = cancel self.checkpoint_interval = checkpoint_interval self.connection_timeout = connection_timeout @@ -14621,6 +16724,7 @@ def __init__(self, self.doc_ids = doc_ids self.filter = filter self.http_connections = http_connections + self.owner = owner self.query_params = query_params self.retries_per_request = retries_per_request self.selector = selector @@ -14630,84 +16734,101 @@ def __init__(self, self.source_proxy = source_proxy self.target = target self.target_proxy = target_proxy + self.use_bulk_get = use_bulk_get self.use_checkpoints = use_checkpoints self.user_ctx = user_ctx + self.winning_revs_only = winning_revs_only self.worker_batch_size = worker_batch_size self.worker_processes = worker_processes - for _key, _value in kwargs.items(): - setattr(self, _key, _value) + for k, v in kwargs.items(): + if k not in ReplicationDocument._properties: + if not isinstance(v, object): + raise ValueError('Value for additional property {} must be of type object'.format(k)) + setattr(self, k, v) + else: + raise ValueError('Property {} cannot be specified as an additional property'.format(k)) @classmethod def from_dict(cls, _dict: Dict) -> 'ReplicationDocument': """Initialize a ReplicationDocument object from a json dictionary.""" args = {} - if '_attachments' in _dict: - args['attachments'] = {k : Attachment.from_dict(v) for k, v in _dict.get('_attachments').items()} - if '_conflicts' in _dict: - args['conflicts'] = _dict.get('_conflicts') - if '_deleted' in _dict: - args['deleted'] = _dict.get('_deleted') - if '_deleted_conflicts' in _dict: - args['deleted_conflicts'] = _dict.get('_deleted_conflicts') - if '_id' in _dict: - args['id'] = _dict.get('_id') - if '_local_seq' in _dict: - args['local_seq'] = _dict.get('_local_seq') - if '_rev' in _dict: - args['rev'] = _dict.get('_rev') - if '_revisions' in _dict: - args['revisions'] = Revisions.from_dict(_dict.get('_revisions')) - if '_revs_info' in _dict: - args['revs_info'] = [DocumentRevisionStatus.from_dict(x) for x in _dict.get('_revs_info')] - if 'cancel' in _dict: - args['cancel'] = _dict.get('cancel') - if 'checkpoint_interval' in _dict: - args['checkpoint_interval'] = _dict.get('checkpoint_interval') - if 'connection_timeout' in _dict: - args['connection_timeout'] = _dict.get('connection_timeout') - if 'continuous' in _dict: - args['continuous'] = _dict.get('continuous') - if 'create_target' in _dict: - args['create_target'] = _dict.get('create_target') - if 'create_target_params' in _dict: - args['create_target_params'] = ReplicationCreateTargetParameters.from_dict(_dict.get('create_target_params')) - if 'doc_ids' in _dict: - args['doc_ids'] = _dict.get('doc_ids') - if 'filter' in _dict: - args['filter'] = _dict.get('filter') - if 'http_connections' in _dict: - args['http_connections'] = _dict.get('http_connections') - if 'query_params' in _dict: - args['query_params'] = _dict.get('query_params') - if 'retries_per_request' in _dict: - args['retries_per_request'] = _dict.get('retries_per_request') - if 'selector' in _dict: - args['selector'] = _dict.get('selector') - if 'since_seq' in _dict: - args['since_seq'] = _dict.get('since_seq') - if 'socket_options' in _dict: - args['socket_options'] = _dict.get('socket_options') - if 'source' in _dict: - args['source'] = ReplicationDatabase.from_dict(_dict.get('source')) + if (attachments := _dict.get('_attachments')) is not None: + args['_attachments'] = {k: Attachment.from_dict(v) for k, v in attachments.items()} + if (conflicts := _dict.get('_conflicts')) is not None: + args['_conflicts'] = conflicts + if (deleted := _dict.get('_deleted')) is not None: + args['_deleted'] = deleted + if (deleted_conflicts := _dict.get('_deleted_conflicts')) is not None: + args['_deleted_conflicts'] = deleted_conflicts + if (id := _dict.get('_id')) is not None: + args['_id'] = id + if (local_seq := _dict.get('_local_seq')) is not None: + args['_local_seq'] = local_seq + if (rev := _dict.get('_rev')) is not None: + args['_rev'] = rev + if (revisions := _dict.get('_revisions')) is not None: + args['_revisions'] = Revisions.from_dict(revisions) + if (revs_info := _dict.get('_revs_info')) is not None: + args['_revs_info'] = [DocumentRevisionStatus.from_dict(v) for v in revs_info] + if (cancel := _dict.get('cancel')) is not None: + args['cancel'] = cancel + if (checkpoint_interval := _dict.get('checkpoint_interval')) is not None: + args['checkpoint_interval'] = checkpoint_interval + if (connection_timeout := _dict.get('connection_timeout')) is not None: + args['connection_timeout'] = connection_timeout + if (continuous := _dict.get('continuous')) is not None: + args['continuous'] = continuous + if (create_target := _dict.get('create_target')) is not None: + args['create_target'] = create_target + if (create_target_params := _dict.get('create_target_params')) is not None: + args['create_target_params'] = ReplicationCreateTargetParameters.from_dict(create_target_params) + if (doc_ids := _dict.get('doc_ids')) is not None: + args['doc_ids'] = doc_ids + if (filter := _dict.get('filter')) is not None: + args['filter'] = filter + if (http_connections := _dict.get('http_connections')) is not None: + args['http_connections'] = http_connections + if (owner := _dict.get('owner')) is not None: + args['owner'] = owner + if (query_params := _dict.get('query_params')) is not None: + args['query_params'] = query_params + if (retries_per_request := _dict.get('retries_per_request')) is not None: + args['retries_per_request'] = retries_per_request + if (selector := _dict.get('selector')) is not None: + args['selector'] = selector + if (since_seq := _dict.get('since_seq')) is not None: + args['since_seq'] = since_seq + if (socket_options := _dict.get('socket_options')) is not None: + args['socket_options'] = socket_options + if (source := _dict.get('source')) is not None: + args['source'] = ReplicationDatabase.from_dict(source) else: raise ValueError('Required property \'source\' not present in ReplicationDocument JSON') - if 'source_proxy' in _dict: - args['source_proxy'] = _dict.get('source_proxy') - if 'target' in _dict: - args['target'] = ReplicationDatabase.from_dict(_dict.get('target')) + if (source_proxy := _dict.get('source_proxy')) is not None: + args['source_proxy'] = source_proxy + if (target := _dict.get('target')) is not None: + args['target'] = ReplicationDatabase.from_dict(target) else: raise ValueError('Required property \'target\' not present in ReplicationDocument JSON') - if 'target_proxy' in _dict: - args['target_proxy'] = _dict.get('target_proxy') - if 'use_checkpoints' in _dict: - args['use_checkpoints'] = _dict.get('use_checkpoints') - if 'user_ctx' in _dict: - args['user_ctx'] = UserContext.from_dict(_dict.get('user_ctx')) - if 'worker_batch_size' in _dict: - args['worker_batch_size'] = _dict.get('worker_batch_size') - if 'worker_processes' in _dict: - args['worker_processes'] = _dict.get('worker_processes') - args.update({k:v for (k, v) in _dict.items() if k not in cls._properties}) + if (target_proxy := _dict.get('target_proxy')) is not None: + args['target_proxy'] = target_proxy + if (use_bulk_get := _dict.get('use_bulk_get')) is not None: + args['use_bulk_get'] = use_bulk_get + if (use_checkpoints := _dict.get('use_checkpoints')) is not None: + args['use_checkpoints'] = use_checkpoints + if (user_ctx := _dict.get('user_ctx')) is not None: + args['user_ctx'] = UserContext.from_dict(user_ctx) + if (winning_revs_only := _dict.get('winning_revs_only')) is not None: + args['winning_revs_only'] = winning_revs_only + if (worker_batch_size := _dict.get('worker_batch_size')) is not None: + args['worker_batch_size'] = worker_batch_size + if (worker_processes := _dict.get('worker_processes')) is not None: + args['worker_processes'] = worker_processes + for k, v in _dict.items(): + if k not in cls._properties: + if not isinstance(v, object): + raise ValueError('Value for additional property {} must be of type object'.format(k)) + args[k] = v return cls(**args) @classmethod @@ -14718,24 +16839,39 @@ def _from_dict(cls, _dict): def to_dict(self) -> Dict: """Return a json dictionary representing this model.""" _dict = {} - if hasattr(self, 'attachments') and self.attachments is not None: - _dict['_attachments'] = {k : v.to_dict() for k, v in self.attachments.items()} - if hasattr(self, 'conflicts') and self.conflicts is not None: - _dict['_conflicts'] = self.conflicts - if hasattr(self, 'deleted') and self.deleted is not None: - _dict['_deleted'] = self.deleted - if hasattr(self, 'deleted_conflicts') and self.deleted_conflicts is not None: - _dict['_deleted_conflicts'] = self.deleted_conflicts - if hasattr(self, 'id') and self.id is not None: - _dict['_id'] = self.id - if hasattr(self, 'local_seq') and self.local_seq is not None: - _dict['_local_seq'] = self.local_seq - if hasattr(self, 'rev') and self.rev is not None: - _dict['_rev'] = self.rev - if hasattr(self, 'revisions') and self.revisions is not None: - _dict['_revisions'] = self.revisions.to_dict() - if hasattr(self, 'revs_info') and self.revs_info is not None: - _dict['_revs_info'] = [x.to_dict() for x in self.revs_info] + if hasattr(self, '_attachments') and self._attachments is not None: + _attachments_map = {} + for k, v in self._attachments.items(): + if isinstance(v, dict): + _attachments_map[k] = v + else: + _attachments_map[k] = v.to_dict() + _dict['_attachments'] = _attachments_map + if hasattr(self, '_conflicts') and self._conflicts is not None: + _dict['_conflicts'] = self._conflicts + if hasattr(self, '_deleted') and self._deleted is not None: + _dict['_deleted'] = self._deleted + if hasattr(self, '_deleted_conflicts') and self._deleted_conflicts is not None: + _dict['_deleted_conflicts'] = self._deleted_conflicts + if hasattr(self, '_id') and self._id is not None: + _dict['_id'] = self._id + if hasattr(self, '_local_seq') and self._local_seq is not None: + _dict['_local_seq'] = self._local_seq + if hasattr(self, '_rev') and self._rev is not None: + _dict['_rev'] = self._rev + if hasattr(self, '_revisions') and self._revisions is not None: + if isinstance(self._revisions, dict): + _dict['_revisions'] = self._revisions + else: + _dict['_revisions'] = self._revisions.to_dict() + if hasattr(self, '_revs_info') and self._revs_info is not None: + _revs_info_list = [] + for v in self._revs_info: + if isinstance(v, dict): + _revs_info_list.append(v) + else: + _revs_info_list.append(v.to_dict()) + _dict['_revs_info'] = _revs_info_list if hasattr(self, 'cancel') and self.cancel is not None: _dict['cancel'] = self.cancel if hasattr(self, 'checkpoint_interval') and self.checkpoint_interval is not None: @@ -14747,13 +16883,18 @@ def to_dict(self) -> Dict: if hasattr(self, 'create_target') and self.create_target is not None: _dict['create_target'] = self.create_target if hasattr(self, 'create_target_params') and self.create_target_params is not None: - _dict['create_target_params'] = self.create_target_params.to_dict() + if isinstance(self.create_target_params, dict): + _dict['create_target_params'] = self.create_target_params + else: + _dict['create_target_params'] = self.create_target_params.to_dict() if hasattr(self, 'doc_ids') and self.doc_ids is not None: _dict['doc_ids'] = self.doc_ids if hasattr(self, 'filter') and self.filter is not None: _dict['filter'] = self.filter if hasattr(self, 'http_connections') and self.http_connections is not None: _dict['http_connections'] = self.http_connections + if hasattr(self, 'owner') and self.owner is not None: + _dict['owner'] = self.owner if hasattr(self, 'query_params') and self.query_params is not None: _dict['query_params'] = self.query_params if hasattr(self, 'retries_per_request') and self.retries_per_request is not None: @@ -14765,24 +16906,36 @@ def to_dict(self) -> Dict: if hasattr(self, 'socket_options') and self.socket_options is not None: _dict['socket_options'] = self.socket_options if hasattr(self, 'source') and self.source is not None: - _dict['source'] = self.source.to_dict() + if isinstance(self.source, dict): + _dict['source'] = self.source + else: + _dict['source'] = self.source.to_dict() if hasattr(self, 'source_proxy') and self.source_proxy is not None: _dict['source_proxy'] = self.source_proxy if hasattr(self, 'target') and self.target is not None: - _dict['target'] = self.target.to_dict() + if isinstance(self.target, dict): + _dict['target'] = self.target + else: + _dict['target'] = self.target.to_dict() if hasattr(self, 'target_proxy') and self.target_proxy is not None: _dict['target_proxy'] = self.target_proxy + if hasattr(self, 'use_bulk_get') and self.use_bulk_get is not None: + _dict['use_bulk_get'] = self.use_bulk_get if hasattr(self, 'use_checkpoints') and self.use_checkpoints is not None: _dict['use_checkpoints'] = self.use_checkpoints if hasattr(self, 'user_ctx') and self.user_ctx is not None: - _dict['user_ctx'] = self.user_ctx.to_dict() + if isinstance(self.user_ctx, dict): + _dict['user_ctx'] = self.user_ctx + else: + _dict['user_ctx'] = self.user_ctx.to_dict() + if hasattr(self, 'winning_revs_only') and self.winning_revs_only is not None: + _dict['winning_revs_only'] = self.winning_revs_only if hasattr(self, 'worker_batch_size') and self.worker_batch_size is not None: _dict['worker_batch_size'] = self.worker_batch_size if hasattr(self, 'worker_processes') and self.worker_processes is not None: _dict['worker_processes'] = self.worker_processes - for _key in [k for k in vars(self).keys() if k not in ReplicationDocument._properties]: - if getattr(self, _key, None) is not None: - _dict[_key] = getattr(self, _key) + for k in [_k for _k in vars(self).keys() if _k not in ReplicationDocument._properties]: + _dict[k] = getattr(self, k) return _dict def _to_dict(self): @@ -14790,21 +16943,23 @@ def _to_dict(self): return self.to_dict() def get_properties(self) -> Dict: - """Return a dictionary of arbitrary properties from this instance of ReplicationDocument""" + """Return the additional properties from this instance of ReplicationDocument in the form of a dict.""" _dict = {} - - for _key in [k for k in vars(self).keys() if k not in ReplicationDocument._properties]: - _dict[_key] = getattr(self, _key) + for k in [_k for _k in vars(self).keys() if _k not in ReplicationDocument._properties]: + _dict[k] = getattr(self, k) return _dict def set_properties(self, _dict: dict): - """Set a dictionary of arbitrary properties to this instance of ReplicationDocument""" - for _key in [k for k in vars(self).keys() if k not in ReplicationDocument._properties]: - delattr(self, _key) - - for _key, _value in _dict.items(): - if _key not in ReplicationDocument._properties: - setattr(self, _key, _value) + """Set a dictionary of additional properties in this instance of ReplicationDocument""" + for k in [_k for _k in vars(self).keys() if _k not in ReplicationDocument._properties]: + delattr(self, k) + for k, v in _dict.items(): + if k not in ReplicationDocument._properties: + if not isinstance(v, object): + raise ValueError('Value for additional property {} must be of type object'.format(k)) + setattr(self, k, v) + else: + raise ValueError('Property {} cannot be specified as an additional property'.format(k)) def __str__(self) -> str: """Return a `str` version of this ReplicationDocument object.""" @@ -14820,18 +16975,21 @@ def __ne__(self, other: 'ReplicationDocument') -> bool: """Return `true` when self and other are not equal, false otherwise.""" return not self == other -class Revisions(): + +class Revisions: """ Schema for list of revision information. - :attr List[str] ids: Array of valid revision IDs, in reverse order (latest + :param List[str] ids: Array of valid revision IDs, in reverse order (latest first). - :attr int start: Prefix number for the latest revision. + :param int start: Prefix number for the latest revision. """ - def __init__(self, - ids: List[str], - start: int) -> None: + def __init__( + self, + ids: List[str], + start: int, + ) -> None: """ Initialize a Revisions object. @@ -14846,12 +17004,12 @@ def __init__(self, def from_dict(cls, _dict: Dict) -> 'Revisions': """Initialize a Revisions object from a json dictionary.""" args = {} - if 'ids' in _dict: - args['ids'] = _dict.get('ids') + if (ids := _dict.get('ids')) is not None: + args['ids'] = ids else: raise ValueError('Required property \'ids\' not present in Revisions JSON') - if 'start' in _dict: - args['start'] = _dict.get('start') + if (start := _dict.get('start')) is not None: + args['start'] = start else: raise ValueError('Required property \'start\' not present in Revisions JSON') return cls(**args) @@ -14888,19 +17046,22 @@ def __ne__(self, other: 'Revisions') -> bool: """Return `true` when self and other are not equal, false otherwise.""" return not self == other -class RevsDiff(): + +class RevsDiff: """ Schema for information about missing revs and possible ancestors. - :attr List[str] missing: (optional) List of missing revisions. - :attr List[str] possible_ancestors: (optional) List of possible ancestor + :param List[str] missing: (optional) List of missing revisions. + :param List[str] possible_ancestors: (optional) List of possible ancestor revisions. """ - def __init__(self, - *, - missing: List[str] = None, - possible_ancestors: List[str] = None) -> None: + def __init__( + self, + *, + missing: Optional[List[str]] = None, + possible_ancestors: Optional[List[str]] = None, + ) -> None: """ Initialize a RevsDiff object. @@ -14915,10 +17076,10 @@ def __init__(self, def from_dict(cls, _dict: Dict) -> 'RevsDiff': """Initialize a RevsDiff object from a json dictionary.""" args = {} - if 'missing' in _dict: - args['missing'] = _dict.get('missing') - if 'possible_ancestors' in _dict: - args['possible_ancestors'] = _dict.get('possible_ancestors') + if (missing := _dict.get('missing')) is not None: + args['missing'] = missing + if (possible_ancestors := _dict.get('possible_ancestors')) is not None: + args['possible_ancestors'] = possible_ancestors return cls(**args) @classmethod @@ -14953,21 +17114,24 @@ def __ne__(self, other: 'RevsDiff') -> bool: """Return `true` when self and other are not equal, false otherwise.""" return not self == other -class SchedulerDocsResult(): + +class SchedulerDocsResult: """ Schema for a listing of replication scheduler documents. - :attr int total_rows: Number of total rows. - :attr List[SchedulerDocument] docs: Array of replication scheduler doc objects. + :param int total_rows: Total number of replication scheduler documents. + :param List[SchedulerDocument] docs: Array of replication scheduler doc objects. """ - def __init__(self, - total_rows: int, - docs: List['SchedulerDocument']) -> None: + def __init__( + self, + total_rows: int, + docs: List['SchedulerDocument'], + ) -> None: """ Initialize a SchedulerDocsResult object. - :param int total_rows: Number of total rows. + :param int total_rows: Total number of replication scheduler documents. :param List[SchedulerDocument] docs: Array of replication scheduler doc objects. """ @@ -14978,12 +17142,12 @@ def __init__(self, def from_dict(cls, _dict: Dict) -> 'SchedulerDocsResult': """Initialize a SchedulerDocsResult object from a json dictionary.""" args = {} - if 'total_rows' in _dict: - args['total_rows'] = _dict.get('total_rows') + if (total_rows := _dict.get('total_rows')) is not None: + args['total_rows'] = total_rows else: raise ValueError('Required property \'total_rows\' not present in SchedulerDocsResult JSON') - if 'docs' in _dict: - args['docs'] = [SchedulerDocument.from_dict(x) for x in _dict.get('docs')] + if (docs := _dict.get('docs')) is not None: + args['docs'] = [SchedulerDocument.from_dict(v) for v in docs] else: raise ValueError('Required property \'docs\' not present in SchedulerDocsResult JSON') return cls(**args) @@ -14999,7 +17163,13 @@ def to_dict(self) -> Dict: if hasattr(self, 'total_rows') and self.total_rows is not None: _dict['total_rows'] = self.total_rows if hasattr(self, 'docs') and self.docs is not None: - _dict['docs'] = [x.to_dict() for x in self.docs] + docs_list = [] + for v in self.docs: + if isinstance(v, dict): + docs_list.append(v) + else: + docs_list.append(v.to_dict()) + _dict['docs'] = docs_list return _dict def _to_dict(self): @@ -15020,48 +17190,55 @@ def __ne__(self, other: 'SchedulerDocsResult') -> bool: """Return `true` when self and other are not equal, false otherwise.""" return not self == other -class SchedulerDocument(): + +class SchedulerDocument: """ Schema for a replication scheduler document. - :attr str database: Database where replication document came from. - :attr str doc_id: Replication document ID. - :attr int error_count: Consecutive errors count. Indicates how many times in a + :param str database: Database where replication document came from. + :param str doc_id: Replication document ID. + :param int error_count: Consecutive errors count. Indicates how many times in a row this replication has crashed. Replication will be retried with an exponential backoff based on this number. As soon as the replication succeeds this count is reset to 0. To can be used to get an idea why a particular replication is not making progress. - :attr str id: Replication ID, or null if state is completed or failed. - :attr SchedulerInfo info: Schema for scheduler document information. A JSON + :param str id: Replication ID, or null if state is completed or failed. + :param SchedulerInfo info: Schema for scheduler document information. A JSON object that may contain additional information about the state. For error states this will contain an error field and string value. - :attr datetime last_updated: Timestamp of last state update. - :attr str node: (optional) Cluster node where the job is running. - :attr str source: (optional) Replication source. - :attr str source_proxy: (optional) Address of the (http or socks5 protocol) - proxy server through which replication with the source database occurs. - :attr datetime start_time: Timestamp of when the replication was started. - :attr str state: Schema for replication state. - :attr str target: (optional) Replication target. - :attr str target_proxy: (optional) Address of the (http or socks5 protocol) - proxy server through which replication with the target database occurs. + :param datetime last_updated: Timestamp of last state update. + :param str node: (optional) Cluster node where the job is running. + :param str source: (optional) Replication source. + :param str source_proxy: (optional) Deprecated: Forbidden in IBM Cloudant + mediated replications. + Address of the (http or socks5 protocol) proxy server through which replication + with the source database occurs. + :param datetime start_time: Timestamp of when the replication was started. + :param str state: Schema for replication state. + :param str target: (optional) Replication target. + :param str target_proxy: (optional) Deprecated: Forbidden in IBM Cloudant + mediated replications. + Address of the (http or socks5 protocol) proxy server through which replication + with the target database occurs. """ - def __init__(self, - database: str, - doc_id: str, - error_count: int, - id: str, - info: 'SchedulerInfo', - last_updated: datetime, - start_time: datetime, - state: str, - *, - node: str = None, - source: str = None, - source_proxy: str = None, - target: str = None, - target_proxy: str = None) -> None: + def __init__( + self, + database: str, + doc_id: str, + error_count: int, + id: str, + info: 'SchedulerInfo', + last_updated: datetime, + start_time: datetime, + state: str, + *, + node: Optional[str] = None, + source: Optional[str] = None, + source_proxy: Optional[str] = None, + target: Optional[str] = None, + target_proxy: Optional[str] = None, + ) -> None: """ Initialize a SchedulerDocument object. @@ -15081,13 +17258,15 @@ def __init__(self, :param str state: Schema for replication state. :param str node: (optional) Cluster node where the job is running. :param str source: (optional) Replication source. - :param str source_proxy: (optional) Address of the (http or socks5 - protocol) proxy server through which replication with the source database - occurs. + :param str source_proxy: (optional) Deprecated: Forbidden in IBM Cloudant + mediated replications. + Address of the (http or socks5 protocol) proxy server through which + replication with the source database occurs. :param str target: (optional) Replication target. - :param str target_proxy: (optional) Address of the (http or socks5 - protocol) proxy server through which replication with the target database - occurs. + :param str target_proxy: (optional) Deprecated: Forbidden in IBM Cloudant + mediated replications. + Address of the (http or socks5 protocol) proxy server through which + replication with the target database occurs. """ self.database = database self.doc_id = doc_id @@ -15107,48 +17286,48 @@ def __init__(self, def from_dict(cls, _dict: Dict) -> 'SchedulerDocument': """Initialize a SchedulerDocument object from a json dictionary.""" args = {} - if 'database' in _dict: - args['database'] = _dict.get('database') + if (database := _dict.get('database')) is not None: + args['database'] = database else: raise ValueError('Required property \'database\' not present in SchedulerDocument JSON') - if 'doc_id' in _dict: - args['doc_id'] = _dict.get('doc_id') + if (doc_id := _dict.get('doc_id')) is not None: + args['doc_id'] = doc_id else: raise ValueError('Required property \'doc_id\' not present in SchedulerDocument JSON') - if 'error_count' in _dict: - args['error_count'] = _dict.get('error_count') + if (error_count := _dict.get('error_count')) is not None: + args['error_count'] = error_count else: raise ValueError('Required property \'error_count\' not present in SchedulerDocument JSON') - if 'id' in _dict: - args['id'] = _dict.get('id') + if (id := _dict.get('id')) is not None: + args['id'] = id else: - raise ValueError('Required property \'id\' not present in SchedulerDocument JSON') - if 'info' in _dict: - args['info'] = SchedulerInfo.from_dict(_dict.get('info')) + args['id'] = None + if (info := _dict.get('info')) is not None: + args['info'] = SchedulerInfo.from_dict(info) else: - raise ValueError('Required property \'info\' not present in SchedulerDocument JSON') - if 'last_updated' in _dict: - args['last_updated'] = string_to_datetime(_dict.get('last_updated')) + args['info'] = None + if (last_updated := _dict.get('last_updated')) is not None: + args['last_updated'] = string_to_datetime(last_updated) else: raise ValueError('Required property \'last_updated\' not present in SchedulerDocument JSON') - if 'node' in _dict: - args['node'] = _dict.get('node') - if 'source' in _dict: - args['source'] = _dict.get('source') - if 'source_proxy' in _dict: - args['source_proxy'] = _dict.get('source_proxy') - if 'start_time' in _dict: - args['start_time'] = string_to_datetime(_dict.get('start_time')) + if (node := _dict.get('node')) is not None: + args['node'] = node + if (source := _dict.get('source')) is not None: + args['source'] = source + if (source_proxy := _dict.get('source_proxy')) is not None: + args['source_proxy'] = source_proxy + if (start_time := _dict.get('start_time')) is not None: + args['start_time'] = string_to_datetime(start_time) else: raise ValueError('Required property \'start_time\' not present in SchedulerDocument JSON') - if 'state' in _dict: - args['state'] = _dict.get('state') + if (state := _dict.get('state')) is not None: + args['state'] = state else: raise ValueError('Required property \'state\' not present in SchedulerDocument JSON') - if 'target' in _dict: - args['target'] = _dict.get('target') - if 'target_proxy' in _dict: - args['target_proxy'] = _dict.get('target_proxy') + if (target := _dict.get('target')) is not None: + args['target'] = target + if (target_proxy := _dict.get('target_proxy')) is not None: + args['target_proxy'] = target_proxy return cls(**args) @classmethod @@ -15168,7 +17347,10 @@ def to_dict(self) -> Dict: if hasattr(self, 'id') and self.id is not None: _dict['id'] = self.id if hasattr(self, 'info') and self.info is not None: - _dict['info'] = self.info.to_dict() + if isinstance(self.info, dict): + _dict['info'] = self.info + else: + _dict['info'] = self.info.to_dict() if hasattr(self, 'last_updated') and self.last_updated is not None: _dict['last_updated'] = datetime_to_string(self.last_updated) if hasattr(self, 'node') and self.node is not None: @@ -15209,6 +17391,7 @@ class StateEnum(str, Enum): """ Schema for replication state. """ + INITIALIZING = 'initializing' ERROR = 'error' PENDING = 'pending' @@ -15218,44 +17401,47 @@ class StateEnum(str, Enum): FAILED = 'failed' -class SchedulerInfo(): + +class SchedulerInfo: """ Schema for scheduler document information. A JSON object that may contain additional information about the state. For error states this will contain an error field and string value. - :attr int changes_pending: (optional) The count of changes not yet replicated. - :attr str checkpointed_source_seq: (optional) The source sequence id which was + :param int changes_pending: (optional) The count of changes not yet replicated. + :param str checkpointed_source_seq: (optional) The source sequence id which was last successfully replicated. - :attr int doc_write_failures: (optional) The count of docs which failed to be + :param int doc_write_failures: (optional) The count of docs which failed to be written to the target. - :attr int docs_read: (optional) The count of docs which have been read from the + :param int docs_read: (optional) The count of docs which have been read from the source. - :attr int docs_written: (optional) The count of docs which have been written to + :param int docs_written: (optional) The count of docs which have been written to the target. - :attr str error: (optional) Replication error message. - :attr int missing_revisions_found: (optional) The count of revisions which were + :param str error: (optional) Replication error message. + :param int missing_revisions_found: (optional) The count of revisions which were found on the source, but missing from the target. - :attr int revisions_checked: (optional) The count of revisions which have been + :param int revisions_checked: (optional) The count of revisions which have been checked since this replication began. - :attr str source_seq: (optional) The last sequence number obtained from the + :param str source_seq: (optional) The last sequence number obtained from the source database changes feed. - :attr str through_seq: (optional) The last sequence number processed by the + :param str through_seq: (optional) The last sequence number processed by the replicator. """ - def __init__(self, - *, - changes_pending: int = None, - checkpointed_source_seq: str = None, - doc_write_failures: int = None, - docs_read: int = None, - docs_written: int = None, - error: str = None, - missing_revisions_found: int = None, - revisions_checked: int = None, - source_seq: str = None, - through_seq: str = None) -> None: + def __init__( + self, + *, + changes_pending: Optional[int] = None, + checkpointed_source_seq: Optional[str] = None, + doc_write_failures: Optional[int] = None, + docs_read: Optional[int] = None, + docs_written: Optional[int] = None, + error: Optional[str] = None, + missing_revisions_found: Optional[int] = None, + revisions_checked: Optional[int] = None, + source_seq: Optional[str] = None, + through_seq: Optional[str] = None, + ) -> None: """ Initialize a SchedulerInfo object. @@ -15294,26 +17480,26 @@ def __init__(self, def from_dict(cls, _dict: Dict) -> 'SchedulerInfo': """Initialize a SchedulerInfo object from a json dictionary.""" args = {} - if 'changes_pending' in _dict: - args['changes_pending'] = _dict.get('changes_pending') - if 'checkpointed_source_seq' in _dict: - args['checkpointed_source_seq'] = _dict.get('checkpointed_source_seq') - if 'doc_write_failures' in _dict: - args['doc_write_failures'] = _dict.get('doc_write_failures') - if 'docs_read' in _dict: - args['docs_read'] = _dict.get('docs_read') - if 'docs_written' in _dict: - args['docs_written'] = _dict.get('docs_written') - if 'error' in _dict: - args['error'] = _dict.get('error') - if 'missing_revisions_found' in _dict: - args['missing_revisions_found'] = _dict.get('missing_revisions_found') - if 'revisions_checked' in _dict: - args['revisions_checked'] = _dict.get('revisions_checked') - if 'source_seq' in _dict: - args['source_seq'] = _dict.get('source_seq') - if 'through_seq' in _dict: - args['through_seq'] = _dict.get('through_seq') + if (changes_pending := _dict.get('changes_pending')) is not None: + args['changes_pending'] = changes_pending + if (checkpointed_source_seq := _dict.get('checkpointed_source_seq')) is not None: + args['checkpointed_source_seq'] = checkpointed_source_seq + if (doc_write_failures := _dict.get('doc_write_failures')) is not None: + args['doc_write_failures'] = doc_write_failures + if (docs_read := _dict.get('docs_read')) is not None: + args['docs_read'] = docs_read + if (docs_written := _dict.get('docs_written')) is not None: + args['docs_written'] = docs_written + if (error := _dict.get('error')) is not None: + args['error'] = error + if (missing_revisions_found := _dict.get('missing_revisions_found')) is not None: + args['missing_revisions_found'] = missing_revisions_found + if (revisions_checked := _dict.get('revisions_checked')) is not None: + args['revisions_checked'] = revisions_checked + if (source_seq := _dict.get('source_seq')) is not None: + args['source_seq'] = source_seq + if (through_seq := _dict.get('through_seq')) is not None: + args['through_seq'] = through_seq return cls(**args) @classmethod @@ -15364,38 +17550,41 @@ def __ne__(self, other: 'SchedulerInfo') -> bool: """Return `true` when self and other are not equal, false otherwise.""" return not self == other -class SchedulerJob(): + +class SchedulerJob: """ Schema for a replication scheduler job. - :attr str database: Replication document database. - :attr str doc_id: Replication document ID. - :attr List[SchedulerJobEvent] history: Timestamped history of events as a list + :param str database: Replication document database. + :param str doc_id: Replication document ID. + :param List[SchedulerJobEvent] history: Timestamped history of events as a list of objects. - :attr str id: Schema for a replication job id. - :attr SchedulerInfo info: Schema for scheduler document information. A JSON + :param str id: Schema for a replication job id. + :param SchedulerInfo info: Schema for scheduler document information. A JSON object that may contain additional information about the state. For error states this will contain an error field and string value. - :attr str node: Cluster node where the job is running. - :attr str pid: Replication process ID. - :attr str source: Replication source. - :attr datetime start_time: Timestamp of when the replication was started. - :attr str target: Replication target. - :attr str user: Name of user running replication. + :param str node: Cluster node where the job is running. + :param str pid: Replication process ID. + :param str source: Replication source. + :param datetime start_time: Timestamp of when the replication was started. + :param str target: Replication target. + :param str user: Name of user running the process. """ - def __init__(self, - database: str, - doc_id: str, - history: List['SchedulerJobEvent'], - id: str, - info: 'SchedulerInfo', - node: str, - pid: str, - source: str, - start_time: datetime, - target: str, - user: str) -> None: + def __init__( + self, + database: str, + doc_id: str, + history: List['SchedulerJobEvent'], + id: str, + info: 'SchedulerInfo', + node: str, + pid: str, + source: str, + start_time: datetime, + target: str, + user: str, + ) -> None: """ Initialize a SchedulerJob object. @@ -15412,7 +17601,7 @@ def __init__(self, :param str source: Replication source. :param datetime start_time: Timestamp of when the replication was started. :param str target: Replication target. - :param str user: Name of user running replication. + :param str user: Name of user running the process. """ self.database = database self.doc_id = doc_id @@ -15430,50 +17619,50 @@ def __init__(self, def from_dict(cls, _dict: Dict) -> 'SchedulerJob': """Initialize a SchedulerJob object from a json dictionary.""" args = {} - if 'database' in _dict: - args['database'] = _dict.get('database') + if (database := _dict.get('database')) is not None: + args['database'] = database else: raise ValueError('Required property \'database\' not present in SchedulerJob JSON') - if 'doc_id' in _dict: - args['doc_id'] = _dict.get('doc_id') + if (doc_id := _dict.get('doc_id')) is not None: + args['doc_id'] = doc_id else: raise ValueError('Required property \'doc_id\' not present in SchedulerJob JSON') - if 'history' in _dict: - args['history'] = [SchedulerJobEvent.from_dict(x) for x in _dict.get('history')] + if (history := _dict.get('history')) is not None: + args['history'] = [SchedulerJobEvent.from_dict(v) for v in history] else: raise ValueError('Required property \'history\' not present in SchedulerJob JSON') - if 'id' in _dict: - args['id'] = _dict.get('id') + if (id := _dict.get('id')) is not None: + args['id'] = id else: raise ValueError('Required property \'id\' not present in SchedulerJob JSON') - if 'info' in _dict: - args['info'] = SchedulerInfo.from_dict(_dict.get('info')) + if (info := _dict.get('info')) is not None: + args['info'] = SchedulerInfo.from_dict(info) else: - raise ValueError('Required property \'info\' not present in SchedulerJob JSON') - if 'node' in _dict: - args['node'] = _dict.get('node') + args['info'] = None + if (node := _dict.get('node')) is not None: + args['node'] = node else: raise ValueError('Required property \'node\' not present in SchedulerJob JSON') - if 'pid' in _dict: - args['pid'] = _dict.get('pid') + if (pid := _dict.get('pid')) is not None: + args['pid'] = pid else: - raise ValueError('Required property \'pid\' not present in SchedulerJob JSON') - if 'source' in _dict: - args['source'] = _dict.get('source') + args['pid'] = None + if (source := _dict.get('source')) is not None: + args['source'] = source else: raise ValueError('Required property \'source\' not present in SchedulerJob JSON') - if 'start_time' in _dict: - args['start_time'] = string_to_datetime(_dict.get('start_time')) + if (start_time := _dict.get('start_time')) is not None: + args['start_time'] = string_to_datetime(start_time) else: raise ValueError('Required property \'start_time\' not present in SchedulerJob JSON') - if 'target' in _dict: - args['target'] = _dict.get('target') + if (target := _dict.get('target')) is not None: + args['target'] = target else: raise ValueError('Required property \'target\' not present in SchedulerJob JSON') - if 'user' in _dict: - args['user'] = _dict.get('user') + if (user := _dict.get('user')) is not None: + args['user'] = user else: - raise ValueError('Required property \'user\' not present in SchedulerJob JSON') + args['user'] = None return cls(**args) @classmethod @@ -15489,11 +17678,20 @@ def to_dict(self) -> Dict: if hasattr(self, 'doc_id') and self.doc_id is not None: _dict['doc_id'] = self.doc_id if hasattr(self, 'history') and self.history is not None: - _dict['history'] = [x.to_dict() for x in self.history] + history_list = [] + for v in self.history: + if isinstance(v, dict): + history_list.append(v) + else: + history_list.append(v.to_dict()) + _dict['history'] = history_list if hasattr(self, 'id') and self.id is not None: _dict['id'] = self.id if hasattr(self, 'info') and self.info is not None: - _dict['info'] = self.info.to_dict() + if isinstance(self.info, dict): + _dict['info'] = self.info + else: + _dict['info'] = self.info.to_dict() if hasattr(self, 'node') and self.node is not None: _dict['node'] = self.node if hasattr(self, 'pid') and self.pid is not None: @@ -15526,20 +17724,23 @@ def __ne__(self, other: 'SchedulerJob') -> bool: """Return `true` when self and other are not equal, false otherwise.""" return not self == other -class SchedulerJobEvent(): + +class SchedulerJobEvent: """ Schema for a replication scheduler job event. - :attr str reason: (optional) Reason for current state of event. - :attr datetime timestamp: Timestamp of the event. - :attr str type: Type of the event. + :param str reason: (optional) Reason for current state of event. + :param datetime timestamp: Timestamp of the event. + :param str type: Type of the event. """ - def __init__(self, - timestamp: datetime, - type: str, - *, - reason: str = None) -> None: + def __init__( + self, + timestamp: datetime, + type: str, + *, + reason: Optional[str] = None, + ) -> None: """ Initialize a SchedulerJobEvent object. @@ -15555,14 +17756,14 @@ def __init__(self, def from_dict(cls, _dict: Dict) -> 'SchedulerJobEvent': """Initialize a SchedulerJobEvent object from a json dictionary.""" args = {} - if 'reason' in _dict: - args['reason'] = _dict.get('reason') - if 'timestamp' in _dict: - args['timestamp'] = string_to_datetime(_dict.get('timestamp')) + if (reason := _dict.get('reason')) is not None: + args['reason'] = reason + if (timestamp := _dict.get('timestamp')) is not None: + args['timestamp'] = string_to_datetime(timestamp) else: raise ValueError('Required property \'timestamp\' not present in SchedulerJobEvent JSON') - if 'type' in _dict: - args['type'] = _dict.get('type') + if (type := _dict.get('type')) is not None: + args['type'] = type else: raise ValueError('Required property \'type\' not present in SchedulerJobEvent JSON') return cls(**args) @@ -15601,21 +17802,24 @@ def __ne__(self, other: 'SchedulerJobEvent') -> bool: """Return `true` when self and other are not equal, false otherwise.""" return not self == other -class SchedulerJobsResult(): + +class SchedulerJobsResult: """ Schema for a listing of replication scheduler jobs. - :attr int total_rows: Number of total rows. - :attr List[SchedulerJob] jobs: Array of replication job objects. + :param int total_rows: Total number of replication jobs. + :param List[SchedulerJob] jobs: Array of replication job objects. """ - def __init__(self, - total_rows: int, - jobs: List['SchedulerJob']) -> None: + def __init__( + self, + total_rows: int, + jobs: List['SchedulerJob'], + ) -> None: """ Initialize a SchedulerJobsResult object. - :param int total_rows: Number of total rows. + :param int total_rows: Total number of replication jobs. :param List[SchedulerJob] jobs: Array of replication job objects. """ self.total_rows = total_rows @@ -15625,12 +17829,12 @@ def __init__(self, def from_dict(cls, _dict: Dict) -> 'SchedulerJobsResult': """Initialize a SchedulerJobsResult object from a json dictionary.""" args = {} - if 'total_rows' in _dict: - args['total_rows'] = _dict.get('total_rows') + if (total_rows := _dict.get('total_rows')) is not None: + args['total_rows'] = total_rows else: raise ValueError('Required property \'total_rows\' not present in SchedulerJobsResult JSON') - if 'jobs' in _dict: - args['jobs'] = [SchedulerJob.from_dict(x) for x in _dict.get('jobs')] + if (jobs := _dict.get('jobs')) is not None: + args['jobs'] = [SchedulerJob.from_dict(v) for v in jobs] else: raise ValueError('Required property \'jobs\' not present in SchedulerJobsResult JSON') return cls(**args) @@ -15646,7 +17850,13 @@ def to_dict(self) -> Dict: if hasattr(self, 'total_rows') and self.total_rows is not None: _dict['total_rows'] = self.total_rows if hasattr(self, 'jobs') and self.jobs is not None: - _dict['jobs'] = [x.to_dict() for x in self.jobs] + jobs_list = [] + for v in self.jobs: + if isinstance(v, dict): + jobs_list.append(v) + else: + jobs_list.append(v.to_dict()) + _dict['jobs'] = jobs_list return _dict def _to_dict(self): @@ -15667,15 +17877,18 @@ def __ne__(self, other: 'SchedulerJobsResult') -> bool: """Return `true` when self and other are not equal, false otherwise.""" return not self == other -class SearchAnalyzeResult(): + +class SearchAnalyzeResult: """ Schema for the output of testing search analyzer tokenization. - :attr List[str] tokens: tokens. + :param List[str] tokens: tokens. """ - def __init__(self, - tokens: List[str]) -> None: + def __init__( + self, + tokens: List[str], + ) -> None: """ Initialize a SearchAnalyzeResult object. @@ -15687,8 +17900,8 @@ def __init__(self, def from_dict(cls, _dict: Dict) -> 'SearchAnalyzeResult': """Initialize a SearchAnalyzeResult object from a json dictionary.""" args = {} - if 'tokens' in _dict: - args['tokens'] = _dict.get('tokens') + if (tokens := _dict.get('tokens')) is not None: + args['tokens'] = tokens else: raise ValueError('Required property \'tokens\' not present in SearchAnalyzeResult JSON') return cls(**args) @@ -15723,36 +17936,116 @@ def __ne__(self, other: 'SearchAnalyzeResult') -> bool: """Return `true` when self and other are not equal, false otherwise.""" return not self == other -class SearchIndexDefinition(): + +class SearchDiskSizeInformation: + """ + Schema for search index disk size. + + :param str name: The name of the search index prefixed by the design document ID + where the index is stored. + :param SearchIndexDiskSize search_index: Schema for search index disk size. + """ + + def __init__( + self, + name: str, + search_index: 'SearchIndexDiskSize', + ) -> None: + """ + Initialize a SearchDiskSizeInformation object. + + :param str name: The name of the search index prefixed by the design + document ID where the index is stored. + :param SearchIndexDiskSize search_index: Schema for search index disk size. + """ + self.name = name + self.search_index = search_index + + @classmethod + def from_dict(cls, _dict: Dict) -> 'SearchDiskSizeInformation': + """Initialize a SearchDiskSizeInformation object from a json dictionary.""" + args = {} + if (name := _dict.get('name')) is not None: + args['name'] = name + else: + raise ValueError('Required property \'name\' not present in SearchDiskSizeInformation JSON') + if (search_index := _dict.get('search_index')) is not None: + args['search_index'] = SearchIndexDiskSize.from_dict(search_index) + else: + raise ValueError('Required property \'search_index\' not present in SearchDiskSizeInformation JSON') + return cls(**args) + + @classmethod + def _from_dict(cls, _dict): + """Initialize a SearchDiskSizeInformation object from a json dictionary.""" + return cls.from_dict(_dict) + + def to_dict(self) -> Dict: + """Return a json dictionary representing this model.""" + _dict = {} + if hasattr(self, 'name') and self.name is not None: + _dict['name'] = self.name + if hasattr(self, 'search_index') and self.search_index is not None: + if isinstance(self.search_index, dict): + _dict['search_index'] = self.search_index + else: + _dict['search_index'] = self.search_index.to_dict() + return _dict + + def _to_dict(self): + """Return a json dictionary representing this model.""" + return self.to_dict() + + def __str__(self) -> str: + """Return a `str` version of this SearchDiskSizeInformation object.""" + return json.dumps(self.to_dict(), indent=2) + + def __eq__(self, other: 'SearchDiskSizeInformation') -> bool: + """Return `true` when self and other are equal, false otherwise.""" + if not isinstance(other, self.__class__): + return False + return self.__dict__ == other.__dict__ + + def __ne__(self, other: 'SearchDiskSizeInformation') -> bool: + """Return `true` when self and other are not equal, false otherwise.""" + return not self == other + + +class SearchIndexDefinition: """ Schema for a search index definition. - :attr AnalyzerConfiguration analyzer: (optional) Schema for a search analyzer - configuration. - :attr str index: String form of a JavaScript function that is called for each + :param AnalyzerConfiguration analyzer: (optional) Analyzer configuration for + search indexes. The default and fields properties are only applicable for the + `perfield` analyzer name. + :param str index: String form of a JavaScript function that is called for each document in the database. The function takes the document as a parameter, extracts some data from it, and then calls the `index` function to index that data. The index function takes 2, or optionally 3, parameters. - * The first parameter is the name of the field you intend to use when - querying the index. If the special value `"default"` is used when you - define the name, you do not have to specify a field name at query time. - * The second parameter is the data to be indexed. This data must be only a - string, number, or boolean. Other types will cause an error to be thrown - by the index function call. + * The first parameter is the name of the field you intend to use + when querying the index. If the special value `"default"` is used + when you define the name, you do not have to specify a field name + at query time. + * The second parameter is the data to be indexed. This data must be + only a string, number, or boolean. Other types will cause an error + to be thrown by the index function call. * The optional third parameter is a JavaScript object with these properties: * `facet` - boolean, default `false` - Creates a faceted index. - * `index` - boolean, default `true` - If set to `false`, the data - cannot be used for searches, but can still be retrieved from the - index if `store` is set to `true`. - * `store` - boolean, default `true` - If true, the value is returned - in the search result; otherwise, the value is not returned. + * `index` - boolean, default `true` - If set to `false`, the + data cannot be used for searches, but can still be retrieved + from the index if `store` is set to `true`. + * `store` - boolean, default `true` - If true, the value is + returned in the search result; otherwise, the value is not + returned. """ - def __init__(self, - index: str, - *, - analyzer: 'AnalyzerConfiguration' = None) -> None: + def __init__( + self, + index: str, + *, + analyzer: Optional['AnalyzerConfiguration'] = None, + ) -> None: """ Initialize a SearchIndexDefinition object. @@ -15761,22 +18054,25 @@ def __init__(self, parameter, extracts some data from it, and then calls the `index` function to index that data. The index function takes 2, or optionally 3, parameters. - * The first parameter is the name of the field you intend to use when - querying the index. If the special value `"default"` is used when you - define the name, you do not have to specify a field name at query time. - * The second parameter is the data to be indexed. This data must be only a - string, number, or boolean. Other types will cause an error to be thrown - by the index function call. + * The first parameter is the name of the field you intend to use + when querying the index. If the special value `"default"` is used + when you define the name, you do not have to specify a field name + at query time. + * The second parameter is the data to be indexed. This data must be + only a string, number, or boolean. Other types will cause an error + to be thrown by the index function call. * The optional third parameter is a JavaScript object with these properties: * `facet` - boolean, default `false` - Creates a faceted index. - * `index` - boolean, default `true` - If set to `false`, the data - cannot be used for searches, but can still be retrieved from the - index if `store` is set to `true`. - * `store` - boolean, default `true` - If true, the value is returned - in the search result; otherwise, the value is not returned. - :param AnalyzerConfiguration analyzer: (optional) Schema for a search - analyzer configuration. + * `index` - boolean, default `true` - If set to `false`, the + data cannot be used for searches, but can still be retrieved + from the index if `store` is set to `true`. + * `store` - boolean, default `true` - If true, the value is + returned in the search result; otherwise, the value is not + returned. + :param AnalyzerConfiguration analyzer: (optional) Analyzer configuration + for search indexes. The default and fields properties are only applicable + for the `perfield` analyzer name. """ self.analyzer = analyzer self.index = index @@ -15785,10 +18081,10 @@ def __init__(self, def from_dict(cls, _dict: Dict) -> 'SearchIndexDefinition': """Initialize a SearchIndexDefinition object from a json dictionary.""" args = {} - if 'analyzer' in _dict: - args['analyzer'] = AnalyzerConfiguration.from_dict(_dict.get('analyzer')) - if 'index' in _dict: - args['index'] = _dict.get('index') + if (analyzer := _dict.get('analyzer')) is not None: + args['analyzer'] = AnalyzerConfiguration.from_dict(analyzer) + if (index := _dict.get('index')) is not None: + args['index'] = index else: raise ValueError('Required property \'index\' not present in SearchIndexDefinition JSON') return cls(**args) @@ -15802,7 +18098,10 @@ def to_dict(self) -> Dict: """Return a json dictionary representing this model.""" _dict = {} if hasattr(self, 'analyzer') and self.analyzer is not None: - _dict['analyzer'] = self.analyzer.to_dict() + if isinstance(self.analyzer, dict): + _dict['analyzer'] = self.analyzer + else: + _dict['analyzer'] = self.analyzer.to_dict() if hasattr(self, 'index') and self.index is not None: _dict['index'] = self.index return _dict @@ -15825,25 +18124,86 @@ def __ne__(self, other: 'SearchIndexDefinition') -> bool: """Return `true` when self and other are not equal, false otherwise.""" return not self == other -class SearchIndexInfo(): + +class SearchIndexDiskSize: + """ + Schema for search index disk size. + + :param int disk_size: (optional) The size of the search index on disk. + """ + + def __init__( + self, + *, + disk_size: Optional[int] = None, + ) -> None: + """ + Initialize a SearchIndexDiskSize object. + + :param int disk_size: (optional) The size of the search index on disk. + """ + self.disk_size = disk_size + + @classmethod + def from_dict(cls, _dict: Dict) -> 'SearchIndexDiskSize': + """Initialize a SearchIndexDiskSize object from a json dictionary.""" + args = {} + if (disk_size := _dict.get('disk_size')) is not None: + args['disk_size'] = disk_size + return cls(**args) + + @classmethod + def _from_dict(cls, _dict): + """Initialize a SearchIndexDiskSize object from a json dictionary.""" + return cls.from_dict(_dict) + + def to_dict(self) -> Dict: + """Return a json dictionary representing this model.""" + _dict = {} + if hasattr(self, 'disk_size') and self.disk_size is not None: + _dict['disk_size'] = self.disk_size + return _dict + + def _to_dict(self): + """Return a json dictionary representing this model.""" + return self.to_dict() + + def __str__(self) -> str: + """Return a `str` version of this SearchIndexDiskSize object.""" + return json.dumps(self.to_dict(), indent=2) + + def __eq__(self, other: 'SearchIndexDiskSize') -> bool: + """Return `true` when self and other are equal, false otherwise.""" + if not isinstance(other, self.__class__): + return False + return self.__dict__ == other.__dict__ + + def __ne__(self, other: 'SearchIndexDiskSize') -> bool: + """Return `true` when self and other are not equal, false otherwise.""" + return not self == other + + +class SearchIndexInfo: """ Schema for metadata information about a search index. - :attr int committed_seq: The committed sequence identifier. - :attr int disk_size: The size of the search index on disk. - :attr int doc_count: The count of the number of indexed documents. - :attr int doc_del_count: The number of deleted documents. - :attr int pending_seq: The pending sequence identifier. - :attr str signature: Unique signature of the search index. + :param int committed_seq: The committed sequence identifier. + :param int disk_size: The size of the search index on disk. + :param int doc_count: The count of the number of indexed documents. + :param int doc_del_count: The number of deleted documents. + :param int pending_seq: The pending sequence identifier. + :param str signature: Unique signature of the search index. """ - def __init__(self, - committed_seq: int, - disk_size: int, - doc_count: int, - doc_del_count: int, - pending_seq: int, - signature: str) -> None: + def __init__( + self, + committed_seq: int, + disk_size: int, + doc_count: int, + doc_del_count: int, + pending_seq: int, + signature: str, + ) -> None: """ Initialize a SearchIndexInfo object. @@ -15865,28 +18225,28 @@ def __init__(self, def from_dict(cls, _dict: Dict) -> 'SearchIndexInfo': """Initialize a SearchIndexInfo object from a json dictionary.""" args = {} - if 'committed_seq' in _dict: - args['committed_seq'] = _dict.get('committed_seq') + if (committed_seq := _dict.get('committed_seq')) is not None: + args['committed_seq'] = committed_seq else: raise ValueError('Required property \'committed_seq\' not present in SearchIndexInfo JSON') - if 'disk_size' in _dict: - args['disk_size'] = _dict.get('disk_size') + if (disk_size := _dict.get('disk_size')) is not None: + args['disk_size'] = disk_size else: raise ValueError('Required property \'disk_size\' not present in SearchIndexInfo JSON') - if 'doc_count' in _dict: - args['doc_count'] = _dict.get('doc_count') + if (doc_count := _dict.get('doc_count')) is not None: + args['doc_count'] = doc_count else: raise ValueError('Required property \'doc_count\' not present in SearchIndexInfo JSON') - if 'doc_del_count' in _dict: - args['doc_del_count'] = _dict.get('doc_del_count') + if (doc_del_count := _dict.get('doc_del_count')) is not None: + args['doc_del_count'] = doc_del_count else: raise ValueError('Required property \'doc_del_count\' not present in SearchIndexInfo JSON') - if 'pending_seq' in _dict: - args['pending_seq'] = _dict.get('pending_seq') + if (pending_seq := _dict.get('pending_seq')) is not None: + args['pending_seq'] = pending_seq else: raise ValueError('Required property \'pending_seq\' not present in SearchIndexInfo JSON') - if 'signature' in _dict: - args['signature'] = _dict.get('signature') + if (signature := _dict.get('signature')) is not None: + args['signature'] = signature else: raise ValueError('Required property \'signature\' not present in SearchIndexInfo JSON') return cls(**args) @@ -15931,19 +18291,22 @@ def __ne__(self, other: 'SearchIndexInfo') -> bool: """Return `true` when self and other are not equal, false otherwise.""" return not self == other -class SearchInfoResult(): + +class SearchInfoResult: """ Schema for search index information. - :attr str name: The name of the search index prefixed by the design document ID + :param str name: The name of the search index prefixed by the design document ID where the index is stored. - :attr SearchIndexInfo search_index: Schema for metadata information about a + :param SearchIndexInfo search_index: Schema for metadata information about a search index. """ - def __init__(self, - name: str, - search_index: 'SearchIndexInfo') -> None: + def __init__( + self, + name: str, + search_index: 'SearchIndexInfo', + ) -> None: """ Initialize a SearchInfoResult object. @@ -15959,12 +18322,12 @@ def __init__(self, def from_dict(cls, _dict: Dict) -> 'SearchInfoResult': """Initialize a SearchInfoResult object from a json dictionary.""" args = {} - if 'name' in _dict: - args['name'] = _dict.get('name') + if (name := _dict.get('name')) is not None: + args['name'] = name else: raise ValueError('Required property \'name\' not present in SearchInfoResult JSON') - if 'search_index' in _dict: - args['search_index'] = SearchIndexInfo.from_dict(_dict.get('search_index')) + if (search_index := _dict.get('search_index')) is not None: + args['search_index'] = SearchIndexInfo.from_dict(search_index) else: raise ValueError('Required property \'search_index\' not present in SearchInfoResult JSON') return cls(**args) @@ -15980,7 +18343,10 @@ def to_dict(self) -> Dict: if hasattr(self, 'name') and self.name is not None: _dict['name'] = self.name if hasattr(self, 'search_index') and self.search_index is not None: - _dict['search_index'] = self.search_index.to_dict() + if isinstance(self.search_index, dict): + _dict['search_index'] = self.search_index + else: + _dict['search_index'] = self.search_index.to_dict() return _dict def _to_dict(self): @@ -16001,37 +18367,43 @@ def __ne__(self, other: 'SearchInfoResult') -> bool: """Return `true` when self and other are not equal, false otherwise.""" return not self == other -class SearchResult(): + +class SearchResult: """ Schema for the result of a query search operation. - :attr int total_rows: Number of total rows. - :attr str bookmark: (optional) Opaque bookmark token used when paginating + :param int total_rows: Total number of rows in the index matching the search + query. The limit may truncate the number of matches returned. + :param str bookmark: (optional) Opaque bookmark token used when paginating results. - :attr str by: (optional) Grouped search matches. - :attr dict counts: (optional) The counts facet syntax returns the number of + :param str by: (optional) Grouped search matches. + :param dict counts: (optional) The counts facet syntax returns the number of query results for each unique value of each named field. - :attr dict ranges: (optional) The range facet syntax reuses the standard Lucene + :param dict ranges: (optional) The range facet syntax reuses the standard Lucene syntax for ranges to return counts of results that fit into each specified category. - :attr List[SearchResultRow] rows: (optional) Array of row objects. - :attr List[SearchResultProperties] groups: (optional) Array of grouped search + :param List[SearchResultRow] rows: Array of row objects. + :param List[SearchResultProperties] groups: (optional) Array of grouped search matches. """ - def __init__(self, - total_rows: int, - *, - bookmark: str = None, - by: str = None, - counts: dict = None, - ranges: dict = None, - rows: List['SearchResultRow'] = None, - groups: List['SearchResultProperties'] = None) -> None: + def __init__( + self, + total_rows: int, + rows: List['SearchResultRow'], + *, + bookmark: Optional[str] = None, + by: Optional[str] = None, + counts: Optional[dict] = None, + ranges: Optional[dict] = None, + groups: Optional[List['SearchResultProperties']] = None, + ) -> None: """ Initialize a SearchResult object. - :param int total_rows: Number of total rows. + :param int total_rows: Total number of rows in the index matching the + search query. The limit may truncate the number of matches returned. + :param List[SearchResultRow] rows: Array of row objects. :param str bookmark: (optional) Opaque bookmark token used when paginating results. :param str by: (optional) Grouped search matches. @@ -16040,7 +18412,6 @@ def __init__(self, :param dict ranges: (optional) The range facet syntax reuses the standard Lucene syntax for ranges to return counts of results that fit into each specified category. - :param List[SearchResultRow] rows: (optional) Array of row objects. :param List[SearchResultProperties] groups: (optional) Array of grouped search matches. """ @@ -16056,22 +18427,24 @@ def __init__(self, def from_dict(cls, _dict: Dict) -> 'SearchResult': """Initialize a SearchResult object from a json dictionary.""" args = {} - if 'total_rows' in _dict: - args['total_rows'] = _dict.get('total_rows') + if (total_rows := _dict.get('total_rows')) is not None: + args['total_rows'] = total_rows else: raise ValueError('Required property \'total_rows\' not present in SearchResult JSON') - if 'bookmark' in _dict: - args['bookmark'] = _dict.get('bookmark') - if 'by' in _dict: - args['by'] = _dict.get('by') - if 'counts' in _dict: - args['counts'] = _dict.get('counts') - if 'ranges' in _dict: - args['ranges'] = _dict.get('ranges') - if 'rows' in _dict: - args['rows'] = [SearchResultRow.from_dict(x) for x in _dict.get('rows')] - if 'groups' in _dict: - args['groups'] = [SearchResultProperties.from_dict(x) for x in _dict.get('groups')] + if (bookmark := _dict.get('bookmark')) is not None: + args['bookmark'] = bookmark + if (by := _dict.get('by')) is not None: + args['by'] = by + if (counts := _dict.get('counts')) is not None: + args['counts'] = counts + if (ranges := _dict.get('ranges')) is not None: + args['ranges'] = ranges + if (rows := _dict.get('rows')) is not None: + args['rows'] = [SearchResultRow.from_dict(v) for v in rows] + else: + raise ValueError('Required property \'rows\' not present in SearchResult JSON') + if (groups := _dict.get('groups')) is not None: + args['groups'] = [SearchResultProperties.from_dict(v) for v in groups] return cls(**args) @classmethod @@ -16093,9 +18466,21 @@ def to_dict(self) -> Dict: if hasattr(self, 'ranges') and self.ranges is not None: _dict['ranges'] = self.ranges if hasattr(self, 'rows') and self.rows is not None: - _dict['rows'] = [x.to_dict() for x in self.rows] + rows_list = [] + for v in self.rows: + if isinstance(v, dict): + rows_list.append(v) + else: + rows_list.append(v.to_dict()) + _dict['rows'] = rows_list if hasattr(self, 'groups') and self.groups is not None: - _dict['groups'] = [x.to_dict() for x in self.groups] + groups_list = [] + for v in self.groups: + if isinstance(v, dict): + groups_list.append(v) + else: + groups_list.append(v.to_dict()) + _dict['groups'] = groups_list return _dict def _to_dict(self): @@ -16116,34 +18501,40 @@ def __ne__(self, other: 'SearchResult') -> bool: """Return `true` when self and other are not equal, false otherwise.""" return not self == other -class SearchResultProperties(): + +class SearchResultProperties: """ Schema for the result of a query search operation. - :attr int total_rows: Number of total rows. - :attr str bookmark: (optional) Opaque bookmark token used when paginating + :param int total_rows: Total number of rows in the index matching the search + query. The limit may truncate the number of matches returned. + :param str bookmark: (optional) Opaque bookmark token used when paginating results. - :attr str by: (optional) Grouped search matches. - :attr dict counts: (optional) The counts facet syntax returns the number of + :param str by: (optional) Grouped search matches. + :param dict counts: (optional) The counts facet syntax returns the number of query results for each unique value of each named field. - :attr dict ranges: (optional) The range facet syntax reuses the standard Lucene + :param dict ranges: (optional) The range facet syntax reuses the standard Lucene syntax for ranges to return counts of results that fit into each specified category. - :attr List[SearchResultRow] rows: (optional) Array of row objects. + :param List[SearchResultRow] rows: Array of row objects. """ - def __init__(self, - total_rows: int, - *, - bookmark: str = None, - by: str = None, - counts: dict = None, - ranges: dict = None, - rows: List['SearchResultRow'] = None) -> None: + def __init__( + self, + total_rows: int, + rows: List['SearchResultRow'], + *, + bookmark: Optional[str] = None, + by: Optional[str] = None, + counts: Optional[dict] = None, + ranges: Optional[dict] = None, + ) -> None: """ Initialize a SearchResultProperties object. - :param int total_rows: Number of total rows. + :param int total_rows: Total number of rows in the index matching the + search query. The limit may truncate the number of matches returned. + :param List[SearchResultRow] rows: Array of row objects. :param str bookmark: (optional) Opaque bookmark token used when paginating results. :param str by: (optional) Grouped search matches. @@ -16152,7 +18543,6 @@ def __init__(self, :param dict ranges: (optional) The range facet syntax reuses the standard Lucene syntax for ranges to return counts of results that fit into each specified category. - :param List[SearchResultRow] rows: (optional) Array of row objects. """ self.total_rows = total_rows self.bookmark = bookmark @@ -16165,20 +18555,22 @@ def __init__(self, def from_dict(cls, _dict: Dict) -> 'SearchResultProperties': """Initialize a SearchResultProperties object from a json dictionary.""" args = {} - if 'total_rows' in _dict: - args['total_rows'] = _dict.get('total_rows') + if (total_rows := _dict.get('total_rows')) is not None: + args['total_rows'] = total_rows else: raise ValueError('Required property \'total_rows\' not present in SearchResultProperties JSON') - if 'bookmark' in _dict: - args['bookmark'] = _dict.get('bookmark') - if 'by' in _dict: - args['by'] = _dict.get('by') - if 'counts' in _dict: - args['counts'] = _dict.get('counts') - if 'ranges' in _dict: - args['ranges'] = _dict.get('ranges') - if 'rows' in _dict: - args['rows'] = [SearchResultRow.from_dict(x) for x in _dict.get('rows')] + if (bookmark := _dict.get('bookmark')) is not None: + args['bookmark'] = bookmark + if (by := _dict.get('by')) is not None: + args['by'] = by + if (counts := _dict.get('counts')) is not None: + args['counts'] = counts + if (ranges := _dict.get('ranges')) is not None: + args['ranges'] = ranges + if (rows := _dict.get('rows')) is not None: + args['rows'] = [SearchResultRow.from_dict(v) for v in rows] + else: + raise ValueError('Required property \'rows\' not present in SearchResultProperties JSON') return cls(**args) @classmethod @@ -16200,7 +18592,13 @@ def to_dict(self) -> Dict: if hasattr(self, 'ranges') and self.ranges is not None: _dict['ranges'] = self.ranges if hasattr(self, 'rows') and self.rows is not None: - _dict['rows'] = [x.to_dict() for x in self.rows] + rows_list = [] + for v in self.rows: + if isinstance(v, dict): + rows_list.append(v) + else: + rows_list.append(v.to_dict()) + _dict['rows'] = rows_list return _dict def _to_dict(self): @@ -16221,24 +18619,27 @@ def __ne__(self, other: 'SearchResultProperties') -> bool: """Return `true` when self and other are not equal, false otherwise.""" return not self == other -class SearchResultRow(): + +class SearchResultRow: """ Schema for a row of the result of a query search operation. - :attr Document doc: (optional) Schema for a document. - :attr dict fields: Schema for the fields returned by a query search operation, a - map of field name to value. - :attr dict highlights: (optional) Returns the context in which a search term was - mentioned so that you can display more emphasized results to a user. - :attr str id: Schema for a document ID. + :param Document doc: (optional) Schema for a document. + :param dict fields: Schema for the fields returned by a query search operation, + a map of field name to value. + :param dict highlights: (optional) Returns the context in which a search term + was mentioned so that you can display more emphasized results to a user. + :param str id: Schema for a document ID. """ - def __init__(self, - fields: dict, - id: str, - *, - doc: 'Document' = None, - highlights: dict = None) -> None: + def __init__( + self, + fields: dict, + id: str, + *, + doc: Optional['Document'] = None, + highlights: Optional[dict] = None, + ) -> None: """ Initialize a SearchResultRow object. @@ -16259,16 +18660,16 @@ def __init__(self, def from_dict(cls, _dict: Dict) -> 'SearchResultRow': """Initialize a SearchResultRow object from a json dictionary.""" args = {} - if 'doc' in _dict: - args['doc'] = Document.from_dict(_dict.get('doc')) - if 'fields' in _dict: - args['fields'] = _dict.get('fields') + if (doc := _dict.get('doc')) is not None: + args['doc'] = Document.from_dict(doc) + if (fields := _dict.get('fields')) is not None: + args['fields'] = fields else: raise ValueError('Required property \'fields\' not present in SearchResultRow JSON') - if 'highlights' in _dict: - args['highlights'] = _dict.get('highlights') - if 'id' in _dict: - args['id'] = _dict.get('id') + if (highlights := _dict.get('highlights')) is not None: + args['highlights'] = highlights + if (id := _dict.get('id')) is not None: + args['id'] = id else: raise ValueError('Required property \'id\' not present in SearchResultRow JSON') return cls(**args) @@ -16282,7 +18683,10 @@ def to_dict(self) -> Dict: """Return a json dictionary representing this model.""" _dict = {} if hasattr(self, 'doc') and self.doc is not None: - _dict['doc'] = self.doc.to_dict() + if isinstance(self.doc, dict): + _dict['doc'] = self.doc + else: + _dict['doc'] = self.doc.to_dict() if hasattr(self, 'fields') and self.fields is not None: _dict['fields'] = self.fields if hasattr(self, 'highlights') and self.highlights is not None: @@ -16309,55 +18713,58 @@ def __ne__(self, other: 'SearchResultRow') -> bool: """Return `true` when self and other are not equal, false otherwise.""" return not self == other -class Security(): + +class Security: """ - Schema for a security document. + Schema for a Cloudant security document. - :attr SecurityObject admins: (optional) Schema for names and roles to map to a - database permission. - :attr SecurityObject members: (optional) Schema for names and roles to map to a + :param SecurityObject admins: (optional) Schema for names and roles to map to a database permission. - :attr dict cloudant: (optional) Database permissions for Cloudant users and/or - API keys. - :attr bool couchdb_auth_only: (optional) Manage permissions using the `_users` + :param dict cloudant: (optional) Database permissions for Cloudant users, API + keys or both. + :param bool couchdb_auth_only: (optional) Manage permissions using the `_users` database only. + :param SecurityObject members: (optional) Schema for names and roles to map to a + database permission. """ - def __init__(self, - *, - admins: 'SecurityObject' = None, - members: 'SecurityObject' = None, - cloudant: dict = None, - couchdb_auth_only: bool = None) -> None: + def __init__( + self, + *, + admins: Optional['SecurityObject'] = None, + cloudant: Optional[dict] = None, + couchdb_auth_only: Optional[bool] = None, + members: Optional['SecurityObject'] = None, + ) -> None: """ Initialize a Security object. :param SecurityObject admins: (optional) Schema for names and roles to map to a database permission. - :param SecurityObject members: (optional) Schema for names and roles to map - to a database permission. - :param dict cloudant: (optional) Database permissions for Cloudant users - and/or API keys. + :param dict cloudant: (optional) Database permissions for Cloudant users, + API keys or both. :param bool couchdb_auth_only: (optional) Manage permissions using the `_users` database only. + :param SecurityObject members: (optional) Schema for names and roles to map + to a database permission. """ self.admins = admins - self.members = members self.cloudant = cloudant self.couchdb_auth_only = couchdb_auth_only + self.members = members @classmethod def from_dict(cls, _dict: Dict) -> 'Security': """Initialize a Security object from a json dictionary.""" args = {} - if 'admins' in _dict: - args['admins'] = SecurityObject.from_dict(_dict.get('admins')) - if 'members' in _dict: - args['members'] = SecurityObject.from_dict(_dict.get('members')) - if 'cloudant' in _dict: - args['cloudant'] = _dict.get('cloudant') - if 'couchdb_auth_only' in _dict: - args['couchdb_auth_only'] = _dict.get('couchdb_auth_only') + if (admins := _dict.get('admins')) is not None: + args['admins'] = SecurityObject.from_dict(admins) + if (cloudant := _dict.get('cloudant')) is not None: + args['cloudant'] = cloudant + if (couchdb_auth_only := _dict.get('couchdb_auth_only')) is not None: + args['couchdb_auth_only'] = couchdb_auth_only + if (members := _dict.get('members')) is not None: + args['members'] = SecurityObject.from_dict(members) return cls(**args) @classmethod @@ -16369,13 +18776,19 @@ def to_dict(self) -> Dict: """Return a json dictionary representing this model.""" _dict = {} if hasattr(self, 'admins') and self.admins is not None: - _dict['admins'] = self.admins.to_dict() - if hasattr(self, 'members') and self.members is not None: - _dict['members'] = self.members.to_dict() + if isinstance(self.admins, dict): + _dict['admins'] = self.admins + else: + _dict['admins'] = self.admins.to_dict() if hasattr(self, 'cloudant') and self.cloudant is not None: _dict['cloudant'] = self.cloudant if hasattr(self, 'couchdb_auth_only') and self.couchdb_auth_only is not None: _dict['couchdb_auth_only'] = self.couchdb_auth_only + if hasattr(self, 'members') and self.members is not None: + if isinstance(self.members, dict): + _dict['members'] = self.members + else: + _dict['members'] = self.members.to_dict() return _dict def _to_dict(self): @@ -16398,8 +18811,9 @@ def __ne__(self, other: 'Security') -> bool: class CloudantEnum(str, Enum): """ - Database permissions for Cloudant users and/or API keys. + Database permissions for Cloudant users, API keys or both. """ + READER = '_reader' WRITER = '_writer' ADMIN = '_admin' @@ -16410,18 +18824,21 @@ class CloudantEnum(str, Enum): SECURITY = '_security' -class SecurityObject(): + +class SecurityObject: """ Schema for names and roles to map to a database permission. - :attr List[str] names: (optional) List of usernames. - :attr List[str] roles: (optional) List of roles. + :param List[str] names: (optional) List of usernames. + :param List[str] roles: (optional) List of roles. """ - def __init__(self, - *, - names: List[str] = None, - roles: List[str] = None) -> None: + def __init__( + self, + *, + names: Optional[List[str]] = None, + roles: Optional[List[str]] = None, + ) -> None: """ Initialize a SecurityObject object. @@ -16435,10 +18852,10 @@ def __init__(self, def from_dict(cls, _dict: Dict) -> 'SecurityObject': """Initialize a SecurityObject object from a json dictionary.""" args = {} - if 'names' in _dict: - args['names'] = _dict.get('names') - if 'roles' in _dict: - args['roles'] = _dict.get('roles') + if (names := _dict.get('names')) is not None: + args['names'] = names + if (roles := _dict.get('roles')) is not None: + args['roles'] = roles return cls(**args) @classmethod @@ -16473,62 +18890,157 @@ def __ne__(self, other: 'SecurityObject') -> bool: """Return `true` when self and other are not equal, false otherwise.""" return not self == other -class ServerInformation(): + +class SelectorHint: + """ + Schema for extra information on the selector. + + :param List[str] indexable_fields: A list of fields in the given selector that + can be used to restrict the query. + :param str type: A type of the index. + :param List[str] unindexable_fields: A list of fields in the given selector that + can't be used to restrict the query. + """ + + def __init__( + self, + indexable_fields: List[str], + type: str, + unindexable_fields: List[str], + ) -> None: + """ + Initialize a SelectorHint object. + + :param List[str] indexable_fields: A list of fields in the given selector + that can be used to restrict the query. + :param str type: A type of the index. + :param List[str] unindexable_fields: A list of fields in the given selector + that can't be used to restrict the query. + """ + self.indexable_fields = indexable_fields + self.type = type + self.unindexable_fields = unindexable_fields + + @classmethod + def from_dict(cls, _dict: Dict) -> 'SelectorHint': + """Initialize a SelectorHint object from a json dictionary.""" + args = {} + if (indexable_fields := _dict.get('indexable_fields')) is not None: + args['indexable_fields'] = indexable_fields + else: + raise ValueError('Required property \'indexable_fields\' not present in SelectorHint JSON') + if (type := _dict.get('type')) is not None: + args['type'] = type + else: + raise ValueError('Required property \'type\' not present in SelectorHint JSON') + if (unindexable_fields := _dict.get('unindexable_fields')) is not None: + args['unindexable_fields'] = unindexable_fields + else: + raise ValueError('Required property \'unindexable_fields\' not present in SelectorHint JSON') + return cls(**args) + + @classmethod + def _from_dict(cls, _dict): + """Initialize a SelectorHint object from a json dictionary.""" + return cls.from_dict(_dict) + + def to_dict(self) -> Dict: + """Return a json dictionary representing this model.""" + _dict = {} + if hasattr(self, 'indexable_fields') and self.indexable_fields is not None: + _dict['indexable_fields'] = self.indexable_fields + if hasattr(self, 'type') and self.type is not None: + _dict['type'] = self.type + if hasattr(self, 'unindexable_fields') and self.unindexable_fields is not None: + _dict['unindexable_fields'] = self.unindexable_fields + return _dict + + def _to_dict(self): + """Return a json dictionary representing this model.""" + return self.to_dict() + + def __str__(self) -> str: + """Return a `str` version of this SelectorHint object.""" + return json.dumps(self.to_dict(), indent=2) + + def __eq__(self, other: 'SelectorHint') -> bool: + """Return `true` when self and other are equal, false otherwise.""" + if not isinstance(other, self.__class__): + return False + return self.__dict__ == other.__dict__ + + def __ne__(self, other: 'SelectorHint') -> bool: + """Return `true` when self and other are not equal, false otherwise.""" + return not self == other + + class TypeEnum(str, Enum): + """ + A type of the index. + """ + + JSON = 'json' + TEXT = 'text' + + + +class ServerInformation: """ Schema for information about the server instance. - :attr str couchdb: Welcome message. - :attr List[str] features: List of enabled optional features. - :attr ServerVendor vendor: Schema for server vendor information. - :attr str version: Apache CouchDB version. - :attr List[str] features_flags: List of feature flags. + :param str couchdb: Welcome message. + :param List[str] features: List of enabled optional features. + :param List[str] features_flags: List of feature flags. + :param ServerVendor vendor: Schema for server vendor information. + :param str version: Apache CouchDB version. """ - def __init__(self, - couchdb: str, - features: List[str], - vendor: 'ServerVendor', - version: str, - features_flags: List[str]) -> None: + def __init__( + self, + couchdb: str, + features: List[str], + features_flags: List[str], + vendor: 'ServerVendor', + version: str, + ) -> None: """ Initialize a ServerInformation object. :param str couchdb: Welcome message. :param List[str] features: List of enabled optional features. + :param List[str] features_flags: List of feature flags. :param ServerVendor vendor: Schema for server vendor information. :param str version: Apache CouchDB version. - :param List[str] features_flags: List of feature flags. """ self.couchdb = couchdb self.features = features + self.features_flags = features_flags self.vendor = vendor self.version = version - self.features_flags = features_flags @classmethod def from_dict(cls, _dict: Dict) -> 'ServerInformation': """Initialize a ServerInformation object from a json dictionary.""" args = {} - if 'couchdb' in _dict: - args['couchdb'] = _dict.get('couchdb') + if (couchdb := _dict.get('couchdb')) is not None: + args['couchdb'] = couchdb else: raise ValueError('Required property \'couchdb\' not present in ServerInformation JSON') - if 'features' in _dict: - args['features'] = _dict.get('features') + if (features := _dict.get('features')) is not None: + args['features'] = features else: raise ValueError('Required property \'features\' not present in ServerInformation JSON') - if 'vendor' in _dict: - args['vendor'] = ServerVendor.from_dict(_dict.get('vendor')) + if (features_flags := _dict.get('features_flags')) is not None: + args['features_flags'] = features_flags + else: + raise ValueError('Required property \'features_flags\' not present in ServerInformation JSON') + if (vendor := _dict.get('vendor')) is not None: + args['vendor'] = ServerVendor.from_dict(vendor) else: raise ValueError('Required property \'vendor\' not present in ServerInformation JSON') - if 'version' in _dict: - args['version'] = _dict.get('version') + if (version := _dict.get('version')) is not None: + args['version'] = version else: raise ValueError('Required property \'version\' not present in ServerInformation JSON') - if 'features_flags' in _dict: - args['features_flags'] = _dict.get('features_flags') - else: - raise ValueError('Required property \'features_flags\' not present in ServerInformation JSON') return cls(**args) @classmethod @@ -16543,12 +19055,15 @@ def to_dict(self) -> Dict: _dict['couchdb'] = self.couchdb if hasattr(self, 'features') and self.features is not None: _dict['features'] = self.features + if hasattr(self, 'features_flags') and self.features_flags is not None: + _dict['features_flags'] = self.features_flags if hasattr(self, 'vendor') and self.vendor is not None: - _dict['vendor'] = self.vendor.to_dict() + if isinstance(self.vendor, dict): + _dict['vendor'] = self.vendor + else: + _dict['vendor'] = self.vendor.to_dict() if hasattr(self, 'version') and self.version is not None: _dict['version'] = self.version - if hasattr(self, 'features_flags') and self.features_flags is not None: - _dict['features_flags'] = self.features_flags return _dict def _to_dict(self): @@ -16569,43 +19084,68 @@ def __ne__(self, other: 'ServerInformation') -> bool: """Return `true` when self and other are not equal, false otherwise.""" return not self == other -class ServerVendor(): + +class ServerVendor: """ Schema for server vendor information. - :attr str name: Vendor name. - :attr str variant: (optional) Vendor variant. - :attr str version: (optional) Vendor version. + :param str name: Vendor name. + :param str variant: Vendor variant. + :param str version: Vendor version. + + This type supports additional properties of type str. """ - def __init__(self, - name: str, - *, - variant: str = None, - version: str = None) -> None: + # The set of defined properties for the class + _properties = frozenset(['name', 'variant', 'version']) + + def __init__( + self, + name: str, + variant: str, + version: str, + **kwargs: Optional[str], + ) -> None: """ Initialize a ServerVendor object. :param str name: Vendor name. - :param str variant: (optional) Vendor variant. - :param str version: (optional) Vendor version. + :param str variant: Vendor variant. + :param str version: Vendor version. + :param str **kwargs: (optional) Additional properties of type str """ self.name = name self.variant = variant self.version = version + for k, v in kwargs.items(): + if k not in ServerVendor._properties: + if not isinstance(v, str): + raise ValueError('Value for additional property {} must be of type str'.format(k)) + setattr(self, k, v) + else: + raise ValueError('Property {} cannot be specified as an additional property'.format(k)) @classmethod def from_dict(cls, _dict: Dict) -> 'ServerVendor': """Initialize a ServerVendor object from a json dictionary.""" args = {} - if 'name' in _dict: - args['name'] = _dict.get('name') + if (name := _dict.get('name')) is not None: + args['name'] = name else: raise ValueError('Required property \'name\' not present in ServerVendor JSON') - if 'variant' in _dict: - args['variant'] = _dict.get('variant') - if 'version' in _dict: - args['version'] = _dict.get('version') + if (variant := _dict.get('variant')) is not None: + args['variant'] = variant + else: + raise ValueError('Required property \'variant\' not present in ServerVendor JSON') + if (version := _dict.get('version')) is not None: + args['version'] = version + else: + raise ValueError('Required property \'version\' not present in ServerVendor JSON') + for k, v in _dict.items(): + if k not in cls._properties: + if not isinstance(v, str): + raise ValueError('Value for additional property {} must be of type str'.format(k)) + args[k] = v return cls(**args) @classmethod @@ -16622,12 +19162,33 @@ def to_dict(self) -> Dict: _dict['variant'] = self.variant if hasattr(self, 'version') and self.version is not None: _dict['version'] = self.version + for k in [_k for _k in vars(self).keys() if _k not in ServerVendor._properties]: + _dict[k] = getattr(self, k) return _dict def _to_dict(self): """Return a json dictionary representing this model.""" return self.to_dict() + def get_properties(self) -> Dict: + """Return the additional properties from this instance of ServerVendor in the form of a dict.""" + _dict = {} + for k in [_k for _k in vars(self).keys() if _k not in ServerVendor._properties]: + _dict[k] = getattr(self, k) + return _dict + + def set_properties(self, _dict: dict): + """Set a dictionary of additional properties in this instance of ServerVendor""" + for k in [_k for _k in vars(self).keys() if _k not in ServerVendor._properties]: + delattr(self, k) + for k, v in _dict.items(): + if k not in ServerVendor._properties: + if not isinstance(v, str): + raise ValueError('Value for additional property {} must be of type str'.format(k)) + setattr(self, k, v) + else: + raise ValueError('Property {} cannot be specified as an additional property'.format(k)) + def __str__(self) -> str: """Return a `str` version of this ServerVendor object.""" return json.dumps(self.to_dict(), indent=2) @@ -16642,20 +19203,31 @@ def __ne__(self, other: 'ServerVendor') -> bool: """Return `true` when self and other are not equal, false otherwise.""" return not self == other -class SessionAuthentication(): + class VariantEnum(str, Enum): + """ + Vendor variant. + """ + + PAAS = 'paas' + + + +class SessionAuthentication: """ Schema for session authentication information. - :attr str authenticated: (optional) authenticated. - :attr str authentication_db: (optional) authentication_db. - :attr List[str] authentication_handlers: authentication_handlers. + :param str authenticated: (optional) authenticated. + :param str authentication_db: (optional) authentication_db. + :param List[str] authentication_handlers: authentication_handlers. """ - def __init__(self, - authentication_handlers: List[str], - *, - authenticated: str = None, - authentication_db: str = None) -> None: + def __init__( + self, + authentication_handlers: List[str], + *, + authenticated: Optional[str] = None, + authentication_db: Optional[str] = None, + ) -> None: """ Initialize a SessionAuthentication object. @@ -16671,12 +19243,12 @@ def __init__(self, def from_dict(cls, _dict: Dict) -> 'SessionAuthentication': """Initialize a SessionAuthentication object from a json dictionary.""" args = {} - if 'authenticated' in _dict: - args['authenticated'] = _dict.get('authenticated') - if 'authentication_db' in _dict: - args['authentication_db'] = _dict.get('authentication_db') - if 'authentication_handlers' in _dict: - args['authentication_handlers'] = _dict.get('authentication_handlers') + if (authenticated := _dict.get('authenticated')) is not None: + args['authenticated'] = authenticated + if (authentication_db := _dict.get('authentication_db')) is not None: + args['authentication_db'] = authentication_db + if (authentication_handlers := _dict.get('authentication_handlers')) is not None: + args['authentication_handlers'] = authentication_handlers else: raise ValueError('Required property \'authentication_handlers\' not present in SessionAuthentication JSON') return cls(**args) @@ -16715,19 +19287,23 @@ def __ne__(self, other: 'SessionAuthentication') -> bool: """Return `true` when self and other are not equal, false otherwise.""" return not self == other -class SessionInformation(): + +class SessionInformation: """ Schema for information about a session. - :attr bool ok: ok. - :attr SessionAuthentication info: Schema for session authentication information. - :attr UserContext user_ctx: Schema for the user context of a session. + :param bool ok: ok. + :param SessionAuthentication info: Schema for session authentication + information. + :param UserContext user_ctx: Schema for the user context of a session. """ - def __init__(self, - ok: bool, - info: 'SessionAuthentication', - user_ctx: 'UserContext') -> None: + def __init__( + self, + ok: bool, + info: 'SessionAuthentication', + user_ctx: 'UserContext', + ) -> None: """ Initialize a SessionInformation object. @@ -16744,16 +19320,16 @@ def __init__(self, def from_dict(cls, _dict: Dict) -> 'SessionInformation': """Initialize a SessionInformation object from a json dictionary.""" args = {} - if 'ok' in _dict: - args['ok'] = _dict.get('ok') + if (ok := _dict.get('ok')) is not None: + args['ok'] = ok else: raise ValueError('Required property \'ok\' not present in SessionInformation JSON') - if 'info' in _dict: - args['info'] = SessionAuthentication.from_dict(_dict.get('info')) + if (info := _dict.get('info')) is not None: + args['info'] = SessionAuthentication.from_dict(info) else: raise ValueError('Required property \'info\' not present in SessionInformation JSON') - if 'userCtx' in _dict: - args['user_ctx'] = UserContext.from_dict(_dict.get('userCtx')) + if (user_ctx := _dict.get('userCtx')) is not None: + args['user_ctx'] = UserContext.from_dict(user_ctx) else: raise ValueError('Required property \'userCtx\' not present in SessionInformation JSON') return cls(**args) @@ -16769,9 +19345,15 @@ def to_dict(self) -> Dict: if hasattr(self, 'ok') and self.ok is not None: _dict['ok'] = self.ok if hasattr(self, 'info') and self.info is not None: - _dict['info'] = self.info.to_dict() + if isinstance(self.info, dict): + _dict['info'] = self.info + else: + _dict['info'] = self.info.to_dict() if hasattr(self, 'user_ctx') and self.user_ctx is not None: - _dict['userCtx'] = self.user_ctx.to_dict() + if isinstance(self.user_ctx, dict): + _dict['userCtx'] = self.user_ctx + else: + _dict['userCtx'] = self.user_ctx.to_dict() return _dict def _to_dict(self): @@ -16792,16 +19374,19 @@ def __ne__(self, other: 'SessionInformation') -> bool: """Return `true` when self and other are not equal, false otherwise.""" return not self == other -class ShardsInformation(): + +class ShardsInformation: """ Schema for a shards object that maps the hash value range for each shard to the array of nodes that contain a copy of that shard. - :attr dict shards: Mapping of shard hash value range to a list of nodes. + :param dict shards: Mapping of shard hash value range to a list of nodes. """ - def __init__(self, - shards: dict) -> None: + def __init__( + self, + shards: dict, + ) -> None: """ Initialize a ShardsInformation object. @@ -16813,8 +19398,8 @@ def __init__(self, def from_dict(cls, _dict: Dict) -> 'ShardsInformation': """Initialize a ShardsInformation object from a json dictionary.""" args = {} - if 'shards' in _dict: - args['shards'] = _dict.get('shards') + if (shards := _dict.get('shards')) is not None: + args['shards'] = shards else: raise ValueError('Required property \'shards\' not present in ShardsInformation JSON') return cls(**args) @@ -16849,34 +19434,38 @@ def __ne__(self, other: 'ShardsInformation') -> bool: """Return `true` when self and other are not equal, false otherwise.""" return not self == other -class ThroughputInformation(): + +class ThroughputInformation: """ Schema for detailed information about throughput capacity with breakdown by specific throughput requests classes. - :attr int blocks: A number of blocks of throughput units. A block consists of - 100 reads/sec, 50 writes/sec, and 5 global queries/sec of provisioned throughput - capacity. - :attr int query: Provisioned global queries capacity in operations per second. - :attr int read: Provisioned reads capacity in operations per second. - :attr int write: Provisioned writes capacity in operations per second. + :param int blocks: (optional) A number of blocks of throughput units. A block + consists of 100 reads/sec, 50 writes/sec, and 5 global queries/sec of + provisioned throughput capacity. Not available for some plans. + :param int query: Provisioned global queries capacity in operations per second. + :param int read: Provisioned reads capacity in operations per second. + :param int write: Provisioned writes capacity in operations per second. """ - def __init__(self, - blocks: int, - query: int, - read: int, - write: int) -> None: + def __init__( + self, + query: int, + read: int, + write: int, + *, + blocks: Optional[int] = None, + ) -> None: """ Initialize a ThroughputInformation object. - :param int blocks: A number of blocks of throughput units. A block consists - of 100 reads/sec, 50 writes/sec, and 5 global queries/sec of provisioned - throughput capacity. :param int query: Provisioned global queries capacity in operations per second. :param int read: Provisioned reads capacity in operations per second. :param int write: Provisioned writes capacity in operations per second. + :param int blocks: (optional) A number of blocks of throughput units. A + block consists of 100 reads/sec, 50 writes/sec, and 5 global queries/sec of + provisioned throughput capacity. Not available for some plans. """ self.blocks = blocks self.query = query @@ -16887,20 +19476,18 @@ def __init__(self, def from_dict(cls, _dict: Dict) -> 'ThroughputInformation': """Initialize a ThroughputInformation object from a json dictionary.""" args = {} - if 'blocks' in _dict: - args['blocks'] = _dict.get('blocks') - else: - raise ValueError('Required property \'blocks\' not present in ThroughputInformation JSON') - if 'query' in _dict: - args['query'] = _dict.get('query') + if (blocks := _dict.get('blocks')) is not None: + args['blocks'] = blocks + if (query := _dict.get('query')) is not None: + args['query'] = query else: raise ValueError('Required property \'query\' not present in ThroughputInformation JSON') - if 'read' in _dict: - args['read'] = _dict.get('read') + if (read := _dict.get('read')) is not None: + args['read'] = read else: raise ValueError('Required property \'read\' not present in ThroughputInformation JSON') - if 'write' in _dict: - args['write'] = _dict.get('write') + if (write := _dict.get('write')) is not None: + args['write'] = write else: raise ValueError('Required property \'write\' not present in ThroughputInformation JSON') return cls(**args) @@ -16941,23 +19528,31 @@ def __ne__(self, other: 'ThroughputInformation') -> bool: """Return `true` when self and other are not equal, false otherwise.""" return not self == other -class UpInformation(): + +class UpInformation: """ Schema for information about the up state of the server. - :attr object seeds: seeds. - :attr str status: status. + :param str cluster: (optional) cluster. + :param dict seeds: (optional) seeds. + :param str status: status. """ - def __init__(self, - seeds: object, - status: str) -> None: + def __init__( + self, + status: str, + *, + cluster: Optional[str] = None, + seeds: Optional[dict] = None, + ) -> None: """ Initialize a UpInformation object. - :param object seeds: seeds. :param str status: status. + :param str cluster: (optional) cluster. + :param dict seeds: (optional) seeds. """ + self.cluster = cluster self.seeds = seeds self.status = status @@ -16965,12 +19560,12 @@ def __init__(self, def from_dict(cls, _dict: Dict) -> 'UpInformation': """Initialize a UpInformation object from a json dictionary.""" args = {} - if 'seeds' in _dict: - args['seeds'] = _dict.get('seeds') - else: - raise ValueError('Required property \'seeds\' not present in UpInformation JSON') - if 'status' in _dict: - args['status'] = _dict.get('status') + if (cluster := _dict.get('cluster')) is not None: + args['cluster'] = cluster + if (seeds := _dict.get('seeds')) is not None: + args['seeds'] = seeds + if (status := _dict.get('status')) is not None: + args['status'] = status else: raise ValueError('Required property \'status\' not present in UpInformation JSON') return cls(**args) @@ -16983,6 +19578,8 @@ def _from_dict(cls, _dict): def to_dict(self) -> Dict: """Return a json dictionary representing this model.""" _dict = {} + if hasattr(self, 'cluster') and self.cluster is not None: + _dict['cluster'] = self.cluster if hasattr(self, 'seeds') and self.seeds is not None: _dict['seeds'] = self.seeds if hasattr(self, 'status') and self.status is not None: @@ -17011,29 +19608,118 @@ class StatusEnum(str, Enum): """ status. """ + MAINTENANCE_MODE = 'maintenance_mode' NOLB = 'nolb' OK = 'ok' -class UserContext(): + +class UpdatesPending: + """ + Schema for an ability to tell if view is up-to-date without querying it. + + :param int minimum: Sum of shard copies with the least amount of work to do. + :param int preferred: Sum of unique shards. This value is zero when at least one + copy of every shard range is up-to-date and the view is able to answer a query + without index building delays. + :param int total: Sum of all shard copies. + """ + + def __init__( + self, + minimum: int, + preferred: int, + total: int, + ) -> None: + """ + Initialize a UpdatesPending object. + + :param int minimum: Sum of shard copies with the least amount of work to + do. + :param int preferred: Sum of unique shards. This value is zero when at + least one copy of every shard range is up-to-date and the view is able to + answer a query without index building delays. + :param int total: Sum of all shard copies. + """ + self.minimum = minimum + self.preferred = preferred + self.total = total + + @classmethod + def from_dict(cls, _dict: Dict) -> 'UpdatesPending': + """Initialize a UpdatesPending object from a json dictionary.""" + args = {} + if (minimum := _dict.get('minimum')) is not None: + args['minimum'] = minimum + else: + raise ValueError('Required property \'minimum\' not present in UpdatesPending JSON') + if (preferred := _dict.get('preferred')) is not None: + args['preferred'] = preferred + else: + raise ValueError('Required property \'preferred\' not present in UpdatesPending JSON') + if (total := _dict.get('total')) is not None: + args['total'] = total + else: + raise ValueError('Required property \'total\' not present in UpdatesPending JSON') + return cls(**args) + + @classmethod + def _from_dict(cls, _dict): + """Initialize a UpdatesPending object from a json dictionary.""" + return cls.from_dict(_dict) + + def to_dict(self) -> Dict: + """Return a json dictionary representing this model.""" + _dict = {} + if hasattr(self, 'minimum') and self.minimum is not None: + _dict['minimum'] = self.minimum + if hasattr(self, 'preferred') and self.preferred is not None: + _dict['preferred'] = self.preferred + if hasattr(self, 'total') and self.total is not None: + _dict['total'] = self.total + return _dict + + def _to_dict(self): + """Return a json dictionary representing this model.""" + return self.to_dict() + + def __str__(self) -> str: + """Return a `str` version of this UpdatesPending object.""" + return json.dumps(self.to_dict(), indent=2) + + def __eq__(self, other: 'UpdatesPending') -> bool: + """Return `true` when self and other are equal, false otherwise.""" + if not isinstance(other, self.__class__): + return False + return self.__dict__ == other.__dict__ + + def __ne__(self, other: 'UpdatesPending') -> bool: + """Return `true` when self and other are not equal, false otherwise.""" + return not self == other + + +class UserContext: """ Schema for the user context of a session. - :attr str db: (optional) Database name in the context of the provided operation. - :attr str name: User name. - :attr List[str] roles: List of user roles. + :param str db: (optional) Database name in the context of the provided + operation. + :param str name: Name of user running the process. + :param List[str] roles: List of user roles. """ - def __init__(self, - name: str, - roles: List[str], - *, - db: str = None) -> None: + def __init__( + self, + name: str, + roles: List[str], + *, + db: Optional[str] = None, + ) -> None: """ Initialize a UserContext object. - :param str name: User name. + :param str name: Name of user running the process. :param List[str] roles: List of user roles. :param str db: (optional) Database name in the context of the provided operation. @@ -17046,14 +19732,14 @@ def __init__(self, def from_dict(cls, _dict: Dict) -> 'UserContext': """Initialize a UserContext object from a json dictionary.""" args = {} - if 'db' in _dict: - args['db'] = _dict.get('db') - if 'name' in _dict: - args['name'] = _dict.get('name') + if (db := _dict.get('db')) is not None: + args['db'] = db + if (name := _dict.get('name')) is not None: + args['name'] = name else: - raise ValueError('Required property \'name\' not present in UserContext JSON') - if 'roles' in _dict: - args['roles'] = _dict.get('roles') + args['name'] = None + if (roles := _dict.get('roles')) is not None: + args['roles'] = roles else: raise ValueError('Required property \'roles\' not present in UserContext JSON') return cls(**args) @@ -17096,6 +19782,7 @@ class RolesEnum(str, Enum): """ Schema for a security role. """ + READER = '_reader' WRITER = '_writer' ADMIN = '_admin' @@ -17106,15 +19793,18 @@ class RolesEnum(str, Enum): SECURITY = '_security' -class UuidsResult(): + +class UuidsResult: """ Schema for a set of uuids generated by the server. - :attr List[str] uuids: uuids. + :param List[str] uuids: uuids. """ - def __init__(self, - uuids: List[str]) -> None: + def __init__( + self, + uuids: List[str], + ) -> None: """ Initialize a UuidsResult object. @@ -17126,8 +19816,8 @@ def __init__(self, def from_dict(cls, _dict: Dict) -> 'UuidsResult': """Initialize a UuidsResult object from a json dictionary.""" args = {} - if 'uuids' in _dict: - args['uuids'] = _dict.get('uuids') + if (uuids := _dict.get('uuids')) is not None: + args['uuids'] = uuids else: raise ValueError('Required property \'uuids\' not present in UuidsResult JSON') return cls(**args) @@ -17162,17 +19852,20 @@ def __ne__(self, other: 'UuidsResult') -> bool: """Return `true` when self and other are not equal, false otherwise.""" return not self == other -class ViewQueriesResult(): + +class ViewQueriesResult: """ Schema for the results of a queries view operation. - :attr List[ViewResult] results: An array of result objects - one for each query. - Each result object contains the same fields as the response to a regular view - request. + :param List[ViewResult] results: An array of result objects - one for each + query. Each result object contains the same fields as the response to a regular + view request. """ - def __init__(self, - results: List['ViewResult']) -> None: + def __init__( + self, + results: List['ViewResult'], + ) -> None: """ Initialize a ViewQueriesResult object. @@ -17186,8 +19879,8 @@ def __init__(self, def from_dict(cls, _dict: Dict) -> 'ViewQueriesResult': """Initialize a ViewQueriesResult object from a json dictionary.""" args = {} - if 'results' in _dict: - args['results'] = [ViewResult.from_dict(x) for x in _dict.get('results')] + if (results := _dict.get('results')) is not None: + args['results'] = [ViewResult.from_dict(v) for v in results] else: raise ValueError('Required property \'results\' not present in ViewQueriesResult JSON') return cls(**args) @@ -17201,7 +19894,13 @@ def to_dict(self) -> Dict: """Return a json dictionary representing this model.""" _dict = {} if hasattr(self, 'results') and self.results is not None: - _dict['results'] = [x.to_dict() for x in self.results] + results_list = [] + for v in self.results: + if isinstance(v, dict): + results_list.append(v) + else: + results_list.append(v.to_dict()) + _dict['results'] = results_list return _dict def _to_dict(self): @@ -17222,60 +19921,71 @@ def __ne__(self, other: 'ViewQueriesResult') -> bool: """Return `true` when self and other are not equal, false otherwise.""" return not self == other -class ViewQuery(): + +class ViewQuery: """ Schema for a query view operation. - :attr bool att_encoding_info: (optional) Parameter to specify whether to include - the encoding information in attachment stubs if the particular attachment is - compressed. - :attr bool attachments: (optional) Parameter to specify whether to include - attachments bodies in a response. - :attr bool conflicts: (optional) Parameter to specify whether to include a list - of conflicted revisions in each returned document. Active only when - `include_docs` is `true`. - :attr bool descending: (optional) Parameter to specify whether to return the + :param bool att_encoding_info: (optional) Parameter to specify whether to + include the encoding information for compressed attachments. This only applies + when requesting documents in the response. + :param bool attachments: (optional) Parameter to specify whether to include + attachment content in included document content or only the attachment metadata. + This only applies when requesting documents in the response. + :param bool conflicts: (optional) Parameter to specify whether to include a list + of conflicted revisions in each returned document. This only applies when + requesting documents in the response. + :param bool descending: (optional) Parameter to specify whether to return the documents in descending by key order. - :attr bool include_docs: (optional) Parameter to specify whether to include the + :param bool include_docs: (optional) Parameter to specify whether to include the full content of the documents in the response. - :attr bool inclusive_end: (optional) Parameter to specify whether the specified - end key should be included in the result. - :attr int limit: (optional) Parameter to specify the number of returned + :param bool inclusive_end: (optional) Parameter to specify whether to include + the specified end key in the result. + :param int limit: (optional) Parameter to specify the number of returned documents to limit the result to. - :attr int skip: (optional) Parameter to specify the number of records before + :param int skip: (optional) Parameter to specify the number of records before starting to return the results. - :attr bool update_seq: (optional) Parameter to specify whether to include in the - response an update_seq value indicating the sequence id of the database the view - reflects. - :attr object end_key: (optional) Schema for any JSON type. - :attr str end_key_doc_id: (optional) Schema for a document ID. - :attr bool group: (optional) Parameter to specify whether to group reduced + :param bool update_seq: (optional) Parameter to specify whether to include in + the response an update_seq value indicating the sequence id of the database the + view reflects. + :param object end_key: (optional) Schema for any JSON type. + :param str end_key_doc_id: (optional) Stop returning rows at the specified + document ID. No effect if using `group` or not providing end key. + :param bool group: (optional) Parameter to specify whether to group reduced results by key. Valid only if a reduce function defined in the view. If the view emits key in JSON array format, then it is possible to reduce groups further based on the number of array elements with the `group_level` parameter. - :attr int group_level: (optional) Parameter to specify a group level to be used. - Only applicable if the view uses keys that are JSON arrays. Implies group is - `true`. Group level groups the reduced results by the specified number of array - elements. If unset, results are grouped by the entire array key, returning a - reduced value for each complete key. - :attr object key: (optional) Schema for any JSON type. - :attr List[object] keys: (optional) Parameter to specify returning only - documents that match any of the specified keys. A JSON array of keys that match - the key type emitted by the view function. - :attr bool reduce: (optional) Parameter to specify whether to use the reduce + :param int group_level: (optional) Parameter to specify a group level to be + used. Only applicable if the view uses keys that are JSON arrays. Implies group + is `true`. Group level groups the reduced results by the specified number of + array elements. If unset, results are grouped by the entire array key, returning + a reduced value for each complete key. + :param object key: (optional) Parameter to specify to return only rows that + match the specified key. String representation of any JSON type that matches the + key type emitted by the view function. + :param List[object] keys: (optional) Parameter to specify returning only rows + that match any of the specified keys. A JSON array of keys that match the key + type emitted by the view function. + :param bool reduce: (optional) Parameter to specify whether to use the reduce function in a map-reduce view. Default is true when a reduce function is defined. - :attr bool stable: (optional) Query parameter to specify whether use the same - replica of the index on each request. The default value `false` contacts all + A default `reduce` view type can be disabled to behave like a + `map` by setting `reduce=false` explicitly. + Be aware that `include_docs=true` is only for `map` views. Reduced views do not + include document IDs in view rows. + :param bool stable: (optional) Query parameter to specify whether use the same + replica of the index on each request. The default value `false` contacts all replicas and returns the result from the first, fastest, responder. Setting it - to `true` when used in conjunction with `update=false` may improve consistency - at the expense of increased latency and decreased throughput if the selected - replica is not the fastest of the available replicas. + to `true` when used in conjunction with + `update=false` may improve consistency at the expense of increased latency and + decreased throughput if the selected replica is not the fastest of the available + replicas. **Note:** In general setting `true` is discouraged and is strictly not recommended when using `update=true`. - :attr object start_key: (optional) Schema for any JSON type. - :attr str start_key_doc_id: (optional) Schema for a document ID. - :attr str update: (optional) Parameter to specify whether or not the view in + :param object start_key: (optional) Schema for any JSON type. + :param str start_key_doc_id: (optional) Start returning rows at the specified + document ID. No effect if using `group` or not providing start key. + :param str update: (optional) Parameter to specify whether or not the view in question should be updated prior to responding to the user. * `true` - Return results after the view is updated. * `false` - Return results without updating the view. @@ -17283,45 +19993,48 @@ class ViewQuery(): them immediately after the request. """ - def __init__(self, - *, - att_encoding_info: bool = None, - attachments: bool = None, - conflicts: bool = None, - descending: bool = None, - include_docs: bool = None, - inclusive_end: bool = None, - limit: int = None, - skip: int = None, - update_seq: bool = None, - end_key: object = None, - end_key_doc_id: str = None, - group: bool = None, - group_level: int = None, - key: object = None, - keys: List[object] = None, - reduce: bool = None, - stable: bool = None, - start_key: object = None, - start_key_doc_id: str = None, - update: str = None) -> None: + def __init__( + self, + *, + att_encoding_info: Optional[bool] = None, + attachments: Optional[bool] = None, + conflicts: Optional[bool] = None, + descending: Optional[bool] = None, + include_docs: Optional[bool] = None, + inclusive_end: Optional[bool] = None, + limit: Optional[int] = None, + skip: Optional[int] = None, + update_seq: Optional[bool] = None, + end_key: Optional[object] = None, + end_key_doc_id: Optional[str] = None, + group: Optional[bool] = None, + group_level: Optional[int] = None, + key: Optional[object] = None, + keys: Optional[List[object]] = None, + reduce: Optional[bool] = None, + stable: Optional[bool] = None, + start_key: Optional[object] = None, + start_key_doc_id: Optional[str] = None, + update: Optional[str] = None, + ) -> None: """ Initialize a ViewQuery object. :param bool att_encoding_info: (optional) Parameter to specify whether to - include the encoding information in attachment stubs if the particular - attachment is compressed. + include the encoding information for compressed attachments. This only + applies when requesting documents in the response. :param bool attachments: (optional) Parameter to specify whether to include - attachments bodies in a response. + attachment content in included document content or only the attachment + metadata. This only applies when requesting documents in the response. :param bool conflicts: (optional) Parameter to specify whether to include a - list of conflicted revisions in each returned document. Active only when - `include_docs` is `true`. + list of conflicted revisions in each returned document. This only applies + when requesting documents in the response. :param bool descending: (optional) Parameter to specify whether to return the documents in descending by key order. :param bool include_docs: (optional) Parameter to specify whether to include the full content of the documents in the response. - :param bool inclusive_end: (optional) Parameter to specify whether the - specified end key should be included in the result. + :param bool inclusive_end: (optional) Parameter to specify whether to + include the specified end key in the result. :param int limit: (optional) Parameter to specify the number of returned documents to limit the result to. :param int skip: (optional) Parameter to specify the number of records @@ -17330,7 +20043,8 @@ def __init__(self, in the response an update_seq value indicating the sequence id of the database the view reflects. :param object end_key: (optional) Schema for any JSON type. - :param str end_key_doc_id: (optional) Schema for a document ID. + :param str end_key_doc_id: (optional) Stop returning rows at the specified + document ID. No effect if using `group` or not providing end key. :param bool group: (optional) Parameter to specify whether to group reduced results by key. Valid only if a reduce function defined in the view. If the view emits key in JSON array format, then it is possible to reduce groups @@ -17341,24 +20055,32 @@ def __init__(self, group is `true`. Group level groups the reduced results by the specified number of array elements. If unset, results are grouped by the entire array key, returning a reduced value for each complete key. - :param object key: (optional) Schema for any JSON type. + :param object key: (optional) Parameter to specify to return only rows that + match the specified key. String representation of any JSON type that + matches the key type emitted by the view function. :param List[object] keys: (optional) Parameter to specify returning only - documents that match any of the specified keys. A JSON array of keys that - match the key type emitted by the view function. + rows that match any of the specified keys. A JSON array of keys that match + the key type emitted by the view function. :param bool reduce: (optional) Parameter to specify whether to use the reduce function in a map-reduce view. Default is true when a reduce function is defined. + A default `reduce` view type can be disabled to behave like a + `map` by setting `reduce=false` explicitly. + Be aware that `include_docs=true` is only for `map` views. Reduced views do + not include document IDs in view rows. :param bool stable: (optional) Query parameter to specify whether use the - same replica of the index on each request. The default value `false` - contacts all replicas and returns the result from the first, fastest, + same replica of the index on each request. The default value `false` + contacts all replicas and returns the result from the first, fastest, responder. Setting it to `true` when used in conjunction with - `update=false` may improve consistency at the expense of increased latency + `update=false` may improve consistency at the expense of increased latency and decreased throughput if the selected replica is not the fastest of the - available replicas. + available replicas. **Note:** In general setting `true` is discouraged and is strictly not recommended when using `update=true`. :param object start_key: (optional) Schema for any JSON type. - :param str start_key_doc_id: (optional) Schema for a document ID. + :param str start_key_doc_id: (optional) Start returning rows at the + specified document ID. No effect if using `group` or not providing start + key. :param str update: (optional) Parameter to specify whether or not the view in question should be updated prior to responding to the user. * `true` - Return results after the view is updated. @@ -17391,46 +20113,46 @@ def __init__(self, def from_dict(cls, _dict: Dict) -> 'ViewQuery': """Initialize a ViewQuery object from a json dictionary.""" args = {} - if 'att_encoding_info' in _dict: - args['att_encoding_info'] = _dict.get('att_encoding_info') - if 'attachments' in _dict: - args['attachments'] = _dict.get('attachments') - if 'conflicts' in _dict: - args['conflicts'] = _dict.get('conflicts') - if 'descending' in _dict: - args['descending'] = _dict.get('descending') - if 'include_docs' in _dict: - args['include_docs'] = _dict.get('include_docs') - if 'inclusive_end' in _dict: - args['inclusive_end'] = _dict.get('inclusive_end') - if 'limit' in _dict: - args['limit'] = _dict.get('limit') - if 'skip' in _dict: - args['skip'] = _dict.get('skip') - if 'update_seq' in _dict: - args['update_seq'] = _dict.get('update_seq') - if 'end_key' in _dict: - args['end_key'] = _dict.get('end_key') - if 'end_key_doc_id' in _dict: - args['end_key_doc_id'] = _dict.get('end_key_doc_id') - if 'group' in _dict: - args['group'] = _dict.get('group') - if 'group_level' in _dict: - args['group_level'] = _dict.get('group_level') - if 'key' in _dict: - args['key'] = _dict.get('key') - if 'keys' in _dict: - args['keys'] = _dict.get('keys') - if 'reduce' in _dict: - args['reduce'] = _dict.get('reduce') - if 'stable' in _dict: - args['stable'] = _dict.get('stable') - if 'start_key' in _dict: - args['start_key'] = _dict.get('start_key') - if 'start_key_doc_id' in _dict: - args['start_key_doc_id'] = _dict.get('start_key_doc_id') - if 'update' in _dict: - args['update'] = _dict.get('update') + if (att_encoding_info := _dict.get('att_encoding_info')) is not None: + args['att_encoding_info'] = att_encoding_info + if (attachments := _dict.get('attachments')) is not None: + args['attachments'] = attachments + if (conflicts := _dict.get('conflicts')) is not None: + args['conflicts'] = conflicts + if (descending := _dict.get('descending')) is not None: + args['descending'] = descending + if (include_docs := _dict.get('include_docs')) is not None: + args['include_docs'] = include_docs + if (inclusive_end := _dict.get('inclusive_end')) is not None: + args['inclusive_end'] = inclusive_end + if (limit := _dict.get('limit')) is not None: + args['limit'] = limit + if (skip := _dict.get('skip')) is not None: + args['skip'] = skip + if (update_seq := _dict.get('update_seq')) is not None: + args['update_seq'] = update_seq + if (end_key := _dict.get('end_key')) is not None: + args['end_key'] = end_key + if (end_key_doc_id := _dict.get('end_key_doc_id')) is not None: + args['end_key_doc_id'] = end_key_doc_id + if (group := _dict.get('group')) is not None: + args['group'] = group + if (group_level := _dict.get('group_level')) is not None: + args['group_level'] = group_level + if (key := _dict.get('key')) is not None: + args['key'] = key + if (keys := _dict.get('keys')) is not None: + args['keys'] = keys + if (reduce := _dict.get('reduce')) is not None: + args['reduce'] = reduce + if (stable := _dict.get('stable')) is not None: + args['stable'] = stable + if (start_key := _dict.get('start_key')) is not None: + args['start_key'] = start_key + if (start_key_doc_id := _dict.get('start_key_doc_id')) is not None: + args['start_key_doc_id'] = start_key_doc_id + if (update := _dict.get('update')) is not None: + args['update'] = update return cls(**args) @classmethod @@ -17510,30 +20232,40 @@ class UpdateEnum(str, Enum): * `lazy` - Return the view results without waiting for an update, but update them immediately after the request. """ + TRUE = 'true' FALSE = 'false' LAZY = 'lazy' -class ViewResult(): + +class ViewResult: """ Schema for the result of a query view operation. - :attr int total_rows: (optional) Number of total rows. - :attr str update_seq: (optional) Current update sequence for the database. - :attr List[ViewResultRow] rows: rows. + :param int total_rows: (optional) Total number of rows in the view index. Note + that if the request query narrows the view this is not the number of matching + rows. The number of matching rows, up to the specified `limit`, is the size of + the `rows` array. + :param str update_seq: (optional) Current update sequence for the database. + :param List[ViewResultRow] rows: rows. """ - def __init__(self, - rows: List['ViewResultRow'], - *, - total_rows: int = None, - update_seq: str = None) -> None: + def __init__( + self, + rows: List['ViewResultRow'], + *, + total_rows: Optional[int] = None, + update_seq: Optional[str] = None, + ) -> None: """ Initialize a ViewResult object. :param List[ViewResultRow] rows: rows. - :param int total_rows: (optional) Number of total rows. + :param int total_rows: (optional) Total number of rows in the view index. + Note that if the request query narrows the view this is not the number of + matching rows. The number of matching rows, up to the specified `limit`, is + the size of the `rows` array. :param str update_seq: (optional) Current update sequence for the database. """ self.total_rows = total_rows @@ -17544,12 +20276,12 @@ def __init__(self, def from_dict(cls, _dict: Dict) -> 'ViewResult': """Initialize a ViewResult object from a json dictionary.""" args = {} - if 'total_rows' in _dict: - args['total_rows'] = _dict.get('total_rows') - if 'update_seq' in _dict: - args['update_seq'] = _dict.get('update_seq') - if 'rows' in _dict: - args['rows'] = [ViewResultRow.from_dict(x) for x in _dict.get('rows')] + if (total_rows := _dict.get('total_rows')) is not None: + args['total_rows'] = total_rows + if (update_seq := _dict.get('update_seq')) is not None: + args['update_seq'] = update_seq + if (rows := _dict.get('rows')) is not None: + args['rows'] = [ViewResultRow.from_dict(v) for v in rows] else: raise ValueError('Required property \'rows\' not present in ViewResult JSON') return cls(**args) @@ -17567,7 +20299,13 @@ def to_dict(self) -> Dict: if hasattr(self, 'update_seq') and self.update_seq is not None: _dict['update_seq'] = self.update_seq if hasattr(self, 'rows') and self.rows is not None: - _dict['rows'] = [x.to_dict() for x in self.rows] + rows_list = [] + for v in self.rows: + if isinstance(v, dict): + rows_list.append(v) + else: + rows_list.append(v.to_dict()) + _dict['rows'] = rows_list return _dict def _to_dict(self): @@ -17588,28 +20326,33 @@ def __ne__(self, other: 'ViewResult') -> bool: """Return `true` when self and other are not equal, false otherwise.""" return not self == other -class ViewResultRow(): + +class ViewResultRow: """ Schema for a row of a view result. - :attr str caused_by: (optional) The cause of the error (if available). - :attr str error: (optional) The name of the error. - :attr str reason: (optional) The reason the error occurred (if available). - :attr Document doc: (optional) Schema for a document. - :attr str id: (optional) Schema for a document ID. - :attr object key: Schema for any JSON type. - :attr object value: Schema for any JSON type. + :param str caused_by: (optional) The cause of the error (if available). + :param str error: (optional) The name of the error. + :param str reason: (optional) The reason the error occurred (if available). + :param int ref: (optional) An internal error reference (if available). + :param Document doc: (optional) Schema for a document. + :param str id: (optional) Schema for a document ID. + :param object key: Schema for any JSON type. + :param object value: Schema for any JSON type. """ - def __init__(self, - key: object, - value: object, - *, - caused_by: str = None, - error: str = None, - reason: str = None, - doc: 'Document' = None, - id: str = None) -> None: + def __init__( + self, + key: object, + value: object, + *, + caused_by: Optional[str] = None, + error: Optional[str] = None, + reason: Optional[str] = None, + ref: Optional[int] = None, + doc: Optional['Document'] = None, + id: Optional[str] = None, + ) -> None: """ Initialize a ViewResultRow object. @@ -17618,12 +20361,14 @@ def __init__(self, :param str caused_by: (optional) The cause of the error (if available). :param str error: (optional) The name of the error. :param str reason: (optional) The reason the error occurred (if available). + :param int ref: (optional) An internal error reference (if available). :param Document doc: (optional) Schema for a document. :param str id: (optional) Schema for a document ID. """ self.caused_by = caused_by self.error = error self.reason = reason + self.ref = ref self.doc = doc self.id = id self.key = key @@ -17633,24 +20378,26 @@ def __init__(self, def from_dict(cls, _dict: Dict) -> 'ViewResultRow': """Initialize a ViewResultRow object from a json dictionary.""" args = {} - if 'caused_by' in _dict: - args['caused_by'] = _dict.get('caused_by') - if 'error' in _dict: - args['error'] = _dict.get('error') - if 'reason' in _dict: - args['reason'] = _dict.get('reason') - if 'doc' in _dict: - args['doc'] = Document.from_dict(_dict.get('doc')) - if 'id' in _dict: - args['id'] = _dict.get('id') - if 'key' in _dict: - args['key'] = _dict.get('key') + if (caused_by := _dict.get('caused_by')) is not None: + args['caused_by'] = caused_by + if (error := _dict.get('error')) is not None: + args['error'] = error + if (reason := _dict.get('reason')) is not None: + args['reason'] = reason + if (ref := _dict.get('ref')) is not None: + args['ref'] = ref + if (doc := _dict.get('doc')) is not None: + args['doc'] = Document.from_dict(doc) + if (id := _dict.get('id')) is not None: + args['id'] = id + if (key := _dict.get('key')) is not None: + args['key'] = key else: - raise ValueError('Required property \'key\' not present in ViewResultRow JSON') - if 'value' in _dict: - args['value'] = _dict.get('value') + args['key'] = None + if (value := _dict.get('value')) is not None: + args['value'] = value else: - raise ValueError('Required property \'value\' not present in ViewResultRow JSON') + args['value'] = None return cls(**args) @classmethod @@ -17667,8 +20414,13 @@ def to_dict(self) -> Dict: _dict['error'] = self.error if hasattr(self, 'reason') and self.reason is not None: _dict['reason'] = self.reason + if hasattr(self, 'ref') and self.ref is not None: + _dict['ref'] = self.ref if hasattr(self, 'doc') and self.doc is not None: - _dict['doc'] = self.doc.to_dict() + if isinstance(self.doc, dict): + _dict['doc'] = self.doc + else: + _dict['doc'] = self.doc.to_dict() if hasattr(self, 'id') and self.id is not None: _dict['id'] = self.id if hasattr(self, 'key') and self.key is not None: @@ -17694,168 +20446,3 @@ def __eq__(self, other: 'ViewResultRow') -> bool: def __ne__(self, other: 'ViewResultRow') -> bool: """Return `true` when self and other are not equal, false otherwise.""" return not self == other - -class GeoJsonGeometry(GeoJsonGeometryObject): - """ - Schema for a GeoJSON geometry. - - :attr str type: The type of GeoJSON Geometry. - :attr List[object] coordinates: Used for all geometry types except - `GeometryCollection`. The structure of the elements in the array varies by - geometry type. - """ - - def __init__(self, - type: str, - coordinates: List[object]) -> None: - """ - Initialize a GeoJsonGeometry object. - - :param str type: The type of GeoJSON Geometry. - :param List[object] coordinates: Used for all geometry types except - `GeometryCollection`. The structure of the elements in the array varies by - geometry type. - """ - # pylint: disable=super-init-not-called - self.type = type - self.coordinates = coordinates - - @classmethod - def from_dict(cls, _dict: Dict) -> 'GeoJsonGeometry': - """Initialize a GeoJsonGeometry object from a json dictionary.""" - args = {} - if 'type' in _dict: - args['type'] = _dict.get('type') - else: - raise ValueError('Required property \'type\' not present in GeoJsonGeometry JSON') - if 'coordinates' in _dict: - args['coordinates'] = _dict.get('coordinates') - else: - raise ValueError('Required property \'coordinates\' not present in GeoJsonGeometry JSON') - return cls(**args) - - @classmethod - def _from_dict(cls, _dict): - """Initialize a GeoJsonGeometry object from a json dictionary.""" - return cls.from_dict(_dict) - - def to_dict(self) -> Dict: - """Return a json dictionary representing this model.""" - _dict = {} - if hasattr(self, 'type') and self.type is not None: - _dict['type'] = self.type - if hasattr(self, 'coordinates') and self.coordinates is not None: - _dict['coordinates'] = self.coordinates - return _dict - - def _to_dict(self): - """Return a json dictionary representing this model.""" - return self.to_dict() - - def __str__(self) -> str: - """Return a `str` version of this GeoJsonGeometry object.""" - return json.dumps(self.to_dict(), indent=2) - - def __eq__(self, other: 'GeoJsonGeometry') -> bool: - """Return `true` when self and other are equal, false otherwise.""" - if not isinstance(other, self.__class__): - return False - return self.__dict__ == other.__dict__ - - def __ne__(self, other: 'GeoJsonGeometry') -> bool: - """Return `true` when self and other are not equal, false otherwise.""" - return not self == other - - class TypeEnum(str, Enum): - """ - The type of GeoJSON Geometry. - """ - POINT = 'Point' - MULTIPOINT = 'MultiPoint' - LINESTRING = 'LineString' - MULTILINESTRING = 'MultiLineString' - POLYGON = 'Polygon' - MULTIPOLYGON = 'MultiPolygon' - GEOMETRYCOLLECTION = 'GeometryCollection' - - -class GeoJsonGeometryCollection(GeoJsonGeometryObject): - """ - Schema for a GeoJSON GeometryCollection type geometry. - - :attr str type: The type of GeoJSON Geometry. - :attr List[GeoJsonGeometry] geometries: Used for the `GeometryCollection` type. - """ - - def __init__(self, - type: str, - geometries: List['GeoJsonGeometry']) -> None: - """ - Initialize a GeoJsonGeometryCollection object. - - :param str type: The type of GeoJSON Geometry. - :param List[GeoJsonGeometry] geometries: Used for the `GeometryCollection` - type. - """ - # pylint: disable=super-init-not-called - self.type = type - self.geometries = geometries - - @classmethod - def from_dict(cls, _dict: Dict) -> 'GeoJsonGeometryCollection': - """Initialize a GeoJsonGeometryCollection object from a json dictionary.""" - args = {} - if 'type' in _dict: - args['type'] = _dict.get('type') - else: - raise ValueError('Required property \'type\' not present in GeoJsonGeometryCollection JSON') - if 'geometries' in _dict: - args['geometries'] = [GeoJsonGeometry.from_dict(x) for x in _dict.get('geometries')] - else: - raise ValueError('Required property \'geometries\' not present in GeoJsonGeometryCollection JSON') - return cls(**args) - - @classmethod - def _from_dict(cls, _dict): - """Initialize a GeoJsonGeometryCollection object from a json dictionary.""" - return cls.from_dict(_dict) - - def to_dict(self) -> Dict: - """Return a json dictionary representing this model.""" - _dict = {} - if hasattr(self, 'type') and self.type is not None: - _dict['type'] = self.type - if hasattr(self, 'geometries') and self.geometries is not None: - _dict['geometries'] = [x.to_dict() for x in self.geometries] - return _dict - - def _to_dict(self): - """Return a json dictionary representing this model.""" - return self.to_dict() - - def __str__(self) -> str: - """Return a `str` version of this GeoJsonGeometryCollection object.""" - return json.dumps(self.to_dict(), indent=2) - - def __eq__(self, other: 'GeoJsonGeometryCollection') -> bool: - """Return `true` when self and other are equal, false otherwise.""" - if not isinstance(other, self.__class__): - return False - return self.__dict__ == other.__dict__ - - def __ne__(self, other: 'GeoJsonGeometryCollection') -> bool: - """Return `true` when self and other are not equal, false otherwise.""" - return not self == other - - class TypeEnum(str, Enum): - """ - The type of GeoJSON Geometry. - """ - POINT = 'Point' - MULTIPOINT = 'MultiPoint' - LINESTRING = 'LineString' - MULTILINESTRING = 'MultiLineString' - POLYGON = 'Polygon' - MULTIPOLYGON = 'MultiPolygon' - GEOMETRYCOLLECTION = 'GeometryCollection' - diff --git a/ibmcloudant/couchdb_session_authenticator.py b/ibmcloudant/couchdb_session_authenticator.py index 0e7dbede..90de04d5 100644 --- a/ibmcloudant/couchdb_session_authenticator.py +++ b/ibmcloudant/couchdb_session_authenticator.py @@ -1,6 +1,6 @@ # coding: utf-8 -# © Copyright IBM Corporation 2020. +# © Copyright IBM Corporation 2020, 2025. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -16,7 +16,9 @@ """ Module for handling session authentication """ -from requests import Request +from requests import Request, Session +from requests.cookies import RequestsCookieJar + from ibm_cloud_sdk_core.authenticators import Authenticator from .couchdb_session_token_manager import CouchDbSessionTokenManager @@ -39,17 +41,29 @@ class CouchDbSessionAuthenticator(Authenticator): AUTHTYPE_COUCHDB_SESSION = 'COUCHDB_SESSION' - def __init__(self, username: str, password: str): - self.jar = None - - self.token_manager = CouchDbSessionTokenManager(username, password) + def __init__(self, + username: str, + password: str, + disable_ssl_verification: bool = False) -> None: + if not isinstance(disable_ssl_verification, bool): + raise TypeError('disable_ssl_verification must be a bool') + + self.token_manager = CouchDbSessionTokenManager( + username, + password, + disable_ssl_verification=disable_ssl_verification + ) self.validate() - def set_jar(self, jar): - """Sets the cookie jar for the authenticator. + def _set_http_client(self, http_client: Session, jar: RequestsCookieJar) -> None: + """Sets base serivice's http client for the authenticator. This is an internal method called by BaseService. Not to be called directly. """ - self.jar = jar + if isinstance(http_client, Session): + self.token_manager.http_client = http_client + self.token_manager.jar = jar + else: + raise TypeError("http_client parameter must be a requests.sessions.Session") def validate(self): """Validates the username, and password for session token requests. @@ -72,11 +86,7 @@ def authenticate(self, req: Request): Args: req: Ignored. BaseService uses the cookie jar for every request """ - jar = self.token_manager.get_token() - # Requests seem to save cookies only for Sessions. BaseService is - # hard-coded to work with "regular" requests requests so updating - # the jar manually is necessary - self.jar.update(jar) + self.token_manager.get_token() def authentication_type(self) -> str: """Returns this authenticator's type ('COUCHDB_SESSION').""" diff --git a/ibmcloudant/couchdb_session_get_authenticator_patch.py b/ibmcloudant/couchdb_session_get_authenticator_patch.py index d9038227..cd31ef97 100644 --- a/ibmcloudant/couchdb_session_get_authenticator_patch.py +++ b/ibmcloudant/couchdb_session_get_authenticator_patch.py @@ -1,6 +1,6 @@ # coding: utf-8 -# © Copyright IBM Corporation 2020, 2021. +# © Copyright IBM Corporation 2020, 2022. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -33,6 +33,7 @@ def new_construct_authenticator(config): # pylint: disable=missing-docstring if auth_type == 'COUCHDB_SESSION': return CouchDbSessionAuthenticator( username=config.get('USERNAME'), - password=config.get('PASSWORD') + password=config.get('PASSWORD'), + disable_ssl_verification=config.get('DISABLE_SSL', 'false').lower() == 'true' ) return old_construct_authenticator(config) diff --git a/ibmcloudant/couchdb_session_token_manager.py b/ibmcloudant/couchdb_session_token_manager.py index 5badcf00..32512abf 100644 --- a/ibmcloudant/couchdb_session_token_manager.py +++ b/ibmcloudant/couchdb_session_token_manager.py @@ -1,6 +1,6 @@ # coding: utf-8 -# © Copyright IBM Corporation 2020, 2022. +# © Copyright IBM Corporation 2020, 2025. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -16,6 +16,8 @@ """ Module for managing session authentication token """ +from requests import Session + from ibm_cloud_sdk_core.token_managers.token_manager import TokenManager @@ -41,14 +43,20 @@ class CouchDbSessionTokenManager(TokenManager): def __init__(self, username: str, password: str, url: str = None, + disable_ssl_verification: bool = False, ): - super().__init__(url) + super().__init__( + url, + disable_ssl_verification=disable_ssl_verification, + ) self.username = username self.password = password self.token = None + self.http_client = None self.http_config = {} + self.jar = None self.headers = None def request_token(self): @@ -59,14 +67,19 @@ def request_token(self): A CookieJar of Cookies the server sent back. """ - response = self._request( + if not isinstance(self.http_client, Session): + raise TypeError("http_client parameter must be a requests.sessions.Session") + + response = self.http_client.request( method='POST', url=self.url + "/_session", headers=self.headers, json={ 'username': self.username, 'password': self.password, - } + }, + cookies=self.jar, + **self.http_config ) return response diff --git a/ibmcloudant/features/__init__.py b/ibmcloudant/features/__init__.py new file mode 100644 index 00000000..c366e733 --- /dev/null +++ b/ibmcloudant/features/__init__.py @@ -0,0 +1,14 @@ +# coding: utf-8 +# © Copyright IBM Corporation 2022, 2023. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. diff --git a/ibmcloudant/features/changes_follower.py b/ibmcloudant/features/changes_follower.py new file mode 100644 index 00000000..004afce2 --- /dev/null +++ b/ibmcloudant/features/changes_follower.py @@ -0,0 +1,465 @@ +# coding: utf-8 + +# © Copyright IBM Corporation 2022, 2024. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +""" +A helper for using the changes feed. +""" +import logging +import time +import random +import math +from datetime import datetime, timezone, timedelta +import functools +from queue import Queue +from threading import Thread, Event + +from enum import Enum, auto +from typing import Dict, Iterator + +from ibm_cloud_sdk_core import ApiException + +from ibmcloudant.cloudant_v1 import ( + CloudantV1, + PostChangesEnums, + ChangesResultItem, +) + +# max timedelta in milliseconds +_FOREVER = round(timedelta.max.total_seconds() * 1000) - 1 +# 1 minute in millisec +_MIN_CLIENT_TIMEOUT = 60000 +# To give the changes request a chance to be answered +# before the client timeout it is set to 3 seconds less. +_LONGPOLL_TIMEOUT = _MIN_CLIENT_TIMEOUT - 3000 +_BATCH_SIZE = 10000 + +# Base delay in milliseconds between unsuccessful attempts to pull changes feed +# in presence of transient errors +_BASE_DELAY = 100 +# Once we reach this number of retries we'll be capping the backoff +_EXP_RETRY_GATE = int(math.log(_LONGPOLL_TIMEOUT / _BASE_DELAY) / math.log(2)) + + +class _Mode(Enum): + """ + Enums for changes follower's operation mode. + """ + FINITE = auto() + LISTEN = auto() + + +class _TransientErrorSuppression(Enum): + """ + Enums for changes follower's transient errors suppression mode. + """ + ALWAYS = auto() + NEVER = auto() + TIMER = auto() + + +class _ChangesFollowerIterator: + """ + The ChangesFollowerIterator implements iterator interface. + + This class is for internal use by ChangesFollower, which provides + the user facing API. + + Args: + changes_caller: A partial function that pulls changes feed from CouchDB + for a given "since" parameter. + mode: Enum representing either one-off consumption of changes feed (FINITE) + or constant following the changes feed (LISTEN) + error_tolerance: The duration to suppress errors, + measured from the previous successful request. + """ + + def __init__( + self, changes_caller, mode: _Mode, error_tolerance: int + ) -> None: + self.changes_caller = changes_caller + self._changes_iter = iter([]) + self.mode = mode + self._transient_suppression = _TransientErrorSuppression.TIMER + if error_tolerance == 0: + self._transient_suppression = _TransientErrorSuppression.NEVER + elif error_tolerance == _FOREVER: + self._transient_suppression = _TransientErrorSuppression.ALWAYS + self.error_tolerance = timedelta(milliseconds=error_tolerance) + self.since = 'now' if mode is _Mode.LISTEN else '0' + self._success_timestamp = datetime.now(timezone.utc) + self._request_thread = Thread(target=self._request_callback) + self._buffer = Queue() + self._pending = None + self._has_next = True + self._retry = 0 + self._limit = None + self._stop = Event() + self.logger = logging.getLogger(__name__) + + @property + def limit(self) -> int: + return self._limit + + @limit.setter + def limit(self, value: int) -> None: + if value < 0: + raise ValueError('Limit must not be negative.') + self._limit = value + + @property + def since(self) -> str: + return self._since + + @since.setter + def since(self, value: str) -> None: + self._since = value + + def _start(self) -> None: + self._request_thread.start() + + def stop(self) -> None: + # shortcut limit and cancel in-flight + self.limit = 0 + self._stop.set() + try: + self._buffer.task_done() + except Exception: + pass + self._request_thread.join() + + def __iter__(self): + return self + + def __next__(self): + while True: + if self.limit is not None and self.limit == 0: + if not self._stop.is_set(): + self.stop() + raise StopIteration + try: + item = next(self._changes_iter) + if self.limit is not None and self.limit > 0: + self.limit -= 1 + return item + except StopIteration as exc: + data = self._buffer.get() + if self._stop.is_set(): + raise StopIteration from exc + if isinstance(data, Exception): + raise data from None + self._changes_iter = iter( + (ChangesResultItem.from_dict(item) for item in data) + ) + self._buffer.task_done() + + def _request_callback(self): + while True: + try: + if not self._has_next or self._stop.is_set(): + raise StopIteration + result = self.changes_caller(since=self.since).get_result() + self.since = result.get('last_seq') + self._pending = result.get('pending') + self._retry = 0 + if self._transient_suppression == _TransientErrorSuppression.TIMER: + self._success_timestamp = datetime.now(timezone.utc) + if self.mode == _Mode.FINITE and self._pending == 0: + self._has_next = False + results = result['results'] + self.logger.debug(f'_request_callback results {results}') + self._buffer.join() + if self._stop.is_set(): + raise StopIteration + self._buffer.put(results) + except StopIteration as e: + self.logger.debug('Iterator stopped.') + self._buffer.join() + self._buffer.put(e) + break + except Exception as e: + self.logger.debug(f'Exception getting changes {e}') + if ( + self._transient_suppression == _TransientErrorSuppression.NEVER + or ( + self._transient_suppression + == _TransientErrorSuppression.TIMER + and self._success_timestamp + self.error_tolerance + < datetime.now(timezone.utc) + ) + ): + self.logger.debug('Error tolerance deadline exceeded.') + self._buffer.join() + self._buffer.put(e) + break + if type(e) is ApiException and e.status_code in [400, 401, 403, 404]: + self.logger.debug('Terminal error.') + self._buffer.join() + self._buffer.put(e) + break + self.retry_delay() + + def retry_delay(self): + """ + Method retry_delay implements full jitter delay algorithm. + This is an exponential capped backoff with added jitter + to spread retry calls in case of multiple followers + started simultaneously for different feeds on the same account. + + The base delay is set to 100 ms and cap is set to the changes + feed pull's timeout. + + Algorithm reference: https://aws.amazon.com/blogs/architecture/exponential-backoff-and-jitter/ + """ + if (self._retry >= _EXP_RETRY_GATE): + exp_delay = _LONGPOLL_TIMEOUT + else: + exp_delay = pow(2, self._retry) * _BASE_DELAY + jitter_delay = random.uniform(0, exp_delay) + time.sleep(round(jitter_delay / 1000, 3)) + self._retry += 1 + + +class ChangesFollower: + """ + ChangesFollower is a helper for using the changes feed. + + There are two modes of operation: + start_one_off() to fetch the changes from the supplied since sequence + until there are no further pending changes. + start() to fetch the changes from the supplied since sequence + and then continuing to listen indefinitely for further new changes. + + The starting sequence ID can be changed for either mode by using "since". + + By default when using: + start_one_off() the feed will start from the beginning. + start() the feed will start from "now". + + In either mode the iterator can be terminated early by calling stop(). + + By default ChangesFollower will suppress transient errors indefinitely + and endeavour to run to completion or listen forever. For applications + where that behaviour is not desirable an alternate options is + available where a duration may be specified to limit the time since the + last successful response that transient errors will be suppressed. + + It should be noted that errors considered terminal, for example, the + database not existing or invalid credentials are never suppressed and will + throw an exception immediately. + + The named arguments for "post_changes" are used to configure the behaviour + of the ChangesFollower. However, a subset of the options are invalid as + they are configured internally by the implementation and will cause an + ValueError exception to be thrown if supplied. + + These invalid options are: + - descending + - feed + - heartbeat + - last_event_id + - timeout + + Only the value of "_selector" is permitted for the post_changes's "filter" option. + Selector based filters perform better than JS based filters and using one + of the alternative JS based filter types will cause ChangesFollower + to throw a ValueError exception. + + It should also be noted that the "limit" parameter will truncate the + iterator at the given number of changes in either operating mode. + + The ChangesFollower requires the Cloudant client to have HTTP call and + read timeouts of at least 1 minute. The default client configuration has + sufficiently long timeouts. + + :param CloudantV1 service: A client for the Cloudant service. + :param int error_tolerance: A duration to suppress transient errors for set in milliseconds. + :return: None + """ + + def __init__( + self, service: CloudantV1, *, error_tolerance: int = _FOREVER, **kwargs + ) -> None: + self.options = kwargs + self.limit = self.options.get('limit') + self._set_defaults(_Mode.FINITE) + self.service = service + self.error_tolerance = error_tolerance + self._iter = None + self.logger = logging.getLogger(__name__) + # Check the timeouts are suitable + timeouts = self.service.http_config.get('timeout', 0) + if isinstance(timeouts, int): + call_timeout, read_timeout = timeouts, timeouts + else: + call_timeout, read_timeout = timeouts + call_timeout, read_timeout = call_timeout * 1000, read_timeout * 1000 + if ( + call_timeout > 0 + and call_timeout < _MIN_CLIENT_TIMEOUT + or read_timeout > 0 + and read_timeout < _MIN_CLIENT_TIMEOUT + ): + raise ValueError( + 'To use {} the client read and call timeouts must be at least' + ' {:d} ms. The client read timeout is {:d}' + ' ms and the call timeout is {:d} ms.'.format( + type(self).__name__, + _MIN_CLIENT_TIMEOUT, + read_timeout, + call_timeout, + ) + ) + + @property + def error_tolerance(self) -> int: + return self._error_tolerance + + @error_tolerance.setter + def error_tolerance(self, value: int) -> None: + if value > _FOREVER: + raise ValueError( + f'Error tolerance duration must not be larger than {_FOREVER}.' + ) + if value < 0: + raise ValueError('Error tolerance duration must not be negative.') + self._error_tolerance = value + + @property + def options(self) -> Dict: + return self._options + + @options.setter + def options(self, value: Dict): + class_name = type(self).__name__ + if value.get('db') is None: + error_fmt = 'The option db must be provided when using {}.' + raise ValueError(error_fmt.format(class_name)) + opts = ['descending', 'feed', 'heartbeat', 'last_event_id', 'timeout'] + invalid_options = [o for o in opts if value.get(o) is not None] + if value.get('filter') and value.get('filter') != '_selector': + invalid_options.append(f"filter={value.get('filter')}") + if len(invalid_options) > 0: + invalid_opts_list = ', '.join(invalid_options) + error_fmt = 'The options {} are invalid when using {}.' + if len(invalid_options) == 1: + error_fmt = "The option '{}' is invalid when using {}." + raise ValueError(error_fmt.format(invalid_opts_list, class_name)) + self._options = value + + def _set_defaults(self, mode: _Mode, limit: int = None): + if mode == _Mode.FINITE: + defaults = { + 'feed': PostChangesEnums.Feed.NORMAL + } + elif mode == _Mode.LISTEN: + defaults = { + 'feed': PostChangesEnums.Feed.LONGPOLL, + 'timeout': _LONGPOLL_TIMEOUT, + } + if limit is not None: + self.logger.debug(f'Applying changes limit {limit}') + defaults['limit'] = limit + self._options = {**self._options, **defaults} + + def start(self) -> Iterator[ChangesResultItem]: + """ + Return all available changes and keep listening for new changes + until reaching an end condition. + + The end conditions are: + - a terminal error (e.g. unauthorized client). + - transient errors occur for longer than the error + suppression duration. + - the number of changes received reaches the limit specified + in the "post_changes" args used to instantiate + this ChangesFollower. + - ChangesFollower's stop() is called. + + The same change may be received more than once. + + Returns an iterator of ChangesResultItem per change. + + Throws ValueError if ChangesFollower's start() or start_one_off() + was already called or ApiException if a terminal error + or unsupressed transient error is recevied from the service + when fetching changes + """ + return self._run(_Mode.LISTEN) + + def start_one_off(self) -> Iterator[ChangesResultItem]: + """ + Return all available changes until there are no further changes + pending or reaching an end condition. + + The end conditions are: + - a terminal error (e.g. unauthorized client). + - transient errors occur for longer than the error + suppression duration. + - the number of changes received reaches the limit specified + in the "post_changes" args used to instantiate + this ChangesFollower. + - ChangesFollower's stop() is called. + + The same change may be received more than once. + + Returns an iterator of ChangesResultItem per change. + + Throws ValueError if ChangesFollower's start() or start_one_off() + was already called or ApiException if a terminal error + or unsupressed transient error is recevied from the service + when fetching changes + """ + return self._run(_Mode.FINITE) + + def stop(self) -> None: + """ + Stop this ChangesFollower. + + Note that synchronous iterator blocks so this stop method + needs to be called from a different thread to have any effect. + """ + self._iter.stop() + + def _run(self, mode: _Mode): + if self._iter is not None: + raise RuntimeError('Cannot start a feed that has already started.') + + batch_size = _BATCH_SIZE + if self.options.get('include_docs', False): + resp = self.service.get_database_information( + db=self.options.get('db') + ).get_result() + docs = resp.get('doc_count', 0) + sizes = resp.get('sizes', {}) + external_size = sizes.get('external', 0) + if external_size > 0 and docs > 0: + batch_size = max(int(5 * 1024 * 1024 / (external_size / docs + 500)), 1) + + if self.limit is not None: + batch_size = self.limit if self.limit < batch_size else batch_size + + self._set_defaults(mode, batch_size) + changes_caller = functools.partial( + self.service.post_changes, **self.options + ) + self._iter = _ChangesFollowerIterator( + changes_caller, mode, self.error_tolerance + ) + if self.limit is not None: + self._iter.limit = self.limit + if self.options.get('since') is not None: + self._iter.since = self.options.get('since') + self._iter._start() + return self._iter diff --git a/ibmcloudant/features/pagination.py b/ibmcloudant/features/pagination.py new file mode 100644 index 00000000..3ef485c5 --- /dev/null +++ b/ibmcloudant/features/pagination.py @@ -0,0 +1,453 @@ +# coding: utf-8 +# © Copyright IBM Corporation 2025. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +""" + Feature for paginating requests. + + Import :class:`~ibmcloudant.Pagination` and :class:`~ibmcloudant.PagerType` + from :mod:`ibmcloudant`. + Use :meth:`Pagination.new_pagination` to create a :class:`Pagination` + for the specific :class:`PagerType` operation and options. +""" + +from abc import abstractmethod +from collections.abc import Callable, Iterable, Iterator, Sequence +from enum import auto, Enum +from functools import partial +from types import MappingProxyType +from typing import Generic, Optional, Protocol, TypeVar + +from ibm_cloud_sdk_core import DetailedResponse +from ibmcloudant.cloudant_v1 import CloudantV1,\ + AllDocsResult, DocsResultRow, Document, FindResult, SearchResult, SearchResultRow, ViewResult, ViewResultRow + +# Type variable for the result +R = TypeVar('R', AllDocsResult, FindResult, SearchResult, ViewResult) +# Type variable for the items +I = TypeVar('I', DocsResultRow, Document, SearchResultRow, ViewResultRow) +# Type variable for the key in key based paging +K = TypeVar('K') + +_MAX_LIMIT = 200 +_MIN_LIMIT = 1 +_DOCS_KEY_ERROR = "No need to paginate as 'key' returns a single result for an ID." +_VIEW_KEY_ERROR = "Use 'start_key' and 'end_key' instead." + +class PagerType(Enum): + """ + Enumeration of the available Pager types + """ + + POST_ALL_DOCS = auto() + POST_DESIGN_DOCS = auto() + POST_FIND = auto() + POST_PARTITION_ALL_DOCS = auto() + POST_PARTITION_FIND = auto() + POST_PARTITION_SEARCH = auto() + POST_PARTITION_VIEW = auto() + POST_SEARCH = auto() + POST_VIEW = auto() + +class Pager(Protocol[I]): + """ + Protocol for pagination of Cloudant operations. + + Use Pager.new_pager to create a new pager for one of + the operation types in PagerType. + """ + + @abstractmethod + def has_next(self) -> bool: + """ + returns False if there are no more pages + """ + + raise NotImplementedError() + + @abstractmethod + def get_next(self) -> Sequence[I]: + """ + returns the next page of results + """ + + raise NotImplementedError() + + @abstractmethod + def get_all(self) -> Sequence[I]: + """ + returns all the pages of results in single list + """ + + raise NotImplementedError() + +class Pagination: + """ + Entry point for the pagination features. + + Use :meth:`Pagination.new_pagination` to create a :class:`Pagination` + instance for the specific :class:`PagerType` operation and options. + + Then create a Pager or Iterable using one of the functions: + * :meth:`pager` - for an IBM Cloud SDK style Pager + * :meth:`pages` - for a page Iterable + * :meth:`rows` - for a row Iterable + """ + + def __init__(self, client: CloudantV1, type: PagerType, opts: dict): + self._client = client + self._operation_type = type + self._initial_opts = dict(opts) + + def pager(self) -> Pager[I]: + """ + Create a new IBM Cloud SDK style Pager. + This type is useful for retrieving one page at a time from a function call. + """ + + return _IteratorPager(self.pages) + + def pages(self) -> Iterable[Sequence[I]]: + """ + Create a new Iterable for all the pages. + This type is useful for handling pages in a for loop. + + for page in Pagination.new_pagination(client, **opts).pages(): + ... + """ + + return self._operation_type(self._client, self._initial_opts) + + def rows(self) -> Iterable[I]: + """ + Create a new Iterable for all the rows from all the pages. + This type is useful for handling rows in a for loop. + + for row in Pagination.new_pagination(client, **opts).rows(): + ... + """ + + for page in self.pages(): + yield from page + + @classmethod + def _validate_limit(cls, opts: dict): + limit: int | None = opts.get('limit') + # For None case the valid default limit of _MAX_LIMIT will be applied later + if limit is not None: + if limit > _MAX_LIMIT: + raise ValueError(f'The provided limit {limit} exceeds the maximum page size value of {_MAX_LIMIT}.') + if limit < _MIN_LIMIT: + raise ValueError(f'The provided limit {limit} is lower than the minimum page size value of {_MIN_LIMIT}.') + + @classmethod + def _validate_option_absent(cls, invalid_opt: str, opts: dict, message_suffix: Optional[str]=None): + # check if the invalid_opt is present in opts dict + if invalid_opt in opts: + raise ValueError(f"The option '{invalid_opt}' is invalid when using pagination.{' ' + message_suffix if message_suffix else ''}") + + @classmethod + def _validate_options_absent(cls, invalid_opts: Sequence[str], opts: dict): + # for each invalid_opts entry check if it is present in opts dict + for invalid_opt in invalid_opts: + cls._validate_option_absent(invalid_opt, opts) + + @classmethod + def new_pagination(cls, client:CloudantV1, type: PagerType, **kwargs): + """ + Create a new Pagination. + client: CloudantV1 - the Cloudant service client + type: PagerType - the operation type to paginate + kwargs: dict - the options for the operation + """ + + # Validate the limit + cls._validate_limit(kwargs) + if type == PagerType.POST_ALL_DOCS: + cls._validate_option_absent('key', kwargs, _DOCS_KEY_ERROR) + cls._validate_options_absent(('keys',), kwargs) + return Pagination(client, _AllDocsPageIterator, kwargs) + if type == PagerType.POST_DESIGN_DOCS: + cls._validate_option_absent('key', kwargs, _DOCS_KEY_ERROR) + cls._validate_options_absent(('keys',), kwargs) + return Pagination(client, _DesignDocsPageIterator, kwargs) + if type == PagerType.POST_FIND: + return Pagination(client, _FindPageIterator, kwargs) + if type == PagerType.POST_PARTITION_ALL_DOCS: + cls._validate_option_absent('key', kwargs, _DOCS_KEY_ERROR) + cls._validate_options_absent(('keys',), kwargs) + return Pagination(client, _AllDocsPartitionPageIterator, kwargs) + if type == PagerType.POST_PARTITION_FIND: + return Pagination(client, _FindPartitionPageIterator, kwargs) + if type == PagerType.POST_PARTITION_SEARCH: + return Pagination(client, _SearchPartitionPageIterator, kwargs) + if type == PagerType.POST_PARTITION_VIEW: + cls._validate_option_absent('key', kwargs, _VIEW_KEY_ERROR) + cls._validate_options_absent(('keys',), kwargs) + return Pagination(client, _ViewPartitionPageIterator, kwargs) + if type == PagerType.POST_SEARCH: + cls._validate_options_absent(('counts', 'group_field', 'group_limit', 'group_sort', 'ranges',), kwargs) + return Pagination(client, _SearchPageIterator, kwargs) + if type == PagerType.POST_VIEW: + cls._validate_option_absent('key', kwargs, _VIEW_KEY_ERROR) + cls._validate_options_absent(('keys',), kwargs) + return Pagination(client, _ViewPageIterator, kwargs) + +class _IteratorPagerState(Enum): + NEW = auto() + GET_NEXT = auto() + GET_ALL = auto() + CONSUMED = auto() + +class _IteratorPager(Pager[I]): + + _state_consumed_msg = 'This pager has been consumed, use a new Pager.' + _state_mixed_msg = 'Cannot mix get_all() and get_next() use only one method or make a new Pager.' + + def __init__(self, iterable_func: Callable[[], Iterator[Sequence[I]]]): + self._iterable_func: Callable[[], Iterator[Sequence[I]]] = iterable_func + self._iterator: Iterator[Sequence[I]] = iter(self._iterable_func()) + self._state: _IteratorPagerState = _IteratorPagerState.NEW + + def has_next(self) -> bool: + """ + returns False if there are no more pages + """ + + return self._iterator._has_next + + def get_next(self) -> Sequence[I]: + """ + returns the next page of results + """ + self._check_state(mode=_IteratorPagerState.GET_NEXT) + page: Sequence[I] = next(self._iterator) + if not self._iterator._has_next: + self._state = _IteratorPagerState.CONSUMED + return page + + def get_all(self) -> Sequence[I]: + """ + returns all the pages of results in single list + """ + + self._check_state(mode=_IteratorPagerState.GET_ALL) + all_items: list[I] = [] + for page in self._iterable_func(): + all_items.extend(page) + self._state = _IteratorPagerState.CONSUMED + return (*all_items,) + + def _check_state(self, mode: _IteratorPagerState): + if self._state == mode: + return + if self._state == _IteratorPagerState.NEW: + self._state = mode + return + if self._state == _IteratorPagerState.CONSUMED: + raise Exception(_IteratorPager._state_consumed_msg) + raise Exception(_IteratorPager._state_mixed_msg) + +class _BasePageIterator(Iterator[Sequence[I]]): + + def __init__(self, + client: CloudantV1, + operation: Callable[..., DetailedResponse], + page_opts: Sequence[str], + opts: dict): + self._client: CloudantV1 = client + self._has_next: bool = True + # split the opts into fixed and page parts based on page_opts + self._next_page_opts: dict = {} + fixed_opts: dict = dict(opts) + # Get the page size and set the limit acoordingly + self._page_size: int = self._page_size_from_opts_limit(fixed_opts) + fixed_opts['limit'] = self._page_size + # Remove the options that change per page + for k in page_opts: + if v := fixed_opts.pop(k, None): + self._next_page_opts[k] = v + fixed_opts = MappingProxyType(fixed_opts) + # Partial method with the fixed ops + self._next_request_function: Callable[..., DetailedResponse] = partial(operation, **fixed_opts) + + def __iter__(self) -> Iterator[Sequence[I]]: + return self + + def __next__(self) -> Sequence[I]: + if self._has_next: + return (*self._next_request(),) + raise StopIteration() + + def _next_request(self) -> list[I]: + response: DetailedResponse = self._next_request_function(**self._next_page_opts) + result: dict = response.get_result() + typed_result: R = self._result_converter()(result) + items: list[I] = self._items(typed_result) + if len(items) < self._page_size: + self._has_next = False + else: + self._next_page_opts = self._get_next_page_options(typed_result) + return items + + def _page_size_from_opts_limit(self, opts:dict) -> int: + return opts.get('limit', _MAX_LIMIT) + + @abstractmethod + def _result_converter(self) -> Callable[[dict], R]: + raise NotImplementedError() + + @abstractmethod + def _items(self, result: R) -> list[I]: + raise NotImplementedError() + + @abstractmethod + def _get_next_page_options(self, result: R) -> dict: + raise NotImplementedError() + +class _KeyPageIterator(_BasePageIterator, Generic[K]): + + def __init__(self, client: CloudantV1, operation: Callable[..., DetailedResponse], opts: dict): + super().__init__(client, operation, ('skip', 'start_key', 'start_key_doc_id',), opts) + self._boundary_failure: Optional[str] = None + + def _next_request(self) -> list[I]: + if self._boundary_failure is not None: + raise Exception(self._boundary_failure) + items: list[I] = super()._next_request() + if self._has_next: + last_item: I = items.pop() + if len(items) > 0: + # Get, but don't remove the last item from the list + penultimate_item: I = items[-1] + self._boundary_failure: Optional[str] = self.check_boundary(penultimate_item, last_item) + return items + + def _get_next_page_options(self, result: R) -> dict: + # last item is used for next page options + last_item = self._items(result)[-1] + return { + 'start_key': last_item.key, + 'start_key_doc_id': last_item.id, + } + + def _items(self, result: R) -> list[I]: + return result.rows + + def _page_size_from_opts_limit(self, opts:dict) -> int: + return super()._page_size_from_opts_limit(opts) + 1 + + @abstractmethod + def check_boundary(self, penultimate_item: I, last_item: I) -> Optional[str]: + raise NotImplementedError() + +class _BookmarkPageIterator(_BasePageIterator): + + def __init__(self, client: CloudantV1, operation: Callable[..., DetailedResponse], opts: dict, extra_page_opts:Sequence[str]=()): + super().__init__(client, operation, ('bookmark',) + extra_page_opts, opts) + + def _get_next_page_options(self, result: R) -> dict: + return {'bookmark': result.bookmark} + +class _AllDocsBasePageIterator(_KeyPageIterator[str]): + + def _result_converter(self) -> Callable[[dict], AllDocsResult]: + return AllDocsResult.from_dict + + def _get_next_page_options(self, result: R) -> dict: + # Remove start_key_doc_id for all_docs paging + opts: dict = super()._get_next_page_options(result) + del opts['start_key_doc_id'] + return opts + + def check_boundary(self, penultimate_item: I, last_item: I) -> Optional[str]: + # IDs are always unique in _all_docs pagers so return None + return None + +class _AllDocsPageIterator(_AllDocsBasePageIterator): + + def __init__(self, client: CloudantV1, opts: dict): + super().__init__(client, client.post_all_docs, opts) + +class _AllDocsPartitionPageIterator(_AllDocsBasePageIterator): + + def __init__(self, client: CloudantV1, opts: dict): + super().__init__(client, client.post_partition_all_docs, opts) + +class _DesignDocsPageIterator(_AllDocsBasePageIterator): + + def __init__(self, client: CloudantV1, opts: dict): + super().__init__(client, client.post_design_docs, opts) + +class _FindBasePageIterator(_BookmarkPageIterator): + + def __init__(self, client: CloudantV1, operation: Callable[..., DetailedResponse], opts: dict): + # Find requests allow skip, but it should only be used on the first request. + # Since we don't want it on subsequent page requests we need to exclude it from + # fixed opts used for the partial function. + super().__init__(client, operation, opts, extra_page_opts=('skip',)) + + def _items(self, result: FindResult): + return result.docs + + def _result_converter(self): + return FindResult.from_dict + +class _FindPageIterator(_FindBasePageIterator): + + def __init__(self, client: CloudantV1, opts: dict): + super().__init__(client, client.post_find, opts) + +class _FindPartitionPageIterator(_FindBasePageIterator): + + def __init__(self, client: CloudantV1, opts: dict): + super().__init__(client, client.post_partition_find, opts) + +class _SearchBasePageIterator(_BookmarkPageIterator): + + def _items(self, result: SearchResult): + return result.rows + + def _result_converter(self): + return SearchResult.from_dict + +class _SearchPageIterator(_SearchBasePageIterator): + + def __init__(self, client: CloudantV1, opts: dict): + super().__init__(client, client.post_search, opts) + +class _SearchPartitionPageIterator(_SearchBasePageIterator): + + def __init__(self, client: CloudantV1, opts: dict): + super().__init__(client, client.post_partition_search, opts) + +class _ViewBasePageIterator(_KeyPageIterator[any]): + + def _result_converter(self): + return ViewResult.from_dict + + def check_boundary(self, penultimate_item: I, last_item: I) -> Optional[str]: + if penultimate_item.id == (boundary_id := last_item.id) \ + and penultimate_item.key == (boundary_key := last_item.key): + return f'Cannot paginate on a boundary containing identical keys {boundary_key} and document IDs {boundary_id}' + return None + +class _ViewPageIterator(_ViewBasePageIterator): + + def __init__(self, client: CloudantV1, opts: dict): + super().__init__(client, client.post_view, opts) + +class _ViewPartitionPageIterator(_ViewBasePageIterator): + + def __init__(self, client: CloudantV1, opts: dict): + super().__init__(client, client.post_partition_view, opts) diff --git a/ibmcloudant/version.py b/ibmcloudant/version.py index 8f8a95f9..eef3bb98 100644 --- a/ibmcloudant/version.py +++ b/ibmcloudant/version.py @@ -17,4 +17,4 @@ """ Version of ibmcloudant """ -__version__ = '0.1.4' +__version__ = '0.11.10' diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 00000000..faa1aaf9 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,48 @@ +[build-system] +requires = ["flit_core >=4.0.2,<5"] +build-backend = "flit_core.buildapi" + +[project] +name = "ibmcloudant" +version = "0.11.10" +description = "Python client library for IBM Cloudant" +dependencies = [ + "ibm_cloud_sdk_core==3.26.0", + "requests>=2.32.4,<3.0.0", + "python_dateutil>=2.5.3,<3.0.0", + "PyJWT>=2.0.1,<3.0.0", +] +authors = [ + { name="IBM", email="cldtsdks@us.ibm.com" }, +] +readme = "README.md" +requires-python = ">=3.10" +license = {file = "LICENSE"} +classifiers = [ + "Programming Language :: Python", + "Programming Language :: Python :: 3", + "Programming Language :: Python :: 3.10", + "Programming Language :: Python :: 3.11", + "Programming Language :: Python :: 3.12", + "Programming Language :: Python :: 3.13", + "Programming Language :: Python :: 3.14", + "Development Status :: 5 - Production/Stable", + "Intended Audience :: Developers", + "License :: OSI Approved :: Apache Software License", + "Operating System :: OS Independent", + "Topic :: Software Development :: Libraries :: Python Modules", + "Topic :: Software Development :: Libraries :: Application Frameworks", +] +keywords = [ + "ibmcloudant", + "Cloudant", + "IBM", + "database", + "client", + "SDK", + "official" +] + +[project.urls] +"Homepage" = "https://github.com/IBM/cloudant-python-sdk" +"Bug Tracker" = "https://github.com/IBM/cloudant-python-sdk/issues" diff --git a/requirements-dev.txt b/requirements-dev.txt index f24d39f8..c2adb262 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -1,15 +1,10 @@ +# build dependencies +flit==4.0.2 # test dependencies -pytest==7.1.2 -responses==0.21.0 -python_dotenv==0.20.0 -pylint==2.14.5 -tox==3.25.1 -pytest-rerunfailures==10.2 - +pytest==9.1.1 +responses==0.26.2 +python_dotenv==1.2.3 +pylint==4.0.7 # code coverage -coverage==6.4.3 -pytest-cov==3.0.0 - -# documentation -recommonmark==0.7.1 -Sphinx==5.1.1 +coverage==7.15.4 +pytest-cov==7.1.0 diff --git a/requirements-docs.txt b/requirements-docs.txt new file mode 100644 index 00000000..7f1f1ef5 --- /dev/null +++ b/requirements-docs.txt @@ -0,0 +1,6 @@ +# documentation +recommonmark==0.7.1 +Sphinx==9.1.0 +sphinx-autodoc2==0.5.0 +myst-parser==5.0.0 +sphinx-rtd-theme==3.1.0 diff --git a/requirements.txt b/requirements.txt deleted file mode 100644 index b8831881..00000000 --- a/requirements.txt +++ /dev/null @@ -1,5 +0,0 @@ -ibm_cloud_sdk_core==3.16.0 -# dependencies of ibm_cloud_sdk_core: -requests>=2.20,<3.0 -python_dateutil>=2.5.3,<3.0.0 -PyJWT>=2.0.1,<3.0.0 diff --git a/scripts/publish_buildinfo.sh b/scripts/publish_buildinfo.sh index c757ef08..273c7c29 100755 --- a/scripts/publish_buildinfo.sh +++ b/scripts/publish_buildinfo.sh @@ -5,12 +5,12 @@ file="/tmp/build.json" tmpfile="/tmp/build.json.tmp" 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" printf "Artifactory URL: $ARTIFACT_URL\n" -ARTIFACTS=$(curl -H "X-JFrog-Art-Api:$ARTIFACTORY_APIKEY" "$ARTIFACT_URL") +ARTIFACTS=$(curl -f -H "Authorization: Bearer $ARTIFACTORY_CREDS_PSW" "$ARTIFACT_URL") HAS_CHILDREN_RESULT=$(echo $ARTIFACTS | jq 'has("children")') # if 'children' array exists then grab all aritfact uris, else grab parent uri if [[ "$HAS_CHILDREN_RESULT" == "false" ]]; then ARTIFACTS_URI=$(echo $ARTIFACTS | jq -r '.uri' | sed 's:.*/:/:') -else +else ARTIFACTS_URI=$(echo $ARTIFACTS | jq -r '.children[] | .uri') fi @@ -22,14 +22,14 @@ fi # create new (or write over existing) build.json file echo -n "" > $file # get current published build -CURRENT_BUILD=$(curl -H "X-JFrog-Art-Api:$ARTIFACTORY_APIKEY" "$STAGE_ROOT"build/"$BUILD_NAME"/"$BUILD_NUMBER") +CURRENT_BUILD=$(curl -f -H "Authorization: Bearer $ARTIFACTORY_CREDS_PSW" "$STAGE_ROOT"build/"$BUILD_NAME"/"$BUILD_NUMBER") echo $CURRENT_BUILD CURRENT_BUILD_INFO=$(echo "$CURRENT_BUILD" | jq -r '.buildInfo') echo $CURRENT_BUILD_INFO | tee $file # put build name, number, and url on published artifacts URL_WITH_PROPS="$ARTIFACT_URL?properties=build.name=$BUILD_NAME;build.number=$BUILD_NUMBER;build.url=$BUILD_URL" -curl -i -X PUT -H "X-JFrog-Art-Api:$ARTIFACTORY_APIKEY" "$URL_WITH_PROPS" +curl --fail-with-body -i -X PUT -H "Authorization: Bearer $ARTIFACTORY_CREDS_PSW" "$URL_WITH_PROPS" # add artifact type and module id to build info file jq --arg type "$TYPE" '.type = $type' $file > $tmpfile && mv $tmpfile $file @@ -40,10 +40,10 @@ for artifact in $ARTIFACTS_URI; do # node does not have 'children' artifacts if [[ "$HAS_CHILDREN_RESULT" == "false" ]]; then ARTIFACT_INFO_URL="$ARTIFACT_URL" - else + else ARTIFACT_INFO_URL="$ARTIFACT_URL$artifact" fi - GET_ARTIFACT=$(curl -H "X-JFrog-Art-Api:$ARTIFACTORY_APIKEY" $ARTIFACT_INFO_URL | jq -r .checksums) + GET_ARTIFACT=$(curl -f -H "Authorization: Bearer $ARTIFACTORY_CREDS_PSW" $ARTIFACT_INFO_URL | jq -r .checksums) MD5=$(echo $GET_ARTIFACT | jq -r .md5) SHA1=$(echo $GET_ARTIFACT | jq -r .sha1) NAME="${artifact#*/}" @@ -57,4 +57,4 @@ else exit 1 fi -curl -i -X PUT -H "X-JFrog-Art-Api:$ARTIFACTORY_APIKEY" -H "Content-Type: application/json" "$STAGE_ROOT"build --upload-file $file +curl --fail-with-body -i -X PUT -H "Authorization: Bearer $ARTIFACTORY_CREDS_PSW" -H "Content-Type: application/json" "$STAGE_ROOT"build --upload-file $file diff --git a/scripts/pydoc/publish-doc.sh b/scripts/pydoc/publish-doc.sh index 39b5c19f..fe71d9ed 100755 --- a/scripts/pydoc/publish-doc.sh +++ b/scripts/pydoc/publish-doc.sh @@ -8,7 +8,8 @@ GIT_REPO=$(git remote get-url origin) # Create documentation printf ">>>>> Generate new documentation\n" -python3 -m tox -e docs +pip install -r requirements-docs.txt +sphinx-build source apidocs -bhtml # Clone gh-pages branch printf ">>>>> Publishing pydoc for release build: repo=%s branch=%s build_num=%s job_name=%s\n" ${GIT_REPO} ${BRANCH_NAME} ${BUILD_NUMBER} ${JOB_NAME} diff --git a/scripts/setup_couch.sh b/scripts/setup_couch.sh index edbdf130..188682f2 100755 --- a/scripts/setup_couch.sh +++ b/scripts/setup_couch.sh @@ -2,9 +2,10 @@ set -ev -printenv | grep "^SERVER_" > cloudant.env +printenv | grep "^SERVER_" >> cloudant_v1.env # if you change the image version please regenerate example output captures -docker start couchdb || docker run --name couchdb --rm -e COUCHDB_USER="$SERVER_USERNAME" -e COUCHDB_PASSWORD="$SERVER_PASSWORD" -p 5984:5984 -d apache/couchdb:3 +# use image at ARTIFACTORY_DOCKER_REPO_VIRTUAL registry if set, otherwise use default registry + # shellcheck disable=SC2016 timeout 120 bash -c 'while [[ "$(curl -s -o /dev/null -w ''%{http_code}'' ${SERVER_URL}/_up)" != "200" ]]; do sleep 5; done' || false curl -XPUT -u "$SERVER_USERNAME":"$SERVER_PASSWORD" "$SERVER_URL"/_users diff --git a/scripts/setup_wiremock.sh b/scripts/setup_wiremock.sh index bad1c738..b46974e7 100755 --- a/scripts/setup_wiremock.sh +++ b/scripts/setup_wiremock.sh @@ -3,8 +3,11 @@ set -ev printenv | grep "^WIREMOCK_" > wiremock.env +# setup env file for gen ITs +echo -e "CLOUDANT_AUTH_TYPE=noauth\nCLOUDANT_URL=$WIREMOCK_URL" >> cloudant_v1.env -docker start wiremock || docker run --name wiremock --rm -d -p "$WIREMOCK_PORT":8080 wiremock/wiremock:latest +# use image at ARTIFACTORY_DOCKER_REPO_VIRTUAL registry if set, otherwise use default registry timeout 120 bash -c 'while [[ "$(curl -s --location -o /dev/null -w ''%{http_code}'' ${WIREMOCK_URL}/__admin)" != "200" ]]; do sleep 2; done' || false curl "$WIREMOCK_URL"/__admin/mappings/import -X POST -d @stubs/mappings.json +curl "$WIREMOCK_URL"/__admin/mappings/import -X POST -d @stubs/gen-its-mappings.json echo "Wiremock started" diff --git a/setup.py b/setup.py deleted file mode 100644 index 7c1099c8..00000000 --- a/setup.py +++ /dev/null @@ -1,94 +0,0 @@ -#!/usr/bin/env python - -# © Copyright IBM Corporation 2020. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -from setuptools import setup -from setuptools.command.test import test as TestCommand -import os -import sys -import pkg_resources - -__version__ = '0.1.4' -PACKAGE_NAME = 'ibmcloudant' -PACKAGE_DESC = 'Python client library for IBM Cloudant' - -with open('requirements.txt') as f: - install_requires = [str(req) for req in pkg_resources.parse_requirements(f)] -with open('requirements-dev.txt') as f: - tests_require = [str(req) for req in pkg_resources.parse_requirements(f)] - -if sys.argv[-1] == 'publish': - # test server - os.system('python setup.py register -r pypitest') - os.system('python setup.py sdist upload -r pypitest') - - # production server - os.system('python setup.py register -r pypi') - os.system('python setup.py sdist upload -r pypi') - sys.exit() - -class PyTest(TestCommand): - def finalize_options(self): - TestCommand.finalize_options(self) - self.test_args = ['--strict', '--verbose', '--tb=long', 'test'] - self.test_suite = True - - def run_tests(self): - import pytest - errcode = pytest.main(self.test_args) - sys.exit(errcode) - -class PyTestUnit(PyTest): - def finalize_options(self): - self.test_args = ['--strict', '--verbose', '--tb=long', 'test/unit'] - -class PyTestIntegration(PyTest): - def finalize_options(self): - self.test_args = ['--strict', '--verbose', '--tb=long', 'test/integration'] - -with open('README.md', 'r', encoding='utf-8') as fh: - readme = fh.read() - -setup(name=PACKAGE_NAME.replace('_', '-'), - version=__version__, - description=PACKAGE_DESC, - license='Apache 2.0', - install_requires=install_requires, - tests_require=tests_require, - cmdclass={'test': PyTest, 'test_unit': PyTestUnit, 'test_integration': PyTestIntegration}, - author='IBM', - author_email='cldtsdks@us.ibm.com', - long_description=readme, - long_description_content_type='text/markdown', - url='https://github.com/IBM/cloudant-python-sdk', - packages=[PACKAGE_NAME], - include_package_data=True, - keywords=PACKAGE_NAME, - classifiers=[ - 'Programming Language :: Python', - 'Programming Language :: Python :: 3', - 'Programming Language :: Python :: 3.7', - 'Programming Language :: Python :: 3.8', - 'Programming Language :: Python :: 3.9', - 'Programming Language :: Python :: 3.10', - 'Development Status :: 4 - Beta', - 'Intended Audience :: Developers', - 'License :: OSI Approved :: Apache Software License', - 'Operating System :: OS Independent', - 'Topic :: Software Development :: Libraries :: Python Modules', - 'Topic :: Software Development :: Libraries :: Application Frameworks', - ], - zip_safe=True - ) diff --git a/source/conf.py b/source/conf.py index 5997ccda..f379a313 100644 --- a/source/conf.py +++ b/source/conf.py @@ -15,14 +15,14 @@ # import os import sys +import datetime sys.path.insert(0, os.path.abspath('..')) print(os.path.abspath('..')) - # -- Project information ----------------------------------------------------- project = 'Cloudant Python SDK' -copyright = 'Copyright IBM Corp. 2021' +copyright = f'Copyright IBM Corp. 2021, {datetime.date.today().year}' author = '@IBM/cloudant-sdks' # -- General configuration --------------------------------------------------- @@ -31,7 +31,34 @@ # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom # ones. extensions = [ - 'sphinx.ext.autodoc' + 'autodoc2', + 'myst_parser', + 'sphinx_rtd_theme', +] + +myst_enable_extensions = ['colon_fence', 'fieldlist'] + +autodoc2_packages = [ + { + 'path': '../ibmcloudant', + 'exclude_files': [ + 'common.py', + 'version.py', + 'cloudant_base_service.py', + 'couchdb_session_get_authenticator_patch.py', + 'couchdb_session_token_manager.py' + ] + } +] + +autodoc2_sort_names = True + +autodoc2_index_template = None + +autodoc2_hidden_objects = ['dunder', 'private', 'inherited'] + +autodoc2_docstring_parser_regexes = [ + (r'.*', 'myst'), ] # Add any paths that contain templates here, relative to this directory. @@ -40,15 +67,38 @@ # List of patterns, relative to source directory, that match files and # directories to ignore when looking for source files. # This pattern also affects html_static_path and html_extra_path. -exclude_patterns = [] +exclude_patterns = ['apidocs/ibmcloudant/ibmcloudant.rst', 'apidocs/ibmcloudant/ibmcloudant.features.rst'] + +# -- Patch generated rst ----------------------------------------------------- +file_path = '../ibmcloudant/cloudant_v1.py' +print(f'[patching] file {file_path}') +# read source file +with open(file_path, 'r') as file: + lines = file.readlines() +# patch broken markup +codeblock = False +for i in range(len(lines)): + # replace note with tip admonition + if '### Note' in lines[i]: + lines[i] = ' :::{tip}\n' + codeblock = True + if codeblock and lines[i] == '\n': + lines[i] = ' :::\n\n' + codeblock = False +# write back +with open(file_path, 'w') as file: + file.writelines(lines) # -- Options for HTML output ------------------------------------------------- # The theme to use for HTML and HTML Help pages. See the documentation for # a list of builtin themes. # -html_theme = 'alabaster' +html_theme = 'sphinx_rtd_theme' +html_theme_options = { + 'collapse_navigation': False +} # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, diff --git a/source/index.rst b/source/index.rst index 36870288..f7836149 100644 --- a/source/index.rst +++ b/source/index.rst @@ -1,22 +1,30 @@ .. Cloudant Python SDK documentation master file, created by sphinx-quickstart on Tue Jan 5 13:47:50 2021. - Copyright IBM Corporation 2021. + Copyright IBM Corporation 2021, 2023. SPDX-License-Identifier: Apache-2.0 You can adapt this file completely to your liking, but it should at least contain the root `toctree` directive. -Welcome to Cloudant Python SDK's documentation! -=============================================== +Cloudant Python SDK's Documentation +=================================== + +CloudantV1 class +---------------- + +:class:`CloudantV1 ` + +Modules +------- .. toctree:: :maxdepth: 4 - :caption: Contents: - modules + cloudant_v1 + couchdb_session_authenticator + changes_follower Indices and tables -================== +------------------ * :ref:`genindex` * :ref:`modindex` -* :ref:`search` diff --git a/stubs/gen-its-mappings.json b/stubs/gen-its-mappings.json new file mode 100644 index 00000000..16772714 --- /dev/null +++ b/stubs/gen-its-mappings.json @@ -0,0 +1,3683 @@ +{ + "mappings": [ + { + "id": "3afba80c-759a-3356-acf2-93866111665b", + "request": { + "url": "/", + "method": "GET" + }, + "response": { + "status": 200, + "headers": { + "Cache-Control": "must-revalidate", + "Content-Type": "application/json", + "Date": "Mon, 16 May 2022 21:08:31 GMT", + "Server": "CouchDB/3.2.1 (Erlang OTP/20)", + "transaction-id": "95afae62f0ea5c9f26c869d9d00e2b8a", + "X-Cloudant-Action": "cloudantnosqldb.account-meta-info.read", + "X-Couch-Request-ID": "95afae62f0", + "X-Frame-Options": "DENY", + "Strict-Transport-Security": "max-age=31536000", + "X-Content-Type-Options": "nosniff", + "X-Cloudant-Request-Class": "unlimited", + "X-Cloudant-Backend": "bm-cc-us-east-05", + "Via": "1.1 lb2.bm-cc-us-east-05 (Glum/1.101.5)", + "Vary": "Accept-Encoding" + }, + "jsonBody": { + "couchdb": "Welcome", + "version": "3.2.1", + "vendor": { + "name": "IBM Cloudant", + "version": "8287", + "variant": "paas" + }, + "features": [ + "geo", + "search", + "access-ready", + "iam", + "partitioned", + "pluggable-storage-engines", + "scheduler" + ], + "features_flags": [ + "partitioned" + ] + } + }, + "uuid": "3afba80c-759a-3356-acf2-93866111665b" + }, + { + "id": "082f68f8-c053-36d3-9eb1-cb8686d296aa", + "request": { + "url": "/_active_tasks", + "method": "GET" + }, + "response": { + "status": 200, + "body": "[]", + "headers": { + "Cache-Control": "must-revalidate", + "Content-Type": "application/json", + "Date": "Mon, 16 May 2022 21:09:00 GMT", + "Server": "CouchDB/3.2.1 (Erlang OTP/20)", + "transaction-id": "249fc0a91bbf42406f8d93f1e20e401f", + "X-Cloudant-Action": "cloudantnosqldb.account-active-tasks.read", + "X-Couch-Request-ID": "249fc0a91b", + "X-Frame-Options": "DENY", + "Strict-Transport-Security": "max-age=31536000", + "X-Content-Type-Options": "nosniff", + "X-Cloudant-Request-Class": "unlimited", + "X-Cloudant-Backend": "bm-cc-us-east-05", + "Via": "1.1 lb2.bm-cc-us-east-05 (Glum/1.101.5)", + "Vary": "Accept-Encoding" + } + }, + "uuid": "082f68f8-c053-36d3-9eb1-cb8686d296aa" + }, + { + "id": "ad18e9e4-40a9-3cda-afe4-28217cbd04f5", + "request": { + "url": "/_all_dbs?descending=false&end_key=testString&limit=0&skip=0&start_key=testString", + "method": "GET" + }, + "response": { + "status": 200, + "body": "[\"testString\"]", + "headers": { + "Cache-Control": "must-revalidate", + "Content-Type": "application/json", + "Date": "Mon, 16 May 2022 21:08:34 GMT", + "ETag": "W/\"8I1PKGO1BVSLINUMI3N4GOJTG\"", + "Server": "CouchDB/3.2.1 (Erlang OTP/20)", + "transaction-id": "3153310816b744a750175766d6c3c436", + "X-Cloudant-Action": "cloudantnosqldb.account-all-dbs.read", + "X-Couch-Request-ID": "3153310816", + "X-Frame-Options": "DENY", + "Strict-Transport-Security": "max-age=31536000", + "X-Content-Type-Options": "nosniff", + "X-Cloudant-Request-Class": "unlimited", + "X-Cloudant-Backend": "bm-cc-us-east-05", + "Via": "1.1 lb2.bm-cc-us-east-05 (Glum/1.101.5)", + "Vary": "Accept-Encoding" + } + }, + "uuid": "ad18e9e4-40a9-3cda-afe4-28217cbd04f5" + }, + { + "id": "544965f4-c99a-38ac-8b9d-b8ae4eafc952", + "request": { + "url": "/_api/v2/api_keys", + "method": "POST" + }, + "response": { + "status": 201, + "headers": { + "Date": "Mon, 16 May 2022 21:08:53 GMT", + "Content-Type": "application/json", + "Cache-Control": "no-cache, no-store, must-revalidate", + "Vary": "Accept-Encoding", + "X-Cloudant-Action": "cloudantnosqldb.sapi.apikeys", + "x-envoy-upstream-service-time": "1075", + "cf-cache-status": "DYNAMIC", + "expect-ct": "max-age=604800, report-uri=\"https://report-uri.cloudflare.com/cdn-cgi/beacon/expect-ct\"", + "Server": "cloudflare", + "cf-ray": "70c717515d76586c-IAD", + "Strict-Transport-Security": "max-age=31536000", + "X-Content-Type-Options": "nosniff", + "X-Cloudant-Request-Class": "unlimited", + "X-Cloudant-Backend": "sapi.lb2.bm-cc-us-east-05.cloudant.net", + "Via": "1.1 lb2.bm-cc-us-east-05 (Glum/1.101.5)" + }, + "jsonBody": { + "ok": true, + "key": "apikey-00112233445566778899aabbccddeeff", + "password": "0000112233445566778899aaabbbcccdddeeefff" + } + }, + "uuid": "544965f4-c99a-38ac-8b9d-b8ae4eafc952" + }, + { + "id": "cf012f70-bff3-3596-bc47-c1f4cf97212d", + "request": { + "url": "/_api/v2/db/testString/_security", + "method": "PUT", + "bodyPatterns": [ + { + "matchesJsonPath": "$.[?(@.cloudant == null || @.cloudant.key1 != null)]", + "ignoreArrayOrder": false, + "ignoreExtraElements": true + } + ] + }, + "response": { + "status": 200, + "headers": { + "Date": "Mon, 16 May 2022 21:08:54 GMT", + "Content-Type": "application/json", + "Cache-Control": "no-cache, no-store, must-revalidate", + "Vary": "Accept-Encoding", + "X-Cloudant-Action": "cloudantnosqldb.sapi.db-security", + "x-envoy-upstream-service-time": "1461", + "cf-cache-status": "DYNAMIC", + "expect-ct": "max-age=604800, report-uri=\"https://report-uri.cloudflare.com/cdn-cgi/beacon/expect-ct\"", + "Server": "cloudflare", + "cf-ray": "70c717591f00822a-IAD", + "Strict-Transport-Security": "max-age=31536000", + "X-Content-Type-Options": "nosniff", + "X-Cloudant-Request-Class": "unlimited", + "X-Cloudant-Backend": "sapi.lb2.bm-cc-us-east-05.cloudant.net", + "Via": "1.1 lb2.bm-cc-us-east-05 (Glum/1.101.5)" + }, + "jsonBody": { + "ok": true + } + }, + "uuid": "cf012f70-bff3-3596-bc47-c1f4cf97212d" + }, + { + "id": "44f68031-9fad-3ed6-9871-d9e7c5fd396a", + "request": { + "url": "/_api/v2/user/activity_tracker/events", + "method": "GET" + }, + "response": { + "status": 200, + "headers": { + "Date": "Mon, 16 May 2022 21:09:05 GMT", + "Content-Type": "application/json", + "Cache-Control": "no-cache, no-store, must-revalidate", + "Vary": "Accept-Encoding", + "X-Cloudant-Action": "cloudantnosqldb.activity-tracker-event-types.read", + "x-envoy-upstream-service-time": "5165", + "cf-cache-status": "DYNAMIC", + "expect-ct": "max-age=604800, report-uri=\"https://report-uri.cloudflare.com/cdn-cgi/beacon/expect-ct\"", + "Server": "cloudflare", + "cf-ray": "70c71787be096fd7-IAD", + "Strict-Transport-Security": "max-age=31536000", + "X-Content-Type-Options": "nosniff", + "X-Cloudant-Request-Class": "unlimited", + "X-Cloudant-Backend": "sapi.lb2.bm-cc-us-east-05.cloudant.net", + "Via": "1.1 lb2.bm-cc-us-east-05 (Glum/1.101.5)" + }, + "jsonBody": { + "types": [ + "data", + "management" + ] + } + }, + "uuid": "44f68031-9fad-3ed6-9871-d9e7c5fd396a" + }, + { + "id": "e42d0204-7e2b-3850-bab6-3188dad08068", + "request": { + "url": "/_api/v2/user/activity_tracker/events", + "method": "POST", + "bodyPatterns": [ + { + "equalToJson": "{\"types\": [\"management\", \"data\"]}", + "ignoreArrayOrder": true, + "ignoreExtraElements": true + } + ] + }, + "response": { + "status": 200, + "headers": { + "Date": "Mon, 16 May 2022 21:09:07 GMT", + "Content-Type": "application/json", + "Cache-Control": "no-cache, no-store, must-revalidate", + "Vary": "Accept-Encoding", + "X-Cloudant-Action": "cloudantnosqldb.activity-tracker-event-types.write", + "x-envoy-upstream-service-time": "1396", + "cf-cache-status": "DYNAMIC", + "expect-ct": "max-age=604800, report-uri=\"https://report-uri.cloudflare.com/cdn-cgi/beacon/expect-ct\"", + "Server": "cloudflare", + "cf-ray": "70c717a998cb5d80-IAD", + "Strict-Transport-Security": "max-age=31536000", + "X-Content-Type-Options": "nosniff", + "X-Cloudant-Request-Class": "unlimited", + "X-Cloudant-Backend": "sapi.lb1.bm-cc-us-east-05.cloudant.net", + "Via": "1.1 lb1.bm-cc-us-east-05 (Glum/1.101.5)" + }, + "jsonBody": { + "ok": true + } + }, + "uuid": "e42d0204-7e2b-3850-bab6-3188dad08068" + }, + { + "id": "8593b1aa-6297-3120-a04e-c199e2808167", + "request": { + "url": "/_api/v2/user/capacity/databases", + "method": "GET" + }, + "response": { + "status": 200, + "headers": { + "Server": "Cloudant", + "X-Content-Type-Options": "nosniff", + "X-Cloudant-Backend": "bigblue", + "X-Couch-Request-ID": "686418ce", + "X-Cloudant-Request-Class": "unlimited", + "Date": "Wed, 29 Jan 2025 00:26:57 GMT", + "Via": "1.1 lb4.bigblue (Glum)", + "X-Frame-Options": "DENY", + "X-Cloudant-Action": "cloudantnosqldb.account-capacity-dbs.read", + "Strict-Transport-Security": "max-age=31536000", + "content-security-policy": "sandbox", + "Cache-Control": "must-revalidate", + "Content-Type": "application/json" + }, + "jsonBody": { + "current": { + "databases": { + "total": 200 + } + } + } + }, + "uuid": "8593b1aa-6297-3120-a04e-c199e2808167" + }, + { + "id": "f3eef08d-1cac-3c95-90bb-e1d95690c74b", + "request": { + "url": "/_api/v2/user/capacity/throughput", + "method": "PUT", + "bodyPatterns": [ + { + "matchesJsonPath": "$.blocks", + "ignoreArrayOrder": false, + "ignoreExtraElements": true + } + ] + }, + "response": { + "status": 200, + "headers": { + "Date": "Mon, 16 May 2022 21:08:34 GMT", + "Content-Type": "application/json", + "Cache-Control": "no-cache, no-store, must-revalidate", + "Vary": "Accept-Encoding", + "X-Cloudant-Action": "cloudantnosqldb.capacity-throughput.write", + "x-envoy-upstream-service-time": "838", + "cf-cache-status": "DYNAMIC", + "expect-ct": "max-age=604800, report-uri=\"https://report-uri.cloudflare.com/cdn-cgi/beacon/expect-ct\"", + "Server": "cloudflare", + "cf-ray": "70c716dc9fa05a2e-IAD", + "Strict-Transport-Security": "max-age=31536000", + "X-Content-Type-Options": "nosniff", + "X-Cloudant-Request-Class": "unlimited", + "X-Cloudant-Backend": "sapi.lb2.bm-cc-us-east-05.cloudant.net", + "Via": "1.1 lb2.bm-cc-us-east-05 (Glum/1.101.5)" + }, + "jsonBody": { + "current": { + "throughput": { + "blocks": 5, + "query": 25, + "read": 500, + "write": 250 + } + }, + "target": { + "throughput": { + "blocks": 10, + "query": 50, + "read": 1000, + "write": 500 + } + } + } + }, + "uuid": "f3eef08d-1cac-3c95-90bb-e1d95690c74b" + }, + { + "id": "472c4134-a11c-3c72-980c-85518316a338", + "request": { + "url": "/_api/v2/user/capacity/throughput", + "method": "GET" + }, + "response": { + "status": 200, + "headers": { + "Date": "Mon, 16 May 2022 21:08:33 GMT", + "Content-Type": "application/json", + "Cache-Control": "no-cache, no-store, must-revalidate", + "Vary": "Accept-Encoding", + "X-Cloudant-Action": "cloudantnosqldb.capacity-throughput.read", + "x-envoy-upstream-service-time": "1135", + "cf-cache-status": "DYNAMIC", + "expect-ct": "max-age=604800, report-uri=\"https://report-uri.cloudflare.com/cdn-cgi/beacon/expect-ct\"", + "Server": "cloudflare", + "cf-ray": "70c716d47ab17ffd-IAD", + "Strict-Transport-Security": "max-age=31536000", + "X-Content-Type-Options": "nosniff", + "X-Cloudant-Request-Class": "unlimited", + "X-Cloudant-Backend": "sapi.lb2.bm-cc-us-east-05.cloudant.net", + "Via": "1.1 lb2.bm-cc-us-east-05 (Glum/1.101.5)" + }, + "jsonBody": { + "current": { + "throughput": { + "read": 20, + "write": 10, + "query": 5 + } + } + } + }, + "uuid": "472c4134-a11c-3c72-980c-85518316a338" + }, + { + "id": "620fd692-b6a6-3b64-967c-ffee174c2cff", + "request": { + "url": "/_api/v2/user/config/cors", + "method": "PUT", + "bodyPatterns": [ + { + "equalToJson": "{\"allow_credentials\": true, \"enable_cors\": true, \"origins\": [ \"https://example.com\", \"https://www.example.com\" ]}", + "ignoreArrayOrder": false, + "ignoreExtraElements": false + } + ] + }, + "response": { + "status": 200, + "headers": { + "Date": "Fri, 20 May 2022 18:04:13 GMT", + "Content-Type": "application/json", + "Cache-Control": "no-cache, no-store, must-revalidate", + "Vary": "Accept-Encoding", + "X-Cloudant-Action": "cloudantnosqldb.sapi.usercors", + "x-envoy-upstream-service-time": "2487", + "cf-cache-status": "DYNAMIC", + "expect-ct": "max-age=604800, report-uri=\"https://report-uri.cloudflare.com/cdn-cgi/beacon/expect-ct\"", + "Server": "cloudflare", + "cf-ray": "70e6fe465b125962-IAD", + "Strict-Transport-Security": "max-age=31536000", + "X-Content-Type-Options": "nosniff", + "X-Cloudant-Request-Class": "unlimited", + "X-Cloudant-Backend": "sapi.lb2.bm-cc-us-east-05.cloudant.net", + "Via": "1.1 lb2.bm-cc-us-east-05 (Glum/1.102.0)" + }, + "jsonBody": { + "ok": true + } + }, + "uuid": "620fd692-b6a6-3b64-967c-ffee174c2cff" + }, + { + "id": "a2e4e696-e631-33f8-b168-5015048cdad8", + "request": { + "url": "/_api/v2/user/config/cors", + "method": "GET" + }, + "response": { + "status": 200, + "headers": { + "Date": "Mon, 16 May 2022 21:08:56 GMT", + "Content-Type": "application/json", + "Cache-Control": "no-cache, no-store, must-revalidate", + "Vary": "Accept-Encoding", + "X-Cloudant-Action": "cloudantnosqldb.sapi.usercors", + "x-envoy-upstream-service-time": "1371", + "cf-cache-status": "DYNAMIC", + "expect-ct": "max-age=604800, report-uri=\"https://report-uri.cloudflare.com/cdn-cgi/beacon/expect-ct\"", + "Server": "cloudflare", + "cf-ray": "70c717635b6d82ea-IAD", + "Strict-Transport-Security": "max-age=31536000", + "X-Content-Type-Options": "nosniff", + "X-Cloudant-Request-Class": "unlimited", + "X-Cloudant-Backend": "sapi.lb2.bm-cc-us-east-05.cloudant.net", + "Via": "1.1 lb2.bm-cc-us-east-05 (Glum/1.101.5)" + }, + "jsonBody": { + "enable_cors": true, + "origins": [ + "https://example.com" + ], + "allow_credentials": false + } + }, + "uuid": "a2e4e696-e631-33f8-b168-5015048cdad8" + }, + { + "id": "8593b1aa-6297-3120-a04e-c199e2808168", + "request": { + "url": "/_api/v2/user/current/databases", + "method": "GET" + }, + "response": { + "status": 200, + "headers": { + "Server": "Cloudant", + "X-Content-Type-Options": "nosniff", + "X-Cloudant-Backend": "bigblue", + "X-Couch-Request-ID": "686418ce", + "X-Cloudant-Request-Class": "unlimited", + "Date": "Wed, 29 Jan 2025 00:26:57 GMT", + "Via": "1.1 lb4.bigblue (Glum)", + "X-Frame-Options": "DENY", + "X-Cloudant-Action": "cloudantnosqldb.account-current-dbs.read", + "Strict-Transport-Security": "max-age=31536000", + "content-security-policy": "sandbox", + "Cache-Control": "must-revalidate", + "Content-Type": "application/json" + }, + "jsonBody": { + "databases": { + "total": 42 + } + } + }, + "uuid": "8593b1aa-6297-3120-a04e-c199e2808168" + }, + { + "id": "8c38b725-a6ac-3f63-a723-9f4925ae23fd", + "request": { + "url": "/_api/v2/user/current/throughput", + "method": "GET" + }, + "response": { + "status": 200, + "headers": { + "Date": "Mon, 16 May 2022 21:09:09 GMT", + "Content-Type": "application/json", + "Cache-Control": "no-cache, no-store, must-revalidate", + "Vary": "Accept-Encoding", + "X-Cloudant-Action": "cloudantnosqldb.current-throughput.read", + "x-envoy-upstream-service-time": "1269", + "cf-cache-status": "DYNAMIC", + "expect-ct": "max-age=604800, report-uri=\"https://report-uri.cloudflare.com/cdn-cgi/beacon/expect-ct\"", + "Server": "cloudflare", + "cf-ray": "70c717b35e550660-IAD", + "Strict-Transport-Security": "max-age=31536000", + "X-Content-Type-Options": "nosniff", + "X-Cloudant-Request-Class": "unlimited", + "X-Cloudant-Backend": "sapi.lb1.bm-cc-us-east-05.cloudant.net", + "Via": "1.1 lb1.bm-cc-us-east-05 (Glum/1.101.5)" + }, + "jsonBody": { + "throughput": { + "read": 0, + "write": 0, + "query": 0 + } + } + }, + "uuid": "8c38b725-a6ac-3f63-a723-9f4925ae23fd" + }, + { + "id": "1caf7257-c094-3a41-92ad-a6e49a376615", + "request": { + "urlPath": "/_db_updates", + "method": "GET", + "queryParameters": { + "feed": { + "equalTo": "normal" + }, + "heartbeat": { + "equalTo": "1" + }, + "since": { + "equalTo": "0" + }, + "timeout": { + "equalTo": "60000" + } + } + }, + "response": { + "status": 200, + "headers": { + "Cache-Control": "must-revalidate", + "Content-Type": "application/json", + "Date": "Mon, 16 May 2022 21:08:34 GMT", + "Server": "CouchDB/3.2.1 (Erlang OTP/20)", + "transaction-id": "2d4a268c250238deeff1fe847741aca0", + "X-Cloudant-Action": "cloudantnosqldb.account-db-updates.read", + "X-Couch-Request-ID": "2d4a268c25", + "X-Frame-Options": "DENY", + "Strict-Transport-Security": "max-age=31536000", + "X-Content-Type-Options": "nosniff", + "X-Cloudant-Request-Class": "unlimited", + "X-Cloudant-Backend": "bm-cc-us-east-05", + "Via": "1.1 lb2.bm-cc-us-east-05 (Glum/1.101.5)" + }, + "jsonBody": { + "last_seq": "1-g1AAAAMFeJydkcENgjAUhquYePHqBN48EEBj4klXUOgAbSExDUJi8OwUrqDQJZyCJZhBqD8XE0Iiwulvmvf1e68vJITMToZPFj4X8SXY-3xjijC--ixKLMv-ns0oSEIUjxnhc0o9yUgTW_3GaojypVKZPBlsdMbdVFhbW6yd_15p2pwOG0SK77SuevcJSHP06rbILicgj0ul0t5OQBm_aV22yI5l1FAVTciR0ntfKzAXGOKJz-prBpYCQ7y1fg1wl8AQB0rzAW4MnCMeShUD3Bi4QGDTGrT8AMkPBk0", + "results": [ + { + "dbname": "events", + "seq": "1-g1AAAAMFeJydkcENgjAUhquYePHqBN48EEBj4klXUOgAbSExDUJi8OwUrqDQJZyCJZhBqD8XE0Iiwulvmvf1e68vJITMToZPFj4X8SXY-3xjijC--ixKLMv-ns0oSEIUjxnhc0o9yUgTW_3GaojypVKZPBlsdMbdVFhbW6yd_15p2pwOG0SK77SuevcJSHP06rbILicgj0ul0t5OQBm_aV22yI5l1FAVTciR0ntfKzAXGOKJz-prBpYCQ7y1fg1wl8AQB0rzAW4MnCMeShUD3Bi4QGDTGrT8AMkPBk0", + "type": "created" + } + ] + } + }, + "uuid": "1caf7257-c094-3a41-92ad-a6e49a376615" + }, + { + "id": "0e3d6794-25ed-3bf2-90ea-34410b429c76", + "request": { + "url": "/_dbs_info", + "method": "POST", + "bodyPatterns": [ + { + "equalToJson": "{\"keys\": [\"products\", \"users\", \"orders\"]}", + "ignoreArrayOrder": false, + "ignoreExtraElements": true + } + ] + }, + "response": { + "status": 200, + "headers": { + "Cache-Control": "must-revalidate", + "Content-Type": "application/json", + "Date": "Mon, 16 May 2022 21:08:34 GMT", + "Server": "CouchDB/3.2.1 (Erlang OTP/20)", + "transaction-id": "20e0a09fd367bbb1463ff32d6616f4e6", + "X-Cloudant-Action": "cloudantnosqldb.account-dbs-info.read", + "X-Couch-Request-ID": "20e0a09fd3", + "X-Frame-Options": "DENY", + "Strict-Transport-Security": "max-age=31536000", + "X-Content-Type-Options": "nosniff", + "X-Cloudant-Request-Class": "unlimited", + "X-Cloudant-Backend": "bm-cc-us-east-05", + "Via": "1.1 lb2.bm-cc-us-east-05 (Glum/1.101.5)", + "Vary": "Accept-Encoding" + }, + "jsonBody": [ + { + "key": "testdb", + "info": { + "db_name": "testdb", + "instance_start_time": "1652999321", + "purge_seq": "0-g1AAAAQXeJzLYWBgEMhgTmHQSElKzi9KdUhJMtRLytVNTtYtLdZNTSwu0TUw1UvOyS9NScwr0ctLLckB6mDKYwGSDB-A1H8gyEpkABmhiTCCeDMeQMx4DzUD4QwToo24ADHiPoYR5kQbcQBixHkMI4j3yAaIEfspCYwFEDPWUxAYEyBGzKcgMBogRvSTFRhJBUAyqZ7sVJGUANKfj9BPWiAkBYC0x2PTTkwAJDmAtPtj006U4w1A2u3J97wCSL8-2Z4XAGmXJ9fziQxJ_BC9WQB5BlMh", + "update_seq": "14-g1AAAAereJy908tNwzAYwHHT8jjSDeAIhxTbiWPnRDeADcCfH6qqPhBtz7ABbAAbwAawAWxAN4ANwI4jpbk1SG4OjhQl30_OPxkjhHrDrkYnGtTs1gw0kD5MEqWS5Twxcr5IMOur8Wyp5XTRn5rF2D3Rme66Ff240687RsOu7EzchQPLcJpp1mKct0_rmzfHVwH_9jgqcYazlGeizbzmzrON8c-Af9U4yWXGqGgxrmnzje33YH_UtmDCgCYtxv23-Guw32qbG4u5Ntso_hzwlxpXime8YNso_hjwp7XPrbAU22ILxe-D_bC2cWA5FjZqcbhxK9xV_3dVmyrCDI1bG649PGvAqSo01TJuabj08FUDNnmusSFRK8PAuxeVuxMKE0ssp3ELY--eN_Zrtdug4pELH3v4rIL3ShhEqhk1kQv3PHxUwfslTAtgTEDUwhLBYeM1C4GNkdBizugP-uNrDA", + "sizes": { + "file": 666707, + "external": 304, + "active": 12758 + }, + "props": {}, + "doc_del_count": 1, + "doc_count": 3, + "disk_format_version": 8, + "compact_running": false, + "cluster": { + "q": 16, + "n": 3, + "w": 2, + "r": 2 + } + } + } + ] + }, + "uuid": "0e3d6794-25ed-3bf2-90ea-34410b429c76" + }, + { + "id": "2da72265-1cef-3bc6-bb1b-2cdeb811ceba", + "request": { + "url": "/_membership", + "method": "GET" + }, + "response": { + "status": 200, + "headers": { + "Cache-Control": "must-revalidate", + "Content-Type": "application/json", + "Date": "Mon, 16 May 2022 21:08:31 GMT", + "Server": "CouchDB/3.2.1 (Erlang OTP/20)", + "transaction-id": "fa692f7cfd6a091210084696d74c9e78", + "X-Cloudant-Action": "cloudantnosqldb.cluster-membership.read", + "X-Frame-Options": "DENY", + "Strict-Transport-Security": "max-age=31536000", + "X-Content-Type-Options": "nosniff", + "X-Cloudant-Request-Class": "unlimited", + "X-Cloudant-Backend": "bm-cc-us-east-05", + "Via": "1.1 lb2.bm-cc-us-east-05 (Glum/1.101.5)", + "Vary": "Accept-Encoding" + }, + "jsonBody": { + "all_nodes": [ + "dbcore@db1.bm-cc-us-east-05.cloudant.net", + "dbcore@db10.bm-cc-us-east-05.cloudant.net", + "dbcore@db11.bm-cc-us-east-05.cloudant.net", + "dbcore@db12.bm-cc-us-east-05.cloudant.net", + "dbcore@db2.bm-cc-us-east-05.cloudant.net", + "dbcore@db3.bm-cc-us-east-05.cloudant.net", + "dbcore@db4.bm-cc-us-east-05.cloudant.net", + "dbcore@db5.bm-cc-us-east-05.cloudant.net", + "dbcore@db6.bm-cc-us-east-05.cloudant.net", + "dbcore@db7.bm-cc-us-east-05.cloudant.net", + "dbcore@db8.bm-cc-us-east-05.cloudant.net", + "dbcore@db9.bm-cc-us-east-05.cloudant.net" + ], + "cluster_nodes": [ + "dbcore@db1.bm-cc-us-east-05.cloudant.net", + "dbcore@db10.bm-cc-us-east-05.cloudant.net", + "dbcore@db11.bm-cc-us-east-05.cloudant.net", + "dbcore@db12.bm-cc-us-east-05.cloudant.net", + "dbcore@db2.bm-cc-us-east-05.cloudant.net", + "dbcore@db3.bm-cc-us-east-05.cloudant.net", + "dbcore@db4.bm-cc-us-east-05.cloudant.net", + "dbcore@db5.bm-cc-us-east-05.cloudant.net", + "dbcore@db6.bm-cc-us-east-05.cloudant.net", + "dbcore@db7.bm-cc-us-east-05.cloudant.net", + "dbcore@db8.bm-cc-us-east-05.cloudant.net", + "dbcore@db9.bm-cc-us-east-05.cloudant.net" + ] + } + }, + "uuid": "2da72265-1cef-3bc6-bb1b-2cdeb811ceba" + }, + { + "id": "109ff4b5-4d2a-32b8-890d-f15cfe54173b", + "request": { + "urlPath": "/_replicator", + "method": "POST", + "queryParameters": { + "batch": { + "equalTo": "ok" + } + }, + "bodyPatterns": [ + { + "equalToJson": "{\"source\": {\"url\": \"https://my-source-instance.cloudantnosqldb.appdomain.cloud.example/animaldb\"}, \"target\": {\"url\": \"https://my-source-instance.cloudantnosqldb.appdomain.cloud.example/animaldb\"}}", + "ignoreArrayOrder": false, + "ignoreExtraElements": true + } + ] + }, + "response": { + "status": 201, + "headers": { + "Cache-Control": "must-revalidate", + "Content-Type": "application/json", + "Date": "Fri, 20 May 2022 16:54:50 GMT", + "ETag": "W/\"22-257d246167d34fcc2ca07926ba0213d1\"", + "Location": "https://84ab8038-d8f1-450c-9645-4d16b448eb31-bluemix.cloudant.com/_replicator/testString", + "Server": "CouchDB/3.2.1 (Erlang OTP/20)", + "transaction-id": "ce157aeb0e62a81f05c0e8c9e4d24c55", + "X-Cloudant-Action": "cloudantnosqldb.replication.write", + "X-Couch-Request-ID": "ce157aeb0e", + "X-Frame-Options": "DENY", + "Strict-Transport-Security": "max-age=31536000", + "X-Content-Type-Options": "nosniff", + "X-Cloudant-Request-Class": "write", + "X-Cloudant-Backend": "bm-cc-us-east-05", + "Via": "1.1 lb1.bm-cc-us-east-05 (Glum/1.102.0)", + "Vary": "Accept-Encoding" + }, + "jsonBody": { + "ok": true, + "id": "testString", + "rev": "1-257d246167d34fcc2ca07926ba0213d1" + } + }, + "uuid": "109ff4b5-4d2a-32b8-890d-f15cfe54173b" + }, + { + "id": "ea621c78-5215-34ac-94ce-95e13e1182bd", + "request": { + "url": "/_replicator/testString", + "method": "HEAD" + }, + "response": { + "status": 200, + "headers": { + "Cache-Control": "must-revalidate", + "Content-Type": "application/json", + "Date": "Fri, 20 May 2022 16:18:40 GMT", + "ETag": "\"2-2eb76179e2608844d7fab7a4ab67c5bf\"", + "Server": "CouchDB/3.2.1 (Erlang OTP/20)", + "transaction-id": "a957f9a0e94e5cc392c318ddfd9530a1", + "X-Cloudant-Action": "cloudantnosqldb.replication.read", + "X-Couch-Request-ID": "a957f9a0e9", + "X-Frame-Options": "DENY", + "Strict-Transport-Security": "max-age=31536000", + "X-Content-Type-Options": "nosniff", + "X-Cloudant-Request-Class": "lookup", + "X-Cloudant-Backend": "bm-cc-us-east-05", + "Via": "1.1 lb2.bm-cc-us-east-05 (Glum/1.101.7)" + } + }, + "uuid": "ea621c78-5215-34ac-94ce-95e13e1182bd" + }, + { + "id": "ee208ab6-a73d-3325-b9b0-afcdaaaccab3", + "request": { + "urlPath": "/_replicator/testString", + "method": "DELETE", + "queryParameters": { + "batch": { + "equalTo": "ok" + }, + "rev": { + "equalTo": "testString" + } + } + }, + "response": { + "status": 200, + "headers": { + "Cache-Control": "must-revalidate", + "Content-Type": "application/json", + "Date": "Mon, 23 May 2022 19:04:39 GMT", + "ETag": "W/\"27-364238f68a4eb90d4e10ad0ca5ad7772\"", + "Server": "CouchDB/3.2.1 (Erlang OTP/20)", + "transaction-id": "68250b09c2b37af95a2b3507bd887d90", + "X-Cloudant-Action": "cloudantnosqldb.replication.write", + "X-Couch-Request-ID": "68250b09c2", + "X-Frame-Options": "DENY", + "Strict-Transport-Security": "max-age=31536000", + "X-Content-Type-Options": "nosniff", + "X-Cloudant-Request-Class": "write", + "X-Cloudant-Backend": "bm-cc-us-east-05", + "Via": "1.1 lb1.bm-cc-us-east-05 (Glum/1.102.0)", + "Vary": "Accept-Encoding" + }, + "jsonBody": { + "ok": true, + "id": "testString", + "rev": "3-364238f68a4eb90d4e10ad0ca5ad7772" + } + }, + "uuid": "ee208ab6-a73d-3325-b9b0-afcdaaaccab3" + }, + { + "id": "109ff4b5-4d2a-32b8-890d-f15cfe54173a", + "request": { + "urlPath": "/_replicator/testString", + "method": "PUT", + "queryParameters": { + "batch": { + "equalTo": "ok" + }, + "rev": { + "equalTo": "testString" + }, + "new_edits": { + "equalTo": "false" + } + }, + "bodyPatterns": [ + { + "equalToJson": "{\"source\": {\"url\": \"https://my-source-instance.cloudantnosqldb.appdomain.cloud.example/animaldb\"}, \"target\": {\"url\": \"https://my-source-instance.cloudantnosqldb.appdomain.cloud.example/animaldb\"}}", + "ignoreArrayOrder": false, + "ignoreExtraElements": true + } + ] + }, + "response": { + "status": 201, + "headers": { + "Cache-Control": "must-revalidate", + "Content-Type": "application/json", + "Date": "Fri, 20 May 2022 16:54:50 GMT", + "ETag": "W/\"22-257d246167d34fcc2ca07926ba0213d1\"", + "Location": "https://84ab8038-d8f1-450c-9645-4d16b448eb31-bluemix.cloudant.com/_replicator/testString", + "Server": "CouchDB/3.2.1 (Erlang OTP/20)", + "transaction-id": "ce157aeb0e62a81f05c0e8c9e4d24c55", + "X-Cloudant-Action": "cloudantnosqldb.replication.write", + "X-Couch-Request-ID": "ce157aeb0e", + "X-Frame-Options": "DENY", + "Strict-Transport-Security": "max-age=31536000", + "X-Content-Type-Options": "nosniff", + "X-Cloudant-Request-Class": "write", + "X-Cloudant-Backend": "bm-cc-us-east-05", + "Via": "1.1 lb1.bm-cc-us-east-05 (Glum/1.102.0)", + "Vary": "Accept-Encoding" + }, + "jsonBody": { + "ok": true, + "id": "testString", + "rev": "1-257d246167d34fcc2ca07926ba0213d1" + } + }, + "uuid": "109ff4b5-4d2a-32b8-890d-f15cfe54173a" + }, + { + "id": "44eb7118-b9d0-38ee-8ada-9097415e6950", + "request": { + "urlPath": "/_replicator/testString", + "method": "GET", + "queryParameters": { + "att_encoding_info": { + "equalTo": "false" + }, + "attachments": { + "equalTo": "false" + }, + "deleted_conflicts": { + "equalTo": "false" + }, + "conflicts": { + "equalTo": "false" + }, + "latest": { + "equalTo": "false" + }, + "local_seq": { + "equalTo": "false" + }, + "meta": { + "equalTo": "false" + }, + "rev": { + "equalTo": "testString" + }, + "revs": { + "equalTo": "false" + }, + "revs_info": { + "equalTo": "false" + } + } + }, + "response": { + "status": 200, + "headers": { + "Cache-Control": "must-revalidate", + "Content-Type": "application/json", + "Date": "Fri, 20 May 2022 16:20:06 GMT", + "ETag": "W/\"2-2eb76179e2608844d7fab7a4ab67c5bf\"", + "Server": "CouchDB/3.2.1 (Erlang OTP/20)", + "transaction-id": "2211e195b24b7597aa72a5b7aa9bb756", + "X-Cloudant-Action": "cloudantnosqldb.replication.read", + "X-Couch-Request-ID": "2211e195b2", + "X-Frame-Options": "DENY", + "Strict-Transport-Security": "max-age=31536000", + "X-Content-Type-Options": "nosniff", + "X-Cloudant-Request-Class": "lookup", + "X-Cloudant-Backend": "bm-cc-us-east-05", + "Via": "1.1 lb2.bm-cc-us-east-05 (Glum/1.101.7)", + "Vary": "Accept-Encoding" + }, + "jsonBody": { + "_id": "testString", + "_rev": "2-2eb76179e2608844d7fab7a4ab67c5bf", + "source": { + "url": "testString" + }, + "target": { + "url": "testString" + }, + "owner": "iam-ServiceId-d3730bc8-88fb-4a35-9f1d-6e17f6cf369e", + "_replication_state": "failed", + "_replication_state_time": "2022-05-16T21:08:50Z", + "_replication_state_reason": "{error,{case_clause,{error,invalid_uri}}}" + } + }, + "uuid": "44eb7118-b9d0-38ee-8ada-9097415e6950" + }, + { + "id": "13d8a8e0-e54e-3e8f-9649-d902a9950f31", + "request": { + "url": "/_scheduler/docs?limit=0&skip=0&states=initializing", + "method": "GET" + }, + "response": { + "status": 200, + "headers": { + "Cache-Control": "must-revalidate", + "content-security-policy": "sandbox", + "Content-Type": "application/json", + "Date": "Mon, 16 May 2022 21:08:50 GMT", + "Server": "CouchDB/3.2.1 (Erlang OTP/20)", + "transaction-id": "95a00aef1c8f1b5d2ccd575501f38e8a", + "X-Cloudant-Action": "cloudantnosqldb.replication-scheduler.read", + "X-Couch-Request-ID": "95a00aef1c", + "X-Frame-Options": "DENY", + "Strict-Transport-Security": "max-age=31536000", + "X-Content-Type-Options": "nosniff", + "X-Cloudant-Request-Class": "unlimited", + "X-Cloudant-Backend": "bm-cc-us-east-05", + "Via": "1.1 lb2.bm-cc-us-east-05 (Glum/1.101.5)", + "Vary": "Accept-Encoding" + }, + "jsonBody": { + "total_rows": 1, + "offset": 0, + "docs": [ + { + "database": "84ab8038-d8f1-450c-9645-4d16b448eb31-bluemix/_replicator", + "doc_id": "e945ba14464aef7dd519ba36ae6f9466", + "id": null, + "source": "https://84ab8038-d8f1-450c-9645-4d16b448eb31-bluemix.cloudant.com/animaldb-target/", + "target": "https://84ab8038-d8f1-450c-9645-4d16b448eb31-bluemix.cloudant.com/animaldb/", + "state": "completed", + "error_count": 0, + "info": { + "revisions_checked": 15, + "missing_revisions_found": 15, + "docs_read": 15, + "docs_written": 15, + "changes_pending": null, + "doc_write_failures": 0, + "checkpointed_source_seq": "15-g1AAAAcaeJyt1MFNwzAUgGHTIiFOdAM4wiHFdmPHOdENYAPwsx2Vqk0Rbc-wAWwAG8AGsAFsQDeADYpTVyQ50CZuL4kUJf-nxM8ZIIRavaZGJxrU6NZ0NRDWhmGgVDAdB0aOJwEmbTUYTbVMJ-3UTAb2kYZEcDCfz_u9pmwM7YU9iIgxgDU6_gt1Vnc8VGjZIxwu4Z0FHCkBRIlSi25fPsrk05JsGAUFUHzlePswzuCzEiy5lFrSIhzWgnkVuJvB5yUYFOYCS_9FrgRfZPDlEkYLOCEmDIWF96epNsl1arRP-Sorj0pl2qGMJnjT8k1WviuVcRhzJfWK8pppWZTTXXtE9_Zk4w_5fpMMKGPKfymq448OfyoMYGSiiJsN9lx1_dnpL7muGO2EOPHfd9XxV4e_5d89hAg4Ft57j665u4C_O_wjxzmNRSi496LXwD8d_pXPc0woSUD8P8816jNX_87rSsSJYWQr9R9XL_4xATBwVWPV-r9_cTjG" + }, + "start_time": "2021-11-07T23:54:45Z", + "last_updated": "2021-11-07T23:54:49Z" + } + ] + } + }, + "uuid": "13d8a8e0-e54e-3e8f-9649-d902a9950f31" + }, + { + "id": "775f4f0a-3b8a-37e2-aa47-140285fb2b1c", + "request": { + "url": "/_scheduler/docs/_replicator/testString", + "method": "GET" + }, + "response": { + "status": 200, + "headers": { + "Cache-Control": "must-revalidate", + "Content-Type": "application/json", + "Date": "Mon, 16 May 2022 21:08:50 GMT", + "Server": "CouchDB/3.2.1 (Erlang OTP/20)", + "transaction-id": "2dd32790877ffef6279a3f9fe908836f", + "X-Cloudant-Action": "cloudantnosqldb.replication-scheduler.read", + "X-Couch-Request-ID": "2dd3279087", + "X-Frame-Options": "DENY", + "Strict-Transport-Security": "max-age=31536000", + "X-Content-Type-Options": "nosniff", + "X-Cloudant-Request-Class": "unlimited", + "X-Cloudant-Backend": "bm-cc-us-east-05", + "Via": "1.1 lb2.bm-cc-us-east-05 (Glum/1.101.5)", + "Vary": "Accept-Encoding" + }, + "jsonBody": { + "database": "_replicator", + "doc_id": "testString", + "id": null, + "source": null, + "target": null, + "state": "failed", + "error_count": 1, + "info": { + "error": "{error,{case_clause,{error,invalid_uri}}}" + }, + "start_time": "2022-05-16T21:08:50Z", + "last_updated": "2022-05-16T21:08:50Z" + } + }, + "uuid": "775f4f0a-3b8a-37e2-aa47-140285fb2b1c" + }, + { + "id": "266af994-f5f5-31d5-bcda-15a9ea3c86b5", + "request": { + "url": "/_scheduler/docs/_replicator/testString", + "method": "HEAD" + }, + "response": { + "status": 200, + "headers": { + "Cache-Control": "must-revalidate", + "Content-Type": "application/json", + "Date": "Fri, 20 May 2022 16:22:17 GMT", + "Server": "CouchDB/3.2.1 (Erlang OTP/20)", + "transaction-id": "da1acdc1a05b5d5bbaca269dca53b94d", + "X-Cloudant-Action": "cloudantnosqldb.replication-scheduler.read", + "X-Couch-Request-ID": "da1acdc1a0", + "X-Frame-Options": "DENY", + "Strict-Transport-Security": "max-age=31536000", + "X-Content-Type-Options": "nosniff", + "X-Cloudant-Request-Class": "unlimited", + "X-Cloudant-Backend": "bm-cc-us-east-05", + "Via": "1.1 lb2.bm-cc-us-east-05 (Glum/1.101.7)" + } + }, + "uuid": "266af994-f5f5-31d5-bcda-15a9ea3c86b5" + }, + { + "id": "5c158d49-97e6-3f2b-99ec-2f6629a13c77", + "request": { + "url": "/_scheduler/jobs?limit=25&skip=0", + "method": "GET" + }, + "response": { + "status": 200, + "jsonBody": { + "total_rows": 1, + "offset": 0, + "jobs": [ + { + "database": "_replicator", + "doc_id": "testString", + "history": [ + { + "timestamp": "2022-05-16T21:08:50Z", + "type": "started" + }, + { + "timestamp": "2022-05-16T21:08:50Z", + "type": "error" + } + ], + "id": "testString", + "info": { + "changes_pending": null, + "checkpointed_source_seq": "113-g1AAAACTeJzLYWBgYMpgTmHgz8tPSTV0MDQy1zMAQsMckEQiQ1L9____szKYE01ygQLsZsYGqcamiZjKcRqRxwIkGRqA1H-oSbZgk1KMLCzTDE0wdWUBAF6HJIQ", + "doc_write_failures": 0, + "docs_read": 2, + "docs_written": 2, + "missing_revisions_found": 0, + "revisions_checked": 2, + "source_seq": "2-g1AAAACTeJzLYWBgYMpgTmHgz8tPSTV0MDQy1zMAQsMckEQiQ1L9____szKYE1lzgQLsBsZm5pZJJpjKcRqRxwIkGRqA1H-oSexgk4yMkhITjS0wdWUBADfEJBg", + "through_seq": "2-g1AAAACTeJzLYWBgYMpgTmHgz8tPSTV0MDQy1zMAQsMckEQiQ1L9____szKYE1lzgQLsBsZm5pZJJpjKcRqRxwIkGRqA1H-oSexgk4yMkhITjS0wdWUBADfEJBg" + }, + "node": "node1@127.0.0.1", + "pid": "<0.1850.0>", + "source": "http://test.com/testString", + "start_time": "2022-05-16T21:08:50Z", + "target": "http://adm:*****@localhost:15984/testString/", + "user": null + } + ] + }, + "headers": { + "Cache-Control": "must-revalidate", + "Content-Type": "application/json", + "Date": "Fri, 20 May 2022 16:54:51 GMT", + "Server": "CouchDB/3.2.1 (Erlang OTP/20)", + "transaction-id": "d97a5d3526ee45e215450a0abea76caf", + "X-Cloudant-Action": "cloudantnosqldb.replication-scheduler.read", + "X-Couch-Request-ID": "d97a5d3526", + "X-Frame-Options": "DENY", + "Strict-Transport-Security": "max-age=31536000", + "X-Content-Type-Options": "nosniff", + "X-Cloudant-Request-Class": "unlimited", + "X-Cloudant-Backend": "bm-cc-us-east-05", + "Via": "1.1 lb1.bm-cc-us-east-05 (Glum/1.102.0)", + "Vary": "Accept-Encoding" + } + }, + "uuid": "5c158d49-97e6-3f2b-99ec-2f6629a13c77" + }, + { + "id": "e9f68b82-7693-38ea-bd6a-66227a0086bf", + "request": { + "url": "/_scheduler/jobs/testString", + "method": "GET" + }, + "response": { + "status": 200, + "jsonBody": { + "database": "_replicator", + "doc_id": "testString", + "history": [ + { + "timestamp": "2022-05-16T21:08:50Z", + "type": "started" + }, + { + "timestamp": "2022-05-16T21:08:50Z", + "type": "error" + } + ], + "id": "testString", + "info": { + "changes_pending": null, + "checkpointed_source_seq": "113-g1AAAACTeJzLYWBgYMpgTmHgz8tPSTV0MDQy1zMAQsMckEQiQ1L9____szKYE01ygQLsZsYGqcamiZjKcRqRxwIkGRqA1H-oSbZgk1KMLCzTDE0wdWUBAF6HJIQ", + "doc_write_failures": 0, + "docs_read": 2, + "docs_written": 2, + "missing_revisions_found": 0, + "revisions_checked": 2, + "source_seq": "2-g1AAAACTeJzLYWBgYMpgTmHgz8tPSTV0MDQy1zMAQsMckEQiQ1L9____szKYE1lzgQLsBsZm5pZJJpjKcRqRxwIkGRqA1H-oSexgk4yMkhITjS0wdWUBADfEJBg", + "through_seq": "2-g1AAAACTeJzLYWBgYMpgTmHgz8tPSTV0MDQy1zMAQsMckEQiQ1L9____szKYE1lzgQLsBsZm5pZJJpjKcRqRxwIkGRqA1H-oSexgk4yMkhITjS0wdWUBADfEJBg" + }, + "node": "node1@127.0.0.1", + "pid": "<0.1850.0>", + "source": "http://test.com/testString", + "start_time": "2022-05-16T21:08:50Z", + "target": "http://adm:*****@localhost:15984/testString/", + "user": null + }, + "headers": { + "Cache-Control": "must-revalidate", + "content-security-policy": "sandbox", + "Content-Type": "application/json", + "Date": "Fri, 20 May 2022 17:07:47 GMT", + "Server": "CouchDB/3.2.1 (Erlang OTP/20)", + "transaction-id": "7842b56965a4f079106b35134a6e7e7d", + "X-Cloudant-Action": "cloudantnosqldb.replication-scheduler.read", + "X-Couch-Request-ID": "7842b56965", + "X-Frame-Options": "DENY", + "Strict-Transport-Security": "max-age=31536000", + "X-Content-Type-Options": "nosniff", + "X-Cloudant-Request-Class": "unlimited", + "X-Cloudant-Backend": "bm-cc-us-east-05", + "Via": "1.1 lb2.bm-cc-us-east-05 (Glum/1.102.0)" + } + }, + "uuid": "e9f68b82-7693-38ea-bd6a-66227a0086bf" + }, + { + "id": "e1623757-bf89-30e0-b209-1f5601bc039a", + "request": { + "url": "/_scheduler/jobs/testString", + "method": "HEAD" + }, + "response": { + "status": 200, + "headers": { + "Cache-Control": "must-revalidate", + "content-security-policy": "sandbox", + "Content-Type": "application/json", + "Date": "Fri, 20 May 2022 17:06:24 GMT", + "Server": "CouchDB/3.2.1 (Erlang OTP/20)", + "transaction-id": "397edf40473b25285c6c9048a9159f5d", + "X-Cloudant-Action": "cloudantnosqldb.replication-scheduler.read", + "X-Couch-Request-ID": "397edf4047", + "X-Frame-Options": "DENY", + "Strict-Transport-Security": "max-age=31536000", + "X-Content-Type-Options": "nosniff", + "X-Cloudant-Request-Class": "unlimited", + "X-Cloudant-Backend": "bm-cc-us-east-05", + "Via": "1.1 lb2.bm-cc-us-east-05 (Glum/1.102.0)" + } + }, + "uuid": "e1623757-bf89-30e0-b209-1f5601bc039a" + }, + { + "id": "1244a623-9af3-3200-b5cf-72605afcbcdc", + "request": { + "url": "/_search_analyze", + "method": "POST", + "bodyPatterns": [ + { + "equalToJson": "{\"analyzer\": \"english\", \"text\": \"running is fun\"}", + "ignoreArrayOrder": false, + "ignoreExtraElements": true + } + ] + }, + "response": { + "status": 200, + "headers": { + "Cache-Control": "must-revalidate", + "Content-Type": "application/json", + "Date": "Mon, 16 May 2022 21:08:45 GMT", + "Server": "CouchDB/3.2.1 (Erlang OTP/20)", + "transaction-id": "752119aa13519496f4b8ca931ac9805d", + "X-Cloudant-Action": "cloudantnosqldb.account-search-analyze.execute", + "X-Couch-Request-ID": "752119aa13", + "X-Frame-Options": "DENY", + "Strict-Transport-Security": "max-age=31536000", + "X-Content-Type-Options": "nosniff", + "X-Cloudant-Request-Class": "query", + "X-Cloudant-Backend": "bm-cc-us-east-05", + "Via": "1.1 lb2.bm-cc-us-east-05 (Glum/1.101.5)", + "Vary": "Accept-Encoding" + }, + "jsonBody": { + "tokens": [ + "teststring" + ] + } + }, + "uuid": "1244a623-9af3-3200-b5cf-72605afcbcdc" + }, + { + "id": "7d66f494-6c33-3ad6-a9fb-478a37bdddb1", + "request": { + "url": "/_session", + "method": "GET" + }, + "response": { + "status": 200, + "headers": { + "Cache-Control": "must-revalidate", + "Content-Type": "application/json", + "Date": "Mon, 16 May 2022 21:08:51 GMT", + "Server": "CouchDB/3.2.1 (Erlang OTP/20)", + "transaction-id": "09b190ce0497173c7cd117d33f0cbce0", + "X-Cloudant-Action": "cloudantnosqldb.session.read", + "X-Frame-Options": "DENY", + "Strict-Transport-Security": "max-age=31536000", + "X-Content-Type-Options": "nosniff", + "X-Cloudant-Request-Class": "unlimited", + "X-Cloudant-Backend": "bm-cc-us-east-05", + "Via": "1.1 lb2.bm-cc-us-east-05 (Glum/1.101.5)", + "Vary": "Accept-Encoding" + }, + "jsonBody": { + "userCtx": { + "roles": [ + "_reader", + "_writer", + "_admin" + ], + "name": "iam-ServiceId-d3730bf4-80fx-4a35-9f1d-6e17f6cf369e" + }, + "ok": true, + "info": { + "authentication_handlers": [ + "iam", + "cookie", + "default", + "local" + ], + "authenticated": "iam", + "authentication_db": "redacted_auth_db" + } + } + }, + "uuid": "7d66f494-6c33-3ad6-a9fb-478a37bdddb1" + }, + { + "id": "2dbb122f-e2d2-37ba-948d-dfb542bed417", + "request": { + "url": "/_up", + "method": "GET" + }, + "response": { + "status": 200, + "headers": { + "Cache-Control": "must-revalidate", + "Content-Type": "application/json", + "Date": "Mon, 16 May 2022 21:09:00 GMT", + "Server": "CouchDB/3.2.1 (Erlang OTP/20)", + "transaction-id": "54aa03b6b2f86864aeb66bb8880a7410", + "X-Cloudant-Action": "cloudantnosqldb.account-up.read", + "X-Couch-Request-ID": "54aa03b6b2", + "X-Frame-Options": "DENY", + "Strict-Transport-Security": "max-age=31536000", + "X-Content-Type-Options": "nosniff", + "X-Cloudant-Request-Class": "unlimited", + "X-Cloudant-Backend": "bm-cc-us-east-05", + "Via": "1.1 lb2.bm-cc-us-east-05 (Glum/1.101.5)", + "Vary": "Accept-Encoding" + }, + "jsonBody": { + "status": "ok", + "seeds": {} + } + }, + "uuid": "2dbb122f-e2d2-37ba-948d-dfb542bed417" + }, + { + "id": "327b6531-390c-3921-8fa8-2d3a15a5c354", + "request": { + "url": "/_up", + "method": "HEAD" + }, + "response": { + "status": 200, + "headers": { + "Cache-Control": "must-revalidate", + "Content-Type": "application/json", + "Date": "Mon, 16 May 2022 21:08:59 GMT", + "Server": "CouchDB/3.2.1 (Erlang OTP/20)", + "transaction-id": "a3174645ff67c7323413c124304c3120", + "X-Cloudant-Action": "cloudantnosqldb.account-up.read", + "X-Couch-Request-ID": "a3174645ff", + "X-Frame-Options": "DENY", + "Strict-Transport-Security": "max-age=31536000", + "X-Content-Type-Options": "nosniff", + "X-Cloudant-Request-Class": "unlimited", + "X-Cloudant-Backend": "bm-cc-us-east-05", + "Via": "1.1 lb2.bm-cc-us-east-05 (Glum/1.101.5)" + } + }, + "uuid": "327b6531-390c-3921-8fa8-2d3a15a5c354" + }, + { + "id": "ff71d02c-9458-3cc5-8bda-10ef97e619e0", + "request": { + "url": "/_uuids?count=1", + "method": "GET" + }, + "response": { + "status": 200, + "headers": { + "Cache-Control": "no-cache", + "Content-Type": "application/json", + "Date": "Mon, 16 May 2022 21:08:31 GMT", + "ETag": "W/\"6QFC7LLFUJMXCK2CLNVW17C9T\"", + "Expires": "Mon, 01 Jan 1990 00:00:00 GMT", + "Pragma": "no-cache", + "Server": "CouchDB/3.2.1 (Erlang OTP/20)", + "transaction-id": "7b3d88210d51e4558a56f0a18a17a185", + "X-Cloudant-Action": "cloudantnosqldb.account-uuids.read", + "X-Frame-Options": "DENY", + "Strict-Transport-Security": "max-age=31536000", + "X-Content-Type-Options": "nosniff", + "X-Cloudant-Request-Class": "lookup", + "X-Cloudant-Backend": "bm-cc-us-east-05", + "Via": "1.1 lb2.bm-cc-us-east-05 (Glum/1.101.5)", + "Vary": "Accept-Encoding" + }, + "jsonBody": { + "uuids": [ + "4cf311226d91632bf1480e4edeac069e" + ] + } + }, + "uuid": "ff71d02c-9458-3cc5-8bda-10ef97e619e0" + }, + { + "id": "32a541c3-6915-3b1d-a8d8-26df21c8e3b4", + "request": { + "url": "/testString", + "method": "HEAD" + }, + "response": { + "status": 200, + "headers": { + "Cache-Control": "must-revalidate", + "Content-Type": "application/json", + "Date": "Mon, 16 May 2022 21:08:34 GMT", + "Server": "CouchDB/3.2.1 (Erlang OTP/20)", + "transaction-id": "5129cddfb161533246b92249c36bf908", + "X-Cloudant-Action": "cloudantnosqldb.database-info.read", + "X-Couch-Request-ID": "5129cddfb1", + "X-Frame-Options": "DENY", + "Strict-Transport-Security": "max-age=31536000", + "X-Content-Type-Options": "nosniff", + "X-Cloudant-Request-Class": "unlimited", + "X-Cloudant-Backend": "bm-cc-us-east-05", + "Via": "1.1 lb2.bm-cc-us-east-05 (Glum/1.101.5)" + } + }, + "uuid": "32a541c3-6915-3b1d-a8d8-26df21c8e3b4" + }, + { + "id": "28948775-8ce7-3d39-88ba-16ef4e7bf6f4", + "request": { + "url": "/testString/_all_docs/queries", + "method": "POST", + "bodyPatterns": [ + { + "equalToJson": "{\"queries\": [{}]}", + "ignoreArrayOrder": false, + "ignoreExtraElements": true + } + ] + }, + "response": { + "status": 200, + "headers": { + "Cache-Control": "must-revalidate", + "content-security-policy": "sandbox", + "Content-Type": "application/json", + "Date": "Mon, 16 May 2022 21:08:36 GMT", + "Server": "CouchDB/3.2.1 (Erlang OTP/20)", + "transaction-id": "1aa1063b6a539c0dfdc6fdc223e0e30e", + "X-Cloudant-Action": "cloudantnosqldb.any-document.read", + "X-Couch-Request-ID": "1aa1063b6a", + "X-Frame-Options": "DENY", + "Strict-Transport-Security": "max-age=31536000", + "X-Content-Type-Options": "nosniff", + "X-Cloudant-Request-Class": "query", + "X-Cloudant-Backend": "bm-cc-us-east-05", + "Via": "1.1 lb2.bm-cc-us-east-05 (Glum/1.101.5)", + "Vary": "Accept-Encoding" + }, + "jsonBody": { + "results": [ + { + "total_rows": 2, + "offset": 0, + "rows": [ + { + "id": "_design/testString", + "key": "_design/testString", + "value": { + "rev": "5-be9ac99e964a971214f795dacceba0fe" + } + }, + { + "id": "testString", + "key": "testString", + "value": { + "rev": "6-80028e69ea6e259eba0b419c3750128b" + } + } + ] + } + ] + } + }, + "uuid": "28948775-8ce7-3d39-88ba-16ef4e7bf6f4" + }, + { + "id": "7be40bd0-8e36-3edd-a7b5-e45656e2e5ad", + "request": { + "url": "/testString/_all_docs", + "method": "POST", + "bodyPatterns": [ + { + "equalToJson": "{}", + "ignoreArrayOrder": false, + "ignoreExtraElements": true + } + ] + }, + "response": { + "status": 200, + "headers": { + "Cache-Control": "must-revalidate", + "Content-Type": "application/json", + "Date": "Mon, 16 May 2022 21:08:35 GMT", + "Server": "CouchDB/3.2.1 (Erlang OTP/20)", + "transaction-id": "aeee0214122f14bdb09aaca920bfb854", + "X-Cloudant-Action": "cloudantnosqldb.any-document.read", + "X-Couch-Request-ID": "aeee021412", + "X-Frame-Options": "DENY", + "Strict-Transport-Security": "max-age=31536000", + "X-Content-Type-Options": "nosniff", + "X-Cloudant-Request-Class": "query", + "X-Cloudant-Backend": "bm-cc-us-east-05", + "Via": "1.1 lb2.bm-cc-us-east-05 (Glum/1.101.5)", + "Vary": "Accept-Encoding" + }, + "jsonBody": { + "total_rows": 2, + "offset": 0, + "rows": [ + { + "id": "_design/testString", + "key": "_design/testString", + "value": { + "rev": "5-be9ac99e964a971214f795dacceba0fe" + } + }, + { + "id": "testString", + "key": "testString", + "value": { + "rev": "6-80028e69ea6e259eba0b419c3750128b" + } + } + ] + } + }, + "uuid": "7be40bd0-8e36-3edd-a7b5-e45656e2e5ad" + }, + { + "id": "28b9ab32-ea1f-3dad-b95b-a7a4d85577a8", + "request": { + "url": "/testString/_bulk_docs", + "method": "POST", + "bodyPatterns": [ + { + "matchesJsonPath": "$.[?(@ == null || @.key1 != null)]", + "ignoreArrayOrder": false, + "ignoreExtraElements": true + } + ] + }, + "response": { + "status": 201, + "headers": { + "Cache-Control": "must-revalidate", + "Content-Type": "application/json", + "Date": "Mon, 16 May 2022 21:08:37 GMT", + "Server": "CouchDB/3.2.1 (Erlang OTP/20)", + "transaction-id": "3e01e9903d1dc1d839031298207d4698", + "X-Cloudant-Action": "cloudantnosqldb.data-document.write", + "x-cloudant-cost-write": "1", + "X-Couch-Request-ID": "3e01e9903d", + "X-Frame-Options": "DENY", + "Strict-Transport-Security": "max-age=31536000", + "X-Content-Type-Options": "nosniff", + "X-Cloudant-Request-Class": "write", + "X-Cloudant-Backend": "bm-cc-us-east-05", + "Via": "1.1 lb2.bm-cc-us-east-05 (Glum/1.101.5)", + "Vary": "Accept-Encoding" + }, + "jsonBody": [ + { + "ok": true, + "id": "a40deaed41325036f37b18a5c48e5c5c", + "rev": "1-967a00dff5e02add41819138abb3284d" + } + ] + }, + "uuid": "28b9ab32-ea1f-3dad-b95b-a7a4d85577a8" + }, + { + "id": "74cf16a4-75b1-3914-a50d-014650c7140a", + "request": { + "urlPath": "/testString/_bulk_get", + "method": "POST", + "queryParameters": { + "attachments": { + "equalTo": "false" + }, + "latest": { + "equalTo": "false" + }, + "revs": { + "equalTo": "false" + } + }, + "bodyPatterns": [ + { + "equalToJson": "{\"docs\": [{\"atts_since\": [\"1-99b02e08da151943c2dcb40090160bb8\"], \"id\": \"order00067\", \"rev\": \"3-917fa2381192822767f010b95b45325b\"}]}", + "ignoreArrayOrder": false, + "ignoreExtraElements": true + } + ] + }, + "response": { + "status": 200, + "headers": { + "Cache-Control": "must-revalidate", + "Content-Type": "application/json", + "Date": "Mon, 16 May 2022 21:08:37 GMT", + "Server": "CouchDB/3.2.1 (Erlang OTP/20)", + "transaction-id": "e007ac087f2f353b5deac55d745bf37d", + "X-Cloudant-Action": "cloudantnosqldb.any-document.read", + "x-cloudant-cost-lookup": "1", + "X-Couch-Request-ID": "e007ac087f", + "X-Frame-Options": "DENY", + "Strict-Transport-Security": "max-age=31536000", + "X-Content-Type-Options": "nosniff", + "X-Cloudant-Request-Class": "lookup", + "X-Cloudant-Backend": "bm-cc-us-east-05", + "Via": "1.1 lb2.bm-cc-us-east-05 (Glum/1.101.5)", + "Vary": "Accept-Encoding" + }, + "jsonBody": { + "results": [ + { + "id": "order00067", + "docs": [ + { + "error": { + "id": "order00067", + "rev": "3-917fa2381192822767f010b95b45325b", + "error": "not_found", + "reason": "missing" + } + } + ] + } + ] + } + }, + "uuid": "74cf16a4-75b1-3914-a50d-014650c7140a" + }, + { + "id": "020940d2-536c-36f7-9bf8-67b3b3fce208", + "request": { + "urlPath": "/testString/_changes", + "method": "POST", + "queryParameters": { + "att_encoding_info": { + "equalTo": "false" + }, + "attachments": { + "equalTo": "false" + }, + "conflicts": { + "equalTo": "false" + }, + "descending": { + "equalTo": "false" + }, + "feed": { + "equalTo": "normal" + }, + "filter": { + "equalTo": "testString" + }, + "heartbeat": { + "equalTo": "1" + }, + "include_docs": { + "equalTo": "false" + }, + "limit": { + "equalTo": "0" + }, + "seq_interval": { + "equalTo": "1" + }, + "since": { + "equalTo": "0" + }, + "style": { + "equalTo": "main_only" + }, + "timeout": { + "equalTo": "60000" + }, + "view": { + "equalTo": "testString" + } + } + }, + "response": { + "status": 200, + "jsonBody": { + "results": [ + { + "seq": "1-g1AAAAT7eJzLYWBgEMhgTmHQTElKzi9KdUhJMjTSS8rVTU7WLS3WTU0sLtE1MNVLzskvTUnMK9HLSy3JAWphSmRI4v___39WIgNIswZcsyURepMEgGSSPDbtZsRoVwBp1wdpz2BOZMgF8tiTks1NU9OMSDAK1V5ivJxkALLXHuFsEoMsyQGk35_sUAsAaY8nO9QSQNrzUUItNTEl1dLAkrahVgCyt57sUMtjAZIMDUAKaEQ_WSEHMWICxIj5ZIUexIgFECPWg0KQERyCholmaUlpqTQNQYjdGyB276ckFA9AzDhPQShegBhxn4JQfAAx4j0iHSZbmAETYhIdQvEDxG5QWswCAAkkmjk", + "id": "testString", + "changes": [ + { + "rev": "1-967a00dff5e02add41819138abb3284d" + } + ] + }, + { + "seq": "2-g1AAAAZVeJy9lM9NwzAYxQ1F4kg3gCMcEtz8c3yiG8AG4M_-oqpKE0TTM2wAG8AGsAFsABvABnSDYGOJ1LckKL04kpX3fk_fs50TQsazkSInCmR5g1MFk8CHhSelt1p6KJaVR2Nf5uVKiaLyC6xyLdkVBA7qup7PRoIs9MZ-lFBOFetiZLDHf3_zFlQY6xUODVgQV560kR8Z-amTGySLMQs6WLncNsMCarhnTeyOw4ap0Z87uVWKEiQMPO8LA750wGHMMGDYwapHUVeGWzpcFAo55cMWdW24t72LKvb0Su70R1vcN9klywIeZMOWZeEPFv64cVIYC6NUDVqYZT9Z9rNh7_yyJyLJIMNBS7PsF8t-_U9xb9bjvckf80ghhNso7sPCP3u9bNbiy1p8bxy8NNG3BrYw_7Vlm4sz_wEJbgPy", + "id": "_design/testString", + "changes": [ + { + "rev": "1-7a60109c85ab05898824bb73aef71884" + } + ] + } + ], + "last_seq": "2-g1AAAAZVeJy9lM9NwzAYxQ1F4kg3gCMcEtz8c3yiG8AG4M_-oqpKE0TTM2wAG8AGsAFsABvABnSDYGOJ1LckKL04kpX3fk_fs50TQsazkSInCmR5g1MFk8CHhSelt1p6KJaVR2Nf5uVKiaLyC6xyLdkVBA7qup7PRoIs9MZ-lFBOFetiZLDHf3_zFlQY6xUODVgQV560kR8Z-amTGySLMQs6WLncNsMCarhnTeyOw4ap0Z87uVWKEiQMPO8LA750wGHMMGDYwapHUVeGWzpcFAo55cMWdW24t72LKvb0Su70R1vcN9klywIeZMOWZeEPFv64cVIYC6NUDVqYZT9Z9rNh7_yyJyLJIMNBS7PsF8t-_U9xb9bjvckf80ghhNso7sPCP3u9bNbiy1p8bxy8NNG3BrYw_7Vlm4sz_wEJbgPy", + "pending": 0 + }, + "headers": { + "Cache-Control": "must-revalidate", + "Content-Type": "application/json", + "Date": "Mon, 16 May 2022 21:08:34 GMT", + "ETag": "W/\"8DLU1VJV4CEKAI17LB0GT0N6O\"", + "Server": "CouchDB/3.2.1 (Erlang OTP/20)", + "transaction-id": "404db768c41f8545f254f1dfd0ea5629", + "X-Cloudant-Action": "cloudantnosqldb.any-document.read", + "X-Couch-Request-ID": "404db768c4", + "X-Frame-Options": "DENY", + "Strict-Transport-Security": "max-age=31536000", + "X-Content-Type-Options": "nosniff", + "X-Cloudant-Request-Class": "unlimited", + "X-Cloudant-Backend": "bm-cc-us-east-05", + "Via": "1.1 lb2.bm-cc-us-east-05 (Glum/1.101.5)", + "Vary": "Accept-Encoding" + } + }, + "uuid": "020940d2-536c-36f7-9bf8-67b3b3fce208" + }, + { + "id": "461288f0-48dd-3060-9313-54cbccd47cad", + "request": { + "urlPath": "/testString/_design/testString", + "method": "PUT", + "queryParameters": { + "batch": { + "equalTo": "ok" + }, + "rev": { + "equalTo": "testString" + }, + "new_edits": { + "equalTo": "false" + } + }, + "bodyPatterns": [ + { + "equalToJson": "{}", + "ignoreArrayOrder": false, + "ignoreExtraElements": true + } + ] + }, + "response": { + "status": 201, + "headers": { + "Cache-Control": "must-revalidate", + "Content-Type": "application/json", + "Date": "Wed, 18 May 2022 21:27:49 GMT", + "ETag": "W/\"3-7144018200809db9cf3c818ad73db361\"", + "Location": "https://84ab8038-d8f1-450c-9645-4d16b448eb31-bluemix.cloudant.com/testString/_design%2FtestString", + "Server": "CouchDB/3.2.1 (Erlang OTP/20)", + "transaction-id": "e124a1c51ad9305dc6b7d0c9b221b4e9", + "X-Cloudant-Action": "cloudantnosqldb.design-document.write", + "X-Couch-Request-ID": "e124a1c51a", + "X-Frame-Options": "DENY", + "Strict-Transport-Security": "max-age=31536000", + "X-Content-Type-Options": "nosniff", + "X-Cloudant-Request-Class": "write", + "X-Cloudant-Backend": "bm-cc-us-east-05", + "Via": "1.1 lb1.bm-cc-us-east-05 (Glum/1.101.5)", + "Vary": "Accept-Encoding" + }, + "jsonBody": { + "ok": true, + "id": "_design/testString", + "rev": "3-7144018200809db9cf3c818ad73db361" + } + }, + "uuid": "461288f0-48dd-3060-9313-54cbccd47cad" + }, + { + "id": "b03a36c9-109f-32af-a3ce-fe7116672291", + "request": { + "url": "/testString/_design/testString", + "method": "HEAD" + }, + "response": { + "status": 200, + "headers": { + "Cache-Control": "must-revalidate", + "Content-Type": "application/json", + "Date": "Wed, 18 May 2022 20:45:03 GMT", + "ETag": "\"1-967a00dff5e02add41819138abb3284d\"", + "Server": "CouchDB/3.2.1 (Erlang OTP/20)", + "transaction-id": "c44b096c253fb87b996ae27d08075657", + "X-Cloudant-Action": "cloudantnosqldb.any-document.read", + "X-Couch-Request-ID": "c44b096c25", + "X-Frame-Options": "DENY", + "Strict-Transport-Security": "max-age=31536000", + "X-Content-Type-Options": "nosniff", + "X-Cloudant-Request-Class": "lookup", + "X-Cloudant-Backend": "bm-cc-us-east-05", + "Via": "1.1 lb2.bm-cc-us-east-05 (Glum/1.101.5)" + } + }, + "uuid": "b03a36c9-109f-32af-a3ce-fe7116672291" + }, + { + "id": "fff55e78-260e-3b17-a8cf-1934e090287e", + "request": { + "urlPath": "/testString/_design/testString", + "method": "GET", + "queryParameters": { + "att_encoding_info": { + "equalTo": "false" + }, + "attachments": { + "equalTo": "false" + }, + "deleted_conflicts": { + "equalTo": "false" + }, + "conflicts": { + "equalTo": "false" + }, + "latest": { + "equalTo": "false" + }, + "local_seq": { + "equalTo": "false" + }, + "meta": { + "equalTo": "false" + }, + "rev": { + "equalTo": "testString" + }, + "revs": { + "equalTo": "false" + }, + "revs_info": { + "equalTo": "false" + } + } + }, + "response": { + "status": 200, + "headers": { + "Cache-Control": "must-revalidate", + "Content-Type": "application/json", + "Date": "Wed, 18 May 2022 21:14:31 GMT", + "ETag": "W/\"1-967a00dff5e02add41819138abb3284d\"", + "Server": "CouchDB/3.2.1 (Erlang OTP/20)", + "transaction-id": "c7fa2320139c35e187a736e478811ae0", + "X-Cloudant-Action": "cloudantnosqldb.any-document.read", + "X-Couch-Request-ID": "c7fa232013", + "X-Frame-Options": "DENY", + "Strict-Transport-Security": "max-age=31536000", + "X-Content-Type-Options": "nosniff", + "X-Cloudant-Request-Class": "lookup", + "X-Cloudant-Backend": "bm-cc-us-east-05", + "Via": "1.1 lb2.bm-cc-us-east-05 (Glum/1.101.5)", + "Vary": "Accept-Encoding" + }, + "jsonBody": { + "_id": "_design/testString", + "_rev": "1-967a00dff5e02add41819138abb3284d" + } + }, + "uuid": "fff55e78-260e-3b17-a8cf-1934e090287e" + }, + { + "id": "720ca71a-295f-3d91-aa44-a7dddf28f915", + "request": { + "url": "/testString/_design/testString/_info", + "method": "GET" + }, + "response": { + "status": 200, + "headers": { + "Cache-Control": "must-revalidate", + "content-security-policy": "sandbox", + "Content-Type": "application/json", + "Date": "Mon, 16 May 2022 21:08:39 GMT", + "Server": "CouchDB/3.2.1 (Erlang OTP/20)", + "transaction-id": "fd54336e518033cf53162cba352a86bb", + "X-Cloudant-Action": "cloudantnosqldb.any-document.read", + "X-Couch-Request-ID": "fd54336e51", + "X-Frame-Options": "DENY", + "Strict-Transport-Security": "max-age=31536000", + "X-Content-Type-Options": "nosniff", + "X-Cloudant-Request-Class": "unlimited", + "X-Cloudant-Backend": "bm-cc-us-east-05", + "Via": "1.1 lb2.bm-cc-us-east-05 (Glum/1.101.5)", + "Vary": "Accept-Encoding" + }, + "jsonBody": { + "name": "testString", + "view_index": { + "updates_pending": { + "minimum": 9, + "preferred": 7, + "total": 9 + }, + "waiting_commit": false, + "waiting_clients": 0, + "updater_running": false, + "update_seq": 0, + "sizes": { + "file": 816, + "external": 0, + "active": 0 + }, + "signature": "3e823c2a4383ac0c18d4e574135a5b08", + "purge_seq": 0, + "language": "javascript", + "compact_running": false, + "collator_versions": [ + "58.0.6.48" + ] + } + } + }, + "uuid": "720ca71a-295f-3d91-aa44-a7dddf28f915" + }, + { + "id": "8e817ede-8647-3ada-ab72-58dfc263fc30", + "request": { + "url": "/testString/_design/testString/_search/testString", + "method": "POST", + "bodyPatterns": [ + { + "equalToJson": "{\"query\": \"name:Jane* AND active:True\"}", + "ignoreArrayOrder": false, + "ignoreExtraElements": true + } + ] + }, + "response": { + "status": 200, + "headers": { + "Cache-Control": "must-revalidate", + "Content-Type": "application/json", + "Date": "Fri, 20 May 2022 15:49:53 GMT", + "Server": "CouchDB/3.2.1 (Erlang OTP/20)", + "transaction-id": "9b54ed06009bf95d8137e21ebc705792", + "X-Cloudant-Action": "cloudantnosqldb.any-document.read", + "X-Couch-Request-ID": "9b54ed0600", + "X-Frame-Options": "DENY", + "Strict-Transport-Security": "max-age=31536000", + "X-Content-Type-Options": "nosniff", + "X-Cloudant-Request-Class": "query", + "X-Cloudant-Backend": "bm-cc-us-east-05", + "Via": "1.1 lb1.bm-cc-us-east-05 (Glum/1.101.7)", + "Vary": "Accept-Encoding" + }, + "jsonBody": { + "total_rows": 0, + "bookmark": "g2o", + "rows": [] + } + }, + "uuid": "8e817ede-8647-3ada-ab72-58dfc263fc30" + }, + { + "id": "bbecd8e4-e759-3d2a-959b-d5e9f94b7dd9", + "request": { + "url": "/testString/_design/testString/_search_disk_size/testString", + "method": "GET" + }, + "response": { + "status": 200, + "headers": { + "X-Frame-Options": "DENY", + "X-Cloudant-Action": "cloudantnosqldb.any-document.read", + "Strict-Transport-Security": "max-age=31536000", + "Server": "Cloudant", + "Cache-Control": "must-revalidate", + "X-Content-Type-Options": "nosniff", + "X-Cloudant-Backend": "bigblue", + "X-Couch-Request-ID": "6b16a74e", + "X-Cloudant-Request-Class": "unlimited", + "Date": "Wed, 08 Jan 2025 17:25:47 GMT", + "Via": "1.1 lb3.bigblue (Glum)", + "Content-Type": "application/json" + }, + "jsonBody": { + "name": "_design/testString/testString", + "search_index": { + "disk_size": 11906 + } + } + }, + "uuid": "bbecd8e4-e759-3d2a-959b-d5e9f94b7dd9" + }, + { + "id": "e6c0db1b-cd2e-379e-b7a6-27ec75049831", + "request": { + "url": "/testString/_design/testString/_search_info/testString", + "method": "GET" + }, + "response": { + "status": 200, + "headers": { + "Cache-Control": "must-revalidate", + "Content-Type": "application/json", + "Date": "Fri, 20 May 2022 15:53:44 GMT", + "Server": "CouchDB/3.2.1 (Erlang OTP/20)", + "transaction-id": "3404cc7ff86fd35a4b20916886f83157", + "X-Cloudant-Action": "cloudantnosqldb.any-document.read", + "X-Couch-Request-ID": "3404cc7ff8", + "X-Frame-Options": "DENY", + "Strict-Transport-Security": "max-age=31536000", + "X-Content-Type-Options": "nosniff", + "X-Cloudant-Request-Class": "unlimited", + "X-Cloudant-Backend": "bm-cc-us-east-05", + "Via": "1.1 lb1.bm-cc-us-east-05 (Glum/1.101.7)", + "Vary": "Accept-Encoding" + }, + "jsonBody": { + "name": "_design/testString/testString", + "search_index": { + "signature": "61f0b78b3e5e995c781fdf4314be424d", + "pending_seq": 2, + "doc_del_count": 0, + "doc_count": 2, + "disk_size": 2919, + "committed_seq": 2 + } + } + }, + "uuid": "e6c0db1b-cd2e-379e-b7a6-27ec75049831" + }, + { + "id": "d3c833ba-4501-3b32-b148-5bd28a803971", + "request": { + "url": "/testString/_design/testString/_view/testString", + "method": "POST", + "bodyPatterns": [ + { + "equalToJson": "{}", + "ignoreArrayOrder": false, + "ignoreExtraElements": true + } + ] + }, + "response": { + "status": 200, + "headers": { + "Cache-Control": "must-revalidate", + "Content-Type": "application/json", + "Date": "Wed, 18 May 2022 21:36:38 GMT", + "Server": "CouchDB/3.2.1 (Erlang OTP/20)", + "transaction-id": "32896eea2831f2fd3e9a395511d1ad9d", + "X-Cloudant-Action": "cloudantnosqldb.any-document.read", + "X-Couch-Request-ID": "32896eea28", + "X-Frame-Options": "DENY", + "Strict-Transport-Security": "max-age=31536000", + "X-Content-Type-Options": "nosniff", + "X-Cloudant-Request-Class": "query", + "X-Cloudant-Backend": "bm-cc-us-east-05", + "Via": "1.1 lb2.bm-cc-us-east-05 (Glum/1.101.5)", + "Vary": "Accept-Encoding" + }, + "jsonBody": { + "total_rows": 1, + "offset": 0, + "rows": [ + { + "id": "testString", + "key": "testString", + "value": 1 + } + ] + } + }, + "uuid": "d3c833ba-4501-3b32-b148-5bd28a803971" + }, + { + "id": "c3486bd0-042f-355e-8981-2660f426fc78", + "request": { + "url": "/testString/_design/testString/_view/testString/queries", + "method": "POST", + "bodyPatterns": [ + { + "equalToJson": "{\"queries\": [{}]}", + "ignoreArrayOrder": false, + "ignoreExtraElements": true + } + ] + }, + "response": { + "status": 200, + "headers": { + "Cache-Control": "must-revalidate", + "Content-Type": "application/json", + "Date": "Thu, 19 May 2022 21:34:23 GMT", + "Server": "CouchDB/3.2.1 (Erlang OTP/20)", + "transaction-id": "3dcd8e06f37f2c9447847b215a34c7bc", + "X-Cloudant-Action": "cloudantnosqldb.any-document.read", + "X-Couch-Request-ID": "3dcd8e06f3", + "X-Frame-Options": "DENY", + "Strict-Transport-Security": "max-age=31536000", + "X-Content-Type-Options": "nosniff", + "X-Cloudant-Request-Class": "query", + "X-Cloudant-Backend": "bm-cc-us-east-05", + "Via": "1.1 lb1.bm-cc-us-east-05 (Glum/1.101.6)", + "Vary": "Accept-Encoding" + }, + "jsonBody": { + "results": [ + { + "total_rows": 1, + "offset": 0, + "rows": [ + { + "id": "testString", + "key": "testString", + "value": 1 + } + ] + } + ] + } + }, + "uuid": "c3486bd0-042f-355e-8981-2660f426fc78" + }, + { + "id": "4620004f-c409-3896-a6a9-8a2dacfa5356", + "request": { + "urlPath": "/testString/_design/testString", + "method": "DELETE", + "queryParameters": { + "batch": { + "equalTo": "ok" + }, + "rev": { + "equalTo": "testString" + } + } + }, + "response": { + "status": 200, + "headers": { + "Cache-Control": "must-revalidate", + "Content-Type": "application/json", + "Date": "Mon, 23 May 2022 20:45:39 GMT", + "ETag": "W/\"4-111f03850e4f2067647dcc724ded6355\"", + "Server": "CouchDB/3.2.1 (Erlang OTP/20)", + "transaction-id": "30bea87f8ef003389fe71c1bdcd9d60c", + "X-Cloudant-Action": "cloudantnosqldb.design-document.write", + "X-Couch-Request-ID": "30bea87f8e", + "X-Frame-Options": "DENY", + "Strict-Transport-Security": "max-age=31536000", + "X-Content-Type-Options": "nosniff", + "X-Cloudant-Request-Class": "write", + "X-Cloudant-Backend": "bm-cc-us-east-05", + "Via": "1.1 lb2.bm-cc-us-east-05 (Glum/1.102.0)", + "Vary": "Accept-Encoding" + }, + "jsonBody": { + "ok": true, + "id": "_design/testString", + "rev": "2-111f03850e4f2067647dcc724ded6355" + } + }, + "uuid": "4620004f-c409-3896-a6a9-8a2dacfa5356" + }, + { + "id": "e0812d78-e939-3d3b-b4d7-c0ec30388f56", + "request": { + "url": "/testString/_design_docs", + "method": "POST", + "bodyPatterns": [ + { + "equalToJson": "{}", + "ignoreArrayOrder": false, + "ignoreExtraElements": true + } + ] + }, + "response": { + "status": 200, + "headers": { + "Cache-Control": "must-revalidate", + "Content-Type": "application/json", + "Date": "Tue, 24 May 2022 20:50:44 GMT", + "Server": "CouchDB/3.2.1 (Erlang OTP/20)", + "transaction-id": "90a04990685e86a438d58f8341c53fbd", + "X-Cloudant-Action": "cloudantnosqldb.any-document.read", + "X-Couch-Request-ID": "90a0499068", + "X-Frame-Options": "DENY", + "Strict-Transport-Security": "max-age=31536000", + "X-Content-Type-Options": "nosniff", + "X-Cloudant-Request-Class": "query", + "X-Cloudant-Backend": "bm-cc-us-east-05", + "Via": "1.1 lb1.bm-cc-us-east-05 (Glum/1.102.0)", + "Vary": "Accept-Encoding" + }, + "jsonBody": { + "total_rows": 1, + "offset": 0, + "rows": [ + { + "id": "_design/testString", + "key": "_design/testString", + "value": { + "rev": "5-be9ac99e964a971214f795dacceba0fe" + } + } + ] + } + }, + "uuid": "e0812d78-e939-3d3b-b4d7-c0ec30388f56" + }, + { + "id": "30c2e356-94cd-3c5a-b5a0-2396b5bc4eaf", + "request": { + "url": "/testString/_design_docs/queries", + "method": "POST", + "bodyPatterns": [ + { + "equalToJson": "{\"queries\": [{}]}", + "ignoreArrayOrder": false, + "ignoreExtraElements": true + } + ] + }, + "response": { + "status": 200, + "headers": { + "Cache-Control": "must-revalidate", + "content-security-policy": "sandbox", + "Content-Type": "application/json", + "Date": "Tue, 24 May 2022 20:53:32 GMT", + "Server": "CouchDB/3.2.1 (Erlang OTP/20)", + "transaction-id": "abf1f943184e48e3667be3cc7bfecd92", + "X-Cloudant-Action": "cloudantnosqldb.any-document.read", + "X-Couch-Request-ID": "abf1f94318", + "X-Frame-Options": "DENY", + "Strict-Transport-Security": "max-age=31536000", + "X-Content-Type-Options": "nosniff", + "X-Cloudant-Request-Class": "query", + "X-Cloudant-Backend": "bm-cc-us-east-05", + "Via": "1.1 lb2.bm-cc-us-east-05 (Glum/1.102.0)", + "Vary": "Accept-Encoding" + }, + "jsonBody": { + "results": [ + { + "total_rows": 1, + "offset": 0, + "rows": [ + { + "id": "_design/testString", + "key": "_design/testString", + "value": { + "rev": "5-be9ac99e964a971214f795dacceba0fe" + } + } + ] + } + ] + } + }, + "uuid": "30c2e356-94cd-3c5a-b5a0-2396b5bc4eaf" + }, + { + "id": "83a8375b-20e0-36b2-ba07-cbc994140f3e", + "request": { + "url": "/testString/_explain", + "method": "POST", + "bodyPatterns": [ + { + "equalToJson": "{\"bookmark\" : \"testString\", \"conflicts\" : true, \"execution_stats\" : true, \"fields\" : [ \"_id\", \"type\", \"name\", \"email\" ], \"limit\" : 3, \"r\" : 1, \"selector\" : { }, \"skip\" : 0, \"sort\" : [ { \"key1\": \"asc\" } ], \"stable\" : true, \"update\" : \"true\", \"use_index\" : [ \"testString\" ]}", + "ignoreArrayOrder": false, + "ignoreExtraElements": true + } + ] + }, + "response": { + "status": 200, + "headers": { + "Cache-Control": "must-revalidate", + "Content-Type": "application/json", + "Date": "Mon, 16 May 2022 21:08:45 GMT", + "Server": "CouchDB/3.2.1 (Erlang OTP/20)", + "transaction-id": "5db5f421aa87d09bf677dab0df8b409b", + "X-Cloudant-Action": "cloudantnosqldb.any-document.read", + "X-Couch-Request-ID": "5db5f421aa", + "X-Frame-Options": "DENY", + "Strict-Transport-Security": "max-age=31536000", + "X-Content-Type-Options": "nosniff", + "X-Cloudant-Request-Class": "query", + "X-Cloudant-Backend": "bm-cc-us-east-05", + "Via": "1.1 lb2.bm-cc-us-east-05 (Glum/1.101.5)", + "Vary": "Accept-Encoding" + }, + "jsonBody": { + "dbname": "account-bluemix/testString", + "index": { + "ddoc": null, + "name": "_all_docs", + "type": "special", + "def": { + "fields": [ + { + "_id": "asc" + } + ] + } + }, + "partitioned": "undefined", + "selector": { + "key1": { + "$eq": "testString" + } + }, + "opts": { + "use_index": [], + "bookmark": "nil", + "limit": 10000000000, + "skip": 0, + "sort": {}, + "fields": [ + "testString" + ], + "partition": "", + "r": 1, + "conflicts": false, + "stale": false, + "update": true, + "stable": false, + "execution_stats": false + }, + "limit": 10000000000, + "skip": 0, + "fields": [ + "testString" + ], + "mrargs": { + "include_docs": true, + "view_type": "map", + "reduce": false, + "partition": null, + "start_key": null, + "end_key": "", + "direction": "fwd", + "stable": false, + "update": true, + "conflicts": "undefined" + } + } + }, + "uuid": "83a8375b-20e0-36b2-ba07-cbc994140f3e" + }, + { + "id": "e63e760d-4bad-31e3-865c-4d4e2c3ab28a", + "request": { + "url": "/testString/_find", + "method": "POST", + "bodyPatterns": [ + { + "equalToJson": "{\"bookmark\" : \"testString\", \"conflicts\" : true, \"execution_stats\" : true, \"fields\" : [ \"_id\", \"type\", \"name\", \"email\" ], \"limit\" : 3, \"r\" : 1, \"selector\" : { }, \"skip\" : 0, \"sort\" : [ { \"key1\": \"asc\" } ], \"stable\" : true, \"update\" : \"true\", \"use_index\" : [ \"testString\" ]}", + "ignoreArrayOrder": false, + "ignoreExtraElements": true + } + ] + }, + "response": { + "status": 200, + "headers": { + "Cache-Control": "must-revalidate", + "Content-Type": "application/json", + "Date": "Mon, 16 May 2022 21:08:44 GMT", + "Server": "CouchDB/3.2.1 (Erlang OTP/20)", + "transaction-id": "131553237ddf5d488049455ff3c78c24", + "X-Cloudant-Action": "cloudantnosqldb.any-document.read", + "X-Couch-Request-ID": "131553237d", + "X-Frame-Options": "DENY", + "Strict-Transport-Security": "max-age=31536000", + "X-Content-Type-Options": "nosniff", + "X-Cloudant-Request-Class": "query", + "X-Cloudant-Backend": "bm-cc-us-east-05", + "Via": "1.1 lb2.bm-cc-us-east-05 (Glum/1.101.5)", + "Vary": "Accept-Encoding" + }, + "jsonBody": { + "docs": [], + "bookmark": "nil", + "warning": "No matching index found, create an index to optimize query time." + } + }, + "uuid": "e63e760d-4bad-31e3-865c-4d4e2c3ab28a" + }, + { + "id": "0aae4e1b-4552-3154-8b80-76bd1e4fe0a4", + "request": { + "urlPattern": "/testString/_index/_design/(.*)/json/(.*)", + "method": "DELETE" + }, + "response": { + "status": 200, + "headers": { + "Cache-Control": "must-revalidate", + "Content-Type": "application/json", + "Date": "Mon, 23 May 2022 20:26:34 GMT", + "Server": "CouchDB/3.2.1 (Erlang OTP/20)", + "transaction-id": "bdcc11a6e2426df69ea7f0dedeb61dc4", + "X-Cloudant-Action": "cloudantnosqldb.design-document.write", + "X-Couch-Request-ID": "bdcc11a6e2", + "X-Frame-Options": "DENY", + "Strict-Transport-Security": "max-age=31536000", + "X-Content-Type-Options": "nosniff", + "X-Cloudant-Request-Class": "query", + "X-Cloudant-Backend": "bm-cc-us-east-05", + "Via": "1.1 lb1.bm-cc-us-east-05 (Glum/1.102.0)", + "Vary": "Accept-Encoding" + }, + "jsonBody": { + "ok": true + } + }, + "uuid": "0aae4e1b-4552-3154-8b80-76bd1e4fe0a4" + }, + { + "id": "8a7e9c07-5c90-37ff-a9ed-4374e25145ea", + "request": { + "url": "/testString/_index", + "method": "GET" + }, + "response": { + "status": 200, + "headers": { + "Cache-Control": "must-revalidate", + "Content-Type": "application/json", + "Date": "Mon, 23 May 2022 20:24:06 GMT", + "Server": "CouchDB/3.2.1 (Erlang OTP/20)", + "transaction-id": "fb5312b548be009f9658ad7343f24efb", + "X-Cloudant-Action": "cloudantnosqldb.any-document.read", + "X-Couch-Request-ID": "fb5312b548", + "X-Frame-Options": "DENY", + "Strict-Transport-Security": "max-age=31536000", + "X-Content-Type-Options": "nosniff", + "X-Cloudant-Request-Class": "query", + "X-Cloudant-Backend": "bm-cc-us-east-05", + "Via": "1.1 lb1.bm-cc-us-east-05 (Glum/1.102.0)", + "Vary": "Accept-Encoding" + }, + "jsonBody": { + "total_rows": 2, + "indexes": [ + { + "ddoc": null, + "name": "_all_docs", + "type": "special", + "def": { + "fields": [ + { + "_id": "asc" + } + ] + } + }, + { + "ddoc": "_design/3fa64f0768edfd1dd7ec862c7415b3fe8138b393", + "name": "3fa64f0768edfd1dd7ec862c7415b3fe8138b393", + "type": "json", + "partitioned": false, + "def": { + "fields": [ + { + "_id": "asc" + } + ] + } + } + ] + } + }, + "uuid": "8a7e9c07-5c90-37ff-a9ed-4374e25145ea" + }, + { + "id": "6026c807-3ad5-3944-a4cc-6a4906aaefd3", + "request": { + "url": "/testString/_index", + "method": "POST", + "bodyPatterns": [ + { + "equalToJson": "{\"index\": {}}", + "ignoreArrayOrder": false, + "ignoreExtraElements": true + } + ] + }, + "response": { + "status": 200, + "headers": { + "Cache-Control": "must-revalidate", + "Content-Type": "application/json", + "Date": "Fri, 20 May 2022 15:42:53 GMT", + "Server": "CouchDB/3.2.1 (Erlang OTP/20)", + "transaction-id": "b4c0baceee481be0eb2174c4a9962c58", + "X-Cloudant-Action": "cloudantnosqldb.design-document.write", + "X-Couch-Request-ID": "b4c0baceee", + "X-Frame-Options": "DENY", + "Strict-Transport-Security": "max-age=31536000", + "X-Content-Type-Options": "nosniff", + "X-Cloudant-Request-Class": "query", + "X-Cloudant-Backend": "bm-cc-us-east-05", + "Via": "1.1 lb2.bm-cc-us-east-05 (Glum/1.101.7)", + "Vary": "Accept-Encoding" + }, + "jsonBody": { + "result": "created", + "id": "_design/3fa64f0768edfd1dd7ec862c7415b3fe8138b393", + "name": "3fa64f0768edfd1dd7ec862c7415b3fe8138b393" + } + }, + "uuid": "6026c807-3ad5-3944-a4cc-6a4906aaefd3" + }, + { + "id": "c8bd56cb-8f5b-3521-80a9-75231b5a5069", + "request": { + "urlPath": "/testString/_local/testString", + "method": "PUT", + "queryParameters": { + "batch": { + "equalTo": "ok" + } + }, + "bodyPatterns": [ + { + "equalToJson": "{}", + "ignoreArrayOrder": false, + "ignoreExtraElements": true + } + ] + }, + "response": { + "status": 201, + "headers": { + "Cache-Control": "must-revalidate", + "Content-Type": "application/json", + "Date": "Fri, 20 May 2022 18:32:13 GMT", + "ETag": "W/\"5ZVXQYO7VLEOU0TL9VXDNP5PV\"", + "Location": "https://84ab8038-d8f1-450c-9645-4d16b448eb31-bluemix.cloudant.com/testString/_local%2FtestString", + "Server": "CouchDB/3.2.1 (Erlang OTP/20)", + "transaction-id": "6cb9599b2c12bc8ca784be001ad71f92", + "X-Cloudant-Action": "cloudantnosqldb.local-document.write", + "X-Couch-Request-ID": "6cb9599b2c", + "X-Frame-Options": "DENY", + "Strict-Transport-Security": "max-age=31536000", + "X-Content-Type-Options": "nosniff", + "X-Cloudant-Request-Class": "write", + "X-Cloudant-Backend": "bm-cc-us-east-05", + "Via": "1.1 lb2.bm-cc-us-east-05 (Glum/1.102.0)", + "Vary": "Accept-Encoding" + }, + "jsonBody": { + "ok": true, + "id": "_local/testString", + "rev": "0-1" + } + }, + "uuid": "c8bd56cb-8f5b-3521-80a9-75231b5a5069" + }, + { + "id": "e2fa7386-36e2-36f0-ad24-0b3e7ebd3585", + "request": { + "url": "/testString/_local/testString", + "method": "HEAD" + }, + "response": { + "status": 200, + "headers": { + "Cache-Control": "must-revalidate", + "Content-Type": "application/json", + "Date": "Fri, 20 May 2022 18:34:22 GMT", + "ETag": "\"5ZVXQYO7VLEOU0TL9VXDNP5PV\"", + "Server": "CouchDB/3.2.1 (Erlang OTP/20)", + "transaction-id": "0acbffcb1d8029d190789dddd3521397", + "X-Cloudant-Action": "cloudantnosqldb.any-document.read", + "X-Couch-Request-ID": "0acbffcb1d", + "X-Frame-Options": "DENY", + "Strict-Transport-Security": "max-age=31536000", + "X-Content-Type-Options": "nosniff", + "X-Cloudant-Request-Class": "lookup", + "X-Cloudant-Backend": "bm-cc-us-east-05", + "Via": "1.1 lb2.bm-cc-us-east-05 (Glum/1.102.0)" + } + }, + "uuid": "e2fa7386-36e2-36f0-ad24-0b3e7ebd3585" + }, + { + "id": "825527a5-47ec-355d-b00a-0877fbb0a29e", + "request": { + "urlPath": "/testString/_local/testString", + "method": "DELETE", + "queryParameters": { + "batch": { + "equalTo": "ok" + } + } + }, + "response": { + "status": 200, + "headers": { + "Cache-Control": "must-revalidate", + "Content-Type": "application/json", + "Date": "Mon, 16 May 2022 21:09:08 GMT", + "ETag": "W/\"DKGUB9HWEX2KQNSCSMV0JG1KK\"", + "Server": "CouchDB/3.2.1 (Erlang OTP/20)", + "transaction-id": "758b33bb54b870dde75c57382a6bc1b6", + "X-Cloudant-Action": "cloudantnosqldb.local-document.write", + "X-Couch-Request-ID": "758b33bb54", + "X-Frame-Options": "DENY", + "Strict-Transport-Security": "max-age=31536000", + "X-Content-Type-Options": "nosniff", + "X-Cloudant-Request-Class": "write", + "X-Cloudant-Backend": "bm-cc-us-east-05", + "Via": "1.1 lb1.bm-cc-us-east-05 (Glum/1.101.5)", + "Vary": "Accept-Encoding" + }, + "jsonBody": { + "ok": true, + "id": "_local/testString", + "rev": "0-0" + } + }, + "uuid": "825527a5-47ec-355d-b00a-0877fbb0a29e" + }, + { + "id": "a169db5c-1dd0-3bd6-a373-5422ecfd1509", + "request": { + "urlPath": "/testString/_local/testString", + "method": "GET", + "queryParameters": { + "att_encoding_info": { + "equalTo": "false" + }, + "attachments": { + "equalTo": "false" + }, + "local_seq": { + "equalTo": "false" + } + } + }, + "response": { + "status": 200, + "headers": { + "Cache-Control": "must-revalidate", + "Content-Type": "application/json", + "Date": "Fri, 20 May 2022 18:34:22 GMT", + "ETag": "W/\"5ZVXQYO7VLEOU0TL9VXDNP5PV\"", + "Server": "CouchDB/3.2.1 (Erlang OTP/20)", + "transaction-id": "eb9e1ce6c4a1d44b41dea28aa5136d02", + "X-Cloudant-Action": "cloudantnosqldb.any-document.read", + "X-Couch-Request-ID": "eb9e1ce6c4", + "X-Frame-Options": "DENY", + "Strict-Transport-Security": "max-age=31536000", + "X-Content-Type-Options": "nosniff", + "X-Cloudant-Request-Class": "lookup", + "X-Cloudant-Backend": "bm-cc-us-east-05", + "Via": "1.1 lb2.bm-cc-us-east-05 (Glum/1.102.0)", + "Vary": "Accept-Encoding" + }, + "jsonBody": { + "_id": "_local/testString", + "_rev": "0-1" + } + }, + "uuid": "a169db5c-1dd0-3bd6-a373-5422ecfd1509" + }, + { + "id": "af6271d1-4985-3dc8-9662-501d6a81d30d", + "request": { + "url": "/testString/_partition/testString", + "method": "GET" + }, + "response": { + "status": 200, + "headers": { + "Cache-Control": "must-revalidate", + "content-security-policy": "sandbox", + "Content-Type": "application/json", + "Date": "Thu, 19 May 2022 22:19:26 GMT", + "Server": "CouchDB/3.2.1 (Erlang OTP/20)", + "transaction-id": "9a31e19b4ed0ae92c12fdae6b2cfd5da", + "X-Cloudant-Action": "cloudantnosqldb.any-document.read", + "X-Couch-Request-ID": "9a31e19b4e", + "X-Frame-Options": "DENY", + "Strict-Transport-Security": "max-age=31536000", + "X-Content-Type-Options": "nosniff", + "X-Cloudant-Request-Class": "unlimited", + "X-Cloudant-Backend": "bm-cc-us-east-05", + "Via": "1.1 lb1.bm-cc-us-east-05 (Glum/1.101.6)", + "Vary": "Accept-Encoding" + }, + "jsonBody": { + "db_name": "account-bluemix/testString", + "sizes": { + "active": 39, + "external": 2 + }, + "partition": "testString", + "doc_count": 1, + "doc_del_count": 0 + } + }, + "uuid": "af6271d1-4985-3dc8-9662-501d6a81d30d" + }, + { + "id": "2c580fc6-7377-38e3-8356-c7f2a36cad84", + "request": { + "url": "/testString/_partition/testString/_all_docs", + "method": "POST", + "bodyPatterns": [ + { + "equalToJson": "{}", + "ignoreArrayOrder": false, + "ignoreExtraElements": true + } + ] + }, + "response": { + "status": 200, + "headers": { + "Cache-Control": "must-revalidate", + "Content-Type": "application/json", + "Date": "Thu, 19 May 2022 22:20:22 GMT", + "Server": "CouchDB/3.2.1 (Erlang OTP/20)", + "transaction-id": "3a686f700406d04e7fb1ace02dc471bc", + "X-Cloudant-Action": "cloudantnosqldb.any-document.read", + "X-Couch-Request-ID": "3a686f7004", + "X-Frame-Options": "DENY", + "Strict-Transport-Security": "max-age=31536000", + "X-Content-Type-Options": "nosniff", + "X-Cloudant-Request-Class": "lookup", + "X-Cloudant-Backend": "bm-cc-us-east-05", + "Via": "1.1 lb1.bm-cc-us-east-05 (Glum/1.101.6)", + "Vary": "Accept-Encoding" + }, + "jsonBody": { + "total_rows": 1, + "offset": 0, + "rows": [ + { + "id": "testString:testString", + "key": "testString:testString", + "value": { + "rev": "1-967a00dff5e02add41819138abb3284d" + } + } + ] + } + }, + "uuid": "2c580fc6-7377-38e3-8356-c7f2a36cad84" + }, + { + "id": "7d36c625-d941-34ba-99bb-519bd6ca923f", + "request": { + "url": "/testString/_partition/testString/_design/testString/_search/testString", + "method": "POST", + "bodyPatterns": [ + { + "equalToJson": "{\"query\": \"name:Jane* AND active:True\"}", + "ignoreArrayOrder": false, + "ignoreExtraElements": true + } + ] + }, + "response": { + "status": 200, + "headers": { + "Cache-Control": "must-revalidate", + "Content-Type": "application/json", + "Date": "Thu, 19 May 2022 22:15:39 GMT", + "Server": "CouchDB/3.2.1 (Erlang OTP/20)", + "transaction-id": "17165e96850d9d0e13b1278163366b94", + "X-Cloudant-Action": "cloudantnosqldb.any-document.read", + "X-Couch-Request-ID": "17165e9685", + "X-Frame-Options": "DENY", + "Strict-Transport-Security": "max-age=31536000", + "X-Content-Type-Options": "nosniff", + "X-Cloudant-Request-Class": "lookup", + "X-Cloudant-Backend": "bm-cc-us-east-05", + "Via": "1.1 lb2.bm-cc-us-east-05 (Glum/1.101.6)", + "Vary": "Accept-Encoding" + }, + "jsonBody": { + "total_rows": 0, + "bookmark": "g2o", + "rows": [] + } + }, + "uuid": "7d36c625-d941-34ba-99bb-519bd6ca923f" + }, + { + "id": "ecaf4992-8865-3623-86d5-9181c46ed721", + "request": { + "url": "/testString/_partition/testString/_design/testString/_view/testString", + "method": "POST", + "bodyPatterns": [ + { + "equalToJson": "{}", + "ignoreArrayOrder": false, + "ignoreExtraElements": true + } + ] + }, + "response": { + "status": 200, + "headers": { + "Cache-Control": "must-revalidate", + "Content-Type": "application/json", + "Date": "Thu, 19 May 2022 22:23:18 GMT", + "Server": "CouchDB/3.2.1 (Erlang OTP/20)", + "transaction-id": "690674ef064cae7a0cd532056e85622c", + "X-Cloudant-Action": "cloudantnosqldb.any-document.read", + "X-Couch-Request-ID": "690674ef06", + "X-Frame-Options": "DENY", + "Strict-Transport-Security": "max-age=31536000", + "X-Content-Type-Options": "nosniff", + "X-Cloudant-Request-Class": "lookup", + "X-Cloudant-Backend": "bm-cc-us-east-05", + "Via": "1.1 lb1.bm-cc-us-east-05 (Glum/1.101.6)", + "Vary": "Accept-Encoding" + }, + "jsonBody": { + "total_rows": 1, + "offset": 0, + "rows": [ + { + "id": "testString:testString", + "key": "testString:testString", + "value": 1 + } + ] + } + }, + "uuid": "ecaf4992-8865-3623-86d5-9181c46ed721" + }, + { + "id": "ca84624c-4bda-11ee-be56-0242ac120002", + "request": { + "url": "/testString/_partition/testString/_explain", + "method": "POST", + "bodyPatterns": [ + { + "equalToJson": "{\"bookmark\": \"testString\",\"conflicts\" : true, \"execution_stats\" : true, \"fields\" : [ \"productId\", \"name\", \"description\" ], \"limit\" : 25, \"selector\" : { }, \"skip\" : 0, \"sort\" : [ { \"key1\": \"asc\" } ], \"stable\" : true, \"update\" : \"true\", \"use_index\" : [ \"testString\" ]}", + "ignoreArrayOrder": false, + "ignoreExtraElements": true + } + ] + }, + "response": { + "status": 200, + "headers": { + "Cache-Control": "must-revalidate", + "Content-Type": "application/json", + "Date": "Tue, 05 Sep 2023 12:10:54 GMT", + "Server": "CouchDB/3.3.2 (Erlang OTP/24)", + "transaction-id": "5ebc08e24c058f8a9d9793c32c226bf3", + "X-Cloudant-Action": "cloudantnosqldb.any-document.read", + "X-Couch-Request-ID": "5baeb05f80", + "X-Frame-Options": "DENY", + "Strict-Transport-Security": "max-age=31536000", + "X-Content-Type-Options": "nosniff", + "X-Cloudant-Request-Class": "lookup", + "X-Cloudant-Backend": "bm-cc-us-east-05", + "Via": "1.1 lb2.bm-cc-us-east-05 (Glum/1.101.6)", + "Vary": "Accept-Encoding" + }, + "jsonBody": { + "dbname": "account-bluemix/testString", + "index": { + "ddoc": null, + "name": "_all_docs", + "type": "special", + "def": { + "fields": [ + { + "_id": "asc" + } + ] + } + }, + "partitioned": "undefined", + "selector": { + "key1": { + "$eq": "testString" + } + }, + "opts": { + "use_index": [], + "bookmark": "nil", + "limit": 10000000000, + "skip": 0, + "sort": {}, + "fields": [ + "testString" + ], + "partition": "testString", + "r": 1, + "conflicts": false, + "stale": false, + "update": true, + "stable": false, + "execution_stats": false + }, + "limit": 10000000000, + "skip": 0, + "fields": [ + "testString" + ], + "mrargs": { + "include_docs": true, + "view_type": "map", + "reduce": false, + "partition": "testString", + "start_key": null, + "end_key": "", + "direction": "fwd", + "stable": false, + "update": true, + "conflicts": "undefined" + } + } + }, + "uuid": "ca84624c-4bda-11ee-be56-0242ac120002" + }, + { + "id": "337c4338-3094-3c88-a44e-981e449b1d7a", + "request": { + "url": "/testString/_partition/testString/_find", + "method": "POST", + "bodyPatterns": [ + { + "equalToJson": "{\"bookmark\" : \"testString\",\"conflicts\" : true, \"execution_stats\" : true, \"fields\" : [ \"productId\", \"name\", \"description\" ], \"limit\" : 25, \"selector\" : { }, \"skip\" : 0, \"sort\" : [ { \"key1\": \"asc\" } ], \"stable\" : true, \"update\" : \"true\", \"use_index\" : [ \"testString\" ]}", + "ignoreArrayOrder": false, + "ignoreExtraElements": true + } + ] + }, + "response": { + "status": 200, + "headers": { + "Cache-Control": "must-revalidate", + "Content-Type": "application/json", + "Date": "Thu, 19 May 2022 22:26:19 GMT", + "Server": "CouchDB/3.2.1 (Erlang OTP/20)", + "transaction-id": "5ebc08e24c058f8a9d9793c32c226bf3", + "X-Cloudant-Action": "cloudantnosqldb.any-document.read", + "X-Couch-Request-ID": "5ebc08e24c", + "X-Frame-Options": "DENY", + "Strict-Transport-Security": "max-age=31536000", + "X-Content-Type-Options": "nosniff", + "X-Cloudant-Request-Class": "lookup", + "X-Cloudant-Backend": "bm-cc-us-east-05", + "Via": "1.1 lb2.bm-cc-us-east-05 (Glum/1.101.6)", + "Vary": "Accept-Encoding" + }, + "jsonBody": { + "docs": [], + "bookmark": "nil", + "warning": "No matching index found, create an index to optimize query time." + } + }, + "uuid": "337c4338-3094-3c88-a44e-981e449b1d7a" + }, + { + "id": "acadaa2a-bebd-3daf-987b-a8eacc2227e0", + "request": { + "url": "/testString/_revs_diff", + "method": "POST", + "bodyPatterns": [ + { + "matchesJsonPath": "$.[?(@ == null || @.key1 != null)]", + "ignoreArrayOrder": false, + "ignoreExtraElements": true + } + ] + }, + "response": { + "status": 200, + "headers": { + "Cache-Control": "must-revalidate", + "Content-Type": "application/json", + "Date": "Fri, 20 May 2022 18:40:13 GMT", + "Server": "CouchDB/3.2.1 (Erlang OTP/20)", + "transaction-id": "295ee11482f22ffa973e81daf2613898", + "X-Cloudant-Action": "cloudantnosqldb.any-document.read", + "X-Couch-Request-ID": "295ee11482", + "X-Frame-Options": "DENY", + "Strict-Transport-Security": "max-age=31536000", + "X-Content-Type-Options": "nosniff", + "X-Cloudant-Request-Class": "unlimited", + "X-Cloudant-Backend": "bm-cc-us-east-05", + "Via": "1.1 lb1.bm-cc-us-east-05 (Glum/1.102.0)", + "Vary": "Accept-Encoding" + }, + "jsonBody": { + "key1": { + "missing": [ + "1-testString" + ] + } + } + }, + "uuid": "acadaa2a-bebd-3daf-987b-a8eacc2227e0" + }, + { + "id": "60056468-bcb5-3b3f-9815-bf4c95c623b4", + "request": { + "url": "/testString/_security", + "method": "GET" + }, + "response": { + "status": 200, + "headers": { + "Cache-Control": "must-revalidate", + "Content-Type": "application/json", + "Date": "Mon, 16 May 2022 21:08:51 GMT", + "Server": "CouchDB/3.2.1 (Erlang OTP/20)", + "transaction-id": "5d8b354c95486f5a0f06200096ca5612", + "X-Cloudant-Action": "cloudantnosqldb.database-security.read", + "X-Couch-Request-ID": "5d8b354c95", + "X-Frame-Options": "DENY", + "Strict-Transport-Security": "max-age=31536000", + "X-Content-Type-Options": "nosniff", + "X-Cloudant-Request-Class": "lookup", + "X-Cloudant-Backend": "bm-cc-us-east-05", + "Via": "1.1 lb2.bm-cc-us-east-05 (Glum/1.101.5)", + "Vary": "Accept-Encoding" + }, + "jsonBody": {} + }, + "uuid": "60056468-bcb5-3b3f-9815-bf4c95c623b4" + }, + { + "id": "28ac8e52-0af2-37e5-b22e-558da950be34", + "request": { + "url": "/testString/_security", + "method": "PUT", + "bodyPatterns": [ + { + "equalToJson": "{}", + "ignoreArrayOrder": false, + "ignoreExtraElements": true + } + ] + }, + "response": { + "status": 200, + "headers": { + "Cache-Control": "must-revalidate", + "Content-Type": "application/json", + "Date": "Mon, 16 May 2022 21:08:51 GMT", + "Server": "CouchDB/3.2.1 (Erlang OTP/20)", + "transaction-id": "a3973a0f6fe6cba29ecb9647fae801f3", + "X-Cloudant-Action": "cloudantnosqldb.database-security.write", + "X-Couch-Request-ID": "a3973a0f6f", + "X-Frame-Options": "DENY", + "Strict-Transport-Security": "max-age=31536000", + "X-Content-Type-Options": "nosniff", + "X-Cloudant-Request-Class": "write", + "X-Cloudant-Backend": "bm-cc-us-east-05", + "Via": "1.1 lb2.bm-cc-us-east-05 (Glum/1.101.5)", + "Vary": "Accept-Encoding" + }, + "jsonBody": { + "ok": true + } + }, + "uuid": "28ac8e52-0af2-37e5-b22e-558da950be34" + }, + { + "id": "b8c0e06d-17c2-3810-9377-7c219ebe3dd9", + "request": { + "url": "/testString/_shards", + "method": "GET" + }, + "response": { + "status": 200, + "headers": { + "Cache-Control": "must-revalidate", + "Content-Type": "application/json", + "Date": "Mon, 16 May 2022 21:08:59 GMT", + "Server": "CouchDB/3.2.1 (Erlang OTP/20)", + "transaction-id": "c7a84aa69921ef4072d42ec31b0d3db3", + "X-Cloudant-Action": "cloudantnosqldb.database-shards.read", + "X-Frame-Options": "DENY", + "Strict-Transport-Security": "max-age=31536000", + "X-Content-Type-Options": "nosniff", + "X-Cloudant-Request-Class": "unlimited", + "X-Cloudant-Backend": "bm-cc-us-east-05", + "Via": "1.1 lb2.bm-cc-us-east-05 (Glum/1.101.5)", + "Vary": "Accept-Encoding" + }, + "jsonBody": { + "shards": { + "00000000-0fffffff": [ + "dbcore@db1.bm-cc-us-east-05.cloudant.net", + "dbcore@db10.bm-cc-us-east-05.cloudant.net", + "dbcore@db12.bm-cc-us-east-05.cloudant.net" + ], + "10000000-1fffffff": [ + "dbcore@db7.bm-cc-us-east-05.cloudant.net", + "dbcore@db8.bm-cc-us-east-05.cloudant.net", + "dbcore@db9.bm-cc-us-east-05.cloudant.net" + ], + "20000000-2fffffff": [ + "dbcore@db4.bm-cc-us-east-05.cloudant.net", + "dbcore@db5.bm-cc-us-east-05.cloudant.net", + "dbcore@db6.bm-cc-us-east-05.cloudant.net" + ], + "30000000-3fffffff": [ + "dbcore@db11.bm-cc-us-east-05.cloudant.net", + "dbcore@db2.bm-cc-us-east-05.cloudant.net", + "dbcore@db3.bm-cc-us-east-05.cloudant.net" + ], + "40000000-4fffffff": [ + "dbcore@db1.bm-cc-us-east-05.cloudant.net", + "dbcore@db10.bm-cc-us-east-05.cloudant.net", + "dbcore@db12.bm-cc-us-east-05.cloudant.net" + ], + "50000000-5fffffff": [ + "dbcore@db7.bm-cc-us-east-05.cloudant.net", + "dbcore@db8.bm-cc-us-east-05.cloudant.net", + "dbcore@db9.bm-cc-us-east-05.cloudant.net" + ], + "60000000-6fffffff": [ + "dbcore@db4.bm-cc-us-east-05.cloudant.net", + "dbcore@db5.bm-cc-us-east-05.cloudant.net", + "dbcore@db6.bm-cc-us-east-05.cloudant.net" + ], + "70000000-7fffffff": [ + "dbcore@db11.bm-cc-us-east-05.cloudant.net", + "dbcore@db2.bm-cc-us-east-05.cloudant.net", + "dbcore@db3.bm-cc-us-east-05.cloudant.net" + ], + "80000000-8fffffff": [ + "dbcore@db1.bm-cc-us-east-05.cloudant.net", + "dbcore@db10.bm-cc-us-east-05.cloudant.net", + "dbcore@db12.bm-cc-us-east-05.cloudant.net" + ], + "90000000-9fffffff": [ + "dbcore@db7.bm-cc-us-east-05.cloudant.net", + "dbcore@db8.bm-cc-us-east-05.cloudant.net", + "dbcore@db9.bm-cc-us-east-05.cloudant.net" + ], + "a0000000-afffffff": [ + "dbcore@db4.bm-cc-us-east-05.cloudant.net", + "dbcore@db5.bm-cc-us-east-05.cloudant.net", + "dbcore@db6.bm-cc-us-east-05.cloudant.net" + ], + "b0000000-bfffffff": [ + "dbcore@db11.bm-cc-us-east-05.cloudant.net", + "dbcore@db2.bm-cc-us-east-05.cloudant.net", + "dbcore@db3.bm-cc-us-east-05.cloudant.net" + ], + "c0000000-cfffffff": [ + "dbcore@db1.bm-cc-us-east-05.cloudant.net", + "dbcore@db10.bm-cc-us-east-05.cloudant.net", + "dbcore@db12.bm-cc-us-east-05.cloudant.net" + ], + "d0000000-dfffffff": [ + "dbcore@db7.bm-cc-us-east-05.cloudant.net", + "dbcore@db8.bm-cc-us-east-05.cloudant.net", + "dbcore@db9.bm-cc-us-east-05.cloudant.net" + ], + "e0000000-efffffff": [ + "dbcore@db4.bm-cc-us-east-05.cloudant.net", + "dbcore@db5.bm-cc-us-east-05.cloudant.net", + "dbcore@db6.bm-cc-us-east-05.cloudant.net" + ], + "f0000000-ffffffff": [ + "dbcore@db11.bm-cc-us-east-05.cloudant.net", + "dbcore@db2.bm-cc-us-east-05.cloudant.net", + "dbcore@db3.bm-cc-us-east-05.cloudant.net" + ] + } + } + }, + "uuid": "b8c0e06d-17c2-3810-9377-7c219ebe3dd9" + }, + { + "id": "d20bae36-5d48-3bf4-b14d-e954b7869d55", + "request": { + "url": "/testString/_shards/testString", + "method": "GET" + }, + "response": { + "status": 200, + "headers": { + "Cache-Control": "must-revalidate", + "content-security-policy": "sandbox", + "Content-Type": "application/json", + "Date": "Mon, 16 May 2022 21:08:59 GMT", + "Server": "CouchDB/3.2.1 (Erlang OTP/20)", + "transaction-id": "b2f00c238f9bd4ffa701399e34d9bf54", + "X-Cloudant-Action": "cloudantnosqldb.database-shards.read", + "X-Frame-Options": "DENY", + "Strict-Transport-Security": "max-age=31536000", + "X-Content-Type-Options": "nosniff", + "X-Cloudant-Request-Class": "unlimited", + "X-Cloudant-Backend": "bm-cc-us-east-05", + "Via": "1.1 lb2.bm-cc-us-east-05 (Glum/1.101.5)", + "Vary": "Accept-Encoding" + }, + "jsonBody": { + "range": "10000000-1fffffff", + "nodes": [ + "dbcore@db7.bm-cc-us-east-05.cloudant.net", + "dbcore@db8.bm-cc-us-east-05.cloudant.net", + "dbcore@db9.bm-cc-us-east-05.cloudant.net" + ] + } + }, + "uuid": "d20bae36-5d48-3bf4-b14d-e954b7869d55" + }, + { + "id": "d317807f-250c-3abd-804d-34e98eebdad8", + "request": { + "urlPath": "/testString", + "method": "POST", + "queryParameters": { + "batch": { + "equalTo": "ok" + } + } + }, + "response": { + "status": 201, + "headers": { + "Cache-Control": "must-revalidate", + "Content-Type": "application/json", + "Date": "Wed, 18 May 2022 17:07:57 GMT", + "Location": "https://ACCOUNT.cloudant.com/testString/a27fe29f38cf3cf647d8575585706955", + "Server": "CouchDB/3.2.1 (Erlang OTP/20)", + "transaction-id": "efbd92438c668c8d821a454cf4e27223", + "X-Cloudant-Action": "cloudantnosqldb.data-document.write", + "X-Couch-Request-ID": "efbd92438c", + "X-Frame-Options": "DENY", + "Strict-Transport-Security": "max-age=31536000", + "X-Content-Type-Options": "nosniff", + "X-Cloudant-Request-Class": "write", + "X-Cloudant-Backend": "bm-cc-us-east-05", + "Via": "1.1 lb1.bm-cc-us-east-05 (Glum/1.101.5)", + "Vary": "Accept-Encoding" + }, + "jsonBody": { + "ok": true, + "id": "a27fe29f38cf3cf647d8575585706955", + "rev": "1-967a00dff5e02add41819138abb3284d" + } + }, + "uuid": "d317807f-250c-3abd-804d-34e98eebdad8" + }, + { + "id": "a2bd99a8-d38d-3c23-8e98-40d6cd029ff1", + "request": { + "url": "/testString", + "method": "DELETE" + }, + "response": { + "status": 200, + "headers": { + "Cache-Control": "must-revalidate", + "Content-Type": "application/json", + "Date": "Mon, 16 May 2022 21:09:10 GMT", + "Server": "CouchDB/3.2.1 (Erlang OTP/20)", + "transaction-id": "992526e30a13656ec4e93001efa2eb7c", + "X-Cloudant-Action": "cloudantnosqldb.database.delete", + "X-Couch-Request-ID": "992526e30a", + "X-Frame-Options": "DENY", + "Strict-Transport-Security": "max-age=31536000", + "X-Content-Type-Options": "nosniff", + "X-Cloudant-Request-Class": "write", + "X-Cloudant-Backend": "bm-cc-us-east-05", + "Via": "1.1 lb1.bm-cc-us-east-05 (Glum/1.101.5)", + "Vary": "Accept-Encoding" + }, + "jsonBody": { + "ok": true + } + }, + "uuid": "a2bd99a8-d38d-3c23-8e98-40d6cd029ff1" + }, + { + "id": "660a0142-29dd-3918-a4aa-f77527e358c2", + "request": { + "url": "/testString", + "method": "GET" + }, + "response": { + "status": 200, + "headers": { + "Cache-Control": "must-revalidate", + "Content-Type": "application/json", + "Date": "Mon, 16 May 2022 21:08:34 GMT", + "Server": "CouchDB/3.2.1 (Erlang OTP/20)", + "transaction-id": "09ea19385a69e38e5ee30e9e87cb6062", + "X-Cloudant-Action": "cloudantnosqldb.database-info.read", + "X-Couch-Request-ID": "09ea19385a", + "X-Frame-Options": "DENY", + "Strict-Transport-Security": "max-age=31536000", + "X-Content-Type-Options": "nosniff", + "X-Cloudant-Request-Class": "unlimited", + "X-Cloudant-Backend": "bm-cc-us-east-05", + "Via": "1.1 lb2.bm-cc-us-east-05 (Glum/1.101.5)", + "Vary": "Accept-Encoding" + }, + "jsonBody": { + "update_seq": "5-g1AAAAereJy91M1NwzAYgGFDkTjSDeAIhwTHSfxzohvABuDPdlRVbYNoeoYNYAPYADaADWAD2AA2ABtXpL4lSG4OiRQl36PkjTNFCA3HA40ONaj6yow0kBRmiVLJcpEYuWgSXKZqWi-1nDfp3DRTe8f2fMfu0Zc9fNttMh5INLMndnNpqNJlj3GhTTvbH97-bG3FqTYCeowLbdHZfvP2e2sTJQpamB7jnH30d3HW_aW_ePzV4Vu_eCkKbSDvM--_xZ88_ryG84xkQm6g-IO3H1s7k7SCymyg-J2379vimrG84HoTxW88frv2qbOKCFLFLQ6Xdg_Xq_W9qo1pmQsetTZcOLcO_itG2qWNRdTScObc8_B_VjJDWOR1DSMHnwaw5kaBgsiFsYNPgsKEcsI1i1v4wLnHwQODYqWpSNzCQ-fuBy7DojBExS0sEewFakGxwO4tdx80-QEwjmuB", + "instance_start_time": "1637834313", + "db_name": "testdb", + "purge_seq": 0, + "sizes": { + "file": 240670, + "external": 79, + "active": 7240 + }, + "props": {}, + "doc_del_count": 0, + "doc_count": 5, + "disk_format_version": 8, + "compact_running": false, + "cluster": { + "q": 16, + "n": 3, + "w": 2, + "r": 2 + } + } + }, + "uuid": "660a0142-29dd-3918-a4aa-f77527e358c2" + }, + { + "id": "939b8665-cf55-3a05-a46d-c208bcc4ada6", + "request": { + "urlPath": "/testString", + "method": "PUT", + "queryParameters": { + "partitioned": { + "equalTo": "false" + }, + "q": { + "equalTo": "16" + } + } + }, + "response": { + "status": 201, + "headers": { + "Cache-Control": "must-revalidate", + "Content-Type": "application/json", + "Date": "Wed, 18 May 2022 16:56:10 GMT", + "Location": "https://account-bluemix.cloudant.com/testString", + "Server": "CouchDB/3.2.1 (Erlang OTP/20)", + "transaction-id": "06ec2ee90406a602224231b8ab7d19aa", + "X-Cloudant-Action": "cloudantnosqldb.database.create", + "X-Couch-Request-ID": "06ec2ee904", + "X-Frame-Options": "DENY", + "Strict-Transport-Security": "max-age=31536000", + "X-Content-Type-Options": "nosniff", + "X-Cloudant-Request-Class": "write", + "X-Cloudant-Backend": "bm-cc-us-east-05", + "Via": "1.1 lb2.bm-cc-us-east-05 (Glum/1.101.5)", + "Vary": "Accept-Encoding" + }, + "jsonBody": { + "ok": true + } + }, + "uuid": "939b8665-cf55-3a05-a46d-c208bcc4ada6" + }, + { + "id": "33f0d8ac-56f3-3a18-9366-761dcb30158f", + "request": { + "urlPath": "/testString/testString", + "method": "DELETE", + "queryParameters": { + "batch": { + "equalTo": "ok" + }, + "rev": { + "equalTo": "testString" + } + } + }, + "response": { + "status": 200, + "headers": { + "Cache-Control": "must-revalidate", + "Content-Type": "application/json", + "Date": "Mon, 23 May 2022 20:31:45 GMT", + "ETag": "W/\"2-49754e12a5d2cbe03c4118bad6709779\"", + "Server": "CouchDB/3.2.1 (Erlang OTP/20)", + "transaction-id": "6beab9bf70a274822c5bcf7dee330078", + "X-Cloudant-Action": "cloudantnosqldb.data-document.write", + "X-Couch-Request-ID": "6beab9bf70", + "X-Frame-Options": "DENY", + "Strict-Transport-Security": "max-age=31536000", + "X-Content-Type-Options": "nosniff", + "X-Cloudant-Request-Class": "write", + "X-Cloudant-Backend": "bm-cc-us-east-05", + "Via": "1.1 lb2.bm-cc-us-east-05 (Glum/1.102.0)", + "Vary": "Accept-Encoding" + }, + "jsonBody": { + "ok": true, + "id": "testString", + "rev": "2-49754e12a5d2cbe03c4118bad6709779" + } + }, + "uuid": "33f0d8ac-56f3-3a18-9366-761dcb30158f" + }, + { + "id": "ae2f6256-c33c-3e1a-9ed6-d16762069a0c", + "request": { + "urlPath": "/testString/testString", + "method": "GET", + "queryParameters": { + "att_encoding_info": { + "equalTo": "false" + }, + "attachments": { + "equalTo": "false" + }, + "local_seq": { + "equalTo": "false" + }, + "deleted_conflicts": { + "equalTo": "false" + }, + "conflicts": { + "equalTo": "false" + }, + "latest": { + "equalTo": "false" + }, + "meta": { + "equalTo": "false" + }, + "rev": { + "equalTo": "testString" + }, + "revs": { + "equalTo": "false" + }, + "revs_info": { + "equalTo": "false" + } + } + }, + "response": { + "status": 200, + "headers": { + "Cache-Control": "must-revalidate", + "Content-Type": "application/json", + "Date": "Wed, 18 May 2022 17:09:08 GMT", + "ETag": "W/\"3-5570e8bbb34412db757c2df4bfa1099b\"", + "Server": "CouchDB/3.2.1 (Erlang OTP/20)", + "transaction-id": "bb234d40f358cc0ee3e8317890e8f065", + "X-Cloudant-Action": "cloudantnosqldb.any-document.read", + "X-Couch-Request-ID": "bb234d40f3", + "X-Frame-Options": "DENY", + "Strict-Transport-Security": "max-age=31536000", + "X-Content-Type-Options": "nosniff", + "X-Cloudant-Request-Class": "lookup", + "X-Cloudant-Backend": "bm-cc-us-east-05", + "Via": "1.1 lb2.bm-cc-us-east-05 (Glum/1.101.5)", + "Vary": "Accept-Encoding" + }, + "jsonBody": { + "_id": "testString", + "_rev": "3-5570e8bbb34412db757c2df4bfa1099b" + } + }, + "uuid": "ae2f6256-c33c-3e1a-9ed6-d16762069a0c" + }, + { + "id": "559c7479-f8d9-3c89-a91d-6f34122908ed", + "request": { + "url": "/testString/testString?latest=false&rev=testString", + "method": "HEAD" + }, + "response": { + "status": 200, + "headers": { + "Cache-Control": "must-revalidate", + "Content-Type": "application/json", + "Date": "Mon, 16 May 2022 21:08:35 GMT", + "Server": "CouchDB/3.2.1 (Erlang OTP/20)", + "transaction-id": "845550d983dcdb781e95385d4f92d4af", + "X-Cloudant-Action": "cloudantnosqldb.any-document.read", + "X-Couch-Request-ID": "845550d983", + "X-Frame-Options": "DENY", + "Strict-Transport-Security": "max-age=31536000", + "X-Content-Type-Options": "nosniff", + "X-Cloudant-Request-Class": "lookup", + "X-Cloudant-Backend": "bm-cc-us-east-05", + "Via": "1.1 lb2.bm-cc-us-east-05 (Glum/1.101.5)" + } + }, + "uuid": "559c7479-f8d9-3c89-a91d-6f34122908ed" + }, + { + "id": "aed0b4bd-1c80-35b2-b8f9-7da76c4cda69", + "request": { + "url": "/testString/testString/testString?rev=testString", + "method": "GET" + }, + "response": { + "status": 200, + "body": "This is a mock file.", + "headers": { + "Accept-Ranges": "none", + "Cache-Control": "must-revalidate", + "Content-MD5": "Vtnbf/a1yLJ4pm/eEB6fHA==", + "content-security-policy": "sandbox", + "Content-Type": "application/octet-stream", + "Date": "Fri, 20 May 2022 18:27:25 GMT", + "ETag": "\"Vtnbf/a1yLJ4pm/eEB6fHA==\"", + "Server": "CouchDB/3.2.1 (Erlang OTP/20)", + "transaction-id": "ecd7339c8a26c3ed107b5284eb15a76b", + "X-Cloudant-Action": "cloudantnosqldb.any-document.read", + "X-Couch-Request-ID": "ecd7339c8a", + "X-Frame-Options": "DENY", + "Strict-Transport-Security": "max-age=31536000", + "X-Content-Type-Options": "nosniff", + "X-Cloudant-Request-Class": "lookup", + "X-Cloudant-Backend": "bm-cc-us-east-05", + "Via": "1.1 lb2.bm-cc-us-east-05 (Glum/1.102.0)" + } + }, + "uuid": "aed0b4bd-1c80-35b2-b8f9-7da76c4cda69" + }, + { + "id": "efe03f6b-9784-3cd8-a4d3-ba2020661353", + "request": { + "url": "/testString/testString/testString?rev=testString", + "method": "PUT", + "bodyPatterns": [ + { + "equalTo": "This is a mock file." + } + ] + }, + "response": { + "status": 201, + "headers": { + "Cache-Control": "must-revalidate", + "Content-Type": "application/json", + "Date": "Fri, 20 May 2022 18:25:16 GMT", + "Location": "https://84ab8038-d8f1-450c-9645-4d16b448eb31-bluemix.cloudant.com/testString/testString/testString", + "Server": "CouchDB/3.2.1 (Erlang OTP/20)", + "transaction-id": "4326af6ec28e12fcf8eca683ba57da8e", + "X-Cloudant-Action": "cloudantnosqldb.data-document.write", + "X-Couch-Request-ID": "4326af6ec2", + "X-Frame-Options": "DENY", + "Strict-Transport-Security": "max-age=31536000", + "X-Content-Type-Options": "nosniff", + "X-Cloudant-Request-Class": "write", + "X-Cloudant-Backend": "bm-cc-us-east-05", + "Via": "1.1 lb2.bm-cc-us-east-05 (Glum/1.102.0)", + "Vary": "Accept-Encoding" + }, + "jsonBody": { + "ok": true, + "id": "testString", + "rev": "1-23085a965a5e50b32b00e57deb062932" + } + }, + "uuid": "efe03f6b-9784-3cd8-a4d3-ba2020661353" + }, + { + "id": "ecfbef48-33ff-30fe-b848-d09c6c472eac", + "request": { + "url": "/testString/testString/testString?rev=testString", + "method": "HEAD" + }, + "response": { + "status": 200, + "headers": { + "Accept-Ranges": "none", + "Cache-Control": "must-revalidate", + "Content-MD5": "Vtnbf/a1yLJ4pm/eEB6fHA==", + "content-security-policy": "sandbox", + "Content-Type": "application/octet-stream", + "Date": "Fri, 20 May 2022 18:26:53 GMT", + "ETag": "\"Vtnbf/a1yLJ4pm/eEB6fHA==\"", + "Server": "CouchDB/3.2.1 (Erlang OTP/20)", + "transaction-id": "4e77768c5d87bc873bbcc11961504516", + "X-Cloudant-Action": "cloudantnosqldb.any-document.read", + "X-Couch-Request-ID": "4e77768c5d", + "X-Frame-Options": "DENY", + "Strict-Transport-Security": "max-age=31536000", + "X-Content-Type-Options": "nosniff", + "X-Cloudant-Request-Class": "lookup", + "X-Cloudant-Backend": "bm-cc-us-east-05", + "Via": "1.1 lb2.bm-cc-us-east-05 (Glum/1.102.0)" + } + }, + "uuid": "ecfbef48-33ff-30fe-b848-d09c6c472eac" + }, + { + "id": "bf49e660-6802-3a7c-9975-5e451f4e1204", + "request": { + "urlPath": "/testString/testString/testString", + "method": "DELETE", + "queryParameters": { + "batch": { + "equalTo": "ok" + }, + "rev": { + "equalTo": "testString" + } + } + }, + "response": { + "status": 200, + "headers": { + "Cache-Control": "must-revalidate", + "Content-Type": "application/json", + "Date": "Mon, 23 May 2022 20:59:10 GMT", + "Server": "CouchDB/3.2.1 (Erlang OTP/20)", + "transaction-id": "b00e9dd9c3835c3ca04049cc2c3deaeb", + "X-Cloudant-Action": "cloudantnosqldb.data-document.write", + "X-Couch-Request-ID": "b00e9dd9c3", + "X-Frame-Options": "DENY", + "Strict-Transport-Security": "max-age=31536000", + "X-Content-Type-Options": "nosniff", + "X-Cloudant-Request-Class": "write", + "X-Cloudant-Backend": "bm-cc-us-east-05", + "Via": "1.1 lb2.bm-cc-us-east-05 (Glum/1.102.0)", + "Vary": "Accept-Encoding" + }, + "jsonBody": { + "ok": true, + "id": "testString", + "rev": "4-6705bb55c13a40bb465ab96fd810d996" + } + }, + "uuid": "bf49e660-6802-3a7c-9975-5e451f4e1204" + }, + { + "id": "5a60a196-eed3-33f2-9507-d6276639e89e", + "request": { + "urlPath": "/testString/testString", + "method": "PUT", + "queryParameters": { + "batch": { + "equalTo": "ok" + }, + "rev": { + "equalTo": "testString" + }, + "new_edits": { + "equalTo": "false" + } + }, + "bodyPatterns": [ + { + "equalToJson": "{}", + "ignoreArrayOrder": false, + "ignoreExtraElements": true + } + ] + }, + "response": { + "status": 201, + "headers": { + "Cache-Control": "must-revalidate", + "Content-Type": "application/json", + "Date": "Wed, 25 May 2022 19:15:19 GMT", + "ETag": "W/\"6-80028e69ea6e259eba0b419c3750128b\"", + "Location": "https://84ab8038-d8f1-450c-9645-4d16b448eb31-bluemix.cloudant.com/testString/testString", + "Server": "CouchDB/3.2.1 (Erlang OTP/20)", + "transaction-id": "0d39f720ff6eca60e344f22d000060c5", + "X-Cloudant-Action": "cloudantnosqldb.data-document.write", + "X-Couch-Request-ID": "0d39f720ff", + "X-Frame-Options": "DENY", + "Strict-Transport-Security": "max-age=31536000", + "X-Content-Type-Options": "nosniff", + "X-Cloudant-Request-Class": "write", + "X-Cloudant-Backend": "bm-cc-us-east-05", + "Via": "1.1 lb2.bm-cc-us-east-05 (Glum/1.102.0)", + "Vary": "Accept-Encoding" + }, + "jsonBody": { + "ok": true, + "id": "testString", + "rev": "2-80028e69ea6e259eba0b419c3750128b" + } + }, + "uuid": "5a60a196-eed3-33f2-9507-d6276639e89e" + } + ] +} diff --git a/stubs/mappings.json b/stubs/mappings.json index 2be7b778..b860410d 100644 --- a/stubs/mappings.json +++ b/stubs/mappings.json @@ -1,404 +1,553 @@ -{"mappings":[{ - "id" : "9fa984ad-62e6-4e74-a0ee-a531711f5dbc", - "name" : "orders_example", - "request" : { - "url" : "/orders/example?rev=2-4e2178e85cffb32d38ba4e451f6ca376", - "method" : "DELETE" - }, - "response" : { - "status" : 200, - "body" : "{\"ok\":true,\"id\":\"example\",\"rev\":\"3-4f1787d7a0520f825bd36822d7be627a\"}\n", - "headers" : { - "Cache-Control" : "must-revalidate", - "Content-Type" : "application/json", - "Date" : "Tue, 21 Jul 2020 07:58:57 GMT", - "ETag" : "\"3-4f1787d7a0520f825bd36822d7be627a\"", - "Server" : "CouchDB/3.0.0 (Erlang OTP/20)", - "X-Couch-Request-ID" : "1b41b9bfa1", - "X-CouchDB-Body-Time" : "0" +{ + "mappings": [ + { + "id": "4e7ecd46-9129-42a1-8899-985567444ad6", + "name": "orders_example", + "request": { + "url": "/orders/example?rev=4-bf3f53fe029a70e6451e375ccd36599c", + "method": "DELETE" + }, + "response": { + "status": 404, + "headers": { + "Cache-Control": "must-revalidate", + "Content-Type": "application/json", + "Date": "Tue, 21 Jul 2020 07:58:57 GMT", + "Server": "CouchDB/3.0.0 (Erlang OTP/20)", + "X-Couch-Request-ID": "750c2d635a", + "X-CouchDB-Body-Time": "0" + }, + "jsonBody": { + "error": "not_found", + "reason": "deleted" + } + }, + "uuid": "4e7ecd46-9129-42a1-8899-985567444ad6", + "persistent": true, + "insertionIndex": 18 + }, + { + "id": "9fa984ad-62e6-4e74-a0ee-a531711f5dbc", + "name": "orders_example", + "request": { + "url": "/orders/example?rev=2-4e2178e85cffb32d38ba4e451f6ca376", + "method": "DELETE" + }, + "response": { + "status": 200, + "headers": { + "Cache-Control": "must-revalidate", + "Content-Type": "application/json", + "Date": "Tue, 21 Jul 2020 07:58:57 GMT", + "ETag": "\"3-4f1787d7a0520f825bd36822d7be627a\"", + "Server": "CouchDB/3.0.0 (Erlang OTP/20)", + "X-Couch-Request-ID": "1b41b9bfa1", + "X-CouchDB-Body-Time": "0" + }, + "jsonBody": { + "ok": true, + "id": "example", + "rev": "3-4f1787d7a0520f825bd36822d7be627a" + } + }, + "uuid": "9fa984ad-62e6-4e74-a0ee-a531711f5dbc", + "persistent": true, + "insertionIndex": 17 + }, + { + "id": "75821ec1-cfed-41f0-bdef-cf3524a0ec4a", + "name": "orders_example", + "request": { + "url": "/orders/example", + "method": "GET" + }, + "response": { + "status": 404, + "headers": { + "Cache-Control": "must-revalidate", + "Content-Type": "application/json", + "Date": "Tue, 21 Jul 2020 07:58:57 GMT", + "Server": "CouchDB/3.0.0 (Erlang OTP/20)", + "X-Couch-Request-ID": "403d112b46", + "X-CouchDB-Body-Time": "0" + }, + "jsonBody": { + "error": "not_found", + "reason": "deleted" + } + }, + "uuid": "75821ec1-cfed-41f0-bdef-cf3524a0ec4a", + "persistent": true, + "scenarioName": "scenario-2-orders-example", + "requiredScenarioState": "scenario-2-orders-example-6", + "insertionIndex": 16 + }, + { + "id": "9fa984ad-62e6-4e74-a0ee-a531711f5dba", + "name": "orders_example", + "request": { + "url": "/orders/example?rev=3-4f1787d7a0520f825bd36822d7be627a", + "method": "DELETE" + }, + "response": { + "status": 200, + "headers": { + "Cache-Control": "must-revalidate", + "Content-Type": "application/json", + "Date": "Tue, 21 Jul 2020 07:58:57 GMT", + "ETag": "\"4-bf3f53fe029a70e6451e375ccd36599c\"", + "Server": "CouchDB/3.0.0 (Erlang OTP/20)", + "X-Couch-Request-ID": "1b41b9bfa1", + "X-CouchDB-Body-Time": "0" + }, + "jsonBody": { + "ok": true, + "id": "example", + "rev": "4-bf3f53fe029a70e6451e375ccd36599c" + } + }, + "uuid": "9fa984ad-62e6-4e74-a0ee-a531711f5dba", + "persistent": true, + "insertionIndex": 15 + }, + { + "id": "777d45f4-fdb3-43c4-835f-1fcafcbfd26b", + "name": "orders_example", + "request": { + "url": "/orders/example", + "method": "GET" + }, + "response": { + "status": 200, + "headers": { + "Cache-Control": "must-revalidate", + "Content-Type": "application/json", + "Date": "Tue, 21 Jul 2020 07:58:57 GMT", + "ETag": "\"4-bf3f53fe029a70e6451e375ccd36599c\"", + "Server": "CouchDB/3.0.0 (Erlang OTP/20)", + "X-Couch-Request-ID": "175d24412f", + "X-CouchDB-Body-Time": "0" + }, + "jsonBody": { + "_id": "example", + "_rev": "4-bf3f53fe029a70e6451e375ccd36599c", + "name": "Bob Smith", + "address": "19 Front Street, Darlington, DL5 1TY" + } + }, + "uuid": "777d45f4-fdb3-43c4-835f-1fcafcbfd26b", + "persistent": true, + "scenarioName": "scenario-2-orders-example", + "requiredScenarioState": "scenario-2-orders-example-5", + "newScenarioState": "scenario-2-orders-example-6", + "insertionIndex": 14 + }, + { + "id": "9d0f096d-1d43-414f-8831-5e6e6abe7659", + "name": "orders", + "request": { + "url": "/orders", + "method": "POST", + "bodyPatterns": [ + { + "equalToJson": "{\"_id\":\"example\",\"_rev\":\"3-4f1787d7a0520f825bd36822d7be627a\",\"name\":\"Bob Smith\",\"address\":\"19 Front Street, Darlington, DL5 1TY\"}", + "ignoreArrayOrder": true, + "ignoreExtraElements": true + } + ] + }, + "response": { + "status": 201, + "headers": { + "Cache-Control": "must-revalidate", + "Content-Type": "application/json", + "Date": "Tue, 21 Jul 2020 07:58:57 GMT", + "Location": "http://localhost:5984/orders/example", + "Server": "CouchDB/3.0.0 (Erlang OTP/20)", + "X-Couch-Request-ID": "32c3176598", + "X-CouchDB-Body-Time": "0" + }, + "jsonBody": { + "ok": true, + "id": "example", + "rev": "4-bf3f53fe029a70e6451e375ccd36599c" + } + }, + "uuid": "9d0f096d-1d43-414f-8831-5e6e6abe7659", + "persistent": true, + "insertionIndex": 13 + }, + { + "id": "232b9d9d-1506-44a9-a11a-66639eb9250e", + "name": "orders_example", + "request": { + "url": "/orders/example", + "method": "GET" + }, + "response": { + "status": 200, + "headers": { + "Cache-Control": "must-revalidate", + "Content-Type": "application/json", + "Date": "Tue, 21 Jul 2020 07:58:57 GMT", + "ETag": "\"3-4f1787d7a0520f825bd36822d7be627a\"", + "Server": "CouchDB/3.0.0 (Erlang OTP/20)", + "X-Couch-Request-ID": "4b23a37b39", + "X-CouchDB-Body-Time": "0" + }, + "jsonBody": { + "_id": "example", + "_rev": "3-4f1787d7a0520f825bd36822d7be627a", + "name": "Bob Smith", + "address": "19 Front Street, Darlington, DL5 1TY" + } + }, + "uuid": "232b9d9d-1506-44a9-a11a-66639eb9250e", + "persistent": true, + "scenarioName": "scenario-2-orders-example", + "requiredScenarioState": "scenario-2-orders-example-4", + "newScenarioState": "scenario-2-orders-example-6", + "insertionIndex": 12 + }, + { + "id": "d2d672a9-90d5-4959-b7c6-cef7538fd233", + "name": "orders", + "request": { + "url": "/orders", + "method": "POST", + "bodyPatterns": [ + { + "equalToJson": "{\"_id\":\"example\",\"_rev\":\"2-4e2178e85cffb32d38ba4e451f6ca376\",\"name\":\"Bob Smith\",\"address\":\"19 Front Street, Darlington, DL5 1TY\"}", + "ignoreArrayOrder": true, + "ignoreExtraElements": true + } + ] + }, + "response": { + "status": 201, + "headers": { + "Cache-Control": "must-revalidate", + "Content-Type": "application/json", + "Date": "Tue, 21 Jul 2020 07:58:57 GMT", + "Location": "http://localhost:5984/orders/example", + "Server": "CouchDB/3.0.0 (Erlang OTP/20)", + "X-Couch-Request-ID": "260d229417", + "X-CouchDB-Body-Time": "0" + }, + "jsonBody": { + "ok": true, + "id": "example", + "rev": "3-4f1787d7a0520f825bd36822d7be627a" + } + }, + "uuid": "d2d672a9-90d5-4959-b7c6-cef7538fd233", + "persistent": true, + "insertionIndex": 11 + }, + { + "id": "b19304eb-3f50-4af2-b692-23847652a661", + "name": "orders_example", + "request": { + "url": "/orders/example", + "method": "GET" + }, + "response": { + "status": 200, + "headers": { + "Cache-Control": "must-revalidate", + "Content-Type": "application/json", + "Date": "Tue, 21 Jul 2020 07:58:57 GMT", + "ETag": "\"2-4e2178e85cffb32d38ba4e451f6ca376\"", + "Server": "CouchDB/3.0.0 (Erlang OTP/20)", + "X-Couch-Request-ID": "fd177b95bf", + "X-CouchDB-Body-Time": "0" + }, + "jsonBody": { + "_id": "example", + "_rev": "2-4e2178e85cffb32d38ba4e451f6ca376", + "name": "Bob Smith", + "joined": "2019-01-24T10:42:59.000Z" + } + }, + "uuid": "b19304eb-3f50-4af2-b692-23847652a661", + "persistent": true, + "scenarioName": "scenario-2-orders-example", + "requiredScenarioState": "scenario-2-orders-example-3", + "newScenarioState": "scenario-2-orders-example-4", + "insertionIndex": 10 + }, + { + "id": "61d277cc-c7e5-4390-8845-4276000c224e", + "name": "orders", + "request": { + "url": "/orders", + "method": "POST", + "bodyPatterns": [ + { + "equalToJson": "{\"_id\": \"example\", \"_rev\": \"1-1b403633540686aa32d013fda9041a5d\", \"name\": \"Bob Smith\", \"address\": \"19 Front Street, Darlington, DL5 1TY\"}", + "ignoreArrayOrder": true, + "ignoreExtraElements": true + } + ] + }, + "response": { + "status": 201, + "headers": { + "Cache-Control": "must-revalidate", + "Content-Type": "application/json", + "Date": "Tue, 21 Jul 2020 07:58:57 GMT", + "Location": "http://localhost:5984/orders/example", + "Server": "CouchDB/3.0.0 (Erlang OTP/20)", + "X-Couch-Request-ID": "ef31b7a04b", + "X-CouchDB-Body-Time": "0" + }, + "jsonBody": { + "ok": true, + "id": "example", + "rev": "2-4e2178e85cffb32d38ba4e451f6ca376" + } + }, + "uuid": "61d277cc-c7e5-4390-8845-4276000c224e", + "persistent": true, + "insertionIndex": 9 + }, + { + "id": "c8ad5e93-3d1c-4fd4-a6ed-92f28a683a23", + "name": "orders_example", + "request": { + "url": "/orders/example", + "method": "GET" + }, + "response": { + "status": 200, + "headers": { + "Cache-Control": "must-revalidate", + "Content-Type": "application/json", + "Date": "Tue, 21 Jul 2020 07:58:57 GMT", + "ETag": "\"1-1b403633540686aa32d013fda9041a5d\"", + "Server": "CouchDB/3.0.0 (Erlang OTP/20)", + "X-Couch-Request-ID": "f9bef554c3", + "X-CouchDB-Body-Time": "0" + }, + "jsonBody": { + "_id": "example", + "_rev": "1-1b403633540686aa32d013fda9041a5d", + "name": "Bob Smith", + "joined": "2019-01-24T10:42:59.000Z" + } + }, + "uuid": "c8ad5e93-3d1c-4fd4-a6ed-92f28a683a23", + "persistent": true, + "scenarioName": "scenario-2-orders-example", + "requiredScenarioState": "scenario-2-orders-example-2", + "newScenarioState": "scenario-2-orders-example-3", + "insertionIndex": 8 + }, + { + "id": "b6d3f5e3-8689-4740-8a5a-273c9ed9f5a7", + "name": "orders", + "request": { + "url": "/orders", + "method": "PUT" + }, + "response": { + "status": 412, + "headers": { + "Cache-Control": "must-revalidate", + "Content-Type": "application/json", + "Date": "Tue, 21 Jul 2020 07:58:57 GMT", + "Server": "CouchDB/3.0.0 (Erlang OTP/20)", + "X-Couch-Request-ID": "474375177b", + "X-CouchDB-Body-Time": "0" + }, + "jsonBody": { + "error": "file_exists", + "reason": "The database could not be created, the file already exists." + } + }, + "uuid": "b6d3f5e3-8689-4740-8a5a-273c9ed9f5a7", + "persistent": true, + "scenarioName": "scenario-1-orders", + "requiredScenarioState": "scenario-1-orders-2", + "insertionIndex": 7 + }, + { + "id": "666242fb-0be6-4e5d-8e68-fbfa863f3f3a", + "name": "orders", + "request": { + "url": "/orders", + "method": "POST", + "bodyPatterns": [ + { + "equalToJson": "{\"_id\":\"example\",\"name\":\"Bob Smith\",\"joined\":\"2019-01-24T10:42:59.000Z\"}", + "ignoreArrayOrder": true, + "ignoreExtraElements": true + } + ] + }, + "response": { + "status": 201, + "headers": { + "Cache-Control": "must-revalidate", + "Content-Type": "application/json", + "Date": "Tue, 21 Jul 2020 07:58:57 GMT", + "Location": "http://localhost:5984/orders/example", + "Server": "CouchDB/3.0.0 (Erlang OTP/20)", + "X-Couch-Request-ID": "1528aeb812", + "X-CouchDB-Body-Time": "0" + }, + "jsonBody": { + "ok": true, + "id": "example", + "rev": "1-1b403633540686aa32d013fda9041a5d" + } + }, + "uuid": "666242fb-0be6-4e5d-8e68-fbfa863f3f3a", + "persistent": true, + "insertionIndex": 6 + }, + { + "id": "05def288-5864-48e7-87ff-f7552a9dd1c2", + "name": "orders", + "request": { + "url": "/orders", + "method": "PUT" + }, + "response": { + "status": 201, + "headers": { + "Cache-Control": "must-revalidate", + "Content-Type": "application/json", + "Date": "Tue, 21 Jul 2020 07:58:57 GMT", + "Location": "http://localhost:5984/orders", + "Server": "CouchDB/3.0.0 (Erlang OTP/20)", + "X-Couch-Request-ID": "141b3ba868", + "X-CouchDB-Body-Time": "0" + }, + "jsonBody": { + "ok": true + } + }, + "uuid": "05def288-5864-48e7-87ff-f7552a9dd1c2", + "persistent": true, + "scenarioName": "scenario-1-orders", + "requiredScenarioState": "Started", + "newScenarioState": "scenario-1-orders-2", + "insertionIndex": 4 + }, + { + "id": "af927fc5-341a-4f9d-9c48-83a57aa7b505", + "name": "orders_example", + "request": { + "url": "/orders/example", + "method": "GET" + }, + "response": { + "status": 200, + "headers": { + "Cache-Control": "must-revalidate", + "Content-Type": "application/json", + "Date": "Tue, 21 Jul 2020 07:58:57 GMT", + "ETag": "\"1-1b403633540686aa32d013fda9041a5d\"", + "Server": "CouchDB/3.0.0 (Erlang OTP/20)", + "X-Couch-Request-ID": "f9bef554c3", + "X-CouchDB-Body-Time": "0" + }, + "jsonBody": { + "_id": "example", + "_rev": "1-1b403633540686aa32d013fda9041a5d", + "name": "Bob Smith", + "joined": "2019-01-24T10:42:59.000Z" + } + }, + "uuid": "af927fc5-341a-4f9d-9c48-83a57aa7b505", + "persistent": true, + "scenarioName": "scenario-2-orders-example", + "requiredScenarioState": "Started", + "newScenarioState": "scenario-2-orders-example-2", + "insertionIndex": 3 + }, + { + "id": "e8a4edcf-8d9d-431b-8a99-76f5d9ad8a7b", + "name": "orders", + "request": { + "url": "/orders", + "method": "GET" + }, + "response": { + "status": 200, + "headers": { + "Cache-Control": "must-revalidate", + "Content-Type": "application/json", + "Date": "Tue, 21 Jul 2020 07:58:57 GMT", + "Server": "CouchDB/3.0.0 (Erlang OTP/20)", + "X-Couch-Request-ID": "832fcf2ea2", + "X-CouchDB-Body-Time": "0" + }, + "jsonBody": { + "db_name": "orders", + "purge_seq": "0-g1AAAABPeJzLYWBgYMpgTmHgzcvPy09JdcjLz8gvLskBCeexAEmGBiD1HwiyEhlwqEtkSKqHKMgCAIT2GV4", + "update_seq": "1-g1AAAABPeJzLYWBgYMpgTmHgzcvPy09JdcjLz8gvLskBCeexAEmGBiD1HwiyEjlxqEtkSKoHK2DLAgCGPRlt", + "sizes": { + "file": 57751, + "external": 3450, + "active": 7355 + }, + "props": {}, + "doc_del_count": 0, + "doc_count": 1, + "disk_format_version": 8, + "compact_running": false, + "cluster": { + "q": 2, + "n": 1, + "w": 1, + "r": 1 + }, + "instance_start_time": "0" + } + }, + "uuid": "e8a4edcf-8d9d-431b-8a99-76f5d9ad8a7b", + "persistent": true, + "insertionIndex": 2 + }, + { + "id": "50eb71a6-e709-4d04-aba8-861a4135cdb8", + "name": "", + "request": { + "url": "/", + "method": "GET" + }, + "response": { + "status": 200, + "headers": { + "Cache-Control": "must-revalidate", + "Content-Type": "application/json", + "Date": "Tue, 21 Jul 2020 07:58:57 GMT", + "Server": "CouchDB/3.0.0 (Erlang OTP/20)", + "X-Couch-Request-ID": "b6cf47349d", + "X-CouchDB-Body-Time": "0" + }, + "jsonBody": { + "couchdb": "Welcome", + "version": "2.1.1", + "git_sha": "03a77db6c", + "uuid": "cffa3c2cc703d6caed7ec9a9f6d110e9", + "features": [ + "access-ready", + "partitioned", + "pluggable-storage-engines", + "reshard", + "scheduler" + ], + "vendor": { + "name": "The Apache Software Foundation" + } + } + }, + "uuid": "50eb71a6-e709-4d04-aba8-861a4135cdb8", + "persistent": true, + "insertionIndex": 1 } - }, - "uuid" : "9fa984ad-62e6-4e74-a0ee-a531711f5dbc", - "persistent" : true, - "insertionIndex" : 17 -},{ - "id" : "75821ec1-cfed-41f0-bdef-cf3524a0ec4a", - "name" : "orders_example", - "request" : { - "url" : "/orders/example", - "method" : "GET" - }, - "response" : { - "status" : 404, - "body" : "{\"error\":\"not_found\",\"reason\":\"deleted\"}\n", - "headers" : { - "Cache-Control" : "must-revalidate", - "Content-Type" : "application/json", - "Date" : "Tue, 21 Jul 2020 07:58:57 GMT", - "Server" : "CouchDB/3.0.0 (Erlang OTP/20)", - "X-Couch-Request-ID" : "403d112b46", - "X-CouchDB-Body-Time" : "0" - } - }, - "uuid" : "75821ec1-cfed-41f0-bdef-cf3524a0ec4a", - "persistent" : true, - "scenarioName" : "scenario-2-orders-example", - "requiredScenarioState" : "scenario-2-orders-example-6", - "insertionIndex" : 16 -},{ - "id" : "9fa984ad-62e6-4e74-a0ee-a531711f5dba", - "name" : "orders_example", - "request" : { - "url" : "/orders/example?rev=3-4f1787d7a0520f825bd36822d7be627a", - "method" : "DELETE" - }, - "response" : { - "status" : 200, - "body" : "{\"ok\":true,\"id\":\"example\",\"rev\":\"4-bf3f53fe029a70e6451e375ccd36599c\"}\n", - "headers" : { - "Cache-Control" : "must-revalidate", - "Content-Type" : "application/json", - "Date" : "Tue, 21 Jul 2020 07:58:57 GMT", - "ETag" : "\"4-bf3f53fe029a70e6451e375ccd36599c\"", - "Server" : "CouchDB/3.0.0 (Erlang OTP/20)", - "X-Couch-Request-ID" : "1b41b9bfa1", - "X-CouchDB-Body-Time" : "0" - } - }, - "uuid" : "9fa984ad-62e6-4e74-a0ee-a531711f5dba", - "persistent" : true, - "insertionIndex" : 15 -},{ - "id" : "777d45f4-fdb3-43c4-835f-1fcafcbfd26b", - "name" : "orders_example", - "request" : { - "url" : "/orders/example", - "method" : "GET" - }, - "response" : { - "status" : 200, - "body" : "{\"_id\":\"example\",\"_rev\":\"4-bf3f53fe029a70e6451e375ccd36599c\",\"name\":\"Bob Smith\",\"address\":\"19 Front Street, Darlington, DL5 1TY\"}\n", - "headers" : { - "Cache-Control" : "must-revalidate", - "Content-Type" : "application/json", - "Date" : "Tue, 21 Jul 2020 07:58:57 GMT", - "ETag" : "\"4-bf3f53fe029a70e6451e375ccd36599c\"", - "Server" : "CouchDB/3.0.0 (Erlang OTP/20)", - "X-Couch-Request-ID" : "175d24412f", - "X-CouchDB-Body-Time" : "0" - } - }, - "uuid" : "777d45f4-fdb3-43c4-835f-1fcafcbfd26b", - "persistent" : true, - "scenarioName" : "scenario-2-orders-example", - "requiredScenarioState" : "scenario-2-orders-example-5", - "newScenarioState" : "scenario-2-orders-example-6", - "insertionIndex" : 14 -},{ - "id" : "9d0f096d-1d43-414f-8831-5e6e6abe7659", - "name" : "orders", - "request" : { - "url" : "/orders", - "method" : "POST", - "bodyPatterns" : [ { - "equalToJson" : "{\"_id\":\"example\",\"_rev\":\"3-4f1787d7a0520f825bd36822d7be627a\",\"name\":\"Bob Smith\",\"address\":\"19 Front Street, Darlington, DL5 1TY\"}", - "ignoreArrayOrder" : true, - "ignoreExtraElements" : true - } ] - }, - "response" : { - "status" : 201, - "body" : "{\"ok\":true,\"id\":\"example\",\"rev\":\"4-bf3f53fe029a70e6451e375ccd36599c\"}\n", - "headers" : { - "Cache-Control" : "must-revalidate", - "Content-Type" : "application/json", - "Date" : "Tue, 21 Jul 2020 07:58:57 GMT", - "Location" : "http://localhost:5984/orders/example", - "Server" : "CouchDB/3.0.0 (Erlang OTP/20)", - "X-Couch-Request-ID" : "32c3176598", - "X-CouchDB-Body-Time" : "0" - } - }, - "uuid" : "9d0f096d-1d43-414f-8831-5e6e6abe7659", - "persistent" : true, - "insertionIndex" : 13 -},{ - "id" : "232b9d9d-1506-44a9-a11a-66639eb9250e", - "name" : "orders_example", - "request" : { - "url" : "/orders/example", - "method" : "GET" - }, - "response" : { - "status" : 200, - "body" : "{\"_id\":\"example\",\"_rev\":\"3-4f1787d7a0520f825bd36822d7be627a\",\"name\":\"Bob Smith\",\"address\":\"19 Front Street, Darlington, DL5 1TY\"}\n", - "headers" : { - "Cache-Control" : "must-revalidate", - "Content-Type" : "application/json", - "Date" : "Tue, 21 Jul 2020 07:58:57 GMT", - "ETag" : "\"3-4f1787d7a0520f825bd36822d7be627a\"", - "Server" : "CouchDB/3.0.0 (Erlang OTP/20)", - "X-Couch-Request-ID" : "4b23a37b39", - "X-CouchDB-Body-Time" : "0" - } - }, - "uuid" : "232b9d9d-1506-44a9-a11a-66639eb9250e", - "persistent" : true, - "scenarioName" : "scenario-2-orders-example", - "requiredScenarioState" : "scenario-2-orders-example-4", - "newScenarioState" : "scenario-2-orders-example-5", - "insertionIndex" : 12 -},{ - "id" : "d2d672a9-90d5-4959-b7c6-cef7538fd233", - "name" : "orders", - "request" : { - "url" : "/orders", - "method" : "POST", - "bodyPatterns" : [ { - "equalToJson" : "{\"_id\":\"example\",\"_rev\":\"2-4e2178e85cffb32d38ba4e451f6ca376\",\"name\":\"Bob Smith\",\"address\":\"19 Front Street, Darlington, DL5 1TY\"}", - "ignoreArrayOrder" : true, - "ignoreExtraElements" : true - } ] - }, - "response" : { - "status" : 201, - "body" : "{\"ok\":true,\"id\":\"example\",\"rev\":\"3-4f1787d7a0520f825bd36822d7be627a\"}\n", - "headers" : { - "Cache-Control" : "must-revalidate", - "Content-Type" : "application/json", - "Date" : "Tue, 21 Jul 2020 07:58:57 GMT", - "Location" : "http://localhost:5984/orders/example", - "Server" : "CouchDB/3.0.0 (Erlang OTP/20)", - "X-Couch-Request-ID" : "260d229417", - "X-CouchDB-Body-Time" : "0" - } - }, - "uuid" : "d2d672a9-90d5-4959-b7c6-cef7538fd233", - "persistent" : true, - "insertionIndex" : 11 -},{ - "id" : "b19304eb-3f50-4af2-b692-23847652a661", - "name" : "orders_example", - "request" : { - "url" : "/orders/example", - "method" : "GET" - }, - "response" : { - "status" : 200, - "body" : "{\"_id\":\"example\",\"_rev\":\"2-4e2178e85cffb32d38ba4e451f6ca376\",\"name\":\"Bob Smith\",\"joined\":\"2019-01-24T10:42:59.000Z\"}\n", - "headers" : { - "Cache-Control" : "must-revalidate", - "Content-Type" : "application/json", - "Date" : "Tue, 21 Jul 2020 07:58:57 GMT", - "ETag" : "\"2-4e2178e85cffb32d38ba4e451f6ca376\"", - "Server" : "CouchDB/3.0.0 (Erlang OTP/20)", - "X-Couch-Request-ID" : "fd177b95bf", - "X-CouchDB-Body-Time" : "0" - } - }, - "uuid" : "b19304eb-3f50-4af2-b692-23847652a661", - "persistent" : true, - "scenarioName" : "scenario-2-orders-example", - "requiredScenarioState" : "scenario-2-orders-example-3", - "newScenarioState" : "scenario-2-orders-example-4", - "insertionIndex" : 10 -},{ - "id" : "61d277cc-c7e5-4390-8845-4276000c224e", - "name" : "orders", - "request" : { - "url" : "/orders", - "method" : "POST", - "bodyPatterns" : [ { - "equalToJson" : "{\"_id\": \"example\", \"_rev\": \"1-1b403633540686aa32d013fda9041a5d\", \"name\": \"Bob Smith\", \"address\": \"19 Front Street, Darlington, DL5 1TY\"}", - "ignoreArrayOrder" : true, - "ignoreExtraElements" : true - } ] - }, - "response" : { - "status" : 201, - "body" : "{\"ok\":true,\"id\":\"example\",\"rev\":\"2-4e2178e85cffb32d38ba4e451f6ca376\"}\n", - "headers" : { - "Cache-Control" : "must-revalidate", - "Content-Type" : "application/json", - "Date" : "Tue, 21 Jul 2020 07:58:57 GMT", - "Location" : "http://localhost:5984/orders/example", - "Server" : "CouchDB/3.0.0 (Erlang OTP/20)", - "X-Couch-Request-ID" : "ef31b7a04b", - "X-CouchDB-Body-Time" : "0" - } - }, - "uuid" : "61d277cc-c7e5-4390-8845-4276000c224e", - "persistent" : true, - "insertionIndex" : 9 -},{ - "id" : "c8ad5e93-3d1c-4fd4-a6ed-92f28a683a23", - "name" : "orders_example", - "request" : { - "url" : "/orders/example", - "method" : "GET" - }, - "response" : { - "status" : 200, - "body" : "{\"_id\":\"example\",\"_rev\":\"1-1b403633540686aa32d013fda9041a5d\",\"name\":\"Bob Smith\",\"joined\":\"2019-01-24T10:42:59.000Z\"}\n", - "headers" : { - "Cache-Control" : "must-revalidate", - "Content-Type" : "application/json", - "Date" : "Tue, 21 Jul 2020 07:58:57 GMT", - "ETag" : "\"1-1b403633540686aa32d013fda9041a5d\"", - "Server" : "CouchDB/3.0.0 (Erlang OTP/20)", - "X-Couch-Request-ID" : "f9bef554c3", - "X-CouchDB-Body-Time" : "0" - } - }, - "uuid" : "c8ad5e93-3d1c-4fd4-a6ed-92f28a683a23", - "persistent" : true, - "scenarioName" : "scenario-2-orders-example", - "requiredScenarioState" : "Started", - "newScenarioState" : "scenario-2-orders-example-3", - "insertionIndex" : 8 -},{ - "id" : "b6d3f5e3-8689-4740-8a5a-273c9ed9f5a7", - "name" : "orders", - "request" : { - "url" : "/orders", - "method" : "PUT" - }, - "response" : { - "status" : 412, - "body" : "{\"error\":\"file_exists\",\"reason\":\"The database could not be created, the file already exists.\"}\n", - "headers" : { - "Cache-Control" : "must-revalidate", - "Content-Type" : "application/json", - "Date" : "Tue, 21 Jul 2020 07:58:57 GMT", - "Server" : "CouchDB/3.0.0 (Erlang OTP/20)", - "X-Couch-Request-ID" : "474375177b", - "X-CouchDB-Body-Time" : "0" - } - }, - "uuid" : "b6d3f5e3-8689-4740-8a5a-273c9ed9f5a7", - "persistent" : true, - "scenarioName" : "scenario-1-orders", - "requiredScenarioState" : "scenario-1-orders-2", - "insertionIndex" : 7 -},{ - "id" : "666242fb-0be6-4e5d-8e68-fbfa863f3f3a", - "name" : "orders", - "request" : { - "url" : "/orders", - "method" : "POST", - "bodyPatterns" : [ { - "equalToJson" : "{\"_id\":\"example\",\"name\":\"Bob Smith\",\"joined\":\"2019-01-24T10:42:59.000Z\"}", - "ignoreArrayOrder" : true, - "ignoreExtraElements" : true - } ] - }, - "response" : { - "status" : 201, - "body" : "{\"ok\":true,\"id\":\"example\",\"rev\":\"1-1b403633540686aa32d013fda9041a5d\"}\n", - "headers" : { - "Cache-Control" : "must-revalidate", - "Content-Type" : "application/json", - "Date" : "Tue, 21 Jul 2020 07:58:57 GMT", - "Location" : "http://localhost:5984/orders/example", - "Server" : "CouchDB/3.0.0 (Erlang OTP/20)", - "X-Couch-Request-ID" : "1528aeb812", - "X-CouchDB-Body-Time" : "0" - } - }, - "uuid" : "666242fb-0be6-4e5d-8e68-fbfa863f3f3a", - "persistent" : true, - "insertionIndex" : 6 -},{ - "id" : "05def288-5864-48e7-87ff-f7552a9dd1c2", - "name" : "orders", - "request" : { - "url" : "/orders", - "method" : "PUT" - }, - "response" : { - "status" : 201, - "body" : "{\"ok\":true}\n", - "headers" : { - "Cache-Control" : "must-revalidate", - "Content-Type" : "application/json", - "Date" : "Tue, 21 Jul 2020 07:58:57 GMT", - "Location" : "http://localhost:5984/orders", - "Server" : "CouchDB/3.0.0 (Erlang OTP/20)", - "X-Couch-Request-ID" : "141b3ba868", - "X-CouchDB-Body-Time" : "0" - } - }, - "uuid" : "05def288-5864-48e7-87ff-f7552a9dd1c2", - "persistent" : true, - "scenarioName" : "scenario-1-orders", - "requiredScenarioState" : "Started", - "newScenarioState" : "scenario-1-orders-2", - "insertionIndex" : 4 -},{ - "id" : "af927fc5-341a-4f9d-9c48-83a57aa7b505", - "name" : "animaldb_zebra", - "request" : { - "url" : "/animaldb/zebra", - "method" : "GET" - }, - "response" : { - "status" : 200, - "body" : "{\"_id\":\"zebra\",\"_rev\":\"3-750dac460a6cc41e6999f8943b8e603e\",\"wiki_page\":\"http://en.wikipedia.org/wiki/Plains_zebra\",\"min_length\":2,\"max_length\":2.5,\"min_weight\":175,\"max_weight\":387,\"class\":\"mammal\",\"diet\":\"herbivore\"}\n", - "headers" : { - "Cache-Control" : "must-revalidate", - "Content-Type" : "application/json", - "Date" : "Tue, 21 Jul 2020 07:58:57 GMT", - "ETag" : "\"3-750dac460a6cc41e6999f8943b8e603e\"", - "Server" : "CouchDB/3.0.0 (Erlang OTP/20)", - "X-Couch-Request-ID" : "59a32f4530", - "X-CouchDB-Body-Time" : "0" - } - }, - "uuid" : "af927fc5-341a-4f9d-9c48-83a57aa7b505", - "persistent" : true, - "insertionIndex" : 3 -},{ - "id" : "e8a4edcf-8d9d-431b-8a99-76f5d9ad8a7b", - "name" : "animaldb", - "request" : { - "url" : "/animaldb", - "method" : "GET" - }, - "response" : { - "status" : 200, - "body" : "{\"db_name\":\"animaldb\",\"purge_seq\":\"0-g1AAAABPeJzLYWBgYMpgTmHgzcvPy09JdcjLz8gvLskBCeexAEmGBiD1HwiyEhlwqEtkSKqHKMgCAIT2GV4\",\"update_seq\":\"15-g1AAAABPeJzLYWBgYMpgTmHgzcvPy09JdcjLz8gvLskBCeexAEmGBiD1HwiyEjlxqEtkSKoHK2DLAgCGPRlt\",\"sizes\":{\"file\":57751,\"external\":3450,\"active\":7355},\"props\":{},\"doc_del_count\":3,\"doc_count\":11,\"disk_format_version\":8,\"compact_running\":false,\"cluster\":{\"q\":2,\"n\":1,\"w\":1,\"r\":1},\"instance_start_time\":\"0\"}\n", - "headers" : { - "Cache-Control" : "must-revalidate", - "Content-Type" : "application/json", - "Date" : "Tue, 21 Jul 2020 07:58:57 GMT", - "Server" : "CouchDB/3.0.0 (Erlang OTP/20)", - "X-Couch-Request-ID" : "832fcf2ea2", - "X-CouchDB-Body-Time" : "0" - } - }, - "uuid" : "e8a4edcf-8d9d-431b-8a99-76f5d9ad8a7b", - "persistent" : true, - "insertionIndex" : 2 -},{ - "id" : "50eb71a6-e709-4d04-aba8-861a4135cdb8", - "name" : "", - "request" : { - "url" : "/", - "method" : "GET" - }, - "response" : { - "status" : 200, - "body" : "{\"couchdb\":\"Welcome\",\"version\":\"2.1.1\",\"git_sha\":\"03a77db6c\",\"uuid\":\"cffa3c2cc703d6caed7ec9a9f6d110e9\",\"features\":[\"access-ready\",\"partitioned\",\"pluggable-storage-engines\",\"reshard\",\"scheduler\"],\"vendor\":{\"name\":\"The Apache Software Foundation\"}}\n", - "headers" : { - "Cache-Control" : "must-revalidate", - "Content-Type" : "application/json", - "Date" : "Tue, 21 Jul 2020 07:58:57 GMT", - "Server" : "CouchDB/3.0.0 (Erlang OTP/20)", - "X-Couch-Request-ID" : "b6cf47349d", - "X-CouchDB-Body-Time" : "0" - } - }, - "uuid" : "50eb71a6-e709-4d04-aba8-861a4135cdb8", - "persistent" : true, - "insertionIndex" : 1 -}]} + ] +} diff --git a/test/examples/output/create_db_and_doc.txt b/test/examples/output/create_db_and_doc.txt index 52cd7a00..8dfe98cf 100644 --- a/test/examples/output/create_db_and_doc.txt +++ b/test/examples/output/create_db_and_doc.txt @@ -1,8 +1,7 @@ "orders" database created. -You have created the document: +You have created the document. Response body: { - "_id": "example", - "_rev": "1-1b403633540686aa32d013fda9041a5d", - "name": "Bob Smith", - "joined": "2019-01-24T10:42:99.000Z" + "ok": true, + "id": "example", + "rev": "1-1b403633540686aa32d013fda9041a5d" } \ No newline at end of file diff --git a/test/examples/output/delete_doc2.txt b/test/examples/output/delete_doc2.txt new file mode 100644 index 00000000..35ea06bd --- /dev/null +++ b/test/examples/output/delete_doc2.txt @@ -0,0 +1 @@ +Cannot delete document because either "orders" database or "example" document was not found. \ No newline at end of file diff --git a/test/examples/output/get_info_from_existing_database.txt b/test/examples/output/get_info_from_existing_database.txt index c7564307..ba544c46 100644 --- a/test/examples/output/get_info_from_existing_database.txt +++ b/test/examples/output/get_info_from_existing_database.txt @@ -1,14 +1,9 @@ -Server Version: 2.1.1 -Document count in "animaldb" database is 11. +Server Version: 3.2.1 +Document count in "orders" database is 1. Document retrieved from database: { - "_id": "zebra", - "_rev": "3-750dac460a6cc41e6999f8943b8e603e", - "wiki_page": "http://en.wikipedia.org/wiki/Plains_zebra", - "min_length": 2, - "max_length": 2.5, - "min_weight": 175, - "max_weight": 387, - "class": "mammal", - "diet": "herbivore" + "_id": "example", + "_rev": "1-1b403633540686aa32d013fda9041a5d", + "name": "Bob Smith", + "joined": "2019-01-24T10:42:59.000Z" } \ No newline at end of file diff --git a/test/examples/output/update_doc.txt b/test/examples/output/update_doc.txt index 4ec63a73..5ed0026e 100644 --- a/test/examples/output/update_doc.txt +++ b/test/examples/output/update_doc.txt @@ -1,3 +1,4 @@ +You have updated the document: { "_id": "example", "_rev": "2-4e2178e85cffb32d38ba4e451f6ca376", diff --git a/test/examples/output/update_doc2.txt b/test/examples/output/update_doc2.txt new file mode 100644 index 00000000..3bf62984 --- /dev/null +++ b/test/examples/output/update_doc2.txt @@ -0,0 +1,7 @@ +You have updated the document: +{ + "_id": "example", + "_rev": "3-4f1787d7a0520f825bd36822d7be627a", + "name": "Bob Smith", + "address": "19 Front Street, Darlington, DL5 1TY" +} \ No newline at end of file diff --git a/test/examples/src/create_db_and_doc.py b/test/examples/src/create_db_and_doc.py index 1e6c78c1..92472605 100644 --- a/test/examples/src/create_db_and_doc.py +++ b/test/examples/src/create_db_and_doc.py @@ -1,6 +1,6 @@ # coding: utf-8 -# © Copyright IBM Corporation 2020, 2022. +# © Copyright IBM Corporation 2020, 2023. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -14,14 +14,11 @@ # See the License for the specific language governing permissions and # limitations under the License. -import logging +import json from ibm_cloud_sdk_core import ApiException from ibmcloudant.cloudant_v1 import CloudantV1, Document -# Set logging level to show only critical logs -logging.basicConfig(level=logging.CRITICAL) - # 1. Create a client with `CLOUDANT` default service name ============= client = CloudantV1.new_instance() @@ -36,17 +33,17 @@ if put_database_result["ok"]: print(f'"{example_db_name}" database created.') except ApiException as ae: - if ae.code == 412: + if ae.status_code == 412: print(f'Cannot create "{example_db_name}" database, ' + 'it already exists.') # 3. Create a document ================================================ # Create a document object with "example" id example_doc_id = "example" -# Setting `id` for the document is optional when "post_document" -# function is used for CREATE. When `id` is not provided the server +# Setting `_id` for the document is optional when "post_document" +# function is used for CREATE. When `_id` is not provided the server # will generate one for your document. -example_document: Document = Document(id=example_doc_id) +example_document: Document = Document(_id=example_doc_id) # Add "name" and "joined" fields to the document example_document.name = "Bob Smith" @@ -70,7 +67,5 @@ """ # ===================================================================== -# Keeping track of the revision number of the document object -# is necessary for further UPDATE/DELETE operations: -example_document.rev = create_document_response["rev"] -print(f'You have created the document:\n{example_document}') +response_body = json.dumps(create_document_response, indent=2) +print(f'You have created the document. Response body:\n{response_body}') diff --git a/test/examples/src/delete_doc.py b/test/examples/src/delete_doc.py index a345b7bc..5cc9461c 100644 --- a/test/examples/src/delete_doc.py +++ b/test/examples/src/delete_doc.py @@ -1,6 +1,6 @@ # coding: utf-8 -# © Copyright IBM Corporation 2020, 2022. +# © Copyright IBM Corporation 2020, 2023. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -14,14 +14,9 @@ # See the License for the specific language governing permissions and # limitations under the License. -import logging - from ibm_cloud_sdk_core import ApiException from ibmcloudant.cloudant_v1 import CloudantV1 -# Set logging level to show only critical logs -logging.basicConfig(level=logging.CRITICAL) - # 1. Create a client with `CLOUDANT` default service name ============= client = CloudantV1.new_instance() @@ -46,7 +41,7 @@ print('You have deleted the document.') except ApiException as ae: - if ae.code == 404: + if ae.status_code == 404: print('Cannot delete document because either ' + - f'"{example_db_name}" database or "{example_doc_id}"' + + f'"{example_db_name}" database or "{example_doc_id}" ' + 'document was not found.') diff --git a/test/examples/src/features/initialize.py b/test/examples/src/features/initialize.py new file mode 100644 index 00000000..48420e02 --- /dev/null +++ b/test/examples/src/features/initialize.py @@ -0,0 +1,30 @@ +# © Copyright IBM Corporation 2023. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from ibmcloudant import ChangesFollower +from ibmcloudant.cloudant_v1 import CloudantV1 + +client = CloudantV1.new_instance() + +cf_params = { + 'db': 'example', # Required: the database name. + 'limit': 100, # Optional: return only 100 changes (including duplicates). + 'since': '3-g1AG3...' # Optional: start from this sequence ID (e.g. with a value read from persistent storage). +} + +changes_follower = ChangesFollower( + service=client, # Required: the Cloudant service client instance. + error_tolerance=10000, # Optional: suppress transient errors for at least 10 seconds before terminating. + **cf_params # Required: changes feed configuration options dict. +) diff --git a/test/examples/src/features/pagination/all_docs_pagination.py b/test/examples/src/features/pagination/all_docs_pagination.py new file mode 100644 index 00000000..3373f3cd --- /dev/null +++ b/test/examples/src/features/pagination/all_docs_pagination.py @@ -0,0 +1,60 @@ +# © Copyright IBM Corporation 2025. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on +# an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the +# specific language governing permissions and limitations under the License. + +from ibmcloudant import Pager, Pagination, PagerType +from ibmcloudant.cloudant_v1 import CloudantV1 + +# Initialize service +service = CloudantV1.new_instance() + +# Setup options +opts = { + 'db': 'orders', # example database name + 'limit': 50, # limit option sets the page size, + 'start_key': 'abc' # start from example doc ID abc +} + +# Create pagination +pagination = Pagination.new_pagination( + service, PagerType.POST_ALL_DOCS, **opts) +# pagination can be reused without side-effects as a factory for iterables or pagers +# options are fixed at pagination creation time + +# Option: iterate pages +# Ideal for using a for loop with each page. +# Each call to pages() returns a fresh iterator that can be traversed once. +for page in pagination.pages(): + # Do something with page + pass + +# Option: iterate rows +# Ideal for using a for loop with each row. +# Each call to rows() returns a fresh iterator that can be traversed once. +for row in pagination.rows(): + # Do something with row + pass + +# Option: use pager next page +# For retrieving one page at a time with a method call. +pager: Pager = pagination.pager() +if pager.has_next(): + page = pager.get_next() + # Do something with page + +# Option: use pager all results +# For retrieving all result rows in a single list +# Note: all result rows may be very large! +# Preferably use iterables instead of get_all for memory efficiency with large result sets. +all_pager: Pager = pagination.pager() +all_rows = all_pager.get_all() +for page in all_rows: + # Do something with row + pass diff --git a/test/examples/src/features/pagination/design_docs_pagination.py b/test/examples/src/features/pagination/design_docs_pagination.py new file mode 100644 index 00000000..7e7ed6c7 --- /dev/null +++ b/test/examples/src/features/pagination/design_docs_pagination.py @@ -0,0 +1,59 @@ +# © Copyright IBM Corporation 2025. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on +# an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the +# specific language governing permissions and limitations under the License. + +from ibmcloudant import Pager, Pagination, PagerType +from ibmcloudant.cloudant_v1 import CloudantV1 + +# Initialize service +service = CloudantV1.new_instance() + +# Setup options +opts = { + 'db': 'shoppers', # example database name + 'limit': 50 # limit option sets the page size +} + +# Create pagination +pagination = Pagination.new_pagination( + service, PagerType.POST_DESIGN_DOCS, **opts) +# pagination can be reused without side-effects as a factory for iterables or pagers +# options are fixed at pagination creation time + +# Option: iterate pages +# Ideal for using a for loop with each page. +# Each call to pages() returns a fresh iterator that can be traversed once. +for page in pagination.pages(): + # Do something with page + pass + +# Option: iterate rows +# Ideal for using a for loop with each row. +# Each call to rows() returns a fresh iterator that can be traversed once. +for row in pagination.rows(): + # Do something with row + pass + +# Option: use pager next page +# For retrieving one page at a time with a method call. +pager: Pager = pagination.pager() +if pager.has_next(): + page = pager.get_next() + # Do something with page + +# Option: use pager all results +# For retrieving all result rows in a single list +# Note: all result rows may be very large! +# Preferably use iterables instead of get_all for memory efficiency with large result sets. +all_pager: Pager = pagination.pager() +all_rows = all_pager.get_all() +for page in all_rows: + # Do something with row + pass diff --git a/test/examples/src/features/pagination/find_pagination.py b/test/examples/src/features/pagination/find_pagination.py new file mode 100644 index 00000000..26b2d337 --- /dev/null +++ b/test/examples/src/features/pagination/find_pagination.py @@ -0,0 +1,62 @@ +# © Copyright IBM Corporation 2025. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on +# an 'AS IS' BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the +# specific language governing permissions and limitations under the License. + +from ibmcloudant import Pager, Pagination, PagerType +from ibmcloudant.cloudant_v1 import CloudantV1 + +# Initialize service +service = CloudantV1.new_instance() + +# Setup options +opts = { + 'db': 'shoppers', # example database name + 'limit': 50, # limit option sets the page size + 'fields': ['_id', 'type', 'name', 'email'], # return these fields + 'selector': {'email_verified': True}, # select docs with verified emails + 'sort': [{'email': 'desc'}] # sort descending by email +} + +# Create pagination +pagination = Pagination.new_pagination( + service, PagerType.POST_FIND, **opts) +# pagination can be reused without side-effects as a factory for iterables or pagers +# options are fixed at pagination creation time + +# Option: iterate pages +# Ideal for using a for loop with each page. +# Each call to pages() returns a fresh iterator that can be traversed once. +for page in pagination.pages(): + # Do something with page + pass + +# Option: iterate rows +# Ideal for using a for loop with each row. +# Each call to rows() returns a fresh iterator that can be traversed once. +for row in pagination.rows(): + # Do something with row + pass + +# Option: use pager next page +# For retrieving one page at a time with a method call. +pager: Pager = pagination.pager() +if pager.has_next(): + page = pager.get_next() + # Do something with page + +# Option: use pager all results +# For retrieving all result rows in a single list +# Note: all result rows may be very large! +# Preferably use iterables instead of get_all for memory efficiency with large result sets. +all_pager: Pager = pagination.pager() +all_rows = all_pager.get_all() +for page in all_rows: + # Do something with row + pass diff --git a/test/examples/src/features/pagination/partition_all_docs_pagination.py b/test/examples/src/features/pagination/partition_all_docs_pagination.py new file mode 100644 index 00000000..6a4c3ecc --- /dev/null +++ b/test/examples/src/features/pagination/partition_all_docs_pagination.py @@ -0,0 +1,60 @@ +# © Copyright IBM Corporation 2025. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on +# an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the +# specific language governing permissions and limitations under the License. + +from ibmcloudant import Pager, Pagination, PagerType +from ibmcloudant.cloudant_v1 import CloudantV1 + +# Initialize service +service = CloudantV1.new_instance() + +# Setup options +opts = { + 'db': 'events', # example database name + 'limit': 50, # limit option sets the page size + 'partition_key': 'ns1HJS13AMkK', # query only this partition +} + +# Create pagination +pagination = Pagination.new_pagination( + service, PagerType.POST_PARTITION_ALL_DOCS, **opts) +# pagination can be reused without side-effects as a factory for iterables or pagers +# options are fixed at pagination creation time + +# Option: iterate pages +# Ideal for using a for loop with each page. +# Each call to pages() returns a fresh iterator that can be traversed once. +for page in pagination.pages(): + # Do something with page + pass + +# Option: iterate rows +# Ideal for using a for loop with each row. +# Each call to rows() returns a fresh iterator that can be traversed once. +for row in pagination.rows(): + # Do something with row + pass + +# Option: use pager next page +# For retrieving one page at a time with a method call. +pager: Pager = pagination.pager() +if pager.has_next(): + page = pager.get_next() + # Do something with page + +# Option: use pager all results +# For retrieving all result rows in a single list +# Note: all result rows may be very large! +# Preferably use iterables instead of get_all for memory efficiency with large result sets. +all_pager: Pager = pagination.pager() +all_rows = all_pager.get_all() +for page in all_rows: + # Do something with row + pass diff --git a/test/examples/src/features/pagination/partition_find_pagination.py b/test/examples/src/features/pagination/partition_find_pagination.py new file mode 100644 index 00000000..db3aacb9 --- /dev/null +++ b/test/examples/src/features/pagination/partition_find_pagination.py @@ -0,0 +1,62 @@ +# © Copyright IBM Corporation 2025. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on +# an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the +# specific language governing permissions and limitations under the License. + +from ibmcloudant import Pager, Pagination, PagerType +from ibmcloudant.cloudant_v1 import CloudantV1 + +# Initialize service +service = CloudantV1.new_instance() + +# Setup options +opts = { + 'db': 'events', # example database name + 'limit': 50, # limit option sets the page size + 'partition_key': 'ns1HJS13AMkK', # query only this partition + 'fields': ['productId', 'eventType', 'date'], # return these fields + 'selector': {'userId': 'abc123'} # select documents with "userId" field equal to "abc123" +} + +# Create pagination +pagination = Pagination.new_pagination( + service, PagerType.POST_PARTITION_FIND, **opts) +# pagination can be reused without side-effects as a factory for iterables or pagers +# options are fixed at pagination creation time + +# Option: iterate pages +# Ideal for using a for loop with each page. +# Each call to pages() returns a fresh iterator that can be traversed once. +for page in pagination.pages(): + # Do something with page + pass + +# Option: iterate rows +# Ideal for using a for loop with each row. +# Each call to rows() returns a fresh iterator that can be traversed once. +for row in pagination.rows(): + # Do something with row + pass + +# Option: use pager next page +# For retrieving one page at a time with a method call. +pager: Pager = pagination.pager() +if pager.has_next(): + page = pager.get_next() + # Do something with page + +# Option: use pager all results +# For retrieving all result rows in a single list +# Note: all result rows may be very large! +# Preferably use iterables instead of get_all for memory efficiency with large result sets. +all_pager: Pager = pagination.pager() +all_rows = all_pager.get_all() +for page in all_rows: + # Do something with row + pass diff --git a/test/examples/src/features/pagination/partition_search_pagination.py b/test/examples/src/features/pagination/partition_search_pagination.py new file mode 100644 index 00000000..9347be48 --- /dev/null +++ b/test/examples/src/features/pagination/partition_search_pagination.py @@ -0,0 +1,63 @@ +# © Copyright IBM Corporation 2025. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on +# an 'AS IS' BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the +# specific language governing permissions and limitations under the License. + +from ibmcloudant import Pager, Pagination, PagerType +from ibmcloudant.cloudant_v1 import CloudantV1 + +# Initialize service +service = CloudantV1.new_instance() + +# Setup options +opts = { + 'db': 'events', # example database name + 'limit': 50, # limit option sets the page size + 'partition_key': 'ns1HJS13AMkK', # query only this partition + 'ddoc': 'checkout', # use the allUsers design document + 'index': 'findByDate', # search in this index + 'query': 'date:[2019-01-01T12:00:00.000Z TO 2019-01-31T12:00:00.000Z]' # Lucene search query +} + +# Create pagination +pagination = Pagination.new_pagination( + service, PagerType.POST_PARTITION_SEARCH, **opts) +# pagination can be reused without side-effects as a factory for iterables or pagers +# options are fixed at pagination creation time + +# Option: iterate pages +# Ideal for using a for loop with each page. +# Each call to pages() returns a fresh iterator that can be traversed once. +for page in pagination.pages(): + # Do something with page + pass + +# Option: iterate rows +# Ideal for using a for loop with each row. +# Each call to rows() returns a fresh iterator that can be traversed once. +for row in pagination.rows(): + # Do something with row + pass + +# Option: use pager next page +# For retrieving one page at a time with a method call. +pager: Pager = pagination.pager() +if pager.has_next(): + page = pager.get_next() + # Do something with page + +# Option: use pager all results +# For retrieving all result rows in a single list +# Note: all result rows may be very large! +# Preferably use iterables instead of get_all for memory efficiency with large result sets. +all_pager: Pager = pagination.pager() +all_rows = all_pager.get_all() +for page in all_rows: + # Do something with row + pass diff --git a/test/examples/src/features/pagination/partition_view_pagination.py b/test/examples/src/features/pagination/partition_view_pagination.py new file mode 100644 index 00000000..df9143cc --- /dev/null +++ b/test/examples/src/features/pagination/partition_view_pagination.py @@ -0,0 +1,62 @@ +# © Copyright IBM Corporation 2025. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on +# an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the +# specific language governing permissions and limitations under the License. + +from ibmcloudant import Pager, Pagination, PagerType +from ibmcloudant.cloudant_v1 import CloudantV1 + +# Initialize service +service = CloudantV1.new_instance() + +# Setup options +opts = { + 'db': 'events', # example database name + 'limit': 50, # limit option sets the page size + 'partition_key': 'ns1HJS13AMkK', # query only this partition + 'ddoc': 'checkout', # use the checkout design document + 'view': 'byProductId' # the view to use +} + +# Create pagination +pagination = Pagination.new_pagination( + service, PagerType.POST_PARTITION_VIEW, **opts) +# pagination can be reused without side-effects as a factory for iterables or pagers +# options are fixed at pagination creation time + +# Option: iterate pages +# Ideal for using a for loop with each page. +# Each call to pages() returns a fresh iterator that can be traversed once. +for page in pagination.pages(): + # Do something with page + pass + +# Option: iterate rows +# Ideal for using a for loop with each row. +# Each call to rows() returns a fresh iterator that can be traversed once. +for row in pagination.rows(): + # Do something with row + pass + +# Option: use pager next page +# For retrieving one page at a time with a method call. +pager: Pager = pagination.pager() +if pager.has_next(): + page = pager.get_next() + # Do something with page + +# Option: use pager all results +# For retrieving all result rows in a single list +# Note: all result rows may be very large! +# Preferably use iterables instead of get_all for memory efficiency with large result sets. +all_pager: Pager = pagination.pager() +all_rows = all_pager.get_all() +for page in all_rows: + # Do something with row + pass diff --git a/test/examples/src/features/pagination/search_pagination.py b/test/examples/src/features/pagination/search_pagination.py new file mode 100644 index 00000000..7dcd4550 --- /dev/null +++ b/test/examples/src/features/pagination/search_pagination.py @@ -0,0 +1,62 @@ +# © Copyright IBM Corporation 2025. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on +# an 'AS IS' BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the +# specific language governing permissions and limitations under the License. + +from ibmcloudant import Pager, Pagination, PagerType +from ibmcloudant.cloudant_v1 import CloudantV1 + +# Initialize service +service = CloudantV1.new_instance() + +# Setup options +opts = { + 'db': 'shoppers', # example database name + 'limit': 50, # limit option sets the page size + 'ddoc': 'allUsers', # use the allUsers design document + 'index': 'activeUsers', # search in this index + 'query': 'name:Jane* AND active:True' # Lucene search query +} + +# Create pagination +pagination = Pagination.new_pagination( + service, PagerType.POST_SEARCH, **opts) +# pagination can be reused without side-effects as a factory for iterables or pagers +# options are fixed at pagination creation time + +# Option: iterate pages +# Ideal for using a for loop with each page. +# Each call to pages() returns a fresh iterator that can be traversed once. +for page in pagination.pages(): + # Do something with page + pass + +# Option: iterate rows +# Ideal for using a for loop with each row. +# Each call to rows() returns a fresh iterator that can be traversed once. +for row in pagination.rows(): + # Do something with row + pass + +# Option: use pager next page +# For retrieving one page at a time with a method call. +pager: Pager = pagination.pager() +if pager.has_next(): + page = pager.get_next() + # Do something with page + +# Option: use pager all results +# For retrieving all result rows in a single list +# Note: all result rows may be very large! +# Preferably use iterables instead of get_all for memory efficiency with large result sets. +all_pager: Pager = pagination.pager() +all_rows = all_pager.get_all() +for page in all_rows: + # Do something with row + pass diff --git a/test/examples/src/features/pagination/view_pagination.py b/test/examples/src/features/pagination/view_pagination.py new file mode 100644 index 00000000..53bff03d --- /dev/null +++ b/test/examples/src/features/pagination/view_pagination.py @@ -0,0 +1,61 @@ +# © Copyright IBM Corporation 2025. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on +# an 'AS IS' BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the +# specific language governing permissions and limitations under the License. + +from ibmcloudant import Pager, Pagination, PagerType +from ibmcloudant.cloudant_v1 import CloudantV1 + +# Initialize service +service = CloudantV1.new_instance() + +# Setup options +opts = { + 'db': 'shoppers', # example database name + 'limit': 50, # limit option sets the page size + 'ddoc': 'allUsers', # use the allUsers design document + 'view': 'getVerifiedEmails' # the view to use +} + +# Create pagination +pagination = Pagination.new_pagination( + service, PagerType.POST_VIEW, **opts) +# pagination can be reused without side-effects as a factory for iterables or pagers +# options are fixed at pagination creation time + +# Option: iterate pages +# Ideal for using a for loop with each page. +# Each call to pages() returns a fresh iterator that can be traversed once. +for page in pagination.pages(): + # Do something with page + pass + +# Option: iterate rows +# Ideal for using a for loop with each row. +# Each call to rows() returns a fresh iterator that can be traversed once. +for row in pagination.rows(): + # Do something with row + pass + +# Option: use pager next page +# For retrieving one page at a time with a method call. +pager: Pager = pagination.pager() +if pager.has_next(): + page = pager.get_next() + # Do something with page + +# Option: use pager all results +# For retrieving all result rows in a single list +# Note: all result rows may be very large! +# Preferably use iterables instead of get_all for memory efficiency with large result sets. +all_pager: Pager = pagination.pager() +all_rows = all_pager.get_all() +for page in all_rows: + # Do something with row + pass diff --git a/test/examples/src/features/start.py b/test/examples/src/features/start.py new file mode 100644 index 00000000..bb78ebbe --- /dev/null +++ b/test/examples/src/features/start.py @@ -0,0 +1,29 @@ +# © Copyright IBM Corporation 2023. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from collections.abc import Iterable + +from ibmcloudant import ChangesFollower +from ibmcloudant.cloudant_v1 import CloudantV1, ChangesResultItem + +client = CloudantV1.new_instance() + +changes_follower = ChangesFollower( + service=client, + **{'db': 'example'}) + +changes_items: Iterable[ChangesResultItem] = changes_follower.start() +# Note: iterable will not do anything until it is iterated +# Create a for loop to iterate over the flow of changes +# for changes_item in changes_items: ... diff --git a/test/examples/src/features/start_and_process.py b/test/examples/src/features/start_and_process.py new file mode 100644 index 00000000..997ab87d --- /dev/null +++ b/test/examples/src/features/start_and_process.py @@ -0,0 +1,42 @@ +# © Copyright IBM Corporation 2023. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from ibmcloudant import ChangesFollower +from ibmcloudant.cloudant_v1 import CloudantV1 + +client = CloudantV1.new_instance() + +# Start from a previously persisted seq +# Normally this would be read by the app from persistent storage +# e.g. previously_persisted_seq = your_app_persistence_read_func() +previously_persisted_seq = '3-g1AG3...' +changes_follower = ChangesFollower( + service=client, + **{'db': 'example', 'since': previously_persisted_seq}) + +changes_items = changes_follower.start() +for changes_item in changes_items: + # do something with changes + print(changes_item.id) + for change in changes_item.changes: + print(change.rev) + # when change item processing is complete app can store seq + seq = changes_item.seq + # write seq to persistent storage for use as since if required to resume later + # e.g. your_app_persistence_write_func(seq) + # keep processing changes until the application is terminated or some other stop condition is reached + +# Note: iterator above is blocking, code here will be unreachable +# until the iteration is stopped or another stop condition is reached. +# For long running followers careful consideration should be made of where to call stop on the iterator. diff --git a/test/examples/src/features/start_one_off.py b/test/examples/src/features/start_one_off.py new file mode 100644 index 00000000..bdb615f1 --- /dev/null +++ b/test/examples/src/features/start_one_off.py @@ -0,0 +1,29 @@ +# © Copyright IBM Corporation 2023. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from collections.abc import Iterable + +from ibmcloudant import ChangesFollower +from ibmcloudant.cloudant_v1 import CloudantV1, ChangesResultItem + +client = CloudantV1.new_instance() + +changes_follower = ChangesFollower( + service=client, + **{'db': 'example'}) + +changes_items: Iterable[ChangesResultItem] = changes_follower.start_one_off() +# Note: iterable will not do anything until it is iterated +# Create a for loop to iterate over the flow of changes +# for changes_item in changes_items: ... diff --git a/test/examples/src/features/start_one_off_and_process.py b/test/examples/src/features/start_one_off_and_process.py new file mode 100644 index 00000000..29fc905d --- /dev/null +++ b/test/examples/src/features/start_one_off_and_process.py @@ -0,0 +1,40 @@ +# © Copyright IBM Corporation 2023. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from ibmcloudant import ChangesFollower +from ibmcloudant.cloudant_v1 import CloudantV1 + +client = CloudantV1.new_instance() + +# Start from a previously persisted seq +# Normally this would be read by the app from persistent storage +# e.g. previously_persisted_seq = your_app_persistence_read_func() +previously_persisted_seq = '3-g1AG3...' +changes_follower = ChangesFollower( + service=client, + **{'db': 'example', 'since': previously_persisted_seq}) + +changes_items = changes_follower.start_one_off() +for changes_item in changes_items: + # do something with changes + print(changes_item.id) + for change in changes_item.changes: + print(change.rev) + # when change item processing is complete app can store seq + seq = changes_item.seq + # write seq to persistent storage for use as since if required to resume later + # e.g. your_app_persistence_write_func(seq) + +# Note: iterator above is blocking, code here will be unreachable +# until all changes are processed (or another stop condition is reached). diff --git a/test/examples/src/features/stop.py b/test/examples/src/features/stop.py new file mode 100644 index 00000000..90de2a38 --- /dev/null +++ b/test/examples/src/features/stop.py @@ -0,0 +1,37 @@ +# © Copyright IBM Corporation 2023. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from ibmcloudant import ChangesFollower +from ibmcloudant.cloudant_v1 import CloudantV1 + +client = CloudantV1.new_instance() +changes_follower = ChangesFollower( + service=client, + **{'db': 'example'}) +changes_items = changes_follower.start() + +for changes_item in changes_items: + # Option 1: call stop after some condition + # Note that since the iterator is blocking at least one item + # must be returned from it to reach to this point. + # Additional changes may be processed before the iterator stops. + changes_follower.stop() + +# Option 2: call stop method when you want to end the continuous loop from +# outside the iterator. For example, you've put the changes follower in a +# separate thread and need to call stop on the main thread. +# Note: in this context the call must be made from a different thread because +# code immediately following the iterator is unreachable until the iterator +# has stopped. +changes_follower.stop() diff --git a/test/examples/src/get_info_from_existing_database.py b/test/examples/src/get_info_from_existing_database.py index a789371d..08ad38bd 100644 --- a/test/examples/src/get_info_from_existing_database.py +++ b/test/examples/src/get_info_from_existing_database.py @@ -18,8 +18,8 @@ from ibmcloudant.cloudant_v1 import CloudantV1 -# 1. Create a Cloudant client with "EXAMPLES" service name ============ -client = CloudantV1.new_instance(service_name="EXAMPLES") +# 1. Create a client with `CLOUDANT` default service name ============ +client = CloudantV1.new_instance() # 2. Get server information =========================================== server_information = client.get_server_information( @@ -27,8 +27,8 @@ print(f'Server Version: {server_information["version"]}') -# 3. Get database information for "animaldb" ========================== -db_name = "animaldb" +# 3. Get database information for "orders" ========================== +db_name = "orders" db_information = client.get_database_information( db=db_name @@ -40,11 +40,11 @@ print(f'Document count in \"{db_information["db_name"]}\" ' f'database is {document_count}.') -# 5. Get zebra document out of the database by document id ============ -document_about_zebra = client.get_document( +# 5. Get "example" document out of the database by document id ============ +document_example = client.get_document( db=db_name, - doc_id="zebra" + doc_id="example" ).get_result() print(f'Document retrieved from database:\n' - f'{json.dumps(document_about_zebra, indent=2)}') + f'{json.dumps(document_example, indent=2)}') diff --git a/test/examples/src/test_readme.py b/test/examples/src/test_readme.py new file mode 100644 index 00000000..b8f5da38 --- /dev/null +++ b/test/examples/src/test_readme.py @@ -0,0 +1,110 @@ +# © Copyright IBM Corporation 2025. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + + +# Testsuite for the examples in the README file. + +import unittest +import subprocess +import sys +import os +import requests +from pathlib import Path + +parent_dir = Path(__file__).resolve().parent.parent + + +class TestReadmeExamples(unittest.TestCase): + + @classmethod + def setUpClass(cls): + """Set up test environment before all tests""" + # Get WireMock URL from environment + wiremock_url = os.environ.get('WIREMOCK_URL') + + # Reset WireMock scenarios + requests.post(f"{wiremock_url}/__admin/scenarios/reset") + + # Set authentication environment variables + os.environ['CLOUDANT_URL'] = wiremock_url + os.environ['CLOUDANT_AUTH_TYPE'] = 'noauth' + + def run_example_and_check_output(self, script_path, expected_output_path): + env = os.environ.copy() + + # Ensure the subprocess uses the same site-packages + python_path = sys.path.copy() + if 'PYTHONPATH' in env: + python_path.append(env['PYTHONPATH']) + env['PYTHONPATH'] = os.pathsep.join(python_path) + + result = subprocess.run( + [sys.executable, str(script_path)], + capture_output=True, + text=True, + timeout=5, + env=env, + ) + if result.returncode != 0: + self.fail(f"Script failed with return code {result.returncode}\n" + f"STDOUT:\n{result.stdout}\n" + f"STDERR:\n{result.stderr}") + + with open(expected_output_path, 'r') as f: + expected_output = f.read().strip() + + actual_output = result.stdout.strip() + + self.assertEqual(actual_output, expected_output, + f"Output mismatch.\nExpected:\n{expected_output}\n\nGot:\n{actual_output}") + + def test_1_create_db_and_doc_example(self): + """Creates db and doc for the first time""" + script = parent_dir / "src" / "create_db_and_doc.py" + output = parent_dir / "output" / "create_db_and_doc.txt" + self.run_example_and_check_output(script, output) + + def test_2_get_info_from_existing_database_example(self): + """Gets document from orders database""" + script = parent_dir / "src" / "get_info_from_existing_database.py" + output = parent_dir / "output" / "get_info_from_existing_database.txt" + self.run_example_and_check_output(script, output) + + def test_3_update_doc_example_first_time(self): + """Updates doc for the first time""" + script = parent_dir / "src" / "update_doc.py" + output = parent_dir / "output" / "update_doc.txt" + self.run_example_and_check_output(script, output) + + def test_4_update_doc_example_second_time(self): + """Updates doc for the second time""" + script = parent_dir / "src" / "update_doc.py" + output = parent_dir / "output" / "update_doc2.txt" + self.run_example_and_check_output(script, output) + + def test_5_delete_doc_example_existing(self): + """Deletes existing doc""" + script = parent_dir / "src" / "delete_doc.py" + output = parent_dir / "output" / "delete_doc.txt" + self.run_example_and_check_output(script, output) + + def test_6_delete_doc_example_non_existing(self): + """Deletes non-existing doc""" + script = parent_dir / "src" / "delete_doc.py" + output = parent_dir / "output" / "delete_doc2.txt" + self.run_example_and_check_output(script, output) + + +if __name__ == '__main__': + unittest.main() diff --git a/test/examples/src/update_doc.py b/test/examples/src/update_doc.py index d15232ce..82170758 100644 --- a/test/examples/src/update_doc.py +++ b/test/examples/src/update_doc.py @@ -1,6 +1,6 @@ # coding: utf-8 -# © Copyright IBM Corporation 2020, 2022. +# © Copyright IBM Corporation 2020, 2023. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -15,14 +15,10 @@ # limitations under the License. import json -import logging from ibm_cloud_sdk_core import ApiException from ibmcloudant.cloudant_v1 import CloudantV1 -# Set logging level to show only critical logs -logging.basicConfig(level=logging.CRITICAL) - # 1. Create a client with `CLOUDANT` default service name ============= client = CloudantV1.new_instance() @@ -92,7 +88,7 @@ json.dumps(document, indent=2)) except ApiException as ae: - if ae.code == 404: - print('Cannot delete document because either ' + + if ae.status_code == 404: + print('Cannot update document because either ' + f'"{example_db_name}" database or "{example_doc_id}" ' + 'document was not found.') diff --git a/test/integration/test_cloudant_v1.py b/test/integration/test_cloudant_v1.py new file mode 100644 index 00000000..0fbb061a --- /dev/null +++ b/test/integration/test_cloudant_v1.py @@ -0,0 +1,2166 @@ +# -*- coding: utf-8 -*- +# (C) Copyright IBM Corp. 2026. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +""" +Integration Tests for CloudantV1 +""" + +from ibm_cloud_sdk_core import * +import io +import os +import pytest +from ibmcloudant.cloudant_v1 import * + +# Config file name +config_file = 'cloudant_v1.env' + + +class TestCloudantV1: + """ + Integration Test Class for CloudantV1 + """ + + @classmethod + def setup_class(cls): + if os.path.exists(config_file): + os.environ['IBM_CREDENTIALS_FILE'] = config_file + + cls.cloudant_service = CloudantV1.new_instance( + ) + assert cls.cloudant_service is not None + + cls.config = read_external_sources(CloudantV1.DEFAULT_SERVICE_NAME) + assert cls.config is not None + + cls.cloudant_service.enable_retries() + + print('Setup complete.') + + needscredentials = pytest.mark.skipif( + not os.path.exists(config_file), reason="External configuration not available, skipping..." + ) + + @needscredentials + def test_get_server_information(self): + response = self.cloudant_service.get_server_information() + + assert response.get_status_code() == 200 + server_information = response.get_result() + assert server_information is not None + + @needscredentials + def test_get_capacity_throughput_information(self): + response = self.cloudant_service.get_capacity_throughput_information() + + assert response.get_status_code() == 200 + capacity_throughput_information = response.get_result() + assert capacity_throughput_information is not None + + @needscredentials + def test_put_capacity_throughput_configuration(self): + response = self.cloudant_service.put_capacity_throughput_configuration( + blocks=10, + ) + + assert response.get_status_code() == 200 + capacity_throughput_information = response.get_result() + assert capacity_throughput_information is not None + + @needscredentials + def test_get_uuids(self): + response = self.cloudant_service.get_uuids( + count=1, + ) + + assert response.get_status_code() == 200 + uuids_result = response.get_result() + assert uuids_result is not None + + @needscredentials + def test_get_db_updates(self): + response = self.cloudant_service.get_db_updates( + descending=False, + feed='normal', + heartbeat=1, + limit=0, + timeout=60000, + since='0', + ) + + assert response.get_status_code() == 200 + db_updates = response.get_result() + assert db_updates is not None + + @needscredentials + def test_post_changes(self): + response = self.cloudant_service.post_changes( + db='testString', + doc_ids=['0007741142412418284'], + fields=['testString'], + selector={'anyKey': 'anyValue'}, + last_event_id='testString', + att_encoding_info=False, + attachments=False, + conflicts=False, + descending=False, + feed='normal', + filter='testString', + heartbeat=1, + include_docs=False, + limit=0, + seq_interval=1, + since='0', + style='main_only', + timeout=60000, + view='testString', + ) + + assert response.get_status_code() == 200 + changes_result = response.get_result() + assert changes_result is not None + + @needscredentials + def test_post_changes_as_stream(self): + response = self.cloudant_service.post_changes_as_stream( + db='testString', + doc_ids=['0007741142412418284'], + fields=['testString'], + selector={'anyKey': 'anyValue'}, + last_event_id='testString', + att_encoding_info=False, + attachments=False, + conflicts=False, + descending=False, + feed='normal', + filter='testString', + heartbeat=1, + include_docs=False, + limit=0, + seq_interval=1, + since='0', + style='main_only', + timeout=60000, + view='testString', + ) + + assert response.get_status_code() == 200 + result = response.get_result() + assert result is not None + + @needscredentials + def test_head_database(self): + response = self.cloudant_service.head_database( + db='testString', + ) + + assert response.get_status_code() == 200 + + @needscredentials + def test_get_all_dbs(self): + response = self.cloudant_service.get_all_dbs( + descending=False, + end_key='testString', + limit=0, + skip=0, + start_key='testString', + ) + + assert response.get_status_code() == 200 + result = response.get_result() + assert result is not None + + @needscredentials + def test_post_dbs_info(self): + response = self.cloudant_service.post_dbs_info( + keys=['products', 'users', 'orders'], + ) + + assert response.get_status_code() == 200 + list_dbs_info_result = response.get_result() + assert list_dbs_info_result is not None + + @needscredentials + def test_get_database_information(self): + response = self.cloudant_service.get_database_information( + db='testString', + ) + + assert response.get_status_code() == 200 + database_information = response.get_result() + assert database_information is not None + + @needscredentials + def test_put_database(self): + response = self.cloudant_service.put_database( + db='testString', + partitioned=False, + q=16, + ) + + assert response.get_status_code() == 201 + ok = response.get_result() + assert ok is not None + + @needscredentials + def test_head_document(self): + response = self.cloudant_service.head_document( + db='testString', + doc_id='testString', + if_none_match='testString', + latest=False, + rev='testString', + ) + + assert response.get_status_code() == 200 + + @needscredentials + def test_post_document(self): + # Construct a dict representation of a Attachment model + attachment_model = { + 'content_type': 'testString', + 'data': 'VGhpcyBpcyBhIG1vY2sgYnl0ZSBhcnJheSB2YWx1ZS4=', + 'digest': 'testString', + 'encoded_length': 0, + 'encoding': 'testString', + 'follows': True, + 'length': 0, + 'revpos': 1, + 'stub': True, + } + # Construct a dict representation of a Revisions model + revisions_model = { + 'ids': ['testString'], + 'start': 1, + } + # Construct a dict representation of a DocumentRevisionStatus model + document_revision_status_model = { + 'rev': 'testString', + 'status': 'available', + } + # Construct a dict representation of a Document model + document_model = { + '_attachments': {'key1': attachment_model}, + '_conflicts': ['testString'], + '_deleted': True, + '_deleted_conflicts': ['testString'], + '_id': 'exampleid', + '_local_seq': 'testString', + '_rev': 'testString', + '_revisions': revisions_model, + '_revs_info': [document_revision_status_model], + 'brand': 'Foo', + 'colours': '["red","green","black","blue"]', + 'description': 'Slim Colourful Design Electronic Cooking Appliance for ...', + 'image': 'assets/img/0gmsnghhew.jpg', + 'keywords': '["Foo","Scales","Weight","Digital","Kitchen"]', + 'name': 'Digital Kitchen Scales', + 'price': '14.99', + 'productId': '1000042', + 'taxonomy': '["Home","Kitchen","Small Appliances"]', + 'type': 'product', + } + + response = self.cloudant_service.post_document( + db='testString', + document=document_model, + content_type='application/json', + batch='ok', + ) + + assert response.get_status_code() == 201 + document_result = response.get_result() + assert document_result is not None + + @needscredentials + def test_post_all_docs(self): + response = self.cloudant_service.post_all_docs( + db='testString', + att_encoding_info=False, + attachments=False, + conflicts=False, + descending=False, + include_docs=False, + inclusive_end=True, + limit=10, + skip=0, + update_seq=False, + end_key='testString', + key='testString', + keys=['testString'], + start_key='0007741142412418284', + ) + + assert response.get_status_code() == 200 + all_docs_result = response.get_result() + assert all_docs_result is not None + + @needscredentials + def test_post_all_docs_as_stream(self): + response = self.cloudant_service.post_all_docs_as_stream( + db='testString', + att_encoding_info=False, + attachments=False, + conflicts=False, + descending=False, + include_docs=False, + inclusive_end=True, + limit=10, + skip=0, + update_seq=False, + end_key='testString', + key='testString', + keys=['testString'], + start_key='0007741142412418284', + ) + + assert response.get_status_code() == 200 + result = response.get_result() + assert result is not None + + @needscredentials + def test_post_all_docs_queries(self): + # Construct a dict representation of a AllDocsQuery model + all_docs_query_model = { + 'att_encoding_info': False, + 'attachments': False, + 'conflicts': False, + 'descending': False, + 'include_docs': False, + 'inclusive_end': True, + 'limit': 0, + 'skip': 0, + 'update_seq': False, + 'end_key': 'testString', + 'key': 'testString', + 'keys': ['small-appliances:1000042', 'small-appliances:1000043'], + 'start_key': 'testString', + } + + response = self.cloudant_service.post_all_docs_queries( + db='testString', + queries=[all_docs_query_model], + ) + + assert response.get_status_code() == 200 + all_docs_queries_result = response.get_result() + assert all_docs_queries_result is not None + + @needscredentials + def test_post_all_docs_queries_as_stream(self): + # Construct a dict representation of a AllDocsQuery model + all_docs_query_model = { + 'att_encoding_info': False, + 'attachments': False, + 'conflicts': False, + 'descending': False, + 'include_docs': False, + 'inclusive_end': True, + 'limit': 0, + 'skip': 0, + 'update_seq': False, + 'end_key': 'testString', + 'key': 'testString', + 'keys': ['small-appliances:1000042', 'small-appliances:1000043'], + 'start_key': 'testString', + } + + response = self.cloudant_service.post_all_docs_queries_as_stream( + db='testString', + queries=[all_docs_query_model], + ) + + assert response.get_status_code() == 200 + result = response.get_result() + assert result is not None + + @needscredentials + def test_post_bulk_docs(self): + # Construct a dict representation of a Attachment model + attachment_model = { + 'content_type': 'testString', + 'data': 'VGhpcyBpcyBhIG1vY2sgYnl0ZSBhcnJheSB2YWx1ZS4=', + 'digest': 'testString', + 'encoded_length': 0, + 'encoding': 'testString', + 'follows': True, + 'length': 0, + 'revpos': 1, + 'stub': True, + } + # Construct a dict representation of a Revisions model + revisions_model = { + 'ids': ['testString'], + 'start': 1, + } + # Construct a dict representation of a DocumentRevisionStatus model + document_revision_status_model = { + 'rev': 'testString', + 'status': 'available', + } + # Construct a dict representation of a Document model + document_model = { + '_attachments': {'key1': attachment_model}, + '_conflicts': ['testString'], + '_deleted': True, + '_deleted_conflicts': ['testString'], + '_id': '0007241142412418284', + '_local_seq': 'testString', + '_rev': 'testString', + '_revisions': revisions_model, + '_revs_info': [document_revision_status_model], + 'date': '2019-01-28T10:44:22.000Z', + 'eventType': 'addedToBasket', + 'productId': '1000042', + 'type': 'event', + 'userid': 'abc123', + } + # Construct a dict representation of a BulkDocs model + bulk_docs_model = { + 'docs': [document_model], + 'new_edits': True, + } + + response = self.cloudant_service.post_bulk_docs( + db='testString', + bulk_docs=bulk_docs_model, + ) + + assert response.get_status_code() == 201 + list_document_result = response.get_result() + assert list_document_result is not None + + @needscredentials + def test_post_bulk_get(self): + # Construct a dict representation of a BulkGetQueryDocument model + bulk_get_query_document_model = { + 'atts_since': ['1-99b02e08da151943c2dcb40090160bb8'], + 'id': 'order00067', + 'rev': '3-917fa2381192822767f010b95b45325b', + } + + response = self.cloudant_service.post_bulk_get( + db='testString', + docs=[bulk_get_query_document_model], + attachments=False, + att_encoding_info=False, + latest=False, + revs=False, + ) + + assert response.get_status_code() == 200 + bulk_get_result = response.get_result() + assert bulk_get_result is not None + + @needscredentials + def test_post_bulk_get_as_mixed(self): + # Construct a dict representation of a BulkGetQueryDocument model + bulk_get_query_document_model = { + 'atts_since': ['1-99b02e08da151943c2dcb40090160bb8'], + 'id': 'order00067', + 'rev': '3-917fa2381192822767f010b95b45325b', + } + + response = self.cloudant_service.post_bulk_get_as_mixed( + db='testString', + docs=[bulk_get_query_document_model], + attachments=False, + att_encoding_info=False, + latest=False, + revs=False, + ) + + assert response.get_status_code() == 200 + result = response.get_result() + assert result is not None + + @needscredentials + def test_post_bulk_get_as_related(self): + # Construct a dict representation of a BulkGetQueryDocument model + bulk_get_query_document_model = { + 'atts_since': ['1-99b02e08da151943c2dcb40090160bb8'], + 'id': 'order00067', + 'rev': '3-917fa2381192822767f010b95b45325b', + } + + response = self.cloudant_service.post_bulk_get_as_related( + db='testString', + docs=[bulk_get_query_document_model], + attachments=False, + att_encoding_info=False, + latest=False, + revs=False, + ) + + assert response.get_status_code() == 200 + result = response.get_result() + assert result is not None + + @needscredentials + def test_post_bulk_get_as_stream(self): + # Construct a dict representation of a BulkGetQueryDocument model + bulk_get_query_document_model = { + 'atts_since': ['1-99b02e08da151943c2dcb40090160bb8'], + 'id': 'order00067', + 'rev': '3-917fa2381192822767f010b95b45325b', + } + + response = self.cloudant_service.post_bulk_get_as_stream( + db='testString', + docs=[bulk_get_query_document_model], + attachments=False, + att_encoding_info=False, + latest=False, + revs=False, + ) + + assert response.get_status_code() == 200 + result = response.get_result() + assert result is not None + + @needscredentials + def test_get_document(self): + response = self.cloudant_service.get_document( + db='testString', + doc_id='testString', + if_none_match='testString', + attachments=False, + att_encoding_info=False, + conflicts=False, + deleted_conflicts=False, + latest=False, + local_seq=False, + meta=False, + rev='testString', + revs=False, + revs_info=False, + ) + + assert response.get_status_code() == 200 + document = response.get_result() + assert document is not None + + @needscredentials + def test_get_document_as_mixed(self): + response = self.cloudant_service.get_document_as_mixed( + db='testString', + doc_id='testString', + if_none_match='testString', + attachments=False, + att_encoding_info=False, + conflicts=False, + deleted_conflicts=False, + latest=False, + local_seq=False, + meta=False, + rev='testString', + revs=False, + revs_info=False, + ) + + assert response.get_status_code() == 200 + result = response.get_result() + assert result is not None + + @needscredentials + def test_get_document_as_related(self): + response = self.cloudant_service.get_document_as_related( + db='testString', + doc_id='testString', + if_none_match='testString', + attachments=False, + att_encoding_info=False, + conflicts=False, + deleted_conflicts=False, + latest=False, + local_seq=False, + meta=False, + rev='testString', + revs=False, + revs_info=False, + ) + + assert response.get_status_code() == 200 + result = response.get_result() + assert result is not None + + @needscredentials + def test_get_document_as_stream(self): + response = self.cloudant_service.get_document_as_stream( + db='testString', + doc_id='testString', + if_none_match='testString', + attachments=False, + att_encoding_info=False, + conflicts=False, + deleted_conflicts=False, + latest=False, + local_seq=False, + meta=False, + rev='testString', + revs=False, + revs_info=False, + ) + + assert response.get_status_code() == 200 + result = response.get_result() + assert result is not None + + @needscredentials + def test_put_document(self): + # Construct a dict representation of a Attachment model + attachment_model = { + 'content_type': 'testString', + 'data': 'VGhpcyBpcyBhIG1vY2sgYnl0ZSBhcnJheSB2YWx1ZS4=', + 'digest': 'testString', + 'encoded_length': 0, + 'encoding': 'testString', + 'follows': True, + 'length': 0, + 'revpos': 1, + 'stub': True, + } + # Construct a dict representation of a Revisions model + revisions_model = { + 'ids': ['testString'], + 'start': 1, + } + # Construct a dict representation of a DocumentRevisionStatus model + document_revision_status_model = { + 'rev': 'testString', + 'status': 'available', + } + # Construct a dict representation of a Document model + document_model = { + '_attachments': {'key1': attachment_model}, + '_conflicts': ['testString'], + '_deleted': True, + '_deleted_conflicts': ['testString'], + '_id': 'exampleid', + '_local_seq': 'testString', + '_rev': 'testString', + '_revisions': revisions_model, + '_revs_info': [document_revision_status_model], + 'brand': 'Foo', + 'colours': '["red","green","black","blue"]', + 'description': 'Slim Colourful Design Electronic Cooking Appliance for ...', + 'image': 'assets/img/0gmsnghhew.jpg', + 'keywords': '["Foo","Scales","Weight","Digital","Kitchen"]', + 'name': 'Digital Kitchen Scales', + 'price': '14.99', + 'productId': '1000042', + 'taxonomy': '["Home","Kitchen","Small Appliances"]', + 'type': 'product', + } + + response = self.cloudant_service.put_document( + db='testString', + doc_id='testString', + document=document_model, + content_type='application/json', + if_match='testString', + batch='ok', + new_edits=False, + rev='testString', + ) + + assert response.get_status_code() == 201 + document_result = response.get_result() + assert document_result is not None + + @needscredentials + def test_head_design_document(self): + response = self.cloudant_service.head_design_document( + db='testString', + ddoc='testString', + if_none_match='testString', + ) + + assert response.get_status_code() == 200 + + @needscredentials + def test_get_design_document(self): + response = self.cloudant_service.get_design_document( + db='testString', + ddoc='testString', + if_none_match='testString', + attachments=False, + att_encoding_info=False, + conflicts=False, + deleted_conflicts=False, + latest=False, + local_seq=False, + meta=False, + rev='testString', + revs=False, + revs_info=False, + ) + + assert response.get_status_code() == 200 + design_document = response.get_result() + assert design_document is not None + + @needscredentials + def test_put_design_document(self): + # Construct a dict representation of a Attachment model + attachment_model = { + 'content_type': 'testString', + 'data': 'VGhpcyBpcyBhIG1vY2sgYnl0ZSBhcnJheSB2YWx1ZS4=', + 'digest': 'testString', + 'encoded_length': 0, + 'encoding': 'testString', + 'follows': True, + 'length': 0, + 'revpos': 1, + 'stub': True, + } + # Construct a dict representation of a Revisions model + revisions_model = { + 'ids': ['testString'], + 'start': 1, + } + # Construct a dict representation of a DocumentRevisionStatus model + document_revision_status_model = { + 'rev': 'testString', + 'status': 'available', + } + # Construct a dict representation of a Analyzer model + analyzer_model = { + 'name': 'classic', + 'stopwords': ['testString'], + } + # Construct a dict representation of a AnalyzerConfiguration model + analyzer_configuration_model = { + 'name': 'standard', + 'stopwords': ['testString'], + 'default': analyzer_model, + 'fields': {'key1': analyzer_model}, + } + # Construct a dict representation of a SearchIndexDefinition model + search_index_definition_model = { + 'analyzer': analyzer_configuration_model, + 'index': 'function (doc) {\n index("price", doc.price);\n}', + } + # Construct a dict representation of a DesignDocumentOptions model + design_document_options_model = { + 'partitioned': True, + } + # Construct a dict representation of a DesignDocumentViewsMapReduce model + design_document_views_map_reduce_model = { + 'map': 'function(doc) {\n emit(doc.productId, [doc.brand, doc.name, doc.description])\n}', + 'reduce': 'testString', + } + # Construct a dict representation of a DesignDocument model + design_document_model = { + '_attachments': {'key1': attachment_model}, + '_conflicts': ['testString'], + '_deleted': True, + '_deleted_conflicts': ['testString'], + '_id': '_design/appliances', + '_local_seq': 'testString', + '_rev': '8-7e2537e5989294471061e0cfd7292725', + '_revisions': revisions_model, + '_revs_info': [document_revision_status_model], + 'autoupdate': True, + 'filters': {'key1': 'testString'}, + 'indexes': {'findByPrice': search_index_definition_model}, + 'language': 'javascript', + 'options': design_document_options_model, + 'validate_doc_update': 'testString', + 'views': {'byApplianceProdId': design_document_views_map_reduce_model}, + 'foo': 'testString', + } + + response = self.cloudant_service.put_design_document( + db='testString', + ddoc='testString', + design_document=design_document_model, + if_match='testString', + batch='ok', + new_edits=False, + rev='testString', + ) + + assert response.get_status_code() == 201 + document_result = response.get_result() + assert document_result is not None + + @needscredentials + def test_get_design_document_information(self): + response = self.cloudant_service.get_design_document_information( + db='testString', + ddoc='testString', + ) + + assert response.get_status_code() == 200 + design_document_information = response.get_result() + assert design_document_information is not None + + @needscredentials + def test_post_design_docs(self): + response = self.cloudant_service.post_design_docs( + db='testString', + att_encoding_info=False, + attachments=False, + conflicts=False, + descending=False, + include_docs=False, + inclusive_end=True, + limit=10, + skip=0, + update_seq=False, + end_key='testString', + key='testString', + keys=['testString'], + start_key='0007741142412418284', + ) + + assert response.get_status_code() == 200 + all_docs_result = response.get_result() + assert all_docs_result is not None + + @needscredentials + def test_post_design_docs_queries(self): + # Construct a dict representation of a AllDocsQuery model + all_docs_query_model = { + 'att_encoding_info': False, + 'attachments': False, + 'conflicts': False, + 'descending': False, + 'include_docs': False, + 'inclusive_end': True, + 'limit': 0, + 'skip': 0, + 'update_seq': False, + 'end_key': 'testString', + 'key': 'testString', + 'keys': ['small-appliances:1000042', 'small-appliances:1000043'], + 'start_key': 'testString', + } + + response = self.cloudant_service.post_design_docs_queries( + db='testString', + queries=[all_docs_query_model], + accept='application/json', + ) + + assert response.get_status_code() == 200 + all_docs_queries_result = response.get_result() + assert all_docs_queries_result is not None + + @needscredentials + def test_post_view(self): + response = self.cloudant_service.post_view( + db='testString', + ddoc='testString', + view='testString', + att_encoding_info=False, + attachments=False, + conflicts=False, + descending=False, + include_docs=True, + inclusive_end=True, + limit=10, + skip=0, + update_seq=False, + end_key='testString', + end_key_doc_id='testString', + group=False, + group_level=1, + key='testString', + keys=['examplekey'], + reduce=True, + stable=False, + start_key='testString', + start_key_doc_id='testString', + update='true', + ) + + assert response.get_status_code() == 200 + view_result = response.get_result() + assert view_result is not None + + @needscredentials + def test_post_view_as_stream(self): + response = self.cloudant_service.post_view_as_stream( + db='testString', + ddoc='testString', + view='testString', + att_encoding_info=False, + attachments=False, + conflicts=False, + descending=False, + include_docs=True, + inclusive_end=True, + limit=10, + skip=0, + update_seq=False, + end_key='testString', + end_key_doc_id='testString', + group=False, + group_level=1, + key='testString', + keys=['examplekey'], + reduce=True, + stable=False, + start_key='testString', + start_key_doc_id='testString', + update='true', + ) + + assert response.get_status_code() == 200 + result = response.get_result() + assert result is not None + + @needscredentials + def test_post_view_queries(self): + # Construct a dict representation of a ViewQuery model + view_query_model = { + 'att_encoding_info': False, + 'attachments': False, + 'conflicts': False, + 'descending': False, + 'include_docs': True, + 'inclusive_end': True, + 'limit': 5, + 'skip': 0, + 'update_seq': False, + 'end_key': 'testString', + 'end_key_doc_id': 'testString', + 'group': False, + 'group_level': 1, + 'key': 'testString', + 'keys': ['testString'], + 'reduce': True, + 'stable': False, + 'start_key': 'testString', + 'start_key_doc_id': 'testString', + 'update': 'true', + } + + response = self.cloudant_service.post_view_queries( + db='testString', + ddoc='testString', + view='testString', + queries=[view_query_model], + ) + + assert response.get_status_code() == 200 + view_queries_result = response.get_result() + assert view_queries_result is not None + + @needscredentials + def test_post_view_queries_as_stream(self): + # Construct a dict representation of a ViewQuery model + view_query_model = { + 'att_encoding_info': False, + 'attachments': False, + 'conflicts': False, + 'descending': False, + 'include_docs': True, + 'inclusive_end': True, + 'limit': 5, + 'skip': 0, + 'update_seq': False, + 'end_key': 'testString', + 'end_key_doc_id': 'testString', + 'group': False, + 'group_level': 1, + 'key': 'testString', + 'keys': ['testString'], + 'reduce': True, + 'stable': False, + 'start_key': 'testString', + 'start_key_doc_id': 'testString', + 'update': 'true', + } + + response = self.cloudant_service.post_view_queries_as_stream( + db='testString', + ddoc='testString', + view='testString', + queries=[view_query_model], + ) + + assert response.get_status_code() == 200 + result = response.get_result() + assert result is not None + + @needscredentials + def test_get_partition_information(self): + response = self.cloudant_service.get_partition_information( + db='testString', + partition_key='testString', + ) + + assert response.get_status_code() == 200 + partition_information = response.get_result() + assert partition_information is not None + + @needscredentials + def test_post_partition_all_docs(self): + response = self.cloudant_service.post_partition_all_docs( + db='testString', + partition_key='testString', + att_encoding_info=False, + attachments=False, + conflicts=False, + descending=False, + include_docs=False, + inclusive_end=True, + limit=10, + skip=0, + update_seq=False, + end_key='testString', + key='testString', + keys=['testString'], + start_key='0007741142412418284', + ) + + assert response.get_status_code() == 200 + all_docs_result = response.get_result() + assert all_docs_result is not None + + @needscredentials + def test_post_partition_all_docs_as_stream(self): + response = self.cloudant_service.post_partition_all_docs_as_stream( + db='testString', + partition_key='testString', + att_encoding_info=False, + attachments=False, + conflicts=False, + descending=False, + include_docs=False, + inclusive_end=True, + limit=10, + skip=0, + update_seq=False, + end_key='testString', + key='testString', + keys=['testString'], + start_key='0007741142412418284', + ) + + assert response.get_status_code() == 200 + result = response.get_result() + assert result is not None + + @needscredentials + def test_post_partition_search(self): + response = self.cloudant_service.post_partition_search( + db='testString', + partition_key='testString', + ddoc='testString', + index='testString', + query='name:Jane* AND active:True', + bookmark='testString', + highlight_fields=['testString'], + highlight_number=1, + highlight_post_tag='', + highlight_pre_tag='', + highlight_size=100, + include_docs=False, + include_fields=['testString'], + limit=3, + sort=['testString'], + stale='ok', + ) + + assert response.get_status_code() == 200 + search_result = response.get_result() + assert search_result is not None + + @needscredentials + def test_post_partition_search_as_stream(self): + response = self.cloudant_service.post_partition_search_as_stream( + db='testString', + partition_key='testString', + ddoc='testString', + index='testString', + query='name:Jane* AND active:True', + bookmark='testString', + highlight_fields=['testString'], + highlight_number=1, + highlight_post_tag='', + highlight_pre_tag='', + highlight_size=100, + include_docs=False, + include_fields=['testString'], + limit=3, + sort=['testString'], + stale='ok', + ) + + assert response.get_status_code() == 200 + result = response.get_result() + assert result is not None + + @needscredentials + def test_post_partition_view(self): + response = self.cloudant_service.post_partition_view( + db='testString', + partition_key='testString', + ddoc='testString', + view='testString', + att_encoding_info=False, + attachments=False, + conflicts=False, + descending=False, + include_docs=True, + inclusive_end=True, + limit=10, + skip=0, + update_seq=False, + end_key='testString', + end_key_doc_id='testString', + group=False, + group_level=1, + key='testString', + keys=['examplekey'], + reduce=True, + start_key='testString', + start_key_doc_id='testString', + update='true', + ) + + assert response.get_status_code() == 200 + view_result = response.get_result() + assert view_result is not None + + @needscredentials + def test_post_partition_view_as_stream(self): + response = self.cloudant_service.post_partition_view_as_stream( + db='testString', + partition_key='testString', + ddoc='testString', + view='testString', + att_encoding_info=False, + attachments=False, + conflicts=False, + descending=False, + include_docs=True, + inclusive_end=True, + limit=10, + skip=0, + update_seq=False, + end_key='testString', + end_key_doc_id='testString', + group=False, + group_level=1, + key='testString', + keys=['examplekey'], + reduce=True, + start_key='testString', + start_key_doc_id='testString', + update='true', + ) + + assert response.get_status_code() == 200 + result = response.get_result() + assert result is not None + + @needscredentials + def test_post_partition_explain(self): + response = self.cloudant_service.post_partition_explain( + db='testString', + partition_key='testString', + selector={'type': {'$eq': 'product'}}, + allow_fallback=True, + bookmark='testString', + conflicts=True, + execution_stats=True, + fields=['productId', 'name', 'description'], + limit=25, + skip=0, + sort=[{'key1': 'asc'}], + stable=True, + update='true', + use_index=['testString'], + ) + + assert response.get_status_code() == 200 + explain_result = response.get_result() + assert explain_result is not None + + @needscredentials + def test_post_partition_find(self): + response = self.cloudant_service.post_partition_find( + db='testString', + partition_key='testString', + selector={'type': {'$eq': 'product'}}, + allow_fallback=True, + bookmark='testString', + conflicts=True, + execution_stats=True, + fields=['productId', 'name', 'description'], + limit=25, + skip=0, + sort=[{'key1': 'asc'}], + stable=True, + update='true', + use_index=['testString'], + ) + + assert response.get_status_code() == 200 + find_result = response.get_result() + assert find_result is not None + + @needscredentials + def test_post_partition_find_as_stream(self): + response = self.cloudant_service.post_partition_find_as_stream( + db='testString', + partition_key='testString', + selector={'type': {'$eq': 'product'}}, + allow_fallback=True, + bookmark='testString', + conflicts=True, + execution_stats=True, + fields=['productId', 'name', 'description'], + limit=25, + skip=0, + sort=[{'key1': 'asc'}], + stable=True, + update='true', + use_index=['testString'], + ) + + assert response.get_status_code() == 200 + result = response.get_result() + assert result is not None + + @needscredentials + def test_post_explain(self): + response = self.cloudant_service.post_explain( + db='testString', + selector={'email_verified': {'$eq': True}}, + allow_fallback=True, + bookmark='testString', + conflicts=True, + execution_stats=True, + fields=['_id', 'type', 'name', 'email'], + limit=3, + skip=0, + sort=[{'key1': 'asc'}], + stable=True, + update='true', + use_index=['testString'], + r=1, + ) + + assert response.get_status_code() == 200 + explain_result = response.get_result() + assert explain_result is not None + + @needscredentials + def test_post_find(self): + response = self.cloudant_service.post_find( + db='testString', + selector={'email_verified': {'$eq': True}}, + allow_fallback=True, + bookmark='testString', + conflicts=True, + execution_stats=True, + fields=['_id', 'type', 'name', 'email'], + limit=3, + skip=0, + sort=[{'key1': 'asc'}], + stable=True, + update='true', + use_index=['testString'], + r=1, + ) + + assert response.get_status_code() == 200 + find_result = response.get_result() + assert find_result is not None + + @needscredentials + def test_post_find_as_stream(self): + response = self.cloudant_service.post_find_as_stream( + db='testString', + selector={'email_verified': {'$eq': True}}, + allow_fallback=True, + bookmark='testString', + conflicts=True, + execution_stats=True, + fields=['_id', 'type', 'name', 'email'], + limit=3, + skip=0, + sort=[{'key1': 'asc'}], + stable=True, + update='true', + use_index=['testString'], + r=1, + ) + + assert response.get_status_code() == 200 + result = response.get_result() + assert result is not None + + @needscredentials + def test_get_indexes_information(self): + response = self.cloudant_service.get_indexes_information( + db='testString', + ) + + assert response.get_status_code() == 200 + indexes_information = response.get_result() + assert indexes_information is not None + + @needscredentials + def test_post_index(self): + # Construct a dict representation of a Analyzer model + analyzer_model = { + 'name': 'classic', + 'stopwords': ['testString'], + } + # Construct a dict representation of a IndexTextOperatorDefaultField model + index_text_operator_default_field_model = { + 'analyzer': analyzer_model, + 'enabled': True, + } + # Construct a dict representation of a IndexField model + index_field_model = { + 'name': 'asc', + 'type': 'boolean', + 'foo': 'asc', + } + # Construct a dict representation of a IndexDefinition model + index_definition_model = { + 'default_analyzer': analyzer_model, + 'default_field': index_text_operator_default_field_model, + 'fields': [index_field_model], + 'index_array_lengths': True, + 'partial_filter_selector': {'anyKey': 'anyValue'}, + } + + response = self.cloudant_service.post_index( + db='testString', + index=index_definition_model, + ddoc='json-index', + name='getUserByName', + partitioned=True, + type='json', + ) + + assert response.get_status_code() == 200 + index_result = response.get_result() + assert index_result is not None + + @needscredentials + def test_post_search_analyze(self): + response = self.cloudant_service.post_search_analyze( + analyzer='english', + text='running is fun', + ) + + assert response.get_status_code() == 200 + search_analyze_result = response.get_result() + assert search_analyze_result is not None + + @needscredentials + def test_post_search(self): + response = self.cloudant_service.post_search( + db='testString', + ddoc='testString', + index='testString', + query='name:Jane* AND active:True', + bookmark='testString', + highlight_fields=['testString'], + highlight_number=1, + highlight_post_tag='', + highlight_pre_tag='', + highlight_size=100, + include_docs=False, + include_fields=['testString'], + limit=3, + sort=['testString'], + stale='ok', + counts=['testString'], + drilldown=[['testString']], + group_field='testString', + group_limit=1, + group_sort=['testString'], + ranges={'key1': {'key1': 'testString'}}, + ) + + assert response.get_status_code() == 200 + search_result = response.get_result() + assert search_result is not None + + @needscredentials + def test_post_search_as_stream(self): + response = self.cloudant_service.post_search_as_stream( + db='testString', + ddoc='testString', + index='testString', + query='name:Jane* AND active:True', + bookmark='testString', + highlight_fields=['testString'], + highlight_number=1, + highlight_post_tag='', + highlight_pre_tag='', + highlight_size=100, + include_docs=False, + include_fields=['testString'], + limit=3, + sort=['testString'], + stale='ok', + counts=['testString'], + drilldown=[['testString']], + group_field='testString', + group_limit=1, + group_sort=['testString'], + ranges={'key1': {'key1': 'testString'}}, + ) + + assert response.get_status_code() == 200 + result = response.get_result() + assert result is not None + + @needscredentials + def test_get_search_disk_size(self): + response = self.cloudant_service.get_search_disk_size( + db='testString', + ddoc='testString', + index='testString', + ) + + assert response.get_status_code() == 200 + search_disk_size_information = response.get_result() + assert search_disk_size_information is not None + + @needscredentials + def test_get_search_info(self): + response = self.cloudant_service.get_search_info( + db='testString', + ddoc='testString', + index='testString', + ) + + assert response.get_status_code() == 200 + search_info_result = response.get_result() + assert search_info_result is not None + + @needscredentials + def test_head_replication_document(self): + response = self.cloudant_service.head_replication_document( + doc_id='testString', + if_none_match='testString', + ) + + assert response.get_status_code() == 200 + + @needscredentials + def test_head_scheduler_document(self): + response = self.cloudant_service.head_scheduler_document( + doc_id='testString', + ) + + assert response.get_status_code() == 200 + + @needscredentials + def test_head_scheduler_job(self): + response = self.cloudant_service.head_scheduler_job( + job_id='testString', + ) + + assert response.get_status_code() == 200 + + @needscredentials + def test_post_replicator(self): + # Construct a dict representation of a Attachment model + attachment_model = { + 'content_type': 'testString', + 'data': 'VGhpcyBpcyBhIG1vY2sgYnl0ZSBhcnJheSB2YWx1ZS4=', + 'digest': 'testString', + 'encoded_length': 0, + 'encoding': 'testString', + 'follows': True, + 'length': 0, + 'revpos': 1, + 'stub': True, + } + # Construct a dict representation of a Revisions model + revisions_model = { + 'ids': ['testString'], + 'start': 1, + } + # Construct a dict representation of a DocumentRevisionStatus model + document_revision_status_model = { + 'rev': 'testString', + 'status': 'available', + } + # Construct a dict representation of a ReplicationCreateTargetParameters model + replication_create_target_parameters_model = { + 'n': 3, + 'partitioned': False, + 'q': 1, + } + # Construct a dict representation of a ReplicationDatabaseAuthBasic model + replication_database_auth_basic_model = { + 'password': 'testString', + 'username': 'testString', + } + # Construct a dict representation of a ReplicationDatabaseAuthIam model + replication_database_auth_iam_model = { + 'api_key': 'testString', + } + # Construct a dict representation of a ReplicationDatabaseAuth model + replication_database_auth_model = { + 'basic': replication_database_auth_basic_model, + 'iam': replication_database_auth_iam_model, + } + # Construct a dict representation of a ReplicationDatabase model + replication_database_model = { + 'auth': replication_database_auth_model, + 'headers': {'key1': 'testString'}, + 'url': 'https://my-source-instance.cloudantnosqldb.appdomain.cloud.example/animaldb', + } + # Construct a dict representation of a UserContext model + user_context_model = { + 'db': 'testString', + 'name': 'john', + 'roles': ['_replicator'], + } + # Construct a dict representation of a ReplicationDocument model + replication_document_model = { + '_attachments': {'key1': attachment_model}, + '_conflicts': ['testString'], + '_deleted': True, + '_deleted_conflicts': ['testString'], + '_id': 'testString', + '_local_seq': 'testString', + '_rev': 'testString', + '_revisions': revisions_model, + '_revs_info': [document_revision_status_model], + 'cancel': False, + 'checkpoint_interval': 4500, + 'connection_timeout': 15000, + 'continuous': True, + 'create_target': True, + 'create_target_params': replication_create_target_parameters_model, + 'doc_ids': ['badger', 'lemur', 'llama'], + 'filter': 'ddoc/my_filter', + 'http_connections': 10, + 'owner': 'testString', + 'query_params': {'key1': 'testString'}, + 'retries_per_request': 3, + 'selector': {'_id': {'$regex': 'docid'}}, + 'since_seq': '34-g1AAAAGjeJzLYWBgYMlgTmGQT0lKzi9KdU', + 'socket_options': '[{keepalive, true}, {nodelay, false}]', + 'source': replication_database_model, + 'source_proxy': 'testString', + 'target': replication_database_model, + 'target_proxy': 'testString', + 'use_bulk_get': True, + 'use_checkpoints': False, + 'user_ctx': user_context_model, + 'winning_revs_only': False, + 'worker_batch_size': 400, + 'worker_processes': 3, + 'foo': 'testString', + } + + response = self.cloudant_service.post_replicator( + replication_document=replication_document_model, + batch='ok', + ) + + assert response.get_status_code() == 201 + document_result = response.get_result() + assert document_result is not None + + @needscredentials + def test_get_replication_document(self): + response = self.cloudant_service.get_replication_document( + doc_id='testString', + if_none_match='testString', + attachments=False, + att_encoding_info=False, + conflicts=False, + deleted_conflicts=False, + latest=False, + local_seq=False, + meta=False, + rev='testString', + revs=False, + revs_info=False, + ) + + assert response.get_status_code() == 200 + replication_document = response.get_result() + assert replication_document is not None + + @needscredentials + def test_put_replication_document(self): + # Construct a dict representation of a Attachment model + attachment_model = { + 'content_type': 'testString', + 'data': 'VGhpcyBpcyBhIG1vY2sgYnl0ZSBhcnJheSB2YWx1ZS4=', + 'digest': 'testString', + 'encoded_length': 0, + 'encoding': 'testString', + 'follows': True, + 'length': 0, + 'revpos': 1, + 'stub': True, + } + # Construct a dict representation of a Revisions model + revisions_model = { + 'ids': ['testString'], + 'start': 1, + } + # Construct a dict representation of a DocumentRevisionStatus model + document_revision_status_model = { + 'rev': 'testString', + 'status': 'available', + } + # Construct a dict representation of a ReplicationCreateTargetParameters model + replication_create_target_parameters_model = { + 'n': 3, + 'partitioned': False, + 'q': 1, + } + # Construct a dict representation of a ReplicationDatabaseAuthBasic model + replication_database_auth_basic_model = { + 'password': 'testString', + 'username': 'testString', + } + # Construct a dict representation of a ReplicationDatabaseAuthIam model + replication_database_auth_iam_model = { + 'api_key': 'testString', + } + # Construct a dict representation of a ReplicationDatabaseAuth model + replication_database_auth_model = { + 'basic': replication_database_auth_basic_model, + 'iam': replication_database_auth_iam_model, + } + # Construct a dict representation of a ReplicationDatabase model + replication_database_model = { + 'auth': replication_database_auth_model, + 'headers': {'key1': 'testString'}, + 'url': 'https://my-source-instance.cloudantnosqldb.appdomain.cloud.example/animaldb', + } + # Construct a dict representation of a UserContext model + user_context_model = { + 'db': 'testString', + 'name': 'john', + 'roles': ['_replicator'], + } + # Construct a dict representation of a ReplicationDocument model + replication_document_model = { + '_attachments': {'key1': attachment_model}, + '_conflicts': ['testString'], + '_deleted': True, + '_deleted_conflicts': ['testString'], + '_id': 'testString', + '_local_seq': 'testString', + '_rev': 'testString', + '_revisions': revisions_model, + '_revs_info': [document_revision_status_model], + 'cancel': False, + 'checkpoint_interval': 4500, + 'connection_timeout': 15000, + 'continuous': True, + 'create_target': True, + 'create_target_params': replication_create_target_parameters_model, + 'doc_ids': ['badger', 'lemur', 'llama'], + 'filter': 'ddoc/my_filter', + 'http_connections': 10, + 'owner': 'testString', + 'query_params': {'key1': 'testString'}, + 'retries_per_request': 3, + 'selector': {'_id': {'$regex': 'docid'}}, + 'since_seq': '34-g1AAAAGjeJzLYWBgYMlgTmGQT0lKzi9KdU', + 'socket_options': '[{keepalive, true}, {nodelay, false}]', + 'source': replication_database_model, + 'source_proxy': 'testString', + 'target': replication_database_model, + 'target_proxy': 'testString', + 'use_bulk_get': True, + 'use_checkpoints': False, + 'user_ctx': user_context_model, + 'winning_revs_only': False, + 'worker_batch_size': 400, + 'worker_processes': 3, + 'foo': 'testString', + } + + response = self.cloudant_service.put_replication_document( + doc_id='testString', + replication_document=replication_document_model, + if_match='testString', + batch='ok', + new_edits=False, + rev='testString', + ) + + assert response.get_status_code() == 201 + document_result = response.get_result() + assert document_result is not None + + @needscredentials + def test_get_scheduler_docs(self): + response = self.cloudant_service.get_scheduler_docs( + limit=0, + skip=0, + states=['initializing'], + ) + + assert response.get_status_code() == 200 + scheduler_docs_result = response.get_result() + assert scheduler_docs_result is not None + + @needscredentials + def test_get_scheduler_document(self): + response = self.cloudant_service.get_scheduler_document( + doc_id='testString', + ) + + assert response.get_status_code() == 200 + scheduler_document = response.get_result() + assert scheduler_document is not None + + @needscredentials + def test_get_scheduler_jobs(self): + response = self.cloudant_service.get_scheduler_jobs( + limit=25, + skip=0, + ) + + assert response.get_status_code() == 200 + scheduler_jobs_result = response.get_result() + assert scheduler_jobs_result is not None + + @needscredentials + def test_get_scheduler_job(self): + response = self.cloudant_service.get_scheduler_job( + job_id='testString', + ) + + assert response.get_status_code() == 200 + scheduler_job = response.get_result() + assert scheduler_job is not None + + @needscredentials + def test_get_session_information(self): + response = self.cloudant_service.get_session_information() + + assert response.get_status_code() == 200 + session_information = response.get_result() + assert session_information is not None + + @needscredentials + def test_post_api_keys(self): + response = self.cloudant_service.post_api_keys() + + assert response.get_status_code() == 201 + api_keys_result = response.get_result() + assert api_keys_result is not None + + @needscredentials + def test_put_cloudant_security_configuration(self): + # Construct a dict representation of a SecurityObject model + security_object_model = { + 'names': ['testString'], + 'roles': ['testString'], + } + + response = self.cloudant_service.put_cloudant_security_configuration( + db='testString', + cloudant={'antsellseadespecteposene': ['_reader', '_writer', '_admin'], 'garbados': ['_reader', '_writer'], 'nobody': ['_reader']}, + admins=security_object_model, + couchdb_auth_only=True, + members=security_object_model, + ) + + assert response.get_status_code() == 200 + ok = response.get_result() + assert ok is not None + + @needscredentials + def test_get_security(self): + response = self.cloudant_service.get_security( + db='testString', + ) + + assert response.get_status_code() == 200 + security = response.get_result() + assert security is not None + + @needscredentials + def test_put_security(self): + # Construct a dict representation of a SecurityObject model + security_object_model = { + 'names': ['superuser'], + 'roles': ['admins'], + } + + response = self.cloudant_service.put_security( + db='testString', + admins=security_object_model, + cloudant={'antsellseadespecteposene': ['_reader', '_writer', '_admin']}, + couchdb_auth_only=True, + members=security_object_model, + ) + + assert response.get_status_code() == 200 + ok = response.get_result() + assert ok is not None + + @needscredentials + def test_get_cors_information(self): + response = self.cloudant_service.get_cors_information() + + assert response.get_status_code() == 200 + cors_information = response.get_result() + assert cors_information is not None + + @needscredentials + def test_put_cors_configuration(self): + response = self.cloudant_service.put_cors_configuration( + origins=['https://example.com', 'https://www.example.com'], + allow_credentials=True, + enable_cors=True, + ) + + assert response.get_status_code() == 200 + ok = response.get_result() + assert ok is not None + + @needscredentials + def test_head_attachment(self): + response = self.cloudant_service.head_attachment( + db='testString', + doc_id='testString', + attachment_name='testString', + if_match='testString', + if_none_match='testString', + rev='testString', + ) + + assert response.get_status_code() == 200 + + @needscredentials + def test_get_attachment(self): + response = self.cloudant_service.get_attachment( + db='testString', + doc_id='testString', + attachment_name='testString', + if_match='testString', + if_none_match='testString', + range='testString', + rev='testString', + ) + + assert response.get_status_code() == 200 + result = response.get_result() + assert result is not None + + @needscredentials + def test_put_attachment(self): + response = self.cloudant_service.put_attachment( + db='testString', + doc_id='testString', + attachment_name='testString', + attachment=io.BytesIO(b'This is a mock file.').getvalue(), + content_type='application/octet-stream', + if_match='testString', + rev='testString', + ) + + assert response.get_status_code() == 201 + document_result = response.get_result() + assert document_result is not None + + @needscredentials + def test_head_local_document(self): + response = self.cloudant_service.head_local_document( + db='testString', + doc_id='testString', + if_none_match='testString', + ) + + assert response.get_status_code() == 200 + + @needscredentials + def test_get_local_document(self): + response = self.cloudant_service.get_local_document( + db='testString', + doc_id='testString', + accept='application/json', + if_none_match='testString', + attachments=False, + att_encoding_info=False, + local_seq=False, + ) + + assert response.get_status_code() == 200 + document = response.get_result() + assert document is not None + + @needscredentials + def test_put_local_document(self): + # Construct a dict representation of a Attachment model + attachment_model = { + 'content_type': 'testString', + 'data': 'VGhpcyBpcyBhIG1vY2sgYnl0ZSBhcnJheSB2YWx1ZS4=', + 'digest': 'testString', + 'encoded_length': 0, + 'encoding': 'testString', + 'follows': True, + 'length': 0, + 'revpos': 1, + 'stub': True, + } + # Construct a dict representation of a Revisions model + revisions_model = { + 'ids': ['testString'], + 'start': 1, + } + # Construct a dict representation of a DocumentRevisionStatus model + document_revision_status_model = { + 'rev': 'testString', + 'status': 'available', + } + # Construct a dict representation of a Document model + document_model = { + '_attachments': {'key1': attachment_model}, + '_conflicts': ['testString'], + '_deleted': True, + '_deleted_conflicts': ['testString'], + '_id': 'exampleid', + '_local_seq': 'testString', + '_rev': 'testString', + '_revisions': revisions_model, + '_revs_info': [document_revision_status_model], + 'brand': 'Foo', + 'colours': '["red","green","black","blue"]', + 'description': 'Slim Colourful Design Electronic Cooking Appliance for ...', + 'image': 'assets/img/0gmsnghhew.jpg', + 'keywords': '["Foo","Scales","Weight","Digital","Kitchen"]', + 'name': 'Digital Kitchen Scales', + 'price': '14.99', + 'productId': '1000042', + 'taxonomy': '["Home","Kitchen","Small Appliances"]', + 'type': 'product', + } + + response = self.cloudant_service.put_local_document( + db='testString', + doc_id='testString', + document=document_model, + content_type='application/json', + batch='ok', + ) + + assert response.get_status_code() == 201 + document_result = response.get_result() + assert document_result is not None + + @needscredentials + def test_post_revs_diff(self): + response = self.cloudant_service.post_revs_diff( + db='testString', + document_revisions={'key1': ['testString']}, + ) + + assert response.get_status_code() == 200 + dict = response.get_result() + assert dict is not None + + @needscredentials + def test_get_shards_information(self): + response = self.cloudant_service.get_shards_information( + db='testString', + ) + + assert response.get_status_code() == 200 + shards_information = response.get_result() + assert shards_information is not None + + @needscredentials + def test_get_document_shards_info(self): + response = self.cloudant_service.get_document_shards_info( + db='testString', + doc_id='testString', + ) + + assert response.get_status_code() == 200 + document_shard_info = response.get_result() + assert document_shard_info is not None + + @needscredentials + def test_head_up_information(self): + response = self.cloudant_service.head_up_information() + + assert response.get_status_code() == 200 + + @needscredentials + def test_get_active_tasks(self): + response = self.cloudant_service.get_active_tasks() + + assert response.get_status_code() == 200 + list_active_task = response.get_result() + assert list_active_task is not None + + @needscredentials + def test_get_activity_tracker_events(self): + response = self.cloudant_service.get_activity_tracker_events() + + assert response.get_status_code() == 200 + activity_tracker_events = response.get_result() + assert activity_tracker_events is not None + + @needscredentials + def test_post_activity_tracker_events(self): + response = self.cloudant_service.post_activity_tracker_events( + types=['management', 'data'], + ) + + assert response.get_status_code() == 200 + ok = response.get_result() + assert ok is not None + + @needscredentials + def test_get_capacity_databases_information(self): + response = self.cloudant_service.get_capacity_databases_information() + + assert response.get_status_code() == 200 + capacity_databases_information = response.get_result() + assert capacity_databases_information is not None + + @needscredentials + def test_get_current_databases_information(self): + response = self.cloudant_service.get_current_databases_information() + + assert response.get_status_code() == 200 + current_databases_information = response.get_result() + assert current_databases_information is not None + + @needscredentials + def test_get_current_throughput_information(self): + response = self.cloudant_service.get_current_throughput_information() + + assert response.get_status_code() == 200 + current_throughput_information = response.get_result() + assert current_throughput_information is not None + + @needscredentials + def test_get_membership_information(self): + response = self.cloudant_service.get_membership_information() + + assert response.get_status_code() == 200 + membership_information = response.get_result() + assert membership_information is not None + + @needscredentials + def test_get_up_information(self): + response = self.cloudant_service.get_up_information() + + assert response.get_status_code() == 200 + up_information = response.get_result() + assert up_information is not None + + @needscredentials + def test_delete_database(self): + response = self.cloudant_service.delete_database( + db='testString', + ) + + assert response.get_status_code() == 200 + ok = response.get_result() + assert ok is not None + + @needscredentials + def test_delete_document(self): + response = self.cloudant_service.delete_document( + db='testString', + doc_id='testString', + if_match='testString', + batch='ok', + rev='testString', + ) + + assert response.get_status_code() == 200 + document_result = response.get_result() + assert document_result is not None + + @needscredentials + def test_delete_design_document(self): + response = self.cloudant_service.delete_design_document( + db='testString', + ddoc='testString', + if_match='testString', + batch='ok', + rev='testString', + ) + + assert response.get_status_code() == 200 + document_result = response.get_result() + assert document_result is not None + + @needscredentials + def test_delete_index(self): + response = self.cloudant_service.delete_index( + db='testString', + ddoc='testString', + type='json', + index='testString', + ) + + assert response.get_status_code() == 200 + ok = response.get_result() + assert ok is not None + + @needscredentials + def test_delete_replication_document(self): + response = self.cloudant_service.delete_replication_document( + doc_id='testString', + if_match='testString', + batch='ok', + rev='testString', + ) + + assert response.get_status_code() == 200 + document_result = response.get_result() + assert document_result is not None + + @needscredentials + def test_delete_attachment(self): + response = self.cloudant_service.delete_attachment( + db='testString', + doc_id='testString', + attachment_name='testString', + if_match='testString', + rev='testString', + batch='ok', + ) + + assert response.get_status_code() == 200 + document_result = response.get_result() + assert document_result is not None + + @needscredentials + def test_delete_local_document(self): + response = self.cloudant_service.delete_local_document( + db='testString', + doc_id='testString', + batch='ok', + ) + + assert response.get_status_code() == 200 + document_result = response.get_result() + assert document_result is not None diff --git a/test/integration/test_integration.py b/test/integration/test_integration.py deleted file mode 100644 index 74205500..00000000 --- a/test/integration/test_integration.py +++ /dev/null @@ -1,45 +0,0 @@ -from ibmcloudant.cloudant_v1 import CloudantV1 -import os -import unittest - -# Read config file -configFile = 'cloudant.env' -configLoaded = None - -if os.path.exists(configFile): - os.environ['IBM_CREDENTIALS_FILE'] = configFile - configLoaded = True -else: - print('External configuration was not found, skipping tests...') - -class TestIntegration(unittest.TestCase): - def setUp(self): - if not configLoaded: - self.skipTest("External configuration not available, skipping...") - - self.cloudant = CloudantV1.new_instance(service_name='SERVER') - self.db_name = os.environ.get('DATABASE_NAME', 'stores') - self.assertIsNotNone(self.cloudant) - self.assertIsNotNone(self.db_name) - - def test_server_information(self): - response = self.cloudant.get_server_information() - self.assertIsNotNone(response) - result = response.get_result() - self.assertIsNotNone(result) - self.assertIsNotNone(result['couchdb']) - self.assertIsNotNone(result['version']) - - def test_head_database(self): - response = self.cloudant.head_database(self.db_name) - self.assertIsNotNone(response) - self.assertIsNotNone(response.get_headers()) - self.assertTrue(len(response.get_headers()) > 0) - - def test_all_docs(self): - response = self.cloudant.post_all_docs(self.db_name) - self.assertIsNotNone(response) - result = response.get_result() - self.assertIsNotNone(result) - self.assertIsNotNone(result['rows']) - self.assertTrue(len(result['rows']) > 0) diff --git a/test/integration/test_timeout.py b/test/integration/test_timeout.py index 3a6a475a..ece750b9 100644 --- a/test/integration/test_timeout.py +++ b/test/integration/test_timeout.py @@ -1,6 +1,6 @@ # coding: utf-8 -# © Copyright IBM Corporation 2021. +# © Copyright IBM Corporation 2021, 2025. # # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -86,8 +86,24 @@ def get_authenticate_arguments(req): return req.mock_calls[0] @staticmethod - def get_request_arguments(tci, srv, idx): - tci.assertEqual(srv.http_client.request.call_count, idx + 1) + def get_request_arguments(tci, srv, idx, call_count=None): + """ + Retrieve the arguments passed to a mocked HTTP request. + + Parameters: + tci (unittest.mock.Mock): A Mock object used for assertions. + srv (Service): The instance of CloudantV1 service. + idx (int): The index of the request in srv.http_client.request.mock_calls. + call_count (int, optional): The total call count for the request. Defaults to None. + + Returns: + list: The arguments passed to the HTTP request at the specified index. + + Raises: + AssertionError: If the actual call count of the request does not match the provided or calculated call_count. + """ + call_count = idx + 1 if call_count is None else call_count + tci.assertEqual(srv.http_client.request.call_count, call_count) return srv.http_client.request.mock_calls[idx] # Every method tests an authenticator. @@ -142,31 +158,27 @@ def test_timeout_cloudantv1_sessionauth(self): ) my_service.set_service_url("http://cloudant.example") - # Mock out authentication - orig_request = requests.request - requests.request = Mock(return_value=Helpers.get_mocked_response()) - # Mock out request response Helpers.mock_out_cloudant_request(my_service) - testcases = Helpers.defineTestCases(my_service) + # Call the server + my_service.get_server_information() - for tc_num, tc in enumerate(testcases): - tc['set_timeout'](CUSTOM_TIMEOUT_CONFIG) - - # Call the server - my_service.get_server_information() + # Assert timeout is set to the authenticator + auth_args = Helpers.get_request_arguments(self, my_service.authenticator.token_manager, 0, call_count=2) + self.assertEqual(auth_args.kwargs['timeout'], DEFAULT_TIMEOUT) - # Assert timeout is set to the authenticator - auth_args = Helpers.get_authenticate_arguments(requests.request) - Helpers.assert_default_timeout_setting(self, auth_args) + # Assert timeout is set to the server request + req_args = Helpers.get_request_arguments(self, my_service, 1, call_count=2) + self.assertEqual(req_args.kwargs['timeout'], DEFAULT_TIMEOUT) - # Assert timeout is set in the server request - req_args = Helpers.get_request_arguments(self, my_service, tc_num) - tc['assert_func'](self, req_args) + # Set a custom timeout and repeat the request. Client should be already authenticated. + my_service.set_http_config(CUSTOM_TIMEOUT_CONFIG) + my_service.get_server_information() - # Set back requests.request - requests.request = orig_request + # Assert the custom timeout is set to the server request + req_args = Helpers.get_request_arguments(self, my_service, 2, call_count=3) + self.assertEqual(req_args.kwargs['timeout'], CUSTOM_TIMEOUT) def test_timeout_cloudantv1_iamauth(self): authenticator = IAMAuthenticator('apikey') diff --git a/test/unit/features/conftest.py b/test/unit/features/conftest.py new file mode 100644 index 00000000..dab13b99 --- /dev/null +++ b/test/unit/features/conftest.py @@ -0,0 +1,391 @@ +# coding: utf-8 + +# © Copyright IBM Corporation 2022, 2024. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +""" +Shared tests' fixtures +""" + +from typing import Sequence +import unittest +import pytest +import os +import json +import responses + +from threading import Timer + +import itertools +from itertools import islice + +from requests import codes +from requests.exceptions import ConnectionError + +from ibm_cloud_sdk_core import ApiException, DetailedResponse + +from ibmcloudant.cloudant_v1 import ( + AllDocsResult, + CloudantV1, + FindResult, + PostChangesEnums, + ChangesResult, + ChangesResultItem, + SearchResult, + ViewResult, +) + +from ibmcloudant.features.changes_follower import ( + _LONGPOLL_TIMEOUT, + _BATCH_SIZE, + _Mode, +) +from ibmcloudant.features.pagination import PagerType + + +@pytest.fixture(scope='class') +def kwargs(request): + request.cls.kwarg_valid = { + 'include_docs': True, + 'doc_ids': ['foo', 'bar', 'baz'], + 'att_encoding_info': True, + 'attachments': True, + 'conflicts': True, + 'filter': '_selector', + 'selector': {'selector': {'foo': 'bar'}}, + } + + request.cls.kwarg_invalid = { + 'descending': True, + 'feed': PostChangesEnums.Feed.CONTINUOUS, + 'heartbeat': 150, + 'last_event_id': '9876-alotofcharactersthatarenotreallyrandom', + 'timeout': 3600000, + 'filter': '_view', + } + + +@pytest.fixture(scope='class') +def timeouts(request): + longpoll_timeout = int(_LONGPOLL_TIMEOUT / 1000) + request.cls.timeouts_valid = [60, (60, 60), 120, 300, (120, 300)] + request.cls.timeouts_invalid = [ + 15, + 30, + (30, 15), + longpoll_timeout, + (longpoll_timeout, longpoll_timeout), + ] + + +@pytest.fixture(scope='class') +def errors(request): + request.cls.terminal_errors = [ + 'bad_request', + 'unauthorized', + 'forbidden', + 'not_found', + ] + request.cls.transient_errors = [ + 'too_many_requests', + 'internal_server_error', + 'bad_gateway', + 'gateway_timeout', + 'bad_json', + 'bad_io', + ] + + +@pytest.fixture(scope='class') +def limits(request): + request.cls.limits = [ + 100, + _BATCH_SIZE, + _BATCH_SIZE + 123, + ] + +class MockClientBaseCase(unittest.TestCase): + @classmethod + def setUpClass(cls): + # Setup client env config + os.environ['TEST_SERVICE_AUTH_TYPE'] = 'noAuth' + os.environ['TEST_SERVICE_URL'] = 'http://localhost:5984' + cls.client = CloudantV1.new_instance( + service_name='TEST_SERVICE', + ) + + def make_error_tuple(self, error: str) -> tuple[any]: + if error == 'bad_io': + return (200, {}, ConnectionError('peer reset')) + elif error == 'bad_json': + return (200, {}, '{') + else: + return ( + codes[error], + {}, + json.dumps({'error': error}), + ) + + def make_error(self, error: str) -> dict[str:any]: + error_tuple = self.make_error_tuple(error) + if error == 'bad_io': + return {'body': error_tuple[2]} + else: + return { + 'status': error_tuple[0], + 'content_type': 'application/json', + 'body': error_tuple[2], + } + + def make_error_exception(self, error: str) -> Exception: + error_dict = self.make_error(error) + if error == 'bad_io': + return error_dict['body'] + elif error == 'bad_json': + return ApiException(code=error_dict['status'], + message='Error processing the HTTP response',) + return ApiException(error_dict['status']) + +class ChangesFollowerBaseCase(MockClientBaseCase): + + def prepare_mock_changes( + self, + batches=0, + errors=[], + db_info_doc_count=500_000, + db_info_doc_size=523, + ): + class changes_callback: + def __init__(self): + self._batch_num = 1 + self._errors = itertools.cycle(errors) + self._return_error = False + + def __call__(self, request): + if self._return_error: + self._return_error = False + error = next(self._errors) + return self.make_error_tuple(error) + # this stands for "large" seq in empty result case + last_seq = f'{batches * _BATCH_SIZE}-abcdef' + pending = 0 + items = [] + if self._batch_num <= batches: + # we start from doc idx 000001 + start = (self._batch_num - 1) * _BATCH_SIZE + 1 + stop = start + _BATCH_SIZE + last_seq = f'{stop-1}-abcdef' + pending = ( + batches * _BATCH_SIZE - (self._batch_num) * _BATCH_SIZE + ) + for idx in range(start, stop, 1): + items.append( + ChangesResultItem( + id=f'{idx:06}', + changes=[], + seq=f'{idx}-abcdef', + ) + ) + resp = ChangesResult( + last_seq=last_seq, pending=pending, results=items + ).to_dict() + self._batch_num += 1 + if len(errors) > 0: + self._return_error = True + return ( + 200, + {}, + json.dumps(resp), + ) + + _base_url = os.environ.get('TEST_SERVER_URL', 'http://localhost:5984') + url = _base_url + '/db' + responses.get( + url, + status=200, + content_type='application/json', + json={ + 'doc_count': db_info_doc_count, + 'sizes': {'external': db_info_doc_count * db_info_doc_size}, + }, + ) + + url = _base_url + '/db/_changes' + return responses.add_callback( + responses.POST, + url, + content_type='application/json', + callback=changes_callback(), + ) + + def prepare_mock_with_error(self, error: str): + _base_url = os.environ.get('TEST_SERVER_URL', 'http://localhost:5984') + url = _base_url + '/db/_changes' + return responses.post(url, **self.make_error(error)) + + def runner(self, follower, mode, timeout=1, stop_after=0): + """ + blocking runner with timeout + """ + + def main(): + if mode == _Mode.LISTEN: + changes = follower.start() + elif mode == _Mode.FINITE: + changes = follower.start_one_off() + stop_timer = Timer(timeout, follower.stop) + stop_timer.start() + counter = 0 + try: + for _ in changes: + counter += 1 + if stop_after > 0 and stop_after == counter: + follower.stop() + break + finally: + stop_timer.cancel() + return counter + + return main() + +class PaginationMockSupport: + all_docs_pagers: Sequence[PagerType] = ( + PagerType.POST_ALL_DOCS, + PagerType.POST_DESIGN_DOCS, + PagerType.POST_PARTITION_ALL_DOCS + ) + view_pagers: Sequence[PagerType] = ( + PagerType.POST_VIEW, + PagerType.POST_PARTITION_VIEW + ) + # the key pager types (n+1 paging) + key_pagers: Sequence[PagerType] = all_docs_pagers + view_pagers + find_pagers: Sequence[PagerType] = ( + PagerType.POST_FIND, + PagerType.POST_PARTITION_FIND) + search_pagers: Sequence[PagerType] = ( + PagerType.POST_SEARCH, + PagerType.POST_PARTITION_SEARCH) + + # Map of pager type to a tuple of patch function name, result wrapper lambda, result row lambda + operation_map: dict[PagerType:str] = { + PagerType.POST_ALL_DOCS: 'ibmcloudant.cloudant_v1.CloudantV1.post_all_docs', + PagerType.POST_DESIGN_DOCS: 'ibmcloudant.cloudant_v1.CloudantV1.post_design_docs', + PagerType.POST_FIND: 'ibmcloudant.cloudant_v1.CloudantV1.post_find', + PagerType.POST_PARTITION_ALL_DOCS: 'ibmcloudant.cloudant_v1.CloudantV1.post_partition_all_docs', + PagerType.POST_PARTITION_FIND: 'ibmcloudant.cloudant_v1.CloudantV1.post_partition_find', + PagerType.POST_PARTITION_SEARCH: 'ibmcloudant.cloudant_v1.CloudantV1.post_partition_search', + PagerType.POST_PARTITION_VIEW: 'ibmcloudant.cloudant_v1.CloudantV1.post_partition_view', + PagerType.POST_SEARCH: 'ibmcloudant.cloudant_v1.CloudantV1.post_search', + PagerType.POST_VIEW: 'ibmcloudant.cloudant_v1.CloudantV1.post_view' + } + + def make_wrapper(pager_type: PagerType, total: int, rows: Sequence) -> dict[str:any]: + if pager_type in PaginationMockSupport.key_pagers: + return {'total_rows': total, 'rows': rows} + else: + bkmk = 'emptypagebookmark' + last_row = rows[-1] if len(rows) > 0 else None + if pager_type in PaginationMockSupport.find_pagers: + return {'bookmark': last_row['_id'] if last_row else bkmk, 'docs': rows} + elif pager_type in PaginationMockSupport.search_pagers: + return {'bookmark': last_row['id'] if last_row else bkmk, 'total_rows': total, 'rows': rows} + else: + raise Exception('Unknown pager type, fail test.') + + def make_row(pager_type: PagerType, i: int) -> dict[str:any]: + id = f'testdoc{i}' + rev = f'1-abc{i}' + if pager_type in PaginationMockSupport.key_pagers: + if pager_type in (PagerType.POST_VIEW, PagerType.POST_PARTITION_VIEW): + key = i + value = 1 + else: + key = id + value = {'rev': rev} + return {'id': id, 'key': key, 'value': value} + elif pager_type in PaginationMockSupport.find_pagers: + return {'_id':id, '_rev': rev, 'testfield': i} + elif pager_type in PaginationMockSupport.search_pagers: + return {'fields':{}, 'id': id} + else: + raise Exception('Unknown pager type, fail test.') + +class PaginationMockResponse: + """ + Test class for mocking page responses. + """ + def __init__(self, + total_items: int, + page_size: int, + pager_type: PagerType + ): + self.total_items: int = total_items + self.page_size: int = page_size + self.pages = self.generator() + self.pager_type: PagerType = pager_type + self.plus_one_paging: bool = self.pager_type in PaginationMockSupport.key_pagers + self.expected_pages: list[list] = [] + + # for compatibility with python <= 3.12 + def batched(self, iterable, n): + """Batch data into tuples of length n. The last batch may be shorter.""" + it = iter(iterable) + while True: + batch = tuple(islice(it, n)) + if not batch: + break + yield batch + + def generator(self): + for page in self.batched(range(0, self.total_items), self.page_size): + rows = [PaginationMockSupport.make_row(self.pager_type, i) for i in page] + if self.plus_one_paging: + # Add an n+1 row for key based paging if more pages + if (n_plus_one := page[-1] + 1) < self.total_items: + rows.append(PaginationMockSupport.make_row(self.pager_type, n_plus_one)) + yield DetailedResponse(response=PaginationMockSupport.make_wrapper(self.pager_type, self.total_items, rows)) + yield DetailedResponse(response=PaginationMockSupport.make_wrapper(self.pager_type, self.total_items, [])) + + def convert_result(self, result: dict) -> Sequence: + if self.pager_type in PaginationMockSupport.all_docs_pagers: + return AllDocsResult.from_dict(result).rows + elif self.pager_type in PaginationMockSupport.find_pagers: + return FindResult.from_dict(result).docs + elif self.pager_type in PaginationMockSupport.search_pagers: + return SearchResult.from_dict(result).rows + elif self.pager_type in PaginationMockSupport.view_pagers: + return ViewResult.from_dict(result).rows + + def get_next_page(self, **kwargs): + # return next(self.pages) + # ignore kwargs + # get next page + page = next(self.pages) + # convert to an expected page + rows = self.convert_result(page.get_result()) + if len(rows) > self.page_size and self.plus_one_paging: + self.expected_pages.append(rows[:-1]) + else: + self.expected_pages.append(rows) + return page + + def get_expected_page(self, page: int) -> list: + return self.expected_pages[page - 1] + + def all_expected_items(self) -> list: + all_items: list = [] + for page in self.expected_pages: + all_items.extend(page) + return all_items diff --git a/test/unit/features/test_changes_follower.py b/test/unit/features/test_changes_follower.py new file mode 100644 index 00000000..e31a6d17 --- /dev/null +++ b/test/unit/features/test_changes_follower.py @@ -0,0 +1,652 @@ +# coding: utf-8 + +# © Copyright IBM Corporation 2022, 2024. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +""" +Test methods in the changes follower module +""" + +import sys +import timeit + +import pytest +import responses +from conftest import ChangesFollowerBaseCase +from ibm_cloud_sdk_core import ApiException +from requests.exceptions import ConnectionError + +from ibmcloudant.cloudant_v1 import PostChangesEnums +from ibmcloudant.features.changes_follower import ( + _BATCH_SIZE, + _FOREVER, + _LONGPOLL_TIMEOUT, + ChangesFollower, + _Mode, +) + +# the largest positive integer supported by the platform +MAX_BATCHES = sys.maxsize / _BATCH_SIZE + + +@pytest.mark.usefixtures("timeouts") +class TestChangesFollowerInitialization(ChangesFollowerBaseCase): + def test_minimal_initialization(self): + try: + ChangesFollower(self.client, db="db") + except BaseException: + self.fail("There should be no exception.") + + def test_validate_missing_database_name(self): + regx = "The option db must be provided when using ChangesFollower." + with self.assertRaisesRegex(ValueError, regx): + ChangesFollower(self.client) + + def test_validate_overflow_tolerance(self): + regx = "Error tolerance duration must not be larger than" + with self.assertRaisesRegex(ValueError, regx): + ChangesFollower(self.client, db="db", error_tolerance=_FOREVER + 1) + + def test_validate_negative_tolerance(self): + regx = "Error tolerance duration must not be negative." + with self.assertRaisesRegex(ValueError, regx): + ChangesFollower(self.client, db="db", error_tolerance=-1) + + def test_initialization_with_valid_client_timeout(self): + for timeout in self.timeouts_valid: + try: + self.client.set_http_config({"timeout": timeout}) + ChangesFollower(self.client, db="db") + except BaseException: + self.fail("There should be no exception.") + + def test_initialization_with_invalid_client_timeout(self): + for timeout in self.timeouts_invalid: + self.client.set_http_config({"timeout": timeout}) + regx = "timeouts must be at least" + with self.assertRaisesRegex(ValueError, regx): + ChangesFollower(self.client, db="db") + + +@pytest.mark.usefixtures("kwargs") +class TestChangesFollowerOptions(ChangesFollowerBaseCase): + def test_validate_options_valid_cases(self): + try: + ChangesFollower(self.client, db="db", **self.kwarg_valid) + except BaseException: + self.fail("There should be no illegal argument exception.") + + def test_validate_options_invalid_cases(self): + for opt, val in self.kwarg_invalid.items(): + if opt == "filter": + error_opt = f"filter={val}" + else: + error_opt = opt + regx = f"The option '{error_opt}' is invalid when using ChangesFollower." + with self.assertRaisesRegex(ValueError, regx): + ChangesFollower(self.client, db="db", **{opt: val}) + + def test_validate_options_multiple_invalid_cases(self): + error_opts = "" + for opt, val in self.kwarg_invalid.items(): + if opt == "filter": + error_opts += f"filter={val}, " + else: + error_opts += f"{opt}, " + if error_opts.endswith(", "): + error_opts = error_opts[: -len(", ")] + regx = f"The options {error_opts} are invalid when using ChangesFollower." + with self.assertRaisesRegex(ValueError, regx): + ChangesFollower(self.client, db="db", **self.kwarg_invalid) + + def test_set_defaults(self): + follower = ChangesFollower(self.client, db="db", **self.kwarg_valid) + expected = { + "feed": PostChangesEnums.Feed.NORMAL, + "timeout": None, + } + for opt, val in expected.items(): + self.assertEqual(follower.options.get(opt), val) + + def test_set_defaults_listen(self): + follower = ChangesFollower(self.client, db="db", **self.kwarg_valid) + follower._set_defaults(_Mode.LISTEN) + expected = { + "feed": PostChangesEnums.Feed.LONGPOLL, + "timeout": _LONGPOLL_TIMEOUT, + } + for opt, val in expected.items(): + self.assertEqual(follower.options.get(opt), val) + + def test_set_defaults_with_limit(self): + follower = ChangesFollower(self.client, db="db", **self.kwarg_valid) + follower._set_defaults(_Mode.FINITE, limit=12) + expected = { + "feed": PostChangesEnums.Feed.NORMAL, + "timeout": None, + "limit": 12, + } + for opt, val in expected.items(): + self.assertEqual(follower.options.get(opt), val) + + def test_set_defaults_listen_with_limit(self): + follower = ChangesFollower(self.client, db="db", **self.kwarg_valid) + follower._set_defaults(_Mode.LISTEN, limit=12) + expected = { + "feed": PostChangesEnums.Feed.LONGPOLL, + "timeout": _LONGPOLL_TIMEOUT, + "limit": 12, + } + for opt, val in expected.items(): + self.assertEqual(follower.options.get(opt), val) + + def test_set_defaults_with_kwarg_limit(self): + kwarg = {**self.kwarg_valid, **{"limit": 24}} + follower = ChangesFollower(self.client, db="db", **kwarg) + follower._set_defaults(_Mode.LISTEN, limit=12) + self.assertEqual(follower.options.get("limit"), 12) + + +@pytest.mark.usefixtures("limits", "errors") +class TestChangesFollowerFinite(ChangesFollowerBaseCase): + @responses.activate + def test_start_one_off(self): + """ + Checks that a FINITE mode completes successfully + for a fixed number of batches. + """ + batches = 6 + self.prepare_mock_changes(batches=batches) + follower = ChangesFollower(self.client, db="db") + changes = follower.start_one_off() + count = sum(1 for _ in changes) + self.assertEqual( + count, + batches * _BATCH_SIZE, + "There should be the expected number of changes.", + ) + + @responses.activate + def test_start_one_off_terminal_errors(self): + """ + Checks that a FINITE mode errors for all terminal errors. + """ + for error in self.terminal_errors: + self.prepare_mock_with_error(error) + follower = ChangesFollower(self.client, db="db") + changes = follower.start_one_off() + with self.assertRaisesRegex(ApiException, error): + next(changes) + + @responses.activate + def test_start_one_off_transient_errors_no_suppression(self): + """ + Checks that a FINITE mode errors for all transient errors + when not suppressing. + """ + for error in self.transient_errors: + self.prepare_mock_with_error(error) + follower = ChangesFollower(self.client, db="db", error_tolerance=0) + start = timeit.default_timer() + changes = follower.start_one_off() + if error == "bad_io": + with self.assertRaises(ConnectionError): + next(changes) + else: + if error == "bad_json": + error = "Error processing the HTTP response" + with self.assertRaisesRegex(ApiException, error): + next(changes) + stop = timeit.default_timer() - start + self.assertLess( + stop, + 0.300, + "There should be no exception delay.", + ) + + @responses.activate + def test_start_one_off_transient_errors_with_suppression_duration(self): + """ + Checks that a FINITE mode repeatedly encountering transient errors + will terminate with an exception after a duration. + """ + for error in self.transient_errors: + self.prepare_mock_with_error(error) + follower = ChangesFollower(self.client, db="db", error_tolerance=100) + start = timeit.default_timer() + changes = follower.start_one_off() + if error == "bad_io": + with self.assertRaises(ConnectionError): + next(changes) + else: + if error == "bad_json": + error = "Error processing the HTTP response" + with self.assertRaisesRegex(ApiException, error): + next(changes) + stop = timeit.default_timer() - start + self.assertGreaterEqual( + stop, + 0.100, + "The exception delay should be longer error_tolerance.", + ) + + @responses.activate + def test_start_one_off_transient_errors_with_suppression_does_complete( + self, + ): + """ + Checks that a FINITE mode repeatedly encountering transient errors + will complete successfully if not exceeding the duration. + """ + batches = 5 + self.prepare_mock_changes( + batches=batches, + errors=self.transient_errors, + ) + follower = ChangesFollower(self.client, db="db", error_tolerance=300) + changes = follower.start_one_off() + count = sum(1 for _ in changes) + self.assertEqual( + count, + batches * _BATCH_SIZE, + "There should be the expected number of changes.", + ) + + @responses.activate + def test_start_one_off_transient_errors_max_suppression_does_not_complete( + self, + ): + """ + Checks that a FINITE mode repeatedly encountering transient errors + will keep trying indefinitely with max suppression. + """ + for error in self.transient_errors: + try: + self.prepare_mock_with_error(error) + follower = ChangesFollower(self.client, db="db") + count = self.runner(follower, _Mode.FINITE, timeout=0.5) + except BaseException: + self.fail("There should be no exception.") + self.assertEqual(count, 0, "There should be no changes.") + + @responses.activate + def test_start_one_off_transient_errors_with_max_suppression_does_complete( + self, + ): + """ + Checks that a FINITE mode encountering transient errors + will complete successfully with max suppression. + """ + batches = 4 + self.prepare_mock_changes(batches=batches, errors=self.transient_errors) + follower = ChangesFollower(self.client, db="db") + changes = follower.start_one_off() + count = sum(1 for _ in changes) + self.assertEqual( + count, + batches * _BATCH_SIZE, + "There should be the expected number of changes.", + ) + + @responses.activate + def test_stop(self): + """ + Checks calling stop for the FINITE case. + """ + try: + self.prepare_mock_changes(batches=MAX_BATCHES) + follower = ChangesFollower(self.client, db="db") + start = timeit.default_timer() + count = self.runner(follower, _Mode.FINITE, timeout=2, stop_after=1000) + stop = timeit.default_timer() - start + except BaseException: + self.fail("There should be no exception.") + self.assertGreaterEqual(count, 1000, "There should be some changes.") + self.assertLess(stop, 2, "The thread should have stopped before the wait time.") + + @responses.activate + def test_state_error(self): + """ + Checks that a FINITE follower can only be started once. + """ + try: + self.prepare_mock_changes(batches=MAX_BATCHES) + follower = ChangesFollower(self.client, db="db") + self.runner(follower, _Mode.FINITE, timeout=1, stop_after=1000) + with self.assertRaisesRegex( + RuntimeError, "Cannot start a feed that has already started." + ): + follower.start_one_off() + with self.assertRaisesRegex( + RuntimeError, "Cannot start a feed that has already started." + ): + follower.start() + except BaseException: + self.fail("There should be no exception.") + + @responses.activate + def test_limit(self): + """ + Checks that setting a limit terminates iterations early for FINITE mode + and limits smaller, the same and larger than the default batch size. + """ + for limit in self.limits: + try: + self.prepare_mock_changes(batches=MAX_BATCHES) + follower = ChangesFollower(self.client, db="db", limit=limit) + count = self.runner(follower, _Mode.FINITE, timeout=180) + except BaseException: + self.fail("There should be no exception.") + self.assertEqual( + count, + limit, + "There should be the correct number of changes.", + ) + + @responses.activate + def test_retry_delay(self): + """ + Checks that a FINITE follower delays between retries. + + For a time frame in 600ms an exponential backoff would make 3 retry + attempts (first immideately, for duration of 100ms, second after + that for duration of 200ms, and third after 100ms+200ms for duration + of 400ms). + + In the same time frame a full jitter backoff would make more attempts + because of its random delay, realistically we can expect ~4-5. + + We can safely tripple this number, check for no more for 15 calls + and still be sure that we have delay working, because without it + we are looking at +1000 calls in the same time frame. + """ + try: + error = self.transient_errors[0] + resp = self.prepare_mock_with_error(error) + follower = ChangesFollower(self.client, db="db") + count = self.runner(follower, _Mode.FINITE, timeout=0.6) + except BaseException: + self.fail("There should be no exception.") + self.assertEqual(count, 0, "There should be no changes.") + self.assertLessEqual(resp.call_count, 15, "Call count should not exceed limit.") + + @responses.activate + def test_batch_size(self): + """ + Checks that setting includeDocs forces a calculation of batch size + and asserts the size. + + Mocks a DB of 500_000 docs of 523 bytes each to give an expected batch + size of 5125 + + 523 bytes + 500 bytes of changes overhead = 1023 bytes + 5 MiB / 1023 bytes = 5125 docs per batch + """ + self.prepare_mock_changes(batches=1) + follower = ChangesFollower( + self.client, db="db", error_tolerance=0, include_docs=True + ) + changes = follower.start_one_off() + next(changes) + params = responses.calls[1].request.params + self.assertEqual( + params["limit"], + "5125", + "Limit should be set to the expected value.", + ) + + @responses.activate + def test_batch_size_minimum(self): + """ + Checks that setting includeDocs forces a calculation of batch size + and asserts the size. + + Mocks a DB of 1 docs of less than 5 MiB size to give an expected batch + size of 0 + + Checks that the minimum batch_size of 1 is set. + """ + self.prepare_mock_changes( + batches=1, db_info_doc_count=1, db_info_doc_size=(5 * 1024 * 1024 - 1) + ) + follower = ChangesFollower( + self.client, db="db", error_tolerance=0, include_docs=True + ) + changes = follower.start_one_off() + next(changes) + params = responses.calls[1].request.params + self.assertEqual( + params["limit"], + "1", + "Limit should be set to the expected value.", + ) + + @responses.activate + def test_batch_size_with_limit(self): + """ + Checks that setting includeDocs and limit that below calculated + batch sets batch size to limit + """ + self.prepare_mock_changes(batches=1) + follower = ChangesFollower( + self.client, + db="db", + error_tolerance=0, + limit=1000, + include_docs=True, + ) + changes = follower.start_one_off() + next(changes) + params = responses.calls[1].request.params + self.assertEqual( + params["limit"], + "1000", + "Limit should be set to the expected value.", + ) + + +@pytest.mark.usefixtures("limits", "errors") +class TestChangesFollowerListen(ChangesFollowerBaseCase): + @responses.activate + def test_start(self): + """ + Checks that a LISTEN mode completes successfully (after stopping) + with some batches. + """ + try: + self.prepare_mock_changes(batches=3) + follower = ChangesFollower(self.client, db="db") + count = self.runner(follower, _Mode.LISTEN, timeout=2) + except BaseException: + self.fail("There should be no exception.") + self.assertGreater(count, 2 * _BATCH_SIZE + 1, "There should be some changes.") + + @responses.activate + def test_start_terminal_errors(self): + """ + Checks that a LISTEN mode errors for all terminal errors. + """ + for error in self.terminal_errors: + self.prepare_mock_with_error(error) + follower = ChangesFollower(self.client, db="db") + with self.assertRaisesRegex(ApiException, error): + self.runner(follower, _Mode.LISTEN, timeout=1) + + @responses.activate + def test_start_transient_errors_no_suppression(self): + """ + Checks that a LISTEN mode errors for all transient errors + when not suppressing. + """ + for error in self.transient_errors: + self.prepare_mock_with_error(error) + follower = ChangesFollower(self.client, db="db", error_tolerance=0) + if error == "bad_io": + with self.assertRaises(ConnectionError): + self.runner(follower, _Mode.LISTEN, timeout=1) + else: + if error == "bad_json": + error = "Error processing the HTTP response" + with self.assertRaisesRegex(ApiException, error): + self.runner(follower, _Mode.LISTEN, timeout=1) + + @responses.activate + def test_start_transient_errors_with_suppression_error_termination(self): + """ + Checks that a LISTEN mode errors for all transient errors + when exceeding the suppression duration. + """ + for error in self.transient_errors: + resp = self.prepare_mock_with_error(error) + follower = ChangesFollower(self.client, db="db", error_tolerance=100) + if error == "bad_io": + with self.assertRaises(ConnectionError): + self.runner(follower, _Mode.LISTEN, timeout=1) + else: + if error == "bad_json": + error = "Error processing the HTTP response" + with self.assertRaisesRegex(ApiException, error): + self.runner(follower, _Mode.LISTEN, timeout=1) + self.assertGreater(resp.call_count, 1, "Mock server should receive calls.") + + @responses.activate + def test_start_transient_errors_with_suppression_all_changes(self): + """ + Checks that a LISTEN mode gets changes and can be stopped cleanly + with transient errors when not exceeding the suppression duration. + """ + batches = 2 + self.prepare_mock_changes( + batches=batches, + errors=self.transient_errors, + ) + try: + follower = ChangesFollower(self.client, db="db", error_tolerance=300) + count = self.runner(follower, _Mode.LISTEN, timeout=1) + except BaseException: + self.fail("There should be no exception.") + self.assertEqual( + count, + batches * _BATCH_SIZE, + "There should be the correct number of changes.", + ) + + @responses.activate + def test_start_transient_errors_with_max_suppression(self): + """ + Checks that a LISTEN mode keeps running with transient errors + (until stopped cleanly) with max suppression. + """ + for error in self.transient_errors: + try: + resp = self.prepare_mock_with_error(error) + follower = ChangesFollower(self.client, db="db") + count = self.runner(follower, _Mode.LISTEN, timeout=1) + except BaseException: + self.fail("There should be no exception.") + self.assertEqual(count, 0, "There should be no changes.") + self.assertGreater(resp.call_count, 1, "Mock server should receive calls.") + + @responses.activate + def test_start_transient_errors_with_max_suppression_all_changes(self): + """ + Checks that a LISTEN mode runs through transient errors + with max suppression to receive changes until stopped. + """ + batches = 2 + self.prepare_mock_changes( + batches=batches, + errors=self.transient_errors, + ) + try: + follower = ChangesFollower(self.client, db="db") + count = self.runner(follower, _Mode.LISTEN, timeout=1) + except BaseException: + self.fail("There should be no exception.") + self.assertEqual( + count, + batches * _BATCH_SIZE, + "There should be the correct number of changes.", + ) + + @responses.activate + def test_stop(self): + """ + Checks calling stop for the LISTEN case. + """ + try: + self.prepare_mock_changes(batches=MAX_BATCHES) + follower = ChangesFollower(self.client, db="db") + start = timeit.default_timer() + count = self.runner(follower, _Mode.LISTEN, timeout=2, stop_after=1000) + stop = timeit.default_timer() - start + except BaseException: + self.fail("There should be no exception.") + self.assertGreaterEqual(count, 1000, "There should be some changes.") + self.assertLess(stop, 2, "The thread should have stopped before the wait time.") + + @responses.activate + def test_state_error(self): + """ + Checks that a LISTEN follower can only be started once. + """ + try: + self.prepare_mock_changes(batches=MAX_BATCHES) + follower = ChangesFollower(self.client, db="db") + self.runner(follower, _Mode.LISTEN, timeout=1, stop_after=1000) + with self.assertRaisesRegex( + RuntimeError, "Cannot start a feed that has already started." + ): + follower.start_one_off() + with self.assertRaisesRegex( + RuntimeError, "Cannot start a feed that has already started." + ): + follower.start() + except BaseException: + self.fail("There should be no exception.") + + @responses.activate + def test_limit(self): + """ + Checks that setting a limit terminates iterations early for LISTEN mode + and limits smaller, the same and larger than the default batch size. + """ + for limit in self.limits: + try: + self.prepare_mock_changes(batches=MAX_BATCHES) + follower = ChangesFollower(self.client, db="db", limit=limit) + count = self.runner(follower, _Mode.LISTEN, timeout=180) + except BaseException: + self.fail("There should be no exception.") + self.assertEqual( + count, + limit, + "There should be the correct number of changes.", + ) + + @responses.activate + def test_retry_delay(self): + """ + Checks that a LISTEN follower delays between retries. + See the FINITE version of the test for additional comments. + """ + try: + error = self.transient_errors[0] + resp = self.prepare_mock_with_error(error) + follower = ChangesFollower(self.client, db="db") + count = self.runner(follower, _Mode.LISTEN, timeout=0.6) + except BaseException: + self.fail("There should be no exception.") + self.assertEqual(count, 0, "There should be no changes.") + self.assertLessEqual(resp.call_count, 15, "Call count should not exceed limit.") diff --git a/test/unit/features/test_pagination_base.py b/test/unit/features/test_pagination_base.py new file mode 100644 index 00000000..85006d51 --- /dev/null +++ b/test/unit/features/test_pagination_base.py @@ -0,0 +1,406 @@ +# coding: utf-8 + +# © Copyright IBM Corporation 2025. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from collections.abc import Callable +from re import escape +from typing import Iterable +from unittest.mock import Mock, patch +from ibmcloudant.cloudant_v1 import ViewResult, ViewResultRow +from ibmcloudant.features.pagination import _BasePageIterator, _IteratorPager, Pager, PagerType, Pagination +from conftest import MockClientBaseCase, PaginationMockResponse + +class BasePageMockResponses(PaginationMockResponse): + """ + Test class for mocking page responses. + """ + def __init__(self, total_items: int, page_size: int): + super().__init__(total_items, page_size, PagerType.POST_VIEW) + # This test uses View structures, but doesn't do n+1 like a view/key pager + # Override plus_one_paging to accommodate this weird hybrid + self.plus_one_paging = False + +class BaseTestPageIterator(_BasePageIterator): + """ + A test subclass of the _BasePager under test. + """ + operation: Callable = None + page_keys: list[str] = [] + + def __init__(self, client, opts): + super().__init__(client, BaseTestPageIterator.operation or client.post_view, BaseTestPageIterator.page_keys, opts) + + def _result_converter(self) -> Callable[[dict], ViewResult]: + return lambda d: ViewResult.from_dict(d) + + def _items(self, result: ViewResult) -> tuple[ViewResultRow]: + return result.rows + + def _get_next_page_options(self, result: ViewResult) -> dict: + if len(result.rows ) == 0: + self.assertFail("Test failure: tried to setNextPageOptions on empty page.") + else: + return {'start_key': result.rows[-1].key} + +class TestBasePageIterator(MockClientBaseCase): + def test_init(self): + operation = self.client.post_view + opts = {'db': 'test', 'limit': 20} + page_iterator: Iterable[ViewResultRow] = BaseTestPageIterator(self.client, opts) + # Assert client is set + self.assertEqual(page_iterator._client, self.client, 'The supplied client should be set.') + # Assert operation is set + self.assertIsNotNone(page_iterator._next_request_function, 'The operation function should be set.') + # Assert partial function parts are as expected + self.assertEqual(page_iterator._next_request_function.func, operation, 'The partial function should be the operation.') + self.assertEqual(page_iterator._next_request_function.keywords, opts, 'The partial function kwargs should be the options.') + + def test_partial_options(self): + static_opts = {'db': 'test', 'limit': 20, 'baz': 'faz'} + page_opts = {'foo': 'boo', 'bar': 'far'} + opts = {**static_opts, **page_opts} + # Use page_opts.keys() to pass the list of names for page options + with patch('test_pagination_base.BaseTestPageIterator.page_keys', page_opts.keys()): + page_iterator: Iterable[ViewResultRow] = BaseTestPageIterator(self.client, opts) + # Assert partial function has only static opts + self.assertEqual(page_iterator._next_request_function.keywords, static_opts, 'The partial function kwargs should be only the static options.') + # Assert next page options + self.assertEqual(page_iterator._next_page_opts, page_opts, 'The next page options should match the expected.') + + def test_default_page_size(self): + opts = {'db': 'test'} + page_iterator: Iterable[ViewResultRow] = BaseTestPageIterator(self.client, opts) + # Assert the default page size + expected_page_size = 200 + self.assertEqual(page_iterator._page_size, expected_page_size, 'The default page size should be set.') + self.assertEqual(page_iterator._next_request_function.keywords, opts | {'limit': expected_page_size}, 'The default page size should be present in the options.') + + def test_limit_page_size(self): + opts = {'db': 'test', 'limit': 42} + page_iterator: Iterable[ViewResultRow] = BaseTestPageIterator(self.client, opts) + # Assert the provided page size + expected_page_size = 42 + self.assertEqual(page_iterator._page_size, expected_page_size, 'The default page size should be set.') + self.assertEqual(page_iterator._next_request_function.keywords, opts | {'limit': expected_page_size}, 'The default page size should be present in the options.') + + def test_has_next_initially_true(self): + opts = {'limit': 1} + page_iterator: Iterable[ViewResultRow] = BaseTestPageIterator(self.client, opts) + # Assert _has_next + self.assertTrue(page_iterator._has_next, '_has_next should initially return True.') + + def test_has_next_true_for_result_equal_to_limit(self): + page_size = 1 + # Init with mock that returns only a single row + with patch('test_pagination_base.BaseTestPageIterator.operation', BasePageMockResponses(1, page_size).get_next_page): + page_iterator: Iterable[ViewResultRow] = BaseTestPageIterator( + self.client, + {'limit': page_size}) + # Get first page with 1 result + next(page_iterator) + # Assert _has_next + self.assertTrue(page_iterator._has_next, '_has_next should return True.') + + def test_has_next_false_for_result_less_than_limit(self): + page_size = 1 + # Init with mock that returns zero rows + with patch('test_pagination_base.BaseTestPageIterator.operation', BasePageMockResponses(0, page_size).get_next_page): + page_iterator: Iterable[ViewResultRow] = BaseTestPageIterator( + self.client, + {'limit': page_size}) + # Get first page with 0 result + next(page_iterator) + # Assert _has_next + self.assertFalse(page_iterator._has_next, '_has_next should return False.') + + def test_next_first_page(self): + page_size = 25 + # Mock that returns one page of 25 items + mock = BasePageMockResponses(page_size, page_size) + with patch('test_pagination_base.BaseTestPageIterator.operation', mock.get_next_page): + page_iterator: Iterable[ViewResultRow] = BaseTestPageIterator( + self.client, + {'limit': page_size}) + # Get first page + actual_page: list[ViewResultRow] = next(page_iterator) + # Assert first page + self.assertSequenceEqual(actual_page, mock.get_expected_page(1), "The actual page should match the expected page") + + def test_next_two_pages(self): + page_size = 3 + # Mock that returns two pages of 3 items + mock = BasePageMockResponses(2*page_size, page_size) + with patch('test_pagination_base.BaseTestPageIterator.operation', mock.get_next_page): + page_iterator: Iterable[ViewResultRow] = BaseTestPageIterator( + self.client, + {'limit': page_size}) + # Get first page + actual_page_1: list[ViewResultRow] = next(page_iterator) + # Assert first page + self.assertSequenceEqual(actual_page_1, mock.get_expected_page(1), "The actual page should match the expected page") + # Assert has_next + self.assertTrue(page_iterator._has_next, '_has_next should return True.') + # Get second page + actual_page_2: list[ViewResultRow] = next(page_iterator) + # Assert first page + self.assertSequenceEqual(actual_page_2, mock.get_expected_page(2), "The actual page should match the expected page") + # Assert has_next, True since page is not smaller than limit + self.assertTrue(page_iterator._has_next, '_has_next should return True.') + + def test_next_until_empty(self): + page_size = 3 + # Mock that returns 3 pages of 3 items + mock = BasePageMockResponses(3*page_size, page_size) + with patch('test_pagination_base.BaseTestPageIterator.operation', mock.get_next_page): + page_iterator: Iterable[ViewResultRow] = BaseTestPageIterator( + self.client, + {'limit': page_size}) + page_count = 0 + actual_items = [] + while page_iterator._has_next: + page_count += 1 + page = next(page_iterator) + # Assert each page is the same or smaller than the limit to confirm all results not in one page + self.assertTrue(len(page) <= page_size, "The actual page size should be the expected page size.") + actual_items.extend(page) + self.assertSequenceEqual(actual_items, mock.all_expected_items(), "The results should match all the pages.") + self.assertEqual(page_count, len(mock.expected_pages), "There should be the correct number of pages.") + + def test_next_until_smaller(self): + page_size = 3 + # Mock that returns 3 pages of 3 items, then 1 more page with 1 item + mock = BasePageMockResponses(3*page_size + 1, page_size) + with patch('test_pagination_base.BaseTestPageIterator.operation', mock.get_next_page): + page_iterator: Iterable[ViewResultRow] = BaseTestPageIterator( + self.client, + {'limit': page_size}) + page_count = 0 + actual_items = [] + while page_iterator._has_next: + page_count += 1 + page = next(page_iterator) + # Assert each page is the same or smaller than the limit to confirm all results not in one page + self.assertTrue(len(page) <= page_size, "The actual page size should be the expected page size.") + actual_items.extend(page) + self.assertSequenceEqual(actual_items, mock.all_expected_items(), "The results should match all the pages.") + self.assertEqual(page_count, len(mock.expected_pages), "There should be the correct number of pages.") + + def test_next_exception(self): + page_size = 2 + # Mock that returns one page of one item + mock = BasePageMockResponses(page_size - 1, page_size) + with patch('test_pagination_base.BaseTestPageIterator.operation', mock.get_next_page): + page_iterator: Iterable[ViewResultRow] = BaseTestPageIterator( + self.client, + {'limit': page_size}) + # Get first and only page + actual_page: list[ViewResultRow] = next(page_iterator) + # Assert page + self.assertSequenceEqual(actual_page, mock.get_expected_page(1), "The actual page should match the expected page") + # Assert _has_next now False + self.assertFalse(page_iterator._has_next, '_has_next should return False.') + # Assert StopIteraton on get_next() + with self.assertRaises(StopIteration): + next(page_iterator) + + def test_pages_immutable(self): + page_size = 1 + mock = BasePageMockResponses(page_size, page_size) + with patch('test_pagination_base.BaseTestPageIterator.operation', mock.get_next_page): + page_iterator: Iterable[ViewResultRow] = BaseTestPageIterator( + self.client, + {'limit': page_size}) + # Get page + actual_page: list[ViewResultRow] = next(page_iterator) + # Assert immutable tuple type + self.assertIsInstance(actual_page, tuple) + + def test_set_next_page_options(self): + page_size = 1 + mock = BasePageMockResponses(5*page_size, page_size) + with patch('test_pagination_base.BaseTestPageIterator.operation', mock.get_next_page): + page_iterator: Iterable[ViewResultRow] = BaseTestPageIterator( + self.client, + {'limit': page_size}) + self.assertIsNone(page_iterator._next_page_opts.get('start_key'), "The start key should intially be None.") + # Since we use a page size of 1, each next page options key, is the same as the element from the page and the page count + page_count = 0 + while page_iterator._has_next: + page = next(page_iterator) + if page_iterator._has_next: + self.assertEqual(page_count, page_iterator._next_page_opts.get('start_key'), "The key should increment per page.") + else: + self.assertEqual(page_count - 1, page_iterator._next_page_opts.get('start_key'), "The options should not be set for the final page.") + page_count += 1 + + def test_next_resumes_after_error(self): + page_size = 1 + mock = BasePageMockResponses(3*page_size, page_size) + with patch('test_pagination_base.BaseTestPageIterator.operation', mock.get_next_page): + page_iterator: Iterable[ViewResultRow] = BaseTestPageIterator( + self.client, + {'limit': page_size}) + self.assertIsNone(page_iterator._next_page_opts.get('start_key'), "The start key should intially be None.") + actual_page = next(page_iterator) + self.assertSequenceEqual(actual_page, mock.get_expected_page(1), "The actual page should match the expected page") + self.assertEqual(0, page_iterator._next_page_opts.get('start_key'), "The start_key should be 0 for the second page.") + with patch('ibmcloudant.features.pagination._BasePageIterator._next_request', Exception('test exception')): + with self.assertRaises(Exception): + next(page_iterator) + self.assertTrue(page_iterator._has_next, '_has_next should return True.') + self.assertEqual(0, page_iterator._next_page_opts.get('start_key'), "The start_key should still be 0 for the second page.") + second_page = next(page_iterator) + self.assertSequenceEqual(second_page, mock.get_expected_page(2), "The actual page should match the expected page") + self.assertTrue(page_iterator._has_next, '_has_next should return False.') + + + def test_pages_iterable(self): + page_size = 23 + mock = BasePageMockResponses(3*page_size-1, page_size) + pagination = Pagination(self.client, BaseTestPageIterator, {'limit': page_size}) + with patch('test_pagination_base.BaseTestPageIterator.operation', mock.get_next_page): + # Check pages are iterable + page_number = 0 + for page in pagination.pages(): + page_number += 1 + self.assertSequenceEqual(page, mock.get_expected_page(page_number), "The actual page should match the expected page") + # Asser the correct number of pages + self.assertEqual(page_number, 3, 'There should have been 3 pages.') + + def test_rows_iterable(self): + page_size = 23 + mock = BasePageMockResponses(3*page_size-1, page_size) + pagination = Pagination(self.client, BaseTestPageIterator, {'limit': page_size}) + with patch('test_pagination_base.BaseTestPageIterator.operation', mock.get_next_page): + actual_items = [] + # Check rows are iterable + for row in pagination.rows(): + actual_items.append(row) + self.assertSequenceEqual(actual_items, mock.all_expected_items(), "The actual rows should match the expected rows.") + + def test_as_pager_get_next_first_page(self): + page_size = 7 + # Mock that returns two pages of 7 items + mock = BasePageMockResponses(2*page_size, page_size) + pagination = Pagination(self.client, BaseTestPageIterator, {'limit': page_size}) + with patch('test_pagination_base.BaseTestPageIterator.operation', mock.get_next_page): + pager = pagination.pager() + # Get first page + actual_page: list[ViewResultRow] = pager.get_next() + self.assertSequenceEqual(actual_page, mock.get_expected_page(1), "The actual page should match the expected page") + + def test_as_pager_get_all(self): + page_size = 11 + # Mock that returns 6 pages of 11 items, then 1 more page with 5 items + mock = BasePageMockResponses(71, page_size) + pagination = Pagination(self.client, BaseTestPageIterator, {'limit': page_size}) + with patch('test_pagination_base.BaseTestPageIterator.operation', mock.get_next_page): + pager: Pager[ViewResultRow] = pagination.pager() + actual_items = pager.get_all() + self.assertSequenceEqual(actual_items, mock.all_expected_items(), "The results should match all the pages.") + # Assert consumed state prevents calling again + with self.assertRaisesRegex(Exception, escape(_IteratorPager._state_consumed_msg)): + pager.get_all() + + def test_as_pager_get_all_restarts_after_error(self): + page_size = 1 + mock = BasePageMockResponses(2*page_size, page_size) + first_page = mock.get_next_page() + # mock response order + # first page, error, first page replay, second page + mockmock = Mock(side_effect=[ + first_page, + Exception('test exception'), + first_page, + mock.get_next_page() + ]) + pagination = Pagination(self.client, BaseTestPageIterator, {'limit': page_size}) + with patch('test_pagination_base.BaseTestPageIterator.operation', mockmock): + pager = pagination.pager() + with self.assertRaises(Exception): + pager.get_all() + self.assertSequenceEqual(pager.get_all(), mock.all_expected_items(), "The results should match all the pages.") + + def test_as_pager_get_next_get_all_raises(self): + page_size = 11 + # Mock that returns 6 pages of 11 items, then 1 more page with 5 items + mock = BasePageMockResponses(71, page_size) + pagination = Pagination(self.client, BaseTestPageIterator, {'limit': page_size}) + with patch('test_pagination_base.BaseTestPageIterator.operation', mock.get_next_page): + pager: Pager[ViewResultRow] = pagination.pager() + first_page = pager.get_next() + self.assertSequenceEqual(first_page, mock.get_expected_page(1), "The actual page should match the expected page") + # Assert raises + with self.assertRaisesRegex(Exception, escape(_IteratorPager._state_mixed_msg)): + pager.get_all() + # Assert second page ok + self.assertSequenceEqual(pager.get_next(), mock.get_expected_page(2), "The actual page should match the expected page") + + def test_as_pager_get_all_get_next_raises(self): + page_size = 1 + mock = BasePageMockResponses(2*page_size, page_size) + first_page = mock.get_next_page() + # mock response order + # first page, error, first page replay, second page + mockmock = Mock(side_effect=[ + first_page, + Exception('test exception') + ]) + pagination = Pagination(self.client, BaseTestPageIterator, {'limit': page_size}) + with patch('test_pagination_base.BaseTestPageIterator.operation', mockmock): + pager = pagination.pager() + # Stop get all part way through so it isn't consumed when we call get Next + with self.assertRaises(Exception): + pager.get_all() + # Assert calling get_next() raises + with self.assertRaisesRegex(Exception, escape(_IteratorPager._state_mixed_msg)): + pager.get_next() + + def test_as_pager_get_next_resumes_after_error(self): + page_size = 1 + mock = BasePageMockResponses(2*page_size, page_size) + # mock response order + # first page, error, second page + mockmock = Mock(side_effect=[ + mock.get_next_page(), + Exception('test exception'), + mock.get_next_page() + ]) + pagination = Pagination(self.client, BaseTestPageIterator, {'limit': page_size}) + with patch('test_pagination_base.BaseTestPageIterator.operation', mockmock): + pager = pagination.pager() + # Assert first page + self.assertSequenceEqual(pager.get_next(), mock.get_expected_page(1), "The actual page should match the expected page") + with self.assertRaises(Exception): + pager.get_next() + # Assert second page after error + self.assertSequenceEqual(pager.get_next(), mock.get_expected_page(2), "The actual page should match the expected page") + + def test_as_pager_get_next_until_consumed(self): + page_size = 7 + # Mock that returns two pages of 7 items + mock = BasePageMockResponses(2*page_size, page_size) + pagination = Pagination(self.client, BaseTestPageIterator, {'limit': page_size}) + with patch('test_pagination_base.BaseTestPageIterator.operation', mock.get_next_page): + pager = pagination.pager() + page_count = 0 + while pager.has_next(): + page_count += 1 + self.assertSequenceEqual(pager.get_next(), mock.get_expected_page(page_count), "The actual page should match the expected page") + # Note 3 because third page is empty + self.assertEqual(page_count, 3, 'There should be the expected number of pages.') + # Assert consumed state prevents calling again + with self.assertRaisesRegex(Exception, escape(_IteratorPager._state_consumed_msg)): + pager.get_next() diff --git a/test/unit/features/test_pagination_bookmark.py b/test/unit/features/test_pagination_bookmark.py new file mode 100644 index 00000000..e9ede072 --- /dev/null +++ b/test/unit/features/test_pagination_bookmark.py @@ -0,0 +1,124 @@ +# coding: utf-8 + +# © Copyright IBM Corporation 2025. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from collections.abc import Callable, Iterator +from unittest.mock import patch +from ibmcloudant.cloudant_v1 import SearchResult, SearchResultRow +from ibmcloudant.features.pagination import _BookmarkPageIterator, PagerType, Pagination +from conftest import MockClientBaseCase, PaginationMockResponse + +class BookmarkTestPageIterator(_BookmarkPageIterator): + """ + A test subclass of the _BookmarkPager under test. + """ + operation: Callable = None + boundary_func: Callable = lambda p,l: None + + def __init__(self, client, opts): + super().__init__(client, BookmarkTestPageIterator.operation or client.post_search, opts) + + def _result_converter(self) -> Callable[[dict], SearchResult]: + return lambda d: SearchResult.from_dict(d) + + def _items(self, result: SearchResult) -> tuple[SearchResultRow]: + return result.rows + +class BookmarkPaginationMockResponses(PaginationMockResponse): + """ + Test class for mocking page responses. + """ + def __init__(self, total_items: int, page_size: int): + super().__init__(total_items, page_size, PagerType.POST_SEARCH) + +class TestBookmarkPageIterator(MockClientBaseCase): + + # Test page size default + def test_default_page_size(self): + page_iterator: Iterator[tuple[SearchResultRow]] = BookmarkTestPageIterator(self.client, {}) + # Assert the limit default as page size + self.assertEqual(page_iterator._page_size, 200, 'The page size should be one more than the default limit.') + + # Test page size limit + def test_limit_page_size(self): + page_iterator: Iterator[tuple[SearchResultRow]] = BookmarkTestPageIterator(self.client, {'limit': 42}) + # Assert the limit provided as page size + self.assertEqual(page_iterator._page_size, 42, 'The page size should be one more than the default limit.') + + # Test all items on page when no more pages + def test_get_next_page_less_than_limit(self): + page_size = 21 + mock = BookmarkPaginationMockResponses(page_size - 1, page_size) + with patch('test_pagination_bookmark.BookmarkTestPageIterator.operation', mock.get_next_page): + page_iterator = BookmarkTestPageIterator(self.client, {'limit': page_size}) + # Get and assert first page + actual_page = next(page_iterator) + self.assertSequenceEqual(actual_page, mock.get_expected_page(1), 'The actual page should match the expected page') + # Assert page size + self.assertEqual(len(actual_page), page_size - 1, 'The actual page size should match the expected page size.') + # Assert has_next False because n+1 limit is 1 more than user page size + self.assertFalse(page_iterator._has_next, '_has_next should return False.') + + # Test correct items on page when limit + def test_get_next_page_equal_to_limit(self): + page_size = 14 + mock = BookmarkPaginationMockResponses(page_size, page_size) + with patch('test_pagination_bookmark.BookmarkTestPageIterator.operation', mock.get_next_page): + page_iterator = BookmarkTestPageIterator(self.client, {'limit': page_size}) + # Get and assert first page + actual_page = next(page_iterator) + self.assertSequenceEqual(actual_page, mock.get_expected_page(1), 'The actual page should match the expected page.') + # Assert page size + self.assertEqual(len(actual_page), page_size, 'The actual page size should match the expected page size.') + # Assert has_next True + self.assertTrue(page_iterator._has_next, '_has_next should return True.') + # Assert bookmark + self.assertEqual(page_iterator._next_page_opts['bookmark'], f'testdoc{page_size - 1}', 'The bookmark should be one less than the page size.') + # Get and assert second page + second_page = next(page_iterator) + # Note row keys are zero indexed so page size - 1 + self.assertEqual(len(second_page), 0, "The second page should be empty.") + self.assertFalse(page_iterator._has_next, '_has_next should return False.') + + # Test correct items on page when n+more + def test_get_next_page_greater_than_limit(self): + page_size = 7 + mock = BookmarkPaginationMockResponses(page_size+2, page_size) + with patch('test_pagination_bookmark.BookmarkTestPageIterator.operation', mock.get_next_page): + page_iterator = BookmarkTestPageIterator(self.client, {'limit': page_size}) + # Get and assert first page + actual_page = next(page_iterator) + self.assertSequenceEqual(actual_page, mock.get_expected_page(1), 'The actual page should match the expected page.') + # Assert page size + self.assertEqual(len(actual_page), page_size, 'The actual page size should match the expected page size.') + # Assert has_next True + self.assertTrue(page_iterator._has_next, '_has_next should return True.') + # Get and assert second page + second_page = next(page_iterator) + self.assertEqual(len(second_page), 2 , 'The second page should have two items.') + # Note row keys are zero indexed so n+1 element that is first item on second page matches page size + self.assertEqual(second_page[0].id, f'testdoc{page_size}', 'The first item key on the second page should match the page size number.') + self.assertSequenceEqual(second_page, mock.get_expected_page(2), "The actual page should match the expected page") + self.assertFalse(page_iterator._has_next, '_has_next should return False.') + + # Test getting all items + def test_get_all(self): + page_size = 3 + mock = BookmarkPaginationMockResponses(page_size*12, page_size) + pagination = Pagination(self.client, BookmarkTestPageIterator, {'limit': page_size}) + with patch('test_pagination_bookmark.BookmarkTestPageIterator.operation', mock.get_next_page): + pager = pagination.pager() + # Get and assert all items + self.assertSequenceEqual(pager.get_all(), mock.all_expected_items(), 'The results should match all the pages.') diff --git a/test/unit/features/test_pagination_key.py b/test/unit/features/test_pagination_key.py new file mode 100644 index 00000000..16faf35f --- /dev/null +++ b/test/unit/features/test_pagination_key.py @@ -0,0 +1,181 @@ +# coding: utf-8 + +# © Copyright IBM Corporation 2025. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from collections.abc import Callable, Iterator +from unittest.mock import Mock, patch +from ibm_cloud_sdk_core import DetailedResponse +from ibmcloudant.cloudant_v1 import ViewResult, ViewResultRow +from ibmcloudant.features.pagination import _KeyPageIterator, PagerType, Pagination +from conftest import MockClientBaseCase, PaginationMockResponse + +class KeyTestPageIterator(_KeyPageIterator): + """ + A test subclass of the _KeyPager under test. + """ + operation: Callable = None + boundary_func: Callable = lambda p,l: None + + def __init__(self, client, opts): + super().__init__(client, KeyTestPageIterator.operation or client.post_view, opts) + + def _result_converter(self) -> Callable[[dict], ViewResult]: + return lambda d: ViewResult.from_dict(d) + + def _items(self, result: ViewResult) -> tuple[ViewResultRow]: + return result.rows + + def _get_next_page_options(self, result: ViewResult) -> dict: + if len(result.rows ) == 0: + self.assertFail("Test failure: tried to setNextPageOptions on empty page.") + else: + return {'start_key': result.rows[-1].key} + + def check_boundary(self, penultimate_item, last_item): + return KeyTestPageIterator.boundary_func(penultimate_item, last_item) + +class KeyPaginationMockResponses(PaginationMockResponse): + """ + Test class for mocking page responses. + """ + def __init__(self, total_items: int, page_size: int): + super().__init__(total_items, page_size, PagerType.POST_VIEW) + +class TestKeyPageIterator(MockClientBaseCase): + + # Test page size default (+1) + def test_default_page_size(self): + page_iterator: Iterator[tuple[ViewResultRow]] = KeyTestPageIterator(self.client, {}) + # Assert the limit default as page size + self.assertEqual(page_iterator._page_size, 201, 'The page size should be one more than the default limit.') + + # Test page size limit (+1) + def test_limit_page_size(self): + page_iterator: Iterator[tuple[ViewResultRow]] = KeyTestPageIterator(self.client, {'limit': 42}) + # Assert the limit provided as page size + self.assertEqual(page_iterator._page_size, 43, 'The page size should be one more than the default limit.') + + # Test all items on page when no more pages + def test_get_next_page_less_than_limit(self): + page_size = 21 + mock = KeyPaginationMockResponses(page_size, page_size) + with patch('test_pagination_key.KeyTestPageIterator.operation', mock.get_next_page): + page_iterator = KeyTestPageIterator(self.client, {'limit': page_size}) + # Get and assert first page + actual_page = next(page_iterator) + self.assertSequenceEqual(actual_page, mock.get_expected_page(1), 'The actual page should match the expected page') + # Assert page size + self.assertEqual(len(actual_page), page_size, 'The actual page size should match the expected page size.') + # Assert has_next False because n+1 limit is 1 more than user page size + self.assertFalse(page_iterator._has_next, '_has_next should return False.') + + # Test correct items on page when n+1 + def test_get_next_page_equal_to_limit(self): + page_size = 14 + mock = KeyPaginationMockResponses(page_size+1, page_size) + with patch('test_pagination_key.KeyTestPageIterator.operation', mock.get_next_page): + page_iterator = KeyTestPageIterator(self.client, {'limit': page_size}) + # Get and assert first page + actual_page = next(page_iterator) + self.assertSequenceEqual(actual_page, mock.get_expected_page(1), 'The actual page should match the expected page.') + # Assert page size + self.assertEqual(len(actual_page), page_size, 'The actual page size should match the expected page size.') + # Assert has_next True + self.assertTrue(page_iterator._has_next, '_has_next should return True.') + # Get and assert second page + second_page = next(page_iterator) + self.assertEqual(len(second_page), 1 , 'The second page should have one item.') + # Note row keys are zero indexed so n+1 element that is first item on second page matches page size + self.assertEqual(second_page[0].key, page_size, 'The first item key on the second page should match the page size number.') + self.assertSequenceEqual(second_page, mock.get_expected_page(2), "The actual page should match the expected page") + self.assertFalse(page_iterator._has_next, '_has_next should return False.') + + # Test correct items on page when n+more + def test_get_next_page_greater_than_limit(self): + page_size = 7 + mock = KeyPaginationMockResponses(page_size+2, page_size) + with patch('test_pagination_key.KeyTestPageIterator.operation', mock.get_next_page): + page_iterator = KeyTestPageIterator(self.client, {'limit': page_size}) + # Get and assert first page + actual_page = next(page_iterator) + self.assertSequenceEqual(actual_page, mock.get_expected_page(1), 'The actual page should match the expected page.') + # Assert page size + self.assertEqual(len(actual_page), page_size, 'The actual page size should match the expected page size.') + # Assert has_next True + self.assertTrue(page_iterator._has_next, '_has_next should return True.') + # Get and assert second page + second_page = next(page_iterator) + self.assertEqual(len(second_page), 2 , 'The second page should have two items.') + # Note row keys are zero indexed so n+1 element that is first item on second page matches page size + self.assertEqual(second_page[0].key, page_size, 'The first item key on the second page should match the page size number.') + self.assertSequenceEqual(second_page, mock.get_expected_page(2), "The actual page should match the expected page") + self.assertFalse(page_iterator._has_next, '_has_next should return False.') + + # Test getting all items + def test_get_all(self): + page_size = 3 + mock = KeyPaginationMockResponses(page_size*12, page_size) + pagination = Pagination(self.client, KeyTestPageIterator, {'limit': page_size}) + with patch('test_pagination_key.KeyTestPageIterator.operation', mock.get_next_page): + pager = pagination.pager() + # Get and assert all items + self.assertSequenceEqual(pager.get_all(), mock.all_expected_items(), 'The results should match all the pages.') + + def test_no_boundary_check_by_default(self): + mock_rows = [ + {'id': '1', 'key': 1, 'value': 1}, + {'id': '1', 'key': 1, 'value': 1} + ] + expected_rows = ViewResult.from_dict({'rows': mock_rows}).rows + mockmock = Mock(return_value=DetailedResponse(response={'rows': mock_rows})) + with patch('test_pagination_key.KeyTestPageIterator.operation', mockmock): + page_iterator = KeyTestPageIterator(self.client, {'limit': 1}) + # Get and assert page + self.assertSequenceEqual(next(page_iterator), (expected_rows[0],)) + + def test_boundary_failure_throws_on_get_next(self): + mock_rows = [ + {'id': '1', 'key': 1, 'value': 1}, + {'id': '1', 'key': 1, 'value': 1} + ] + expected_rows = ViewResult.from_dict({'rows': mock_rows}).rows + mockmock = Mock(return_value=DetailedResponse(response={'rows': mock_rows})) + with patch('test_pagination_key.KeyTestPageIterator.operation', mockmock): + page_iterator = KeyTestPageIterator(self.client, {'limit': 1}) + with patch( + 'test_pagination_key.KeyTestPageIterator.boundary_func', + lambda p,l: 'test error' if p.id == l.id and p.key == l.key else None): + # Get and assert page + self.assertSequenceEqual(next(page_iterator), (expected_rows[0],)) + # Assert has_next True + self.assertTrue(page_iterator._has_next, '_has_next should return True.') + with self.assertRaises(Exception): + next(page_iterator) + + def test_no_boundary_check_when_no_items_left(self): + mock_rows = [ + {'id': '1', 'key': 1, 'value': 1} + ] + expected_rows = ViewResult.from_dict({'rows': mock_rows}).rows + mockmock = Mock(return_value=DetailedResponse(response={'rows': mock_rows})) + with patch('test_pagination_key.KeyTestPageIterator.operation', mockmock): + page_iterator = KeyTestPageIterator(self.client, {'limit': 1}) + with patch( + 'test_pagination_key.KeyTestPageIterator.boundary_func', + Exception('Check boundary should not be called.')): + # Get and assert page if boundary is checked, will raise exception + self.assertSequenceEqual(next(page_iterator), (expected_rows[0],)) + # Assert has_next False + self.assertFalse(page_iterator._has_next, '_has_next should return True.') diff --git a/test/unit/features/test_pagination_operations.py b/test/unit/features/test_pagination_operations.py new file mode 100644 index 00000000..e1b90c81 --- /dev/null +++ b/test/unit/features/test_pagination_operations.py @@ -0,0 +1,207 @@ +# coding: utf-8 + +# © Copyright IBM Corporation 2025. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from unittest.mock import patch + +import pytest +from conftest import MockClientBaseCase, PaginationMockSupport, PaginationMockResponse +from ibmcloudant.features.pagination import _MAX_LIMIT, Pager, PagerType, Pagination + +@pytest.mark.usefixtures("errors") +class TestPaginationOperations(MockClientBaseCase): + + test_page_size = 10 + # tuples of test parameters + # total items, page size + page_sets = ( + (0, test_page_size), + (1, test_page_size), + (test_page_size - 1, test_page_size), + (test_page_size, test_page_size), + (test_page_size + 1, test_page_size), + (3 * test_page_size, test_page_size), + (3 * test_page_size + 1, test_page_size), + (4 * test_page_size - 1, test_page_size), + ) + + def get_expected_pages(self, total: int, page_size: int, pager_type: PagerType) -> int: + full_pages = total // page_size + partial_pages = 0 if (total % page_size == 0) else 1 + expected_pages = full_pages + partial_pages + # Need at least 1 empty page to know there are no more results + # if not ending on a partial page, except if the first page or + # using n+1 paging (because an exact user page is a partial real page). + if partial_pages == 0 and (expected_pages == 0 or pager_type not in PaginationMockSupport.key_pagers): + expected_pages += 1; # Will get at least 1 empty page + return expected_pages + + def test_pager(self): + for pager_type in PagerType: + with self.subTest(pager_type): + for page_set in self.page_sets: + with self.subTest(page_set): + actual_items: set = set() + actual_item_count: int = 0 + actual_page_count: int = 0 + expected_items_count: int = page_set[0] + page_size: int = page_set[1] + expected_page_count: int = self.get_expected_pages(expected_items_count, page_size, pager_type) + with patch(PaginationMockSupport.operation_map[pager_type], PaginationMockResponse(expected_items_count, page_size, pager_type).get_next_page): + pager: Pager = Pagination.new_pagination(self.client, pager_type, limit=page_size).pager() + while(pager.has_next()): + page = pager.get_next() + actual_page_count += 1 + actual_item_count += len(page) + for row in page: + if pager_type in (PagerType.POST_FIND, PagerType.POST_PARTITION_FIND): + id = row._id + else: + id = row.id + actual_items.add(id) + self.assertEqual(actual_page_count, expected_page_count, 'There should be the expected number of pages.') + self.assertEqual(actual_item_count, expected_items_count, 'There should be the expected number of items.') + self.assertEqual(len(actual_items), expected_items_count, 'The items should be unique.') + + def test_pages(self): + for pager_type in PagerType: + with self.subTest(pager_type): + for page_set in self.page_sets: + with self.subTest(page_set): + actual_items: set = set() + actual_item_count: int = 0 + actual_page_count: int = 0 + expected_items_count: int = page_set[0] + page_size: int = page_set[1] + expected_page_count: int = self.get_expected_pages(expected_items_count, page_size, pager_type) + with patch(PaginationMockSupport.operation_map[pager_type], PaginationMockResponse(expected_items_count, page_size, pager_type).get_next_page): + for page in Pagination.new_pagination(self.client, pager_type, limit=page_size).pages(): + actual_page_count += 1 + actual_item_count += len(page) + for row in page: + if pager_type in (PagerType.POST_FIND, PagerType.POST_PARTITION_FIND): + id = row._id + else: + id = row.id + actual_items.add(id) + self.assertEqual(actual_page_count, expected_page_count, 'There should be the expected number of pages.') + self.assertEqual(actual_item_count, expected_items_count, 'There should be the expected number of items.') + self.assertEqual(len(actual_items), expected_items_count, 'The items should be unique.') + + def test_rows(self): + for pager_type in PagerType: + with self.subTest(pager_type): + for page_set in self.page_sets: + with self.subTest(page_set): + actual_items: set = set() + actual_item_count: int = 0 + expected_items_count: int = page_set[0] + page_size: int = page_set[1] + with patch(PaginationMockSupport.operation_map[pager_type], PaginationMockResponse(expected_items_count, page_size, pager_type).get_next_page): + for row in Pagination.new_pagination(self.client, pager_type, limit=page_size).rows(): + actual_item_count += 1 + if pager_type in (PagerType.POST_FIND, PagerType.POST_PARTITION_FIND): + id = row._id + else: + id = row.id + actual_items.add(id) + self.assertEqual(actual_item_count, expected_items_count, 'There should be the expected number of items.') + self.assertEqual(len(actual_items), expected_items_count, 'The items should be unique.') + + def test_pager_errors(self): + page_size = _MAX_LIMIT + for pager_type in PagerType: + with self.subTest(pager_type): + for error in (*self.terminal_errors, *self.transient_errors): + expected_exception = self.make_error_exception(error) + with self.subTest(error): + # mock responses + mock_pages = PaginationMockResponse(2*page_size, page_size, pager_type) + mock_first_page = mock_pages.get_next_page() + mock_second_page = mock_pages.get_next_page() + mock_third_page = mock_pages.get_next_page() # empty + for responses in ( + # first sub-test, error on first page + (expected_exception, mock_first_page, mock_second_page, mock_third_page), + # second sub-test error on second page + (mock_first_page, expected_exception, mock_second_page, mock_third_page), + ): + with patch(PaginationMockSupport.operation_map[pager_type], side_effect=iter(responses)): + pager: Pager = Pagination.new_pagination(self.client, pager_type, limit=page_size).pager() + expect_exception_on_page: int = responses.index(expected_exception) + 1 + actual_page_count: int = 0 + while (pager.has_next()): + actual_page_count += 1 + if actual_page_count == expect_exception_on_page: + with self.assertRaises(type(expected_exception), msg='There should be an exception while paging.'): + pager.get_next() + else: + pager.get_next() + + def test_pages_errors(self): + page_size = _MAX_LIMIT + for pager_type in PagerType: + with self.subTest(pager_type): + for error in (*self.terminal_errors, *self.transient_errors): + expected_exception = self.make_error_exception(error) + with self.subTest(error): + for responses in ( + # first sub-test, error on first page + (expected_exception,), + # second sub-test error on second page + (PaginationMockResponse(2*page_size, page_size, pager_type).get_next_page(), expected_exception), + ): + with patch(PaginationMockSupport.operation_map[pager_type], side_effect=iter(responses)): + actual_page_count: int = 0 + expected_page_count: int = len(responses) - 1 + with self.assertRaises(type(expected_exception), msg='There should be an exception while paging.'): + for page in Pagination.new_pagination(self.client, pager_type, limit=page_size).pages(): + actual_page_count += 1 + self.assertEqual(actual_page_count, expected_page_count, 'Should have got the correct number of pages before error.') + + def test_rows_errors(self): + page_size = _MAX_LIMIT + for pager_type in PagerType: + with self.subTest(pager_type): + for error in (*self.terminal_errors, *self.transient_errors): + expected_exception = self.make_error_exception(error) + with self.subTest(error): + for responses in ( + # first sub-test, error on first page + (expected_exception,), + # second sub-test error on second page + (PaginationMockResponse(2*page_size, page_size, pager_type).get_next_page(), expected_exception), + ): + with patch(PaginationMockSupport.operation_map[pager_type], side_effect=iter(responses)): + actual_item_count: int = 0 + expected_item_count: int = page_size * (len(responses) - 1) + with self.assertRaises(type(expected_exception), msg='There should be an exception while paging.'): + for row in Pagination.new_pagination(self.client, pager_type, limit=page_size).rows(): + actual_item_count += 1 + self.assertEqual(actual_item_count, expected_item_count, 'Should have got the correct number of items before error.') + + # Test skip omitted from subsequent page requests + # Applies to key pagers and find pagers + def test_skip_removed_for_subsquent_page(self): + page_size = 14 + for pager_type in (PaginationMockSupport.key_pagers + PaginationMockSupport.find_pagers): + with self.subTest(pager_type): + with patch(PaginationMockSupport.operation_map[pager_type], PaginationMockResponse(2*page_size, page_size, pager_type).get_next_page): + pager = Pagination.new_pagination(self.client, pager_type, limit=page_size, skip=1).pager() + # Assert first page has skip option + self.assertEqual(pager._iterator._next_page_opts['skip'], 1, 'The skip option should be 1 for the first page') + pager.get_next() + # Assert second page has no skip option + self.assertIsNone(pager._iterator._next_page_opts.get('skip'), 'The skip option should be absent for the next page') diff --git a/test/unit/features/test_pagination_option_validation.py b/test/unit/features/test_pagination_option_validation.py new file mode 100644 index 00000000..c2716425 --- /dev/null +++ b/test/unit/features/test_pagination_option_validation.py @@ -0,0 +1,73 @@ +# coding: utf-8 + +# © Copyright IBM Corporation 2025. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from unittest import TestCase +from ibmcloudant.features.pagination import _DOCS_KEY_ERROR, _MIN_LIMIT, _MAX_LIMIT, _VIEW_KEY_ERROR, PagerType, Pagination + +class TestPaginationOptionValidation(TestCase): + + all_doc_paginations = (PagerType.POST_ALL_DOCS, PagerType.POST_PARTITION_ALL_DOCS, PagerType.POST_DESIGN_DOCS) + find_paginations = (PagerType.POST_PARTITION_FIND, PagerType.POST_FIND) + search_paginations = (PagerType.POST_SEARCH, PagerType.POST_PARTITION_SEARCH) + view_paginations = (PagerType.POST_PARTITION_VIEW, PagerType.POST_VIEW) + view_like_paginations = (*all_doc_paginations, *view_paginations) + all_paginations = (*find_paginations, *search_paginations, *view_like_paginations) + + def test_valid_limits(self): + test_limits = (_MIN_LIMIT, _MAX_LIMIT - 1, _MAX_LIMIT, None) + for limit in test_limits: + for pager_type in self.all_paginations: + with self.subTest(pager_type): + try: + Pagination.new_pagination(None, pager_type, limit=limit) + except ValueError: + self.fail('There should be no ValueError for valid limits.') + + def test_invalid_limits(self): + test_limits = (_MIN_LIMIT - 1, _MAX_LIMIT + 1) + for limit in test_limits: + if (limit == _MIN_LIMIT -1): + msg_regex = f'The provided limit {limit} is lower than the minimum page size value of 1.' + elif (limit == _MAX_LIMIT + 1): + msg_regex= f'The provided limit {limit} exceeds the maximum page size value of 200.' + for pager_type in self.all_paginations: + with self.subTest(pager_type): + with self.assertRaisesRegex(ValueError, msg_regex): + Pagination.new_pagination(None, pager_type, limit=limit) + + def test_keys_value_error_for_view_like(self): + for pager_type in self.view_like_paginations: + with self.subTest(pager_type): + with self.assertRaisesRegex(ValueError, 'The option \'keys\' is invalid when using pagination.'): + Pagination.new_pagination(None, pager_type, keys=['a','b','c']) + + def test_facet_value_errors_for_search(self): + for invalid_opt in ('counts', 'group_field', 'group_limit', 'group_sort', 'ranges',): + with self.subTest(invalid_opt): + with self.assertRaisesRegex(ValueError, f'The option \'{invalid_opt}\' is invalid when using pagination.'): + Pagination.new_pagination(None, PagerType.POST_SEARCH, **{invalid_opt: 'test value'}) + + def test_key_value_error_for_docs(self): + for pager_type in self.all_doc_paginations: + with self.subTest(pager_type): + with self.assertRaisesRegex(ValueError, f'The option \'key\' is invalid when using pagination. {_DOCS_KEY_ERROR}'): + Pagination.new_pagination(None, pager_type, key='a') + + def test_key_value_error_for_views(self): + for pager_type in self.view_paginations: + with self.subTest(pager_type): + with self.assertRaisesRegex(ValueError, f'The option \'key\' is invalid when using pagination. {_VIEW_KEY_ERROR}'): + Pagination.new_pagination(None, pager_type, key={}) diff --git a/test/unit/test_cloudant_base_error_augment.py b/test/unit/test_cloudant_base_error_augment.py new file mode 100644 index 00000000..f126ec93 --- /dev/null +++ b/test/unit/test_cloudant_base_error_augment.py @@ -0,0 +1,476 @@ +# coding: utf-8 + +# © Copyright IBM Corporation 2024. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from typing import Any, Literal, TypedDict +import responses +import unittest +from json.decoder import JSONDecodeError +from ibm_cloud_sdk_core import ApiException +from ibm_cloud_sdk_core.authenticators.no_auth_authenticator import NoAuthAuthenticator +from ibmcloudant.cloudant_v1 import * +from ibmcloudant.cloudant_base_service import _error_response_hook + +class MockResponse(TypedDict): + body: Union[dict[str,Any], str, bytes, None] + headers: dict[str, str] + status: int + +class ExpectedResponse(TypedDict, total=False): + body: Union[dict[str,Any], str, bytes, None] + message: str # not required (but can't be marked that way until 3.11) + +class TestErrorAugment(unittest.TestCase): + + _base_url = 'https://~replace-with-cloudant-host~.cloudantnosqldb.appdomain.cloud' + _db_name = 'testdb' + _doc_id = 'testdoc' + _req_id = 'testreqid' + _error_name = 'test_value' + _error_reason = 'A valid test reason' + _content_type_header = {'content-type': 'application/json'} + _request_id_header = {'x-couch-request-id': _req_id} + _default_mock_headers = _content_type_header | _request_id_header + _error_only_body = {'error': _error_name} + _error_reason_body = {'error': _error_name, 'reason': _error_reason} + _errors_error_only = {'errors': [{'code': _error_name, 'message': _error_name}]} + _errors_error_reason = {'errors': [{'code': _error_name, 'message': f'{_error_name}: {_error_reason}'}]} + _trace = {'trace': _req_id} + + @classmethod + def setUpClass(cls): + cls._service = CloudantV1( + authenticator=NoAuthAuthenticator() + ) + cls._service.set_service_url(cls._base_url) + + def _make_request(self, method: Literal['GET','HEAD'], stream: bool): + if method == responses.GET: + if stream: + return self._service.get_document_as_stream(self._db_name, self._doc_id) + else: + return self._service.get_document(self._db_name, self._doc_id) + elif method == responses.HEAD: + return self._service.head_document(self._db_name, self._doc_id) + else: + raise Exception('Only GET and HEAD requests expected.') + + def _expected_message_from_mock_response(self, mock_response: MockResponse): + body = mock_response['body'] + status_code = mock_response['status'] + err = body['error'] + msg: str = f'Error: {err}' + if 'reason' in body: + reason = body['reason'] + msg += f': {reason}' + msg += f', Status code: {status_code}' + return msg + + def _run_test( + self, + method: Literal['GET', 'HEAD'] = responses.GET, + mock_response: MockResponse = { + 'body': _error_reason_body, + 'headers': _default_mock_headers, + 'status': 444 + }, + expected_response: ExpectedResponse = None, + stream=False + ) -> Union[Exception, None]: + + if expected_response is None: + # Default to a no-augment body pass-through + expected_response = { + 'body': mock_response['body'] + } + + if mock_response['status'] == 200: + expect_raises = False + else: + expect_raises = True + if 'message' not in expected_response: + expected_response['message'] = self._expected_message_from_mock_response(mock_response) + + if isinstance(mock_response['body'], dict): + # stringify dict responses + mock_response['body'] = json.dumps(mock_response['body']) + + caught_exception = None + with responses.RequestsMock(response_callback=self._response_callback) as mock_ctx: + # Add the mock response + # Copy the headers so we can remove content-type for responses preferred format + mock_response['headers'] = dict(mock_response['headers']) + mock_content_type = mock_response['headers'].pop('content-type', None) + mock_ctx.add( + method, + url=self._base_url + f'/{self._db_name}/{self._doc_id}', + content_type=mock_content_type, + **mock_response + ) + if expect_raises: + with self.assertRaisesRegex(ApiException, expected_response['message']) as e_ctx: + self._make_request(method, stream) + caught_exception = e_ctx.exception + actual_status_code = caught_exception.status_code + actual_headers = caught_exception.http_response.headers + # Default to raw body + actual_body = caught_exception.http_response.content + if method != 'HEAD' and mock_content_type: + if mock_content_type.startswith('application/json'): + try: + actual_body = caught_exception.http_response.json() + except JSONDecodeError: + # Catch the malformed JSON case + actual_body = caught_exception.http_response.text + elif mock_content_type == 'text/plain': + actual_body = caught_exception.http_response.text + else: + try: + service_response = self._make_request(method, stream) + actual_status_code = service_response.get_status_code() + actual_headers = service_response.get_headers() + actual_body = service_response.get_result() + except Exception as e: + raise Exception('There should be no exception raised in this case.') from e + # Assert expected status_code + self.assertEqual(mock_response['status'], actual_status_code) + # Assert headers unchanged, re-adding the content-type we had to pop for responses mock + if mock_content_type: + expected_headers = mock_response['headers'] | {'content-type': mock_content_type} + else: + expected_headers = mock_response['headers'] + self.assertEqual(expected_headers, actual_headers) + # Assert expected body + self.assertEqual(expected_response['body'], actual_body) + return caught_exception + + # Responses mocking doesn't actually go through requests it presents + # a requests like API. This means the requests event hooks aren't called + # when mocking with responses. However, the response_callback behaves like + # requests response event hook so we use it to invoke our _error_response_hook + # during testing. + @classmethod + def _response_callback(cls, resp): + return _error_response_hook(resp) + + def test_response_hook_added(self): + self.assertEqual(_error_response_hook, self._service.get_http_client().hooks['response'][0]) + + def test_response_hook_not_double_added_via_set_http_client(self): + client = self._service.get_http_client() + self._service.set_http_client(client) + response_hooks = self._service.get_http_client().hooks['response'] + self.assertEqual(1, len(response_hooks)) + self.assertEqual(_error_response_hook, response_hooks[0]) + + def test_response_hook_added_via_set_http_client(self): + client = self._service.get_http_client() + client.hooks['response'].clear() + self._service.set_http_client(client) + response_hooks = self._service.get_http_client().hooks['response'] + self.assertEqual(1, len(response_hooks)) + self.assertEqual(_error_response_hook, response_hooks[0]) + + def test_augment_error(self): + self._run_test( + mock_response={ + 'body': self._error_only_body, + 'headers': self._content_type_header, + 'status': 444 + }, + expected_response={ + 'body': self._error_only_body | self._errors_error_only + } + ) + + def test_augment_error_with_trace(self): + self._run_test( + mock_response={ + 'body': self._error_only_body, + 'headers': self._default_mock_headers, + 'status': 444 + }, + expected_response={ + 'body': self._error_only_body | self._errors_error_only | self._trace + } + ) + + def test_augment_error_reason(self): + self._run_test( + mock_response={ + 'body': self._error_reason_body, + 'headers': self._content_type_header, + 'status': 444 + }, + expected_response={ + 'body': self._error_reason_body | self._errors_error_reason + } + ) + + def test_augment_error_reason_with_trace(self): + self._run_test( + mock_response={ + 'body': self._error_reason_body, + 'headers': self._default_mock_headers, + 'status': 444 + }, + expected_response={ + 'body': self._error_reason_body | self._errors_error_reason | self._trace + } + ) + + def test_augment_error_reason_with_trace_request_id(self): + self._run_test( + mock_response={ + 'body': self._error_reason_body, + 'headers': self._content_type_header | {'x-request-id': self._req_id}, + 'status': 444 + }, + expected_response={ + 'body': self._error_reason_body | self._errors_error_reason | self._trace + } + ) + + def test_augment_error_reason_with_trace_request_id_preferred(self): + expected_req_id = 'preferred_req_id' + self._run_test( + mock_response={ + 'body': self._error_reason_body, + 'headers': self._default_mock_headers | {'x-request-id': expected_req_id}, + 'status': 444 + }, + expected_response={ + 'body': self._error_reason_body | self._errors_error_reason | {'trace': expected_req_id} + } + ) + + def test_augment_error_reason_stream(self): + self._run_test( + mock_response={ + 'body': self._error_reason_body, + 'headers': self._default_mock_headers, + 'status': 444 + }, + expected_response={ + 'body': self._error_reason_body | self._errors_error_reason | self._trace + }, + stream = True + ) + + def test_augment_json_charset(self): + self._run_test( + mock_response={ + 'body': self._error_reason_body, + 'headers': self._request_id_header | {'content-type': 'application/json; charset=utf-8'}, + 'status': 444 + }, + expected_response={ + 'body': self._error_reason_body | self._errors_error_reason | self._trace + } + ) + + def test_augment_no_header(self): + self._run_test( + mock_response={ + 'body': self._error_reason_body, + 'headers': self._content_type_header, + 'status': 444 + }, + expected_response={ + 'body': self._error_reason_body | self._errors_error_reason + } + ) + + def test_no_augment_success(self): + self._run_test( + mock_response={ + 'body': {'_id': self._doc_id, '_rev': '1-abc', 'foo': 'bar'}, + 'headers': self._default_mock_headers, + 'status': 200 + } + ) + + def test_no_augment_head(self): + self._run_test( + method=responses.HEAD, + mock_response={ + 'body': None, + 'headers': self._default_mock_headers, + 'status': 444 + }, + expected_response={ + 'body': b'', # no body content for HEAD + 'message': 'Error: Unknown error, Status code: 444' + } + ) + + def test_no_augment_id_only(self): + self._run_test( + mock_response={ + 'body': {}, + 'headers': self._default_mock_headers, + 'status': 444 + }, + expected_response={ + 'body': {}, + 'message': 'Error: {}, Status code: 444' + } + ) + + def test_no_augment_existing_trace(self): + test_body = { + 'trace': 'testanotherreqid', + 'error': 'too_many_requests', + 'reason': 'Buy a bigger plan.' + } + self._run_test( + mock_response={ + 'body': test_body, + 'headers': self._default_mock_headers, + 'status': 429 + }, + expected_response={ + 'body': test_body, + 'message': 'Error: too_many_requests, Status code: 429' + } + ) + + def test_no_augment_existing_errors_no_id(self): + test_body = { + 'errors': [ + { + 'code': 'forbidden', + 'message':'forbidden: You must have _reader to access this resource.' + }], + 'error': 'forbidden', # error for use by assertion + 'reason': 'You must have _reader to access this resource.' # reason for use by assertion + } + self._run_test( + mock_response={ + 'body': test_body, + 'headers': self._content_type_header, + 'status': 403 + }, + expected_response={ + 'body': test_body, + } + ) + + def test_augment_trace_existing_errors(self): + test_body = { + 'errors': [ + { + 'code': 'forbidden', + 'message':'forbidden: You must have _reader to access this resource.' + }], + 'error': 'forbidden', # error for use by assertion + 'reason': 'You must have _reader to access this resource.' # reason for use by assertion + } + self._run_test( + mock_response={ + 'body': test_body, + 'headers': self._default_mock_headers, + 'status': 403 + }, + expected_response={ + 'body': test_body | self._trace + } + ) + + def test_no_augment_non_json(self): + test_body = 'foo' + self._run_test( + mock_response={ + 'body': test_body, + 'headers': self._request_id_header | {'content-type': 'text/plain'}, + 'status': 400 + }, + expected_response={ + 'body': test_body, + 'message': f'Error: {test_body}, Status code: 400' + } + ) + + def test_no_augment_no_content_type(self): + test_body = b'000' + self._run_test( + mock_response={ + 'body': test_body, + 'headers': self._request_id_header, + 'status': 444 + }, + expected_response={ + 'body': test_body, + 'message': 'Error: 000, Status code: 444' + } + ) + + def test_no_augment_no_error(self): + test_body = {'foo':'bar', 'reason': 'testing'} + self._run_test( + mock_response={ + 'body': test_body, + 'headers': self._default_mock_headers, + 'status': 400 + }, + expected_response={ + 'body': test_body, + 'message': 'Error: Bad Request, Status code: 400' + } + ) + + def test_no_augment_no_error_no_header(self): + test_body = {'foo':'bar', 'reason': 'testing'} + self._run_test( + mock_response={ + 'body': test_body, + 'headers': self._content_type_header, + 'status': 400 + }, + expected_response={ + 'body': test_body, + 'message': 'Error: Bad Request, Status code: 400' + } + ) + + def test_no_augment_invalid_json(self): + test_body = '{"err' + e = self._run_test( + mock_response={ + 'body': test_body, + 'headers': self._content_type_header, + 'status': 400 + }, + expected_response={ + 'body': test_body, + 'message': 'Error: {"err, Status code: 400' + } + ) + with self.assertRaises(JSONDecodeError): + e.http_response.json() + + def test_augment_error_empty_reason_with_trace(self): + test_body = self._error_only_body | {"reason": ''} + self._run_test( + mock_response={ + 'body': test_body, + 'headers': self._default_mock_headers, + 'status': 444 + }, + expected_response={ + 'body': test_body | self._errors_error_only | self._trace, + "message": f'Error: {self._error_name}, Status code: 444' + } + ) diff --git a/test/unit/test_cloudant_base_validation.py b/test/unit/test_cloudant_base_validation.py index 59f95fd5..629bf456 100644 --- a/test/unit/test_cloudant_base_validation.py +++ b/test/unit/test_cloudant_base_validation.py @@ -28,6 +28,14 @@ def test_validates_doc_id(self): with self.assertRaisesRegex(ValueError, '.+_testDocument.+') as cm: service.get_document('testDatabase', '_testDocument') + def test_validates_doc_id_with_as_stream_operation(self): + service = CloudantV1( + authenticator=NoAuthAuthenticator() + ) + service.set_service_url('https://cloudant.example') + with self.assertRaisesRegex(ValueError, '.+_testDocument.+') as cm: + service.get_document_as_stream('testDatabase', '_testDocument') + def test_validates_doc_id_at_long_service_path(self): service = CloudantV1( authenticator=NoAuthAuthenticator() diff --git a/test/unit/test_cloudant_v1.py b/test/unit/test_cloudant_v1.py index 0348b653..7c3ce166 100644 --- a/test/unit/test_cloudant_v1.py +++ b/test/unit/test_cloudant_v1.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# (C) Copyright IBM Corp. 2022. +# (C) Copyright IBM Corp. 2026. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -21,6 +21,7 @@ from ibm_cloud_sdk_core.authenticators.no_auth_authenticator import NoAuthAuthenticator from ibm_cloud_sdk_core.utils import datetime_to_string, string_to_datetime import base64 +import gzip import inspect import io import json @@ -32,7 +33,6 @@ import responses import tempfile import urllib -import gzip from ibmcloudant.cloudant_v1 import * @@ -40,7 +40,7 @@ authenticator=NoAuthAuthenticator() ) -_base_url = 'http://localhost:5984' +_base_url = 'https://~replace-with-cloudant-host~.cloudantnosqldb.appdomain.cloud' _service.set_service_url(_base_url) @@ -51,23 +51,15 @@ def preprocess_url(operation_path: str): The returned request URL is used to register the mock response so it needs to match the request URL that is formed by the requests library. """ - # First, unquote the path since it might have some quoted/escaped characters in it - # due to how the generator inserts the operation paths into the unit test code. - operation_path = urllib.parse.unquote(operation_path) - # Next, quote the path using urllib so that we approximate what will - # happen during request processing. - operation_path = urllib.parse.quote(operation_path, safe='/') - - # Finally, form the request URL from the base URL and operation path. + # Form the request URL from the base URL and operation path. request_url = _base_url + operation_path # If the request url does NOT end with a /, then just return it as-is. # Otherwise, return a regular expression that matches one or more trailing /. - if re.fullmatch('.*/+', request_url) is None: + if not request_url.endswith('/'): return request_url - else: - return re.compile(request_url.rstrip('/') + '/+') + return re.compile(request_url.rstrip('/') + '/+') ############################################################################## @@ -75,7 +67,8 @@ def preprocess_url(operation_path: str): ############################################################################## # region -class TestNewInstance(): + +class TestNewInstance: """ Test Class for new_instance """ @@ -102,7 +95,8 @@ def test_new_instance_without_authenticator(self): service_name='TEST_SERVICE_NOT_FOUND', ) -class TestGetServerInformation(): + +class TestGetServerInformation: """ Test Class for get_server_information """ @@ -114,17 +108,18 @@ def test_get_server_information_all_params(self): """ # Set up mock url = preprocess_url('/') - mock_response = '{"couchdb": "couchdb", "features": ["features"], "vendor": {"name": "name", "variant": "variant", "version": "version"}, "version": "version", "features_flags": ["features_flags"]}' - responses.add(responses.GET, - url, - body=mock_response, - content_type='application/json', - status=200) + mock_response = '{"couchdb": "couchdb", "features": ["features"], "features_flags": ["features_flags"], "vendor": {"name": "name", "variant": "paas", "version": "version"}, "version": "version"}' + responses.add( + responses.GET, + url, + body=mock_response, + content_type='application/json', + status=200, + ) # Invoke method response = _service.get_server_information() - # Check for correct operation assert len(responses.calls) == 1 assert response.status_code == 200 @@ -138,119 +133,8 @@ def test_get_server_information_all_params_with_retries(self): _service.disable_retries() self.test_get_server_information_all_params() -class TestGetMembershipInformation(): - """ - Test Class for get_membership_information - """ - - @responses.activate - def test_get_membership_information_all_params(self): - """ - get_membership_information() - """ - # Set up mock - url = preprocess_url('/_membership') - mock_response = '{"all_nodes": ["all_nodes"], "cluster_nodes": ["cluster_nodes"]}' - responses.add(responses.GET, - url, - body=mock_response, - content_type='application/json', - status=200) - - # Invoke method - response = _service.get_membership_information() - - - # Check for correct operation - assert len(responses.calls) == 1 - assert response.status_code == 200 - - def test_get_membership_information_all_params_with_retries(self): - # Enable retries and run test_get_membership_information_all_params. - _service.enable_retries() - self.test_get_membership_information_all_params() - - # Disable retries and run test_get_membership_information_all_params. - _service.disable_retries() - self.test_get_membership_information_all_params() - -class TestGetUuids(): - """ - Test Class for get_uuids - """ - - @responses.activate - def test_get_uuids_all_params(self): - """ - get_uuids() - """ - # Set up mock - url = preprocess_url('/_uuids') - mock_response = '{"uuids": ["uuids"]}' - responses.add(responses.GET, - url, - body=mock_response, - content_type='application/json', - status=200) - - # Set up parameter values - count = 1 - - # Invoke method - response = _service.get_uuids( - count=count, - headers={} - ) - - # Check for correct operation - assert len(responses.calls) == 1 - assert response.status_code == 200 - # Validate query params - query_string = responses.calls[0].request.url.split('?',1)[1] - query_string = urllib.parse.unquote_plus(query_string) - assert 'count={}'.format(count) in query_string - - def test_get_uuids_all_params_with_retries(self): - # Enable retries and run test_get_uuids_all_params. - _service.enable_retries() - self.test_get_uuids_all_params() - - # Disable retries and run test_get_uuids_all_params. - _service.disable_retries() - self.test_get_uuids_all_params() - - @responses.activate - def test_get_uuids_required_params(self): - """ - test_get_uuids_required_params() - """ - # Set up mock - url = preprocess_url('/_uuids') - mock_response = '{"uuids": ["uuids"]}' - responses.add(responses.GET, - url, - body=mock_response, - content_type='application/json', - status=200) - - # Invoke method - response = _service.get_uuids() - - - # Check for correct operation - assert len(responses.calls) == 1 - assert response.status_code == 200 - - def test_get_uuids_required_params_with_retries(self): - # Enable retries and run test_get_uuids_required_params. - _service.enable_retries() - self.test_get_uuids_required_params() - - # Disable retries and run test_get_uuids_required_params. - _service.disable_retries() - self.test_get_uuids_required_params() -class TestGetCapacityThroughputInformation(): +class TestGetCapacityThroughputInformation: """ Test Class for get_capacity_throughput_information """ @@ -262,17 +146,18 @@ def test_get_capacity_throughput_information_all_params(self): """ # Set up mock url = preprocess_url('/_api/v2/user/capacity/throughput') - mock_response = '{"current": {"throughput": {"blocks": 0, "query": 0, "read": 0, "write": 0}}, "target": {"throughput": {"blocks": 0, "query": 0, "read": 0, "write": 0}}}' - responses.add(responses.GET, - url, - body=mock_response, - content_type='application/json', - status=200) + mock_response = '{"current": {"throughput": {"blocks": 1, "query": 0, "read": 0, "write": 0}}, "target": {"throughput": {"blocks": 1, "query": 0, "read": 0, "write": 0}}}' + responses.add( + responses.GET, + url, + body=mock_response, + content_type='application/json', + status=200, + ) # Invoke method response = _service.get_capacity_throughput_information() - # Check for correct operation assert len(responses.calls) == 1 assert response.status_code == 200 @@ -286,7 +171,8 @@ def test_get_capacity_throughput_information_all_params_with_retries(self): _service.disable_retries() self.test_get_capacity_throughput_information_all_params() -class TestPutCapacityThroughputConfiguration(): + +class TestPutCapacityThroughputConfiguration: """ Test Class for put_capacity_throughput_configuration """ @@ -298,31 +184,34 @@ def test_put_capacity_throughput_configuration_all_params(self): """ # Set up mock url = preprocess_url('/_api/v2/user/capacity/throughput') - mock_response = '{"current": {"throughput": {"blocks": 0, "query": 0, "read": 0, "write": 0}}, "target": {"throughput": {"blocks": 0, "query": 0, "read": 0, "write": 0}}}' - responses.add(responses.PUT, - url, - body=mock_response, - content_type='application/json', - status=200) + mock_response = '{"current": {"throughput": {"blocks": 1, "query": 0, "read": 0, "write": 0}}, "target": {"throughput": {"blocks": 1, "query": 0, "read": 0, "write": 0}}}' + responses.add( + responses.PUT, + url, + body=mock_response, + content_type='application/json', + status=200, + ) # Set up parameter values - blocks = 0 + blocks = 10 # Invoke method response = _service.put_capacity_throughput_configuration( blocks, - headers={} + headers={}, ) # Check for correct operation assert len(responses.calls) == 1 assert response.status_code == 200 - # decompress gzip compressed request body - responses.calls[0].request.body = gzip.decompress(responses.calls[0].request.body) + # Decompress gzip compressed request body. + raw_body = responses.calls[0].request.body + responses.calls[0].request.body = gzip.decompress(raw_body.read() if isinstance(raw_body, io.IOBase) else raw_body) # Validate body params req_body = json.loads(str(responses.calls[0].request.body, 'utf-8')) - assert req_body['blocks'] == 0 + assert req_body['blocks'] == 10 def test_put_capacity_throughput_configuration_all_params_with_retries(self): # Enable retries and run test_put_capacity_throughput_configuration_all_params. @@ -340,22 +229,24 @@ def test_put_capacity_throughput_configuration_value_error(self): """ # Set up mock url = preprocess_url('/_api/v2/user/capacity/throughput') - mock_response = '{"current": {"throughput": {"blocks": 0, "query": 0, "read": 0, "write": 0}}, "target": {"throughput": {"blocks": 0, "query": 0, "read": 0, "write": 0}}}' - responses.add(responses.PUT, - url, - body=mock_response, - content_type='application/json', - status=200) + mock_response = '{"current": {"throughput": {"blocks": 1, "query": 0, "read": 0, "write": 0}}, "target": {"throughput": {"blocks": 1, "query": 0, "read": 0, "write": 0}}}' + responses.add( + responses.PUT, + url, + body=mock_response, + content_type='application/json', + status=200, + ) # Set up parameter values - blocks = 0 + blocks = 10 # Pass in all but one required param and check for a ValueError req_param_dict = { "blocks": blocks, } for param in req_param_dict.keys(): - req_copy = {key:val if key is not param else None for (key,val) in req_param_dict.items()} + req_copy = {key: val if key is not param else None for (key, val) in req_param_dict.items()} with pytest.raises(ValueError): _service.put_capacity_throughput_configuration(**req_copy) @@ -368,6 +259,87 @@ def test_put_capacity_throughput_configuration_value_error_with_retries(self): _service.disable_retries() self.test_put_capacity_throughput_configuration_value_error() + +class TestGetUuids: + """ + Test Class for get_uuids + """ + + @responses.activate + def test_get_uuids_all_params(self): + """ + get_uuids() + """ + # Set up mock + url = preprocess_url('/_uuids') + mock_response = '{"uuids": ["uuids"]}' + responses.add( + responses.GET, + url, + body=mock_response, + content_type='application/json', + status=200, + ) + + # Set up parameter values + count = 1 + + # Invoke method + response = _service.get_uuids( + count=count, + headers={}, + ) + + # Check for correct operation + assert len(responses.calls) == 1 + assert response.status_code == 200 + # Validate query params + query_string = responses.calls[0].request.url.split('?', 1)[1] + query_string = urllib.parse.unquote_plus(query_string) + assert 'count={}'.format(count) in query_string + + def test_get_uuids_all_params_with_retries(self): + # Enable retries and run test_get_uuids_all_params. + _service.enable_retries() + self.test_get_uuids_all_params() + + # Disable retries and run test_get_uuids_all_params. + _service.disable_retries() + self.test_get_uuids_all_params() + + @responses.activate + def test_get_uuids_required_params(self): + """ + test_get_uuids_required_params() + """ + # Set up mock + url = preprocess_url('/_uuids') + mock_response = '{"uuids": ["uuids"]}' + responses.add( + responses.GET, + url, + body=mock_response, + content_type='application/json', + status=200, + ) + + # Invoke method + response = _service.get_uuids() + + # Check for correct operation + assert len(responses.calls) == 1 + assert response.status_code == 200 + + def test_get_uuids_required_params_with_retries(self): + # Enable retries and run test_get_uuids_required_params. + _service.enable_retries() + self.test_get_uuids_required_params() + + # Disable retries and run test_get_uuids_required_params. + _service.disable_retries() + self.test_get_uuids_required_params() + + # endregion ############################################################################## # End of Service: Server @@ -378,7 +350,8 @@ def test_put_capacity_throughput_configuration_value_error_with_retries(self): ############################################################################## # region -class TestNewInstance(): + +class TestNewInstance: """ Test Class for new_instance """ @@ -405,7 +378,8 @@ def test_new_instance_without_authenticator(self): service_name='TEST_SERVICE_NOT_FOUND', ) -class TestGetDbUpdates(): + +class TestGetDbUpdates: """ Test Class for get_db_updates """ @@ -418,35 +392,43 @@ def test_get_db_updates_all_params(self): # Set up mock url = preprocess_url('/_db_updates') mock_response = '{"last_seq": "last_seq", "results": [{"db_name": "db_name", "seq": "seq", "type": "created"}]}' - responses.add(responses.GET, - url, - body=mock_response, - content_type='application/json', - status=200) + responses.add( + responses.GET, + url, + body=mock_response, + content_type='application/json', + status=200, + ) # Set up parameter values + descending = False feed = 'normal' - heartbeat = 0 - timeout = 0 + heartbeat = 1 + limit = 0 + timeout = 60000 since = '0' # Invoke method response = _service.get_db_updates( + descending=descending, feed=feed, heartbeat=heartbeat, + limit=limit, timeout=timeout, since=since, - headers={} + headers={}, ) # Check for correct operation assert len(responses.calls) == 1 assert response.status_code == 200 # Validate query params - query_string = responses.calls[0].request.url.split('?',1)[1] + query_string = responses.calls[0].request.url.split('?', 1)[1] query_string = urllib.parse.unquote_plus(query_string) + assert 'descending={}'.format('true' if descending else 'false') in query_string assert 'feed={}'.format(feed) in query_string assert 'heartbeat={}'.format(heartbeat) in query_string + assert 'limit={}'.format(limit) in query_string assert 'timeout={}'.format(timeout) in query_string assert 'since={}'.format(since) in query_string @@ -467,16 +449,17 @@ def test_get_db_updates_required_params(self): # Set up mock url = preprocess_url('/_db_updates') mock_response = '{"last_seq": "last_seq", "results": [{"db_name": "db_name", "seq": "seq", "type": "created"}]}' - responses.add(responses.GET, - url, - body=mock_response, - content_type='application/json', - status=200) + responses.add( + responses.GET, + url, + body=mock_response, + content_type='application/json', + status=200, + ) # Invoke method response = _service.get_db_updates() - # Check for correct operation assert len(responses.calls) == 1 assert response.status_code == 200 @@ -490,7 +473,8 @@ def test_get_db_updates_required_params_with_retries(self): _service.disable_retries() self.test_get_db_updates_required_params() -class TestPostChanges(): + +class TestPostChanges: """ Test Class for post_changes """ @@ -502,18 +486,20 @@ def test_post_changes_all_params(self): """ # Set up mock url = preprocess_url('/testString/_changes') - mock_response = '{"last_seq": "last_seq", "pending": 7, "results": [{"changes": [{"rev": "rev"}], "deleted": false, "doc": {"_attachments": {"mapKey": {"content_type": "content_type", "data": "VGhpcyBpcyBhbiBlbmNvZGVkIGJ5dGUgYXJyYXku", "digest": "digest", "encoded_length": 0, "encoding": "encoding", "follows": false, "length": 0, "revpos": 1, "stub": true}}, "_conflicts": ["conflicts"], "_deleted": false, "_deleted_conflicts": ["deleted_conflicts"], "_id": "id", "_local_seq": "local_seq", "_rev": "rev", "_revisions": {"ids": ["ids"], "start": 1}, "_revs_info": [{"rev": "rev", "status": "available"}]}, "id": "id", "seq": "seq"}]}' - responses.add(responses.POST, - url, - body=mock_response, - content_type='application/json', - status=200) + mock_response = '{"last_seq": "last_seq", "pending": 0, "results": [{"changes": [{"rev": "rev"}], "deleted": false, "doc": {"_attachments": {"mapKey": {"content_type": "content_type", "data": "VGhpcyBpcyBhIG1vY2sgYnl0ZSBhcnJheSB2YWx1ZS4=", "digest": "digest", "encoded_length": 0, "encoding": "encoding", "follows": false, "length": 0, "revpos": 1, "stub": true}}, "_conflicts": ["conflicts"], "_deleted": true, "_deleted_conflicts": ["deleted_conflicts"], "_id": "_id", "_local_seq": "_local_seq", "_rev": "_rev", "_revisions": {"ids": ["ids"], "start": 1}, "_revs_info": [{"rev": "rev", "status": "available"}]}, "id": "id", "seq": "seq"}]}' + responses.add( + responses.POST, + url, + body=mock_response, + content_type='application/json', + status=200, + ) # Set up parameter values db = 'testString' - doc_ids = ['testString'] + doc_ids = ['0007741142412418284'] fields = ['testString'] - selector = {'key1': 'testString'} + selector = {'anyKey': 'anyValue'} last_event_id = 'testString' att_encoding_info = False attachments = False @@ -521,13 +507,13 @@ def test_post_changes_all_params(self): descending = False feed = 'normal' filter = 'testString' - heartbeat = 0 + heartbeat = 1 include_docs = False limit = 0 seq_interval = 1 since = '0' style = 'main_only' - timeout = 0 + timeout = 60000 view = 'testString' # Invoke method @@ -551,14 +537,14 @@ def test_post_changes_all_params(self): style=style, timeout=timeout, view=view, - headers={} + headers={}, ) # Check for correct operation assert len(responses.calls) == 1 assert response.status_code == 200 # Validate query params - query_string = responses.calls[0].request.url.split('?',1)[1] + query_string = responses.calls[0].request.url.split('?', 1)[1] query_string = urllib.parse.unquote_plus(query_string) assert 'att_encoding_info={}'.format('true' if att_encoding_info else 'false') in query_string assert 'attachments={}'.format('true' if attachments else 'false') in query_string @@ -574,14 +560,15 @@ def test_post_changes_all_params(self): assert 'style={}'.format(style) in query_string assert 'timeout={}'.format(timeout) in query_string assert 'view={}'.format(view) in query_string - # decompress gzip compressed request body - responses.calls[0].request.body = gzip.decompress(responses.calls[0].request.body) + # Decompress gzip compressed request body. + raw_body = responses.calls[0].request.body + responses.calls[0].request.body = gzip.decompress(raw_body.read() if isinstance(raw_body, io.IOBase) else raw_body) # Validate body params req_body = json.loads(str(responses.calls[0].request.body, 'utf-8')) - assert req_body['doc_ids'] == ['testString'] + assert req_body['doc_ids'] == ['0007741142412418284'] assert req_body['fields'] == ['testString'] - assert req_body['selector'] == {'key1': 'testString'} + assert req_body['selector'] == {'anyKey': 'anyValue'} def test_post_changes_all_params_with_retries(self): # Enable retries and run test_post_changes_all_params. @@ -599,18 +586,20 @@ def test_post_changes_required_params(self): """ # Set up mock url = preprocess_url('/testString/_changes') - mock_response = '{"last_seq": "last_seq", "pending": 7, "results": [{"changes": [{"rev": "rev"}], "deleted": false, "doc": {"_attachments": {"mapKey": {"content_type": "content_type", "data": "VGhpcyBpcyBhbiBlbmNvZGVkIGJ5dGUgYXJyYXku", "digest": "digest", "encoded_length": 0, "encoding": "encoding", "follows": false, "length": 0, "revpos": 1, "stub": true}}, "_conflicts": ["conflicts"], "_deleted": false, "_deleted_conflicts": ["deleted_conflicts"], "_id": "id", "_local_seq": "local_seq", "_rev": "rev", "_revisions": {"ids": ["ids"], "start": 1}, "_revs_info": [{"rev": "rev", "status": "available"}]}, "id": "id", "seq": "seq"}]}' - responses.add(responses.POST, - url, - body=mock_response, - content_type='application/json', - status=200) + mock_response = '{"last_seq": "last_seq", "pending": 0, "results": [{"changes": [{"rev": "rev"}], "deleted": false, "doc": {"_attachments": {"mapKey": {"content_type": "content_type", "data": "VGhpcyBpcyBhIG1vY2sgYnl0ZSBhcnJheSB2YWx1ZS4=", "digest": "digest", "encoded_length": 0, "encoding": "encoding", "follows": false, "length": 0, "revpos": 1, "stub": true}}, "_conflicts": ["conflicts"], "_deleted": true, "_deleted_conflicts": ["deleted_conflicts"], "_id": "_id", "_local_seq": "_local_seq", "_rev": "_rev", "_revisions": {"ids": ["ids"], "start": 1}, "_revs_info": [{"rev": "rev", "status": "available"}]}, "id": "id", "seq": "seq"}]}' + responses.add( + responses.POST, + url, + body=mock_response, + content_type='application/json', + status=200, + ) # Set up parameter values db = 'testString' - doc_ids = ['testString'] + doc_ids = ['0007741142412418284'] fields = ['testString'] - selector = {'key1': 'testString'} + selector = {'anyKey': 'anyValue'} # Invoke method response = _service.post_changes( @@ -618,20 +607,21 @@ def test_post_changes_required_params(self): doc_ids=doc_ids, fields=fields, selector=selector, - headers={} + headers={}, ) # Check for correct operation assert len(responses.calls) == 1 assert response.status_code == 200 - # decompress gzip compressed request body - responses.calls[0].request.body = gzip.decompress(responses.calls[0].request.body) + # Decompress gzip compressed request body. + raw_body = responses.calls[0].request.body + responses.calls[0].request.body = gzip.decompress(raw_body.read() if isinstance(raw_body, io.IOBase) else raw_body) # Validate body params req_body = json.loads(str(responses.calls[0].request.body, 'utf-8')) - assert req_body['doc_ids'] == ['testString'] + assert req_body['doc_ids'] == ['0007741142412418284'] assert req_body['fields'] == ['testString'] - assert req_body['selector'] == {'key1': 'testString'} + assert req_body['selector'] == {'anyKey': 'anyValue'} def test_post_changes_required_params_with_retries(self): # Enable retries and run test_post_changes_required_params. @@ -649,25 +639,27 @@ def test_post_changes_value_error(self): """ # Set up mock url = preprocess_url('/testString/_changes') - mock_response = '{"last_seq": "last_seq", "pending": 7, "results": [{"changes": [{"rev": "rev"}], "deleted": false, "doc": {"_attachments": {"mapKey": {"content_type": "content_type", "data": "VGhpcyBpcyBhbiBlbmNvZGVkIGJ5dGUgYXJyYXku", "digest": "digest", "encoded_length": 0, "encoding": "encoding", "follows": false, "length": 0, "revpos": 1, "stub": true}}, "_conflicts": ["conflicts"], "_deleted": false, "_deleted_conflicts": ["deleted_conflicts"], "_id": "id", "_local_seq": "local_seq", "_rev": "rev", "_revisions": {"ids": ["ids"], "start": 1}, "_revs_info": [{"rev": "rev", "status": "available"}]}, "id": "id", "seq": "seq"}]}' - responses.add(responses.POST, - url, - body=mock_response, - content_type='application/json', - status=200) + mock_response = '{"last_seq": "last_seq", "pending": 0, "results": [{"changes": [{"rev": "rev"}], "deleted": false, "doc": {"_attachments": {"mapKey": {"content_type": "content_type", "data": "VGhpcyBpcyBhIG1vY2sgYnl0ZSBhcnJheSB2YWx1ZS4=", "digest": "digest", "encoded_length": 0, "encoding": "encoding", "follows": false, "length": 0, "revpos": 1, "stub": true}}, "_conflicts": ["conflicts"], "_deleted": true, "_deleted_conflicts": ["deleted_conflicts"], "_id": "_id", "_local_seq": "_local_seq", "_rev": "_rev", "_revisions": {"ids": ["ids"], "start": 1}, "_revs_info": [{"rev": "rev", "status": "available"}]}, "id": "id", "seq": "seq"}]}' + responses.add( + responses.POST, + url, + body=mock_response, + content_type='application/json', + status=200, + ) # Set up parameter values db = 'testString' - doc_ids = ['testString'] + doc_ids = ['0007741142412418284'] fields = ['testString'] - selector = {'key1': 'testString'} + selector = {'anyKey': 'anyValue'} # Pass in all but one required param and check for a ValueError req_param_dict = { "db": db, } for param in req_param_dict.keys(): - req_copy = {key:val if key is not param else None for (key,val) in req_param_dict.items()} + req_copy = {key: val if key is not param else None for (key, val) in req_param_dict.items()} with pytest.raises(ValueError): _service.post_changes(**req_copy) @@ -680,7 +672,8 @@ def test_post_changes_value_error_with_retries(self): _service.disable_retries() self.test_post_changes_value_error() -class TestPostChangesAsStream(): + +class TestPostChangesAsStream: """ Test Class for post_changes_as_stream """ @@ -693,17 +686,19 @@ def test_post_changes_as_stream_all_params(self): # Set up mock url = preprocess_url('/testString/_changes') mock_response = '{"foo": "this is a mock response for JSON streaming"}' - responses.add(responses.POST, - url, - body=mock_response, - content_type='application/json', - status=200) + responses.add( + responses.POST, + url, + body=mock_response, + content_type='application/json', + status=200, + ) # Set up parameter values db = 'testString' - doc_ids = ['testString'] + doc_ids = ['0007741142412418284'] fields = ['testString'] - selector = {'key1': 'testString'} + selector = {'anyKey': 'anyValue'} last_event_id = 'testString' att_encoding_info = False attachments = False @@ -711,13 +706,13 @@ def test_post_changes_as_stream_all_params(self): descending = False feed = 'normal' filter = 'testString' - heartbeat = 0 + heartbeat = 1 include_docs = False limit = 0 seq_interval = 1 since = '0' style = 'main_only' - timeout = 0 + timeout = 60000 view = 'testString' # Invoke method @@ -741,14 +736,14 @@ def test_post_changes_as_stream_all_params(self): style=style, timeout=timeout, view=view, - headers={} + headers={}, ) # Check for correct operation assert len(responses.calls) == 1 assert response.status_code == 200 # Validate query params - query_string = responses.calls[0].request.url.split('?',1)[1] + query_string = responses.calls[0].request.url.split('?', 1)[1] query_string = urllib.parse.unquote_plus(query_string) assert 'att_encoding_info={}'.format('true' if att_encoding_info else 'false') in query_string assert 'attachments={}'.format('true' if attachments else 'false') in query_string @@ -764,14 +759,15 @@ def test_post_changes_as_stream_all_params(self): assert 'style={}'.format(style) in query_string assert 'timeout={}'.format(timeout) in query_string assert 'view={}'.format(view) in query_string - # decompress gzip compressed request body - responses.calls[0].request.body = gzip.decompress(responses.calls[0].request.body) + # Decompress gzip compressed request body. + raw_body = responses.calls[0].request.body + responses.calls[0].request.body = gzip.decompress(raw_body.read() if isinstance(raw_body, io.IOBase) else raw_body) # Validate body params req_body = json.loads(str(responses.calls[0].request.body, 'utf-8')) - assert req_body['doc_ids'] == ['testString'] + assert req_body['doc_ids'] == ['0007741142412418284'] assert req_body['fields'] == ['testString'] - assert req_body['selector'] == {'key1': 'testString'} + assert req_body['selector'] == {'anyKey': 'anyValue'} # Verify streamed JSON response result = response.get_result() @@ -796,17 +792,19 @@ def test_post_changes_as_stream_required_params(self): # Set up mock url = preprocess_url('/testString/_changes') mock_response = '{"foo": "this is a mock response for JSON streaming"}' - responses.add(responses.POST, - url, - body=mock_response, - content_type='application/json', - status=200) + responses.add( + responses.POST, + url, + body=mock_response, + content_type='application/json', + status=200, + ) # Set up parameter values db = 'testString' - doc_ids = ['testString'] + doc_ids = ['0007741142412418284'] fields = ['testString'] - selector = {'key1': 'testString'} + selector = {'anyKey': 'anyValue'} # Invoke method response = _service.post_changes_as_stream( @@ -814,20 +812,21 @@ def test_post_changes_as_stream_required_params(self): doc_ids=doc_ids, fields=fields, selector=selector, - headers={} + headers={}, ) # Check for correct operation assert len(responses.calls) == 1 assert response.status_code == 200 - # decompress gzip compressed request body - responses.calls[0].request.body = gzip.decompress(responses.calls[0].request.body) + # Decompress gzip compressed request body. + raw_body = responses.calls[0].request.body + responses.calls[0].request.body = gzip.decompress(raw_body.read() if isinstance(raw_body, io.IOBase) else raw_body) # Validate body params req_body = json.loads(str(responses.calls[0].request.body, 'utf-8')) - assert req_body['doc_ids'] == ['testString'] + assert req_body['doc_ids'] == ['0007741142412418284'] assert req_body['fields'] == ['testString'] - assert req_body['selector'] == {'key1': 'testString'} + assert req_body['selector'] == {'anyKey': 'anyValue'} # Verify streamed JSON response result = response.get_result() @@ -852,24 +851,26 @@ def test_post_changes_as_stream_value_error(self): # Set up mock url = preprocess_url('/testString/_changes') mock_response = '{"foo": "this is a mock response for JSON streaming"}' - responses.add(responses.POST, - url, - body=mock_response, - content_type='application/json', - status=200) + responses.add( + responses.POST, + url, + body=mock_response, + content_type='application/json', + status=200, + ) # Set up parameter values db = 'testString' - doc_ids = ['testString'] + doc_ids = ['0007741142412418284'] fields = ['testString'] - selector = {'key1': 'testString'} + selector = {'anyKey': 'anyValue'} # Pass in all but one required param and check for a ValueError req_param_dict = { "db": db, } for param in req_param_dict.keys(): - req_copy = {key:val if key is not param else None for (key,val) in req_param_dict.items()} + req_copy = {key: val if key is not param else None for (key, val) in req_param_dict.items()} with pytest.raises(ValueError): _service.post_changes_as_stream(**req_copy) @@ -882,6 +883,7 @@ def test_post_changes_as_stream_value_error_with_retries(self): _service.disable_retries() self.test_post_changes_as_stream_value_error() + # endregion ############################################################################## # End of Service: Changes @@ -892,7 +894,8 @@ def test_post_changes_as_stream_value_error_with_retries(self): ############################################################################## # region -class TestNewInstance(): + +class TestNewInstance: """ Test Class for new_instance """ @@ -919,7 +922,8 @@ def test_new_instance_without_authenticator(self): service_name='TEST_SERVICE_NOT_FOUND', ) -class TestHeadDatabase(): + +class TestHeadDatabase: """ Test Class for head_database """ @@ -931,9 +935,11 @@ def test_head_database_all_params(self): """ # Set up mock url = preprocess_url('/testString') - responses.add(responses.HEAD, - url, - status=200) + responses.add( + responses.HEAD, + url, + status=200, + ) # Set up parameter values db = 'testString' @@ -941,7 +947,7 @@ def test_head_database_all_params(self): # Invoke method response = _service.head_database( db, - headers={} + headers={}, ) # Check for correct operation @@ -964,9 +970,11 @@ def test_head_database_value_error(self): """ # Set up mock url = preprocess_url('/testString') - responses.add(responses.HEAD, - url, - status=200) + responses.add( + responses.HEAD, + url, + status=200, + ) # Set up parameter values db = 'testString' @@ -976,7 +984,7 @@ def test_head_database_value_error(self): "db": db, } for param in req_param_dict.keys(): - req_copy = {key:val if key is not param else None for (key,val) in req_param_dict.items()} + req_copy = {key: val if key is not param else None for (key, val) in req_param_dict.items()} with pytest.raises(ValueError): _service.head_database(**req_copy) @@ -989,7 +997,8 @@ def test_head_database_value_error_with_retries(self): _service.disable_retries() self.test_head_database_value_error() -class TestGetAllDbs(): + +class TestGetAllDbs: """ Test Class for get_all_dbs """ @@ -1002,11 +1011,13 @@ def test_get_all_dbs_all_params(self): # Set up mock url = preprocess_url('/_all_dbs') mock_response = '["operation_response"]' - responses.add(responses.GET, - url, - body=mock_response, - content_type='application/json', - status=200) + responses.add( + responses.GET, + url, + body=mock_response, + content_type='application/json', + status=200, + ) # Set up parameter values descending = False @@ -1022,14 +1033,14 @@ def test_get_all_dbs_all_params(self): limit=limit, skip=skip, start_key=start_key, - headers={} + headers={}, ) # Check for correct operation assert len(responses.calls) == 1 assert response.status_code == 200 # Validate query params - query_string = responses.calls[0].request.url.split('?',1)[1] + query_string = responses.calls[0].request.url.split('?', 1)[1] query_string = urllib.parse.unquote_plus(query_string) assert 'descending={}'.format('true' if descending else 'false') in query_string assert 'end_key={}'.format(end_key) in query_string @@ -1054,16 +1065,17 @@ def test_get_all_dbs_required_params(self): # Set up mock url = preprocess_url('/_all_dbs') mock_response = '["operation_response"]' - responses.add(responses.GET, - url, - body=mock_response, - content_type='application/json', - status=200) + responses.add( + responses.GET, + url, + body=mock_response, + content_type='application/json', + status=200, + ) # Invoke method response = _service.get_all_dbs() - # Check for correct operation assert len(responses.calls) == 1 assert response.status_code == 200 @@ -1077,7 +1089,8 @@ def test_get_all_dbs_required_params_with_retries(self): _service.disable_retries() self.test_get_all_dbs_required_params() -class TestPostDbsInfo(): + +class TestPostDbsInfo: """ Test Class for post_dbs_info """ @@ -1089,31 +1102,34 @@ def test_post_dbs_info_all_params(self): """ # Set up mock url = preprocess_url('/_dbs_info') - mock_response = '[{"error": "error", "info": {"cluster": {"n": 1, "q": 1, "r": 1, "w": 1}, "committed_update_seq": "committed_update_seq", "compact_running": false, "compacted_seq": "compacted_seq", "db_name": "db_name", "disk_format_version": 19, "doc_count": 0, "doc_del_count": 0, "engine": "engine", "props": {"partitioned": false}, "sizes": {"active": 6, "external": 8, "file": 4}, "update_seq": "update_seq", "uuid": "uuid"}, "key": "key"}]' - responses.add(responses.POST, - url, - body=mock_response, - content_type='application/json', - status=200) + mock_response = '[{"error": "error", "info": {"cluster": {"n": 3, "q": 16, "r": 1, "w": 1}, "committed_update_seq": "committed_update_seq", "compact_running": false, "compacted_seq": "compacted_seq", "db_name": "db_name", "disk_format_version": 1, "doc_count": 0, "doc_del_count": 0, "engine": "engine", "instance_start_time": "instance_start_time", "partitioned_indexes": {"count": 0, "indexes": {"search": 0, "view": 0}, "limit": 10}, "props": {"partitioned": false}, "sizes": {"active": 0, "external": 0, "file": 0}, "update_seq": "update_seq", "uuid": "uuid"}, "key": "key"}]' + responses.add( + responses.POST, + url, + body=mock_response, + content_type='application/json', + status=200, + ) # Set up parameter values - keys = ['testString'] + keys = ['products', 'users', 'orders'] # Invoke method response = _service.post_dbs_info( keys, - headers={} + headers={}, ) # Check for correct operation assert len(responses.calls) == 1 assert response.status_code == 200 - # decompress gzip compressed request body - responses.calls[0].request.body = gzip.decompress(responses.calls[0].request.body) + # Decompress gzip compressed request body. + raw_body = responses.calls[0].request.body + responses.calls[0].request.body = gzip.decompress(raw_body.read() if isinstance(raw_body, io.IOBase) else raw_body) # Validate body params req_body = json.loads(str(responses.calls[0].request.body, 'utf-8')) - assert req_body['keys'] == ['testString'] + assert req_body['keys'] == ['products', 'users', 'orders'] def test_post_dbs_info_all_params_with_retries(self): # Enable retries and run test_post_dbs_info_all_params. @@ -1131,22 +1147,24 @@ def test_post_dbs_info_value_error(self): """ # Set up mock url = preprocess_url('/_dbs_info') - mock_response = '[{"error": "error", "info": {"cluster": {"n": 1, "q": 1, "r": 1, "w": 1}, "committed_update_seq": "committed_update_seq", "compact_running": false, "compacted_seq": "compacted_seq", "db_name": "db_name", "disk_format_version": 19, "doc_count": 0, "doc_del_count": 0, "engine": "engine", "props": {"partitioned": false}, "sizes": {"active": 6, "external": 8, "file": 4}, "update_seq": "update_seq", "uuid": "uuid"}, "key": "key"}]' - responses.add(responses.POST, - url, - body=mock_response, - content_type='application/json', - status=200) + mock_response = '[{"error": "error", "info": {"cluster": {"n": 3, "q": 16, "r": 1, "w": 1}, "committed_update_seq": "committed_update_seq", "compact_running": false, "compacted_seq": "compacted_seq", "db_name": "db_name", "disk_format_version": 1, "doc_count": 0, "doc_del_count": 0, "engine": "engine", "instance_start_time": "instance_start_time", "partitioned_indexes": {"count": 0, "indexes": {"search": 0, "view": 0}, "limit": 10}, "props": {"partitioned": false}, "sizes": {"active": 0, "external": 0, "file": 0}, "update_seq": "update_seq", "uuid": "uuid"}, "key": "key"}]' + responses.add( + responses.POST, + url, + body=mock_response, + content_type='application/json', + status=200, + ) # Set up parameter values - keys = ['testString'] + keys = ['products', 'users', 'orders'] # Pass in all but one required param and check for a ValueError req_param_dict = { "keys": keys, } for param in req_param_dict.keys(): - req_copy = {key:val if key is not param else None for (key,val) in req_param_dict.items()} + req_copy = {key: val if key is not param else None for (key, val) in req_param_dict.items()} with pytest.raises(ValueError): _service.post_dbs_info(**req_copy) @@ -1159,7 +1177,8 @@ def test_post_dbs_info_value_error_with_retries(self): _service.disable_retries() self.test_post_dbs_info_value_error() -class TestDeleteDatabase(): + +class TestDeleteDatabase: """ Test Class for delete_database """ @@ -1172,11 +1191,13 @@ def test_delete_database_all_params(self): # Set up mock url = preprocess_url('/testString') mock_response = '{"ok": true}' - responses.add(responses.DELETE, - url, - body=mock_response, - content_type='application/json', - status=200) + responses.add( + responses.DELETE, + url, + body=mock_response, + content_type='application/json', + status=200, + ) # Set up parameter values db = 'testString' @@ -1184,7 +1205,7 @@ def test_delete_database_all_params(self): # Invoke method response = _service.delete_database( db, - headers={} + headers={}, ) # Check for correct operation @@ -1208,11 +1229,13 @@ def test_delete_database_value_error(self): # Set up mock url = preprocess_url('/testString') mock_response = '{"ok": true}' - responses.add(responses.DELETE, - url, - body=mock_response, - content_type='application/json', - status=200) + responses.add( + responses.DELETE, + url, + body=mock_response, + content_type='application/json', + status=200, + ) # Set up parameter values db = 'testString' @@ -1222,7 +1245,7 @@ def test_delete_database_value_error(self): "db": db, } for param in req_param_dict.keys(): - req_copy = {key:val if key is not param else None for (key,val) in req_param_dict.items()} + req_copy = {key: val if key is not param else None for (key, val) in req_param_dict.items()} with pytest.raises(ValueError): _service.delete_database(**req_copy) @@ -1235,7 +1258,8 @@ def test_delete_database_value_error_with_retries(self): _service.disable_retries() self.test_delete_database_value_error() -class TestGetDatabaseInformation(): + +class TestGetDatabaseInformation: """ Test Class for get_database_information """ @@ -1247,12 +1271,14 @@ def test_get_database_information_all_params(self): """ # Set up mock url = preprocess_url('/testString') - mock_response = '{"cluster": {"n": 1, "q": 1, "r": 1, "w": 1}, "committed_update_seq": "committed_update_seq", "compact_running": false, "compacted_seq": "compacted_seq", "db_name": "db_name", "disk_format_version": 19, "doc_count": 0, "doc_del_count": 0, "engine": "engine", "props": {"partitioned": false}, "sizes": {"active": 6, "external": 8, "file": 4}, "update_seq": "update_seq", "uuid": "uuid"}' - responses.add(responses.GET, - url, - body=mock_response, - content_type='application/json', - status=200) + mock_response = '{"cluster": {"n": 3, "q": 16, "r": 1, "w": 1}, "committed_update_seq": "committed_update_seq", "compact_running": false, "compacted_seq": "compacted_seq", "db_name": "db_name", "disk_format_version": 1, "doc_count": 0, "doc_del_count": 0, "engine": "engine", "instance_start_time": "instance_start_time", "partitioned_indexes": {"count": 0, "indexes": {"search": 0, "view": 0}, "limit": 10}, "props": {"partitioned": false}, "sizes": {"active": 0, "external": 0, "file": 0}, "update_seq": "update_seq", "uuid": "uuid"}' + responses.add( + responses.GET, + url, + body=mock_response, + content_type='application/json', + status=200, + ) # Set up parameter values db = 'testString' @@ -1260,7 +1286,7 @@ def test_get_database_information_all_params(self): # Invoke method response = _service.get_database_information( db, - headers={} + headers={}, ) # Check for correct operation @@ -1283,12 +1309,14 @@ def test_get_database_information_value_error(self): """ # Set up mock url = preprocess_url('/testString') - mock_response = '{"cluster": {"n": 1, "q": 1, "r": 1, "w": 1}, "committed_update_seq": "committed_update_seq", "compact_running": false, "compacted_seq": "compacted_seq", "db_name": "db_name", "disk_format_version": 19, "doc_count": 0, "doc_del_count": 0, "engine": "engine", "props": {"partitioned": false}, "sizes": {"active": 6, "external": 8, "file": 4}, "update_seq": "update_seq", "uuid": "uuid"}' - responses.add(responses.GET, - url, - body=mock_response, - content_type='application/json', - status=200) + mock_response = '{"cluster": {"n": 3, "q": 16, "r": 1, "w": 1}, "committed_update_seq": "committed_update_seq", "compact_running": false, "compacted_seq": "compacted_seq", "db_name": "db_name", "disk_format_version": 1, "doc_count": 0, "doc_del_count": 0, "engine": "engine", "instance_start_time": "instance_start_time", "partitioned_indexes": {"count": 0, "indexes": {"search": 0, "view": 0}, "limit": 10}, "props": {"partitioned": false}, "sizes": {"active": 0, "external": 0, "file": 0}, "update_seq": "update_seq", "uuid": "uuid"}' + responses.add( + responses.GET, + url, + body=mock_response, + content_type='application/json', + status=200, + ) # Set up parameter values db = 'testString' @@ -1298,7 +1326,7 @@ def test_get_database_information_value_error(self): "db": db, } for param in req_param_dict.keys(): - req_copy = {key:val if key is not param else None for (key,val) in req_param_dict.items()} + req_copy = {key: val if key is not param else None for (key, val) in req_param_dict.items()} with pytest.raises(ValueError): _service.get_database_information(**req_copy) @@ -1311,7 +1339,8 @@ def test_get_database_information_value_error_with_retries(self): _service.disable_retries() self.test_get_database_information_value_error() -class TestPutDatabase(): + +class TestPutDatabase: """ Test Class for put_database """ @@ -1324,30 +1353,32 @@ def test_put_database_all_params(self): # Set up mock url = preprocess_url('/testString') mock_response = '{"ok": true}' - responses.add(responses.PUT, - url, - body=mock_response, - content_type='application/json', - status=201) + responses.add( + responses.PUT, + url, + body=mock_response, + content_type='application/json', + status=201, + ) # Set up parameter values db = 'testString' partitioned = False - q = 26 + q = 16 # Invoke method response = _service.put_database( db, partitioned=partitioned, q=q, - headers={} + headers={}, ) # Check for correct operation assert len(responses.calls) == 1 assert response.status_code == 201 # Validate query params - query_string = responses.calls[0].request.url.split('?',1)[1] + query_string = responses.calls[0].request.url.split('?', 1)[1] query_string = urllib.parse.unquote_plus(query_string) assert 'partitioned={}'.format('true' if partitioned else 'false') in query_string assert 'q={}'.format(q) in query_string @@ -1369,11 +1400,13 @@ def test_put_database_required_params(self): # Set up mock url = preprocess_url('/testString') mock_response = '{"ok": true}' - responses.add(responses.PUT, - url, - body=mock_response, - content_type='application/json', - status=201) + responses.add( + responses.PUT, + url, + body=mock_response, + content_type='application/json', + status=201, + ) # Set up parameter values db = 'testString' @@ -1381,7 +1414,7 @@ def test_put_database_required_params(self): # Invoke method response = _service.put_database( db, - headers={} + headers={}, ) # Check for correct operation @@ -1405,11 +1438,13 @@ def test_put_database_value_error(self): # Set up mock url = preprocess_url('/testString') mock_response = '{"ok": true}' - responses.add(responses.PUT, - url, - body=mock_response, - content_type='application/json', - status=201) + responses.add( + responses.PUT, + url, + body=mock_response, + content_type='application/json', + status=201, + ) # Set up parameter values db = 'testString' @@ -1419,7 +1454,7 @@ def test_put_database_value_error(self): "db": db, } for param in req_param_dict.keys(): - req_copy = {key:val if key is not param else None for (key,val) in req_param_dict.items()} + req_copy = {key: val if key is not param else None for (key, val) in req_param_dict.items()} with pytest.raises(ValueError): _service.put_database(**req_copy) @@ -1432,6 +1467,7 @@ def test_put_database_value_error_with_retries(self): _service.disable_retries() self.test_put_database_value_error() + # endregion ############################################################################## # End of Service: Databases @@ -1442,7 +1478,8 @@ def test_put_database_value_error_with_retries(self): ############################################################################## # region -class TestNewInstance(): + +class TestNewInstance: """ Test Class for new_instance """ @@ -1469,7 +1506,8 @@ def test_new_instance_without_authenticator(self): service_name='TEST_SERVICE_NOT_FOUND', ) -class TestHeadDocument(): + +class TestHeadDocument: """ Test Class for head_document """ @@ -1481,9 +1519,11 @@ def test_head_document_all_params(self): """ # Set up mock url = preprocess_url('/testString/testString') - responses.add(responses.HEAD, - url, - status=200) + responses.add( + responses.HEAD, + url, + status=200, + ) # Set up parameter values db = 'testString' @@ -1499,14 +1539,14 @@ def test_head_document_all_params(self): if_none_match=if_none_match, latest=latest, rev=rev, - headers={} + headers={}, ) # Check for correct operation assert len(responses.calls) == 1 assert response.status_code == 200 # Validate query params - query_string = responses.calls[0].request.url.split('?',1)[1] + query_string = responses.calls[0].request.url.split('?', 1)[1] query_string = urllib.parse.unquote_plus(query_string) assert 'latest={}'.format('true' if latest else 'false') in query_string assert 'rev={}'.format(rev) in query_string @@ -1527,9 +1567,11 @@ def test_head_document_required_params(self): """ # Set up mock url = preprocess_url('/testString/testString') - responses.add(responses.HEAD, - url, - status=200) + responses.add( + responses.HEAD, + url, + status=200, + ) # Set up parameter values db = 'testString' @@ -1539,7 +1581,7 @@ def test_head_document_required_params(self): response = _service.head_document( db, doc_id, - headers={} + headers={}, ) # Check for correct operation @@ -1562,9 +1604,11 @@ def test_head_document_value_error(self): """ # Set up mock url = preprocess_url('/testString/testString') - responses.add(responses.HEAD, - url, - status=200) + responses.add( + responses.HEAD, + url, + status=200, + ) # Set up parameter values db = 'testString' @@ -1576,7 +1620,7 @@ def test_head_document_value_error(self): "doc_id": doc_id, } for param in req_param_dict.keys(): - req_copy = {key:val if key is not param else None for (key,val) in req_param_dict.items()} + req_copy = {key: val if key is not param else None for (key, val) in req_param_dict.items()} with pytest.raises(ValueError): _service.head_document(**req_copy) @@ -1589,7 +1633,8 @@ def test_head_document_value_error_with_retries(self): _service.disable_retries() self.test_head_document_value_error() -class TestPostDocument(): + +class TestPostDocument: """ Test Class for post_document """ @@ -1601,12 +1646,14 @@ def test_post_document_all_params(self): """ # Set up mock url = preprocess_url('/testString') - mock_response = '{"id": "id", "rev": "rev", "ok": true, "caused_by": "caused_by", "error": "error", "reason": "reason"}' - responses.add(responses.POST, - url, - body=mock_response, - content_type='application/json', - status=201) + mock_response = '{"id": "id", "rev": "rev", "ok": true, "caused_by": "caused_by", "error": "error", "reason": "reason", "ref": 0}' + responses.add( + responses.POST, + url, + body=mock_response, + content_type='application/json', + status=201, + ) # Construct a dict representation of a Attachment model attachment_model = {} @@ -1636,12 +1683,21 @@ def test_post_document_all_params(self): document_model['_conflicts'] = ['testString'] document_model['_deleted'] = True document_model['_deleted_conflicts'] = ['testString'] - document_model['_id'] = 'testString' + document_model['_id'] = 'exampleid' document_model['_local_seq'] = 'testString' document_model['_rev'] = 'testString' document_model['_revisions'] = revisions_model document_model['_revs_info'] = [document_revision_status_model] - document_model['foo'] = 'testString' + document_model['brand'] = 'Foo' + document_model['colours'] = '["red","green","black","blue"]' + document_model['description'] = 'Slim Colourful Design Electronic Cooking Appliance for ...' + document_model['image'] = 'assets/img/0gmsnghhew.jpg' + document_model['keywords'] = '["Foo","Scales","Weight","Digital","Kitchen"]' + document_model['name'] = 'Digital Kitchen Scales' + document_model['price'] = '14.99' + document_model['productId'] = '1000042' + document_model['taxonomy'] = '["Home","Kitchen","Small Appliances"]' + document_model['type'] = 'product' # Set up parameter values db = 'testString' @@ -1655,18 +1711,19 @@ def test_post_document_all_params(self): document, content_type=content_type, batch=batch, - headers={} + headers={}, ) # Check for correct operation assert len(responses.calls) == 1 assert response.status_code == 201 # Validate query params - query_string = responses.calls[0].request.url.split('?',1)[1] + query_string = responses.calls[0].request.url.split('?', 1)[1] query_string = urllib.parse.unquote_plus(query_string) assert 'batch={}'.format(batch) in query_string - # decompress gzip compressed request body - responses.calls[0].request.body = gzip.decompress(responses.calls[0].request.body) + # Decompress gzip compressed request body. + raw_body = responses.calls[0].request.body + responses.calls[0].request.body = gzip.decompress(raw_body.read() if isinstance(raw_body, io.IOBase) else raw_body) # Validate body params @@ -1686,12 +1743,14 @@ def test_post_document_required_params(self): """ # Set up mock url = preprocess_url('/testString') - mock_response = '{"id": "id", "rev": "rev", "ok": true, "caused_by": "caused_by", "error": "error", "reason": "reason"}' - responses.add(responses.POST, - url, - body=mock_response, - content_type='application/json', - status=201) + mock_response = '{"id": "id", "rev": "rev", "ok": true, "caused_by": "caused_by", "error": "error", "reason": "reason", "ref": 0}' + responses.add( + responses.POST, + url, + body=mock_response, + content_type='application/json', + status=201, + ) # Construct a dict representation of a Attachment model attachment_model = {} @@ -1721,12 +1780,21 @@ def test_post_document_required_params(self): document_model['_conflicts'] = ['testString'] document_model['_deleted'] = True document_model['_deleted_conflicts'] = ['testString'] - document_model['_id'] = 'testString' + document_model['_id'] = 'exampleid' document_model['_local_seq'] = 'testString' document_model['_rev'] = 'testString' document_model['_revisions'] = revisions_model document_model['_revs_info'] = [document_revision_status_model] - document_model['foo'] = 'testString' + document_model['brand'] = 'Foo' + document_model['colours'] = '["red","green","black","blue"]' + document_model['description'] = 'Slim Colourful Design Electronic Cooking Appliance for ...' + document_model['image'] = 'assets/img/0gmsnghhew.jpg' + document_model['keywords'] = '["Foo","Scales","Weight","Digital","Kitchen"]' + document_model['name'] = 'Digital Kitchen Scales' + document_model['price'] = '14.99' + document_model['productId'] = '1000042' + document_model['taxonomy'] = '["Home","Kitchen","Small Appliances"]' + document_model['type'] = 'product' # Set up parameter values db = 'testString' @@ -1736,14 +1804,15 @@ def test_post_document_required_params(self): response = _service.post_document( db, document, - headers={} + headers={}, ) # Check for correct operation assert len(responses.calls) == 1 assert response.status_code == 201 - # decompress gzip compressed request body - responses.calls[0].request.body = gzip.decompress(responses.calls[0].request.body) + # Decompress gzip compressed request body. + raw_body = responses.calls[0].request.body + responses.calls[0].request.body = gzip.decompress(raw_body.read() if isinstance(raw_body, io.IOBase) else raw_body) # Validate body params @@ -1763,12 +1832,14 @@ def test_post_document_value_error(self): """ # Set up mock url = preprocess_url('/testString') - mock_response = '{"id": "id", "rev": "rev", "ok": true, "caused_by": "caused_by", "error": "error", "reason": "reason"}' - responses.add(responses.POST, - url, - body=mock_response, - content_type='application/json', - status=201) + mock_response = '{"id": "id", "rev": "rev", "ok": true, "caused_by": "caused_by", "error": "error", "reason": "reason", "ref": 0}' + responses.add( + responses.POST, + url, + body=mock_response, + content_type='application/json', + status=201, + ) # Construct a dict representation of a Attachment model attachment_model = {} @@ -1798,12 +1869,21 @@ def test_post_document_value_error(self): document_model['_conflicts'] = ['testString'] document_model['_deleted'] = True document_model['_deleted_conflicts'] = ['testString'] - document_model['_id'] = 'testString' + document_model['_id'] = 'exampleid' document_model['_local_seq'] = 'testString' document_model['_rev'] = 'testString' document_model['_revisions'] = revisions_model document_model['_revs_info'] = [document_revision_status_model] - document_model['foo'] = 'testString' + document_model['brand'] = 'Foo' + document_model['colours'] = '["red","green","black","blue"]' + document_model['description'] = 'Slim Colourful Design Electronic Cooking Appliance for ...' + document_model['image'] = 'assets/img/0gmsnghhew.jpg' + document_model['keywords'] = '["Foo","Scales","Weight","Digital","Kitchen"]' + document_model['name'] = 'Digital Kitchen Scales' + document_model['price'] = '14.99' + document_model['productId'] = '1000042' + document_model['taxonomy'] = '["Home","Kitchen","Small Appliances"]' + document_model['type'] = 'product' # Set up parameter values db = 'testString' @@ -1815,7 +1895,7 @@ def test_post_document_value_error(self): "document": document, } for param in req_param_dict.keys(): - req_copy = {key:val if key is not param else None for (key,val) in req_param_dict.items()} + req_copy = {key: val if key is not param else None for (key, val) in req_param_dict.items()} with pytest.raises(ValueError): _service.post_document(**req_copy) @@ -1828,7 +1908,8 @@ def test_post_document_value_error_with_retries(self): _service.disable_retries() self.test_post_document_value_error() -class TestPostAllDocs(): + +class TestPostAllDocs: """ Test Class for post_all_docs """ @@ -1840,12 +1921,14 @@ def test_post_all_docs_all_params(self): """ # Set up mock url = preprocess_url('/testString/_all_docs') - mock_response = '{"total_rows": 0, "rows": [{"caused_by": "caused_by", "error": "error", "reason": "reason", "doc": {"_attachments": {"mapKey": {"content_type": "content_type", "data": "VGhpcyBpcyBhbiBlbmNvZGVkIGJ5dGUgYXJyYXku", "digest": "digest", "encoded_length": 0, "encoding": "encoding", "follows": false, "length": 0, "revpos": 1, "stub": true}}, "_conflicts": ["conflicts"], "_deleted": false, "_deleted_conflicts": ["deleted_conflicts"], "_id": "id", "_local_seq": "local_seq", "_rev": "rev", "_revisions": {"ids": ["ids"], "start": 1}, "_revs_info": [{"rev": "rev", "status": "available"}]}, "id": "id", "key": "key", "value": {"rev": "rev"}}], "update_seq": "update_seq"}' - responses.add(responses.POST, - url, - body=mock_response, - content_type='application/json', - status=200) + mock_response = '{"total_rows": 0, "rows": [{"caused_by": "caused_by", "error": "error", "reason": "reason", "ref": 0, "doc": {"_attachments": {"mapKey": {"content_type": "content_type", "data": "VGhpcyBpcyBhIG1vY2sgYnl0ZSBhcnJheSB2YWx1ZS4=", "digest": "digest", "encoded_length": 0, "encoding": "encoding", "follows": false, "length": 0, "revpos": 1, "stub": true}}, "_conflicts": ["conflicts"], "_deleted": true, "_deleted_conflicts": ["deleted_conflicts"], "_id": "_id", "_local_seq": "_local_seq", "_rev": "_rev", "_revisions": {"ids": ["ids"], "start": 1}, "_revs_info": [{"rev": "rev", "status": "available"}]}, "id": "id", "key": "key", "value": {"deleted": false, "rev": "rev"}}], "update_seq": "update_seq"}' + responses.add( + responses.POST, + url, + body=mock_response, + content_type='application/json', + status=200, + ) # Set up parameter values db = 'testString' @@ -1855,13 +1938,13 @@ def test_post_all_docs_all_params(self): descending = False include_docs = False inclusive_end = True - limit = 0 + limit = 10 skip = 0 update_seq = False end_key = 'testString' key = 'testString' keys = ['testString'] - start_key = 'testString' + start_key = '0007741142412418284' # Invoke method response = _service.post_all_docs( @@ -1879,14 +1962,15 @@ def test_post_all_docs_all_params(self): key=key, keys=keys, start_key=start_key, - headers={} + headers={}, ) # Check for correct operation assert len(responses.calls) == 1 assert response.status_code == 200 - # decompress gzip compressed request body - responses.calls[0].request.body = gzip.decompress(responses.calls[0].request.body) + # Decompress gzip compressed request body. + raw_body = responses.calls[0].request.body + responses.calls[0].request.body = gzip.decompress(raw_body.read() if isinstance(raw_body, io.IOBase) else raw_body) # Validate body params req_body = json.loads(str(responses.calls[0].request.body, 'utf-8')) @@ -1896,13 +1980,13 @@ def test_post_all_docs_all_params(self): assert req_body['descending'] == False assert req_body['include_docs'] == False assert req_body['inclusive_end'] == True - assert req_body['limit'] == 0 + assert req_body['limit'] == 10 assert req_body['skip'] == 0 assert req_body['update_seq'] == False assert req_body['end_key'] == 'testString' assert req_body['key'] == 'testString' assert req_body['keys'] == ['testString'] - assert req_body['start_key'] == 'testString' + assert req_body['start_key'] == '0007741142412418284' def test_post_all_docs_all_params_with_retries(self): # Enable retries and run test_post_all_docs_all_params. @@ -1920,12 +2004,14 @@ def test_post_all_docs_value_error(self): """ # Set up mock url = preprocess_url('/testString/_all_docs') - mock_response = '{"total_rows": 0, "rows": [{"caused_by": "caused_by", "error": "error", "reason": "reason", "doc": {"_attachments": {"mapKey": {"content_type": "content_type", "data": "VGhpcyBpcyBhbiBlbmNvZGVkIGJ5dGUgYXJyYXku", "digest": "digest", "encoded_length": 0, "encoding": "encoding", "follows": false, "length": 0, "revpos": 1, "stub": true}}, "_conflicts": ["conflicts"], "_deleted": false, "_deleted_conflicts": ["deleted_conflicts"], "_id": "id", "_local_seq": "local_seq", "_rev": "rev", "_revisions": {"ids": ["ids"], "start": 1}, "_revs_info": [{"rev": "rev", "status": "available"}]}, "id": "id", "key": "key", "value": {"rev": "rev"}}], "update_seq": "update_seq"}' - responses.add(responses.POST, - url, - body=mock_response, - content_type='application/json', - status=200) + mock_response = '{"total_rows": 0, "rows": [{"caused_by": "caused_by", "error": "error", "reason": "reason", "ref": 0, "doc": {"_attachments": {"mapKey": {"content_type": "content_type", "data": "VGhpcyBpcyBhIG1vY2sgYnl0ZSBhcnJheSB2YWx1ZS4=", "digest": "digest", "encoded_length": 0, "encoding": "encoding", "follows": false, "length": 0, "revpos": 1, "stub": true}}, "_conflicts": ["conflicts"], "_deleted": true, "_deleted_conflicts": ["deleted_conflicts"], "_id": "_id", "_local_seq": "_local_seq", "_rev": "_rev", "_revisions": {"ids": ["ids"], "start": 1}, "_revs_info": [{"rev": "rev", "status": "available"}]}, "id": "id", "key": "key", "value": {"deleted": false, "rev": "rev"}}], "update_seq": "update_seq"}' + responses.add( + responses.POST, + url, + body=mock_response, + content_type='application/json', + status=200, + ) # Set up parameter values db = 'testString' @@ -1935,20 +2021,20 @@ def test_post_all_docs_value_error(self): descending = False include_docs = False inclusive_end = True - limit = 0 + limit = 10 skip = 0 update_seq = False end_key = 'testString' key = 'testString' keys = ['testString'] - start_key = 'testString' + start_key = '0007741142412418284' # Pass in all but one required param and check for a ValueError req_param_dict = { "db": db, } for param in req_param_dict.keys(): - req_copy = {key:val if key is not param else None for (key,val) in req_param_dict.items()} + req_copy = {key: val if key is not param else None for (key, val) in req_param_dict.items()} with pytest.raises(ValueError): _service.post_all_docs(**req_copy) @@ -1961,7 +2047,8 @@ def test_post_all_docs_value_error_with_retries(self): _service.disable_retries() self.test_post_all_docs_value_error() -class TestPostAllDocsAsStream(): + +class TestPostAllDocsAsStream: """ Test Class for post_all_docs_as_stream """ @@ -1974,11 +2061,13 @@ def test_post_all_docs_as_stream_all_params(self): # Set up mock url = preprocess_url('/testString/_all_docs') mock_response = '{"foo": "this is a mock response for JSON streaming"}' - responses.add(responses.POST, - url, - body=mock_response, - content_type='application/json', - status=200) + responses.add( + responses.POST, + url, + body=mock_response, + content_type='application/json', + status=200, + ) # Set up parameter values db = 'testString' @@ -1988,13 +2077,13 @@ def test_post_all_docs_as_stream_all_params(self): descending = False include_docs = False inclusive_end = True - limit = 0 + limit = 10 skip = 0 update_seq = False end_key = 'testString' key = 'testString' keys = ['testString'] - start_key = 'testString' + start_key = '0007741142412418284' # Invoke method response = _service.post_all_docs_as_stream( @@ -2012,14 +2101,15 @@ def test_post_all_docs_as_stream_all_params(self): key=key, keys=keys, start_key=start_key, - headers={} + headers={}, ) # Check for correct operation assert len(responses.calls) == 1 assert response.status_code == 200 - # decompress gzip compressed request body - responses.calls[0].request.body = gzip.decompress(responses.calls[0].request.body) + # Decompress gzip compressed request body. + raw_body = responses.calls[0].request.body + responses.calls[0].request.body = gzip.decompress(raw_body.read() if isinstance(raw_body, io.IOBase) else raw_body) # Validate body params req_body = json.loads(str(responses.calls[0].request.body, 'utf-8')) @@ -2029,13 +2119,13 @@ def test_post_all_docs_as_stream_all_params(self): assert req_body['descending'] == False assert req_body['include_docs'] == False assert req_body['inclusive_end'] == True - assert req_body['limit'] == 0 + assert req_body['limit'] == 10 assert req_body['skip'] == 0 assert req_body['update_seq'] == False assert req_body['end_key'] == 'testString' assert req_body['key'] == 'testString' assert req_body['keys'] == ['testString'] - assert req_body['start_key'] == 'testString' + assert req_body['start_key'] == '0007741142412418284' # Verify streamed JSON response result = response.get_result() @@ -2060,11 +2150,13 @@ def test_post_all_docs_as_stream_value_error(self): # Set up mock url = preprocess_url('/testString/_all_docs') mock_response = '{"foo": "this is a mock response for JSON streaming"}' - responses.add(responses.POST, - url, - body=mock_response, - content_type='application/json', - status=200) + responses.add( + responses.POST, + url, + body=mock_response, + content_type='application/json', + status=200, + ) # Set up parameter values db = 'testString' @@ -2074,20 +2166,20 @@ def test_post_all_docs_as_stream_value_error(self): descending = False include_docs = False inclusive_end = True - limit = 0 + limit = 10 skip = 0 update_seq = False end_key = 'testString' key = 'testString' keys = ['testString'] - start_key = 'testString' + start_key = '0007741142412418284' # Pass in all but one required param and check for a ValueError req_param_dict = { "db": db, } for param in req_param_dict.keys(): - req_copy = {key:val if key is not param else None for (key,val) in req_param_dict.items()} + req_copy = {key: val if key is not param else None for (key, val) in req_param_dict.items()} with pytest.raises(ValueError): _service.post_all_docs_as_stream(**req_copy) @@ -2100,7 +2192,8 @@ def test_post_all_docs_as_stream_value_error_with_retries(self): _service.disable_retries() self.test_post_all_docs_as_stream_value_error() -class TestPostAllDocsQueries(): + +class TestPostAllDocsQueries: """ Test Class for post_all_docs_queries """ @@ -2112,12 +2205,14 @@ def test_post_all_docs_queries_all_params(self): """ # Set up mock url = preprocess_url('/testString/_all_docs/queries') - mock_response = '{"results": [{"total_rows": 0, "rows": [{"caused_by": "caused_by", "error": "error", "reason": "reason", "doc": {"_attachments": {"mapKey": {"content_type": "content_type", "data": "VGhpcyBpcyBhbiBlbmNvZGVkIGJ5dGUgYXJyYXku", "digest": "digest", "encoded_length": 0, "encoding": "encoding", "follows": false, "length": 0, "revpos": 1, "stub": true}}, "_conflicts": ["conflicts"], "_deleted": false, "_deleted_conflicts": ["deleted_conflicts"], "_id": "id", "_local_seq": "local_seq", "_rev": "rev", "_revisions": {"ids": ["ids"], "start": 1}, "_revs_info": [{"rev": "rev", "status": "available"}]}, "id": "id", "key": "key", "value": {"rev": "rev"}}], "update_seq": "update_seq"}]}' - responses.add(responses.POST, - url, - body=mock_response, - content_type='application/json', - status=200) + mock_response = '{"results": [{"total_rows": 0, "rows": [{"caused_by": "caused_by", "error": "error", "reason": "reason", "ref": 0, "doc": {"_attachments": {"mapKey": {"content_type": "content_type", "data": "VGhpcyBpcyBhIG1vY2sgYnl0ZSBhcnJheSB2YWx1ZS4=", "digest": "digest", "encoded_length": 0, "encoding": "encoding", "follows": false, "length": 0, "revpos": 1, "stub": true}}, "_conflicts": ["conflicts"], "_deleted": true, "_deleted_conflicts": ["deleted_conflicts"], "_id": "_id", "_local_seq": "_local_seq", "_rev": "_rev", "_revisions": {"ids": ["ids"], "start": 1}, "_revs_info": [{"rev": "rev", "status": "available"}]}, "id": "id", "key": "key", "value": {"deleted": false, "rev": "rev"}}], "update_seq": "update_seq"}]}' + responses.add( + responses.POST, + url, + body=mock_response, + content_type='application/json', + status=200, + ) # Construct a dict representation of a AllDocsQuery model all_docs_query_model = {} @@ -2132,7 +2227,7 @@ def test_post_all_docs_queries_all_params(self): all_docs_query_model['update_seq'] = False all_docs_query_model['end_key'] = 'testString' all_docs_query_model['key'] = 'testString' - all_docs_query_model['keys'] = ['testString'] + all_docs_query_model['keys'] = ['small-appliances:1000042', 'small-appliances:1000043'] all_docs_query_model['start_key'] = 'testString' # Set up parameter values @@ -2143,14 +2238,15 @@ def test_post_all_docs_queries_all_params(self): response = _service.post_all_docs_queries( db, queries, - headers={} + headers={}, ) # Check for correct operation assert len(responses.calls) == 1 assert response.status_code == 200 - # decompress gzip compressed request body - responses.calls[0].request.body = gzip.decompress(responses.calls[0].request.body) + # Decompress gzip compressed request body. + raw_body = responses.calls[0].request.body + responses.calls[0].request.body = gzip.decompress(raw_body.read() if isinstance(raw_body, io.IOBase) else raw_body) # Validate body params req_body = json.loads(str(responses.calls[0].request.body, 'utf-8')) @@ -2172,12 +2268,14 @@ def test_post_all_docs_queries_value_error(self): """ # Set up mock url = preprocess_url('/testString/_all_docs/queries') - mock_response = '{"results": [{"total_rows": 0, "rows": [{"caused_by": "caused_by", "error": "error", "reason": "reason", "doc": {"_attachments": {"mapKey": {"content_type": "content_type", "data": "VGhpcyBpcyBhbiBlbmNvZGVkIGJ5dGUgYXJyYXku", "digest": "digest", "encoded_length": 0, "encoding": "encoding", "follows": false, "length": 0, "revpos": 1, "stub": true}}, "_conflicts": ["conflicts"], "_deleted": false, "_deleted_conflicts": ["deleted_conflicts"], "_id": "id", "_local_seq": "local_seq", "_rev": "rev", "_revisions": {"ids": ["ids"], "start": 1}, "_revs_info": [{"rev": "rev", "status": "available"}]}, "id": "id", "key": "key", "value": {"rev": "rev"}}], "update_seq": "update_seq"}]}' - responses.add(responses.POST, - url, - body=mock_response, - content_type='application/json', - status=200) + mock_response = '{"results": [{"total_rows": 0, "rows": [{"caused_by": "caused_by", "error": "error", "reason": "reason", "ref": 0, "doc": {"_attachments": {"mapKey": {"content_type": "content_type", "data": "VGhpcyBpcyBhIG1vY2sgYnl0ZSBhcnJheSB2YWx1ZS4=", "digest": "digest", "encoded_length": 0, "encoding": "encoding", "follows": false, "length": 0, "revpos": 1, "stub": true}}, "_conflicts": ["conflicts"], "_deleted": true, "_deleted_conflicts": ["deleted_conflicts"], "_id": "_id", "_local_seq": "_local_seq", "_rev": "_rev", "_revisions": {"ids": ["ids"], "start": 1}, "_revs_info": [{"rev": "rev", "status": "available"}]}, "id": "id", "key": "key", "value": {"deleted": false, "rev": "rev"}}], "update_seq": "update_seq"}]}' + responses.add( + responses.POST, + url, + body=mock_response, + content_type='application/json', + status=200, + ) # Construct a dict representation of a AllDocsQuery model all_docs_query_model = {} @@ -2192,7 +2290,7 @@ def test_post_all_docs_queries_value_error(self): all_docs_query_model['update_seq'] = False all_docs_query_model['end_key'] = 'testString' all_docs_query_model['key'] = 'testString' - all_docs_query_model['keys'] = ['testString'] + all_docs_query_model['keys'] = ['small-appliances:1000042', 'small-appliances:1000043'] all_docs_query_model['start_key'] = 'testString' # Set up parameter values @@ -2205,7 +2303,7 @@ def test_post_all_docs_queries_value_error(self): "queries": queries, } for param in req_param_dict.keys(): - req_copy = {key:val if key is not param else None for (key,val) in req_param_dict.items()} + req_copy = {key: val if key is not param else None for (key, val) in req_param_dict.items()} with pytest.raises(ValueError): _service.post_all_docs_queries(**req_copy) @@ -2218,7 +2316,8 @@ def test_post_all_docs_queries_value_error_with_retries(self): _service.disable_retries() self.test_post_all_docs_queries_value_error() -class TestPostAllDocsQueriesAsStream(): + +class TestPostAllDocsQueriesAsStream: """ Test Class for post_all_docs_queries_as_stream """ @@ -2231,11 +2330,13 @@ def test_post_all_docs_queries_as_stream_all_params(self): # Set up mock url = preprocess_url('/testString/_all_docs/queries') mock_response = '{"foo": "this is a mock response for JSON streaming"}' - responses.add(responses.POST, - url, - body=mock_response, - content_type='application/json', - status=200) + responses.add( + responses.POST, + url, + body=mock_response, + content_type='application/json', + status=200, + ) # Construct a dict representation of a AllDocsQuery model all_docs_query_model = {} @@ -2250,7 +2351,7 @@ def test_post_all_docs_queries_as_stream_all_params(self): all_docs_query_model['update_seq'] = False all_docs_query_model['end_key'] = 'testString' all_docs_query_model['key'] = 'testString' - all_docs_query_model['keys'] = ['testString'] + all_docs_query_model['keys'] = ['small-appliances:1000042', 'small-appliances:1000043'] all_docs_query_model['start_key'] = 'testString' # Set up parameter values @@ -2261,14 +2362,15 @@ def test_post_all_docs_queries_as_stream_all_params(self): response = _service.post_all_docs_queries_as_stream( db, queries, - headers={} + headers={}, ) # Check for correct operation assert len(responses.calls) == 1 assert response.status_code == 200 - # decompress gzip compressed request body - responses.calls[0].request.body = gzip.decompress(responses.calls[0].request.body) + # Decompress gzip compressed request body. + raw_body = responses.calls[0].request.body + responses.calls[0].request.body = gzip.decompress(raw_body.read() if isinstance(raw_body, io.IOBase) else raw_body) # Validate body params req_body = json.loads(str(responses.calls[0].request.body, 'utf-8')) @@ -2297,11 +2399,13 @@ def test_post_all_docs_queries_as_stream_value_error(self): # Set up mock url = preprocess_url('/testString/_all_docs/queries') mock_response = '{"foo": "this is a mock response for JSON streaming"}' - responses.add(responses.POST, - url, - body=mock_response, - content_type='application/json', - status=200) + responses.add( + responses.POST, + url, + body=mock_response, + content_type='application/json', + status=200, + ) # Construct a dict representation of a AllDocsQuery model all_docs_query_model = {} @@ -2316,7 +2420,7 @@ def test_post_all_docs_queries_as_stream_value_error(self): all_docs_query_model['update_seq'] = False all_docs_query_model['end_key'] = 'testString' all_docs_query_model['key'] = 'testString' - all_docs_query_model['keys'] = ['testString'] + all_docs_query_model['keys'] = ['small-appliances:1000042', 'small-appliances:1000043'] all_docs_query_model['start_key'] = 'testString' # Set up parameter values @@ -2329,7 +2433,7 @@ def test_post_all_docs_queries_as_stream_value_error(self): "queries": queries, } for param in req_param_dict.keys(): - req_copy = {key:val if key is not param else None for (key,val) in req_param_dict.items()} + req_copy = {key: val if key is not param else None for (key, val) in req_param_dict.items()} with pytest.raises(ValueError): _service.post_all_docs_queries_as_stream(**req_copy) @@ -2342,7 +2446,8 @@ def test_post_all_docs_queries_as_stream_value_error_with_retries(self): _service.disable_retries() self.test_post_all_docs_queries_as_stream_value_error() -class TestPostBulkDocs(): + +class TestPostBulkDocs: """ Test Class for post_bulk_docs """ @@ -2354,12 +2459,14 @@ def test_post_bulk_docs_all_params(self): """ # Set up mock url = preprocess_url('/testString/_bulk_docs') - mock_response = '[{"id": "id", "rev": "rev", "ok": true, "caused_by": "caused_by", "error": "error", "reason": "reason"}]' - responses.add(responses.POST, - url, - body=mock_response, - content_type='application/json', - status=201) + mock_response = '[{"id": "id", "rev": "rev", "ok": true, "caused_by": "caused_by", "error": "error", "reason": "reason", "ref": 0}]' + responses.add( + responses.POST, + url, + body=mock_response, + content_type='application/json', + status=201, + ) # Construct a dict representation of a Attachment model attachment_model = {} @@ -2389,12 +2496,16 @@ def test_post_bulk_docs_all_params(self): document_model['_conflicts'] = ['testString'] document_model['_deleted'] = True document_model['_deleted_conflicts'] = ['testString'] - document_model['_id'] = 'testString' + document_model['_id'] = '0007241142412418284' document_model['_local_seq'] = 'testString' document_model['_rev'] = 'testString' document_model['_revisions'] = revisions_model document_model['_revs_info'] = [document_revision_status_model] - document_model['foo'] = 'testString' + document_model['date'] = '2019-01-28T10:44:22.000Z' + document_model['eventType'] = 'addedToBasket' + document_model['productId'] = '1000042' + document_model['type'] = 'event' + document_model['userid'] = 'abc123' # Construct a dict representation of a BulkDocs model bulk_docs_model = {} @@ -2409,14 +2520,15 @@ def test_post_bulk_docs_all_params(self): response = _service.post_bulk_docs( db, bulk_docs, - headers={} + headers={}, ) # Check for correct operation assert len(responses.calls) == 1 assert response.status_code == 201 - # decompress gzip compressed request body - responses.calls[0].request.body = gzip.decompress(responses.calls[0].request.body) + # Decompress gzip compressed request body. + raw_body = responses.calls[0].request.body + responses.calls[0].request.body = gzip.decompress(raw_body.read() if isinstance(raw_body, io.IOBase) else raw_body) # Validate body params req_body = json.loads(str(responses.calls[0].request.body, 'utf-8')) @@ -2438,12 +2550,14 @@ def test_post_bulk_docs_value_error(self): """ # Set up mock url = preprocess_url('/testString/_bulk_docs') - mock_response = '[{"id": "id", "rev": "rev", "ok": true, "caused_by": "caused_by", "error": "error", "reason": "reason"}]' - responses.add(responses.POST, - url, - body=mock_response, - content_type='application/json', - status=201) + mock_response = '[{"id": "id", "rev": "rev", "ok": true, "caused_by": "caused_by", "error": "error", "reason": "reason", "ref": 0}]' + responses.add( + responses.POST, + url, + body=mock_response, + content_type='application/json', + status=201, + ) # Construct a dict representation of a Attachment model attachment_model = {} @@ -2473,12 +2587,16 @@ def test_post_bulk_docs_value_error(self): document_model['_conflicts'] = ['testString'] document_model['_deleted'] = True document_model['_deleted_conflicts'] = ['testString'] - document_model['_id'] = 'testString' + document_model['_id'] = '0007241142412418284' document_model['_local_seq'] = 'testString' document_model['_rev'] = 'testString' document_model['_revisions'] = revisions_model document_model['_revs_info'] = [document_revision_status_model] - document_model['foo'] = 'testString' + document_model['date'] = '2019-01-28T10:44:22.000Z' + document_model['eventType'] = 'addedToBasket' + document_model['productId'] = '1000042' + document_model['type'] = 'event' + document_model['userid'] = 'abc123' # Construct a dict representation of a BulkDocs model bulk_docs_model = {} @@ -2495,7 +2613,7 @@ def test_post_bulk_docs_value_error(self): "bulk_docs": bulk_docs, } for param in req_param_dict.keys(): - req_copy = {key:val if key is not param else None for (key,val) in req_param_dict.items()} + req_copy = {key: val if key is not param else None for (key, val) in req_param_dict.items()} with pytest.raises(ValueError): _service.post_bulk_docs(**req_copy) @@ -2508,7 +2626,8 @@ def test_post_bulk_docs_value_error_with_retries(self): _service.disable_retries() self.test_post_bulk_docs_value_error() -class TestPostBulkGet(): + +class TestPostBulkGet: """ Test Class for post_bulk_get """ @@ -2520,18 +2639,20 @@ def test_post_bulk_get_all_params(self): """ # Set up mock url = preprocess_url('/testString/_bulk_get') - mock_response = '{"results": [{"docs": [{"error": {"id": "id", "rev": "rev", "ok": true, "caused_by": "caused_by", "error": "error", "reason": "reason"}, "ok": {"_attachments": {"mapKey": {"content_type": "content_type", "data": "VGhpcyBpcyBhbiBlbmNvZGVkIGJ5dGUgYXJyYXku", "digest": "digest", "encoded_length": 0, "encoding": "encoding", "follows": false, "length": 0, "revpos": 1, "stub": true}}, "_conflicts": ["conflicts"], "_deleted": false, "_deleted_conflicts": ["deleted_conflicts"], "_id": "id", "_local_seq": "local_seq", "_rev": "rev", "_revisions": {"ids": ["ids"], "start": 1}, "_revs_info": [{"rev": "rev", "status": "available"}]}}], "id": "id"}]}' - responses.add(responses.POST, - url, - body=mock_response, - content_type='application/json', - status=200) + mock_response = '{"results": [{"docs": [{"error": {"id": "id", "rev": "rev", "ok": true, "caused_by": "caused_by", "error": "error", "reason": "reason", "ref": 0}, "ok": {"_attachments": {"mapKey": {"content_type": "content_type", "data": "VGhpcyBpcyBhIG1vY2sgYnl0ZSBhcnJheSB2YWx1ZS4=", "digest": "digest", "encoded_length": 0, "encoding": "encoding", "follows": false, "length": 0, "revpos": 1, "stub": true}}, "_conflicts": ["conflicts"], "_deleted": true, "_deleted_conflicts": ["deleted_conflicts"], "_id": "_id", "_local_seq": "_local_seq", "_rev": "_rev", "_revisions": {"ids": ["ids"], "start": 1}, "_revs_info": [{"rev": "rev", "status": "available"}]}}], "id": "id"}]}' + responses.add( + responses.POST, + url, + body=mock_response, + content_type='application/json', + status=200, + ) # Construct a dict representation of a BulkGetQueryDocument model bulk_get_query_document_model = {} bulk_get_query_document_model['atts_since'] = ['1-99b02e08da151943c2dcb40090160bb8'] - bulk_get_query_document_model['id'] = 'testString' - bulk_get_query_document_model['rev'] = 'testString' + bulk_get_query_document_model['id'] = 'order00067' + bulk_get_query_document_model['rev'] = '3-917fa2381192822767f010b95b45325b' # Set up parameter values db = 'testString' @@ -2549,21 +2670,22 @@ def test_post_bulk_get_all_params(self): att_encoding_info=att_encoding_info, latest=latest, revs=revs, - headers={} + headers={}, ) # Check for correct operation assert len(responses.calls) == 1 assert response.status_code == 200 # Validate query params - query_string = responses.calls[0].request.url.split('?',1)[1] + query_string = responses.calls[0].request.url.split('?', 1)[1] query_string = urllib.parse.unquote_plus(query_string) assert 'attachments={}'.format('true' if attachments else 'false') in query_string assert 'att_encoding_info={}'.format('true' if att_encoding_info else 'false') in query_string assert 'latest={}'.format('true' if latest else 'false') in query_string assert 'revs={}'.format('true' if revs else 'false') in query_string - # decompress gzip compressed request body - responses.calls[0].request.body = gzip.decompress(responses.calls[0].request.body) + # Decompress gzip compressed request body. + raw_body = responses.calls[0].request.body + responses.calls[0].request.body = gzip.decompress(raw_body.read() if isinstance(raw_body, io.IOBase) else raw_body) # Validate body params req_body = json.loads(str(responses.calls[0].request.body, 'utf-8')) @@ -2585,18 +2707,20 @@ def test_post_bulk_get_required_params(self): """ # Set up mock url = preprocess_url('/testString/_bulk_get') - mock_response = '{"results": [{"docs": [{"error": {"id": "id", "rev": "rev", "ok": true, "caused_by": "caused_by", "error": "error", "reason": "reason"}, "ok": {"_attachments": {"mapKey": {"content_type": "content_type", "data": "VGhpcyBpcyBhbiBlbmNvZGVkIGJ5dGUgYXJyYXku", "digest": "digest", "encoded_length": 0, "encoding": "encoding", "follows": false, "length": 0, "revpos": 1, "stub": true}}, "_conflicts": ["conflicts"], "_deleted": false, "_deleted_conflicts": ["deleted_conflicts"], "_id": "id", "_local_seq": "local_seq", "_rev": "rev", "_revisions": {"ids": ["ids"], "start": 1}, "_revs_info": [{"rev": "rev", "status": "available"}]}}], "id": "id"}]}' - responses.add(responses.POST, - url, - body=mock_response, - content_type='application/json', - status=200) + mock_response = '{"results": [{"docs": [{"error": {"id": "id", "rev": "rev", "ok": true, "caused_by": "caused_by", "error": "error", "reason": "reason", "ref": 0}, "ok": {"_attachments": {"mapKey": {"content_type": "content_type", "data": "VGhpcyBpcyBhIG1vY2sgYnl0ZSBhcnJheSB2YWx1ZS4=", "digest": "digest", "encoded_length": 0, "encoding": "encoding", "follows": false, "length": 0, "revpos": 1, "stub": true}}, "_conflicts": ["conflicts"], "_deleted": true, "_deleted_conflicts": ["deleted_conflicts"], "_id": "_id", "_local_seq": "_local_seq", "_rev": "_rev", "_revisions": {"ids": ["ids"], "start": 1}, "_revs_info": [{"rev": "rev", "status": "available"}]}}], "id": "id"}]}' + responses.add( + responses.POST, + url, + body=mock_response, + content_type='application/json', + status=200, + ) # Construct a dict representation of a BulkGetQueryDocument model bulk_get_query_document_model = {} bulk_get_query_document_model['atts_since'] = ['1-99b02e08da151943c2dcb40090160bb8'] - bulk_get_query_document_model['id'] = 'testString' - bulk_get_query_document_model['rev'] = 'testString' + bulk_get_query_document_model['id'] = 'order00067' + bulk_get_query_document_model['rev'] = '3-917fa2381192822767f010b95b45325b' # Set up parameter values db = 'testString' @@ -2606,14 +2730,15 @@ def test_post_bulk_get_required_params(self): response = _service.post_bulk_get( db, docs, - headers={} + headers={}, ) # Check for correct operation assert len(responses.calls) == 1 assert response.status_code == 200 - # decompress gzip compressed request body - responses.calls[0].request.body = gzip.decompress(responses.calls[0].request.body) + # Decompress gzip compressed request body. + raw_body = responses.calls[0].request.body + responses.calls[0].request.body = gzip.decompress(raw_body.read() if isinstance(raw_body, io.IOBase) else raw_body) # Validate body params req_body = json.loads(str(responses.calls[0].request.body, 'utf-8')) @@ -2635,18 +2760,20 @@ def test_post_bulk_get_value_error(self): """ # Set up mock url = preprocess_url('/testString/_bulk_get') - mock_response = '{"results": [{"docs": [{"error": {"id": "id", "rev": "rev", "ok": true, "caused_by": "caused_by", "error": "error", "reason": "reason"}, "ok": {"_attachments": {"mapKey": {"content_type": "content_type", "data": "VGhpcyBpcyBhbiBlbmNvZGVkIGJ5dGUgYXJyYXku", "digest": "digest", "encoded_length": 0, "encoding": "encoding", "follows": false, "length": 0, "revpos": 1, "stub": true}}, "_conflicts": ["conflicts"], "_deleted": false, "_deleted_conflicts": ["deleted_conflicts"], "_id": "id", "_local_seq": "local_seq", "_rev": "rev", "_revisions": {"ids": ["ids"], "start": 1}, "_revs_info": [{"rev": "rev", "status": "available"}]}}], "id": "id"}]}' - responses.add(responses.POST, - url, - body=mock_response, - content_type='application/json', - status=200) + mock_response = '{"results": [{"docs": [{"error": {"id": "id", "rev": "rev", "ok": true, "caused_by": "caused_by", "error": "error", "reason": "reason", "ref": 0}, "ok": {"_attachments": {"mapKey": {"content_type": "content_type", "data": "VGhpcyBpcyBhIG1vY2sgYnl0ZSBhcnJheSB2YWx1ZS4=", "digest": "digest", "encoded_length": 0, "encoding": "encoding", "follows": false, "length": 0, "revpos": 1, "stub": true}}, "_conflicts": ["conflicts"], "_deleted": true, "_deleted_conflicts": ["deleted_conflicts"], "_id": "_id", "_local_seq": "_local_seq", "_rev": "_rev", "_revisions": {"ids": ["ids"], "start": 1}, "_revs_info": [{"rev": "rev", "status": "available"}]}}], "id": "id"}]}' + responses.add( + responses.POST, + url, + body=mock_response, + content_type='application/json', + status=200, + ) # Construct a dict representation of a BulkGetQueryDocument model bulk_get_query_document_model = {} bulk_get_query_document_model['atts_since'] = ['1-99b02e08da151943c2dcb40090160bb8'] - bulk_get_query_document_model['id'] = 'testString' - bulk_get_query_document_model['rev'] = 'testString' + bulk_get_query_document_model['id'] = 'order00067' + bulk_get_query_document_model['rev'] = '3-917fa2381192822767f010b95b45325b' # Set up parameter values db = 'testString' @@ -2658,7 +2785,7 @@ def test_post_bulk_get_value_error(self): "docs": docs, } for param in req_param_dict.keys(): - req_copy = {key:val if key is not param else None for (key,val) in req_param_dict.items()} + req_copy = {key: val if key is not param else None for (key, val) in req_param_dict.items()} with pytest.raises(ValueError): _service.post_bulk_get(**req_copy) @@ -2671,7 +2798,8 @@ def test_post_bulk_get_value_error_with_retries(self): _service.disable_retries() self.test_post_bulk_get_value_error() -class TestPostBulkGetAsMixed(): + +class TestPostBulkGetAsMixed: """ Test Class for post_bulk_get_as_mixed """ @@ -2684,17 +2812,19 @@ def test_post_bulk_get_as_mixed_all_params(self): # Set up mock url = preprocess_url('/testString/_bulk_get') mock_response = 'This is a mock binary response.' - responses.add(responses.POST, - url, - body=mock_response, - content_type='multipart/mixed', - status=200) + responses.add( + responses.POST, + url, + body=mock_response, + content_type='multipart/mixed', + status=200, + ) # Construct a dict representation of a BulkGetQueryDocument model bulk_get_query_document_model = {} bulk_get_query_document_model['atts_since'] = ['1-99b02e08da151943c2dcb40090160bb8'] - bulk_get_query_document_model['id'] = 'testString' - bulk_get_query_document_model['rev'] = 'testString' + bulk_get_query_document_model['id'] = 'order00067' + bulk_get_query_document_model['rev'] = '3-917fa2381192822767f010b95b45325b' # Set up parameter values db = 'testString' @@ -2712,21 +2842,22 @@ def test_post_bulk_get_as_mixed_all_params(self): att_encoding_info=att_encoding_info, latest=latest, revs=revs, - headers={} + headers={}, ) # Check for correct operation assert len(responses.calls) == 1 assert response.status_code == 200 # Validate query params - query_string = responses.calls[0].request.url.split('?',1)[1] + query_string = responses.calls[0].request.url.split('?', 1)[1] query_string = urllib.parse.unquote_plus(query_string) assert 'attachments={}'.format('true' if attachments else 'false') in query_string assert 'att_encoding_info={}'.format('true' if att_encoding_info else 'false') in query_string assert 'latest={}'.format('true' if latest else 'false') in query_string assert 'revs={}'.format('true' if revs else 'false') in query_string - # decompress gzip compressed request body - responses.calls[0].request.body = gzip.decompress(responses.calls[0].request.body) + # Decompress gzip compressed request body. + raw_body = responses.calls[0].request.body + responses.calls[0].request.body = gzip.decompress(raw_body.read() if isinstance(raw_body, io.IOBase) else raw_body) # Validate body params req_body = json.loads(str(responses.calls[0].request.body, 'utf-8')) @@ -2749,17 +2880,19 @@ def test_post_bulk_get_as_mixed_required_params(self): # Set up mock url = preprocess_url('/testString/_bulk_get') mock_response = 'This is a mock binary response.' - responses.add(responses.POST, - url, - body=mock_response, - content_type='multipart/mixed', - status=200) + responses.add( + responses.POST, + url, + body=mock_response, + content_type='multipart/mixed', + status=200, + ) # Construct a dict representation of a BulkGetQueryDocument model bulk_get_query_document_model = {} bulk_get_query_document_model['atts_since'] = ['1-99b02e08da151943c2dcb40090160bb8'] - bulk_get_query_document_model['id'] = 'testString' - bulk_get_query_document_model['rev'] = 'testString' + bulk_get_query_document_model['id'] = 'order00067' + bulk_get_query_document_model['rev'] = '3-917fa2381192822767f010b95b45325b' # Set up parameter values db = 'testString' @@ -2769,14 +2902,15 @@ def test_post_bulk_get_as_mixed_required_params(self): response = _service.post_bulk_get_as_mixed( db, docs, - headers={} + headers={}, ) # Check for correct operation assert len(responses.calls) == 1 assert response.status_code == 200 - # decompress gzip compressed request body - responses.calls[0].request.body = gzip.decompress(responses.calls[0].request.body) + # Decompress gzip compressed request body. + raw_body = responses.calls[0].request.body + responses.calls[0].request.body = gzip.decompress(raw_body.read() if isinstance(raw_body, io.IOBase) else raw_body) # Validate body params req_body = json.loads(str(responses.calls[0].request.body, 'utf-8')) @@ -2799,17 +2933,19 @@ def test_post_bulk_get_as_mixed_value_error(self): # Set up mock url = preprocess_url('/testString/_bulk_get') mock_response = 'This is a mock binary response.' - responses.add(responses.POST, - url, - body=mock_response, - content_type='multipart/mixed', - status=200) + responses.add( + responses.POST, + url, + body=mock_response, + content_type='multipart/mixed', + status=200, + ) # Construct a dict representation of a BulkGetQueryDocument model bulk_get_query_document_model = {} bulk_get_query_document_model['atts_since'] = ['1-99b02e08da151943c2dcb40090160bb8'] - bulk_get_query_document_model['id'] = 'testString' - bulk_get_query_document_model['rev'] = 'testString' + bulk_get_query_document_model['id'] = 'order00067' + bulk_get_query_document_model['rev'] = '3-917fa2381192822767f010b95b45325b' # Set up parameter values db = 'testString' @@ -2821,7 +2957,7 @@ def test_post_bulk_get_as_mixed_value_error(self): "docs": docs, } for param in req_param_dict.keys(): - req_copy = {key:val if key is not param else None for (key,val) in req_param_dict.items()} + req_copy = {key: val if key is not param else None for (key, val) in req_param_dict.items()} with pytest.raises(ValueError): _service.post_bulk_get_as_mixed(**req_copy) @@ -2834,7 +2970,8 @@ def test_post_bulk_get_as_mixed_value_error_with_retries(self): _service.disable_retries() self.test_post_bulk_get_as_mixed_value_error() -class TestPostBulkGetAsRelated(): + +class TestPostBulkGetAsRelated: """ Test Class for post_bulk_get_as_related """ @@ -2847,17 +2984,19 @@ def test_post_bulk_get_as_related_all_params(self): # Set up mock url = preprocess_url('/testString/_bulk_get') mock_response = 'This is a mock binary response.' - responses.add(responses.POST, - url, - body=mock_response, - content_type='multipart/related', - status=200) + responses.add( + responses.POST, + url, + body=mock_response, + content_type='multipart/related', + status=200, + ) # Construct a dict representation of a BulkGetQueryDocument model bulk_get_query_document_model = {} bulk_get_query_document_model['atts_since'] = ['1-99b02e08da151943c2dcb40090160bb8'] - bulk_get_query_document_model['id'] = 'testString' - bulk_get_query_document_model['rev'] = 'testString' + bulk_get_query_document_model['id'] = 'order00067' + bulk_get_query_document_model['rev'] = '3-917fa2381192822767f010b95b45325b' # Set up parameter values db = 'testString' @@ -2875,21 +3014,22 @@ def test_post_bulk_get_as_related_all_params(self): att_encoding_info=att_encoding_info, latest=latest, revs=revs, - headers={} + headers={}, ) # Check for correct operation assert len(responses.calls) == 1 assert response.status_code == 200 # Validate query params - query_string = responses.calls[0].request.url.split('?',1)[1] + query_string = responses.calls[0].request.url.split('?', 1)[1] query_string = urllib.parse.unquote_plus(query_string) assert 'attachments={}'.format('true' if attachments else 'false') in query_string assert 'att_encoding_info={}'.format('true' if att_encoding_info else 'false') in query_string assert 'latest={}'.format('true' if latest else 'false') in query_string assert 'revs={}'.format('true' if revs else 'false') in query_string - # decompress gzip compressed request body - responses.calls[0].request.body = gzip.decompress(responses.calls[0].request.body) + # Decompress gzip compressed request body. + raw_body = responses.calls[0].request.body + responses.calls[0].request.body = gzip.decompress(raw_body.read() if isinstance(raw_body, io.IOBase) else raw_body) # Validate body params req_body = json.loads(str(responses.calls[0].request.body, 'utf-8')) @@ -2912,17 +3052,19 @@ def test_post_bulk_get_as_related_required_params(self): # Set up mock url = preprocess_url('/testString/_bulk_get') mock_response = 'This is a mock binary response.' - responses.add(responses.POST, - url, - body=mock_response, - content_type='multipart/related', - status=200) + responses.add( + responses.POST, + url, + body=mock_response, + content_type='multipart/related', + status=200, + ) # Construct a dict representation of a BulkGetQueryDocument model bulk_get_query_document_model = {} bulk_get_query_document_model['atts_since'] = ['1-99b02e08da151943c2dcb40090160bb8'] - bulk_get_query_document_model['id'] = 'testString' - bulk_get_query_document_model['rev'] = 'testString' + bulk_get_query_document_model['id'] = 'order00067' + bulk_get_query_document_model['rev'] = '3-917fa2381192822767f010b95b45325b' # Set up parameter values db = 'testString' @@ -2932,14 +3074,15 @@ def test_post_bulk_get_as_related_required_params(self): response = _service.post_bulk_get_as_related( db, docs, - headers={} + headers={}, ) # Check for correct operation assert len(responses.calls) == 1 assert response.status_code == 200 - # decompress gzip compressed request body - responses.calls[0].request.body = gzip.decompress(responses.calls[0].request.body) + # Decompress gzip compressed request body. + raw_body = responses.calls[0].request.body + responses.calls[0].request.body = gzip.decompress(raw_body.read() if isinstance(raw_body, io.IOBase) else raw_body) # Validate body params req_body = json.loads(str(responses.calls[0].request.body, 'utf-8')) @@ -2962,17 +3105,19 @@ def test_post_bulk_get_as_related_value_error(self): # Set up mock url = preprocess_url('/testString/_bulk_get') mock_response = 'This is a mock binary response.' - responses.add(responses.POST, - url, - body=mock_response, - content_type='multipart/related', - status=200) + responses.add( + responses.POST, + url, + body=mock_response, + content_type='multipart/related', + status=200, + ) # Construct a dict representation of a BulkGetQueryDocument model bulk_get_query_document_model = {} bulk_get_query_document_model['atts_since'] = ['1-99b02e08da151943c2dcb40090160bb8'] - bulk_get_query_document_model['id'] = 'testString' - bulk_get_query_document_model['rev'] = 'testString' + bulk_get_query_document_model['id'] = 'order00067' + bulk_get_query_document_model['rev'] = '3-917fa2381192822767f010b95b45325b' # Set up parameter values db = 'testString' @@ -2984,7 +3129,7 @@ def test_post_bulk_get_as_related_value_error(self): "docs": docs, } for param in req_param_dict.keys(): - req_copy = {key:val if key is not param else None for (key,val) in req_param_dict.items()} + req_copy = {key: val if key is not param else None for (key, val) in req_param_dict.items()} with pytest.raises(ValueError): _service.post_bulk_get_as_related(**req_copy) @@ -2997,7 +3142,8 @@ def test_post_bulk_get_as_related_value_error_with_retries(self): _service.disable_retries() self.test_post_bulk_get_as_related_value_error() -class TestPostBulkGetAsStream(): + +class TestPostBulkGetAsStream: """ Test Class for post_bulk_get_as_stream """ @@ -3010,17 +3156,19 @@ def test_post_bulk_get_as_stream_all_params(self): # Set up mock url = preprocess_url('/testString/_bulk_get') mock_response = '{"foo": "this is a mock response for JSON streaming"}' - responses.add(responses.POST, - url, - body=mock_response, - content_type='application/json', - status=200) + responses.add( + responses.POST, + url, + body=mock_response, + content_type='application/json', + status=200, + ) # Construct a dict representation of a BulkGetQueryDocument model bulk_get_query_document_model = {} bulk_get_query_document_model['atts_since'] = ['1-99b02e08da151943c2dcb40090160bb8'] - bulk_get_query_document_model['id'] = 'testString' - bulk_get_query_document_model['rev'] = 'testString' + bulk_get_query_document_model['id'] = 'order00067' + bulk_get_query_document_model['rev'] = '3-917fa2381192822767f010b95b45325b' # Set up parameter values db = 'testString' @@ -3038,21 +3186,22 @@ def test_post_bulk_get_as_stream_all_params(self): att_encoding_info=att_encoding_info, latest=latest, revs=revs, - headers={} + headers={}, ) # Check for correct operation assert len(responses.calls) == 1 assert response.status_code == 200 # Validate query params - query_string = responses.calls[0].request.url.split('?',1)[1] + query_string = responses.calls[0].request.url.split('?', 1)[1] query_string = urllib.parse.unquote_plus(query_string) assert 'attachments={}'.format('true' if attachments else 'false') in query_string assert 'att_encoding_info={}'.format('true' if att_encoding_info else 'false') in query_string assert 'latest={}'.format('true' if latest else 'false') in query_string assert 'revs={}'.format('true' if revs else 'false') in query_string - # decompress gzip compressed request body - responses.calls[0].request.body = gzip.decompress(responses.calls[0].request.body) + # Decompress gzip compressed request body. + raw_body = responses.calls[0].request.body + responses.calls[0].request.body = gzip.decompress(raw_body.read() if isinstance(raw_body, io.IOBase) else raw_body) # Validate body params req_body = json.loads(str(responses.calls[0].request.body, 'utf-8')) @@ -3081,17 +3230,19 @@ def test_post_bulk_get_as_stream_required_params(self): # Set up mock url = preprocess_url('/testString/_bulk_get') mock_response = '{"foo": "this is a mock response for JSON streaming"}' - responses.add(responses.POST, - url, - body=mock_response, - content_type='application/json', - status=200) + responses.add( + responses.POST, + url, + body=mock_response, + content_type='application/json', + status=200, + ) # Construct a dict representation of a BulkGetQueryDocument model bulk_get_query_document_model = {} bulk_get_query_document_model['atts_since'] = ['1-99b02e08da151943c2dcb40090160bb8'] - bulk_get_query_document_model['id'] = 'testString' - bulk_get_query_document_model['rev'] = 'testString' + bulk_get_query_document_model['id'] = 'order00067' + bulk_get_query_document_model['rev'] = '3-917fa2381192822767f010b95b45325b' # Set up parameter values db = 'testString' @@ -3101,14 +3252,15 @@ def test_post_bulk_get_as_stream_required_params(self): response = _service.post_bulk_get_as_stream( db, docs, - headers={} + headers={}, ) # Check for correct operation assert len(responses.calls) == 1 assert response.status_code == 200 - # decompress gzip compressed request body - responses.calls[0].request.body = gzip.decompress(responses.calls[0].request.body) + # Decompress gzip compressed request body. + raw_body = responses.calls[0].request.body + responses.calls[0].request.body = gzip.decompress(raw_body.read() if isinstance(raw_body, io.IOBase) else raw_body) # Validate body params req_body = json.loads(str(responses.calls[0].request.body, 'utf-8')) @@ -3137,17 +3289,19 @@ def test_post_bulk_get_as_stream_value_error(self): # Set up mock url = preprocess_url('/testString/_bulk_get') mock_response = '{"foo": "this is a mock response for JSON streaming"}' - responses.add(responses.POST, - url, - body=mock_response, - content_type='application/json', - status=200) + responses.add( + responses.POST, + url, + body=mock_response, + content_type='application/json', + status=200, + ) # Construct a dict representation of a BulkGetQueryDocument model bulk_get_query_document_model = {} bulk_get_query_document_model['atts_since'] = ['1-99b02e08da151943c2dcb40090160bb8'] - bulk_get_query_document_model['id'] = 'testString' - bulk_get_query_document_model['rev'] = 'testString' + bulk_get_query_document_model['id'] = 'order00067' + bulk_get_query_document_model['rev'] = '3-917fa2381192822767f010b95b45325b' # Set up parameter values db = 'testString' @@ -3159,7 +3313,7 @@ def test_post_bulk_get_as_stream_value_error(self): "docs": docs, } for param in req_param_dict.keys(): - req_copy = {key:val if key is not param else None for (key,val) in req_param_dict.items()} + req_copy = {key: val if key is not param else None for (key, val) in req_param_dict.items()} with pytest.raises(ValueError): _service.post_bulk_get_as_stream(**req_copy) @@ -3172,7 +3326,8 @@ def test_post_bulk_get_as_stream_value_error_with_retries(self): _service.disable_retries() self.test_post_bulk_get_as_stream_value_error() -class TestDeleteDocument(): + +class TestDeleteDocument: """ Test Class for delete_document """ @@ -3184,12 +3339,14 @@ def test_delete_document_all_params(self): """ # Set up mock url = preprocess_url('/testString/testString') - mock_response = '{"id": "id", "rev": "rev", "ok": true, "caused_by": "caused_by", "error": "error", "reason": "reason"}' - responses.add(responses.DELETE, - url, - body=mock_response, - content_type='application/json', - status=200) + mock_response = '{"id": "id", "rev": "rev", "ok": true, "caused_by": "caused_by", "error": "error", "reason": "reason", "ref": 0}' + responses.add( + responses.DELETE, + url, + body=mock_response, + content_type='application/json', + status=200, + ) # Set up parameter values db = 'testString' @@ -3205,14 +3362,14 @@ def test_delete_document_all_params(self): if_match=if_match, batch=batch, rev=rev, - headers={} + headers={}, ) # Check for correct operation assert len(responses.calls) == 1 assert response.status_code == 200 # Validate query params - query_string = responses.calls[0].request.url.split('?',1)[1] + query_string = responses.calls[0].request.url.split('?', 1)[1] query_string = urllib.parse.unquote_plus(query_string) assert 'batch={}'.format(batch) in query_string assert 'rev={}'.format(rev) in query_string @@ -3233,12 +3390,14 @@ def test_delete_document_required_params(self): """ # Set up mock url = preprocess_url('/testString/testString') - mock_response = '{"id": "id", "rev": "rev", "ok": true, "caused_by": "caused_by", "error": "error", "reason": "reason"}' - responses.add(responses.DELETE, - url, - body=mock_response, - content_type='application/json', - status=200) + mock_response = '{"id": "id", "rev": "rev", "ok": true, "caused_by": "caused_by", "error": "error", "reason": "reason", "ref": 0}' + responses.add( + responses.DELETE, + url, + body=mock_response, + content_type='application/json', + status=200, + ) # Set up parameter values db = 'testString' @@ -3248,7 +3407,7 @@ def test_delete_document_required_params(self): response = _service.delete_document( db, doc_id, - headers={} + headers={}, ) # Check for correct operation @@ -3271,12 +3430,14 @@ def test_delete_document_value_error(self): """ # Set up mock url = preprocess_url('/testString/testString') - mock_response = '{"id": "id", "rev": "rev", "ok": true, "caused_by": "caused_by", "error": "error", "reason": "reason"}' - responses.add(responses.DELETE, - url, - body=mock_response, - content_type='application/json', - status=200) + mock_response = '{"id": "id", "rev": "rev", "ok": true, "caused_by": "caused_by", "error": "error", "reason": "reason", "ref": 0}' + responses.add( + responses.DELETE, + url, + body=mock_response, + content_type='application/json', + status=200, + ) # Set up parameter values db = 'testString' @@ -3288,7 +3449,7 @@ def test_delete_document_value_error(self): "doc_id": doc_id, } for param in req_param_dict.keys(): - req_copy = {key:val if key is not param else None for (key,val) in req_param_dict.items()} + req_copy = {key: val if key is not param else None for (key, val) in req_param_dict.items()} with pytest.raises(ValueError): _service.delete_document(**req_copy) @@ -3301,7 +3462,8 @@ def test_delete_document_value_error_with_retries(self): _service.disable_retries() self.test_delete_document_value_error() -class TestGetDocument(): + +class TestGetDocument: """ Test Class for get_document """ @@ -3313,12 +3475,14 @@ def test_get_document_all_params(self): """ # Set up mock url = preprocess_url('/testString/testString') - mock_response = '{"_attachments": {"mapKey": {"content_type": "content_type", "data": "VGhpcyBpcyBhbiBlbmNvZGVkIGJ5dGUgYXJyYXku", "digest": "digest", "encoded_length": 0, "encoding": "encoding", "follows": false, "length": 0, "revpos": 1, "stub": true}}, "_conflicts": ["conflicts"], "_deleted": false, "_deleted_conflicts": ["deleted_conflicts"], "_id": "id", "_local_seq": "local_seq", "_rev": "rev", "_revisions": {"ids": ["ids"], "start": 1}, "_revs_info": [{"rev": "rev", "status": "available"}]}' - responses.add(responses.GET, - url, - body=mock_response, - content_type='application/json', - status=200) + mock_response = '{"_attachments": {"mapKey": {"content_type": "content_type", "data": "VGhpcyBpcyBhIG1vY2sgYnl0ZSBhcnJheSB2YWx1ZS4=", "digest": "digest", "encoded_length": 0, "encoding": "encoding", "follows": false, "length": 0, "revpos": 1, "stub": true}}, "_conflicts": ["conflicts"], "_deleted": true, "_deleted_conflicts": ["deleted_conflicts"], "_id": "_id", "_local_seq": "_local_seq", "_rev": "_rev", "_revisions": {"ids": ["ids"], "start": 1}, "_revs_info": [{"rev": "rev", "status": "available"}]}' + responses.add( + responses.GET, + url, + body=mock_response, + content_type='application/json', + status=200, + ) # Set up parameter values db = 'testString' @@ -3350,14 +3514,14 @@ def test_get_document_all_params(self): rev=rev, revs=revs, revs_info=revs_info, - headers={} + headers={}, ) # Check for correct operation assert len(responses.calls) == 1 assert response.status_code == 200 # Validate query params - query_string = responses.calls[0].request.url.split('?',1)[1] + query_string = responses.calls[0].request.url.split('?', 1)[1] query_string = urllib.parse.unquote_plus(query_string) assert 'attachments={}'.format('true' if attachments else 'false') in query_string assert 'att_encoding_info={}'.format('true' if att_encoding_info else 'false') in query_string @@ -3386,12 +3550,14 @@ def test_get_document_required_params(self): """ # Set up mock url = preprocess_url('/testString/testString') - mock_response = '{"_attachments": {"mapKey": {"content_type": "content_type", "data": "VGhpcyBpcyBhbiBlbmNvZGVkIGJ5dGUgYXJyYXku", "digest": "digest", "encoded_length": 0, "encoding": "encoding", "follows": false, "length": 0, "revpos": 1, "stub": true}}, "_conflicts": ["conflicts"], "_deleted": false, "_deleted_conflicts": ["deleted_conflicts"], "_id": "id", "_local_seq": "local_seq", "_rev": "rev", "_revisions": {"ids": ["ids"], "start": 1}, "_revs_info": [{"rev": "rev", "status": "available"}]}' - responses.add(responses.GET, - url, - body=mock_response, - content_type='application/json', - status=200) + mock_response = '{"_attachments": {"mapKey": {"content_type": "content_type", "data": "VGhpcyBpcyBhIG1vY2sgYnl0ZSBhcnJheSB2YWx1ZS4=", "digest": "digest", "encoded_length": 0, "encoding": "encoding", "follows": false, "length": 0, "revpos": 1, "stub": true}}, "_conflicts": ["conflicts"], "_deleted": true, "_deleted_conflicts": ["deleted_conflicts"], "_id": "_id", "_local_seq": "_local_seq", "_rev": "_rev", "_revisions": {"ids": ["ids"], "start": 1}, "_revs_info": [{"rev": "rev", "status": "available"}]}' + responses.add( + responses.GET, + url, + body=mock_response, + content_type='application/json', + status=200, + ) # Set up parameter values db = 'testString' @@ -3401,7 +3567,7 @@ def test_get_document_required_params(self): response = _service.get_document( db, doc_id, - headers={} + headers={}, ) # Check for correct operation @@ -3424,12 +3590,14 @@ def test_get_document_value_error(self): """ # Set up mock url = preprocess_url('/testString/testString') - mock_response = '{"_attachments": {"mapKey": {"content_type": "content_type", "data": "VGhpcyBpcyBhbiBlbmNvZGVkIGJ5dGUgYXJyYXku", "digest": "digest", "encoded_length": 0, "encoding": "encoding", "follows": false, "length": 0, "revpos": 1, "stub": true}}, "_conflicts": ["conflicts"], "_deleted": false, "_deleted_conflicts": ["deleted_conflicts"], "_id": "id", "_local_seq": "local_seq", "_rev": "rev", "_revisions": {"ids": ["ids"], "start": 1}, "_revs_info": [{"rev": "rev", "status": "available"}]}' - responses.add(responses.GET, - url, - body=mock_response, - content_type='application/json', - status=200) + mock_response = '{"_attachments": {"mapKey": {"content_type": "content_type", "data": "VGhpcyBpcyBhIG1vY2sgYnl0ZSBhcnJheSB2YWx1ZS4=", "digest": "digest", "encoded_length": 0, "encoding": "encoding", "follows": false, "length": 0, "revpos": 1, "stub": true}}, "_conflicts": ["conflicts"], "_deleted": true, "_deleted_conflicts": ["deleted_conflicts"], "_id": "_id", "_local_seq": "_local_seq", "_rev": "_rev", "_revisions": {"ids": ["ids"], "start": 1}, "_revs_info": [{"rev": "rev", "status": "available"}]}' + responses.add( + responses.GET, + url, + body=mock_response, + content_type='application/json', + status=200, + ) # Set up parameter values db = 'testString' @@ -3441,7 +3609,7 @@ def test_get_document_value_error(self): "doc_id": doc_id, } for param in req_param_dict.keys(): - req_copy = {key:val if key is not param else None for (key,val) in req_param_dict.items()} + req_copy = {key: val if key is not param else None for (key, val) in req_param_dict.items()} with pytest.raises(ValueError): _service.get_document(**req_copy) @@ -3454,7 +3622,8 @@ def test_get_document_value_error_with_retries(self): _service.disable_retries() self.test_get_document_value_error() -class TestGetDocumentAsMixed(): + +class TestGetDocumentAsMixed: """ Test Class for get_document_as_mixed """ @@ -3467,11 +3636,13 @@ def test_get_document_as_mixed_all_params(self): # Set up mock url = preprocess_url('/testString/testString') mock_response = 'This is a mock binary response.' - responses.add(responses.GET, - url, - body=mock_response, - content_type='multipart/mixed', - status=200) + responses.add( + responses.GET, + url, + body=mock_response, + content_type='multipart/mixed', + status=200, + ) # Set up parameter values db = 'testString' @@ -3503,14 +3674,14 @@ def test_get_document_as_mixed_all_params(self): rev=rev, revs=revs, revs_info=revs_info, - headers={} + headers={}, ) # Check for correct operation assert len(responses.calls) == 1 assert response.status_code == 200 # Validate query params - query_string = responses.calls[0].request.url.split('?',1)[1] + query_string = responses.calls[0].request.url.split('?', 1)[1] query_string = urllib.parse.unquote_plus(query_string) assert 'attachments={}'.format('true' if attachments else 'false') in query_string assert 'att_encoding_info={}'.format('true' if att_encoding_info else 'false') in query_string @@ -3540,11 +3711,13 @@ def test_get_document_as_mixed_required_params(self): # Set up mock url = preprocess_url('/testString/testString') mock_response = 'This is a mock binary response.' - responses.add(responses.GET, - url, - body=mock_response, - content_type='multipart/mixed', - status=200) + responses.add( + responses.GET, + url, + body=mock_response, + content_type='multipart/mixed', + status=200, + ) # Set up parameter values db = 'testString' @@ -3554,7 +3727,7 @@ def test_get_document_as_mixed_required_params(self): response = _service.get_document_as_mixed( db, doc_id, - headers={} + headers={}, ) # Check for correct operation @@ -3578,11 +3751,13 @@ def test_get_document_as_mixed_value_error(self): # Set up mock url = preprocess_url('/testString/testString') mock_response = 'This is a mock binary response.' - responses.add(responses.GET, - url, - body=mock_response, - content_type='multipart/mixed', - status=200) + responses.add( + responses.GET, + url, + body=mock_response, + content_type='multipart/mixed', + status=200, + ) # Set up parameter values db = 'testString' @@ -3594,7 +3769,7 @@ def test_get_document_as_mixed_value_error(self): "doc_id": doc_id, } for param in req_param_dict.keys(): - req_copy = {key:val if key is not param else None for (key,val) in req_param_dict.items()} + req_copy = {key: val if key is not param else None for (key, val) in req_param_dict.items()} with pytest.raises(ValueError): _service.get_document_as_mixed(**req_copy) @@ -3607,7 +3782,8 @@ def test_get_document_as_mixed_value_error_with_retries(self): _service.disable_retries() self.test_get_document_as_mixed_value_error() -class TestGetDocumentAsRelated(): + +class TestGetDocumentAsRelated: """ Test Class for get_document_as_related """ @@ -3620,11 +3796,13 @@ def test_get_document_as_related_all_params(self): # Set up mock url = preprocess_url('/testString/testString') mock_response = 'This is a mock binary response.' - responses.add(responses.GET, - url, - body=mock_response, - content_type='multipart/related', - status=200) + responses.add( + responses.GET, + url, + body=mock_response, + content_type='multipart/related', + status=200, + ) # Set up parameter values db = 'testString' @@ -3656,14 +3834,14 @@ def test_get_document_as_related_all_params(self): rev=rev, revs=revs, revs_info=revs_info, - headers={} + headers={}, ) # Check for correct operation assert len(responses.calls) == 1 assert response.status_code == 200 # Validate query params - query_string = responses.calls[0].request.url.split('?',1)[1] + query_string = responses.calls[0].request.url.split('?', 1)[1] query_string = urllib.parse.unquote_plus(query_string) assert 'attachments={}'.format('true' if attachments else 'false') in query_string assert 'att_encoding_info={}'.format('true' if att_encoding_info else 'false') in query_string @@ -3693,11 +3871,13 @@ def test_get_document_as_related_required_params(self): # Set up mock url = preprocess_url('/testString/testString') mock_response = 'This is a mock binary response.' - responses.add(responses.GET, - url, - body=mock_response, - content_type='multipart/related', - status=200) + responses.add( + responses.GET, + url, + body=mock_response, + content_type='multipart/related', + status=200, + ) # Set up parameter values db = 'testString' @@ -3707,7 +3887,7 @@ def test_get_document_as_related_required_params(self): response = _service.get_document_as_related( db, doc_id, - headers={} + headers={}, ) # Check for correct operation @@ -3731,11 +3911,13 @@ def test_get_document_as_related_value_error(self): # Set up mock url = preprocess_url('/testString/testString') mock_response = 'This is a mock binary response.' - responses.add(responses.GET, - url, - body=mock_response, - content_type='multipart/related', - status=200) + responses.add( + responses.GET, + url, + body=mock_response, + content_type='multipart/related', + status=200, + ) # Set up parameter values db = 'testString' @@ -3747,7 +3929,7 @@ def test_get_document_as_related_value_error(self): "doc_id": doc_id, } for param in req_param_dict.keys(): - req_copy = {key:val if key is not param else None for (key,val) in req_param_dict.items()} + req_copy = {key: val if key is not param else None for (key, val) in req_param_dict.items()} with pytest.raises(ValueError): _service.get_document_as_related(**req_copy) @@ -3760,7 +3942,8 @@ def test_get_document_as_related_value_error_with_retries(self): _service.disable_retries() self.test_get_document_as_related_value_error() -class TestGetDocumentAsStream(): + +class TestGetDocumentAsStream: """ Test Class for get_document_as_stream """ @@ -3773,11 +3956,13 @@ def test_get_document_as_stream_all_params(self): # Set up mock url = preprocess_url('/testString/testString') mock_response = '{"foo": "this is a mock response for JSON streaming"}' - responses.add(responses.GET, - url, - body=mock_response, - content_type='application/json', - status=200) + responses.add( + responses.GET, + url, + body=mock_response, + content_type='application/json', + status=200, + ) # Set up parameter values db = 'testString' @@ -3809,14 +3994,14 @@ def test_get_document_as_stream_all_params(self): rev=rev, revs=revs, revs_info=revs_info, - headers={} + headers={}, ) # Check for correct operation assert len(responses.calls) == 1 assert response.status_code == 200 # Validate query params - query_string = responses.calls[0].request.url.split('?',1)[1] + query_string = responses.calls[0].request.url.split('?', 1)[1] query_string = urllib.parse.unquote_plus(query_string) assert 'attachments={}'.format('true' if attachments else 'false') in query_string assert 'att_encoding_info={}'.format('true' if att_encoding_info else 'false') in query_string @@ -3852,11 +4037,13 @@ def test_get_document_as_stream_required_params(self): # Set up mock url = preprocess_url('/testString/testString') mock_response = '{"foo": "this is a mock response for JSON streaming"}' - responses.add(responses.GET, - url, - body=mock_response, - content_type='application/json', - status=200) + responses.add( + responses.GET, + url, + body=mock_response, + content_type='application/json', + status=200, + ) # Set up parameter values db = 'testString' @@ -3866,7 +4053,7 @@ def test_get_document_as_stream_required_params(self): response = _service.get_document_as_stream( db, doc_id, - headers={} + headers={}, ) # Check for correct operation @@ -3896,11 +4083,13 @@ def test_get_document_as_stream_value_error(self): # Set up mock url = preprocess_url('/testString/testString') mock_response = '{"foo": "this is a mock response for JSON streaming"}' - responses.add(responses.GET, - url, - body=mock_response, - content_type='application/json', - status=200) + responses.add( + responses.GET, + url, + body=mock_response, + content_type='application/json', + status=200, + ) # Set up parameter values db = 'testString' @@ -3912,7 +4101,7 @@ def test_get_document_as_stream_value_error(self): "doc_id": doc_id, } for param in req_param_dict.keys(): - req_copy = {key:val if key is not param else None for (key,val) in req_param_dict.items()} + req_copy = {key: val if key is not param else None for (key, val) in req_param_dict.items()} with pytest.raises(ValueError): _service.get_document_as_stream(**req_copy) @@ -3925,7 +4114,8 @@ def test_get_document_as_stream_value_error_with_retries(self): _service.disable_retries() self.test_get_document_as_stream_value_error() -class TestPutDocument(): + +class TestPutDocument: """ Test Class for put_document """ @@ -3937,12 +4127,14 @@ def test_put_document_all_params(self): """ # Set up mock url = preprocess_url('/testString/testString') - mock_response = '{"id": "id", "rev": "rev", "ok": true, "caused_by": "caused_by", "error": "error", "reason": "reason"}' - responses.add(responses.PUT, - url, - body=mock_response, - content_type='application/json', - status=201) + mock_response = '{"id": "id", "rev": "rev", "ok": true, "caused_by": "caused_by", "error": "error", "reason": "reason", "ref": 0}' + responses.add( + responses.PUT, + url, + body=mock_response, + content_type='application/json', + status=201, + ) # Construct a dict representation of a Attachment model attachment_model = {} @@ -3972,12 +4164,21 @@ def test_put_document_all_params(self): document_model['_conflicts'] = ['testString'] document_model['_deleted'] = True document_model['_deleted_conflicts'] = ['testString'] - document_model['_id'] = 'testString' + document_model['_id'] = 'exampleid' document_model['_local_seq'] = 'testString' document_model['_rev'] = 'testString' document_model['_revisions'] = revisions_model document_model['_revs_info'] = [document_revision_status_model] - document_model['foo'] = 'testString' + document_model['brand'] = 'Foo' + document_model['colours'] = '["red","green","black","blue"]' + document_model['description'] = 'Slim Colourful Design Electronic Cooking Appliance for ...' + document_model['image'] = 'assets/img/0gmsnghhew.jpg' + document_model['keywords'] = '["Foo","Scales","Weight","Digital","Kitchen"]' + document_model['name'] = 'Digital Kitchen Scales' + document_model['price'] = '14.99' + document_model['productId'] = '1000042' + document_model['taxonomy'] = '["Home","Kitchen","Small Appliances"]' + document_model['type'] = 'product' # Set up parameter values db = 'testString' @@ -3999,20 +4200,21 @@ def test_put_document_all_params(self): batch=batch, new_edits=new_edits, rev=rev, - headers={} + headers={}, ) # Check for correct operation assert len(responses.calls) == 1 assert response.status_code == 201 # Validate query params - query_string = responses.calls[0].request.url.split('?',1)[1] + query_string = responses.calls[0].request.url.split('?', 1)[1] query_string = urllib.parse.unquote_plus(query_string) assert 'batch={}'.format(batch) in query_string assert 'new_edits={}'.format('true' if new_edits else 'false') in query_string assert 'rev={}'.format(rev) in query_string - # decompress gzip compressed request body - responses.calls[0].request.body = gzip.decompress(responses.calls[0].request.body) + # Decompress gzip compressed request body. + raw_body = responses.calls[0].request.body + responses.calls[0].request.body = gzip.decompress(raw_body.read() if isinstance(raw_body, io.IOBase) else raw_body) # Validate body params @@ -4032,12 +4234,14 @@ def test_put_document_required_params(self): """ # Set up mock url = preprocess_url('/testString/testString') - mock_response = '{"id": "id", "rev": "rev", "ok": true, "caused_by": "caused_by", "error": "error", "reason": "reason"}' - responses.add(responses.PUT, - url, - body=mock_response, - content_type='application/json', - status=201) + mock_response = '{"id": "id", "rev": "rev", "ok": true, "caused_by": "caused_by", "error": "error", "reason": "reason", "ref": 0}' + responses.add( + responses.PUT, + url, + body=mock_response, + content_type='application/json', + status=201, + ) # Construct a dict representation of a Attachment model attachment_model = {} @@ -4067,12 +4271,21 @@ def test_put_document_required_params(self): document_model['_conflicts'] = ['testString'] document_model['_deleted'] = True document_model['_deleted_conflicts'] = ['testString'] - document_model['_id'] = 'testString' + document_model['_id'] = 'exampleid' document_model['_local_seq'] = 'testString' document_model['_rev'] = 'testString' document_model['_revisions'] = revisions_model document_model['_revs_info'] = [document_revision_status_model] - document_model['foo'] = 'testString' + document_model['brand'] = 'Foo' + document_model['colours'] = '["red","green","black","blue"]' + document_model['description'] = 'Slim Colourful Design Electronic Cooking Appliance for ...' + document_model['image'] = 'assets/img/0gmsnghhew.jpg' + document_model['keywords'] = '["Foo","Scales","Weight","Digital","Kitchen"]' + document_model['name'] = 'Digital Kitchen Scales' + document_model['price'] = '14.99' + document_model['productId'] = '1000042' + document_model['taxonomy'] = '["Home","Kitchen","Small Appliances"]' + document_model['type'] = 'product' # Set up parameter values db = 'testString' @@ -4084,14 +4297,15 @@ def test_put_document_required_params(self): db, doc_id, document, - headers={} + headers={}, ) # Check for correct operation assert len(responses.calls) == 1 assert response.status_code == 201 - # decompress gzip compressed request body - responses.calls[0].request.body = gzip.decompress(responses.calls[0].request.body) + # Decompress gzip compressed request body. + raw_body = responses.calls[0].request.body + responses.calls[0].request.body = gzip.decompress(raw_body.read() if isinstance(raw_body, io.IOBase) else raw_body) # Validate body params @@ -4111,12 +4325,14 @@ def test_put_document_value_error(self): """ # Set up mock url = preprocess_url('/testString/testString') - mock_response = '{"id": "id", "rev": "rev", "ok": true, "caused_by": "caused_by", "error": "error", "reason": "reason"}' - responses.add(responses.PUT, - url, - body=mock_response, - content_type='application/json', - status=201) + mock_response = '{"id": "id", "rev": "rev", "ok": true, "caused_by": "caused_by", "error": "error", "reason": "reason", "ref": 0}' + responses.add( + responses.PUT, + url, + body=mock_response, + content_type='application/json', + status=201, + ) # Construct a dict representation of a Attachment model attachment_model = {} @@ -4146,12 +4362,21 @@ def test_put_document_value_error(self): document_model['_conflicts'] = ['testString'] document_model['_deleted'] = True document_model['_deleted_conflicts'] = ['testString'] - document_model['_id'] = 'testString' + document_model['_id'] = 'exampleid' document_model['_local_seq'] = 'testString' document_model['_rev'] = 'testString' document_model['_revisions'] = revisions_model document_model['_revs_info'] = [document_revision_status_model] - document_model['foo'] = 'testString' + document_model['brand'] = 'Foo' + document_model['colours'] = '["red","green","black","blue"]' + document_model['description'] = 'Slim Colourful Design Electronic Cooking Appliance for ...' + document_model['image'] = 'assets/img/0gmsnghhew.jpg' + document_model['keywords'] = '["Foo","Scales","Weight","Digital","Kitchen"]' + document_model['name'] = 'Digital Kitchen Scales' + document_model['price'] = '14.99' + document_model['productId'] = '1000042' + document_model['taxonomy'] = '["Home","Kitchen","Small Appliances"]' + document_model['type'] = 'product' # Set up parameter values db = 'testString' @@ -4165,7 +4390,7 @@ def test_put_document_value_error(self): "document": document, } for param in req_param_dict.keys(): - req_copy = {key:val if key is not param else None for (key,val) in req_param_dict.items()} + req_copy = {key: val if key is not param else None for (key, val) in req_param_dict.items()} with pytest.raises(ValueError): _service.put_document(**req_copy) @@ -4178,6 +4403,7 @@ def test_put_document_value_error_with_retries(self): _service.disable_retries() self.test_put_document_value_error() + # endregion ############################################################################## # End of Service: Documents @@ -4188,7 +4414,8 @@ def test_put_document_value_error_with_retries(self): ############################################################################## # region -class TestNewInstance(): + +class TestNewInstance: """ Test Class for new_instance """ @@ -4215,7 +4442,8 @@ def test_new_instance_without_authenticator(self): service_name='TEST_SERVICE_NOT_FOUND', ) -class TestHeadDesignDocument(): + +class TestHeadDesignDocument: """ Test Class for head_design_document """ @@ -4227,9 +4455,11 @@ def test_head_design_document_all_params(self): """ # Set up mock url = preprocess_url('/testString/_design/testString') - responses.add(responses.HEAD, - url, - status=200) + responses.add( + responses.HEAD, + url, + status=200, + ) # Set up parameter values db = 'testString' @@ -4241,7 +4471,7 @@ def test_head_design_document_all_params(self): db, ddoc, if_none_match=if_none_match, - headers={} + headers={}, ) # Check for correct operation @@ -4264,9 +4494,11 @@ def test_head_design_document_required_params(self): """ # Set up mock url = preprocess_url('/testString/_design/testString') - responses.add(responses.HEAD, - url, - status=200) + responses.add( + responses.HEAD, + url, + status=200, + ) # Set up parameter values db = 'testString' @@ -4276,7 +4508,7 @@ def test_head_design_document_required_params(self): response = _service.head_design_document( db, ddoc, - headers={} + headers={}, ) # Check for correct operation @@ -4299,9 +4531,11 @@ def test_head_design_document_value_error(self): """ # Set up mock url = preprocess_url('/testString/_design/testString') - responses.add(responses.HEAD, - url, - status=200) + responses.add( + responses.HEAD, + url, + status=200, + ) # Set up parameter values db = 'testString' @@ -4313,7 +4547,7 @@ def test_head_design_document_value_error(self): "ddoc": ddoc, } for param in req_param_dict.keys(): - req_copy = {key:val if key is not param else None for (key,val) in req_param_dict.items()} + req_copy = {key: val if key is not param else None for (key, val) in req_param_dict.items()} with pytest.raises(ValueError): _service.head_design_document(**req_copy) @@ -4326,7 +4560,8 @@ def test_head_design_document_value_error_with_retries(self): _service.disable_retries() self.test_head_design_document_value_error() -class TestDeleteDesignDocument(): + +class TestDeleteDesignDocument: """ Test Class for delete_design_document """ @@ -4338,12 +4573,14 @@ def test_delete_design_document_all_params(self): """ # Set up mock url = preprocess_url('/testString/_design/testString') - mock_response = '{"id": "id", "rev": "rev", "ok": true, "caused_by": "caused_by", "error": "error", "reason": "reason"}' - responses.add(responses.DELETE, - url, - body=mock_response, - content_type='application/json', - status=200) + mock_response = '{"id": "id", "rev": "rev", "ok": true, "caused_by": "caused_by", "error": "error", "reason": "reason", "ref": 0}' + responses.add( + responses.DELETE, + url, + body=mock_response, + content_type='application/json', + status=200, + ) # Set up parameter values db = 'testString' @@ -4359,14 +4596,14 @@ def test_delete_design_document_all_params(self): if_match=if_match, batch=batch, rev=rev, - headers={} + headers={}, ) # Check for correct operation assert len(responses.calls) == 1 assert response.status_code == 200 # Validate query params - query_string = responses.calls[0].request.url.split('?',1)[1] + query_string = responses.calls[0].request.url.split('?', 1)[1] query_string = urllib.parse.unquote_plus(query_string) assert 'batch={}'.format(batch) in query_string assert 'rev={}'.format(rev) in query_string @@ -4387,12 +4624,14 @@ def test_delete_design_document_required_params(self): """ # Set up mock url = preprocess_url('/testString/_design/testString') - mock_response = '{"id": "id", "rev": "rev", "ok": true, "caused_by": "caused_by", "error": "error", "reason": "reason"}' - responses.add(responses.DELETE, - url, - body=mock_response, - content_type='application/json', - status=200) + mock_response = '{"id": "id", "rev": "rev", "ok": true, "caused_by": "caused_by", "error": "error", "reason": "reason", "ref": 0}' + responses.add( + responses.DELETE, + url, + body=mock_response, + content_type='application/json', + status=200, + ) # Set up parameter values db = 'testString' @@ -4402,7 +4641,7 @@ def test_delete_design_document_required_params(self): response = _service.delete_design_document( db, ddoc, - headers={} + headers={}, ) # Check for correct operation @@ -4425,12 +4664,14 @@ def test_delete_design_document_value_error(self): """ # Set up mock url = preprocess_url('/testString/_design/testString') - mock_response = '{"id": "id", "rev": "rev", "ok": true, "caused_by": "caused_by", "error": "error", "reason": "reason"}' - responses.add(responses.DELETE, - url, - body=mock_response, - content_type='application/json', - status=200) + mock_response = '{"id": "id", "rev": "rev", "ok": true, "caused_by": "caused_by", "error": "error", "reason": "reason", "ref": 0}' + responses.add( + responses.DELETE, + url, + body=mock_response, + content_type='application/json', + status=200, + ) # Set up parameter values db = 'testString' @@ -4442,7 +4683,7 @@ def test_delete_design_document_value_error(self): "ddoc": ddoc, } for param in req_param_dict.keys(): - req_copy = {key:val if key is not param else None for (key,val) in req_param_dict.items()} + req_copy = {key: val if key is not param else None for (key, val) in req_param_dict.items()} with pytest.raises(ValueError): _service.delete_design_document(**req_copy) @@ -4455,7 +4696,8 @@ def test_delete_design_document_value_error_with_retries(self): _service.disable_retries() self.test_delete_design_document_value_error() -class TestGetDesignDocument(): + +class TestGetDesignDocument: """ Test Class for get_design_document """ @@ -4467,12 +4709,14 @@ def test_get_design_document_all_params(self): """ # Set up mock url = preprocess_url('/testString/_design/testString') - mock_response = '{"_attachments": {"mapKey": {"content_type": "content_type", "data": "VGhpcyBpcyBhbiBlbmNvZGVkIGJ5dGUgYXJyYXku", "digest": "digest", "encoded_length": 0, "encoding": "encoding", "follows": false, "length": 0, "revpos": 1, "stub": true}}, "_conflicts": ["conflicts"], "_deleted": false, "_deleted_conflicts": ["deleted_conflicts"], "_id": "id", "_local_seq": "local_seq", "_rev": "rev", "_revisions": {"ids": ["ids"], "start": 1}, "_revs_info": [{"rev": "rev", "status": "available"}], "autoupdate": true, "filters": {"mapKey": "inner"}, "indexes": {"mapKey": {"analyzer": {"name": "classic", "stopwords": ["stopwords"], "fields": {"mapKey": {"name": "classic", "stopwords": ["stopwords"]}}}, "index": "index"}}, "language": "javascript", "options": {"partitioned": false}, "validate_doc_update": "validate_doc_update", "views": {"mapKey": {"map": "map", "reduce": "reduce"}}, "st_indexes": {"mapKey": {"index": "index"}}}' - responses.add(responses.GET, - url, - body=mock_response, - content_type='application/json', - status=200) + mock_response = '{"_attachments": {"mapKey": {"content_type": "content_type", "data": "VGhpcyBpcyBhIG1vY2sgYnl0ZSBhcnJheSB2YWx1ZS4=", "digest": "digest", "encoded_length": 0, "encoding": "encoding", "follows": false, "length": 0, "revpos": 1, "stub": true}}, "_conflicts": ["conflicts"], "_deleted": true, "_deleted_conflicts": ["deleted_conflicts"], "_id": "_id", "_local_seq": "_local_seq", "_rev": "_rev", "_revisions": {"ids": ["ids"], "start": 1}, "_revs_info": [{"rev": "rev", "status": "available"}], "autoupdate": true, "filters": {"mapKey": "inner"}, "indexes": {"mapKey": {"analyzer": {"name": "classic", "stopwords": ["stopwords"], "default": {"name": "classic", "stopwords": ["stopwords"]}, "fields": {"mapKey": {"name": "classic", "stopwords": ["stopwords"]}}}, "index": "index"}}, "language": "javascript", "options": {"partitioned": false}, "validate_doc_update": "validate_doc_update", "views": {"mapKey": {"map": "map", "reduce": "reduce"}}}' + responses.add( + responses.GET, + url, + body=mock_response, + content_type='application/json', + status=200, + ) # Set up parameter values db = 'testString' @@ -4504,14 +4748,14 @@ def test_get_design_document_all_params(self): rev=rev, revs=revs, revs_info=revs_info, - headers={} + headers={}, ) # Check for correct operation assert len(responses.calls) == 1 assert response.status_code == 200 # Validate query params - query_string = responses.calls[0].request.url.split('?',1)[1] + query_string = responses.calls[0].request.url.split('?', 1)[1] query_string = urllib.parse.unquote_plus(query_string) assert 'attachments={}'.format('true' if attachments else 'false') in query_string assert 'att_encoding_info={}'.format('true' if att_encoding_info else 'false') in query_string @@ -4540,12 +4784,14 @@ def test_get_design_document_required_params(self): """ # Set up mock url = preprocess_url('/testString/_design/testString') - mock_response = '{"_attachments": {"mapKey": {"content_type": "content_type", "data": "VGhpcyBpcyBhbiBlbmNvZGVkIGJ5dGUgYXJyYXku", "digest": "digest", "encoded_length": 0, "encoding": "encoding", "follows": false, "length": 0, "revpos": 1, "stub": true}}, "_conflicts": ["conflicts"], "_deleted": false, "_deleted_conflicts": ["deleted_conflicts"], "_id": "id", "_local_seq": "local_seq", "_rev": "rev", "_revisions": {"ids": ["ids"], "start": 1}, "_revs_info": [{"rev": "rev", "status": "available"}], "autoupdate": true, "filters": {"mapKey": "inner"}, "indexes": {"mapKey": {"analyzer": {"name": "classic", "stopwords": ["stopwords"], "fields": {"mapKey": {"name": "classic", "stopwords": ["stopwords"]}}}, "index": "index"}}, "language": "javascript", "options": {"partitioned": false}, "validate_doc_update": "validate_doc_update", "views": {"mapKey": {"map": "map", "reduce": "reduce"}}, "st_indexes": {"mapKey": {"index": "index"}}}' - responses.add(responses.GET, - url, - body=mock_response, - content_type='application/json', - status=200) + mock_response = '{"_attachments": {"mapKey": {"content_type": "content_type", "data": "VGhpcyBpcyBhIG1vY2sgYnl0ZSBhcnJheSB2YWx1ZS4=", "digest": "digest", "encoded_length": 0, "encoding": "encoding", "follows": false, "length": 0, "revpos": 1, "stub": true}}, "_conflicts": ["conflicts"], "_deleted": true, "_deleted_conflicts": ["deleted_conflicts"], "_id": "_id", "_local_seq": "_local_seq", "_rev": "_rev", "_revisions": {"ids": ["ids"], "start": 1}, "_revs_info": [{"rev": "rev", "status": "available"}], "autoupdate": true, "filters": {"mapKey": "inner"}, "indexes": {"mapKey": {"analyzer": {"name": "classic", "stopwords": ["stopwords"], "default": {"name": "classic", "stopwords": ["stopwords"]}, "fields": {"mapKey": {"name": "classic", "stopwords": ["stopwords"]}}}, "index": "index"}}, "language": "javascript", "options": {"partitioned": false}, "validate_doc_update": "validate_doc_update", "views": {"mapKey": {"map": "map", "reduce": "reduce"}}}' + responses.add( + responses.GET, + url, + body=mock_response, + content_type='application/json', + status=200, + ) # Set up parameter values db = 'testString' @@ -4555,7 +4801,7 @@ def test_get_design_document_required_params(self): response = _service.get_design_document( db, ddoc, - headers={} + headers={}, ) # Check for correct operation @@ -4578,12 +4824,14 @@ def test_get_design_document_value_error(self): """ # Set up mock url = preprocess_url('/testString/_design/testString') - mock_response = '{"_attachments": {"mapKey": {"content_type": "content_type", "data": "VGhpcyBpcyBhbiBlbmNvZGVkIGJ5dGUgYXJyYXku", "digest": "digest", "encoded_length": 0, "encoding": "encoding", "follows": false, "length": 0, "revpos": 1, "stub": true}}, "_conflicts": ["conflicts"], "_deleted": false, "_deleted_conflicts": ["deleted_conflicts"], "_id": "id", "_local_seq": "local_seq", "_rev": "rev", "_revisions": {"ids": ["ids"], "start": 1}, "_revs_info": [{"rev": "rev", "status": "available"}], "autoupdate": true, "filters": {"mapKey": "inner"}, "indexes": {"mapKey": {"analyzer": {"name": "classic", "stopwords": ["stopwords"], "fields": {"mapKey": {"name": "classic", "stopwords": ["stopwords"]}}}, "index": "index"}}, "language": "javascript", "options": {"partitioned": false}, "validate_doc_update": "validate_doc_update", "views": {"mapKey": {"map": "map", "reduce": "reduce"}}, "st_indexes": {"mapKey": {"index": "index"}}}' - responses.add(responses.GET, - url, - body=mock_response, - content_type='application/json', - status=200) + mock_response = '{"_attachments": {"mapKey": {"content_type": "content_type", "data": "VGhpcyBpcyBhIG1vY2sgYnl0ZSBhcnJheSB2YWx1ZS4=", "digest": "digest", "encoded_length": 0, "encoding": "encoding", "follows": false, "length": 0, "revpos": 1, "stub": true}}, "_conflicts": ["conflicts"], "_deleted": true, "_deleted_conflicts": ["deleted_conflicts"], "_id": "_id", "_local_seq": "_local_seq", "_rev": "_rev", "_revisions": {"ids": ["ids"], "start": 1}, "_revs_info": [{"rev": "rev", "status": "available"}], "autoupdate": true, "filters": {"mapKey": "inner"}, "indexes": {"mapKey": {"analyzer": {"name": "classic", "stopwords": ["stopwords"], "default": {"name": "classic", "stopwords": ["stopwords"]}, "fields": {"mapKey": {"name": "classic", "stopwords": ["stopwords"]}}}, "index": "index"}}, "language": "javascript", "options": {"partitioned": false}, "validate_doc_update": "validate_doc_update", "views": {"mapKey": {"map": "map", "reduce": "reduce"}}}' + responses.add( + responses.GET, + url, + body=mock_response, + content_type='application/json', + status=200, + ) # Set up parameter values db = 'testString' @@ -4595,7 +4843,7 @@ def test_get_design_document_value_error(self): "ddoc": ddoc, } for param in req_param_dict.keys(): - req_copy = {key:val if key is not param else None for (key,val) in req_param_dict.items()} + req_copy = {key: val if key is not param else None for (key, val) in req_param_dict.items()} with pytest.raises(ValueError): _service.get_design_document(**req_copy) @@ -4608,7 +4856,8 @@ def test_get_design_document_value_error_with_retries(self): _service.disable_retries() self.test_get_design_document_value_error() -class TestPutDesignDocument(): + +class TestPutDesignDocument: """ Test Class for put_design_document """ @@ -4620,12 +4869,14 @@ def test_put_design_document_all_params(self): """ # Set up mock url = preprocess_url('/testString/_design/testString') - mock_response = '{"id": "id", "rev": "rev", "ok": true, "caused_by": "caused_by", "error": "error", "reason": "reason"}' - responses.add(responses.PUT, - url, - body=mock_response, - content_type='application/json', - status=201) + mock_response = '{"id": "id", "rev": "rev", "ok": true, "caused_by": "caused_by", "error": "error", "reason": "reason", "ref": 0}' + responses.add( + responses.PUT, + url, + body=mock_response, + content_type='application/json', + status=201, + ) # Construct a dict representation of a Attachment model attachment_model = {} @@ -4656,14 +4907,15 @@ def test_put_design_document_all_params(self): # Construct a dict representation of a AnalyzerConfiguration model analyzer_configuration_model = {} - analyzer_configuration_model['name'] = 'classic' + analyzer_configuration_model['name'] = 'standard' analyzer_configuration_model['stopwords'] = ['testString'] + analyzer_configuration_model['default'] = analyzer_model analyzer_configuration_model['fields'] = {'key1': analyzer_model} # Construct a dict representation of a SearchIndexDefinition model search_index_definition_model = {} search_index_definition_model['analyzer'] = analyzer_configuration_model - search_index_definition_model['index'] = 'testString' + search_index_definition_model['index'] = 'function (doc) {\n index("price", doc.price);\n}' # Construct a dict representation of a DesignDocumentOptions model design_document_options_model = {} @@ -4671,22 +4923,18 @@ def test_put_design_document_all_params(self): # Construct a dict representation of a DesignDocumentViewsMapReduce model design_document_views_map_reduce_model = {} - design_document_views_map_reduce_model['map'] = 'testString' + design_document_views_map_reduce_model['map'] = 'function(doc) {\n emit(doc.productId, [doc.brand, doc.name, doc.description])\n}' design_document_views_map_reduce_model['reduce'] = 'testString' - # Construct a dict representation of a GeoIndexDefinition model - geo_index_definition_model = {} - geo_index_definition_model['index'] = 'testString' - # Construct a dict representation of a DesignDocument model design_document_model = {} design_document_model['_attachments'] = {'key1': attachment_model} design_document_model['_conflicts'] = ['testString'] design_document_model['_deleted'] = True design_document_model['_deleted_conflicts'] = ['testString'] - design_document_model['_id'] = 'testString' + design_document_model['_id'] = '_design/appliances' design_document_model['_local_seq'] = 'testString' - design_document_model['_rev'] = 'testString' + design_document_model['_rev'] = '8-7e2537e5989294471061e0cfd7292725' design_document_model['_revisions'] = revisions_model design_document_model['_revs_info'] = [document_revision_status_model] design_document_model['autoupdate'] = True @@ -4696,7 +4944,6 @@ def test_put_design_document_all_params(self): design_document_model['options'] = design_document_options_model design_document_model['validate_doc_update'] = 'testString' design_document_model['views'] = {'key1': design_document_views_map_reduce_model} - design_document_model['st_indexes'] = {'key1': geo_index_definition_model} design_document_model['foo'] = 'testString' # Set up parameter values @@ -4717,20 +4964,21 @@ def test_put_design_document_all_params(self): batch=batch, new_edits=new_edits, rev=rev, - headers={} + headers={}, ) # Check for correct operation assert len(responses.calls) == 1 assert response.status_code == 201 # Validate query params - query_string = responses.calls[0].request.url.split('?',1)[1] + query_string = responses.calls[0].request.url.split('?', 1)[1] query_string = urllib.parse.unquote_plus(query_string) assert 'batch={}'.format(batch) in query_string assert 'new_edits={}'.format('true' if new_edits else 'false') in query_string assert 'rev={}'.format(rev) in query_string - # decompress gzip compressed request body - responses.calls[0].request.body = gzip.decompress(responses.calls[0].request.body) + # Decompress gzip compressed request body. + raw_body = responses.calls[0].request.body + responses.calls[0].request.body = gzip.decompress(raw_body.read() if isinstance(raw_body, io.IOBase) else raw_body) # Validate body params req_body = json.loads(str(responses.calls[0].request.body, 'utf-8')) @@ -4752,12 +5000,14 @@ def test_put_design_document_required_params(self): """ # Set up mock url = preprocess_url('/testString/_design/testString') - mock_response = '{"id": "id", "rev": "rev", "ok": true, "caused_by": "caused_by", "error": "error", "reason": "reason"}' - responses.add(responses.PUT, - url, - body=mock_response, - content_type='application/json', - status=201) + mock_response = '{"id": "id", "rev": "rev", "ok": true, "caused_by": "caused_by", "error": "error", "reason": "reason", "ref": 0}' + responses.add( + responses.PUT, + url, + body=mock_response, + content_type='application/json', + status=201, + ) # Construct a dict representation of a Attachment model attachment_model = {} @@ -4788,14 +5038,15 @@ def test_put_design_document_required_params(self): # Construct a dict representation of a AnalyzerConfiguration model analyzer_configuration_model = {} - analyzer_configuration_model['name'] = 'classic' + analyzer_configuration_model['name'] = 'standard' analyzer_configuration_model['stopwords'] = ['testString'] + analyzer_configuration_model['default'] = analyzer_model analyzer_configuration_model['fields'] = {'key1': analyzer_model} # Construct a dict representation of a SearchIndexDefinition model search_index_definition_model = {} search_index_definition_model['analyzer'] = analyzer_configuration_model - search_index_definition_model['index'] = 'testString' + search_index_definition_model['index'] = 'function (doc) {\n index("price", doc.price);\n}' # Construct a dict representation of a DesignDocumentOptions model design_document_options_model = {} @@ -4803,22 +5054,18 @@ def test_put_design_document_required_params(self): # Construct a dict representation of a DesignDocumentViewsMapReduce model design_document_views_map_reduce_model = {} - design_document_views_map_reduce_model['map'] = 'testString' + design_document_views_map_reduce_model['map'] = 'function(doc) {\n emit(doc.productId, [doc.brand, doc.name, doc.description])\n}' design_document_views_map_reduce_model['reduce'] = 'testString' - # Construct a dict representation of a GeoIndexDefinition model - geo_index_definition_model = {} - geo_index_definition_model['index'] = 'testString' - # Construct a dict representation of a DesignDocument model design_document_model = {} design_document_model['_attachments'] = {'key1': attachment_model} design_document_model['_conflicts'] = ['testString'] design_document_model['_deleted'] = True design_document_model['_deleted_conflicts'] = ['testString'] - design_document_model['_id'] = 'testString' + design_document_model['_id'] = '_design/appliances' design_document_model['_local_seq'] = 'testString' - design_document_model['_rev'] = 'testString' + design_document_model['_rev'] = '8-7e2537e5989294471061e0cfd7292725' design_document_model['_revisions'] = revisions_model design_document_model['_revs_info'] = [document_revision_status_model] design_document_model['autoupdate'] = True @@ -4828,7 +5075,6 @@ def test_put_design_document_required_params(self): design_document_model['options'] = design_document_options_model design_document_model['validate_doc_update'] = 'testString' design_document_model['views'] = {'key1': design_document_views_map_reduce_model} - design_document_model['st_indexes'] = {'key1': geo_index_definition_model} design_document_model['foo'] = 'testString' # Set up parameter values @@ -4841,14 +5087,15 @@ def test_put_design_document_required_params(self): db, ddoc, design_document, - headers={} + headers={}, ) # Check for correct operation assert len(responses.calls) == 1 assert response.status_code == 201 - # decompress gzip compressed request body - responses.calls[0].request.body = gzip.decompress(responses.calls[0].request.body) + # Decompress gzip compressed request body. + raw_body = responses.calls[0].request.body + responses.calls[0].request.body = gzip.decompress(raw_body.read() if isinstance(raw_body, io.IOBase) else raw_body) # Validate body params req_body = json.loads(str(responses.calls[0].request.body, 'utf-8')) @@ -4870,12 +5117,14 @@ def test_put_design_document_value_error(self): """ # Set up mock url = preprocess_url('/testString/_design/testString') - mock_response = '{"id": "id", "rev": "rev", "ok": true, "caused_by": "caused_by", "error": "error", "reason": "reason"}' - responses.add(responses.PUT, - url, - body=mock_response, - content_type='application/json', - status=201) + mock_response = '{"id": "id", "rev": "rev", "ok": true, "caused_by": "caused_by", "error": "error", "reason": "reason", "ref": 0}' + responses.add( + responses.PUT, + url, + body=mock_response, + content_type='application/json', + status=201, + ) # Construct a dict representation of a Attachment model attachment_model = {} @@ -4906,14 +5155,15 @@ def test_put_design_document_value_error(self): # Construct a dict representation of a AnalyzerConfiguration model analyzer_configuration_model = {} - analyzer_configuration_model['name'] = 'classic' + analyzer_configuration_model['name'] = 'standard' analyzer_configuration_model['stopwords'] = ['testString'] + analyzer_configuration_model['default'] = analyzer_model analyzer_configuration_model['fields'] = {'key1': analyzer_model} # Construct a dict representation of a SearchIndexDefinition model search_index_definition_model = {} search_index_definition_model['analyzer'] = analyzer_configuration_model - search_index_definition_model['index'] = 'testString' + search_index_definition_model['index'] = 'function (doc) {\n index("price", doc.price);\n}' # Construct a dict representation of a DesignDocumentOptions model design_document_options_model = {} @@ -4921,22 +5171,18 @@ def test_put_design_document_value_error(self): # Construct a dict representation of a DesignDocumentViewsMapReduce model design_document_views_map_reduce_model = {} - design_document_views_map_reduce_model['map'] = 'testString' + design_document_views_map_reduce_model['map'] = 'function(doc) {\n emit(doc.productId, [doc.brand, doc.name, doc.description])\n}' design_document_views_map_reduce_model['reduce'] = 'testString' - # Construct a dict representation of a GeoIndexDefinition model - geo_index_definition_model = {} - geo_index_definition_model['index'] = 'testString' - # Construct a dict representation of a DesignDocument model design_document_model = {} design_document_model['_attachments'] = {'key1': attachment_model} design_document_model['_conflicts'] = ['testString'] design_document_model['_deleted'] = True design_document_model['_deleted_conflicts'] = ['testString'] - design_document_model['_id'] = 'testString' + design_document_model['_id'] = '_design/appliances' design_document_model['_local_seq'] = 'testString' - design_document_model['_rev'] = 'testString' + design_document_model['_rev'] = '8-7e2537e5989294471061e0cfd7292725' design_document_model['_revisions'] = revisions_model design_document_model['_revs_info'] = [document_revision_status_model] design_document_model['autoupdate'] = True @@ -4946,7 +5192,6 @@ def test_put_design_document_value_error(self): design_document_model['options'] = design_document_options_model design_document_model['validate_doc_update'] = 'testString' design_document_model['views'] = {'key1': design_document_views_map_reduce_model} - design_document_model['st_indexes'] = {'key1': geo_index_definition_model} design_document_model['foo'] = 'testString' # Set up parameter values @@ -4961,7 +5206,7 @@ def test_put_design_document_value_error(self): "design_document": design_document, } for param in req_param_dict.keys(): - req_copy = {key:val if key is not param else None for (key,val) in req_param_dict.items()} + req_copy = {key: val if key is not param else None for (key, val) in req_param_dict.items()} with pytest.raises(ValueError): _service.put_design_document(**req_copy) @@ -4974,7 +5219,8 @@ def test_put_design_document_value_error_with_retries(self): _service.disable_retries() self.test_put_design_document_value_error() -class TestGetDesignDocumentInformation(): + +class TestGetDesignDocumentInformation: """ Test Class for get_design_document_information """ @@ -4986,12 +5232,14 @@ def test_get_design_document_information_all_params(self): """ # Set up mock url = preprocess_url('/testString/_design/testString/_info') - mock_response = '{"name": "name", "view_index": {"collator_versions": ["collator_versions"], "compact_running": false, "language": "language", "signature": "signature", "sizes": {"active": 6, "external": 8, "file": 4}, "updater_running": false, "waiting_clients": 0, "waiting_commit": true}}' - responses.add(responses.GET, - url, - body=mock_response, - content_type='application/json', - status=200) + mock_response = '{"name": "name", "view_index": {"collator_versions": ["collator_versions"], "compact_running": false, "language": "language", "signature": "signature", "sizes": {"active": 0, "external": 0, "file": 0}, "updater_running": false, "updates_pending": {"minimum": 0, "preferred": 0, "total": 0}, "waiting_clients": 0, "waiting_commit": true}}' + responses.add( + responses.GET, + url, + body=mock_response, + content_type='application/json', + status=200, + ) # Set up parameter values db = 'testString' @@ -5001,7 +5249,7 @@ def test_get_design_document_information_all_params(self): response = _service.get_design_document_information( db, ddoc, - headers={} + headers={}, ) # Check for correct operation @@ -5024,12 +5272,14 @@ def test_get_design_document_information_value_error(self): """ # Set up mock url = preprocess_url('/testString/_design/testString/_info') - mock_response = '{"name": "name", "view_index": {"collator_versions": ["collator_versions"], "compact_running": false, "language": "language", "signature": "signature", "sizes": {"active": 6, "external": 8, "file": 4}, "updater_running": false, "waiting_clients": 0, "waiting_commit": true}}' - responses.add(responses.GET, - url, - body=mock_response, - content_type='application/json', - status=200) + mock_response = '{"name": "name", "view_index": {"collator_versions": ["collator_versions"], "compact_running": false, "language": "language", "signature": "signature", "sizes": {"active": 0, "external": 0, "file": 0}, "updater_running": false, "updates_pending": {"minimum": 0, "preferred": 0, "total": 0}, "waiting_clients": 0, "waiting_commit": true}}' + responses.add( + responses.GET, + url, + body=mock_response, + content_type='application/json', + status=200, + ) # Set up parameter values db = 'testString' @@ -5041,7 +5291,7 @@ def test_get_design_document_information_value_error(self): "ddoc": ddoc, } for param in req_param_dict.keys(): - req_copy = {key:val if key is not param else None for (key,val) in req_param_dict.items()} + req_copy = {key: val if key is not param else None for (key, val) in req_param_dict.items()} with pytest.raises(ValueError): _service.get_design_document_information(**req_copy) @@ -5054,7 +5304,8 @@ def test_get_design_document_information_value_error_with_retries(self): _service.disable_retries() self.test_get_design_document_information_value_error() -class TestPostDesignDocs(): + +class TestPostDesignDocs: """ Test Class for post_design_docs """ @@ -5066,12 +5317,14 @@ def test_post_design_docs_all_params(self): """ # Set up mock url = preprocess_url('/testString/_design_docs') - mock_response = '{"total_rows": 0, "rows": [{"caused_by": "caused_by", "error": "error", "reason": "reason", "doc": {"_attachments": {"mapKey": {"content_type": "content_type", "data": "VGhpcyBpcyBhbiBlbmNvZGVkIGJ5dGUgYXJyYXku", "digest": "digest", "encoded_length": 0, "encoding": "encoding", "follows": false, "length": 0, "revpos": 1, "stub": true}}, "_conflicts": ["conflicts"], "_deleted": false, "_deleted_conflicts": ["deleted_conflicts"], "_id": "id", "_local_seq": "local_seq", "_rev": "rev", "_revisions": {"ids": ["ids"], "start": 1}, "_revs_info": [{"rev": "rev", "status": "available"}]}, "id": "id", "key": "key", "value": {"rev": "rev"}}], "update_seq": "update_seq"}' - responses.add(responses.POST, - url, - body=mock_response, - content_type='application/json', - status=200) + mock_response = '{"total_rows": 0, "rows": [{"caused_by": "caused_by", "error": "error", "reason": "reason", "ref": 0, "doc": {"_attachments": {"mapKey": {"content_type": "content_type", "data": "VGhpcyBpcyBhIG1vY2sgYnl0ZSBhcnJheSB2YWx1ZS4=", "digest": "digest", "encoded_length": 0, "encoding": "encoding", "follows": false, "length": 0, "revpos": 1, "stub": true}}, "_conflicts": ["conflicts"], "_deleted": true, "_deleted_conflicts": ["deleted_conflicts"], "_id": "_id", "_local_seq": "_local_seq", "_rev": "_rev", "_revisions": {"ids": ["ids"], "start": 1}, "_revs_info": [{"rev": "rev", "status": "available"}]}, "id": "id", "key": "key", "value": {"deleted": false, "rev": "rev"}}], "update_seq": "update_seq"}' + responses.add( + responses.POST, + url, + body=mock_response, + content_type='application/json', + status=200, + ) # Set up parameter values db = 'testString' @@ -5081,14 +5334,13 @@ def test_post_design_docs_all_params(self): descending = False include_docs = False inclusive_end = True - limit = 0 + limit = 10 skip = 0 update_seq = False end_key = 'testString' key = 'testString' keys = ['testString'] - start_key = 'testString' - accept = 'application/json' + start_key = '0007741142412418284' # Invoke method response = _service.post_design_docs( @@ -5106,15 +5358,15 @@ def test_post_design_docs_all_params(self): key=key, keys=keys, start_key=start_key, - accept=accept, - headers={} + headers={}, ) # Check for correct operation assert len(responses.calls) == 1 assert response.status_code == 200 - # decompress gzip compressed request body - responses.calls[0].request.body = gzip.decompress(responses.calls[0].request.body) + # Decompress gzip compressed request body. + raw_body = responses.calls[0].request.body + responses.calls[0].request.body = gzip.decompress(raw_body.read() if isinstance(raw_body, io.IOBase) else raw_body) # Validate body params req_body = json.loads(str(responses.calls[0].request.body, 'utf-8')) @@ -5124,13 +5376,13 @@ def test_post_design_docs_all_params(self): assert req_body['descending'] == False assert req_body['include_docs'] == False assert req_body['inclusive_end'] == True - assert req_body['limit'] == 0 + assert req_body['limit'] == 10 assert req_body['skip'] == 0 assert req_body['update_seq'] == False assert req_body['end_key'] == 'testString' assert req_body['key'] == 'testString' assert req_body['keys'] == ['testString'] - assert req_body['start_key'] == 'testString' + assert req_body['start_key'] == '0007741142412418284' def test_post_design_docs_all_params_with_retries(self): # Enable retries and run test_post_design_docs_all_params. @@ -5141,86 +5393,6 @@ def test_post_design_docs_all_params_with_retries(self): _service.disable_retries() self.test_post_design_docs_all_params() - @responses.activate - def test_post_design_docs_required_params(self): - """ - test_post_design_docs_required_params() - """ - # Set up mock - url = preprocess_url('/testString/_design_docs') - mock_response = '{"total_rows": 0, "rows": [{"caused_by": "caused_by", "error": "error", "reason": "reason", "doc": {"_attachments": {"mapKey": {"content_type": "content_type", "data": "VGhpcyBpcyBhbiBlbmNvZGVkIGJ5dGUgYXJyYXku", "digest": "digest", "encoded_length": 0, "encoding": "encoding", "follows": false, "length": 0, "revpos": 1, "stub": true}}, "_conflicts": ["conflicts"], "_deleted": false, "_deleted_conflicts": ["deleted_conflicts"], "_id": "id", "_local_seq": "local_seq", "_rev": "rev", "_revisions": {"ids": ["ids"], "start": 1}, "_revs_info": [{"rev": "rev", "status": "available"}]}, "id": "id", "key": "key", "value": {"rev": "rev"}}], "update_seq": "update_seq"}' - responses.add(responses.POST, - url, - body=mock_response, - content_type='application/json', - status=200) - - # Set up parameter values - db = 'testString' - att_encoding_info = False - attachments = False - conflicts = False - descending = False - include_docs = False - inclusive_end = True - limit = 0 - skip = 0 - update_seq = False - end_key = 'testString' - key = 'testString' - keys = ['testString'] - start_key = 'testString' - - # Invoke method - response = _service.post_design_docs( - db, - att_encoding_info=att_encoding_info, - attachments=attachments, - conflicts=conflicts, - descending=descending, - include_docs=include_docs, - inclusive_end=inclusive_end, - limit=limit, - skip=skip, - update_seq=update_seq, - end_key=end_key, - key=key, - keys=keys, - start_key=start_key, - headers={} - ) - - # Check for correct operation - assert len(responses.calls) == 1 - assert response.status_code == 200 - # decompress gzip compressed request body - responses.calls[0].request.body = gzip.decompress(responses.calls[0].request.body) - - # Validate body params - req_body = json.loads(str(responses.calls[0].request.body, 'utf-8')) - assert req_body['att_encoding_info'] == False - assert req_body['attachments'] == False - assert req_body['conflicts'] == False - assert req_body['descending'] == False - assert req_body['include_docs'] == False - assert req_body['inclusive_end'] == True - assert req_body['limit'] == 0 - assert req_body['skip'] == 0 - assert req_body['update_seq'] == False - assert req_body['end_key'] == 'testString' - assert req_body['key'] == 'testString' - assert req_body['keys'] == ['testString'] - assert req_body['start_key'] == 'testString' - - def test_post_design_docs_required_params_with_retries(self): - # Enable retries and run test_post_design_docs_required_params. - _service.enable_retries() - self.test_post_design_docs_required_params() - - # Disable retries and run test_post_design_docs_required_params. - _service.disable_retries() - self.test_post_design_docs_required_params() - @responses.activate def test_post_design_docs_value_error(self): """ @@ -5228,12 +5400,14 @@ def test_post_design_docs_value_error(self): """ # Set up mock url = preprocess_url('/testString/_design_docs') - mock_response = '{"total_rows": 0, "rows": [{"caused_by": "caused_by", "error": "error", "reason": "reason", "doc": {"_attachments": {"mapKey": {"content_type": "content_type", "data": "VGhpcyBpcyBhbiBlbmNvZGVkIGJ5dGUgYXJyYXku", "digest": "digest", "encoded_length": 0, "encoding": "encoding", "follows": false, "length": 0, "revpos": 1, "stub": true}}, "_conflicts": ["conflicts"], "_deleted": false, "_deleted_conflicts": ["deleted_conflicts"], "_id": "id", "_local_seq": "local_seq", "_rev": "rev", "_revisions": {"ids": ["ids"], "start": 1}, "_revs_info": [{"rev": "rev", "status": "available"}]}, "id": "id", "key": "key", "value": {"rev": "rev"}}], "update_seq": "update_seq"}' - responses.add(responses.POST, - url, - body=mock_response, - content_type='application/json', - status=200) + mock_response = '{"total_rows": 0, "rows": [{"caused_by": "caused_by", "error": "error", "reason": "reason", "ref": 0, "doc": {"_attachments": {"mapKey": {"content_type": "content_type", "data": "VGhpcyBpcyBhIG1vY2sgYnl0ZSBhcnJheSB2YWx1ZS4=", "digest": "digest", "encoded_length": 0, "encoding": "encoding", "follows": false, "length": 0, "revpos": 1, "stub": true}}, "_conflicts": ["conflicts"], "_deleted": true, "_deleted_conflicts": ["deleted_conflicts"], "_id": "_id", "_local_seq": "_local_seq", "_rev": "_rev", "_revisions": {"ids": ["ids"], "start": 1}, "_revs_info": [{"rev": "rev", "status": "available"}]}, "id": "id", "key": "key", "value": {"deleted": false, "rev": "rev"}}], "update_seq": "update_seq"}' + responses.add( + responses.POST, + url, + body=mock_response, + content_type='application/json', + status=200, + ) # Set up parameter values db = 'testString' @@ -5243,20 +5417,20 @@ def test_post_design_docs_value_error(self): descending = False include_docs = False inclusive_end = True - limit = 0 + limit = 10 skip = 0 update_seq = False end_key = 'testString' key = 'testString' keys = ['testString'] - start_key = 'testString' + start_key = '0007741142412418284' # Pass in all but one required param and check for a ValueError req_param_dict = { "db": db, } for param in req_param_dict.keys(): - req_copy = {key:val if key is not param else None for (key,val) in req_param_dict.items()} + req_copy = {key: val if key is not param else None for (key, val) in req_param_dict.items()} with pytest.raises(ValueError): _service.post_design_docs(**req_copy) @@ -5269,7 +5443,8 @@ def test_post_design_docs_value_error_with_retries(self): _service.disable_retries() self.test_post_design_docs_value_error() -class TestPostDesignDocsQueries(): + +class TestPostDesignDocsQueries: """ Test Class for post_design_docs_queries """ @@ -5281,12 +5456,14 @@ def test_post_design_docs_queries_all_params(self): """ # Set up mock url = preprocess_url('/testString/_design_docs/queries') - mock_response = '{"results": [{"total_rows": 0, "rows": [{"caused_by": "caused_by", "error": "error", "reason": "reason", "doc": {"_attachments": {"mapKey": {"content_type": "content_type", "data": "VGhpcyBpcyBhbiBlbmNvZGVkIGJ5dGUgYXJyYXku", "digest": "digest", "encoded_length": 0, "encoding": "encoding", "follows": false, "length": 0, "revpos": 1, "stub": true}}, "_conflicts": ["conflicts"], "_deleted": false, "_deleted_conflicts": ["deleted_conflicts"], "_id": "id", "_local_seq": "local_seq", "_rev": "rev", "_revisions": {"ids": ["ids"], "start": 1}, "_revs_info": [{"rev": "rev", "status": "available"}]}, "id": "id", "key": "key", "value": {"rev": "rev"}}], "update_seq": "update_seq"}]}' - responses.add(responses.POST, - url, - body=mock_response, - content_type='application/json', - status=200) + mock_response = '{"results": [{"total_rows": 0, "rows": [{"caused_by": "caused_by", "error": "error", "reason": "reason", "ref": 0, "doc": {"_attachments": {"mapKey": {"content_type": "content_type", "data": "VGhpcyBpcyBhIG1vY2sgYnl0ZSBhcnJheSB2YWx1ZS4=", "digest": "digest", "encoded_length": 0, "encoding": "encoding", "follows": false, "length": 0, "revpos": 1, "stub": true}}, "_conflicts": ["conflicts"], "_deleted": true, "_deleted_conflicts": ["deleted_conflicts"], "_id": "_id", "_local_seq": "_local_seq", "_rev": "_rev", "_revisions": {"ids": ["ids"], "start": 1}, "_revs_info": [{"rev": "rev", "status": "available"}]}, "id": "id", "key": "key", "value": {"deleted": false, "rev": "rev"}}], "update_seq": "update_seq"}]}' + responses.add( + responses.POST, + url, + body=mock_response, + content_type='application/json', + status=200, + ) # Construct a dict representation of a AllDocsQuery model all_docs_query_model = {} @@ -5301,7 +5478,7 @@ def test_post_design_docs_queries_all_params(self): all_docs_query_model['update_seq'] = False all_docs_query_model['end_key'] = 'testString' all_docs_query_model['key'] = 'testString' - all_docs_query_model['keys'] = ['testString'] + all_docs_query_model['keys'] = ['small-appliances:1000042', 'small-appliances:1000043'] all_docs_query_model['start_key'] = 'testString' # Set up parameter values @@ -5314,14 +5491,15 @@ def test_post_design_docs_queries_all_params(self): db, queries, accept=accept, - headers={} + headers={}, ) # Check for correct operation assert len(responses.calls) == 1 assert response.status_code == 200 - # decompress gzip compressed request body - responses.calls[0].request.body = gzip.decompress(responses.calls[0].request.body) + # Decompress gzip compressed request body. + raw_body = responses.calls[0].request.body + responses.calls[0].request.body = gzip.decompress(raw_body.read() if isinstance(raw_body, io.IOBase) else raw_body) # Validate body params req_body = json.loads(str(responses.calls[0].request.body, 'utf-8')) @@ -5343,12 +5521,14 @@ def test_post_design_docs_queries_required_params(self): """ # Set up mock url = preprocess_url('/testString/_design_docs/queries') - mock_response = '{"results": [{"total_rows": 0, "rows": [{"caused_by": "caused_by", "error": "error", "reason": "reason", "doc": {"_attachments": {"mapKey": {"content_type": "content_type", "data": "VGhpcyBpcyBhbiBlbmNvZGVkIGJ5dGUgYXJyYXku", "digest": "digest", "encoded_length": 0, "encoding": "encoding", "follows": false, "length": 0, "revpos": 1, "stub": true}}, "_conflicts": ["conflicts"], "_deleted": false, "_deleted_conflicts": ["deleted_conflicts"], "_id": "id", "_local_seq": "local_seq", "_rev": "rev", "_revisions": {"ids": ["ids"], "start": 1}, "_revs_info": [{"rev": "rev", "status": "available"}]}, "id": "id", "key": "key", "value": {"rev": "rev"}}], "update_seq": "update_seq"}]}' - responses.add(responses.POST, - url, - body=mock_response, - content_type='application/json', - status=200) + mock_response = '{"results": [{"total_rows": 0, "rows": [{"caused_by": "caused_by", "error": "error", "reason": "reason", "ref": 0, "doc": {"_attachments": {"mapKey": {"content_type": "content_type", "data": "VGhpcyBpcyBhIG1vY2sgYnl0ZSBhcnJheSB2YWx1ZS4=", "digest": "digest", "encoded_length": 0, "encoding": "encoding", "follows": false, "length": 0, "revpos": 1, "stub": true}}, "_conflicts": ["conflicts"], "_deleted": true, "_deleted_conflicts": ["deleted_conflicts"], "_id": "_id", "_local_seq": "_local_seq", "_rev": "_rev", "_revisions": {"ids": ["ids"], "start": 1}, "_revs_info": [{"rev": "rev", "status": "available"}]}, "id": "id", "key": "key", "value": {"deleted": false, "rev": "rev"}}], "update_seq": "update_seq"}]}' + responses.add( + responses.POST, + url, + body=mock_response, + content_type='application/json', + status=200, + ) # Construct a dict representation of a AllDocsQuery model all_docs_query_model = {} @@ -5363,7 +5543,7 @@ def test_post_design_docs_queries_required_params(self): all_docs_query_model['update_seq'] = False all_docs_query_model['end_key'] = 'testString' all_docs_query_model['key'] = 'testString' - all_docs_query_model['keys'] = ['testString'] + all_docs_query_model['keys'] = ['small-appliances:1000042', 'small-appliances:1000043'] all_docs_query_model['start_key'] = 'testString' # Set up parameter values @@ -5374,14 +5554,15 @@ def test_post_design_docs_queries_required_params(self): response = _service.post_design_docs_queries( db, queries, - headers={} + headers={}, ) # Check for correct operation assert len(responses.calls) == 1 assert response.status_code == 200 - # decompress gzip compressed request body - responses.calls[0].request.body = gzip.decompress(responses.calls[0].request.body) + # Decompress gzip compressed request body. + raw_body = responses.calls[0].request.body + responses.calls[0].request.body = gzip.decompress(raw_body.read() if isinstance(raw_body, io.IOBase) else raw_body) # Validate body params req_body = json.loads(str(responses.calls[0].request.body, 'utf-8')) @@ -5403,12 +5584,14 @@ def test_post_design_docs_queries_value_error(self): """ # Set up mock url = preprocess_url('/testString/_design_docs/queries') - mock_response = '{"results": [{"total_rows": 0, "rows": [{"caused_by": "caused_by", "error": "error", "reason": "reason", "doc": {"_attachments": {"mapKey": {"content_type": "content_type", "data": "VGhpcyBpcyBhbiBlbmNvZGVkIGJ5dGUgYXJyYXku", "digest": "digest", "encoded_length": 0, "encoding": "encoding", "follows": false, "length": 0, "revpos": 1, "stub": true}}, "_conflicts": ["conflicts"], "_deleted": false, "_deleted_conflicts": ["deleted_conflicts"], "_id": "id", "_local_seq": "local_seq", "_rev": "rev", "_revisions": {"ids": ["ids"], "start": 1}, "_revs_info": [{"rev": "rev", "status": "available"}]}, "id": "id", "key": "key", "value": {"rev": "rev"}}], "update_seq": "update_seq"}]}' - responses.add(responses.POST, - url, - body=mock_response, - content_type='application/json', - status=200) + mock_response = '{"results": [{"total_rows": 0, "rows": [{"caused_by": "caused_by", "error": "error", "reason": "reason", "ref": 0, "doc": {"_attachments": {"mapKey": {"content_type": "content_type", "data": "VGhpcyBpcyBhIG1vY2sgYnl0ZSBhcnJheSB2YWx1ZS4=", "digest": "digest", "encoded_length": 0, "encoding": "encoding", "follows": false, "length": 0, "revpos": 1, "stub": true}}, "_conflicts": ["conflicts"], "_deleted": true, "_deleted_conflicts": ["deleted_conflicts"], "_id": "_id", "_local_seq": "_local_seq", "_rev": "_rev", "_revisions": {"ids": ["ids"], "start": 1}, "_revs_info": [{"rev": "rev", "status": "available"}]}, "id": "id", "key": "key", "value": {"deleted": false, "rev": "rev"}}], "update_seq": "update_seq"}]}' + responses.add( + responses.POST, + url, + body=mock_response, + content_type='application/json', + status=200, + ) # Construct a dict representation of a AllDocsQuery model all_docs_query_model = {} @@ -5423,7 +5606,7 @@ def test_post_design_docs_queries_value_error(self): all_docs_query_model['update_seq'] = False all_docs_query_model['end_key'] = 'testString' all_docs_query_model['key'] = 'testString' - all_docs_query_model['keys'] = ['testString'] + all_docs_query_model['keys'] = ['small-appliances:1000042', 'small-appliances:1000043'] all_docs_query_model['start_key'] = 'testString' # Set up parameter values @@ -5436,7 +5619,7 @@ def test_post_design_docs_queries_value_error(self): "queries": queries, } for param in req_param_dict.keys(): - req_copy = {key:val if key is not param else None for (key,val) in req_param_dict.items()} + req_copy = {key: val if key is not param else None for (key, val) in req_param_dict.items()} with pytest.raises(ValueError): _service.post_design_docs_queries(**req_copy) @@ -5449,6 +5632,7 @@ def test_post_design_docs_queries_value_error_with_retries(self): _service.disable_retries() self.test_post_design_docs_queries_value_error() + # endregion ############################################################################## # End of Service: DesignDocuments @@ -5459,7 +5643,8 @@ def test_post_design_docs_queries_value_error_with_retries(self): ############################################################################## # region -class TestNewInstance(): + +class TestNewInstance: """ Test Class for new_instance """ @@ -5486,7 +5671,8 @@ def test_new_instance_without_authenticator(self): service_name='TEST_SERVICE_NOT_FOUND', ) -class TestPostView(): + +class TestPostView: """ Test Class for post_view """ @@ -5498,12 +5684,14 @@ def test_post_view_all_params(self): """ # Set up mock url = preprocess_url('/testString/_design/testString/_view/testString') - mock_response = '{"total_rows": 0, "update_seq": "update_seq", "rows": [{"caused_by": "caused_by", "error": "error", "reason": "reason", "doc": {"_attachments": {"mapKey": {"content_type": "content_type", "data": "VGhpcyBpcyBhbiBlbmNvZGVkIGJ5dGUgYXJyYXku", "digest": "digest", "encoded_length": 0, "encoding": "encoding", "follows": false, "length": 0, "revpos": 1, "stub": true}}, "_conflicts": ["conflicts"], "_deleted": false, "_deleted_conflicts": ["deleted_conflicts"], "_id": "id", "_local_seq": "local_seq", "_rev": "rev", "_revisions": {"ids": ["ids"], "start": 1}, "_revs_info": [{"rev": "rev", "status": "available"}]}, "id": "id", "key": "anyValue", "value": "anyValue"}]}' - responses.add(responses.POST, - url, - body=mock_response, - content_type='application/json', - status=200) + mock_response = '{"total_rows": 0, "update_seq": "update_seq", "rows": [{"caused_by": "caused_by", "error": "error", "reason": "reason", "ref": 0, "doc": {"_attachments": {"mapKey": {"content_type": "content_type", "data": "VGhpcyBpcyBhIG1vY2sgYnl0ZSBhcnJheSB2YWx1ZS4=", "digest": "digest", "encoded_length": 0, "encoding": "encoding", "follows": false, "length": 0, "revpos": 1, "stub": true}}, "_conflicts": ["conflicts"], "_deleted": true, "_deleted_conflicts": ["deleted_conflicts"], "_id": "_id", "_local_seq": "_local_seq", "_rev": "_rev", "_revisions": {"ids": ["ids"], "start": 1}, "_revs_info": [{"rev": "rev", "status": "available"}]}, "id": "id", "key": "anyValue", "value": "anyValue"}]}' + responses.add( + responses.POST, + url, + body=mock_response, + content_type='application/json', + status=200, + ) # Set up parameter values db = 'testString' @@ -5513,9 +5701,9 @@ def test_post_view_all_params(self): attachments = False conflicts = False descending = False - include_docs = False + include_docs = True inclusive_end = True - limit = 0 + limit = 10 skip = 0 update_seq = False end_key = 'testString' @@ -5523,7 +5711,7 @@ def test_post_view_all_params(self): group = False group_level = 1 key = 'testString' - keys = ['testString'] + keys = ['examplekey'] reduce = True stable = False start_key = 'testString' @@ -5555,14 +5743,15 @@ def test_post_view_all_params(self): start_key=start_key, start_key_doc_id=start_key_doc_id, update=update, - headers={} + headers={}, ) # Check for correct operation assert len(responses.calls) == 1 assert response.status_code == 200 - # decompress gzip compressed request body - responses.calls[0].request.body = gzip.decompress(responses.calls[0].request.body) + # Decompress gzip compressed request body. + raw_body = responses.calls[0].request.body + responses.calls[0].request.body = gzip.decompress(raw_body.read() if isinstance(raw_body, io.IOBase) else raw_body) # Validate body params req_body = json.loads(str(responses.calls[0].request.body, 'utf-8')) @@ -5570,9 +5759,9 @@ def test_post_view_all_params(self): assert req_body['attachments'] == False assert req_body['conflicts'] == False assert req_body['descending'] == False - assert req_body['include_docs'] == False + assert req_body['include_docs'] == True assert req_body['inclusive_end'] == True - assert req_body['limit'] == 0 + assert req_body['limit'] == 10 assert req_body['skip'] == 0 assert req_body['update_seq'] == False assert req_body['end_key'] == 'testString' @@ -5580,7 +5769,7 @@ def test_post_view_all_params(self): assert req_body['group'] == False assert req_body['group_level'] == 1 assert req_body['key'] == 'testString' - assert req_body['keys'] == ['testString'] + assert req_body['keys'] == ['examplekey'] assert req_body['reduce'] == True assert req_body['stable'] == False assert req_body['start_key'] == 'testString' @@ -5603,12 +5792,14 @@ def test_post_view_value_error(self): """ # Set up mock url = preprocess_url('/testString/_design/testString/_view/testString') - mock_response = '{"total_rows": 0, "update_seq": "update_seq", "rows": [{"caused_by": "caused_by", "error": "error", "reason": "reason", "doc": {"_attachments": {"mapKey": {"content_type": "content_type", "data": "VGhpcyBpcyBhbiBlbmNvZGVkIGJ5dGUgYXJyYXku", "digest": "digest", "encoded_length": 0, "encoding": "encoding", "follows": false, "length": 0, "revpos": 1, "stub": true}}, "_conflicts": ["conflicts"], "_deleted": false, "_deleted_conflicts": ["deleted_conflicts"], "_id": "id", "_local_seq": "local_seq", "_rev": "rev", "_revisions": {"ids": ["ids"], "start": 1}, "_revs_info": [{"rev": "rev", "status": "available"}]}, "id": "id", "key": "anyValue", "value": "anyValue"}]}' - responses.add(responses.POST, - url, - body=mock_response, - content_type='application/json', - status=200) + mock_response = '{"total_rows": 0, "update_seq": "update_seq", "rows": [{"caused_by": "caused_by", "error": "error", "reason": "reason", "ref": 0, "doc": {"_attachments": {"mapKey": {"content_type": "content_type", "data": "VGhpcyBpcyBhIG1vY2sgYnl0ZSBhcnJheSB2YWx1ZS4=", "digest": "digest", "encoded_length": 0, "encoding": "encoding", "follows": false, "length": 0, "revpos": 1, "stub": true}}, "_conflicts": ["conflicts"], "_deleted": true, "_deleted_conflicts": ["deleted_conflicts"], "_id": "_id", "_local_seq": "_local_seq", "_rev": "_rev", "_revisions": {"ids": ["ids"], "start": 1}, "_revs_info": [{"rev": "rev", "status": "available"}]}, "id": "id", "key": "anyValue", "value": "anyValue"}]}' + responses.add( + responses.POST, + url, + body=mock_response, + content_type='application/json', + status=200, + ) # Set up parameter values db = 'testString' @@ -5618,9 +5809,9 @@ def test_post_view_value_error(self): attachments = False conflicts = False descending = False - include_docs = False + include_docs = True inclusive_end = True - limit = 0 + limit = 10 skip = 0 update_seq = False end_key = 'testString' @@ -5628,7 +5819,7 @@ def test_post_view_value_error(self): group = False group_level = 1 key = 'testString' - keys = ['testString'] + keys = ['examplekey'] reduce = True stable = False start_key = 'testString' @@ -5642,7 +5833,7 @@ def test_post_view_value_error(self): "view": view, } for param in req_param_dict.keys(): - req_copy = {key:val if key is not param else None for (key,val) in req_param_dict.items()} + req_copy = {key: val if key is not param else None for (key, val) in req_param_dict.items()} with pytest.raises(ValueError): _service.post_view(**req_copy) @@ -5655,7 +5846,8 @@ def test_post_view_value_error_with_retries(self): _service.disable_retries() self.test_post_view_value_error() -class TestPostViewAsStream(): + +class TestPostViewAsStream: """ Test Class for post_view_as_stream """ @@ -5668,11 +5860,13 @@ def test_post_view_as_stream_all_params(self): # Set up mock url = preprocess_url('/testString/_design/testString/_view/testString') mock_response = '{"foo": "this is a mock response for JSON streaming"}' - responses.add(responses.POST, - url, - body=mock_response, - content_type='application/json', - status=200) + responses.add( + responses.POST, + url, + body=mock_response, + content_type='application/json', + status=200, + ) # Set up parameter values db = 'testString' @@ -5682,9 +5876,9 @@ def test_post_view_as_stream_all_params(self): attachments = False conflicts = False descending = False - include_docs = False + include_docs = True inclusive_end = True - limit = 0 + limit = 10 skip = 0 update_seq = False end_key = 'testString' @@ -5692,7 +5886,7 @@ def test_post_view_as_stream_all_params(self): group = False group_level = 1 key = 'testString' - keys = ['testString'] + keys = ['examplekey'] reduce = True stable = False start_key = 'testString' @@ -5724,14 +5918,15 @@ def test_post_view_as_stream_all_params(self): start_key=start_key, start_key_doc_id=start_key_doc_id, update=update, - headers={} + headers={}, ) # Check for correct operation assert len(responses.calls) == 1 assert response.status_code == 200 - # decompress gzip compressed request body - responses.calls[0].request.body = gzip.decompress(responses.calls[0].request.body) + # Decompress gzip compressed request body. + raw_body = responses.calls[0].request.body + responses.calls[0].request.body = gzip.decompress(raw_body.read() if isinstance(raw_body, io.IOBase) else raw_body) # Validate body params req_body = json.loads(str(responses.calls[0].request.body, 'utf-8')) @@ -5739,9 +5934,9 @@ def test_post_view_as_stream_all_params(self): assert req_body['attachments'] == False assert req_body['conflicts'] == False assert req_body['descending'] == False - assert req_body['include_docs'] == False + assert req_body['include_docs'] == True assert req_body['inclusive_end'] == True - assert req_body['limit'] == 0 + assert req_body['limit'] == 10 assert req_body['skip'] == 0 assert req_body['update_seq'] == False assert req_body['end_key'] == 'testString' @@ -5749,7 +5944,7 @@ def test_post_view_as_stream_all_params(self): assert req_body['group'] == False assert req_body['group_level'] == 1 assert req_body['key'] == 'testString' - assert req_body['keys'] == ['testString'] + assert req_body['keys'] == ['examplekey'] assert req_body['reduce'] == True assert req_body['stable'] == False assert req_body['start_key'] == 'testString' @@ -5779,11 +5974,13 @@ def test_post_view_as_stream_value_error(self): # Set up mock url = preprocess_url('/testString/_design/testString/_view/testString') mock_response = '{"foo": "this is a mock response for JSON streaming"}' - responses.add(responses.POST, - url, - body=mock_response, - content_type='application/json', - status=200) + responses.add( + responses.POST, + url, + body=mock_response, + content_type='application/json', + status=200, + ) # Set up parameter values db = 'testString' @@ -5793,9 +5990,9 @@ def test_post_view_as_stream_value_error(self): attachments = False conflicts = False descending = False - include_docs = False + include_docs = True inclusive_end = True - limit = 0 + limit = 10 skip = 0 update_seq = False end_key = 'testString' @@ -5803,7 +6000,7 @@ def test_post_view_as_stream_value_error(self): group = False group_level = 1 key = 'testString' - keys = ['testString'] + keys = ['examplekey'] reduce = True stable = False start_key = 'testString' @@ -5817,7 +6014,7 @@ def test_post_view_as_stream_value_error(self): "view": view, } for param in req_param_dict.keys(): - req_copy = {key:val if key is not param else None for (key,val) in req_param_dict.items()} + req_copy = {key: val if key is not param else None for (key, val) in req_param_dict.items()} with pytest.raises(ValueError): _service.post_view_as_stream(**req_copy) @@ -5830,7 +6027,8 @@ def test_post_view_as_stream_value_error_with_retries(self): _service.disable_retries() self.test_post_view_as_stream_value_error() -class TestPostViewQueries(): + +class TestPostViewQueries: """ Test Class for post_view_queries """ @@ -5842,12 +6040,14 @@ def test_post_view_queries_all_params(self): """ # Set up mock url = preprocess_url('/testString/_design/testString/_view/testString/queries') - mock_response = '{"results": [{"total_rows": 0, "update_seq": "update_seq", "rows": [{"caused_by": "caused_by", "error": "error", "reason": "reason", "doc": {"_attachments": {"mapKey": {"content_type": "content_type", "data": "VGhpcyBpcyBhbiBlbmNvZGVkIGJ5dGUgYXJyYXku", "digest": "digest", "encoded_length": 0, "encoding": "encoding", "follows": false, "length": 0, "revpos": 1, "stub": true}}, "_conflicts": ["conflicts"], "_deleted": false, "_deleted_conflicts": ["deleted_conflicts"], "_id": "id", "_local_seq": "local_seq", "_rev": "rev", "_revisions": {"ids": ["ids"], "start": 1}, "_revs_info": [{"rev": "rev", "status": "available"}]}, "id": "id", "key": "anyValue", "value": "anyValue"}]}]}' - responses.add(responses.POST, - url, - body=mock_response, - content_type='application/json', - status=200) + mock_response = '{"results": [{"total_rows": 0, "update_seq": "update_seq", "rows": [{"caused_by": "caused_by", "error": "error", "reason": "reason", "ref": 0, "doc": {"_attachments": {"mapKey": {"content_type": "content_type", "data": "VGhpcyBpcyBhIG1vY2sgYnl0ZSBhcnJheSB2YWx1ZS4=", "digest": "digest", "encoded_length": 0, "encoding": "encoding", "follows": false, "length": 0, "revpos": 1, "stub": true}}, "_conflicts": ["conflicts"], "_deleted": true, "_deleted_conflicts": ["deleted_conflicts"], "_id": "_id", "_local_seq": "_local_seq", "_rev": "_rev", "_revisions": {"ids": ["ids"], "start": 1}, "_revs_info": [{"rev": "rev", "status": "available"}]}, "id": "id", "key": "anyValue", "value": "anyValue"}]}]}' + responses.add( + responses.POST, + url, + body=mock_response, + content_type='application/json', + status=200, + ) # Construct a dict representation of a ViewQuery model view_query_model = {} @@ -5855,9 +6055,9 @@ def test_post_view_queries_all_params(self): view_query_model['attachments'] = False view_query_model['conflicts'] = False view_query_model['descending'] = False - view_query_model['include_docs'] = False + view_query_model['include_docs'] = True view_query_model['inclusive_end'] = True - view_query_model['limit'] = 0 + view_query_model['limit'] = 5 view_query_model['skip'] = 0 view_query_model['update_seq'] = False view_query_model['end_key'] = 'testString' @@ -5884,14 +6084,15 @@ def test_post_view_queries_all_params(self): ddoc, view, queries, - headers={} + headers={}, ) # Check for correct operation assert len(responses.calls) == 1 assert response.status_code == 200 - # decompress gzip compressed request body - responses.calls[0].request.body = gzip.decompress(responses.calls[0].request.body) + # Decompress gzip compressed request body. + raw_body = responses.calls[0].request.body + responses.calls[0].request.body = gzip.decompress(raw_body.read() if isinstance(raw_body, io.IOBase) else raw_body) # Validate body params req_body = json.loads(str(responses.calls[0].request.body, 'utf-8')) @@ -5913,12 +6114,14 @@ def test_post_view_queries_value_error(self): """ # Set up mock url = preprocess_url('/testString/_design/testString/_view/testString/queries') - mock_response = '{"results": [{"total_rows": 0, "update_seq": "update_seq", "rows": [{"caused_by": "caused_by", "error": "error", "reason": "reason", "doc": {"_attachments": {"mapKey": {"content_type": "content_type", "data": "VGhpcyBpcyBhbiBlbmNvZGVkIGJ5dGUgYXJyYXku", "digest": "digest", "encoded_length": 0, "encoding": "encoding", "follows": false, "length": 0, "revpos": 1, "stub": true}}, "_conflicts": ["conflicts"], "_deleted": false, "_deleted_conflicts": ["deleted_conflicts"], "_id": "id", "_local_seq": "local_seq", "_rev": "rev", "_revisions": {"ids": ["ids"], "start": 1}, "_revs_info": [{"rev": "rev", "status": "available"}]}, "id": "id", "key": "anyValue", "value": "anyValue"}]}]}' - responses.add(responses.POST, - url, - body=mock_response, - content_type='application/json', - status=200) + mock_response = '{"results": [{"total_rows": 0, "update_seq": "update_seq", "rows": [{"caused_by": "caused_by", "error": "error", "reason": "reason", "ref": 0, "doc": {"_attachments": {"mapKey": {"content_type": "content_type", "data": "VGhpcyBpcyBhIG1vY2sgYnl0ZSBhcnJheSB2YWx1ZS4=", "digest": "digest", "encoded_length": 0, "encoding": "encoding", "follows": false, "length": 0, "revpos": 1, "stub": true}}, "_conflicts": ["conflicts"], "_deleted": true, "_deleted_conflicts": ["deleted_conflicts"], "_id": "_id", "_local_seq": "_local_seq", "_rev": "_rev", "_revisions": {"ids": ["ids"], "start": 1}, "_revs_info": [{"rev": "rev", "status": "available"}]}, "id": "id", "key": "anyValue", "value": "anyValue"}]}]}' + responses.add( + responses.POST, + url, + body=mock_response, + content_type='application/json', + status=200, + ) # Construct a dict representation of a ViewQuery model view_query_model = {} @@ -5926,9 +6129,9 @@ def test_post_view_queries_value_error(self): view_query_model['attachments'] = False view_query_model['conflicts'] = False view_query_model['descending'] = False - view_query_model['include_docs'] = False + view_query_model['include_docs'] = True view_query_model['inclusive_end'] = True - view_query_model['limit'] = 0 + view_query_model['limit'] = 5 view_query_model['skip'] = 0 view_query_model['update_seq'] = False view_query_model['end_key'] = 'testString' @@ -5957,7 +6160,7 @@ def test_post_view_queries_value_error(self): "queries": queries, } for param in req_param_dict.keys(): - req_copy = {key:val if key is not param else None for (key,val) in req_param_dict.items()} + req_copy = {key: val if key is not param else None for (key, val) in req_param_dict.items()} with pytest.raises(ValueError): _service.post_view_queries(**req_copy) @@ -5970,7 +6173,8 @@ def test_post_view_queries_value_error_with_retries(self): _service.disable_retries() self.test_post_view_queries_value_error() -class TestPostViewQueriesAsStream(): + +class TestPostViewQueriesAsStream: """ Test Class for post_view_queries_as_stream """ @@ -5983,11 +6187,13 @@ def test_post_view_queries_as_stream_all_params(self): # Set up mock url = preprocess_url('/testString/_design/testString/_view/testString/queries') mock_response = '{"foo": "this is a mock response for JSON streaming"}' - responses.add(responses.POST, - url, - body=mock_response, - content_type='application/json', - status=200) + responses.add( + responses.POST, + url, + body=mock_response, + content_type='application/json', + status=200, + ) # Construct a dict representation of a ViewQuery model view_query_model = {} @@ -5995,9 +6201,9 @@ def test_post_view_queries_as_stream_all_params(self): view_query_model['attachments'] = False view_query_model['conflicts'] = False view_query_model['descending'] = False - view_query_model['include_docs'] = False + view_query_model['include_docs'] = True view_query_model['inclusive_end'] = True - view_query_model['limit'] = 0 + view_query_model['limit'] = 5 view_query_model['skip'] = 0 view_query_model['update_seq'] = False view_query_model['end_key'] = 'testString' @@ -6024,14 +6230,15 @@ def test_post_view_queries_as_stream_all_params(self): ddoc, view, queries, - headers={} + headers={}, ) # Check for correct operation assert len(responses.calls) == 1 assert response.status_code == 200 - # decompress gzip compressed request body - responses.calls[0].request.body = gzip.decompress(responses.calls[0].request.body) + # Decompress gzip compressed request body. + raw_body = responses.calls[0].request.body + responses.calls[0].request.body = gzip.decompress(raw_body.read() if isinstance(raw_body, io.IOBase) else raw_body) # Validate body params req_body = json.loads(str(responses.calls[0].request.body, 'utf-8')) @@ -6060,11 +6267,13 @@ def test_post_view_queries_as_stream_value_error(self): # Set up mock url = preprocess_url('/testString/_design/testString/_view/testString/queries') mock_response = '{"foo": "this is a mock response for JSON streaming"}' - responses.add(responses.POST, - url, - body=mock_response, - content_type='application/json', - status=200) + responses.add( + responses.POST, + url, + body=mock_response, + content_type='application/json', + status=200, + ) # Construct a dict representation of a ViewQuery model view_query_model = {} @@ -6072,9 +6281,9 @@ def test_post_view_queries_as_stream_value_error(self): view_query_model['attachments'] = False view_query_model['conflicts'] = False view_query_model['descending'] = False - view_query_model['include_docs'] = False + view_query_model['include_docs'] = True view_query_model['inclusive_end'] = True - view_query_model['limit'] = 0 + view_query_model['limit'] = 5 view_query_model['skip'] = 0 view_query_model['update_seq'] = False view_query_model['end_key'] = 'testString' @@ -6103,7 +6312,7 @@ def test_post_view_queries_as_stream_value_error(self): "queries": queries, } for param in req_param_dict.keys(): - req_copy = {key:val if key is not param else None for (key,val) in req_param_dict.items()} + req_copy = {key: val if key is not param else None for (key, val) in req_param_dict.items()} with pytest.raises(ValueError): _service.post_view_queries_as_stream(**req_copy) @@ -6116,6 +6325,7 @@ def test_post_view_queries_as_stream_value_error_with_retries(self): _service.disable_retries() self.test_post_view_queries_as_stream_value_error() + # endregion ############################################################################## # End of Service: Views @@ -6126,7 +6336,8 @@ def test_post_view_queries_as_stream_value_error_with_retries(self): ############################################################################## # region -class TestNewInstance(): + +class TestNewInstance: """ Test Class for new_instance """ @@ -6153,7 +6364,8 @@ def test_new_instance_without_authenticator(self): service_name='TEST_SERVICE_NOT_FOUND', ) -class TestGetPartitionInformation(): + +class TestGetPartitionInformation: """ Test Class for get_partition_information """ @@ -6166,11 +6378,13 @@ def test_get_partition_information_all_params(self): # Set up mock url = preprocess_url('/testString/_partition/testString') mock_response = '{"db_name": "db_name", "doc_count": 0, "doc_del_count": 0, "partition": "partition", "partitioned_indexes": {"count": 0, "indexes": {"search": 0, "view": 0}, "limit": 0}, "sizes": {"active": 0, "external": 0}}' - responses.add(responses.GET, - url, - body=mock_response, - content_type='application/json', - status=200) + responses.add( + responses.GET, + url, + body=mock_response, + content_type='application/json', + status=200, + ) # Set up parameter values db = 'testString' @@ -6180,7 +6394,7 @@ def test_get_partition_information_all_params(self): response = _service.get_partition_information( db, partition_key, - headers={} + headers={}, ) # Check for correct operation @@ -6204,11 +6418,13 @@ def test_get_partition_information_value_error(self): # Set up mock url = preprocess_url('/testString/_partition/testString') mock_response = '{"db_name": "db_name", "doc_count": 0, "doc_del_count": 0, "partition": "partition", "partitioned_indexes": {"count": 0, "indexes": {"search": 0, "view": 0}, "limit": 0}, "sizes": {"active": 0, "external": 0}}' - responses.add(responses.GET, - url, - body=mock_response, - content_type='application/json', - status=200) + responses.add( + responses.GET, + url, + body=mock_response, + content_type='application/json', + status=200, + ) # Set up parameter values db = 'testString' @@ -6220,7 +6436,7 @@ def test_get_partition_information_value_error(self): "partition_key": partition_key, } for param in req_param_dict.keys(): - req_copy = {key:val if key is not param else None for (key,val) in req_param_dict.items()} + req_copy = {key: val if key is not param else None for (key, val) in req_param_dict.items()} with pytest.raises(ValueError): _service.get_partition_information(**req_copy) @@ -6233,7 +6449,8 @@ def test_get_partition_information_value_error_with_retries(self): _service.disable_retries() self.test_get_partition_information_value_error() -class TestPostPartitionAllDocs(): + +class TestPostPartitionAllDocs: """ Test Class for post_partition_all_docs """ @@ -6245,12 +6462,14 @@ def test_post_partition_all_docs_all_params(self): """ # Set up mock url = preprocess_url('/testString/_partition/testString/_all_docs') - mock_response = '{"total_rows": 0, "rows": [{"caused_by": "caused_by", "error": "error", "reason": "reason", "doc": {"_attachments": {"mapKey": {"content_type": "content_type", "data": "VGhpcyBpcyBhbiBlbmNvZGVkIGJ5dGUgYXJyYXku", "digest": "digest", "encoded_length": 0, "encoding": "encoding", "follows": false, "length": 0, "revpos": 1, "stub": true}}, "_conflicts": ["conflicts"], "_deleted": false, "_deleted_conflicts": ["deleted_conflicts"], "_id": "id", "_local_seq": "local_seq", "_rev": "rev", "_revisions": {"ids": ["ids"], "start": 1}, "_revs_info": [{"rev": "rev", "status": "available"}]}, "id": "id", "key": "key", "value": {"rev": "rev"}}], "update_seq": "update_seq"}' - responses.add(responses.POST, - url, - body=mock_response, - content_type='application/json', - status=200) + mock_response = '{"total_rows": 0, "rows": [{"caused_by": "caused_by", "error": "error", "reason": "reason", "ref": 0, "doc": {"_attachments": {"mapKey": {"content_type": "content_type", "data": "VGhpcyBpcyBhIG1vY2sgYnl0ZSBhcnJheSB2YWx1ZS4=", "digest": "digest", "encoded_length": 0, "encoding": "encoding", "follows": false, "length": 0, "revpos": 1, "stub": true}}, "_conflicts": ["conflicts"], "_deleted": true, "_deleted_conflicts": ["deleted_conflicts"], "_id": "_id", "_local_seq": "_local_seq", "_rev": "_rev", "_revisions": {"ids": ["ids"], "start": 1}, "_revs_info": [{"rev": "rev", "status": "available"}]}, "id": "id", "key": "key", "value": {"deleted": false, "rev": "rev"}}], "update_seq": "update_seq"}' + responses.add( + responses.POST, + url, + body=mock_response, + content_type='application/json', + status=200, + ) # Set up parameter values db = 'testString' @@ -6261,13 +6480,13 @@ def test_post_partition_all_docs_all_params(self): descending = False include_docs = False inclusive_end = True - limit = 0 + limit = 10 skip = 0 update_seq = False end_key = 'testString' key = 'testString' keys = ['testString'] - start_key = 'testString' + start_key = '0007741142412418284' # Invoke method response = _service.post_partition_all_docs( @@ -6286,14 +6505,15 @@ def test_post_partition_all_docs_all_params(self): key=key, keys=keys, start_key=start_key, - headers={} + headers={}, ) # Check for correct operation assert len(responses.calls) == 1 assert response.status_code == 200 - # decompress gzip compressed request body - responses.calls[0].request.body = gzip.decompress(responses.calls[0].request.body) + # Decompress gzip compressed request body. + raw_body = responses.calls[0].request.body + responses.calls[0].request.body = gzip.decompress(raw_body.read() if isinstance(raw_body, io.IOBase) else raw_body) # Validate body params req_body = json.loads(str(responses.calls[0].request.body, 'utf-8')) @@ -6303,13 +6523,13 @@ def test_post_partition_all_docs_all_params(self): assert req_body['descending'] == False assert req_body['include_docs'] == False assert req_body['inclusive_end'] == True - assert req_body['limit'] == 0 + assert req_body['limit'] == 10 assert req_body['skip'] == 0 assert req_body['update_seq'] == False assert req_body['end_key'] == 'testString' assert req_body['key'] == 'testString' assert req_body['keys'] == ['testString'] - assert req_body['start_key'] == 'testString' + assert req_body['start_key'] == '0007741142412418284' def test_post_partition_all_docs_all_params_with_retries(self): # Enable retries and run test_post_partition_all_docs_all_params. @@ -6327,12 +6547,14 @@ def test_post_partition_all_docs_value_error(self): """ # Set up mock url = preprocess_url('/testString/_partition/testString/_all_docs') - mock_response = '{"total_rows": 0, "rows": [{"caused_by": "caused_by", "error": "error", "reason": "reason", "doc": {"_attachments": {"mapKey": {"content_type": "content_type", "data": "VGhpcyBpcyBhbiBlbmNvZGVkIGJ5dGUgYXJyYXku", "digest": "digest", "encoded_length": 0, "encoding": "encoding", "follows": false, "length": 0, "revpos": 1, "stub": true}}, "_conflicts": ["conflicts"], "_deleted": false, "_deleted_conflicts": ["deleted_conflicts"], "_id": "id", "_local_seq": "local_seq", "_rev": "rev", "_revisions": {"ids": ["ids"], "start": 1}, "_revs_info": [{"rev": "rev", "status": "available"}]}, "id": "id", "key": "key", "value": {"rev": "rev"}}], "update_seq": "update_seq"}' - responses.add(responses.POST, - url, - body=mock_response, - content_type='application/json', - status=200) + mock_response = '{"total_rows": 0, "rows": [{"caused_by": "caused_by", "error": "error", "reason": "reason", "ref": 0, "doc": {"_attachments": {"mapKey": {"content_type": "content_type", "data": "VGhpcyBpcyBhIG1vY2sgYnl0ZSBhcnJheSB2YWx1ZS4=", "digest": "digest", "encoded_length": 0, "encoding": "encoding", "follows": false, "length": 0, "revpos": 1, "stub": true}}, "_conflicts": ["conflicts"], "_deleted": true, "_deleted_conflicts": ["deleted_conflicts"], "_id": "_id", "_local_seq": "_local_seq", "_rev": "_rev", "_revisions": {"ids": ["ids"], "start": 1}, "_revs_info": [{"rev": "rev", "status": "available"}]}, "id": "id", "key": "key", "value": {"deleted": false, "rev": "rev"}}], "update_seq": "update_seq"}' + responses.add( + responses.POST, + url, + body=mock_response, + content_type='application/json', + status=200, + ) # Set up parameter values db = 'testString' @@ -6343,13 +6565,13 @@ def test_post_partition_all_docs_value_error(self): descending = False include_docs = False inclusive_end = True - limit = 0 + limit = 10 skip = 0 update_seq = False end_key = 'testString' key = 'testString' keys = ['testString'] - start_key = 'testString' + start_key = '0007741142412418284' # Pass in all but one required param and check for a ValueError req_param_dict = { @@ -6357,7 +6579,7 @@ def test_post_partition_all_docs_value_error(self): "partition_key": partition_key, } for param in req_param_dict.keys(): - req_copy = {key:val if key is not param else None for (key,val) in req_param_dict.items()} + req_copy = {key: val if key is not param else None for (key, val) in req_param_dict.items()} with pytest.raises(ValueError): _service.post_partition_all_docs(**req_copy) @@ -6370,7 +6592,8 @@ def test_post_partition_all_docs_value_error_with_retries(self): _service.disable_retries() self.test_post_partition_all_docs_value_error() -class TestPostPartitionAllDocsAsStream(): + +class TestPostPartitionAllDocsAsStream: """ Test Class for post_partition_all_docs_as_stream """ @@ -6383,11 +6606,13 @@ def test_post_partition_all_docs_as_stream_all_params(self): # Set up mock url = preprocess_url('/testString/_partition/testString/_all_docs') mock_response = '{"foo": "this is a mock response for JSON streaming"}' - responses.add(responses.POST, - url, - body=mock_response, - content_type='application/json', - status=200) + responses.add( + responses.POST, + url, + body=mock_response, + content_type='application/json', + status=200, + ) # Set up parameter values db = 'testString' @@ -6398,13 +6623,13 @@ def test_post_partition_all_docs_as_stream_all_params(self): descending = False include_docs = False inclusive_end = True - limit = 0 + limit = 10 skip = 0 update_seq = False end_key = 'testString' key = 'testString' keys = ['testString'] - start_key = 'testString' + start_key = '0007741142412418284' # Invoke method response = _service.post_partition_all_docs_as_stream( @@ -6423,14 +6648,15 @@ def test_post_partition_all_docs_as_stream_all_params(self): key=key, keys=keys, start_key=start_key, - headers={} + headers={}, ) # Check for correct operation assert len(responses.calls) == 1 assert response.status_code == 200 - # decompress gzip compressed request body - responses.calls[0].request.body = gzip.decompress(responses.calls[0].request.body) + # Decompress gzip compressed request body. + raw_body = responses.calls[0].request.body + responses.calls[0].request.body = gzip.decompress(raw_body.read() if isinstance(raw_body, io.IOBase) else raw_body) # Validate body params req_body = json.loads(str(responses.calls[0].request.body, 'utf-8')) @@ -6440,13 +6666,13 @@ def test_post_partition_all_docs_as_stream_all_params(self): assert req_body['descending'] == False assert req_body['include_docs'] == False assert req_body['inclusive_end'] == True - assert req_body['limit'] == 0 + assert req_body['limit'] == 10 assert req_body['skip'] == 0 assert req_body['update_seq'] == False assert req_body['end_key'] == 'testString' assert req_body['key'] == 'testString' assert req_body['keys'] == ['testString'] - assert req_body['start_key'] == 'testString' + assert req_body['start_key'] == '0007741142412418284' # Verify streamed JSON response result = response.get_result() @@ -6471,11 +6697,13 @@ def test_post_partition_all_docs_as_stream_value_error(self): # Set up mock url = preprocess_url('/testString/_partition/testString/_all_docs') mock_response = '{"foo": "this is a mock response for JSON streaming"}' - responses.add(responses.POST, - url, - body=mock_response, - content_type='application/json', - status=200) + responses.add( + responses.POST, + url, + body=mock_response, + content_type='application/json', + status=200, + ) # Set up parameter values db = 'testString' @@ -6486,13 +6714,13 @@ def test_post_partition_all_docs_as_stream_value_error(self): descending = False include_docs = False inclusive_end = True - limit = 0 + limit = 10 skip = 0 update_seq = False end_key = 'testString' key = 'testString' keys = ['testString'] - start_key = 'testString' + start_key = '0007741142412418284' # Pass in all but one required param and check for a ValueError req_param_dict = { @@ -6500,7 +6728,7 @@ def test_post_partition_all_docs_as_stream_value_error(self): "partition_key": partition_key, } for param in req_param_dict.keys(): - req_copy = {key:val if key is not param else None for (key,val) in req_param_dict.items()} + req_copy = {key: val if key is not param else None for (key, val) in req_param_dict.items()} with pytest.raises(ValueError): _service.post_partition_all_docs_as_stream(**req_copy) @@ -6513,7 +6741,8 @@ def test_post_partition_all_docs_as_stream_value_error_with_retries(self): _service.disable_retries() self.test_post_partition_all_docs_as_stream_value_error() -class TestPostPartitionSearch(): + +class TestPostPartitionSearch: """ Test Class for post_partition_search """ @@ -6525,28 +6754,30 @@ def test_post_partition_search_all_params(self): """ # Set up mock url = preprocess_url('/testString/_partition/testString/_design/testString/_search/testString') - mock_response = '{"total_rows": 0, "bookmark": "bookmark", "by": "by", "counts": {"mapKey": {"mapKey": 0}}, "ranges": {"mapKey": {"mapKey": 0}}, "rows": [{"doc": {"_attachments": {"mapKey": {"content_type": "content_type", "data": "VGhpcyBpcyBhbiBlbmNvZGVkIGJ5dGUgYXJyYXku", "digest": "digest", "encoded_length": 0, "encoding": "encoding", "follows": false, "length": 0, "revpos": 1, "stub": true}}, "_conflicts": ["conflicts"], "_deleted": false, "_deleted_conflicts": ["deleted_conflicts"], "_id": "id", "_local_seq": "local_seq", "_rev": "rev", "_revisions": {"ids": ["ids"], "start": 1}, "_revs_info": [{"rev": "rev", "status": "available"}]}, "fields": {"mapKey": "anyValue"}, "highlights": {"mapKey": ["inner"]}, "id": "id"}], "groups": [{"total_rows": 0, "bookmark": "bookmark", "by": "by", "counts": {"mapKey": {"mapKey": 0}}, "ranges": {"mapKey": {"mapKey": 0}}, "rows": [{"doc": {"_attachments": {"mapKey": {"content_type": "content_type", "data": "VGhpcyBpcyBhbiBlbmNvZGVkIGJ5dGUgYXJyYXku", "digest": "digest", "encoded_length": 0, "encoding": "encoding", "follows": false, "length": 0, "revpos": 1, "stub": true}}, "_conflicts": ["conflicts"], "_deleted": false, "_deleted_conflicts": ["deleted_conflicts"], "_id": "id", "_local_seq": "local_seq", "_rev": "rev", "_revisions": {"ids": ["ids"], "start": 1}, "_revs_info": [{"rev": "rev", "status": "available"}]}, "fields": {"mapKey": "anyValue"}, "highlights": {"mapKey": ["inner"]}, "id": "id"}]}]}' - responses.add(responses.POST, - url, - body=mock_response, - content_type='application/json', - status=200) + mock_response = '{"total_rows": 0, "bookmark": "bookmark", "by": "by", "counts": {"mapKey": {"mapKey": 0}}, "ranges": {"mapKey": {"mapKey": 0}}, "rows": [{"doc": {"_attachments": {"mapKey": {"content_type": "content_type", "data": "VGhpcyBpcyBhIG1vY2sgYnl0ZSBhcnJheSB2YWx1ZS4=", "digest": "digest", "encoded_length": 0, "encoding": "encoding", "follows": false, "length": 0, "revpos": 1, "stub": true}}, "_conflicts": ["conflicts"], "_deleted": true, "_deleted_conflicts": ["deleted_conflicts"], "_id": "_id", "_local_seq": "_local_seq", "_rev": "_rev", "_revisions": {"ids": ["ids"], "start": 1}, "_revs_info": [{"rev": "rev", "status": "available"}]}, "fields": {"anyKey": "anyValue"}, "highlights": {"mapKey": ["inner"]}, "id": "id"}], "groups": [{"total_rows": 0, "bookmark": "bookmark", "by": "by", "counts": {"mapKey": {"mapKey": 0}}, "ranges": {"mapKey": {"mapKey": 0}}, "rows": [{"doc": {"_attachments": {"mapKey": {"content_type": "content_type", "data": "VGhpcyBpcyBhIG1vY2sgYnl0ZSBhcnJheSB2YWx1ZS4=", "digest": "digest", "encoded_length": 0, "encoding": "encoding", "follows": false, "length": 0, "revpos": 1, "stub": true}}, "_conflicts": ["conflicts"], "_deleted": true, "_deleted_conflicts": ["deleted_conflicts"], "_id": "_id", "_local_seq": "_local_seq", "_rev": "_rev", "_revisions": {"ids": ["ids"], "start": 1}, "_revs_info": [{"rev": "rev", "status": "available"}]}, "fields": {"anyKey": "anyValue"}, "highlights": {"mapKey": ["inner"]}, "id": "id"}]}]}' + responses.add( + responses.POST, + url, + body=mock_response, + content_type='application/json', + status=200, + ) # Set up parameter values db = 'testString' partition_key = 'testString' ddoc = 'testString' index = 'testString' - query = 'testString' + query = 'name:Jane* AND active:True' bookmark = 'testString' highlight_fields = ['testString'] highlight_number = 1 highlight_post_tag = '' highlight_pre_tag = '' - highlight_size = 1 + highlight_size = 100 include_docs = False include_fields = ['testString'] - limit = 0 + limit = 3 sort = ['testString'] stale = 'ok' @@ -6568,27 +6799,28 @@ def test_post_partition_search_all_params(self): limit=limit, sort=sort, stale=stale, - headers={} + headers={}, ) # Check for correct operation assert len(responses.calls) == 1 assert response.status_code == 200 - # decompress gzip compressed request body - responses.calls[0].request.body = gzip.decompress(responses.calls[0].request.body) + # Decompress gzip compressed request body. + raw_body = responses.calls[0].request.body + responses.calls[0].request.body = gzip.decompress(raw_body.read() if isinstance(raw_body, io.IOBase) else raw_body) # Validate body params req_body = json.loads(str(responses.calls[0].request.body, 'utf-8')) - assert req_body['query'] == 'testString' + assert req_body['query'] == 'name:Jane* AND active:True' assert req_body['bookmark'] == 'testString' assert req_body['highlight_fields'] == ['testString'] assert req_body['highlight_number'] == 1 assert req_body['highlight_post_tag'] == '' assert req_body['highlight_pre_tag'] == '' - assert req_body['highlight_size'] == 1 + assert req_body['highlight_size'] == 100 assert req_body['include_docs'] == False assert req_body['include_fields'] == ['testString'] - assert req_body['limit'] == 0 + assert req_body['limit'] == 3 assert req_body['sort'] == ['testString'] assert req_body['stale'] == 'ok' @@ -6608,28 +6840,30 @@ def test_post_partition_search_value_error(self): """ # Set up mock url = preprocess_url('/testString/_partition/testString/_design/testString/_search/testString') - mock_response = '{"total_rows": 0, "bookmark": "bookmark", "by": "by", "counts": {"mapKey": {"mapKey": 0}}, "ranges": {"mapKey": {"mapKey": 0}}, "rows": [{"doc": {"_attachments": {"mapKey": {"content_type": "content_type", "data": "VGhpcyBpcyBhbiBlbmNvZGVkIGJ5dGUgYXJyYXku", "digest": "digest", "encoded_length": 0, "encoding": "encoding", "follows": false, "length": 0, "revpos": 1, "stub": true}}, "_conflicts": ["conflicts"], "_deleted": false, "_deleted_conflicts": ["deleted_conflicts"], "_id": "id", "_local_seq": "local_seq", "_rev": "rev", "_revisions": {"ids": ["ids"], "start": 1}, "_revs_info": [{"rev": "rev", "status": "available"}]}, "fields": {"mapKey": "anyValue"}, "highlights": {"mapKey": ["inner"]}, "id": "id"}], "groups": [{"total_rows": 0, "bookmark": "bookmark", "by": "by", "counts": {"mapKey": {"mapKey": 0}}, "ranges": {"mapKey": {"mapKey": 0}}, "rows": [{"doc": {"_attachments": {"mapKey": {"content_type": "content_type", "data": "VGhpcyBpcyBhbiBlbmNvZGVkIGJ5dGUgYXJyYXku", "digest": "digest", "encoded_length": 0, "encoding": "encoding", "follows": false, "length": 0, "revpos": 1, "stub": true}}, "_conflicts": ["conflicts"], "_deleted": false, "_deleted_conflicts": ["deleted_conflicts"], "_id": "id", "_local_seq": "local_seq", "_rev": "rev", "_revisions": {"ids": ["ids"], "start": 1}, "_revs_info": [{"rev": "rev", "status": "available"}]}, "fields": {"mapKey": "anyValue"}, "highlights": {"mapKey": ["inner"]}, "id": "id"}]}]}' - responses.add(responses.POST, - url, - body=mock_response, - content_type='application/json', - status=200) + mock_response = '{"total_rows": 0, "bookmark": "bookmark", "by": "by", "counts": {"mapKey": {"mapKey": 0}}, "ranges": {"mapKey": {"mapKey": 0}}, "rows": [{"doc": {"_attachments": {"mapKey": {"content_type": "content_type", "data": "VGhpcyBpcyBhIG1vY2sgYnl0ZSBhcnJheSB2YWx1ZS4=", "digest": "digest", "encoded_length": 0, "encoding": "encoding", "follows": false, "length": 0, "revpos": 1, "stub": true}}, "_conflicts": ["conflicts"], "_deleted": true, "_deleted_conflicts": ["deleted_conflicts"], "_id": "_id", "_local_seq": "_local_seq", "_rev": "_rev", "_revisions": {"ids": ["ids"], "start": 1}, "_revs_info": [{"rev": "rev", "status": "available"}]}, "fields": {"anyKey": "anyValue"}, "highlights": {"mapKey": ["inner"]}, "id": "id"}], "groups": [{"total_rows": 0, "bookmark": "bookmark", "by": "by", "counts": {"mapKey": {"mapKey": 0}}, "ranges": {"mapKey": {"mapKey": 0}}, "rows": [{"doc": {"_attachments": {"mapKey": {"content_type": "content_type", "data": "VGhpcyBpcyBhIG1vY2sgYnl0ZSBhcnJheSB2YWx1ZS4=", "digest": "digest", "encoded_length": 0, "encoding": "encoding", "follows": false, "length": 0, "revpos": 1, "stub": true}}, "_conflicts": ["conflicts"], "_deleted": true, "_deleted_conflicts": ["deleted_conflicts"], "_id": "_id", "_local_seq": "_local_seq", "_rev": "_rev", "_revisions": {"ids": ["ids"], "start": 1}, "_revs_info": [{"rev": "rev", "status": "available"}]}, "fields": {"anyKey": "anyValue"}, "highlights": {"mapKey": ["inner"]}, "id": "id"}]}]}' + responses.add( + responses.POST, + url, + body=mock_response, + content_type='application/json', + status=200, + ) # Set up parameter values db = 'testString' partition_key = 'testString' ddoc = 'testString' index = 'testString' - query = 'testString' + query = 'name:Jane* AND active:True' bookmark = 'testString' highlight_fields = ['testString'] highlight_number = 1 highlight_post_tag = '' highlight_pre_tag = '' - highlight_size = 1 + highlight_size = 100 include_docs = False include_fields = ['testString'] - limit = 0 + limit = 3 sort = ['testString'] stale = 'ok' @@ -6642,7 +6876,7 @@ def test_post_partition_search_value_error(self): "query": query, } for param in req_param_dict.keys(): - req_copy = {key:val if key is not param else None for (key,val) in req_param_dict.items()} + req_copy = {key: val if key is not param else None for (key, val) in req_param_dict.items()} with pytest.raises(ValueError): _service.post_partition_search(**req_copy) @@ -6655,7 +6889,8 @@ def test_post_partition_search_value_error_with_retries(self): _service.disable_retries() self.test_post_partition_search_value_error() -class TestPostPartitionSearchAsStream(): + +class TestPostPartitionSearchAsStream: """ Test Class for post_partition_search_as_stream """ @@ -6668,27 +6903,29 @@ def test_post_partition_search_as_stream_all_params(self): # Set up mock url = preprocess_url('/testString/_partition/testString/_design/testString/_search/testString') mock_response = '{"foo": "this is a mock response for JSON streaming"}' - responses.add(responses.POST, - url, - body=mock_response, - content_type='application/json', - status=200) + responses.add( + responses.POST, + url, + body=mock_response, + content_type='application/json', + status=200, + ) # Set up parameter values db = 'testString' partition_key = 'testString' ddoc = 'testString' index = 'testString' - query = 'testString' + query = 'name:Jane* AND active:True' bookmark = 'testString' highlight_fields = ['testString'] highlight_number = 1 highlight_post_tag = '' highlight_pre_tag = '' - highlight_size = 1 + highlight_size = 100 include_docs = False include_fields = ['testString'] - limit = 0 + limit = 3 sort = ['testString'] stale = 'ok' @@ -6710,27 +6947,28 @@ def test_post_partition_search_as_stream_all_params(self): limit=limit, sort=sort, stale=stale, - headers={} + headers={}, ) # Check for correct operation assert len(responses.calls) == 1 assert response.status_code == 200 - # decompress gzip compressed request body - responses.calls[0].request.body = gzip.decompress(responses.calls[0].request.body) + # Decompress gzip compressed request body. + raw_body = responses.calls[0].request.body + responses.calls[0].request.body = gzip.decompress(raw_body.read() if isinstance(raw_body, io.IOBase) else raw_body) # Validate body params req_body = json.loads(str(responses.calls[0].request.body, 'utf-8')) - assert req_body['query'] == 'testString' + assert req_body['query'] == 'name:Jane* AND active:True' assert req_body['bookmark'] == 'testString' assert req_body['highlight_fields'] == ['testString'] assert req_body['highlight_number'] == 1 assert req_body['highlight_post_tag'] == '' assert req_body['highlight_pre_tag'] == '' - assert req_body['highlight_size'] == 1 + assert req_body['highlight_size'] == 100 assert req_body['include_docs'] == False assert req_body['include_fields'] == ['testString'] - assert req_body['limit'] == 0 + assert req_body['limit'] == 3 assert req_body['sort'] == ['testString'] assert req_body['stale'] == 'ok' @@ -6757,27 +6995,29 @@ def test_post_partition_search_as_stream_value_error(self): # Set up mock url = preprocess_url('/testString/_partition/testString/_design/testString/_search/testString') mock_response = '{"foo": "this is a mock response for JSON streaming"}' - responses.add(responses.POST, - url, - body=mock_response, - content_type='application/json', - status=200) + responses.add( + responses.POST, + url, + body=mock_response, + content_type='application/json', + status=200, + ) # Set up parameter values db = 'testString' partition_key = 'testString' ddoc = 'testString' index = 'testString' - query = 'testString' + query = 'name:Jane* AND active:True' bookmark = 'testString' highlight_fields = ['testString'] highlight_number = 1 highlight_post_tag = '' highlight_pre_tag = '' - highlight_size = 1 + highlight_size = 100 include_docs = False include_fields = ['testString'] - limit = 0 + limit = 3 sort = ['testString'] stale = 'ok' @@ -6790,7 +7030,7 @@ def test_post_partition_search_as_stream_value_error(self): "query": query, } for param in req_param_dict.keys(): - req_copy = {key:val if key is not param else None for (key,val) in req_param_dict.items()} + req_copy = {key: val if key is not param else None for (key, val) in req_param_dict.items()} with pytest.raises(ValueError): _service.post_partition_search_as_stream(**req_copy) @@ -6803,7 +7043,8 @@ def test_post_partition_search_as_stream_value_error_with_retries(self): _service.disable_retries() self.test_post_partition_search_as_stream_value_error() -class TestPostPartitionView(): + +class TestPostPartitionView: """ Test Class for post_partition_view """ @@ -6815,12 +7056,14 @@ def test_post_partition_view_all_params(self): """ # Set up mock url = preprocess_url('/testString/_partition/testString/_design/testString/_view/testString') - mock_response = '{"total_rows": 0, "update_seq": "update_seq", "rows": [{"caused_by": "caused_by", "error": "error", "reason": "reason", "doc": {"_attachments": {"mapKey": {"content_type": "content_type", "data": "VGhpcyBpcyBhbiBlbmNvZGVkIGJ5dGUgYXJyYXku", "digest": "digest", "encoded_length": 0, "encoding": "encoding", "follows": false, "length": 0, "revpos": 1, "stub": true}}, "_conflicts": ["conflicts"], "_deleted": false, "_deleted_conflicts": ["deleted_conflicts"], "_id": "id", "_local_seq": "local_seq", "_rev": "rev", "_revisions": {"ids": ["ids"], "start": 1}, "_revs_info": [{"rev": "rev", "status": "available"}]}, "id": "id", "key": "anyValue", "value": "anyValue"}]}' - responses.add(responses.POST, - url, - body=mock_response, - content_type='application/json', - status=200) + mock_response = '{"total_rows": 0, "update_seq": "update_seq", "rows": [{"caused_by": "caused_by", "error": "error", "reason": "reason", "ref": 0, "doc": {"_attachments": {"mapKey": {"content_type": "content_type", "data": "VGhpcyBpcyBhIG1vY2sgYnl0ZSBhcnJheSB2YWx1ZS4=", "digest": "digest", "encoded_length": 0, "encoding": "encoding", "follows": false, "length": 0, "revpos": 1, "stub": true}}, "_conflicts": ["conflicts"], "_deleted": true, "_deleted_conflicts": ["deleted_conflicts"], "_id": "_id", "_local_seq": "_local_seq", "_rev": "_rev", "_revisions": {"ids": ["ids"], "start": 1}, "_revs_info": [{"rev": "rev", "status": "available"}]}, "id": "id", "key": "anyValue", "value": "anyValue"}]}' + responses.add( + responses.POST, + url, + body=mock_response, + content_type='application/json', + status=200, + ) # Set up parameter values db = 'testString' @@ -6831,9 +7074,9 @@ def test_post_partition_view_all_params(self): attachments = False conflicts = False descending = False - include_docs = False + include_docs = True inclusive_end = True - limit = 0 + limit = 10 skip = 0 update_seq = False end_key = 'testString' @@ -6841,9 +7084,8 @@ def test_post_partition_view_all_params(self): group = False group_level = 1 key = 'testString' - keys = ['testString'] + keys = ['examplekey'] reduce = True - stable = False start_key = 'testString' start_key_doc_id = 'testString' update = 'true' @@ -6870,18 +7112,18 @@ def test_post_partition_view_all_params(self): key=key, keys=keys, reduce=reduce, - stable=stable, start_key=start_key, start_key_doc_id=start_key_doc_id, update=update, - headers={} + headers={}, ) # Check for correct operation assert len(responses.calls) == 1 assert response.status_code == 200 - # decompress gzip compressed request body - responses.calls[0].request.body = gzip.decompress(responses.calls[0].request.body) + # Decompress gzip compressed request body. + raw_body = responses.calls[0].request.body + responses.calls[0].request.body = gzip.decompress(raw_body.read() if isinstance(raw_body, io.IOBase) else raw_body) # Validate body params req_body = json.loads(str(responses.calls[0].request.body, 'utf-8')) @@ -6889,9 +7131,9 @@ def test_post_partition_view_all_params(self): assert req_body['attachments'] == False assert req_body['conflicts'] == False assert req_body['descending'] == False - assert req_body['include_docs'] == False + assert req_body['include_docs'] == True assert req_body['inclusive_end'] == True - assert req_body['limit'] == 0 + assert req_body['limit'] == 10 assert req_body['skip'] == 0 assert req_body['update_seq'] == False assert req_body['end_key'] == 'testString' @@ -6899,9 +7141,8 @@ def test_post_partition_view_all_params(self): assert req_body['group'] == False assert req_body['group_level'] == 1 assert req_body['key'] == 'testString' - assert req_body['keys'] == ['testString'] + assert req_body['keys'] == ['examplekey'] assert req_body['reduce'] == True - assert req_body['stable'] == False assert req_body['start_key'] == 'testString' assert req_body['start_key_doc_id'] == 'testString' assert req_body['update'] == 'true' @@ -6922,12 +7163,14 @@ def test_post_partition_view_value_error(self): """ # Set up mock url = preprocess_url('/testString/_partition/testString/_design/testString/_view/testString') - mock_response = '{"total_rows": 0, "update_seq": "update_seq", "rows": [{"caused_by": "caused_by", "error": "error", "reason": "reason", "doc": {"_attachments": {"mapKey": {"content_type": "content_type", "data": "VGhpcyBpcyBhbiBlbmNvZGVkIGJ5dGUgYXJyYXku", "digest": "digest", "encoded_length": 0, "encoding": "encoding", "follows": false, "length": 0, "revpos": 1, "stub": true}}, "_conflicts": ["conflicts"], "_deleted": false, "_deleted_conflicts": ["deleted_conflicts"], "_id": "id", "_local_seq": "local_seq", "_rev": "rev", "_revisions": {"ids": ["ids"], "start": 1}, "_revs_info": [{"rev": "rev", "status": "available"}]}, "id": "id", "key": "anyValue", "value": "anyValue"}]}' - responses.add(responses.POST, - url, - body=mock_response, - content_type='application/json', - status=200) + mock_response = '{"total_rows": 0, "update_seq": "update_seq", "rows": [{"caused_by": "caused_by", "error": "error", "reason": "reason", "ref": 0, "doc": {"_attachments": {"mapKey": {"content_type": "content_type", "data": "VGhpcyBpcyBhIG1vY2sgYnl0ZSBhcnJheSB2YWx1ZS4=", "digest": "digest", "encoded_length": 0, "encoding": "encoding", "follows": false, "length": 0, "revpos": 1, "stub": true}}, "_conflicts": ["conflicts"], "_deleted": true, "_deleted_conflicts": ["deleted_conflicts"], "_id": "_id", "_local_seq": "_local_seq", "_rev": "_rev", "_revisions": {"ids": ["ids"], "start": 1}, "_revs_info": [{"rev": "rev", "status": "available"}]}, "id": "id", "key": "anyValue", "value": "anyValue"}]}' + responses.add( + responses.POST, + url, + body=mock_response, + content_type='application/json', + status=200, + ) # Set up parameter values db = 'testString' @@ -6938,9 +7181,9 @@ def test_post_partition_view_value_error(self): attachments = False conflicts = False descending = False - include_docs = False + include_docs = True inclusive_end = True - limit = 0 + limit = 10 skip = 0 update_seq = False end_key = 'testString' @@ -6948,9 +7191,8 @@ def test_post_partition_view_value_error(self): group = False group_level = 1 key = 'testString' - keys = ['testString'] + keys = ['examplekey'] reduce = True - stable = False start_key = 'testString' start_key_doc_id = 'testString' update = 'true' @@ -6963,7 +7205,7 @@ def test_post_partition_view_value_error(self): "view": view, } for param in req_param_dict.keys(): - req_copy = {key:val if key is not param else None for (key,val) in req_param_dict.items()} + req_copy = {key: val if key is not param else None for (key, val) in req_param_dict.items()} with pytest.raises(ValueError): _service.post_partition_view(**req_copy) @@ -6976,7 +7218,8 @@ def test_post_partition_view_value_error_with_retries(self): _service.disable_retries() self.test_post_partition_view_value_error() -class TestPostPartitionViewAsStream(): + +class TestPostPartitionViewAsStream: """ Test Class for post_partition_view_as_stream """ @@ -6989,11 +7232,13 @@ def test_post_partition_view_as_stream_all_params(self): # Set up mock url = preprocess_url('/testString/_partition/testString/_design/testString/_view/testString') mock_response = '{"foo": "this is a mock response for JSON streaming"}' - responses.add(responses.POST, - url, - body=mock_response, - content_type='application/json', - status=200) + responses.add( + responses.POST, + url, + body=mock_response, + content_type='application/json', + status=200, + ) # Set up parameter values db = 'testString' @@ -7004,9 +7249,9 @@ def test_post_partition_view_as_stream_all_params(self): attachments = False conflicts = False descending = False - include_docs = False + include_docs = True inclusive_end = True - limit = 0 + limit = 10 skip = 0 update_seq = False end_key = 'testString' @@ -7014,9 +7259,8 @@ def test_post_partition_view_as_stream_all_params(self): group = False group_level = 1 key = 'testString' - keys = ['testString'] + keys = ['examplekey'] reduce = True - stable = False start_key = 'testString' start_key_doc_id = 'testString' update = 'true' @@ -7043,18 +7287,18 @@ def test_post_partition_view_as_stream_all_params(self): key=key, keys=keys, reduce=reduce, - stable=stable, start_key=start_key, start_key_doc_id=start_key_doc_id, update=update, - headers={} + headers={}, ) # Check for correct operation assert len(responses.calls) == 1 assert response.status_code == 200 - # decompress gzip compressed request body - responses.calls[0].request.body = gzip.decompress(responses.calls[0].request.body) + # Decompress gzip compressed request body. + raw_body = responses.calls[0].request.body + responses.calls[0].request.body = gzip.decompress(raw_body.read() if isinstance(raw_body, io.IOBase) else raw_body) # Validate body params req_body = json.loads(str(responses.calls[0].request.body, 'utf-8')) @@ -7062,9 +7306,9 @@ def test_post_partition_view_as_stream_all_params(self): assert req_body['attachments'] == False assert req_body['conflicts'] == False assert req_body['descending'] == False - assert req_body['include_docs'] == False + assert req_body['include_docs'] == True assert req_body['inclusive_end'] == True - assert req_body['limit'] == 0 + assert req_body['limit'] == 10 assert req_body['skip'] == 0 assert req_body['update_seq'] == False assert req_body['end_key'] == 'testString' @@ -7072,9 +7316,8 @@ def test_post_partition_view_as_stream_all_params(self): assert req_body['group'] == False assert req_body['group_level'] == 1 assert req_body['key'] == 'testString' - assert req_body['keys'] == ['testString'] + assert req_body['keys'] == ['examplekey'] assert req_body['reduce'] == True - assert req_body['stable'] == False assert req_body['start_key'] == 'testString' assert req_body['start_key_doc_id'] == 'testString' assert req_body['update'] == 'true' @@ -7102,11 +7345,13 @@ def test_post_partition_view_as_stream_value_error(self): # Set up mock url = preprocess_url('/testString/_partition/testString/_design/testString/_view/testString') mock_response = '{"foo": "this is a mock response for JSON streaming"}' - responses.add(responses.POST, - url, - body=mock_response, - content_type='application/json', - status=200) + responses.add( + responses.POST, + url, + body=mock_response, + content_type='application/json', + status=200, + ) # Set up parameter values db = 'testString' @@ -7117,9 +7362,9 @@ def test_post_partition_view_as_stream_value_error(self): attachments = False conflicts = False descending = False - include_docs = False + include_docs = True inclusive_end = True - limit = 0 + limit = 10 skip = 0 update_seq = False end_key = 'testString' @@ -7127,9 +7372,8 @@ def test_post_partition_view_as_stream_value_error(self): group = False group_level = 1 key = 'testString' - keys = ['testString'] + keys = ['examplekey'] reduce = True - stable = False start_key = 'testString' start_key_doc_id = 'testString' update = 'true' @@ -7142,7 +7386,7 @@ def test_post_partition_view_as_stream_value_error(self): "view": view, } for param in req_param_dict.keys(): - req_copy = {key:val if key is not param else None for (key,val) in req_param_dict.items()} + req_copy = {key: val if key is not param else None for (key, val) in req_param_dict.items()} with pytest.raises(ValueError): _service.post_partition_view_as_stream(**req_copy) @@ -7155,34 +7399,178 @@ def test_post_partition_view_as_stream_value_error_with_retries(self): _service.disable_retries() self.test_post_partition_view_as_stream_value_error() -class TestPostPartitionFind(): + +class TestPostPartitionExplain: """ - Test Class for post_partition_find + Test Class for post_partition_explain """ @responses.activate - def test_post_partition_find_all_params(self): + def test_post_partition_explain_all_params(self): """ - post_partition_find() + post_partition_explain() """ # Set up mock - url = preprocess_url('/testString/_partition/testString/_find') - mock_response = '{"bookmark": "bookmark", "docs": [{"_attachments": {"mapKey": {"content_type": "content_type", "data": "VGhpcyBpcyBhbiBlbmNvZGVkIGJ5dGUgYXJyYXku", "digest": "digest", "encoded_length": 0, "encoding": "encoding", "follows": false, "length": 0, "revpos": 1, "stub": true}}, "_conflicts": ["conflicts"], "_deleted": false, "_deleted_conflicts": ["deleted_conflicts"], "_id": "id", "_local_seq": "local_seq", "_rev": "rev", "_revisions": {"ids": ["ids"], "start": 1}, "_revs_info": [{"rev": "rev", "status": "available"}]}], "execution_stats": {"execution_time_ms": 17, "results_returned": 0, "total_docs_examined": 0, "total_keys_examined": 0, "total_quorum_docs_examined": 0}, "warning": "warning"}' - responses.add(responses.POST, - url, - body=mock_response, - content_type='application/json', - status=200) + url = preprocess_url('/testString/_partition/testString/_explain') + mock_response = '{"covering": true, "dbname": "dbname", "fields": ["fields"], "index": {"ddoc": "ddoc", "def": {"default_analyzer": {"name": "classic", "stopwords": ["stopwords"]}, "default_field": {"analyzer": {"name": "classic", "stopwords": ["stopwords"]}, "enabled": true}, "fields": [{"name": "name", "type": "boolean"}], "index_array_lengths": true, "partial_filter_selector": {"anyKey": "anyValue"}}, "name": "name", "partitioned": false, "type": "json"}, "index_candidates": [{"analysis": {"covering": true, "ranking": 1, "reasons": [{"name": "alphabetically_comes_after"}], "usable": true}, "index": {"ddoc": "ddoc", "def": {"default_analyzer": {"name": "classic", "stopwords": ["stopwords"]}, "default_field": {"analyzer": {"name": "classic", "stopwords": ["stopwords"]}, "enabled": true}, "fields": [{"name": "name", "type": "boolean"}], "index_array_lengths": true, "partial_filter_selector": {"anyKey": "anyValue"}}, "name": "name", "partitioned": false, "type": "json"}}], "limit": 25, "mrargs": {"conflicts": "anyValue", "direction": "fwd", "end_key": "anyValue", "include_docs": true, "partition": "partition", "reduce": true, "stable": true, "start_key": "anyValue", "update": "anyValue", "view_type": "map"}, "opts": {"bookmark": "bookmark", "conflicts": false, "execution_stats": false, "fields": ["fields"], "limit": 25, "partition": "partition", "r": 1, "skip": 0, "sort": "anyValue", "stable": false, "stale": false, "update": true, "use_index": ["use_index"]}, "partitioned": "anyValue", "selector": {"anyKey": "anyValue"}, "selector_hints": [{"indexable_fields": ["indexable_fields"], "type": "json", "unindexable_fields": ["unindexable_fields"]}], "skip": 0}' + responses.add( + responses.POST, + url, + body=mock_response, + content_type='application/json', + status=200, + ) # Set up parameter values db = 'testString' partition_key = 'testString' - selector = {'key1': 'testString'} + selector = {'type': {'$eq': 'product'}} + allow_fallback = True bookmark = 'testString' conflicts = True execution_stats = True - fields = ['testString'] - limit = 0 + fields = ['productId', 'name', 'description'] + limit = 25 + skip = 0 + sort = [{'key1': 'asc'}] + stable = True + update = 'true' + use_index = ['testString'] + + # Invoke method + response = _service.post_partition_explain( + db, + partition_key, + selector, + allow_fallback=allow_fallback, + bookmark=bookmark, + conflicts=conflicts, + execution_stats=execution_stats, + fields=fields, + limit=limit, + skip=skip, + sort=sort, + stable=stable, + update=update, + use_index=use_index, + headers={}, + ) + + # Check for correct operation + assert len(responses.calls) == 1 + assert response.status_code == 200 + # Decompress gzip compressed request body. + raw_body = responses.calls[0].request.body + responses.calls[0].request.body = gzip.decompress(raw_body.read() if isinstance(raw_body, io.IOBase) else raw_body) + + # Validate body params + req_body = json.loads(str(responses.calls[0].request.body, 'utf-8')) + assert req_body['selector'] == {'type': {'$eq': 'product'}} + assert req_body['allow_fallback'] == True + assert req_body['bookmark'] == 'testString' + assert req_body['conflicts'] == True + assert req_body['execution_stats'] == True + assert req_body['fields'] == ['productId', 'name', 'description'] + assert req_body['limit'] == 25 + assert req_body['skip'] == 0 + assert req_body['sort'] == [{'key1': 'asc'}] + assert req_body['stable'] == True + assert req_body['update'] == 'true' + assert req_body['use_index'] == ['testString'] + + def test_post_partition_explain_all_params_with_retries(self): + # Enable retries and run test_post_partition_explain_all_params. + _service.enable_retries() + self.test_post_partition_explain_all_params() + + # Disable retries and run test_post_partition_explain_all_params. + _service.disable_retries() + self.test_post_partition_explain_all_params() + + @responses.activate + def test_post_partition_explain_value_error(self): + """ + test_post_partition_explain_value_error() + """ + # Set up mock + url = preprocess_url('/testString/_partition/testString/_explain') + mock_response = '{"covering": true, "dbname": "dbname", "fields": ["fields"], "index": {"ddoc": "ddoc", "def": {"default_analyzer": {"name": "classic", "stopwords": ["stopwords"]}, "default_field": {"analyzer": {"name": "classic", "stopwords": ["stopwords"]}, "enabled": true}, "fields": [{"name": "name", "type": "boolean"}], "index_array_lengths": true, "partial_filter_selector": {"anyKey": "anyValue"}}, "name": "name", "partitioned": false, "type": "json"}, "index_candidates": [{"analysis": {"covering": true, "ranking": 1, "reasons": [{"name": "alphabetically_comes_after"}], "usable": true}, "index": {"ddoc": "ddoc", "def": {"default_analyzer": {"name": "classic", "stopwords": ["stopwords"]}, "default_field": {"analyzer": {"name": "classic", "stopwords": ["stopwords"]}, "enabled": true}, "fields": [{"name": "name", "type": "boolean"}], "index_array_lengths": true, "partial_filter_selector": {"anyKey": "anyValue"}}, "name": "name", "partitioned": false, "type": "json"}}], "limit": 25, "mrargs": {"conflicts": "anyValue", "direction": "fwd", "end_key": "anyValue", "include_docs": true, "partition": "partition", "reduce": true, "stable": true, "start_key": "anyValue", "update": "anyValue", "view_type": "map"}, "opts": {"bookmark": "bookmark", "conflicts": false, "execution_stats": false, "fields": ["fields"], "limit": 25, "partition": "partition", "r": 1, "skip": 0, "sort": "anyValue", "stable": false, "stale": false, "update": true, "use_index": ["use_index"]}, "partitioned": "anyValue", "selector": {"anyKey": "anyValue"}, "selector_hints": [{"indexable_fields": ["indexable_fields"], "type": "json", "unindexable_fields": ["unindexable_fields"]}], "skip": 0}' + responses.add( + responses.POST, + url, + body=mock_response, + content_type='application/json', + status=200, + ) + + # Set up parameter values + db = 'testString' + partition_key = 'testString' + selector = {'type': {'$eq': 'product'}} + allow_fallback = True + bookmark = 'testString' + conflicts = True + execution_stats = True + fields = ['productId', 'name', 'description'] + limit = 25 + skip = 0 + sort = [{'key1': 'asc'}] + stable = True + update = 'true' + use_index = ['testString'] + + # Pass in all but one required param and check for a ValueError + req_param_dict = { + "db": db, + "partition_key": partition_key, + "selector": selector, + } + for param in req_param_dict.keys(): + req_copy = {key: val if key is not param else None for (key, val) in req_param_dict.items()} + with pytest.raises(ValueError): + _service.post_partition_explain(**req_copy) + + def test_post_partition_explain_value_error_with_retries(self): + # Enable retries and run test_post_partition_explain_value_error. + _service.enable_retries() + self.test_post_partition_explain_value_error() + + # Disable retries and run test_post_partition_explain_value_error. + _service.disable_retries() + self.test_post_partition_explain_value_error() + + +class TestPostPartitionFind: + """ + Test Class for post_partition_find + """ + + @responses.activate + def test_post_partition_find_all_params(self): + """ + post_partition_find() + """ + # Set up mock + url = preprocess_url('/testString/_partition/testString/_find') + mock_response = '{"bookmark": "bookmark", "docs": [{"_attachments": {"mapKey": {"content_type": "content_type", "data": "VGhpcyBpcyBhIG1vY2sgYnl0ZSBhcnJheSB2YWx1ZS4=", "digest": "digest", "encoded_length": 0, "encoding": "encoding", "follows": false, "length": 0, "revpos": 1, "stub": true}}, "_conflicts": ["conflicts"], "_deleted": true, "_deleted_conflicts": ["deleted_conflicts"], "_id": "_id", "_local_seq": "_local_seq", "_rev": "_rev", "_revisions": {"ids": ["ids"], "start": 1}, "_revs_info": [{"rev": "rev", "status": "available"}]}], "execution_stats": {"execution_time_ms": 17, "results_returned": 0, "total_docs_examined": 0, "total_keys_examined": 0, "total_quorum_docs_examined": 0}, "warning": "warning"}' + responses.add( + responses.POST, + url, + body=mock_response, + content_type='application/json', + status=200, + ) + + # Set up parameter values + db = 'testString' + partition_key = 'testString' + selector = {'type': {'$eq': 'product'}} + allow_fallback = True + bookmark = 'testString' + conflicts = True + execution_stats = True + fields = ['productId', 'name', 'description'] + limit = 25 skip = 0 sort = [{'key1': 'asc'}] stable = True @@ -7194,6 +7582,7 @@ def test_post_partition_find_all_params(self): db, partition_key, selector, + allow_fallback=allow_fallback, bookmark=bookmark, conflicts=conflicts, execution_stats=execution_stats, @@ -7204,23 +7593,25 @@ def test_post_partition_find_all_params(self): stable=stable, update=update, use_index=use_index, - headers={} + headers={}, ) # Check for correct operation assert len(responses.calls) == 1 assert response.status_code == 200 - # decompress gzip compressed request body - responses.calls[0].request.body = gzip.decompress(responses.calls[0].request.body) + # Decompress gzip compressed request body. + raw_body = responses.calls[0].request.body + responses.calls[0].request.body = gzip.decompress(raw_body.read() if isinstance(raw_body, io.IOBase) else raw_body) # Validate body params req_body = json.loads(str(responses.calls[0].request.body, 'utf-8')) - assert req_body['selector'] == {'key1': 'testString'} + assert req_body['selector'] == {'type': {'$eq': 'product'}} + assert req_body['allow_fallback'] == True assert req_body['bookmark'] == 'testString' assert req_body['conflicts'] == True assert req_body['execution_stats'] == True - assert req_body['fields'] == ['testString'] - assert req_body['limit'] == 0 + assert req_body['fields'] == ['productId', 'name', 'description'] + assert req_body['limit'] == 25 assert req_body['skip'] == 0 assert req_body['sort'] == [{'key1': 'asc'}] assert req_body['stable'] == True @@ -7243,22 +7634,25 @@ def test_post_partition_find_value_error(self): """ # Set up mock url = preprocess_url('/testString/_partition/testString/_find') - mock_response = '{"bookmark": "bookmark", "docs": [{"_attachments": {"mapKey": {"content_type": "content_type", "data": "VGhpcyBpcyBhbiBlbmNvZGVkIGJ5dGUgYXJyYXku", "digest": "digest", "encoded_length": 0, "encoding": "encoding", "follows": false, "length": 0, "revpos": 1, "stub": true}}, "_conflicts": ["conflicts"], "_deleted": false, "_deleted_conflicts": ["deleted_conflicts"], "_id": "id", "_local_seq": "local_seq", "_rev": "rev", "_revisions": {"ids": ["ids"], "start": 1}, "_revs_info": [{"rev": "rev", "status": "available"}]}], "execution_stats": {"execution_time_ms": 17, "results_returned": 0, "total_docs_examined": 0, "total_keys_examined": 0, "total_quorum_docs_examined": 0}, "warning": "warning"}' - responses.add(responses.POST, - url, - body=mock_response, - content_type='application/json', - status=200) + mock_response = '{"bookmark": "bookmark", "docs": [{"_attachments": {"mapKey": {"content_type": "content_type", "data": "VGhpcyBpcyBhIG1vY2sgYnl0ZSBhcnJheSB2YWx1ZS4=", "digest": "digest", "encoded_length": 0, "encoding": "encoding", "follows": false, "length": 0, "revpos": 1, "stub": true}}, "_conflicts": ["conflicts"], "_deleted": true, "_deleted_conflicts": ["deleted_conflicts"], "_id": "_id", "_local_seq": "_local_seq", "_rev": "_rev", "_revisions": {"ids": ["ids"], "start": 1}, "_revs_info": [{"rev": "rev", "status": "available"}]}], "execution_stats": {"execution_time_ms": 17, "results_returned": 0, "total_docs_examined": 0, "total_keys_examined": 0, "total_quorum_docs_examined": 0}, "warning": "warning"}' + responses.add( + responses.POST, + url, + body=mock_response, + content_type='application/json', + status=200, + ) # Set up parameter values db = 'testString' partition_key = 'testString' - selector = {'key1': 'testString'} + selector = {'type': {'$eq': 'product'}} + allow_fallback = True bookmark = 'testString' conflicts = True execution_stats = True - fields = ['testString'] - limit = 0 + fields = ['productId', 'name', 'description'] + limit = 25 skip = 0 sort = [{'key1': 'asc'}] stable = True @@ -7272,7 +7666,7 @@ def test_post_partition_find_value_error(self): "selector": selector, } for param in req_param_dict.keys(): - req_copy = {key:val if key is not param else None for (key,val) in req_param_dict.items()} + req_copy = {key: val if key is not param else None for (key, val) in req_param_dict.items()} with pytest.raises(ValueError): _service.post_partition_find(**req_copy) @@ -7285,7 +7679,8 @@ def test_post_partition_find_value_error_with_retries(self): _service.disable_retries() self.test_post_partition_find_value_error() -class TestPostPartitionFindAsStream(): + +class TestPostPartitionFindAsStream: """ Test Class for post_partition_find_as_stream """ @@ -7298,21 +7693,24 @@ def test_post_partition_find_as_stream_all_params(self): # Set up mock url = preprocess_url('/testString/_partition/testString/_find') mock_response = '{"foo": "this is a mock response for JSON streaming"}' - responses.add(responses.POST, - url, - body=mock_response, - content_type='application/json', - status=200) + responses.add( + responses.POST, + url, + body=mock_response, + content_type='application/json', + status=200, + ) # Set up parameter values db = 'testString' partition_key = 'testString' - selector = {'key1': 'testString'} + selector = {'type': {'$eq': 'product'}} + allow_fallback = True bookmark = 'testString' conflicts = True execution_stats = True - fields = ['testString'] - limit = 0 + fields = ['productId', 'name', 'description'] + limit = 25 skip = 0 sort = [{'key1': 'asc'}] stable = True @@ -7324,6 +7722,7 @@ def test_post_partition_find_as_stream_all_params(self): db, partition_key, selector, + allow_fallback=allow_fallback, bookmark=bookmark, conflicts=conflicts, execution_stats=execution_stats, @@ -7334,23 +7733,25 @@ def test_post_partition_find_as_stream_all_params(self): stable=stable, update=update, use_index=use_index, - headers={} + headers={}, ) # Check for correct operation assert len(responses.calls) == 1 assert response.status_code == 200 - # decompress gzip compressed request body - responses.calls[0].request.body = gzip.decompress(responses.calls[0].request.body) + # Decompress gzip compressed request body. + raw_body = responses.calls[0].request.body + responses.calls[0].request.body = gzip.decompress(raw_body.read() if isinstance(raw_body, io.IOBase) else raw_body) # Validate body params req_body = json.loads(str(responses.calls[0].request.body, 'utf-8')) - assert req_body['selector'] == {'key1': 'testString'} + assert req_body['selector'] == {'type': {'$eq': 'product'}} + assert req_body['allow_fallback'] == True assert req_body['bookmark'] == 'testString' assert req_body['conflicts'] == True assert req_body['execution_stats'] == True - assert req_body['fields'] == ['testString'] - assert req_body['limit'] == 0 + assert req_body['fields'] == ['productId', 'name', 'description'] + assert req_body['limit'] == 25 assert req_body['skip'] == 0 assert req_body['sort'] == [{'key1': 'asc'}] assert req_body['stable'] == True @@ -7380,21 +7781,24 @@ def test_post_partition_find_as_stream_value_error(self): # Set up mock url = preprocess_url('/testString/_partition/testString/_find') mock_response = '{"foo": "this is a mock response for JSON streaming"}' - responses.add(responses.POST, - url, - body=mock_response, - content_type='application/json', - status=200) + responses.add( + responses.POST, + url, + body=mock_response, + content_type='application/json', + status=200, + ) # Set up parameter values db = 'testString' partition_key = 'testString' - selector = {'key1': 'testString'} + selector = {'type': {'$eq': 'product'}} + allow_fallback = True bookmark = 'testString' conflicts = True execution_stats = True - fields = ['testString'] - limit = 0 + fields = ['productId', 'name', 'description'] + limit = 25 skip = 0 sort = [{'key1': 'asc'}] stable = True @@ -7408,7 +7812,7 @@ def test_post_partition_find_as_stream_value_error(self): "selector": selector, } for param in req_param_dict.keys(): - req_copy = {key:val if key is not param else None for (key,val) in req_param_dict.items()} + req_copy = {key: val if key is not param else None for (key, val) in req_param_dict.items()} with pytest.raises(ValueError): _service.post_partition_find_as_stream(**req_copy) @@ -7421,6 +7825,7 @@ def test_post_partition_find_as_stream_value_error_with_retries(self): _service.disable_retries() self.test_post_partition_find_as_stream_value_error() + # endregion ############################################################################## # End of Service: PartitionedDatabases @@ -7431,7 +7836,8 @@ def test_post_partition_find_as_stream_value_error_with_retries(self): ############################################################################## # region -class TestNewInstance(): + +class TestNewInstance: """ Test Class for new_instance """ @@ -7458,7 +7864,8 @@ def test_new_instance_without_authenticator(self): service_name='TEST_SERVICE_NOT_FOUND', ) -class TestPostExplain(): + +class TestPostExplain: """ Test Class for post_explain """ @@ -7470,21 +7877,24 @@ def test_post_explain_all_params(self): """ # Set up mock url = preprocess_url('/testString/_explain') - mock_response = '{"dbname": "dbname", "fields": ["fields"], "index": {"ddoc": "ddoc", "def": {"default_analyzer": {"name": "classic", "stopwords": ["stopwords"]}, "default_field": {"analyzer": {"name": "classic", "stopwords": ["stopwords"]}, "enabled": true}, "fields": [{"name": "name", "type": "boolean"}], "index_array_lengths": true, "partial_filter_selector": {"mapKey": "anyValue"}}, "name": "name", "type": "json"}, "limit": 0, "opts": {"mapKey": "anyValue"}, "range": {"end_key": ["anyValue"], "start_key": ["anyValue"]}, "selector": {"mapKey": "anyValue"}, "skip": 0}' - responses.add(responses.POST, - url, - body=mock_response, - content_type='application/json', - status=200) + mock_response = '{"covering": true, "dbname": "dbname", "fields": ["fields"], "index": {"ddoc": "ddoc", "def": {"default_analyzer": {"name": "classic", "stopwords": ["stopwords"]}, "default_field": {"analyzer": {"name": "classic", "stopwords": ["stopwords"]}, "enabled": true}, "fields": [{"name": "name", "type": "boolean"}], "index_array_lengths": true, "partial_filter_selector": {"anyKey": "anyValue"}}, "name": "name", "partitioned": false, "type": "json"}, "index_candidates": [{"analysis": {"covering": true, "ranking": 1, "reasons": [{"name": "alphabetically_comes_after"}], "usable": true}, "index": {"ddoc": "ddoc", "def": {"default_analyzer": {"name": "classic", "stopwords": ["stopwords"]}, "default_field": {"analyzer": {"name": "classic", "stopwords": ["stopwords"]}, "enabled": true}, "fields": [{"name": "name", "type": "boolean"}], "index_array_lengths": true, "partial_filter_selector": {"anyKey": "anyValue"}}, "name": "name", "partitioned": false, "type": "json"}}], "limit": 25, "mrargs": {"conflicts": "anyValue", "direction": "fwd", "end_key": "anyValue", "include_docs": true, "partition": "partition", "reduce": true, "stable": true, "start_key": "anyValue", "update": "anyValue", "view_type": "map"}, "opts": {"bookmark": "bookmark", "conflicts": false, "execution_stats": false, "fields": ["fields"], "limit": 25, "partition": "partition", "r": 1, "skip": 0, "sort": "anyValue", "stable": false, "stale": false, "update": true, "use_index": ["use_index"]}, "partitioned": "anyValue", "selector": {"anyKey": "anyValue"}, "selector_hints": [{"indexable_fields": ["indexable_fields"], "type": "json", "unindexable_fields": ["unindexable_fields"]}], "skip": 0}' + responses.add( + responses.POST, + url, + body=mock_response, + content_type='application/json', + status=200, + ) # Set up parameter values db = 'testString' - selector = {'key1': 'testString'} + selector = {'email_verified': {'$eq': True}} + allow_fallback = True bookmark = 'testString' conflicts = True execution_stats = True - fields = ['testString'] - limit = 0 + fields = ['_id', 'type', 'name', 'email'] + limit = 3 skip = 0 sort = [{'key1': 'asc'}] stable = True @@ -7496,6 +7906,7 @@ def test_post_explain_all_params(self): response = _service.post_explain( db, selector, + allow_fallback=allow_fallback, bookmark=bookmark, conflicts=conflicts, execution_stats=execution_stats, @@ -7507,23 +7918,25 @@ def test_post_explain_all_params(self): update=update, use_index=use_index, r=r, - headers={} + headers={}, ) # Check for correct operation assert len(responses.calls) == 1 assert response.status_code == 200 - # decompress gzip compressed request body - responses.calls[0].request.body = gzip.decompress(responses.calls[0].request.body) + # Decompress gzip compressed request body. + raw_body = responses.calls[0].request.body + responses.calls[0].request.body = gzip.decompress(raw_body.read() if isinstance(raw_body, io.IOBase) else raw_body) # Validate body params req_body = json.loads(str(responses.calls[0].request.body, 'utf-8')) - assert req_body['selector'] == {'key1': 'testString'} + assert req_body['selector'] == {'email_verified': {'$eq': True}} + assert req_body['allow_fallback'] == True assert req_body['bookmark'] == 'testString' assert req_body['conflicts'] == True assert req_body['execution_stats'] == True - assert req_body['fields'] == ['testString'] - assert req_body['limit'] == 0 + assert req_body['fields'] == ['_id', 'type', 'name', 'email'] + assert req_body['limit'] == 3 assert req_body['skip'] == 0 assert req_body['sort'] == [{'key1': 'asc'}] assert req_body['stable'] == True @@ -7547,21 +7960,24 @@ def test_post_explain_value_error(self): """ # Set up mock url = preprocess_url('/testString/_explain') - mock_response = '{"dbname": "dbname", "fields": ["fields"], "index": {"ddoc": "ddoc", "def": {"default_analyzer": {"name": "classic", "stopwords": ["stopwords"]}, "default_field": {"analyzer": {"name": "classic", "stopwords": ["stopwords"]}, "enabled": true}, "fields": [{"name": "name", "type": "boolean"}], "index_array_lengths": true, "partial_filter_selector": {"mapKey": "anyValue"}}, "name": "name", "type": "json"}, "limit": 0, "opts": {"mapKey": "anyValue"}, "range": {"end_key": ["anyValue"], "start_key": ["anyValue"]}, "selector": {"mapKey": "anyValue"}, "skip": 0}' - responses.add(responses.POST, - url, - body=mock_response, - content_type='application/json', - status=200) + mock_response = '{"covering": true, "dbname": "dbname", "fields": ["fields"], "index": {"ddoc": "ddoc", "def": {"default_analyzer": {"name": "classic", "stopwords": ["stopwords"]}, "default_field": {"analyzer": {"name": "classic", "stopwords": ["stopwords"]}, "enabled": true}, "fields": [{"name": "name", "type": "boolean"}], "index_array_lengths": true, "partial_filter_selector": {"anyKey": "anyValue"}}, "name": "name", "partitioned": false, "type": "json"}, "index_candidates": [{"analysis": {"covering": true, "ranking": 1, "reasons": [{"name": "alphabetically_comes_after"}], "usable": true}, "index": {"ddoc": "ddoc", "def": {"default_analyzer": {"name": "classic", "stopwords": ["stopwords"]}, "default_field": {"analyzer": {"name": "classic", "stopwords": ["stopwords"]}, "enabled": true}, "fields": [{"name": "name", "type": "boolean"}], "index_array_lengths": true, "partial_filter_selector": {"anyKey": "anyValue"}}, "name": "name", "partitioned": false, "type": "json"}}], "limit": 25, "mrargs": {"conflicts": "anyValue", "direction": "fwd", "end_key": "anyValue", "include_docs": true, "partition": "partition", "reduce": true, "stable": true, "start_key": "anyValue", "update": "anyValue", "view_type": "map"}, "opts": {"bookmark": "bookmark", "conflicts": false, "execution_stats": false, "fields": ["fields"], "limit": 25, "partition": "partition", "r": 1, "skip": 0, "sort": "anyValue", "stable": false, "stale": false, "update": true, "use_index": ["use_index"]}, "partitioned": "anyValue", "selector": {"anyKey": "anyValue"}, "selector_hints": [{"indexable_fields": ["indexable_fields"], "type": "json", "unindexable_fields": ["unindexable_fields"]}], "skip": 0}' + responses.add( + responses.POST, + url, + body=mock_response, + content_type='application/json', + status=200, + ) # Set up parameter values db = 'testString' - selector = {'key1': 'testString'} + selector = {'email_verified': {'$eq': True}} + allow_fallback = True bookmark = 'testString' conflicts = True execution_stats = True - fields = ['testString'] - limit = 0 + fields = ['_id', 'type', 'name', 'email'] + limit = 3 skip = 0 sort = [{'key1': 'asc'}] stable = True @@ -7575,7 +7991,7 @@ def test_post_explain_value_error(self): "selector": selector, } for param in req_param_dict.keys(): - req_copy = {key:val if key is not param else None for (key,val) in req_param_dict.items()} + req_copy = {key: val if key is not param else None for (key, val) in req_param_dict.items()} with pytest.raises(ValueError): _service.post_explain(**req_copy) @@ -7588,7 +8004,8 @@ def test_post_explain_value_error_with_retries(self): _service.disable_retries() self.test_post_explain_value_error() -class TestPostFind(): + +class TestPostFind: """ Test Class for post_find """ @@ -7600,21 +8017,24 @@ def test_post_find_all_params(self): """ # Set up mock url = preprocess_url('/testString/_find') - mock_response = '{"bookmark": "bookmark", "docs": [{"_attachments": {"mapKey": {"content_type": "content_type", "data": "VGhpcyBpcyBhbiBlbmNvZGVkIGJ5dGUgYXJyYXku", "digest": "digest", "encoded_length": 0, "encoding": "encoding", "follows": false, "length": 0, "revpos": 1, "stub": true}}, "_conflicts": ["conflicts"], "_deleted": false, "_deleted_conflicts": ["deleted_conflicts"], "_id": "id", "_local_seq": "local_seq", "_rev": "rev", "_revisions": {"ids": ["ids"], "start": 1}, "_revs_info": [{"rev": "rev", "status": "available"}]}], "execution_stats": {"execution_time_ms": 17, "results_returned": 0, "total_docs_examined": 0, "total_keys_examined": 0, "total_quorum_docs_examined": 0}, "warning": "warning"}' - responses.add(responses.POST, - url, - body=mock_response, - content_type='application/json', - status=200) + mock_response = '{"bookmark": "bookmark", "docs": [{"_attachments": {"mapKey": {"content_type": "content_type", "data": "VGhpcyBpcyBhIG1vY2sgYnl0ZSBhcnJheSB2YWx1ZS4=", "digest": "digest", "encoded_length": 0, "encoding": "encoding", "follows": false, "length": 0, "revpos": 1, "stub": true}}, "_conflicts": ["conflicts"], "_deleted": true, "_deleted_conflicts": ["deleted_conflicts"], "_id": "_id", "_local_seq": "_local_seq", "_rev": "_rev", "_revisions": {"ids": ["ids"], "start": 1}, "_revs_info": [{"rev": "rev", "status": "available"}]}], "execution_stats": {"execution_time_ms": 17, "results_returned": 0, "total_docs_examined": 0, "total_keys_examined": 0, "total_quorum_docs_examined": 0}, "warning": "warning"}' + responses.add( + responses.POST, + url, + body=mock_response, + content_type='application/json', + status=200, + ) # Set up parameter values db = 'testString' - selector = {'key1': 'testString'} + selector = {'email_verified': {'$eq': True}} + allow_fallback = True bookmark = 'testString' conflicts = True execution_stats = True - fields = ['testString'] - limit = 0 + fields = ['_id', 'type', 'name', 'email'] + limit = 3 skip = 0 sort = [{'key1': 'asc'}] stable = True @@ -7626,6 +8046,7 @@ def test_post_find_all_params(self): response = _service.post_find( db, selector, + allow_fallback=allow_fallback, bookmark=bookmark, conflicts=conflicts, execution_stats=execution_stats, @@ -7637,23 +8058,25 @@ def test_post_find_all_params(self): update=update, use_index=use_index, r=r, - headers={} + headers={}, ) # Check for correct operation assert len(responses.calls) == 1 assert response.status_code == 200 - # decompress gzip compressed request body - responses.calls[0].request.body = gzip.decompress(responses.calls[0].request.body) + # Decompress gzip compressed request body. + raw_body = responses.calls[0].request.body + responses.calls[0].request.body = gzip.decompress(raw_body.read() if isinstance(raw_body, io.IOBase) else raw_body) # Validate body params req_body = json.loads(str(responses.calls[0].request.body, 'utf-8')) - assert req_body['selector'] == {'key1': 'testString'} + assert req_body['selector'] == {'email_verified': {'$eq': True}} + assert req_body['allow_fallback'] == True assert req_body['bookmark'] == 'testString' assert req_body['conflicts'] == True assert req_body['execution_stats'] == True - assert req_body['fields'] == ['testString'] - assert req_body['limit'] == 0 + assert req_body['fields'] == ['_id', 'type', 'name', 'email'] + assert req_body['limit'] == 3 assert req_body['skip'] == 0 assert req_body['sort'] == [{'key1': 'asc'}] assert req_body['stable'] == True @@ -7677,21 +8100,24 @@ def test_post_find_value_error(self): """ # Set up mock url = preprocess_url('/testString/_find') - mock_response = '{"bookmark": "bookmark", "docs": [{"_attachments": {"mapKey": {"content_type": "content_type", "data": "VGhpcyBpcyBhbiBlbmNvZGVkIGJ5dGUgYXJyYXku", "digest": "digest", "encoded_length": 0, "encoding": "encoding", "follows": false, "length": 0, "revpos": 1, "stub": true}}, "_conflicts": ["conflicts"], "_deleted": false, "_deleted_conflicts": ["deleted_conflicts"], "_id": "id", "_local_seq": "local_seq", "_rev": "rev", "_revisions": {"ids": ["ids"], "start": 1}, "_revs_info": [{"rev": "rev", "status": "available"}]}], "execution_stats": {"execution_time_ms": 17, "results_returned": 0, "total_docs_examined": 0, "total_keys_examined": 0, "total_quorum_docs_examined": 0}, "warning": "warning"}' - responses.add(responses.POST, - url, - body=mock_response, - content_type='application/json', - status=200) + mock_response = '{"bookmark": "bookmark", "docs": [{"_attachments": {"mapKey": {"content_type": "content_type", "data": "VGhpcyBpcyBhIG1vY2sgYnl0ZSBhcnJheSB2YWx1ZS4=", "digest": "digest", "encoded_length": 0, "encoding": "encoding", "follows": false, "length": 0, "revpos": 1, "stub": true}}, "_conflicts": ["conflicts"], "_deleted": true, "_deleted_conflicts": ["deleted_conflicts"], "_id": "_id", "_local_seq": "_local_seq", "_rev": "_rev", "_revisions": {"ids": ["ids"], "start": 1}, "_revs_info": [{"rev": "rev", "status": "available"}]}], "execution_stats": {"execution_time_ms": 17, "results_returned": 0, "total_docs_examined": 0, "total_keys_examined": 0, "total_quorum_docs_examined": 0}, "warning": "warning"}' + responses.add( + responses.POST, + url, + body=mock_response, + content_type='application/json', + status=200, + ) # Set up parameter values db = 'testString' - selector = {'key1': 'testString'} + selector = {'email_verified': {'$eq': True}} + allow_fallback = True bookmark = 'testString' conflicts = True execution_stats = True - fields = ['testString'] - limit = 0 + fields = ['_id', 'type', 'name', 'email'] + limit = 3 skip = 0 sort = [{'key1': 'asc'}] stable = True @@ -7705,7 +8131,7 @@ def test_post_find_value_error(self): "selector": selector, } for param in req_param_dict.keys(): - req_copy = {key:val if key is not param else None for (key,val) in req_param_dict.items()} + req_copy = {key: val if key is not param else None for (key, val) in req_param_dict.items()} with pytest.raises(ValueError): _service.post_find(**req_copy) @@ -7718,7 +8144,8 @@ def test_post_find_value_error_with_retries(self): _service.disable_retries() self.test_post_find_value_error() -class TestPostFindAsStream(): + +class TestPostFindAsStream: """ Test Class for post_find_as_stream """ @@ -7731,20 +8158,23 @@ def test_post_find_as_stream_all_params(self): # Set up mock url = preprocess_url('/testString/_find') mock_response = '{"foo": "this is a mock response for JSON streaming"}' - responses.add(responses.POST, - url, - body=mock_response, - content_type='application/json', - status=200) + responses.add( + responses.POST, + url, + body=mock_response, + content_type='application/json', + status=200, + ) # Set up parameter values db = 'testString' - selector = {'key1': 'testString'} + selector = {'email_verified': {'$eq': True}} + allow_fallback = True bookmark = 'testString' conflicts = True execution_stats = True - fields = ['testString'] - limit = 0 + fields = ['_id', 'type', 'name', 'email'] + limit = 3 skip = 0 sort = [{'key1': 'asc'}] stable = True @@ -7756,6 +8186,7 @@ def test_post_find_as_stream_all_params(self): response = _service.post_find_as_stream( db, selector, + allow_fallback=allow_fallback, bookmark=bookmark, conflicts=conflicts, execution_stats=execution_stats, @@ -7767,23 +8198,25 @@ def test_post_find_as_stream_all_params(self): update=update, use_index=use_index, r=r, - headers={} + headers={}, ) # Check for correct operation assert len(responses.calls) == 1 assert response.status_code == 200 - # decompress gzip compressed request body - responses.calls[0].request.body = gzip.decompress(responses.calls[0].request.body) + # Decompress gzip compressed request body. + raw_body = responses.calls[0].request.body + responses.calls[0].request.body = gzip.decompress(raw_body.read() if isinstance(raw_body, io.IOBase) else raw_body) # Validate body params req_body = json.loads(str(responses.calls[0].request.body, 'utf-8')) - assert req_body['selector'] == {'key1': 'testString'} + assert req_body['selector'] == {'email_verified': {'$eq': True}} + assert req_body['allow_fallback'] == True assert req_body['bookmark'] == 'testString' assert req_body['conflicts'] == True assert req_body['execution_stats'] == True - assert req_body['fields'] == ['testString'] - assert req_body['limit'] == 0 + assert req_body['fields'] == ['_id', 'type', 'name', 'email'] + assert req_body['limit'] == 3 assert req_body['skip'] == 0 assert req_body['sort'] == [{'key1': 'asc'}] assert req_body['stable'] == True @@ -7814,20 +8247,23 @@ def test_post_find_as_stream_value_error(self): # Set up mock url = preprocess_url('/testString/_find') mock_response = '{"foo": "this is a mock response for JSON streaming"}' - responses.add(responses.POST, - url, - body=mock_response, - content_type='application/json', - status=200) + responses.add( + responses.POST, + url, + body=mock_response, + content_type='application/json', + status=200, + ) # Set up parameter values db = 'testString' - selector = {'key1': 'testString'} + selector = {'email_verified': {'$eq': True}} + allow_fallback = True bookmark = 'testString' conflicts = True execution_stats = True - fields = ['testString'] - limit = 0 + fields = ['_id', 'type', 'name', 'email'] + limit = 3 skip = 0 sort = [{'key1': 'asc'}] stable = True @@ -7841,7 +8277,7 @@ def test_post_find_as_stream_value_error(self): "selector": selector, } for param in req_param_dict.keys(): - req_copy = {key:val if key is not param else None for (key,val) in req_param_dict.items()} + req_copy = {key: val if key is not param else None for (key, val) in req_param_dict.items()} with pytest.raises(ValueError): _service.post_find_as_stream(**req_copy) @@ -7854,7 +8290,8 @@ def test_post_find_as_stream_value_error_with_retries(self): _service.disable_retries() self.test_post_find_as_stream_value_error() -class TestGetIndexesInformation(): + +class TestGetIndexesInformation: """ Test Class for get_indexes_information """ @@ -7866,12 +8303,14 @@ def test_get_indexes_information_all_params(self): """ # Set up mock url = preprocess_url('/testString/_index') - mock_response = '{"total_rows": 0, "indexes": [{"ddoc": "ddoc", "def": {"default_analyzer": {"name": "classic", "stopwords": ["stopwords"]}, "default_field": {"analyzer": {"name": "classic", "stopwords": ["stopwords"]}, "enabled": true}, "fields": [{"name": "name", "type": "boolean"}], "index_array_lengths": true, "partial_filter_selector": {"mapKey": "anyValue"}}, "name": "name", "type": "json"}]}' - responses.add(responses.GET, - url, - body=mock_response, - content_type='application/json', - status=200) + mock_response = '{"total_rows": 0, "indexes": [{"ddoc": "ddoc", "def": {"default_analyzer": {"name": "classic", "stopwords": ["stopwords"]}, "default_field": {"analyzer": {"name": "classic", "stopwords": ["stopwords"]}, "enabled": true}, "fields": [{"name": "name", "type": "boolean"}], "index_array_lengths": true, "partial_filter_selector": {"anyKey": "anyValue"}}, "name": "name", "partitioned": false, "type": "json"}]}' + responses.add( + responses.GET, + url, + body=mock_response, + content_type='application/json', + status=200, + ) # Set up parameter values db = 'testString' @@ -7879,7 +8318,7 @@ def test_get_indexes_information_all_params(self): # Invoke method response = _service.get_indexes_information( db, - headers={} + headers={}, ) # Check for correct operation @@ -7902,12 +8341,14 @@ def test_get_indexes_information_value_error(self): """ # Set up mock url = preprocess_url('/testString/_index') - mock_response = '{"total_rows": 0, "indexes": [{"ddoc": "ddoc", "def": {"default_analyzer": {"name": "classic", "stopwords": ["stopwords"]}, "default_field": {"analyzer": {"name": "classic", "stopwords": ["stopwords"]}, "enabled": true}, "fields": [{"name": "name", "type": "boolean"}], "index_array_lengths": true, "partial_filter_selector": {"mapKey": "anyValue"}}, "name": "name", "type": "json"}]}' - responses.add(responses.GET, - url, - body=mock_response, - content_type='application/json', - status=200) + mock_response = '{"total_rows": 0, "indexes": [{"ddoc": "ddoc", "def": {"default_analyzer": {"name": "classic", "stopwords": ["stopwords"]}, "default_field": {"analyzer": {"name": "classic", "stopwords": ["stopwords"]}, "enabled": true}, "fields": [{"name": "name", "type": "boolean"}], "index_array_lengths": true, "partial_filter_selector": {"anyKey": "anyValue"}}, "name": "name", "partitioned": false, "type": "json"}]}' + responses.add( + responses.GET, + url, + body=mock_response, + content_type='application/json', + status=200, + ) # Set up parameter values db = 'testString' @@ -7917,7 +8358,7 @@ def test_get_indexes_information_value_error(self): "db": db, } for param in req_param_dict.keys(): - req_copy = {key:val if key is not param else None for (key,val) in req_param_dict.items()} + req_copy = {key: val if key is not param else None for (key, val) in req_param_dict.items()} with pytest.raises(ValueError): _service.get_indexes_information(**req_copy) @@ -7930,7 +8371,8 @@ def test_get_indexes_information_value_error_with_retries(self): _service.disable_retries() self.test_get_indexes_information_value_error() -class TestPostIndex(): + +class TestPostIndex: """ Test Class for post_index """ @@ -7943,11 +8385,13 @@ def test_post_index_all_params(self): # Set up mock url = preprocess_url('/testString/_index') mock_response = '{"id": "id", "name": "name", "result": "created"}' - responses.add(responses.POST, - url, - body=mock_response, - content_type='application/json', - status=200) + responses.add( + responses.POST, + url, + body=mock_response, + content_type='application/json', + status=200, + ) # Construct a dict representation of a Analyzer model analyzer_model = {} @@ -7961,7 +8405,7 @@ def test_post_index_all_params(self): # Construct a dict representation of a IndexField model index_field_model = {} - index_field_model['name'] = 'testString' + index_field_model['name'] = 'asc' index_field_model['type'] = 'boolean' index_field_model['foo'] = 'asc' @@ -7971,14 +8415,13 @@ def test_post_index_all_params(self): index_definition_model['default_field'] = index_text_operator_default_field_model index_definition_model['fields'] = [index_field_model] index_definition_model['index_array_lengths'] = True - index_definition_model['partial_filter_selector'] = {'key1': 'testString'} + index_definition_model['partial_filter_selector'] = {'anyKey': 'anyValue'} # Set up parameter values db = 'testString' index = index_definition_model - ddoc = 'testString' - def_ = index_definition_model - name = 'testString' + ddoc = 'json-index' + name = 'getUserByName' partitioned = True type = 'json' @@ -7987,25 +8430,24 @@ def test_post_index_all_params(self): db, index, ddoc=ddoc, - def_=def_, name=name, partitioned=partitioned, type=type, - headers={} + headers={}, ) # Check for correct operation assert len(responses.calls) == 1 assert response.status_code == 200 - # decompress gzip compressed request body - responses.calls[0].request.body = gzip.decompress(responses.calls[0].request.body) + # Decompress gzip compressed request body. + raw_body = responses.calls[0].request.body + responses.calls[0].request.body = gzip.decompress(raw_body.read() if isinstance(raw_body, io.IOBase) else raw_body) # Validate body params req_body = json.loads(str(responses.calls[0].request.body, 'utf-8')) assert req_body['index'] == index_definition_model - assert req_body['ddoc'] == 'testString' - assert req_body['def'] == index_definition_model - assert req_body['name'] == 'testString' + assert req_body['ddoc'] == 'json-index' + assert req_body['name'] == 'getUserByName' assert req_body['partitioned'] == True assert req_body['type'] == 'json' @@ -8026,11 +8468,13 @@ def test_post_index_value_error(self): # Set up mock url = preprocess_url('/testString/_index') mock_response = '{"id": "id", "name": "name", "result": "created"}' - responses.add(responses.POST, - url, - body=mock_response, - content_type='application/json', - status=200) + responses.add( + responses.POST, + url, + body=mock_response, + content_type='application/json', + status=200, + ) # Construct a dict representation of a Analyzer model analyzer_model = {} @@ -8044,7 +8488,7 @@ def test_post_index_value_error(self): # Construct a dict representation of a IndexField model index_field_model = {} - index_field_model['name'] = 'testString' + index_field_model['name'] = 'asc' index_field_model['type'] = 'boolean' index_field_model['foo'] = 'asc' @@ -8054,14 +8498,13 @@ def test_post_index_value_error(self): index_definition_model['default_field'] = index_text_operator_default_field_model index_definition_model['fields'] = [index_field_model] index_definition_model['index_array_lengths'] = True - index_definition_model['partial_filter_selector'] = {'key1': 'testString'} + index_definition_model['partial_filter_selector'] = {'anyKey': 'anyValue'} # Set up parameter values db = 'testString' index = index_definition_model - ddoc = 'testString' - def_ = index_definition_model - name = 'testString' + ddoc = 'json-index' + name = 'getUserByName' partitioned = True type = 'json' @@ -8071,7 +8514,7 @@ def test_post_index_value_error(self): "index": index, } for param in req_param_dict.keys(): - req_copy = {key:val if key is not param else None for (key,val) in req_param_dict.items()} + req_copy = {key: val if key is not param else None for (key, val) in req_param_dict.items()} with pytest.raises(ValueError): _service.post_index(**req_copy) @@ -8084,7 +8527,8 @@ def test_post_index_value_error_with_retries(self): _service.disable_retries() self.test_post_index_value_error() -class TestDeleteIndex(): + +class TestDeleteIndex: """ Test Class for delete_index """ @@ -8097,11 +8541,13 @@ def test_delete_index_all_params(self): # Set up mock url = preprocess_url('/testString/_index/_design/testString/json/testString') mock_response = '{"ok": true}' - responses.add(responses.DELETE, - url, - body=mock_response, - content_type='application/json', - status=200) + responses.add( + responses.DELETE, + url, + body=mock_response, + content_type='application/json', + status=200, + ) # Set up parameter values db = 'testString' @@ -8115,7 +8561,7 @@ def test_delete_index_all_params(self): ddoc, type, index, - headers={} + headers={}, ) # Check for correct operation @@ -8139,11 +8585,13 @@ def test_delete_index_value_error(self): # Set up mock url = preprocess_url('/testString/_index/_design/testString/json/testString') mock_response = '{"ok": true}' - responses.add(responses.DELETE, - url, - body=mock_response, - content_type='application/json', - status=200) + responses.add( + responses.DELETE, + url, + body=mock_response, + content_type='application/json', + status=200, + ) # Set up parameter values db = 'testString' @@ -8159,7 +8607,7 @@ def test_delete_index_value_error(self): "index": index, } for param in req_param_dict.keys(): - req_copy = {key:val if key is not param else None for (key,val) in req_param_dict.items()} + req_copy = {key: val if key is not param else None for (key, val) in req_param_dict.items()} with pytest.raises(ValueError): _service.delete_index(**req_copy) @@ -8172,6 +8620,7 @@ def test_delete_index_value_error_with_retries(self): _service.disable_retries() self.test_delete_index_value_error() + # endregion ############################################################################## # End of Service: Queries @@ -8182,7 +8631,8 @@ def test_delete_index_value_error_with_retries(self): ############################################################################## # region -class TestNewInstance(): + +class TestNewInstance: """ Test Class for new_instance """ @@ -8209,7 +8659,8 @@ def test_new_instance_without_authenticator(self): service_name='TEST_SERVICE_NOT_FOUND', ) -class TestPostSearchAnalyze(): + +class TestPostSearchAnalyze: """ Test Class for post_search_analyze """ @@ -8222,33 +8673,36 @@ def test_post_search_analyze_all_params(self): # Set up mock url = preprocess_url('/_search_analyze') mock_response = '{"tokens": ["tokens"]}' - responses.add(responses.POST, - url, - body=mock_response, - content_type='application/json', - status=200) + responses.add( + responses.POST, + url, + body=mock_response, + content_type='application/json', + status=200, + ) # Set up parameter values - analyzer = 'arabic' - text = 'testString' + analyzer = 'english' + text = 'running is fun' # Invoke method response = _service.post_search_analyze( analyzer, text, - headers={} + headers={}, ) # Check for correct operation assert len(responses.calls) == 1 assert response.status_code == 200 - # decompress gzip compressed request body - responses.calls[0].request.body = gzip.decompress(responses.calls[0].request.body) + # Decompress gzip compressed request body. + raw_body = responses.calls[0].request.body + responses.calls[0].request.body = gzip.decompress(raw_body.read() if isinstance(raw_body, io.IOBase) else raw_body) # Validate body params req_body = json.loads(str(responses.calls[0].request.body, 'utf-8')) - assert req_body['analyzer'] == 'arabic' - assert req_body['text'] == 'testString' + assert req_body['analyzer'] == 'english' + assert req_body['text'] == 'running is fun' def test_post_search_analyze_all_params_with_retries(self): # Enable retries and run test_post_search_analyze_all_params. @@ -8267,15 +8721,17 @@ def test_post_search_analyze_value_error(self): # Set up mock url = preprocess_url('/_search_analyze') mock_response = '{"tokens": ["tokens"]}' - responses.add(responses.POST, - url, - body=mock_response, - content_type='application/json', - status=200) + responses.add( + responses.POST, + url, + body=mock_response, + content_type='application/json', + status=200, + ) # Set up parameter values - analyzer = 'arabic' - text = 'testString' + analyzer = 'english' + text = 'running is fun' # Pass in all but one required param and check for a ValueError req_param_dict = { @@ -8283,7 +8739,7 @@ def test_post_search_analyze_value_error(self): "text": text, } for param in req_param_dict.keys(): - req_copy = {key:val if key is not param else None for (key,val) in req_param_dict.items()} + req_copy = {key: val if key is not param else None for (key, val) in req_param_dict.items()} with pytest.raises(ValueError): _service.post_search_analyze(**req_copy) @@ -8296,7 +8752,8 @@ def test_post_search_analyze_value_error_with_retries(self): _service.disable_retries() self.test_post_search_analyze_value_error() -class TestPostSearch(): + +class TestPostSearch: """ Test Class for post_search """ @@ -8308,27 +8765,29 @@ def test_post_search_all_params(self): """ # Set up mock url = preprocess_url('/testString/_design/testString/_search/testString') - mock_response = '{"total_rows": 0, "bookmark": "bookmark", "by": "by", "counts": {"mapKey": {"mapKey": 0}}, "ranges": {"mapKey": {"mapKey": 0}}, "rows": [{"doc": {"_attachments": {"mapKey": {"content_type": "content_type", "data": "VGhpcyBpcyBhbiBlbmNvZGVkIGJ5dGUgYXJyYXku", "digest": "digest", "encoded_length": 0, "encoding": "encoding", "follows": false, "length": 0, "revpos": 1, "stub": true}}, "_conflicts": ["conflicts"], "_deleted": false, "_deleted_conflicts": ["deleted_conflicts"], "_id": "id", "_local_seq": "local_seq", "_rev": "rev", "_revisions": {"ids": ["ids"], "start": 1}, "_revs_info": [{"rev": "rev", "status": "available"}]}, "fields": {"mapKey": "anyValue"}, "highlights": {"mapKey": ["inner"]}, "id": "id"}], "groups": [{"total_rows": 0, "bookmark": "bookmark", "by": "by", "counts": {"mapKey": {"mapKey": 0}}, "ranges": {"mapKey": {"mapKey": 0}}, "rows": [{"doc": {"_attachments": {"mapKey": {"content_type": "content_type", "data": "VGhpcyBpcyBhbiBlbmNvZGVkIGJ5dGUgYXJyYXku", "digest": "digest", "encoded_length": 0, "encoding": "encoding", "follows": false, "length": 0, "revpos": 1, "stub": true}}, "_conflicts": ["conflicts"], "_deleted": false, "_deleted_conflicts": ["deleted_conflicts"], "_id": "id", "_local_seq": "local_seq", "_rev": "rev", "_revisions": {"ids": ["ids"], "start": 1}, "_revs_info": [{"rev": "rev", "status": "available"}]}, "fields": {"mapKey": "anyValue"}, "highlights": {"mapKey": ["inner"]}, "id": "id"}]}]}' - responses.add(responses.POST, - url, - body=mock_response, - content_type='application/json', - status=200) + mock_response = '{"total_rows": 0, "bookmark": "bookmark", "by": "by", "counts": {"mapKey": {"mapKey": 0}}, "ranges": {"mapKey": {"mapKey": 0}}, "rows": [{"doc": {"_attachments": {"mapKey": {"content_type": "content_type", "data": "VGhpcyBpcyBhIG1vY2sgYnl0ZSBhcnJheSB2YWx1ZS4=", "digest": "digest", "encoded_length": 0, "encoding": "encoding", "follows": false, "length": 0, "revpos": 1, "stub": true}}, "_conflicts": ["conflicts"], "_deleted": true, "_deleted_conflicts": ["deleted_conflicts"], "_id": "_id", "_local_seq": "_local_seq", "_rev": "_rev", "_revisions": {"ids": ["ids"], "start": 1}, "_revs_info": [{"rev": "rev", "status": "available"}]}, "fields": {"anyKey": "anyValue"}, "highlights": {"mapKey": ["inner"]}, "id": "id"}], "groups": [{"total_rows": 0, "bookmark": "bookmark", "by": "by", "counts": {"mapKey": {"mapKey": 0}}, "ranges": {"mapKey": {"mapKey": 0}}, "rows": [{"doc": {"_attachments": {"mapKey": {"content_type": "content_type", "data": "VGhpcyBpcyBhIG1vY2sgYnl0ZSBhcnJheSB2YWx1ZS4=", "digest": "digest", "encoded_length": 0, "encoding": "encoding", "follows": false, "length": 0, "revpos": 1, "stub": true}}, "_conflicts": ["conflicts"], "_deleted": true, "_deleted_conflicts": ["deleted_conflicts"], "_id": "_id", "_local_seq": "_local_seq", "_rev": "_rev", "_revisions": {"ids": ["ids"], "start": 1}, "_revs_info": [{"rev": "rev", "status": "available"}]}, "fields": {"anyKey": "anyValue"}, "highlights": {"mapKey": ["inner"]}, "id": "id"}]}]}' + responses.add( + responses.POST, + url, + body=mock_response, + content_type='application/json', + status=200, + ) # Set up parameter values db = 'testString' ddoc = 'testString' index = 'testString' - query = 'testString' + query = 'name:Jane* AND active:True' bookmark = 'testString' highlight_fields = ['testString'] highlight_number = 1 highlight_post_tag = '' highlight_pre_tag = '' - highlight_size = 1 + highlight_size = 100 include_docs = False include_fields = ['testString'] - limit = 0 + limit = 3 sort = ['testString'] stale = 'ok' counts = ['testString'] @@ -8336,7 +8795,7 @@ def test_post_search_all_params(self): group_field = 'testString' group_limit = 1 group_sort = ['testString'] - ranges = {'key1': {'key1': {'key1': 'testString'}}} + ranges = {'key1': {'key1': 'testString'}} # Invoke method response = _service.post_search( @@ -8361,27 +8820,28 @@ def test_post_search_all_params(self): group_limit=group_limit, group_sort=group_sort, ranges=ranges, - headers={} + headers={}, ) # Check for correct operation assert len(responses.calls) == 1 assert response.status_code == 200 - # decompress gzip compressed request body - responses.calls[0].request.body = gzip.decompress(responses.calls[0].request.body) + # Decompress gzip compressed request body. + raw_body = responses.calls[0].request.body + responses.calls[0].request.body = gzip.decompress(raw_body.read() if isinstance(raw_body, io.IOBase) else raw_body) # Validate body params req_body = json.loads(str(responses.calls[0].request.body, 'utf-8')) - assert req_body['query'] == 'testString' + assert req_body['query'] == 'name:Jane* AND active:True' assert req_body['bookmark'] == 'testString' assert req_body['highlight_fields'] == ['testString'] assert req_body['highlight_number'] == 1 assert req_body['highlight_post_tag'] == '' assert req_body['highlight_pre_tag'] == '' - assert req_body['highlight_size'] == 1 + assert req_body['highlight_size'] == 100 assert req_body['include_docs'] == False assert req_body['include_fields'] == ['testString'] - assert req_body['limit'] == 0 + assert req_body['limit'] == 3 assert req_body['sort'] == ['testString'] assert req_body['stale'] == 'ok' assert req_body['counts'] == ['testString'] @@ -8389,7 +8849,7 @@ def test_post_search_all_params(self): assert req_body['group_field'] == 'testString' assert req_body['group_limit'] == 1 assert req_body['group_sort'] == ['testString'] - assert req_body['ranges'] == {'key1': {'key1': {'key1': 'testString'}}} + assert req_body['ranges'] == {'key1': {'key1': 'testString'}} def test_post_search_all_params_with_retries(self): # Enable retries and run test_post_search_all_params. @@ -8407,27 +8867,29 @@ def test_post_search_value_error(self): """ # Set up mock url = preprocess_url('/testString/_design/testString/_search/testString') - mock_response = '{"total_rows": 0, "bookmark": "bookmark", "by": "by", "counts": {"mapKey": {"mapKey": 0}}, "ranges": {"mapKey": {"mapKey": 0}}, "rows": [{"doc": {"_attachments": {"mapKey": {"content_type": "content_type", "data": "VGhpcyBpcyBhbiBlbmNvZGVkIGJ5dGUgYXJyYXku", "digest": "digest", "encoded_length": 0, "encoding": "encoding", "follows": false, "length": 0, "revpos": 1, "stub": true}}, "_conflicts": ["conflicts"], "_deleted": false, "_deleted_conflicts": ["deleted_conflicts"], "_id": "id", "_local_seq": "local_seq", "_rev": "rev", "_revisions": {"ids": ["ids"], "start": 1}, "_revs_info": [{"rev": "rev", "status": "available"}]}, "fields": {"mapKey": "anyValue"}, "highlights": {"mapKey": ["inner"]}, "id": "id"}], "groups": [{"total_rows": 0, "bookmark": "bookmark", "by": "by", "counts": {"mapKey": {"mapKey": 0}}, "ranges": {"mapKey": {"mapKey": 0}}, "rows": [{"doc": {"_attachments": {"mapKey": {"content_type": "content_type", "data": "VGhpcyBpcyBhbiBlbmNvZGVkIGJ5dGUgYXJyYXku", "digest": "digest", "encoded_length": 0, "encoding": "encoding", "follows": false, "length": 0, "revpos": 1, "stub": true}}, "_conflicts": ["conflicts"], "_deleted": false, "_deleted_conflicts": ["deleted_conflicts"], "_id": "id", "_local_seq": "local_seq", "_rev": "rev", "_revisions": {"ids": ["ids"], "start": 1}, "_revs_info": [{"rev": "rev", "status": "available"}]}, "fields": {"mapKey": "anyValue"}, "highlights": {"mapKey": ["inner"]}, "id": "id"}]}]}' - responses.add(responses.POST, - url, - body=mock_response, - content_type='application/json', - status=200) + mock_response = '{"total_rows": 0, "bookmark": "bookmark", "by": "by", "counts": {"mapKey": {"mapKey": 0}}, "ranges": {"mapKey": {"mapKey": 0}}, "rows": [{"doc": {"_attachments": {"mapKey": {"content_type": "content_type", "data": "VGhpcyBpcyBhIG1vY2sgYnl0ZSBhcnJheSB2YWx1ZS4=", "digest": "digest", "encoded_length": 0, "encoding": "encoding", "follows": false, "length": 0, "revpos": 1, "stub": true}}, "_conflicts": ["conflicts"], "_deleted": true, "_deleted_conflicts": ["deleted_conflicts"], "_id": "_id", "_local_seq": "_local_seq", "_rev": "_rev", "_revisions": {"ids": ["ids"], "start": 1}, "_revs_info": [{"rev": "rev", "status": "available"}]}, "fields": {"anyKey": "anyValue"}, "highlights": {"mapKey": ["inner"]}, "id": "id"}], "groups": [{"total_rows": 0, "bookmark": "bookmark", "by": "by", "counts": {"mapKey": {"mapKey": 0}}, "ranges": {"mapKey": {"mapKey": 0}}, "rows": [{"doc": {"_attachments": {"mapKey": {"content_type": "content_type", "data": "VGhpcyBpcyBhIG1vY2sgYnl0ZSBhcnJheSB2YWx1ZS4=", "digest": "digest", "encoded_length": 0, "encoding": "encoding", "follows": false, "length": 0, "revpos": 1, "stub": true}}, "_conflicts": ["conflicts"], "_deleted": true, "_deleted_conflicts": ["deleted_conflicts"], "_id": "_id", "_local_seq": "_local_seq", "_rev": "_rev", "_revisions": {"ids": ["ids"], "start": 1}, "_revs_info": [{"rev": "rev", "status": "available"}]}, "fields": {"anyKey": "anyValue"}, "highlights": {"mapKey": ["inner"]}, "id": "id"}]}]}' + responses.add( + responses.POST, + url, + body=mock_response, + content_type='application/json', + status=200, + ) # Set up parameter values db = 'testString' ddoc = 'testString' index = 'testString' - query = 'testString' + query = 'name:Jane* AND active:True' bookmark = 'testString' highlight_fields = ['testString'] highlight_number = 1 highlight_post_tag = '' highlight_pre_tag = '' - highlight_size = 1 + highlight_size = 100 include_docs = False include_fields = ['testString'] - limit = 0 + limit = 3 sort = ['testString'] stale = 'ok' counts = ['testString'] @@ -8435,7 +8897,7 @@ def test_post_search_value_error(self): group_field = 'testString' group_limit = 1 group_sort = ['testString'] - ranges = {'key1': {'key1': {'key1': 'testString'}}} + ranges = {'key1': {'key1': 'testString'}} # Pass in all but one required param and check for a ValueError req_param_dict = { @@ -8445,7 +8907,7 @@ def test_post_search_value_error(self): "query": query, } for param in req_param_dict.keys(): - req_copy = {key:val if key is not param else None for (key,val) in req_param_dict.items()} + req_copy = {key: val if key is not param else None for (key, val) in req_param_dict.items()} with pytest.raises(ValueError): _service.post_search(**req_copy) @@ -8458,7 +8920,8 @@ def test_post_search_value_error_with_retries(self): _service.disable_retries() self.test_post_search_value_error() -class TestPostSearchAsStream(): + +class TestPostSearchAsStream: """ Test Class for post_search_as_stream """ @@ -8471,26 +8934,28 @@ def test_post_search_as_stream_all_params(self): # Set up mock url = preprocess_url('/testString/_design/testString/_search/testString') mock_response = '{"foo": "this is a mock response for JSON streaming"}' - responses.add(responses.POST, - url, - body=mock_response, - content_type='application/json', - status=200) + responses.add( + responses.POST, + url, + body=mock_response, + content_type='application/json', + status=200, + ) # Set up parameter values db = 'testString' ddoc = 'testString' index = 'testString' - query = 'testString' + query = 'name:Jane* AND active:True' bookmark = 'testString' highlight_fields = ['testString'] highlight_number = 1 highlight_post_tag = '' highlight_pre_tag = '' - highlight_size = 1 + highlight_size = 100 include_docs = False include_fields = ['testString'] - limit = 0 + limit = 3 sort = ['testString'] stale = 'ok' counts = ['testString'] @@ -8498,7 +8963,7 @@ def test_post_search_as_stream_all_params(self): group_field = 'testString' group_limit = 1 group_sort = ['testString'] - ranges = {'key1': {'key1': {'key1': 'testString'}}} + ranges = {'key1': {'key1': 'testString'}} # Invoke method response = _service.post_search_as_stream( @@ -8523,27 +8988,28 @@ def test_post_search_as_stream_all_params(self): group_limit=group_limit, group_sort=group_sort, ranges=ranges, - headers={} + headers={}, ) # Check for correct operation assert len(responses.calls) == 1 assert response.status_code == 200 - # decompress gzip compressed request body - responses.calls[0].request.body = gzip.decompress(responses.calls[0].request.body) + # Decompress gzip compressed request body. + raw_body = responses.calls[0].request.body + responses.calls[0].request.body = gzip.decompress(raw_body.read() if isinstance(raw_body, io.IOBase) else raw_body) # Validate body params req_body = json.loads(str(responses.calls[0].request.body, 'utf-8')) - assert req_body['query'] == 'testString' + assert req_body['query'] == 'name:Jane* AND active:True' assert req_body['bookmark'] == 'testString' assert req_body['highlight_fields'] == ['testString'] assert req_body['highlight_number'] == 1 assert req_body['highlight_post_tag'] == '' assert req_body['highlight_pre_tag'] == '' - assert req_body['highlight_size'] == 1 + assert req_body['highlight_size'] == 100 assert req_body['include_docs'] == False assert req_body['include_fields'] == ['testString'] - assert req_body['limit'] == 0 + assert req_body['limit'] == 3 assert req_body['sort'] == ['testString'] assert req_body['stale'] == 'ok' assert req_body['counts'] == ['testString'] @@ -8551,7 +9017,7 @@ def test_post_search_as_stream_all_params(self): assert req_body['group_field'] == 'testString' assert req_body['group_limit'] == 1 assert req_body['group_sort'] == ['testString'] - assert req_body['ranges'] == {'key1': {'key1': {'key1': 'testString'}}} + assert req_body['ranges'] == {'key1': {'key1': 'testString'}} # Verify streamed JSON response result = response.get_result() @@ -8576,26 +9042,28 @@ def test_post_search_as_stream_value_error(self): # Set up mock url = preprocess_url('/testString/_design/testString/_search/testString') mock_response = '{"foo": "this is a mock response for JSON streaming"}' - responses.add(responses.POST, - url, - body=mock_response, - content_type='application/json', - status=200) + responses.add( + responses.POST, + url, + body=mock_response, + content_type='application/json', + status=200, + ) # Set up parameter values db = 'testString' ddoc = 'testString' index = 'testString' - query = 'testString' + query = 'name:Jane* AND active:True' bookmark = 'testString' highlight_fields = ['testString'] highlight_number = 1 highlight_post_tag = '' highlight_pre_tag = '' - highlight_size = 1 + highlight_size = 100 include_docs = False include_fields = ['testString'] - limit = 0 + limit = 3 sort = ['testString'] stale = 'ok' counts = ['testString'] @@ -8603,7 +9071,7 @@ def test_post_search_as_stream_value_error(self): group_field = 'testString' group_limit = 1 group_sort = ['testString'] - ranges = {'key1': {'key1': {'key1': 'testString'}}} + ranges = {'key1': {'key1': 'testString'}} # Pass in all but one required param and check for a ValueError req_param_dict = { @@ -8613,7 +9081,7 @@ def test_post_search_as_stream_value_error(self): "query": query, } for param in req_param_dict.keys(): - req_copy = {key:val if key is not param else None for (key,val) in req_param_dict.items()} + req_copy = {key: val if key is not param else None for (key, val) in req_param_dict.items()} with pytest.raises(ValueError): _service.post_search_as_stream(**req_copy) @@ -8626,7 +9094,97 @@ def test_post_search_as_stream_value_error_with_retries(self): _service.disable_retries() self.test_post_search_as_stream_value_error() -class TestGetSearchInfo(): + +class TestGetSearchDiskSize: + """ + Test Class for get_search_disk_size + """ + + @responses.activate + def test_get_search_disk_size_all_params(self): + """ + get_search_disk_size() + """ + # Set up mock + url = preprocess_url('/testString/_design/testString/_search_disk_size/testString') + mock_response = '{"name": "name", "search_index": {"disk_size": 0}}' + responses.add( + responses.GET, + url, + body=mock_response, + content_type='application/json', + status=200, + ) + + # Set up parameter values + db = 'testString' + ddoc = 'testString' + index = 'testString' + + # Invoke method + response = _service.get_search_disk_size( + db, + ddoc, + index, + headers={}, + ) + + # Check for correct operation + assert len(responses.calls) == 1 + assert response.status_code == 200 + + def test_get_search_disk_size_all_params_with_retries(self): + # Enable retries and run test_get_search_disk_size_all_params. + _service.enable_retries() + self.test_get_search_disk_size_all_params() + + # Disable retries and run test_get_search_disk_size_all_params. + _service.disable_retries() + self.test_get_search_disk_size_all_params() + + @responses.activate + def test_get_search_disk_size_value_error(self): + """ + test_get_search_disk_size_value_error() + """ + # Set up mock + url = preprocess_url('/testString/_design/testString/_search_disk_size/testString') + mock_response = '{"name": "name", "search_index": {"disk_size": 0}}' + responses.add( + responses.GET, + url, + body=mock_response, + content_type='application/json', + status=200, + ) + + # Set up parameter values + db = 'testString' + ddoc = 'testString' + index = 'testString' + + # Pass in all but one required param and check for a ValueError + req_param_dict = { + "db": db, + "ddoc": ddoc, + "index": index, + } + for param in req_param_dict.keys(): + req_copy = {key: val if key is not param else None for (key, val) in req_param_dict.items()} + with pytest.raises(ValueError): + _service.get_search_disk_size(**req_copy) + + def test_get_search_disk_size_value_error_with_retries(self): + # Enable retries and run test_get_search_disk_size_value_error. + _service.enable_retries() + self.test_get_search_disk_size_value_error() + + # Disable retries and run test_get_search_disk_size_value_error. + _service.disable_retries() + self.test_get_search_disk_size_value_error() + + +class TestGetSearchInfo: """ Test Class for get_search_info """ @@ -8638,12 +9196,14 @@ def test_get_search_info_all_params(self): """ # Set up mock url = preprocess_url('/testString/_design/testString/_search_info/testString') - mock_response = '{"name": "name", "search_index": {"committed_seq": 13, "disk_size": 0, "doc_count": 0, "doc_del_count": 0, "pending_seq": 11, "signature": "signature"}}' - responses.add(responses.GET, - url, - body=mock_response, - content_type='application/json', - status=200) + mock_response = '{"name": "name", "search_index": {"committed_seq": 0, "disk_size": 0, "doc_count": 0, "doc_del_count": 0, "pending_seq": 0, "signature": "signature"}}' + responses.add( + responses.GET, + url, + body=mock_response, + content_type='application/json', + status=200, + ) # Set up parameter values db = 'testString' @@ -8655,7 +9215,7 @@ def test_get_search_info_all_params(self): db, ddoc, index, - headers={} + headers={}, ) # Check for correct operation @@ -8678,12 +9238,14 @@ def test_get_search_info_value_error(self): """ # Set up mock url = preprocess_url('/testString/_design/testString/_search_info/testString') - mock_response = '{"name": "name", "search_index": {"committed_seq": 13, "disk_size": 0, "doc_count": 0, "doc_del_count": 0, "pending_seq": 11, "signature": "signature"}}' - responses.add(responses.GET, - url, - body=mock_response, - content_type='application/json', - status=200) + mock_response = '{"name": "name", "search_index": {"committed_seq": 0, "disk_size": 0, "doc_count": 0, "doc_del_count": 0, "pending_seq": 0, "signature": "signature"}}' + responses.add( + responses.GET, + url, + body=mock_response, + content_type='application/json', + status=200, + ) # Set up parameter values db = 'testString' @@ -8697,7 +9259,7 @@ def test_get_search_info_value_error(self): "index": index, } for param in req_param_dict.keys(): - req_copy = {key:val if key is not param else None for (key,val) in req_param_dict.items()} + req_copy = {key: val if key is not param else None for (key, val) in req_param_dict.items()} with pytest.raises(ValueError): _service.get_search_info(**req_copy) @@ -8710,17 +9272,19 @@ def test_get_search_info_value_error_with_retries(self): _service.disable_retries() self.test_get_search_info_value_error() + # endregion ############################################################################## # End of Service: Searches ############################################################################## ############################################################################## -# Start of Service: Geospatial +# Start of Service: Replication ############################################################################## # region -class TestNewInstance(): + +class TestNewInstance: """ Test Class for new_instance """ @@ -8747,805 +9311,685 @@ def test_new_instance_without_authenticator(self): service_name='TEST_SERVICE_NOT_FOUND', ) -class TestGetGeo(): + +class TestHeadReplicationDocument: """ - Test Class for get_geo + Test Class for head_replication_document """ @responses.activate - def test_get_geo_all_params(self): + def test_head_replication_document_all_params(self): """ - get_geo() + head_replication_document() """ # Set up mock - url = preprocess_url('/testString/_design/testString/_geo/testString') - mock_response = '{"bookmark": "bookmark", "features": [{"_id": "id", "_rev": "rev", "bbox": [4], "geometry": {"type": "Point", "coordinates": ["anyValue"]}, "properties": {"mapKey": "anyValue"}, "type": "Feature"}], "rows": [{"doc": {"_attachments": {"mapKey": {"content_type": "content_type", "data": "VGhpcyBpcyBhbiBlbmNvZGVkIGJ5dGUgYXJyYXku", "digest": "digest", "encoded_length": 0, "encoding": "encoding", "follows": false, "length": 0, "revpos": 1, "stub": true}}, "_conflicts": ["conflicts"], "_deleted": false, "_deleted_conflicts": ["deleted_conflicts"], "_id": "id", "_local_seq": "local_seq", "_rev": "rev", "_revisions": {"ids": ["ids"], "start": 1}, "_revs_info": [{"rev": "rev", "status": "available"}]}, "geometry": {"type": "Point", "coordinates": ["anyValue"]}, "id": "id", "rev": "rev"}], "type": "FeatureCollection"}' - responses.add(responses.GET, - url, - body=mock_response, - content_type='application/json', - status=200) + url = preprocess_url('/_replicator/testString') + responses.add( + responses.HEAD, + url, + status=200, + ) # Set up parameter values - db = 'testString' - ddoc = 'testString' - index = 'testString' - bbox = 'testString' - bookmark = 'testString' - format = 'view' - g = 'testString' - include_docs = False - lat = -90 - limit = 0 - lon = -180 - nearest = False - radius = 0 - rangex = 0 - rangey = 0 - relation = 'intersects' - skip = 0 - stale = 'ok' + doc_id = 'testString' + if_none_match = 'testString' # Invoke method - response = _service.get_geo( - db, - ddoc, - index, - bbox=bbox, - bookmark=bookmark, - format=format, - g=g, - include_docs=include_docs, - lat=lat, - limit=limit, - lon=lon, - nearest=nearest, - radius=radius, - rangex=rangex, - rangey=rangey, - relation=relation, - skip=skip, - stale=stale, - headers={} + response = _service.head_replication_document( + doc_id, + if_none_match=if_none_match, + headers={}, ) # Check for correct operation assert len(responses.calls) == 1 assert response.status_code == 200 - # Validate query params - query_string = responses.calls[0].request.url.split('?',1)[1] - query_string = urllib.parse.unquote_plus(query_string) - assert 'bbox={}'.format(bbox) in query_string - assert 'bookmark={}'.format(bookmark) in query_string - assert 'format={}'.format(format) in query_string - assert 'g={}'.format(g) in query_string - assert 'include_docs={}'.format('true' if include_docs else 'false') in query_string - assert 'lat={}'.format(lat) in query_string - assert 'limit={}'.format(limit) in query_string - assert 'lon={}'.format(lon) in query_string - assert 'nearest={}'.format('true' if nearest else 'false') in query_string - assert 'radius={}'.format(radius) in query_string - assert 'rangex={}'.format(rangex) in query_string - assert 'rangey={}'.format(rangey) in query_string - assert 'relation={}'.format(relation) in query_string - assert 'skip={}'.format(skip) in query_string - assert 'stale={}'.format(stale) in query_string - def test_get_geo_all_params_with_retries(self): - # Enable retries and run test_get_geo_all_params. + def test_head_replication_document_all_params_with_retries(self): + # Enable retries and run test_head_replication_document_all_params. _service.enable_retries() - self.test_get_geo_all_params() + self.test_head_replication_document_all_params() - # Disable retries and run test_get_geo_all_params. + # Disable retries and run test_head_replication_document_all_params. _service.disable_retries() - self.test_get_geo_all_params() + self.test_head_replication_document_all_params() @responses.activate - def test_get_geo_required_params(self): + def test_head_replication_document_required_params(self): """ - test_get_geo_required_params() + test_head_replication_document_required_params() """ # Set up mock - url = preprocess_url('/testString/_design/testString/_geo/testString') - mock_response = '{"bookmark": "bookmark", "features": [{"_id": "id", "_rev": "rev", "bbox": [4], "geometry": {"type": "Point", "coordinates": ["anyValue"]}, "properties": {"mapKey": "anyValue"}, "type": "Feature"}], "rows": [{"doc": {"_attachments": {"mapKey": {"content_type": "content_type", "data": "VGhpcyBpcyBhbiBlbmNvZGVkIGJ5dGUgYXJyYXku", "digest": "digest", "encoded_length": 0, "encoding": "encoding", "follows": false, "length": 0, "revpos": 1, "stub": true}}, "_conflicts": ["conflicts"], "_deleted": false, "_deleted_conflicts": ["deleted_conflicts"], "_id": "id", "_local_seq": "local_seq", "_rev": "rev", "_revisions": {"ids": ["ids"], "start": 1}, "_revs_info": [{"rev": "rev", "status": "available"}]}, "geometry": {"type": "Point", "coordinates": ["anyValue"]}, "id": "id", "rev": "rev"}], "type": "FeatureCollection"}' - responses.add(responses.GET, - url, - body=mock_response, - content_type='application/json', - status=200) + url = preprocess_url('/_replicator/testString') + responses.add( + responses.HEAD, + url, + status=200, + ) # Set up parameter values - db = 'testString' - ddoc = 'testString' - index = 'testString' + doc_id = 'testString' # Invoke method - response = _service.get_geo( - db, - ddoc, - index, - headers={} + response = _service.head_replication_document( + doc_id, + headers={}, ) # Check for correct operation assert len(responses.calls) == 1 assert response.status_code == 200 - def test_get_geo_required_params_with_retries(self): - # Enable retries and run test_get_geo_required_params. + def test_head_replication_document_required_params_with_retries(self): + # Enable retries and run test_head_replication_document_required_params. _service.enable_retries() - self.test_get_geo_required_params() + self.test_head_replication_document_required_params() - # Disable retries and run test_get_geo_required_params. + # Disable retries and run test_head_replication_document_required_params. _service.disable_retries() - self.test_get_geo_required_params() + self.test_head_replication_document_required_params() @responses.activate - def test_get_geo_value_error(self): + def test_head_replication_document_value_error(self): """ - test_get_geo_value_error() + test_head_replication_document_value_error() """ # Set up mock - url = preprocess_url('/testString/_design/testString/_geo/testString') - mock_response = '{"bookmark": "bookmark", "features": [{"_id": "id", "_rev": "rev", "bbox": [4], "geometry": {"type": "Point", "coordinates": ["anyValue"]}, "properties": {"mapKey": "anyValue"}, "type": "Feature"}], "rows": [{"doc": {"_attachments": {"mapKey": {"content_type": "content_type", "data": "VGhpcyBpcyBhbiBlbmNvZGVkIGJ5dGUgYXJyYXku", "digest": "digest", "encoded_length": 0, "encoding": "encoding", "follows": false, "length": 0, "revpos": 1, "stub": true}}, "_conflicts": ["conflicts"], "_deleted": false, "_deleted_conflicts": ["deleted_conflicts"], "_id": "id", "_local_seq": "local_seq", "_rev": "rev", "_revisions": {"ids": ["ids"], "start": 1}, "_revs_info": [{"rev": "rev", "status": "available"}]}, "geometry": {"type": "Point", "coordinates": ["anyValue"]}, "id": "id", "rev": "rev"}], "type": "FeatureCollection"}' - responses.add(responses.GET, - url, - body=mock_response, - content_type='application/json', - status=200) + url = preprocess_url('/_replicator/testString') + responses.add( + responses.HEAD, + url, + status=200, + ) # Set up parameter values - db = 'testString' - ddoc = 'testString' - index = 'testString' + doc_id = 'testString' # Pass in all but one required param and check for a ValueError req_param_dict = { - "db": db, - "ddoc": ddoc, - "index": index, + "doc_id": doc_id, } for param in req_param_dict.keys(): - req_copy = {key:val if key is not param else None for (key,val) in req_param_dict.items()} + req_copy = {key: val if key is not param else None for (key, val) in req_param_dict.items()} with pytest.raises(ValueError): - _service.get_geo(**req_copy) + _service.head_replication_document(**req_copy) - def test_get_geo_value_error_with_retries(self): - # Enable retries and run test_get_geo_value_error. + def test_head_replication_document_value_error_with_retries(self): + # Enable retries and run test_head_replication_document_value_error. _service.enable_retries() - self.test_get_geo_value_error() + self.test_head_replication_document_value_error() - # Disable retries and run test_get_geo_value_error. + # Disable retries and run test_head_replication_document_value_error. _service.disable_retries() - self.test_get_geo_value_error() + self.test_head_replication_document_value_error() -class TestGetGeoAsStream(): + +class TestHeadSchedulerDocument: """ - Test Class for get_geo_as_stream + Test Class for head_scheduler_document """ @responses.activate - def test_get_geo_as_stream_all_params(self): + def test_head_scheduler_document_all_params(self): """ - get_geo_as_stream() + head_scheduler_document() """ # Set up mock - url = preprocess_url('/testString/_design/testString/_geo/testString') - mock_response = '{"foo": "this is a mock response for JSON streaming"}' - responses.add(responses.GET, - url, - body=mock_response, - content_type='application/json', - status=200) - - # Set up parameter values - db = 'testString' - ddoc = 'testString' - index = 'testString' - bbox = 'testString' - bookmark = 'testString' - format = 'view' - g = 'testString' - include_docs = False - lat = -90 - limit = 0 - lon = -180 - nearest = False - radius = 0 - rangex = 0 - rangey = 0 - relation = 'intersects' - skip = 0 - stale = 'ok' - - # Invoke method - response = _service.get_geo_as_stream( - db, - ddoc, - index, - bbox=bbox, - bookmark=bookmark, - format=format, - g=g, - include_docs=include_docs, - lat=lat, - limit=limit, - lon=lon, - nearest=nearest, - radius=radius, - rangex=rangex, - rangey=rangey, - relation=relation, - skip=skip, - stale=stale, - headers={} + url = preprocess_url('/_scheduler/docs/_replicator/testString') + responses.add( + responses.HEAD, + url, + status=200, ) - # Check for correct operation - assert len(responses.calls) == 1 - assert response.status_code == 200 - # Validate query params - query_string = responses.calls[0].request.url.split('?',1)[1] - query_string = urllib.parse.unquote_plus(query_string) - assert 'bbox={}'.format(bbox) in query_string - assert 'bookmark={}'.format(bookmark) in query_string - assert 'format={}'.format(format) in query_string - assert 'g={}'.format(g) in query_string - assert 'include_docs={}'.format('true' if include_docs else 'false') in query_string - assert 'lat={}'.format(lat) in query_string - assert 'limit={}'.format(limit) in query_string - assert 'lon={}'.format(lon) in query_string - assert 'nearest={}'.format('true' if nearest else 'false') in query_string - assert 'radius={}'.format(radius) in query_string - assert 'rangex={}'.format(rangex) in query_string - assert 'rangey={}'.format(rangey) in query_string - assert 'relation={}'.format(relation) in query_string - assert 'skip={}'.format(skip) in query_string - assert 'stale={}'.format(stale) in query_string - - # Verify streamed JSON response - result = response.get_result() - assert isinstance(result, requests.models.Response) - response_buf = result.iter_content(chunk_size=1024) - assert str(next(response_buf), "utf-8") == mock_response - - def test_get_geo_as_stream_all_params_with_retries(self): - # Enable retries and run test_get_geo_as_stream_all_params. - _service.enable_retries() - self.test_get_geo_as_stream_all_params() - - # Disable retries and run test_get_geo_as_stream_all_params. - _service.disable_retries() - self.test_get_geo_as_stream_all_params() - - @responses.activate - def test_get_geo_as_stream_required_params(self): - """ - test_get_geo_as_stream_required_params() - """ - # Set up mock - url = preprocess_url('/testString/_design/testString/_geo/testString') - mock_response = '{"foo": "this is a mock response for JSON streaming"}' - responses.add(responses.GET, - url, - body=mock_response, - content_type='application/json', - status=200) - # Set up parameter values - db = 'testString' - ddoc = 'testString' - index = 'testString' + doc_id = 'testString' # Invoke method - response = _service.get_geo_as_stream( - db, - ddoc, - index, - headers={} + response = _service.head_scheduler_document( + doc_id, + headers={}, ) # Check for correct operation assert len(responses.calls) == 1 assert response.status_code == 200 - # Verify streamed JSON response - result = response.get_result() - assert isinstance(result, requests.models.Response) - response_buf = result.iter_content(chunk_size=1024) - assert str(next(response_buf), "utf-8") == mock_response - - def test_get_geo_as_stream_required_params_with_retries(self): - # Enable retries and run test_get_geo_as_stream_required_params. + def test_head_scheduler_document_all_params_with_retries(self): + # Enable retries and run test_head_scheduler_document_all_params. _service.enable_retries() - self.test_get_geo_as_stream_required_params() + self.test_head_scheduler_document_all_params() - # Disable retries and run test_get_geo_as_stream_required_params. + # Disable retries and run test_head_scheduler_document_all_params. _service.disable_retries() - self.test_get_geo_as_stream_required_params() + self.test_head_scheduler_document_all_params() @responses.activate - def test_get_geo_as_stream_value_error(self): + def test_head_scheduler_document_value_error(self): """ - test_get_geo_as_stream_value_error() + test_head_scheduler_document_value_error() """ # Set up mock - url = preprocess_url('/testString/_design/testString/_geo/testString') - mock_response = '{"foo": "this is a mock response for JSON streaming"}' - responses.add(responses.GET, - url, - body=mock_response, - content_type='application/json', - status=200) + url = preprocess_url('/_scheduler/docs/_replicator/testString') + responses.add( + responses.HEAD, + url, + status=200, + ) # Set up parameter values - db = 'testString' - ddoc = 'testString' - index = 'testString' + doc_id = 'testString' # Pass in all but one required param and check for a ValueError req_param_dict = { - "db": db, - "ddoc": ddoc, - "index": index, + "doc_id": doc_id, } for param in req_param_dict.keys(): - req_copy = {key:val if key is not param else None for (key,val) in req_param_dict.items()} + req_copy = {key: val if key is not param else None for (key, val) in req_param_dict.items()} with pytest.raises(ValueError): - _service.get_geo_as_stream(**req_copy) + _service.head_scheduler_document(**req_copy) - def test_get_geo_as_stream_value_error_with_retries(self): - # Enable retries and run test_get_geo_as_stream_value_error. + def test_head_scheduler_document_value_error_with_retries(self): + # Enable retries and run test_head_scheduler_document_value_error. _service.enable_retries() - self.test_get_geo_as_stream_value_error() + self.test_head_scheduler_document_value_error() - # Disable retries and run test_get_geo_as_stream_value_error. + # Disable retries and run test_head_scheduler_document_value_error. _service.disable_retries() - self.test_get_geo_as_stream_value_error() + self.test_head_scheduler_document_value_error() -class TestPostGeoCleanup(): + +class TestHeadSchedulerJob: """ - Test Class for post_geo_cleanup + Test Class for head_scheduler_job """ @responses.activate - def test_post_geo_cleanup_all_params(self): + def test_head_scheduler_job_all_params(self): """ - post_geo_cleanup() + head_scheduler_job() """ # Set up mock - url = preprocess_url('/testString/_geo_cleanup') - mock_response = '{"ok": true}' - responses.add(responses.POST, - url, - body=mock_response, - content_type='application/json', - status=202) + url = preprocess_url('/_scheduler/jobs/testString') + responses.add( + responses.HEAD, + url, + status=200, + ) # Set up parameter values - db = 'testString' + job_id = 'testString' # Invoke method - response = _service.post_geo_cleanup( - db, - headers={} + response = _service.head_scheduler_job( + job_id, + headers={}, ) # Check for correct operation assert len(responses.calls) == 1 - assert response.status_code == 202 + assert response.status_code == 200 - def test_post_geo_cleanup_all_params_with_retries(self): - # Enable retries and run test_post_geo_cleanup_all_params. + def test_head_scheduler_job_all_params_with_retries(self): + # Enable retries and run test_head_scheduler_job_all_params. _service.enable_retries() - self.test_post_geo_cleanup_all_params() + self.test_head_scheduler_job_all_params() - # Disable retries and run test_post_geo_cleanup_all_params. + # Disable retries and run test_head_scheduler_job_all_params. _service.disable_retries() - self.test_post_geo_cleanup_all_params() + self.test_head_scheduler_job_all_params() @responses.activate - def test_post_geo_cleanup_value_error(self): + def test_head_scheduler_job_value_error(self): """ - test_post_geo_cleanup_value_error() + test_head_scheduler_job_value_error() """ # Set up mock - url = preprocess_url('/testString/_geo_cleanup') - mock_response = '{"ok": true}' - responses.add(responses.POST, - url, - body=mock_response, - content_type='application/json', - status=202) + url = preprocess_url('/_scheduler/jobs/testString') + responses.add( + responses.HEAD, + url, + status=200, + ) # Set up parameter values - db = 'testString' + job_id = 'testString' # Pass in all but one required param and check for a ValueError req_param_dict = { - "db": db, + "job_id": job_id, } for param in req_param_dict.keys(): - req_copy = {key:val if key is not param else None for (key,val) in req_param_dict.items()} + req_copy = {key: val if key is not param else None for (key, val) in req_param_dict.items()} with pytest.raises(ValueError): - _service.post_geo_cleanup(**req_copy) + _service.head_scheduler_job(**req_copy) - def test_post_geo_cleanup_value_error_with_retries(self): - # Enable retries and run test_post_geo_cleanup_value_error. + def test_head_scheduler_job_value_error_with_retries(self): + # Enable retries and run test_head_scheduler_job_value_error. _service.enable_retries() - self.test_post_geo_cleanup_value_error() + self.test_head_scheduler_job_value_error() - # Disable retries and run test_post_geo_cleanup_value_error. + # Disable retries and run test_head_scheduler_job_value_error. _service.disable_retries() - self.test_post_geo_cleanup_value_error() + self.test_head_scheduler_job_value_error() + -class TestGetGeoIndexInformation(): +class TestPostReplicator: """ - Test Class for get_geo_index_information + Test Class for post_replicator """ @responses.activate - def test_get_geo_index_information_all_params(self): + def test_post_replicator_all_params(self): """ - get_geo_index_information() + post_replicator() """ # Set up mock - url = preprocess_url('/testString/_design/testString/_geo_info/testString') - mock_response = '{"geo_index": {"data_size": 0, "disk_size": 0, "doc_count": 0}, "name": "name"}' - responses.add(responses.GET, - url, - body=mock_response, - content_type='application/json', - status=200) + url = preprocess_url('/_replicator') + mock_response = '{"id": "id", "rev": "rev", "ok": true, "caused_by": "caused_by", "error": "error", "reason": "reason", "ref": 0}' + responses.add( + responses.POST, + url, + body=mock_response, + content_type='application/json', + status=201, + ) + + # Construct a dict representation of a Attachment model + attachment_model = {} + attachment_model['content_type'] = 'testString' + attachment_model['data'] = 'VGhpcyBpcyBhIG1vY2sgYnl0ZSBhcnJheSB2YWx1ZS4=' + attachment_model['digest'] = 'testString' + attachment_model['encoded_length'] = 0 + attachment_model['encoding'] = 'testString' + attachment_model['follows'] = True + attachment_model['length'] = 0 + attachment_model['revpos'] = 1 + attachment_model['stub'] = True + + # Construct a dict representation of a Revisions model + revisions_model = {} + revisions_model['ids'] = ['testString'] + revisions_model['start'] = 1 + + # Construct a dict representation of a DocumentRevisionStatus model + document_revision_status_model = {} + document_revision_status_model['rev'] = 'testString' + document_revision_status_model['status'] = 'available' + + # Construct a dict representation of a ReplicationCreateTargetParameters model + replication_create_target_parameters_model = {} + replication_create_target_parameters_model['n'] = 3 + replication_create_target_parameters_model['partitioned'] = False + replication_create_target_parameters_model['q'] = 1 + + # Construct a dict representation of a ReplicationDatabaseAuthBasic model + replication_database_auth_basic_model = {} + replication_database_auth_basic_model['password'] = 'testString' + replication_database_auth_basic_model['username'] = 'testString' + + # Construct a dict representation of a ReplicationDatabaseAuthIam model + replication_database_auth_iam_model = {} + replication_database_auth_iam_model['api_key'] = 'testString' + + # Construct a dict representation of a ReplicationDatabaseAuth model + replication_database_auth_model = {} + replication_database_auth_model['basic'] = replication_database_auth_basic_model + replication_database_auth_model['iam'] = replication_database_auth_iam_model + + # Construct a dict representation of a ReplicationDatabase model + replication_database_model = {} + replication_database_model['auth'] = replication_database_auth_model + replication_database_model['headers'] = {'key1': 'testString'} + replication_database_model['url'] = 'https://my-source-instance.cloudantnosqldb.appdomain.cloud.example/animaldb' + + # Construct a dict representation of a UserContext model + user_context_model = {} + user_context_model['db'] = 'testString' + user_context_model['name'] = 'john' + user_context_model['roles'] = ['_replicator'] + + # Construct a dict representation of a ReplicationDocument model + replication_document_model = {} + replication_document_model['_attachments'] = {'key1': attachment_model} + replication_document_model['_conflicts'] = ['testString'] + replication_document_model['_deleted'] = True + replication_document_model['_deleted_conflicts'] = ['testString'] + replication_document_model['_id'] = 'testString' + replication_document_model['_local_seq'] = 'testString' + replication_document_model['_rev'] = 'testString' + replication_document_model['_revisions'] = revisions_model + replication_document_model['_revs_info'] = [document_revision_status_model] + replication_document_model['cancel'] = False + replication_document_model['checkpoint_interval'] = 4500 + replication_document_model['connection_timeout'] = 15000 + replication_document_model['continuous'] = True + replication_document_model['create_target'] = True + replication_document_model['create_target_params'] = replication_create_target_parameters_model + replication_document_model['doc_ids'] = ['badger', 'lemur', 'llama'] + replication_document_model['filter'] = 'ddoc/my_filter' + replication_document_model['http_connections'] = 10 + replication_document_model['owner'] = 'testString' + replication_document_model['query_params'] = {'key1': 'testString'} + replication_document_model['retries_per_request'] = 3 + replication_document_model['selector'] = {'_id': {'$regex': 'docid'}} + replication_document_model['since_seq'] = '34-g1AAAAGjeJzLYWBgYMlgTmGQT0lKzi9KdU' + replication_document_model['socket_options'] = '[{keepalive, true}, {nodelay, false}]' + replication_document_model['source'] = replication_database_model + replication_document_model['source_proxy'] = 'testString' + replication_document_model['target'] = replication_database_model + replication_document_model['target_proxy'] = 'testString' + replication_document_model['use_bulk_get'] = True + replication_document_model['use_checkpoints'] = False + replication_document_model['user_ctx'] = user_context_model + replication_document_model['winning_revs_only'] = False + replication_document_model['worker_batch_size'] = 400 + replication_document_model['worker_processes'] = 3 + replication_document_model['foo'] = 'testString' # Set up parameter values - db = 'testString' - ddoc = 'testString' - index = 'testString' + replication_document = replication_document_model + batch = 'ok' # Invoke method - response = _service.get_geo_index_information( - db, - ddoc, - index, - headers={} + response = _service.post_replicator( + replication_document, + batch=batch, + headers={}, ) # Check for correct operation assert len(responses.calls) == 1 - assert response.status_code == 200 + assert response.status_code == 201 + # Validate query params + query_string = responses.calls[0].request.url.split('?', 1)[1] + query_string = urllib.parse.unquote_plus(query_string) + assert 'batch={}'.format(batch) in query_string + # Decompress gzip compressed request body. + raw_body = responses.calls[0].request.body + responses.calls[0].request.body = gzip.decompress(raw_body.read() if isinstance(raw_body, io.IOBase) else raw_body) + + # Validate body params + req_body = json.loads(str(responses.calls[0].request.body, 'utf-8')) + assert req_body == replication_document - def test_get_geo_index_information_all_params_with_retries(self): - # Enable retries and run test_get_geo_index_information_all_params. + def test_post_replicator_all_params_with_retries(self): + # Enable retries and run test_post_replicator_all_params. _service.enable_retries() - self.test_get_geo_index_information_all_params() + self.test_post_replicator_all_params() - # Disable retries and run test_get_geo_index_information_all_params. + # Disable retries and run test_post_replicator_all_params. _service.disable_retries() - self.test_get_geo_index_information_all_params() + self.test_post_replicator_all_params() @responses.activate - def test_get_geo_index_information_value_error(self): + def test_post_replicator_required_params(self): """ - test_get_geo_index_information_value_error() + test_post_replicator_required_params() """ # Set up mock - url = preprocess_url('/testString/_design/testString/_geo_info/testString') - mock_response = '{"geo_index": {"data_size": 0, "disk_size": 0, "doc_count": 0}, "name": "name"}' - responses.add(responses.GET, - url, - body=mock_response, - content_type='application/json', - status=200) - - # Set up parameter values - db = 'testString' - ddoc = 'testString' - index = 'testString' + url = preprocess_url('/_replicator') + mock_response = '{"id": "id", "rev": "rev", "ok": true, "caused_by": "caused_by", "error": "error", "reason": "reason", "ref": 0}' + responses.add( + responses.POST, + url, + body=mock_response, + content_type='application/json', + status=201, + ) - # Pass in all but one required param and check for a ValueError - req_param_dict = { - "db": db, - "ddoc": ddoc, - "index": index, - } - for param in req_param_dict.keys(): - req_copy = {key:val if key is not param else None for (key,val) in req_param_dict.items()} - with pytest.raises(ValueError): - _service.get_geo_index_information(**req_copy) - - def test_get_geo_index_information_value_error_with_retries(self): - # Enable retries and run test_get_geo_index_information_value_error. - _service.enable_retries() - self.test_get_geo_index_information_value_error() - - # Disable retries and run test_get_geo_index_information_value_error. - _service.disable_retries() - self.test_get_geo_index_information_value_error() - -# endregion -############################################################################## -# End of Service: Geospatial -############################################################################## - -############################################################################## -# Start of Service: Replication -############################################################################## -# region - -class TestNewInstance(): - """ - Test Class for new_instance - """ - - def test_new_instance(self): - """ - new_instance() - """ - os.environ['TEST_SERVICE_AUTH_TYPE'] = 'noAuth' - - service = CloudantV1.new_instance( - service_name='TEST_SERVICE', - ) - - assert service is not None - assert isinstance(service, CloudantV1) + # Construct a dict representation of a Attachment model + attachment_model = {} + attachment_model['content_type'] = 'testString' + attachment_model['data'] = 'VGhpcyBpcyBhIG1vY2sgYnl0ZSBhcnJheSB2YWx1ZS4=' + attachment_model['digest'] = 'testString' + attachment_model['encoded_length'] = 0 + attachment_model['encoding'] = 'testString' + attachment_model['follows'] = True + attachment_model['length'] = 0 + attachment_model['revpos'] = 1 + attachment_model['stub'] = True - def test_new_instance_without_authenticator(self): - """ - new_instance_without_authenticator() - """ - with pytest.raises(ValueError, match='authenticator must be provided'): - service = CloudantV1.new_instance( - service_name='TEST_SERVICE_NOT_FOUND', - ) + # Construct a dict representation of a Revisions model + revisions_model = {} + revisions_model['ids'] = ['testString'] + revisions_model['start'] = 1 -class TestHeadReplicationDocument(): - """ - Test Class for head_replication_document - """ + # Construct a dict representation of a DocumentRevisionStatus model + document_revision_status_model = {} + document_revision_status_model['rev'] = 'testString' + document_revision_status_model['status'] = 'available' - @responses.activate - def test_head_replication_document_all_params(self): - """ - head_replication_document() - """ - # Set up mock - url = preprocess_url('/_replicator/testString') - responses.add(responses.HEAD, - url, - status=200) + # Construct a dict representation of a ReplicationCreateTargetParameters model + replication_create_target_parameters_model = {} + replication_create_target_parameters_model['n'] = 3 + replication_create_target_parameters_model['partitioned'] = False + replication_create_target_parameters_model['q'] = 1 - # Set up parameter values - doc_id = 'testString' - if_none_match = 'testString' + # Construct a dict representation of a ReplicationDatabaseAuthBasic model + replication_database_auth_basic_model = {} + replication_database_auth_basic_model['password'] = 'testString' + replication_database_auth_basic_model['username'] = 'testString' - # Invoke method - response = _service.head_replication_document( - doc_id, - if_none_match=if_none_match, - headers={} - ) + # Construct a dict representation of a ReplicationDatabaseAuthIam model + replication_database_auth_iam_model = {} + replication_database_auth_iam_model['api_key'] = 'testString' - # Check for correct operation - assert len(responses.calls) == 1 - assert response.status_code == 200 + # Construct a dict representation of a ReplicationDatabaseAuth model + replication_database_auth_model = {} + replication_database_auth_model['basic'] = replication_database_auth_basic_model + replication_database_auth_model['iam'] = replication_database_auth_iam_model - def test_head_replication_document_all_params_with_retries(self): - # Enable retries and run test_head_replication_document_all_params. - _service.enable_retries() - self.test_head_replication_document_all_params() + # Construct a dict representation of a ReplicationDatabase model + replication_database_model = {} + replication_database_model['auth'] = replication_database_auth_model + replication_database_model['headers'] = {'key1': 'testString'} + replication_database_model['url'] = 'https://my-source-instance.cloudantnosqldb.appdomain.cloud.example/animaldb' - # Disable retries and run test_head_replication_document_all_params. - _service.disable_retries() - self.test_head_replication_document_all_params() + # Construct a dict representation of a UserContext model + user_context_model = {} + user_context_model['db'] = 'testString' + user_context_model['name'] = 'john' + user_context_model['roles'] = ['_replicator'] - @responses.activate - def test_head_replication_document_required_params(self): - """ - test_head_replication_document_required_params() - """ - # Set up mock - url = preprocess_url('/_replicator/testString') - responses.add(responses.HEAD, - url, - status=200) + # Construct a dict representation of a ReplicationDocument model + replication_document_model = {} + replication_document_model['_attachments'] = {'key1': attachment_model} + replication_document_model['_conflicts'] = ['testString'] + replication_document_model['_deleted'] = True + replication_document_model['_deleted_conflicts'] = ['testString'] + replication_document_model['_id'] = 'testString' + replication_document_model['_local_seq'] = 'testString' + replication_document_model['_rev'] = 'testString' + replication_document_model['_revisions'] = revisions_model + replication_document_model['_revs_info'] = [document_revision_status_model] + replication_document_model['cancel'] = False + replication_document_model['checkpoint_interval'] = 4500 + replication_document_model['connection_timeout'] = 15000 + replication_document_model['continuous'] = True + replication_document_model['create_target'] = True + replication_document_model['create_target_params'] = replication_create_target_parameters_model + replication_document_model['doc_ids'] = ['badger', 'lemur', 'llama'] + replication_document_model['filter'] = 'ddoc/my_filter' + replication_document_model['http_connections'] = 10 + replication_document_model['owner'] = 'testString' + replication_document_model['query_params'] = {'key1': 'testString'} + replication_document_model['retries_per_request'] = 3 + replication_document_model['selector'] = {'_id': {'$regex': 'docid'}} + replication_document_model['since_seq'] = '34-g1AAAAGjeJzLYWBgYMlgTmGQT0lKzi9KdU' + replication_document_model['socket_options'] = '[{keepalive, true}, {nodelay, false}]' + replication_document_model['source'] = replication_database_model + replication_document_model['source_proxy'] = 'testString' + replication_document_model['target'] = replication_database_model + replication_document_model['target_proxy'] = 'testString' + replication_document_model['use_bulk_get'] = True + replication_document_model['use_checkpoints'] = False + replication_document_model['user_ctx'] = user_context_model + replication_document_model['winning_revs_only'] = False + replication_document_model['worker_batch_size'] = 400 + replication_document_model['worker_processes'] = 3 + replication_document_model['foo'] = 'testString' # Set up parameter values - doc_id = 'testString' + replication_document = replication_document_model # Invoke method - response = _service.head_replication_document( - doc_id, - headers={} + response = _service.post_replicator( + replication_document, + headers={}, ) # Check for correct operation assert len(responses.calls) == 1 - assert response.status_code == 200 - - def test_head_replication_document_required_params_with_retries(self): - # Enable retries and run test_head_replication_document_required_params. - _service.enable_retries() - self.test_head_replication_document_required_params() - - # Disable retries and run test_head_replication_document_required_params. - _service.disable_retries() - self.test_head_replication_document_required_params() - - @responses.activate - def test_head_replication_document_value_error(self): - """ - test_head_replication_document_value_error() - """ - # Set up mock - url = preprocess_url('/_replicator/testString') - responses.add(responses.HEAD, - url, - status=200) - - # Set up parameter values - doc_id = 'testString' + assert response.status_code == 201 + # Decompress gzip compressed request body. + raw_body = responses.calls[0].request.body + responses.calls[0].request.body = gzip.decompress(raw_body.read() if isinstance(raw_body, io.IOBase) else raw_body) - # Pass in all but one required param and check for a ValueError - req_param_dict = { - "doc_id": doc_id, - } - for param in req_param_dict.keys(): - req_copy = {key:val if key is not param else None for (key,val) in req_param_dict.items()} - with pytest.raises(ValueError): - _service.head_replication_document(**req_copy) + # Validate body params + req_body = json.loads(str(responses.calls[0].request.body, 'utf-8')) + assert req_body == replication_document - def test_head_replication_document_value_error_with_retries(self): - # Enable retries and run test_head_replication_document_value_error. + def test_post_replicator_required_params_with_retries(self): + # Enable retries and run test_post_replicator_required_params. _service.enable_retries() - self.test_head_replication_document_value_error() + self.test_post_replicator_required_params() - # Disable retries and run test_head_replication_document_value_error. + # Disable retries and run test_post_replicator_required_params. _service.disable_retries() - self.test_head_replication_document_value_error() - -class TestHeadSchedulerDocument(): - """ - Test Class for head_scheduler_document - """ + self.test_post_replicator_required_params() @responses.activate - def test_head_scheduler_document_all_params(self): + def test_post_replicator_value_error(self): """ - head_scheduler_document() + test_post_replicator_value_error() """ # Set up mock - url = preprocess_url('/_scheduler/docs/_replicator/testString') - responses.add(responses.HEAD, - url, - status=200) - - # Set up parameter values - doc_id = 'testString' - - # Invoke method - response = _service.head_scheduler_document( - doc_id, - headers={} + url = preprocess_url('/_replicator') + mock_response = '{"id": "id", "rev": "rev", "ok": true, "caused_by": "caused_by", "error": "error", "reason": "reason", "ref": 0}' + responses.add( + responses.POST, + url, + body=mock_response, + content_type='application/json', + status=201, ) - # Check for correct operation - assert len(responses.calls) == 1 - assert response.status_code == 200 - - def test_head_scheduler_document_all_params_with_retries(self): - # Enable retries and run test_head_scheduler_document_all_params. - _service.enable_retries() - self.test_head_scheduler_document_all_params() - - # Disable retries and run test_head_scheduler_document_all_params. - _service.disable_retries() - self.test_head_scheduler_document_all_params() - - @responses.activate - def test_head_scheduler_document_value_error(self): - """ - test_head_scheduler_document_value_error() - """ - # Set up mock - url = preprocess_url('/_scheduler/docs/_replicator/testString') - responses.add(responses.HEAD, - url, - status=200) - - # Set up parameter values - doc_id = 'testString' - - # Pass in all but one required param and check for a ValueError - req_param_dict = { - "doc_id": doc_id, - } - for param in req_param_dict.keys(): - req_copy = {key:val if key is not param else None for (key,val) in req_param_dict.items()} - with pytest.raises(ValueError): - _service.head_scheduler_document(**req_copy) - - def test_head_scheduler_document_value_error_with_retries(self): - # Enable retries and run test_head_scheduler_document_value_error. - _service.enable_retries() - self.test_head_scheduler_document_value_error() - - # Disable retries and run test_head_scheduler_document_value_error. - _service.disable_retries() - self.test_head_scheduler_document_value_error() - -class TestHeadSchedulerJob(): - """ - Test Class for head_scheduler_job - """ + # Construct a dict representation of a Attachment model + attachment_model = {} + attachment_model['content_type'] = 'testString' + attachment_model['data'] = 'VGhpcyBpcyBhIG1vY2sgYnl0ZSBhcnJheSB2YWx1ZS4=' + attachment_model['digest'] = 'testString' + attachment_model['encoded_length'] = 0 + attachment_model['encoding'] = 'testString' + attachment_model['follows'] = True + attachment_model['length'] = 0 + attachment_model['revpos'] = 1 + attachment_model['stub'] = True - @responses.activate - def test_head_scheduler_job_all_params(self): - """ - head_scheduler_job() - """ - # Set up mock - url = preprocess_url('/_scheduler/jobs/testString') - responses.add(responses.HEAD, - url, - status=200) + # Construct a dict representation of a Revisions model + revisions_model = {} + revisions_model['ids'] = ['testString'] + revisions_model['start'] = 1 - # Set up parameter values - job_id = 'testString' + # Construct a dict representation of a DocumentRevisionStatus model + document_revision_status_model = {} + document_revision_status_model['rev'] = 'testString' + document_revision_status_model['status'] = 'available' - # Invoke method - response = _service.head_scheduler_job( - job_id, - headers={} - ) + # Construct a dict representation of a ReplicationCreateTargetParameters model + replication_create_target_parameters_model = {} + replication_create_target_parameters_model['n'] = 3 + replication_create_target_parameters_model['partitioned'] = False + replication_create_target_parameters_model['q'] = 1 - # Check for correct operation - assert len(responses.calls) == 1 - assert response.status_code == 200 + # Construct a dict representation of a ReplicationDatabaseAuthBasic model + replication_database_auth_basic_model = {} + replication_database_auth_basic_model['password'] = 'testString' + replication_database_auth_basic_model['username'] = 'testString' - def test_head_scheduler_job_all_params_with_retries(self): - # Enable retries and run test_head_scheduler_job_all_params. - _service.enable_retries() - self.test_head_scheduler_job_all_params() + # Construct a dict representation of a ReplicationDatabaseAuthIam model + replication_database_auth_iam_model = {} + replication_database_auth_iam_model['api_key'] = 'testString' - # Disable retries and run test_head_scheduler_job_all_params. - _service.disable_retries() - self.test_head_scheduler_job_all_params() + # Construct a dict representation of a ReplicationDatabaseAuth model + replication_database_auth_model = {} + replication_database_auth_model['basic'] = replication_database_auth_basic_model + replication_database_auth_model['iam'] = replication_database_auth_iam_model - @responses.activate - def test_head_scheduler_job_value_error(self): - """ - test_head_scheduler_job_value_error() - """ - # Set up mock - url = preprocess_url('/_scheduler/jobs/testString') - responses.add(responses.HEAD, - url, - status=200) + # Construct a dict representation of a ReplicationDatabase model + replication_database_model = {} + replication_database_model['auth'] = replication_database_auth_model + replication_database_model['headers'] = {'key1': 'testString'} + replication_database_model['url'] = 'https://my-source-instance.cloudantnosqldb.appdomain.cloud.example/animaldb' + + # Construct a dict representation of a UserContext model + user_context_model = {} + user_context_model['db'] = 'testString' + user_context_model['name'] = 'john' + user_context_model['roles'] = ['_replicator'] + + # Construct a dict representation of a ReplicationDocument model + replication_document_model = {} + replication_document_model['_attachments'] = {'key1': attachment_model} + replication_document_model['_conflicts'] = ['testString'] + replication_document_model['_deleted'] = True + replication_document_model['_deleted_conflicts'] = ['testString'] + replication_document_model['_id'] = 'testString' + replication_document_model['_local_seq'] = 'testString' + replication_document_model['_rev'] = 'testString' + replication_document_model['_revisions'] = revisions_model + replication_document_model['_revs_info'] = [document_revision_status_model] + replication_document_model['cancel'] = False + replication_document_model['checkpoint_interval'] = 4500 + replication_document_model['connection_timeout'] = 15000 + replication_document_model['continuous'] = True + replication_document_model['create_target'] = True + replication_document_model['create_target_params'] = replication_create_target_parameters_model + replication_document_model['doc_ids'] = ['badger', 'lemur', 'llama'] + replication_document_model['filter'] = 'ddoc/my_filter' + replication_document_model['http_connections'] = 10 + replication_document_model['owner'] = 'testString' + replication_document_model['query_params'] = {'key1': 'testString'} + replication_document_model['retries_per_request'] = 3 + replication_document_model['selector'] = {'_id': {'$regex': 'docid'}} + replication_document_model['since_seq'] = '34-g1AAAAGjeJzLYWBgYMlgTmGQT0lKzi9KdU' + replication_document_model['socket_options'] = '[{keepalive, true}, {nodelay, false}]' + replication_document_model['source'] = replication_database_model + replication_document_model['source_proxy'] = 'testString' + replication_document_model['target'] = replication_database_model + replication_document_model['target_proxy'] = 'testString' + replication_document_model['use_bulk_get'] = True + replication_document_model['use_checkpoints'] = False + replication_document_model['user_ctx'] = user_context_model + replication_document_model['winning_revs_only'] = False + replication_document_model['worker_batch_size'] = 400 + replication_document_model['worker_processes'] = 3 + replication_document_model['foo'] = 'testString' # Set up parameter values - job_id = 'testString' + replication_document = replication_document_model # Pass in all but one required param and check for a ValueError req_param_dict = { - "job_id": job_id, + "replication_document": replication_document, } for param in req_param_dict.keys(): - req_copy = {key:val if key is not param else None for (key,val) in req_param_dict.items()} + req_copy = {key: val if key is not param else None for (key, val) in req_param_dict.items()} with pytest.raises(ValueError): - _service.head_scheduler_job(**req_copy) + _service.post_replicator(**req_copy) - def test_head_scheduler_job_value_error_with_retries(self): - # Enable retries and run test_head_scheduler_job_value_error. + def test_post_replicator_value_error_with_retries(self): + # Enable retries and run test_post_replicator_value_error. _service.enable_retries() - self.test_head_scheduler_job_value_error() + self.test_post_replicator_value_error() - # Disable retries and run test_head_scheduler_job_value_error. + # Disable retries and run test_post_replicator_value_error. _service.disable_retries() - self.test_head_scheduler_job_value_error() + self.test_post_replicator_value_error() -class TestDeleteReplicationDocument(): + +class TestDeleteReplicationDocument: """ Test Class for delete_replication_document """ @@ -9557,12 +10001,14 @@ def test_delete_replication_document_all_params(self): """ # Set up mock url = preprocess_url('/_replicator/testString') - mock_response = '{"id": "id", "rev": "rev", "ok": true, "caused_by": "caused_by", "error": "error", "reason": "reason"}' - responses.add(responses.DELETE, - url, - body=mock_response, - content_type='application/json', - status=200) + mock_response = '{"id": "id", "rev": "rev", "ok": true, "caused_by": "caused_by", "error": "error", "reason": "reason", "ref": 0}' + responses.add( + responses.DELETE, + url, + body=mock_response, + content_type='application/json', + status=200, + ) # Set up parameter values doc_id = 'testString' @@ -9576,14 +10022,14 @@ def test_delete_replication_document_all_params(self): if_match=if_match, batch=batch, rev=rev, - headers={} + headers={}, ) # Check for correct operation assert len(responses.calls) == 1 assert response.status_code == 200 # Validate query params - query_string = responses.calls[0].request.url.split('?',1)[1] + query_string = responses.calls[0].request.url.split('?', 1)[1] query_string = urllib.parse.unquote_plus(query_string) assert 'batch={}'.format(batch) in query_string assert 'rev={}'.format(rev) in query_string @@ -9604,12 +10050,14 @@ def test_delete_replication_document_required_params(self): """ # Set up mock url = preprocess_url('/_replicator/testString') - mock_response = '{"id": "id", "rev": "rev", "ok": true, "caused_by": "caused_by", "error": "error", "reason": "reason"}' - responses.add(responses.DELETE, - url, - body=mock_response, - content_type='application/json', - status=200) + mock_response = '{"id": "id", "rev": "rev", "ok": true, "caused_by": "caused_by", "error": "error", "reason": "reason", "ref": 0}' + responses.add( + responses.DELETE, + url, + body=mock_response, + content_type='application/json', + status=200, + ) # Set up parameter values doc_id = 'testString' @@ -9617,7 +10065,7 @@ def test_delete_replication_document_required_params(self): # Invoke method response = _service.delete_replication_document( doc_id, - headers={} + headers={}, ) # Check for correct operation @@ -9640,12 +10088,14 @@ def test_delete_replication_document_value_error(self): """ # Set up mock url = preprocess_url('/_replicator/testString') - mock_response = '{"id": "id", "rev": "rev", "ok": true, "caused_by": "caused_by", "error": "error", "reason": "reason"}' - responses.add(responses.DELETE, - url, - body=mock_response, - content_type='application/json', - status=200) + mock_response = '{"id": "id", "rev": "rev", "ok": true, "caused_by": "caused_by", "error": "error", "reason": "reason", "ref": 0}' + responses.add( + responses.DELETE, + url, + body=mock_response, + content_type='application/json', + status=200, + ) # Set up parameter values doc_id = 'testString' @@ -9655,7 +10105,7 @@ def test_delete_replication_document_value_error(self): "doc_id": doc_id, } for param in req_param_dict.keys(): - req_copy = {key:val if key is not param else None for (key,val) in req_param_dict.items()} + req_copy = {key: val if key is not param else None for (key, val) in req_param_dict.items()} with pytest.raises(ValueError): _service.delete_replication_document(**req_copy) @@ -9668,7 +10118,8 @@ def test_delete_replication_document_value_error_with_retries(self): _service.disable_retries() self.test_delete_replication_document_value_error() -class TestGetReplicationDocument(): + +class TestGetReplicationDocument: """ Test Class for get_replication_document """ @@ -9680,12 +10131,14 @@ def test_get_replication_document_all_params(self): """ # Set up mock url = preprocess_url('/_replicator/testString') - mock_response = '{"_attachments": {"mapKey": {"content_type": "content_type", "data": "VGhpcyBpcyBhbiBlbmNvZGVkIGJ5dGUgYXJyYXku", "digest": "digest", "encoded_length": 0, "encoding": "encoding", "follows": false, "length": 0, "revpos": 1, "stub": true}}, "_conflicts": ["conflicts"], "_deleted": false, "_deleted_conflicts": ["deleted_conflicts"], "_id": "id", "_local_seq": "local_seq", "_rev": "rev", "_revisions": {"ids": ["ids"], "start": 1}, "_revs_info": [{"rev": "rev", "status": "available"}], "cancel": true, "checkpoint_interval": 0, "connection_timeout": 0, "continuous": false, "create_target": false, "create_target_params": {"n": 1, "partitioned": false, "q": 1}, "doc_ids": ["doc_ids"], "filter": "filter", "http_connections": 1, "query_params": {"mapKey": "inner"}, "retries_per_request": 0, "selector": {"mapKey": "anyValue"}, "since_seq": "since_seq", "socket_options": "socket_options", "source": {"auth": {"basic": {"password": "password", "username": "username"}, "iam": {"api_key": "api_key"}}, "headers": {"mapKey": "inner"}, "url": "url"}, "source_proxy": "source_proxy", "target": {"auth": {"basic": {"password": "password", "username": "username"}, "iam": {"api_key": "api_key"}}, "headers": {"mapKey": "inner"}, "url": "url"}, "target_proxy": "target_proxy", "use_checkpoints": true, "user_ctx": {"db": "db", "name": "name", "roles": ["_reader"]}, "worker_batch_size": 1, "worker_processes": 1}' - responses.add(responses.GET, - url, - body=mock_response, - content_type='application/json', - status=200) + mock_response = '{"_attachments": {"mapKey": {"content_type": "content_type", "data": "VGhpcyBpcyBhIG1vY2sgYnl0ZSBhcnJheSB2YWx1ZS4=", "digest": "digest", "encoded_length": 0, "encoding": "encoding", "follows": false, "length": 0, "revpos": 1, "stub": true}}, "_conflicts": ["conflicts"], "_deleted": true, "_deleted_conflicts": ["deleted_conflicts"], "_id": "_id", "_local_seq": "_local_seq", "_rev": "_rev", "_revisions": {"ids": ["ids"], "start": 1}, "_revs_info": [{"rev": "rev", "status": "available"}], "cancel": true, "checkpoint_interval": 30000, "connection_timeout": 30000, "continuous": false, "create_target": false, "create_target_params": {"n": 3, "partitioned": false, "q": 16}, "doc_ids": ["doc_ids"], "filter": "filter", "http_connections": 20, "owner": "owner", "query_params": {"mapKey": "inner"}, "retries_per_request": 5, "selector": {"anyKey": "anyValue"}, "since_seq": "since_seq", "socket_options": "socket_options", "source": {"auth": {"basic": {"password": "password", "username": "username"}, "iam": {"api_key": "api_key"}}, "headers": {"mapKey": "inner"}, "url": "url"}, "source_proxy": "source_proxy", "target": {"auth": {"basic": {"password": "password", "username": "username"}, "iam": {"api_key": "api_key"}}, "headers": {"mapKey": "inner"}, "url": "url"}, "target_proxy": "target_proxy", "use_bulk_get": true, "use_checkpoints": true, "user_ctx": {"db": "db", "name": "name", "roles": ["_reader"]}, "winning_revs_only": false, "worker_batch_size": 500, "worker_processes": 4}' + responses.add( + responses.GET, + url, + body=mock_response, + content_type='application/json', + status=200, + ) # Set up parameter values doc_id = 'testString' @@ -9715,14 +10168,14 @@ def test_get_replication_document_all_params(self): rev=rev, revs=revs, revs_info=revs_info, - headers={} + headers={}, ) # Check for correct operation assert len(responses.calls) == 1 assert response.status_code == 200 # Validate query params - query_string = responses.calls[0].request.url.split('?',1)[1] + query_string = responses.calls[0].request.url.split('?', 1)[1] query_string = urllib.parse.unquote_plus(query_string) assert 'attachments={}'.format('true' if attachments else 'false') in query_string assert 'att_encoding_info={}'.format('true' if att_encoding_info else 'false') in query_string @@ -9751,12 +10204,14 @@ def test_get_replication_document_required_params(self): """ # Set up mock url = preprocess_url('/_replicator/testString') - mock_response = '{"_attachments": {"mapKey": {"content_type": "content_type", "data": "VGhpcyBpcyBhbiBlbmNvZGVkIGJ5dGUgYXJyYXku", "digest": "digest", "encoded_length": 0, "encoding": "encoding", "follows": false, "length": 0, "revpos": 1, "stub": true}}, "_conflicts": ["conflicts"], "_deleted": false, "_deleted_conflicts": ["deleted_conflicts"], "_id": "id", "_local_seq": "local_seq", "_rev": "rev", "_revisions": {"ids": ["ids"], "start": 1}, "_revs_info": [{"rev": "rev", "status": "available"}], "cancel": true, "checkpoint_interval": 0, "connection_timeout": 0, "continuous": false, "create_target": false, "create_target_params": {"n": 1, "partitioned": false, "q": 1}, "doc_ids": ["doc_ids"], "filter": "filter", "http_connections": 1, "query_params": {"mapKey": "inner"}, "retries_per_request": 0, "selector": {"mapKey": "anyValue"}, "since_seq": "since_seq", "socket_options": "socket_options", "source": {"auth": {"basic": {"password": "password", "username": "username"}, "iam": {"api_key": "api_key"}}, "headers": {"mapKey": "inner"}, "url": "url"}, "source_proxy": "source_proxy", "target": {"auth": {"basic": {"password": "password", "username": "username"}, "iam": {"api_key": "api_key"}}, "headers": {"mapKey": "inner"}, "url": "url"}, "target_proxy": "target_proxy", "use_checkpoints": true, "user_ctx": {"db": "db", "name": "name", "roles": ["_reader"]}, "worker_batch_size": 1, "worker_processes": 1}' - responses.add(responses.GET, - url, - body=mock_response, - content_type='application/json', - status=200) + mock_response = '{"_attachments": {"mapKey": {"content_type": "content_type", "data": "VGhpcyBpcyBhIG1vY2sgYnl0ZSBhcnJheSB2YWx1ZS4=", "digest": "digest", "encoded_length": 0, "encoding": "encoding", "follows": false, "length": 0, "revpos": 1, "stub": true}}, "_conflicts": ["conflicts"], "_deleted": true, "_deleted_conflicts": ["deleted_conflicts"], "_id": "_id", "_local_seq": "_local_seq", "_rev": "_rev", "_revisions": {"ids": ["ids"], "start": 1}, "_revs_info": [{"rev": "rev", "status": "available"}], "cancel": true, "checkpoint_interval": 30000, "connection_timeout": 30000, "continuous": false, "create_target": false, "create_target_params": {"n": 3, "partitioned": false, "q": 16}, "doc_ids": ["doc_ids"], "filter": "filter", "http_connections": 20, "owner": "owner", "query_params": {"mapKey": "inner"}, "retries_per_request": 5, "selector": {"anyKey": "anyValue"}, "since_seq": "since_seq", "socket_options": "socket_options", "source": {"auth": {"basic": {"password": "password", "username": "username"}, "iam": {"api_key": "api_key"}}, "headers": {"mapKey": "inner"}, "url": "url"}, "source_proxy": "source_proxy", "target": {"auth": {"basic": {"password": "password", "username": "username"}, "iam": {"api_key": "api_key"}}, "headers": {"mapKey": "inner"}, "url": "url"}, "target_proxy": "target_proxy", "use_bulk_get": true, "use_checkpoints": true, "user_ctx": {"db": "db", "name": "name", "roles": ["_reader"]}, "winning_revs_only": false, "worker_batch_size": 500, "worker_processes": 4}' + responses.add( + responses.GET, + url, + body=mock_response, + content_type='application/json', + status=200, + ) # Set up parameter values doc_id = 'testString' @@ -9764,7 +10219,7 @@ def test_get_replication_document_required_params(self): # Invoke method response = _service.get_replication_document( doc_id, - headers={} + headers={}, ) # Check for correct operation @@ -9787,12 +10242,14 @@ def test_get_replication_document_value_error(self): """ # Set up mock url = preprocess_url('/_replicator/testString') - mock_response = '{"_attachments": {"mapKey": {"content_type": "content_type", "data": "VGhpcyBpcyBhbiBlbmNvZGVkIGJ5dGUgYXJyYXku", "digest": "digest", "encoded_length": 0, "encoding": "encoding", "follows": false, "length": 0, "revpos": 1, "stub": true}}, "_conflicts": ["conflicts"], "_deleted": false, "_deleted_conflicts": ["deleted_conflicts"], "_id": "id", "_local_seq": "local_seq", "_rev": "rev", "_revisions": {"ids": ["ids"], "start": 1}, "_revs_info": [{"rev": "rev", "status": "available"}], "cancel": true, "checkpoint_interval": 0, "connection_timeout": 0, "continuous": false, "create_target": false, "create_target_params": {"n": 1, "partitioned": false, "q": 1}, "doc_ids": ["doc_ids"], "filter": "filter", "http_connections": 1, "query_params": {"mapKey": "inner"}, "retries_per_request": 0, "selector": {"mapKey": "anyValue"}, "since_seq": "since_seq", "socket_options": "socket_options", "source": {"auth": {"basic": {"password": "password", "username": "username"}, "iam": {"api_key": "api_key"}}, "headers": {"mapKey": "inner"}, "url": "url"}, "source_proxy": "source_proxy", "target": {"auth": {"basic": {"password": "password", "username": "username"}, "iam": {"api_key": "api_key"}}, "headers": {"mapKey": "inner"}, "url": "url"}, "target_proxy": "target_proxy", "use_checkpoints": true, "user_ctx": {"db": "db", "name": "name", "roles": ["_reader"]}, "worker_batch_size": 1, "worker_processes": 1}' - responses.add(responses.GET, - url, - body=mock_response, - content_type='application/json', - status=200) + mock_response = '{"_attachments": {"mapKey": {"content_type": "content_type", "data": "VGhpcyBpcyBhIG1vY2sgYnl0ZSBhcnJheSB2YWx1ZS4=", "digest": "digest", "encoded_length": 0, "encoding": "encoding", "follows": false, "length": 0, "revpos": 1, "stub": true}}, "_conflicts": ["conflicts"], "_deleted": true, "_deleted_conflicts": ["deleted_conflicts"], "_id": "_id", "_local_seq": "_local_seq", "_rev": "_rev", "_revisions": {"ids": ["ids"], "start": 1}, "_revs_info": [{"rev": "rev", "status": "available"}], "cancel": true, "checkpoint_interval": 30000, "connection_timeout": 30000, "continuous": false, "create_target": false, "create_target_params": {"n": 3, "partitioned": false, "q": 16}, "doc_ids": ["doc_ids"], "filter": "filter", "http_connections": 20, "owner": "owner", "query_params": {"mapKey": "inner"}, "retries_per_request": 5, "selector": {"anyKey": "anyValue"}, "since_seq": "since_seq", "socket_options": "socket_options", "source": {"auth": {"basic": {"password": "password", "username": "username"}, "iam": {"api_key": "api_key"}}, "headers": {"mapKey": "inner"}, "url": "url"}, "source_proxy": "source_proxy", "target": {"auth": {"basic": {"password": "password", "username": "username"}, "iam": {"api_key": "api_key"}}, "headers": {"mapKey": "inner"}, "url": "url"}, "target_proxy": "target_proxy", "use_bulk_get": true, "use_checkpoints": true, "user_ctx": {"db": "db", "name": "name", "roles": ["_reader"]}, "winning_revs_only": false, "worker_batch_size": 500, "worker_processes": 4}' + responses.add( + responses.GET, + url, + body=mock_response, + content_type='application/json', + status=200, + ) # Set up parameter values doc_id = 'testString' @@ -9802,7 +10259,7 @@ def test_get_replication_document_value_error(self): "doc_id": doc_id, } for param in req_param_dict.keys(): - req_copy = {key:val if key is not param else None for (key,val) in req_param_dict.items()} + req_copy = {key: val if key is not param else None for (key, val) in req_param_dict.items()} with pytest.raises(ValueError): _service.get_replication_document(**req_copy) @@ -9815,7 +10272,8 @@ def test_get_replication_document_value_error_with_retries(self): _service.disable_retries() self.test_get_replication_document_value_error() -class TestPutReplicationDocument(): + +class TestPutReplicationDocument: """ Test Class for put_replication_document """ @@ -9827,12 +10285,14 @@ def test_put_replication_document_all_params(self): """ # Set up mock url = preprocess_url('/_replicator/testString') - mock_response = '{"id": "id", "rev": "rev", "ok": true, "caused_by": "caused_by", "error": "error", "reason": "reason"}' - responses.add(responses.PUT, - url, - body=mock_response, - content_type='application/json', - status=201) + mock_response = '{"id": "id", "rev": "rev", "ok": true, "caused_by": "caused_by", "error": "error", "reason": "reason", "ref": 0}' + responses.add( + responses.PUT, + url, + body=mock_response, + content_type='application/json', + status=201, + ) # Construct a dict representation of a Attachment model attachment_model = {} @@ -9858,9 +10318,9 @@ def test_put_replication_document_all_params(self): # Construct a dict representation of a ReplicationCreateTargetParameters model replication_create_target_parameters_model = {} - replication_create_target_parameters_model['n'] = 1 + replication_create_target_parameters_model['n'] = 3 replication_create_target_parameters_model['partitioned'] = False - replication_create_target_parameters_model['q'] = 26 + replication_create_target_parameters_model['q'] = 1 # Construct a dict representation of a ReplicationDatabaseAuthBasic model replication_database_auth_basic_model = {} @@ -9880,13 +10340,13 @@ def test_put_replication_document_all_params(self): replication_database_model = {} replication_database_model['auth'] = replication_database_auth_model replication_database_model['headers'] = {'key1': 'testString'} - replication_database_model['url'] = 'testString' + replication_database_model['url'] = 'https://my-source-instance.cloudantnosqldb.appdomain.cloud.example/animaldb' # Construct a dict representation of a UserContext model user_context_model = {} user_context_model['db'] = 'testString' - user_context_model['name'] = 'testString' - user_context_model['roles'] = ['_reader'] + user_context_model['name'] = 'john' + user_context_model['roles'] = ['_replicator'] # Construct a dict representation of a ReplicationDocument model replication_document_model = {} @@ -9899,28 +10359,31 @@ def test_put_replication_document_all_params(self): replication_document_model['_rev'] = 'testString' replication_document_model['_revisions'] = revisions_model replication_document_model['_revs_info'] = [document_revision_status_model] - replication_document_model['cancel'] = True - replication_document_model['checkpoint_interval'] = 0 - replication_document_model['connection_timeout'] = 0 - replication_document_model['continuous'] = False - replication_document_model['create_target'] = False + replication_document_model['cancel'] = False + replication_document_model['checkpoint_interval'] = 4500 + replication_document_model['connection_timeout'] = 15000 + replication_document_model['continuous'] = True + replication_document_model['create_target'] = True replication_document_model['create_target_params'] = replication_create_target_parameters_model - replication_document_model['doc_ids'] = ['testString'] - replication_document_model['filter'] = 'testString' - replication_document_model['http_connections'] = 1 + replication_document_model['doc_ids'] = ['badger', 'lemur', 'llama'] + replication_document_model['filter'] = 'ddoc/my_filter' + replication_document_model['http_connections'] = 10 + replication_document_model['owner'] = 'testString' replication_document_model['query_params'] = {'key1': 'testString'} - replication_document_model['retries_per_request'] = 0 - replication_document_model['selector'] = {'key1': 'testString'} - replication_document_model['since_seq'] = 'testString' - replication_document_model['socket_options'] = 'testString' + replication_document_model['retries_per_request'] = 3 + replication_document_model['selector'] = {'_id': {'$regex': 'docid'}} + replication_document_model['since_seq'] = '34-g1AAAAGjeJzLYWBgYMlgTmGQT0lKzi9KdU' + replication_document_model['socket_options'] = '[{keepalive, true}, {nodelay, false}]' replication_document_model['source'] = replication_database_model replication_document_model['source_proxy'] = 'testString' replication_document_model['target'] = replication_database_model replication_document_model['target_proxy'] = 'testString' - replication_document_model['use_checkpoints'] = True + replication_document_model['use_bulk_get'] = True + replication_document_model['use_checkpoints'] = False replication_document_model['user_ctx'] = user_context_model - replication_document_model['worker_batch_size'] = 1 - replication_document_model['worker_processes'] = 1 + replication_document_model['winning_revs_only'] = False + replication_document_model['worker_batch_size'] = 400 + replication_document_model['worker_processes'] = 3 replication_document_model['foo'] = 'testString' # Set up parameter values @@ -9939,20 +10402,21 @@ def test_put_replication_document_all_params(self): batch=batch, new_edits=new_edits, rev=rev, - headers={} + headers={}, ) # Check for correct operation assert len(responses.calls) == 1 assert response.status_code == 201 # Validate query params - query_string = responses.calls[0].request.url.split('?',1)[1] + query_string = responses.calls[0].request.url.split('?', 1)[1] query_string = urllib.parse.unquote_plus(query_string) assert 'batch={}'.format(batch) in query_string assert 'new_edits={}'.format('true' if new_edits else 'false') in query_string assert 'rev={}'.format(rev) in query_string - # decompress gzip compressed request body - responses.calls[0].request.body = gzip.decompress(responses.calls[0].request.body) + # Decompress gzip compressed request body. + raw_body = responses.calls[0].request.body + responses.calls[0].request.body = gzip.decompress(raw_body.read() if isinstance(raw_body, io.IOBase) else raw_body) # Validate body params req_body = json.loads(str(responses.calls[0].request.body, 'utf-8')) @@ -9974,12 +10438,14 @@ def test_put_replication_document_required_params(self): """ # Set up mock url = preprocess_url('/_replicator/testString') - mock_response = '{"id": "id", "rev": "rev", "ok": true, "caused_by": "caused_by", "error": "error", "reason": "reason"}' - responses.add(responses.PUT, - url, - body=mock_response, - content_type='application/json', - status=201) + mock_response = '{"id": "id", "rev": "rev", "ok": true, "caused_by": "caused_by", "error": "error", "reason": "reason", "ref": 0}' + responses.add( + responses.PUT, + url, + body=mock_response, + content_type='application/json', + status=201, + ) # Construct a dict representation of a Attachment model attachment_model = {} @@ -10005,9 +10471,9 @@ def test_put_replication_document_required_params(self): # Construct a dict representation of a ReplicationCreateTargetParameters model replication_create_target_parameters_model = {} - replication_create_target_parameters_model['n'] = 1 + replication_create_target_parameters_model['n'] = 3 replication_create_target_parameters_model['partitioned'] = False - replication_create_target_parameters_model['q'] = 26 + replication_create_target_parameters_model['q'] = 1 # Construct a dict representation of a ReplicationDatabaseAuthBasic model replication_database_auth_basic_model = {} @@ -10027,13 +10493,13 @@ def test_put_replication_document_required_params(self): replication_database_model = {} replication_database_model['auth'] = replication_database_auth_model replication_database_model['headers'] = {'key1': 'testString'} - replication_database_model['url'] = 'testString' + replication_database_model['url'] = 'https://my-source-instance.cloudantnosqldb.appdomain.cloud.example/animaldb' # Construct a dict representation of a UserContext model user_context_model = {} user_context_model['db'] = 'testString' - user_context_model['name'] = 'testString' - user_context_model['roles'] = ['_reader'] + user_context_model['name'] = 'john' + user_context_model['roles'] = ['_replicator'] # Construct a dict representation of a ReplicationDocument model replication_document_model = {} @@ -10046,28 +10512,31 @@ def test_put_replication_document_required_params(self): replication_document_model['_rev'] = 'testString' replication_document_model['_revisions'] = revisions_model replication_document_model['_revs_info'] = [document_revision_status_model] - replication_document_model['cancel'] = True - replication_document_model['checkpoint_interval'] = 0 - replication_document_model['connection_timeout'] = 0 - replication_document_model['continuous'] = False - replication_document_model['create_target'] = False + replication_document_model['cancel'] = False + replication_document_model['checkpoint_interval'] = 4500 + replication_document_model['connection_timeout'] = 15000 + replication_document_model['continuous'] = True + replication_document_model['create_target'] = True replication_document_model['create_target_params'] = replication_create_target_parameters_model - replication_document_model['doc_ids'] = ['testString'] - replication_document_model['filter'] = 'testString' - replication_document_model['http_connections'] = 1 + replication_document_model['doc_ids'] = ['badger', 'lemur', 'llama'] + replication_document_model['filter'] = 'ddoc/my_filter' + replication_document_model['http_connections'] = 10 + replication_document_model['owner'] = 'testString' replication_document_model['query_params'] = {'key1': 'testString'} - replication_document_model['retries_per_request'] = 0 - replication_document_model['selector'] = {'key1': 'testString'} - replication_document_model['since_seq'] = 'testString' - replication_document_model['socket_options'] = 'testString' + replication_document_model['retries_per_request'] = 3 + replication_document_model['selector'] = {'_id': {'$regex': 'docid'}} + replication_document_model['since_seq'] = '34-g1AAAAGjeJzLYWBgYMlgTmGQT0lKzi9KdU' + replication_document_model['socket_options'] = '[{keepalive, true}, {nodelay, false}]' replication_document_model['source'] = replication_database_model replication_document_model['source_proxy'] = 'testString' replication_document_model['target'] = replication_database_model replication_document_model['target_proxy'] = 'testString' - replication_document_model['use_checkpoints'] = True + replication_document_model['use_bulk_get'] = True + replication_document_model['use_checkpoints'] = False replication_document_model['user_ctx'] = user_context_model - replication_document_model['worker_batch_size'] = 1 - replication_document_model['worker_processes'] = 1 + replication_document_model['winning_revs_only'] = False + replication_document_model['worker_batch_size'] = 400 + replication_document_model['worker_processes'] = 3 replication_document_model['foo'] = 'testString' # Set up parameter values @@ -10078,14 +10547,15 @@ def test_put_replication_document_required_params(self): response = _service.put_replication_document( doc_id, replication_document, - headers={} + headers={}, ) # Check for correct operation assert len(responses.calls) == 1 assert response.status_code == 201 - # decompress gzip compressed request body - responses.calls[0].request.body = gzip.decompress(responses.calls[0].request.body) + # Decompress gzip compressed request body. + raw_body = responses.calls[0].request.body + responses.calls[0].request.body = gzip.decompress(raw_body.read() if isinstance(raw_body, io.IOBase) else raw_body) # Validate body params req_body = json.loads(str(responses.calls[0].request.body, 'utf-8')) @@ -10107,12 +10577,14 @@ def test_put_replication_document_value_error(self): """ # Set up mock url = preprocess_url('/_replicator/testString') - mock_response = '{"id": "id", "rev": "rev", "ok": true, "caused_by": "caused_by", "error": "error", "reason": "reason"}' - responses.add(responses.PUT, - url, - body=mock_response, - content_type='application/json', - status=201) + mock_response = '{"id": "id", "rev": "rev", "ok": true, "caused_by": "caused_by", "error": "error", "reason": "reason", "ref": 0}' + responses.add( + responses.PUT, + url, + body=mock_response, + content_type='application/json', + status=201, + ) # Construct a dict representation of a Attachment model attachment_model = {} @@ -10138,9 +10610,9 @@ def test_put_replication_document_value_error(self): # Construct a dict representation of a ReplicationCreateTargetParameters model replication_create_target_parameters_model = {} - replication_create_target_parameters_model['n'] = 1 + replication_create_target_parameters_model['n'] = 3 replication_create_target_parameters_model['partitioned'] = False - replication_create_target_parameters_model['q'] = 26 + replication_create_target_parameters_model['q'] = 1 # Construct a dict representation of a ReplicationDatabaseAuthBasic model replication_database_auth_basic_model = {} @@ -10160,13 +10632,13 @@ def test_put_replication_document_value_error(self): replication_database_model = {} replication_database_model['auth'] = replication_database_auth_model replication_database_model['headers'] = {'key1': 'testString'} - replication_database_model['url'] = 'testString' + replication_database_model['url'] = 'https://my-source-instance.cloudantnosqldb.appdomain.cloud.example/animaldb' # Construct a dict representation of a UserContext model user_context_model = {} user_context_model['db'] = 'testString' - user_context_model['name'] = 'testString' - user_context_model['roles'] = ['_reader'] + user_context_model['name'] = 'john' + user_context_model['roles'] = ['_replicator'] # Construct a dict representation of a ReplicationDocument model replication_document_model = {} @@ -10179,28 +10651,31 @@ def test_put_replication_document_value_error(self): replication_document_model['_rev'] = 'testString' replication_document_model['_revisions'] = revisions_model replication_document_model['_revs_info'] = [document_revision_status_model] - replication_document_model['cancel'] = True - replication_document_model['checkpoint_interval'] = 0 - replication_document_model['connection_timeout'] = 0 - replication_document_model['continuous'] = False - replication_document_model['create_target'] = False + replication_document_model['cancel'] = False + replication_document_model['checkpoint_interval'] = 4500 + replication_document_model['connection_timeout'] = 15000 + replication_document_model['continuous'] = True + replication_document_model['create_target'] = True replication_document_model['create_target_params'] = replication_create_target_parameters_model - replication_document_model['doc_ids'] = ['testString'] - replication_document_model['filter'] = 'testString' - replication_document_model['http_connections'] = 1 + replication_document_model['doc_ids'] = ['badger', 'lemur', 'llama'] + replication_document_model['filter'] = 'ddoc/my_filter' + replication_document_model['http_connections'] = 10 + replication_document_model['owner'] = 'testString' replication_document_model['query_params'] = {'key1': 'testString'} - replication_document_model['retries_per_request'] = 0 - replication_document_model['selector'] = {'key1': 'testString'} - replication_document_model['since_seq'] = 'testString' - replication_document_model['socket_options'] = 'testString' + replication_document_model['retries_per_request'] = 3 + replication_document_model['selector'] = {'_id': {'$regex': 'docid'}} + replication_document_model['since_seq'] = '34-g1AAAAGjeJzLYWBgYMlgTmGQT0lKzi9KdU' + replication_document_model['socket_options'] = '[{keepalive, true}, {nodelay, false}]' replication_document_model['source'] = replication_database_model replication_document_model['source_proxy'] = 'testString' replication_document_model['target'] = replication_database_model replication_document_model['target_proxy'] = 'testString' - replication_document_model['use_checkpoints'] = True + replication_document_model['use_bulk_get'] = True + replication_document_model['use_checkpoints'] = False replication_document_model['user_ctx'] = user_context_model - replication_document_model['worker_batch_size'] = 1 - replication_document_model['worker_processes'] = 1 + replication_document_model['winning_revs_only'] = False + replication_document_model['worker_batch_size'] = 400 + replication_document_model['worker_processes'] = 3 replication_document_model['foo'] = 'testString' # Set up parameter values @@ -10213,7 +10688,7 @@ def test_put_replication_document_value_error(self): "replication_document": replication_document, } for param in req_param_dict.keys(): - req_copy = {key:val if key is not param else None for (key,val) in req_param_dict.items()} + req_copy = {key: val if key is not param else None for (key, val) in req_param_dict.items()} with pytest.raises(ValueError): _service.put_replication_document(**req_copy) @@ -10226,7 +10701,8 @@ def test_put_replication_document_value_error_with_retries(self): _service.disable_retries() self.test_put_replication_document_value_error() -class TestGetSchedulerDocs(): + +class TestGetSchedulerDocs: """ Test Class for get_scheduler_docs """ @@ -10239,11 +10715,13 @@ def test_get_scheduler_docs_all_params(self): # Set up mock url = preprocess_url('/_scheduler/docs') mock_response = '{"total_rows": 0, "docs": [{"database": "database", "doc_id": "doc_id", "error_count": 0, "id": "id", "info": {"changes_pending": 0, "checkpointed_source_seq": "checkpointed_source_seq", "doc_write_failures": 0, "docs_read": 0, "docs_written": 0, "error": "error", "missing_revisions_found": 0, "revisions_checked": 0, "source_seq": "source_seq", "through_seq": "through_seq"}, "last_updated": "2019-01-01T12:00:00.000Z", "node": "node", "source": "source", "source_proxy": "source_proxy", "start_time": "2019-01-01T12:00:00.000Z", "state": "initializing", "target": "target", "target_proxy": "target_proxy"}]}' - responses.add(responses.GET, - url, - body=mock_response, - content_type='application/json', - status=200) + responses.add( + responses.GET, + url, + body=mock_response, + content_type='application/json', + status=200, + ) # Set up parameter values limit = 0 @@ -10255,14 +10733,14 @@ def test_get_scheduler_docs_all_params(self): limit=limit, skip=skip, states=states, - headers={} + headers={}, ) # Check for correct operation assert len(responses.calls) == 1 assert response.status_code == 200 # Validate query params - query_string = responses.calls[0].request.url.split('?',1)[1] + query_string = responses.calls[0].request.url.split('?', 1)[1] query_string = urllib.parse.unquote_plus(query_string) assert 'limit={}'.format(limit) in query_string assert 'skip={}'.format(skip) in query_string @@ -10285,16 +10763,17 @@ def test_get_scheduler_docs_required_params(self): # Set up mock url = preprocess_url('/_scheduler/docs') mock_response = '{"total_rows": 0, "docs": [{"database": "database", "doc_id": "doc_id", "error_count": 0, "id": "id", "info": {"changes_pending": 0, "checkpointed_source_seq": "checkpointed_source_seq", "doc_write_failures": 0, "docs_read": 0, "docs_written": 0, "error": "error", "missing_revisions_found": 0, "revisions_checked": 0, "source_seq": "source_seq", "through_seq": "through_seq"}, "last_updated": "2019-01-01T12:00:00.000Z", "node": "node", "source": "source", "source_proxy": "source_proxy", "start_time": "2019-01-01T12:00:00.000Z", "state": "initializing", "target": "target", "target_proxy": "target_proxy"}]}' - responses.add(responses.GET, - url, - body=mock_response, - content_type='application/json', - status=200) + responses.add( + responses.GET, + url, + body=mock_response, + content_type='application/json', + status=200, + ) # Invoke method response = _service.get_scheduler_docs() - # Check for correct operation assert len(responses.calls) == 1 assert response.status_code == 200 @@ -10308,7 +10787,8 @@ def test_get_scheduler_docs_required_params_with_retries(self): _service.disable_retries() self.test_get_scheduler_docs_required_params() -class TestGetSchedulerDocument(): + +class TestGetSchedulerDocument: """ Test Class for get_scheduler_document """ @@ -10321,11 +10801,13 @@ def test_get_scheduler_document_all_params(self): # Set up mock url = preprocess_url('/_scheduler/docs/_replicator/testString') mock_response = '{"database": "database", "doc_id": "doc_id", "error_count": 0, "id": "id", "info": {"changes_pending": 0, "checkpointed_source_seq": "checkpointed_source_seq", "doc_write_failures": 0, "docs_read": 0, "docs_written": 0, "error": "error", "missing_revisions_found": 0, "revisions_checked": 0, "source_seq": "source_seq", "through_seq": "through_seq"}, "last_updated": "2019-01-01T12:00:00.000Z", "node": "node", "source": "source", "source_proxy": "source_proxy", "start_time": "2019-01-01T12:00:00.000Z", "state": "initializing", "target": "target", "target_proxy": "target_proxy"}' - responses.add(responses.GET, - url, - body=mock_response, - content_type='application/json', - status=200) + responses.add( + responses.GET, + url, + body=mock_response, + content_type='application/json', + status=200, + ) # Set up parameter values doc_id = 'testString' @@ -10333,7 +10815,7 @@ def test_get_scheduler_document_all_params(self): # Invoke method response = _service.get_scheduler_document( doc_id, - headers={} + headers={}, ) # Check for correct operation @@ -10357,11 +10839,13 @@ def test_get_scheduler_document_value_error(self): # Set up mock url = preprocess_url('/_scheduler/docs/_replicator/testString') mock_response = '{"database": "database", "doc_id": "doc_id", "error_count": 0, "id": "id", "info": {"changes_pending": 0, "checkpointed_source_seq": "checkpointed_source_seq", "doc_write_failures": 0, "docs_read": 0, "docs_written": 0, "error": "error", "missing_revisions_found": 0, "revisions_checked": 0, "source_seq": "source_seq", "through_seq": "through_seq"}, "last_updated": "2019-01-01T12:00:00.000Z", "node": "node", "source": "source", "source_proxy": "source_proxy", "start_time": "2019-01-01T12:00:00.000Z", "state": "initializing", "target": "target", "target_proxy": "target_proxy"}' - responses.add(responses.GET, - url, - body=mock_response, - content_type='application/json', - status=200) + responses.add( + responses.GET, + url, + body=mock_response, + content_type='application/json', + status=200, + ) # Set up parameter values doc_id = 'testString' @@ -10371,7 +10855,7 @@ def test_get_scheduler_document_value_error(self): "doc_id": doc_id, } for param in req_param_dict.keys(): - req_copy = {key:val if key is not param else None for (key,val) in req_param_dict.items()} + req_copy = {key: val if key is not param else None for (key, val) in req_param_dict.items()} with pytest.raises(ValueError): _service.get_scheduler_document(**req_copy) @@ -10384,7 +10868,8 @@ def test_get_scheduler_document_value_error_with_retries(self): _service.disable_retries() self.test_get_scheduler_document_value_error() -class TestGetSchedulerJobs(): + +class TestGetSchedulerJobs: """ Test Class for get_scheduler_jobs """ @@ -10397,28 +10882,30 @@ def test_get_scheduler_jobs_all_params(self): # Set up mock url = preprocess_url('/_scheduler/jobs') mock_response = '{"total_rows": 0, "jobs": [{"database": "database", "doc_id": "doc_id", "history": [{"reason": "reason", "timestamp": "2019-01-01T12:00:00.000Z", "type": "type"}], "id": "id", "info": {"changes_pending": 0, "checkpointed_source_seq": "checkpointed_source_seq", "doc_write_failures": 0, "docs_read": 0, "docs_written": 0, "error": "error", "missing_revisions_found": 0, "revisions_checked": 0, "source_seq": "source_seq", "through_seq": "through_seq"}, "node": "node", "pid": "pid", "source": "source", "start_time": "2019-01-01T12:00:00.000Z", "target": "target", "user": "user"}]}' - responses.add(responses.GET, - url, - body=mock_response, - content_type='application/json', - status=200) + responses.add( + responses.GET, + url, + body=mock_response, + content_type='application/json', + status=200, + ) # Set up parameter values - limit = 0 + limit = 25 skip = 0 # Invoke method response = _service.get_scheduler_jobs( limit=limit, skip=skip, - headers={} + headers={}, ) # Check for correct operation assert len(responses.calls) == 1 assert response.status_code == 200 # Validate query params - query_string = responses.calls[0].request.url.split('?',1)[1] + query_string = responses.calls[0].request.url.split('?', 1)[1] query_string = urllib.parse.unquote_plus(query_string) assert 'limit={}'.format(limit) in query_string assert 'skip={}'.format(skip) in query_string @@ -10440,16 +10927,17 @@ def test_get_scheduler_jobs_required_params(self): # Set up mock url = preprocess_url('/_scheduler/jobs') mock_response = '{"total_rows": 0, "jobs": [{"database": "database", "doc_id": "doc_id", "history": [{"reason": "reason", "timestamp": "2019-01-01T12:00:00.000Z", "type": "type"}], "id": "id", "info": {"changes_pending": 0, "checkpointed_source_seq": "checkpointed_source_seq", "doc_write_failures": 0, "docs_read": 0, "docs_written": 0, "error": "error", "missing_revisions_found": 0, "revisions_checked": 0, "source_seq": "source_seq", "through_seq": "through_seq"}, "node": "node", "pid": "pid", "source": "source", "start_time": "2019-01-01T12:00:00.000Z", "target": "target", "user": "user"}]}' - responses.add(responses.GET, - url, - body=mock_response, - content_type='application/json', - status=200) + responses.add( + responses.GET, + url, + body=mock_response, + content_type='application/json', + status=200, + ) # Invoke method response = _service.get_scheduler_jobs() - # Check for correct operation assert len(responses.calls) == 1 assert response.status_code == 200 @@ -10463,7 +10951,8 @@ def test_get_scheduler_jobs_required_params_with_retries(self): _service.disable_retries() self.test_get_scheduler_jobs_required_params() -class TestGetSchedulerJob(): + +class TestGetSchedulerJob: """ Test Class for get_scheduler_job """ @@ -10476,11 +10965,13 @@ def test_get_scheduler_job_all_params(self): # Set up mock url = preprocess_url('/_scheduler/jobs/testString') mock_response = '{"database": "database", "doc_id": "doc_id", "history": [{"reason": "reason", "timestamp": "2019-01-01T12:00:00.000Z", "type": "type"}], "id": "id", "info": {"changes_pending": 0, "checkpointed_source_seq": "checkpointed_source_seq", "doc_write_failures": 0, "docs_read": 0, "docs_written": 0, "error": "error", "missing_revisions_found": 0, "revisions_checked": 0, "source_seq": "source_seq", "through_seq": "through_seq"}, "node": "node", "pid": "pid", "source": "source", "start_time": "2019-01-01T12:00:00.000Z", "target": "target", "user": "user"}' - responses.add(responses.GET, - url, - body=mock_response, - content_type='application/json', - status=200) + responses.add( + responses.GET, + url, + body=mock_response, + content_type='application/json', + status=200, + ) # Set up parameter values job_id = 'testString' @@ -10488,7 +10979,7 @@ def test_get_scheduler_job_all_params(self): # Invoke method response = _service.get_scheduler_job( job_id, - headers={} + headers={}, ) # Check for correct operation @@ -10512,11 +11003,13 @@ def test_get_scheduler_job_value_error(self): # Set up mock url = preprocess_url('/_scheduler/jobs/testString') mock_response = '{"database": "database", "doc_id": "doc_id", "history": [{"reason": "reason", "timestamp": "2019-01-01T12:00:00.000Z", "type": "type"}], "id": "id", "info": {"changes_pending": 0, "checkpointed_source_seq": "checkpointed_source_seq", "doc_write_failures": 0, "docs_read": 0, "docs_written": 0, "error": "error", "missing_revisions_found": 0, "revisions_checked": 0, "source_seq": "source_seq", "through_seq": "through_seq"}, "node": "node", "pid": "pid", "source": "source", "start_time": "2019-01-01T12:00:00.000Z", "target": "target", "user": "user"}' - responses.add(responses.GET, - url, - body=mock_response, - content_type='application/json', - status=200) + responses.add( + responses.GET, + url, + body=mock_response, + content_type='application/json', + status=200, + ) # Set up parameter values job_id = 'testString' @@ -10526,7 +11019,7 @@ def test_get_scheduler_job_value_error(self): "job_id": job_id, } for param in req_param_dict.keys(): - req_copy = {key:val if key is not param else None for (key,val) in req_param_dict.items()} + req_copy = {key: val if key is not param else None for (key, val) in req_param_dict.items()} with pytest.raises(ValueError): _service.get_scheduler_job(**req_copy) @@ -10539,6 +11032,7 @@ def test_get_scheduler_job_value_error_with_retries(self): _service.disable_retries() self.test_get_scheduler_job_value_error() + # endregion ############################################################################## # End of Service: Replication @@ -10549,7 +11043,8 @@ def test_get_scheduler_job_value_error_with_retries(self): ############################################################################## # region -class TestNewInstance(): + +class TestNewInstance: """ Test Class for new_instance """ @@ -10576,7 +11071,8 @@ def test_new_instance_without_authenticator(self): service_name='TEST_SERVICE_NOT_FOUND', ) -class TestGetSessionInformation(): + +class TestGetSessionInformation: """ Test Class for get_session_information """ @@ -10589,16 +11085,17 @@ def test_get_session_information_all_params(self): # Set up mock url = preprocess_url('/_session') mock_response = '{"ok": true, "info": {"authenticated": "authenticated", "authentication_db": "authentication_db", "authentication_handlers": ["authentication_handlers"]}, "userCtx": {"db": "db", "name": "name", "roles": ["_reader"]}}' - responses.add(responses.GET, - url, - body=mock_response, - content_type='application/json', - status=200) + responses.add( + responses.GET, + url, + body=mock_response, + content_type='application/json', + status=200, + ) # Invoke method response = _service.get_session_information() - # Check for correct operation assert len(responses.calls) == 1 assert response.status_code == 200 @@ -10612,6 +11109,7 @@ def test_get_session_information_all_params_with_retries(self): _service.disable_retries() self.test_get_session_information_all_params() + # endregion ############################################################################## # End of Service: Authentication @@ -10622,7 +11120,8 @@ def test_get_session_information_all_params_with_retries(self): ############################################################################## # region -class TestNewInstance(): + +class TestNewInstance: """ Test Class for new_instance """ @@ -10649,100 +11148,65 @@ def test_new_instance_without_authenticator(self): service_name='TEST_SERVICE_NOT_FOUND', ) -class TestGetSecurity(): + +class TestPostApiKeys: """ - Test Class for get_security + Test Class for post_api_keys """ @responses.activate - def test_get_security_all_params(self): + def test_post_api_keys_all_params(self): """ - get_security() + post_api_keys() """ # Set up mock - url = preprocess_url('/testString/_security') - mock_response = '{"admins": {"names": ["names"], "roles": ["roles"]}, "members": {"names": ["names"], "roles": ["roles"]}, "cloudant": {"mapKey": ["_reader"]}, "couchdb_auth_only": false}' - responses.add(responses.GET, - url, - body=mock_response, - content_type='application/json', - status=200) - - # Set up parameter values - db = 'testString' + url = preprocess_url('/_api/v2/api_keys') + mock_response = '{"ok": true, "key": "key", "password": "password"}' + responses.add( + responses.POST, + url, + body=mock_response, + content_type='application/json', + status=201, + ) # Invoke method - response = _service.get_security( - db, - headers={} - ) + response = _service.post_api_keys() # Check for correct operation assert len(responses.calls) == 1 - assert response.status_code == 200 + assert response.status_code == 201 - def test_get_security_all_params_with_retries(self): - # Enable retries and run test_get_security_all_params. + def test_post_api_keys_all_params_with_retries(self): + # Enable retries and run test_post_api_keys_all_params. _service.enable_retries() - self.test_get_security_all_params() + self.test_post_api_keys_all_params() - # Disable retries and run test_get_security_all_params. + # Disable retries and run test_post_api_keys_all_params. _service.disable_retries() - self.test_get_security_all_params() - - @responses.activate - def test_get_security_value_error(self): - """ - test_get_security_value_error() - """ - # Set up mock - url = preprocess_url('/testString/_security') - mock_response = '{"admins": {"names": ["names"], "roles": ["roles"]}, "members": {"names": ["names"], "roles": ["roles"]}, "cloudant": {"mapKey": ["_reader"]}, "couchdb_auth_only": false}' - responses.add(responses.GET, - url, - body=mock_response, - content_type='application/json', - status=200) - - # Set up parameter values - db = 'testString' - - # Pass in all but one required param and check for a ValueError - req_param_dict = { - "db": db, - } - for param in req_param_dict.keys(): - req_copy = {key:val if key is not param else None for (key,val) in req_param_dict.items()} - with pytest.raises(ValueError): - _service.get_security(**req_copy) - - def test_get_security_value_error_with_retries(self): - # Enable retries and run test_get_security_value_error. - _service.enable_retries() - self.test_get_security_value_error() + self.test_post_api_keys_all_params() - # Disable retries and run test_get_security_value_error. - _service.disable_retries() - self.test_get_security_value_error() -class TestPutSecurity(): +class TestPutCloudantSecurityConfiguration: """ - Test Class for put_security + Test Class for put_cloudant_security_configuration """ @responses.activate - def test_put_security_all_params(self): + def test_put_cloudant_security_configuration_all_params(self): """ - put_security() + put_cloudant_security_configuration() """ # Set up mock - url = preprocess_url('/testString/_security') + url = preprocess_url('/_api/v2/db/testString/_security') mock_response = '{"ok": true}' - responses.add(responses.PUT, - url, - body=mock_response, - content_type='application/json', - status=200) + responses.add( + responses.PUT, + url, + body=mock_response, + content_type='application/json', + status=200, + ) # Construct a dict representation of a SecurityObject model security_object_model = {} @@ -10751,56 +11215,59 @@ def test_put_security_all_params(self): # Set up parameter values db = 'testString' - admins = security_object_model - members = security_object_model cloudant = {'key1': ['_reader']} + admins = security_object_model couchdb_auth_only = True + members = security_object_model # Invoke method - response = _service.put_security( + response = _service.put_cloudant_security_configuration( db, + cloudant, admins=admins, - members=members, - cloudant=cloudant, couchdb_auth_only=couchdb_auth_only, - headers={} + members=members, + headers={}, ) # Check for correct operation assert len(responses.calls) == 1 assert response.status_code == 200 - # decompress gzip compressed request body - responses.calls[0].request.body = gzip.decompress(responses.calls[0].request.body) + # Decompress gzip compressed request body. + raw_body = responses.calls[0].request.body + responses.calls[0].request.body = gzip.decompress(raw_body.read() if isinstance(raw_body, io.IOBase) else raw_body) # Validate body params req_body = json.loads(str(responses.calls[0].request.body, 'utf-8')) - assert req_body['admins'] == security_object_model - assert req_body['members'] == security_object_model assert req_body['cloudant'] == {'key1': ['_reader']} + assert req_body['admins'] == security_object_model assert req_body['couchdb_auth_only'] == True + assert req_body['members'] == security_object_model - def test_put_security_all_params_with_retries(self): - # Enable retries and run test_put_security_all_params. + def test_put_cloudant_security_configuration_all_params_with_retries(self): + # Enable retries and run test_put_cloudant_security_configuration_all_params. _service.enable_retries() - self.test_put_security_all_params() + self.test_put_cloudant_security_configuration_all_params() - # Disable retries and run test_put_security_all_params. + # Disable retries and run test_put_cloudant_security_configuration_all_params. _service.disable_retries() - self.test_put_security_all_params() + self.test_put_cloudant_security_configuration_all_params() @responses.activate - def test_put_security_value_error(self): + def test_put_cloudant_security_configuration_value_error(self): """ - test_put_security_value_error() + test_put_cloudant_security_configuration_value_error() """ # Set up mock - url = preprocess_url('/testString/_security') + url = preprocess_url('/_api/v2/db/testString/_security') mock_response = '{"ok": true}' - responses.add(responses.PUT, - url, - body=mock_response, - content_type='application/json', - status=200) + responses.add( + responses.PUT, + url, + body=mock_response, + content_type='application/json', + status=200, + ) # Construct a dict representation of a SecurityObject model security_object_model = {} @@ -10809,172 +11276,224 @@ def test_put_security_value_error(self): # Set up parameter values db = 'testString' - admins = security_object_model - members = security_object_model cloudant = {'key1': ['_reader']} + admins = security_object_model couchdb_auth_only = True + members = security_object_model # Pass in all but one required param and check for a ValueError req_param_dict = { "db": db, + "cloudant": cloudant, } for param in req_param_dict.keys(): - req_copy = {key:val if key is not param else None for (key,val) in req_param_dict.items()} + req_copy = {key: val if key is not param else None for (key, val) in req_param_dict.items()} with pytest.raises(ValueError): - _service.put_security(**req_copy) + _service.put_cloudant_security_configuration(**req_copy) - def test_put_security_value_error_with_retries(self): - # Enable retries and run test_put_security_value_error. + def test_put_cloudant_security_configuration_value_error_with_retries(self): + # Enable retries and run test_put_cloudant_security_configuration_value_error. _service.enable_retries() - self.test_put_security_value_error() + self.test_put_cloudant_security_configuration_value_error() - # Disable retries and run test_put_security_value_error. + # Disable retries and run test_put_cloudant_security_configuration_value_error. _service.disable_retries() - self.test_put_security_value_error() + self.test_put_cloudant_security_configuration_value_error() + -class TestPostApiKeys(): +class TestGetSecurity: """ - Test Class for post_api_keys + Test Class for get_security """ @responses.activate - def test_post_api_keys_all_params(self): + def test_get_security_all_params(self): """ - post_api_keys() + get_security() """ # Set up mock - url = preprocess_url('/_api/v2/api_keys') - mock_response = '{"ok": true, "key": "key", "password": "password"}' - responses.add(responses.POST, - url, - body=mock_response, - content_type='application/json', - status=201) + url = preprocess_url('/testString/_security') + mock_response = '{"admins": {"names": ["names"], "roles": ["roles"]}, "cloudant": {"mapKey": ["_reader"]}, "couchdb_auth_only": false, "members": {"names": ["names"], "roles": ["roles"]}}' + responses.add( + responses.GET, + url, + body=mock_response, + content_type='application/json', + status=200, + ) - # Invoke method - response = _service.post_api_keys() + # Set up parameter values + db = 'testString' + # Invoke method + response = _service.get_security( + db, + headers={}, + ) # Check for correct operation assert len(responses.calls) == 1 - assert response.status_code == 201 + assert response.status_code == 200 - def test_post_api_keys_all_params_with_retries(self): - # Enable retries and run test_post_api_keys_all_params. + def test_get_security_all_params_with_retries(self): + # Enable retries and run test_get_security_all_params. _service.enable_retries() - self.test_post_api_keys_all_params() + self.test_get_security_all_params() - # Disable retries and run test_post_api_keys_all_params. + # Disable retries and run test_get_security_all_params. _service.disable_retries() - self.test_post_api_keys_all_params() + self.test_get_security_all_params() + + @responses.activate + def test_get_security_value_error(self): + """ + test_get_security_value_error() + """ + # Set up mock + url = preprocess_url('/testString/_security') + mock_response = '{"admins": {"names": ["names"], "roles": ["roles"]}, "cloudant": {"mapKey": ["_reader"]}, "couchdb_auth_only": false, "members": {"names": ["names"], "roles": ["roles"]}}' + responses.add( + responses.GET, + url, + body=mock_response, + content_type='application/json', + status=200, + ) + + # Set up parameter values + db = 'testString' + + # Pass in all but one required param and check for a ValueError + req_param_dict = { + "db": db, + } + for param in req_param_dict.keys(): + req_copy = {key: val if key is not param else None for (key, val) in req_param_dict.items()} + with pytest.raises(ValueError): + _service.get_security(**req_copy) + + def test_get_security_value_error_with_retries(self): + # Enable retries and run test_get_security_value_error. + _service.enable_retries() + self.test_get_security_value_error() + + # Disable retries and run test_get_security_value_error. + _service.disable_retries() + self.test_get_security_value_error() + -class TestPutCloudantSecurityConfiguration(): +class TestPutSecurity: """ - Test Class for put_cloudant_security_configuration + Test Class for put_security """ @responses.activate - def test_put_cloudant_security_configuration_all_params(self): + def test_put_security_all_params(self): """ - put_cloudant_security_configuration() + put_security() """ # Set up mock - url = preprocess_url('/_api/v2/db/testString/_security') + url = preprocess_url('/testString/_security') mock_response = '{"ok": true}' - responses.add(responses.PUT, - url, - body=mock_response, - content_type='application/json', - status=200) + responses.add( + responses.PUT, + url, + body=mock_response, + content_type='application/json', + status=200, + ) # Construct a dict representation of a SecurityObject model security_object_model = {} - security_object_model['names'] = ['testString'] - security_object_model['roles'] = ['testString'] + security_object_model['names'] = ['superuser'] + security_object_model['roles'] = ['admins'] # Set up parameter values db = 'testString' - cloudant = {'key1': ['_reader']} admins = security_object_model - members = security_object_model + cloudant = {'key1': ['_reader']} couchdb_auth_only = True + members = security_object_model # Invoke method - response = _service.put_cloudant_security_configuration( + response = _service.put_security( db, - cloudant, admins=admins, - members=members, + cloudant=cloudant, couchdb_auth_only=couchdb_auth_only, - headers={} + members=members, + headers={}, ) # Check for correct operation assert len(responses.calls) == 1 assert response.status_code == 200 - # decompress gzip compressed request body - responses.calls[0].request.body = gzip.decompress(responses.calls[0].request.body) + # Decompress gzip compressed request body. + raw_body = responses.calls[0].request.body + responses.calls[0].request.body = gzip.decompress(raw_body.read() if isinstance(raw_body, io.IOBase) else raw_body) # Validate body params req_body = json.loads(str(responses.calls[0].request.body, 'utf-8')) - assert req_body['cloudant'] == {'key1': ['_reader']} assert req_body['admins'] == security_object_model - assert req_body['members'] == security_object_model + assert req_body['cloudant'] == {'key1': ['_reader']} assert req_body['couchdb_auth_only'] == True + assert req_body['members'] == security_object_model - def test_put_cloudant_security_configuration_all_params_with_retries(self): - # Enable retries and run test_put_cloudant_security_configuration_all_params. + def test_put_security_all_params_with_retries(self): + # Enable retries and run test_put_security_all_params. _service.enable_retries() - self.test_put_cloudant_security_configuration_all_params() + self.test_put_security_all_params() - # Disable retries and run test_put_cloudant_security_configuration_all_params. + # Disable retries and run test_put_security_all_params. _service.disable_retries() - self.test_put_cloudant_security_configuration_all_params() + self.test_put_security_all_params() @responses.activate - def test_put_cloudant_security_configuration_value_error(self): + def test_put_security_value_error(self): """ - test_put_cloudant_security_configuration_value_error() + test_put_security_value_error() """ # Set up mock - url = preprocess_url('/_api/v2/db/testString/_security') + url = preprocess_url('/testString/_security') mock_response = '{"ok": true}' - responses.add(responses.PUT, - url, - body=mock_response, - content_type='application/json', - status=200) + responses.add( + responses.PUT, + url, + body=mock_response, + content_type='application/json', + status=200, + ) # Construct a dict representation of a SecurityObject model security_object_model = {} - security_object_model['names'] = ['testString'] - security_object_model['roles'] = ['testString'] + security_object_model['names'] = ['superuser'] + security_object_model['roles'] = ['admins'] # Set up parameter values db = 'testString' - cloudant = {'key1': ['_reader']} admins = security_object_model - members = security_object_model + cloudant = {'key1': ['_reader']} couchdb_auth_only = True + members = security_object_model # Pass in all but one required param and check for a ValueError req_param_dict = { "db": db, - "cloudant": cloudant, } for param in req_param_dict.keys(): - req_copy = {key:val if key is not param else None for (key,val) in req_param_dict.items()} + req_copy = {key: val if key is not param else None for (key, val) in req_param_dict.items()} with pytest.raises(ValueError): - _service.put_cloudant_security_configuration(**req_copy) + _service.put_security(**req_copy) - def test_put_cloudant_security_configuration_value_error_with_retries(self): - # Enable retries and run test_put_cloudant_security_configuration_value_error. + def test_put_security_value_error_with_retries(self): + # Enable retries and run test_put_security_value_error. _service.enable_retries() - self.test_put_cloudant_security_configuration_value_error() + self.test_put_security_value_error() - # Disable retries and run test_put_cloudant_security_configuration_value_error. + # Disable retries and run test_put_security_value_error. _service.disable_retries() - self.test_put_cloudant_security_configuration_value_error() + self.test_put_security_value_error() + # endregion ############################################################################## @@ -10986,7 +11505,8 @@ def test_put_cloudant_security_configuration_value_error_with_retries(self): ############################################################################## # region -class TestNewInstance(): + +class TestNewInstance: """ Test Class for new_instance """ @@ -11013,7 +11533,8 @@ def test_new_instance_without_authenticator(self): service_name='TEST_SERVICE_NOT_FOUND', ) -class TestGetCorsInformation(): + +class TestGetCorsInformation: """ Test Class for get_cors_information """ @@ -11026,16 +11547,17 @@ def test_get_cors_information_all_params(self): # Set up mock url = preprocess_url('/_api/v2/user/config/cors') mock_response = '{"allow_credentials": true, "enable_cors": true, "origins": ["origins"]}' - responses.add(responses.GET, - url, - body=mock_response, - content_type='application/json', - status=200) + responses.add( + responses.GET, + url, + body=mock_response, + content_type='application/json', + status=200, + ) # Invoke method response = _service.get_cors_information() - # Check for correct operation assert len(responses.calls) == 1 assert response.status_code == 200 @@ -11049,7 +11571,8 @@ def test_get_cors_information_all_params_with_retries(self): _service.disable_retries() self.test_get_cors_information_all_params() -class TestPutCorsConfiguration(): + +class TestPutCorsConfiguration: """ Test Class for put_cors_configuration """ @@ -11062,14 +11585,16 @@ def test_put_cors_configuration_all_params(self): # Set up mock url = preprocess_url('/_api/v2/user/config/cors') mock_response = '{"ok": true}' - responses.add(responses.PUT, - url, - body=mock_response, - content_type='application/json', - status=200) + responses.add( + responses.PUT, + url, + body=mock_response, + content_type='application/json', + status=200, + ) # Set up parameter values - origins = ['testString'] + origins = ['https://example.com', 'https://www.example.com'] allow_credentials = True enable_cors = True @@ -11078,18 +11603,19 @@ def test_put_cors_configuration_all_params(self): origins, allow_credentials=allow_credentials, enable_cors=enable_cors, - headers={} + headers={}, ) # Check for correct operation assert len(responses.calls) == 1 assert response.status_code == 200 - # decompress gzip compressed request body - responses.calls[0].request.body = gzip.decompress(responses.calls[0].request.body) + # Decompress gzip compressed request body. + raw_body = responses.calls[0].request.body + responses.calls[0].request.body = gzip.decompress(raw_body.read() if isinstance(raw_body, io.IOBase) else raw_body) # Validate body params req_body = json.loads(str(responses.calls[0].request.body, 'utf-8')) - assert req_body['origins'] == ['testString'] + assert req_body['origins'] == ['https://example.com', 'https://www.example.com'] assert req_body['allow_credentials'] == True assert req_body['enable_cors'] == True @@ -11110,14 +11636,16 @@ def test_put_cors_configuration_value_error(self): # Set up mock url = preprocess_url('/_api/v2/user/config/cors') mock_response = '{"ok": true}' - responses.add(responses.PUT, - url, - body=mock_response, - content_type='application/json', - status=200) + responses.add( + responses.PUT, + url, + body=mock_response, + content_type='application/json', + status=200, + ) # Set up parameter values - origins = ['testString'] + origins = ['https://example.com', 'https://www.example.com'] allow_credentials = True enable_cors = True @@ -11126,7 +11654,7 @@ def test_put_cors_configuration_value_error(self): "origins": origins, } for param in req_param_dict.keys(): - req_copy = {key:val if key is not param else None for (key,val) in req_param_dict.items()} + req_copy = {key: val if key is not param else None for (key, val) in req_param_dict.items()} with pytest.raises(ValueError): _service.put_cors_configuration(**req_copy) @@ -11139,6 +11667,7 @@ def test_put_cors_configuration_value_error_with_retries(self): _service.disable_retries() self.test_put_cors_configuration_value_error() + # endregion ############################################################################## # End of Service: CORS @@ -11149,7 +11678,8 @@ def test_put_cors_configuration_value_error_with_retries(self): ############################################################################## # region -class TestNewInstance(): + +class TestNewInstance: """ Test Class for new_instance """ @@ -11176,7 +11706,8 @@ def test_new_instance_without_authenticator(self): service_name='TEST_SERVICE_NOT_FOUND', ) -class TestHeadAttachment(): + +class TestHeadAttachment: """ Test Class for head_attachment """ @@ -11188,9 +11719,11 @@ def test_head_attachment_all_params(self): """ # Set up mock url = preprocess_url('/testString/testString/testString') - responses.add(responses.HEAD, - url, - status=200) + responses.add( + responses.HEAD, + url, + status=200, + ) # Set up parameter values db = 'testString' @@ -11208,14 +11741,14 @@ def test_head_attachment_all_params(self): if_match=if_match, if_none_match=if_none_match, rev=rev, - headers={} + headers={}, ) # Check for correct operation assert len(responses.calls) == 1 assert response.status_code == 200 # Validate query params - query_string = responses.calls[0].request.url.split('?',1)[1] + query_string = responses.calls[0].request.url.split('?', 1)[1] query_string = urllib.parse.unquote_plus(query_string) assert 'rev={}'.format(rev) in query_string @@ -11235,9 +11768,11 @@ def test_head_attachment_required_params(self): """ # Set up mock url = preprocess_url('/testString/testString/testString') - responses.add(responses.HEAD, - url, - status=200) + responses.add( + responses.HEAD, + url, + status=200, + ) # Set up parameter values db = 'testString' @@ -11249,7 +11784,7 @@ def test_head_attachment_required_params(self): db, doc_id, attachment_name, - headers={} + headers={}, ) # Check for correct operation @@ -11272,9 +11807,11 @@ def test_head_attachment_value_error(self): """ # Set up mock url = preprocess_url('/testString/testString/testString') - responses.add(responses.HEAD, - url, - status=200) + responses.add( + responses.HEAD, + url, + status=200, + ) # Set up parameter values db = 'testString' @@ -11288,7 +11825,7 @@ def test_head_attachment_value_error(self): "attachment_name": attachment_name, } for param in req_param_dict.keys(): - req_copy = {key:val if key is not param else None for (key,val) in req_param_dict.items()} + req_copy = {key: val if key is not param else None for (key, val) in req_param_dict.items()} with pytest.raises(ValueError): _service.head_attachment(**req_copy) @@ -11301,7 +11838,8 @@ def test_head_attachment_value_error_with_retries(self): _service.disable_retries() self.test_head_attachment_value_error() -class TestDeleteAttachment(): + +class TestDeleteAttachment: """ Test Class for delete_attachment """ @@ -11313,12 +11851,14 @@ def test_delete_attachment_all_params(self): """ # Set up mock url = preprocess_url('/testString/testString/testString') - mock_response = '{"id": "id", "rev": "rev", "ok": true, "caused_by": "caused_by", "error": "error", "reason": "reason"}' - responses.add(responses.DELETE, - url, - body=mock_response, - content_type='application/json', - status=200) + mock_response = '{"id": "id", "rev": "rev", "ok": true, "caused_by": "caused_by", "error": "error", "reason": "reason", "ref": 0}' + responses.add( + responses.DELETE, + url, + body=mock_response, + content_type='application/json', + status=200, + ) # Set up parameter values db = 'testString' @@ -11336,14 +11876,14 @@ def test_delete_attachment_all_params(self): if_match=if_match, rev=rev, batch=batch, - headers={} + headers={}, ) # Check for correct operation assert len(responses.calls) == 1 assert response.status_code == 200 # Validate query params - query_string = responses.calls[0].request.url.split('?',1)[1] + query_string = responses.calls[0].request.url.split('?', 1)[1] query_string = urllib.parse.unquote_plus(query_string) assert 'rev={}'.format(rev) in query_string assert 'batch={}'.format(batch) in query_string @@ -11364,12 +11904,14 @@ def test_delete_attachment_required_params(self): """ # Set up mock url = preprocess_url('/testString/testString/testString') - mock_response = '{"id": "id", "rev": "rev", "ok": true, "caused_by": "caused_by", "error": "error", "reason": "reason"}' - responses.add(responses.DELETE, - url, - body=mock_response, - content_type='application/json', - status=200) + mock_response = '{"id": "id", "rev": "rev", "ok": true, "caused_by": "caused_by", "error": "error", "reason": "reason", "ref": 0}' + responses.add( + responses.DELETE, + url, + body=mock_response, + content_type='application/json', + status=200, + ) # Set up parameter values db = 'testString' @@ -11381,7 +11923,7 @@ def test_delete_attachment_required_params(self): db, doc_id, attachment_name, - headers={} + headers={}, ) # Check for correct operation @@ -11404,12 +11946,14 @@ def test_delete_attachment_value_error(self): """ # Set up mock url = preprocess_url('/testString/testString/testString') - mock_response = '{"id": "id", "rev": "rev", "ok": true, "caused_by": "caused_by", "error": "error", "reason": "reason"}' - responses.add(responses.DELETE, - url, - body=mock_response, - content_type='application/json', - status=200) + mock_response = '{"id": "id", "rev": "rev", "ok": true, "caused_by": "caused_by", "error": "error", "reason": "reason", "ref": 0}' + responses.add( + responses.DELETE, + url, + body=mock_response, + content_type='application/json', + status=200, + ) # Set up parameter values db = 'testString' @@ -11423,7 +11967,7 @@ def test_delete_attachment_value_error(self): "attachment_name": attachment_name, } for param in req_param_dict.keys(): - req_copy = {key:val if key is not param else None for (key,val) in req_param_dict.items()} + req_copy = {key: val if key is not param else None for (key, val) in req_param_dict.items()} with pytest.raises(ValueError): _service.delete_attachment(**req_copy) @@ -11436,7 +11980,8 @@ def test_delete_attachment_value_error_with_retries(self): _service.disable_retries() self.test_delete_attachment_value_error() -class TestGetAttachment(): + +class TestGetAttachment: """ Test Class for get_attachment """ @@ -11449,11 +11994,13 @@ def test_get_attachment_all_params(self): # Set up mock url = preprocess_url('/testString/testString/testString') mock_response = 'This is a mock binary response.' - responses.add(responses.GET, - url, - body=mock_response, - content_type='*/*', - status=200) + responses.add( + responses.GET, + url, + body=mock_response, + content_type='*/*', + status=200, + ) # Set up parameter values db = 'testString' @@ -11473,14 +12020,14 @@ def test_get_attachment_all_params(self): if_none_match=if_none_match, range=range, rev=rev, - headers={} + headers={}, ) # Check for correct operation assert len(responses.calls) == 1 assert response.status_code == 200 # Validate query params - query_string = responses.calls[0].request.url.split('?',1)[1] + query_string = responses.calls[0].request.url.split('?', 1)[1] query_string = urllib.parse.unquote_plus(query_string) assert 'rev={}'.format(rev) in query_string @@ -11501,11 +12048,13 @@ def test_get_attachment_required_params(self): # Set up mock url = preprocess_url('/testString/testString/testString') mock_response = 'This is a mock binary response.' - responses.add(responses.GET, - url, - body=mock_response, - content_type='*/*', - status=200) + responses.add( + responses.GET, + url, + body=mock_response, + content_type='*/*', + status=200, + ) # Set up parameter values db = 'testString' @@ -11517,7 +12066,7 @@ def test_get_attachment_required_params(self): db, doc_id, attachment_name, - headers={} + headers={}, ) # Check for correct operation @@ -11541,11 +12090,13 @@ def test_get_attachment_value_error(self): # Set up mock url = preprocess_url('/testString/testString/testString') mock_response = 'This is a mock binary response.' - responses.add(responses.GET, - url, - body=mock_response, - content_type='*/*', - status=200) + responses.add( + responses.GET, + url, + body=mock_response, + content_type='*/*', + status=200, + ) # Set up parameter values db = 'testString' @@ -11559,7 +12110,7 @@ def test_get_attachment_value_error(self): "attachment_name": attachment_name, } for param in req_param_dict.keys(): - req_copy = {key:val if key is not param else None for (key,val) in req_param_dict.items()} + req_copy = {key: val if key is not param else None for (key, val) in req_param_dict.items()} with pytest.raises(ValueError): _service.get_attachment(**req_copy) @@ -11572,7 +12123,8 @@ def test_get_attachment_value_error_with_retries(self): _service.disable_retries() self.test_get_attachment_value_error() -class TestPutAttachment(): + +class TestPutAttachment: """ Test Class for put_attachment """ @@ -11584,12 +12136,14 @@ def test_put_attachment_all_params(self): """ # Set up mock url = preprocess_url('/testString/testString/testString') - mock_response = '{"id": "id", "rev": "rev", "ok": true, "caused_by": "caused_by", "error": "error", "reason": "reason"}' - responses.add(responses.PUT, - url, - body=mock_response, - content_type='application/json', - status=201) + mock_response = '{"id": "id", "rev": "rev", "ok": true, "caused_by": "caused_by", "error": "error", "reason": "reason", "ref": 0}' + responses.add( + responses.PUT, + url, + body=mock_response, + content_type='application/json', + status=201, + ) # Set up parameter values db = 'testString' @@ -11609,18 +12163,19 @@ def test_put_attachment_all_params(self): content_type, if_match=if_match, rev=rev, - headers={} + headers={}, ) # Check for correct operation assert len(responses.calls) == 1 assert response.status_code == 201 # Validate query params - query_string = responses.calls[0].request.url.split('?',1)[1] + query_string = responses.calls[0].request.url.split('?', 1)[1] query_string = urllib.parse.unquote_plus(query_string) assert 'rev={}'.format(rev) in query_string - # decompress gzip compressed request body - responses.calls[0].request.body = gzip.decompress(responses.calls[0].request.body) + # Decompress gzip compressed request body. + raw_body = responses.calls[0].request.body + responses.calls[0].request.body = gzip.decompress(raw_body.read() if isinstance(raw_body, io.IOBase) else raw_body) # Validate body params @@ -11640,12 +12195,14 @@ def test_put_attachment_required_params(self): """ # Set up mock url = preprocess_url('/testString/testString/testString') - mock_response = '{"id": "id", "rev": "rev", "ok": true, "caused_by": "caused_by", "error": "error", "reason": "reason"}' - responses.add(responses.PUT, - url, - body=mock_response, - content_type='application/json', - status=201) + mock_response = '{"id": "id", "rev": "rev", "ok": true, "caused_by": "caused_by", "error": "error", "reason": "reason", "ref": 0}' + responses.add( + responses.PUT, + url, + body=mock_response, + content_type='application/json', + status=201, + ) # Set up parameter values db = 'testString' @@ -11661,14 +12218,15 @@ def test_put_attachment_required_params(self): attachment_name, attachment, content_type, - headers={} + headers={}, ) # Check for correct operation assert len(responses.calls) == 1 assert response.status_code == 201 - # decompress gzip compressed request body - responses.calls[0].request.body = gzip.decompress(responses.calls[0].request.body) + # Decompress gzip compressed request body. + raw_body = responses.calls[0].request.body + responses.calls[0].request.body = gzip.decompress(raw_body.read() if isinstance(raw_body, io.IOBase) else raw_body) # Validate body params @@ -11688,12 +12246,14 @@ def test_put_attachment_value_error(self): """ # Set up mock url = preprocess_url('/testString/testString/testString') - mock_response = '{"id": "id", "rev": "rev", "ok": true, "caused_by": "caused_by", "error": "error", "reason": "reason"}' - responses.add(responses.PUT, - url, - body=mock_response, - content_type='application/json', - status=201) + mock_response = '{"id": "id", "rev": "rev", "ok": true, "caused_by": "caused_by", "error": "error", "reason": "reason", "ref": 0}' + responses.add( + responses.PUT, + url, + body=mock_response, + content_type='application/json', + status=201, + ) # Set up parameter values db = 'testString' @@ -11711,7 +12271,7 @@ def test_put_attachment_value_error(self): "content_type": content_type, } for param in req_param_dict.keys(): - req_copy = {key:val if key is not param else None for (key,val) in req_param_dict.items()} + req_copy = {key: val if key is not param else None for (key, val) in req_param_dict.items()} with pytest.raises(ValueError): _service.put_attachment(**req_copy) @@ -11724,6 +12284,7 @@ def test_put_attachment_value_error_with_retries(self): _service.disable_retries() self.test_put_attachment_value_error() + # endregion ############################################################################## # End of Service: Attachments @@ -11734,7 +12295,8 @@ def test_put_attachment_value_error_with_retries(self): ############################################################################## # region -class TestNewInstance(): + +class TestNewInstance: """ Test Class for new_instance """ @@ -11761,7 +12323,8 @@ def test_new_instance_without_authenticator(self): service_name='TEST_SERVICE_NOT_FOUND', ) -class TestHeadLocalDocument(): + +class TestHeadLocalDocument: """ Test Class for head_local_document """ @@ -11773,9 +12336,11 @@ def test_head_local_document_all_params(self): """ # Set up mock url = preprocess_url('/testString/_local/testString') - responses.add(responses.HEAD, - url, - status=200) + responses.add( + responses.HEAD, + url, + status=200, + ) # Set up parameter values db = 'testString' @@ -11787,7 +12352,7 @@ def test_head_local_document_all_params(self): db, doc_id, if_none_match=if_none_match, - headers={} + headers={}, ) # Check for correct operation @@ -11810,9 +12375,11 @@ def test_head_local_document_required_params(self): """ # Set up mock url = preprocess_url('/testString/_local/testString') - responses.add(responses.HEAD, - url, - status=200) + responses.add( + responses.HEAD, + url, + status=200, + ) # Set up parameter values db = 'testString' @@ -11822,7 +12389,7 @@ def test_head_local_document_required_params(self): response = _service.head_local_document( db, doc_id, - headers={} + headers={}, ) # Check for correct operation @@ -11845,9 +12412,11 @@ def test_head_local_document_value_error(self): """ # Set up mock url = preprocess_url('/testString/_local/testString') - responses.add(responses.HEAD, - url, - status=200) + responses.add( + responses.HEAD, + url, + status=200, + ) # Set up parameter values db = 'testString' @@ -11859,7 +12428,7 @@ def test_head_local_document_value_error(self): "doc_id": doc_id, } for param in req_param_dict.keys(): - req_copy = {key:val if key is not param else None for (key,val) in req_param_dict.items()} + req_copy = {key: val if key is not param else None for (key, val) in req_param_dict.items()} with pytest.raises(ValueError): _service.head_local_document(**req_copy) @@ -11872,7 +12441,8 @@ def test_head_local_document_value_error_with_retries(self): _service.disable_retries() self.test_head_local_document_value_error() -class TestDeleteLocalDocument(): + +class TestDeleteLocalDocument: """ Test Class for delete_local_document """ @@ -11884,12 +12454,14 @@ def test_delete_local_document_all_params(self): """ # Set up mock url = preprocess_url('/testString/_local/testString') - mock_response = '{"id": "id", "rev": "rev", "ok": true, "caused_by": "caused_by", "error": "error", "reason": "reason"}' - responses.add(responses.DELETE, - url, - body=mock_response, - content_type='application/json', - status=200) + mock_response = '{"id": "id", "rev": "rev", "ok": true, "caused_by": "caused_by", "error": "error", "reason": "reason", "ref": 0}' + responses.add( + responses.DELETE, + url, + body=mock_response, + content_type='application/json', + status=200, + ) # Set up parameter values db = 'testString' @@ -11901,14 +12473,14 @@ def test_delete_local_document_all_params(self): db, doc_id, batch=batch, - headers={} + headers={}, ) # Check for correct operation assert len(responses.calls) == 1 assert response.status_code == 200 # Validate query params - query_string = responses.calls[0].request.url.split('?',1)[1] + query_string = responses.calls[0].request.url.split('?', 1)[1] query_string = urllib.parse.unquote_plus(query_string) assert 'batch={}'.format(batch) in query_string @@ -11928,12 +12500,14 @@ def test_delete_local_document_required_params(self): """ # Set up mock url = preprocess_url('/testString/_local/testString') - mock_response = '{"id": "id", "rev": "rev", "ok": true, "caused_by": "caused_by", "error": "error", "reason": "reason"}' - responses.add(responses.DELETE, - url, - body=mock_response, - content_type='application/json', - status=200) + mock_response = '{"id": "id", "rev": "rev", "ok": true, "caused_by": "caused_by", "error": "error", "reason": "reason", "ref": 0}' + responses.add( + responses.DELETE, + url, + body=mock_response, + content_type='application/json', + status=200, + ) # Set up parameter values db = 'testString' @@ -11943,7 +12517,7 @@ def test_delete_local_document_required_params(self): response = _service.delete_local_document( db, doc_id, - headers={} + headers={}, ) # Check for correct operation @@ -11966,12 +12540,14 @@ def test_delete_local_document_value_error(self): """ # Set up mock url = preprocess_url('/testString/_local/testString') - mock_response = '{"id": "id", "rev": "rev", "ok": true, "caused_by": "caused_by", "error": "error", "reason": "reason"}' - responses.add(responses.DELETE, - url, - body=mock_response, - content_type='application/json', - status=200) + mock_response = '{"id": "id", "rev": "rev", "ok": true, "caused_by": "caused_by", "error": "error", "reason": "reason", "ref": 0}' + responses.add( + responses.DELETE, + url, + body=mock_response, + content_type='application/json', + status=200, + ) # Set up parameter values db = 'testString' @@ -11983,7 +12559,7 @@ def test_delete_local_document_value_error(self): "doc_id": doc_id, } for param in req_param_dict.keys(): - req_copy = {key:val if key is not param else None for (key,val) in req_param_dict.items()} + req_copy = {key: val if key is not param else None for (key, val) in req_param_dict.items()} with pytest.raises(ValueError): _service.delete_local_document(**req_copy) @@ -11996,7 +12572,8 @@ def test_delete_local_document_value_error_with_retries(self): _service.disable_retries() self.test_delete_local_document_value_error() -class TestGetLocalDocument(): + +class TestGetLocalDocument: """ Test Class for get_local_document """ @@ -12008,12 +12585,14 @@ def test_get_local_document_all_params(self): """ # Set up mock url = preprocess_url('/testString/_local/testString') - mock_response = '{"_attachments": {"mapKey": {"content_type": "content_type", "data": "VGhpcyBpcyBhbiBlbmNvZGVkIGJ5dGUgYXJyYXku", "digest": "digest", "encoded_length": 0, "encoding": "encoding", "follows": false, "length": 0, "revpos": 1, "stub": true}}, "_conflicts": ["conflicts"], "_deleted": false, "_deleted_conflicts": ["deleted_conflicts"], "_id": "id", "_local_seq": "local_seq", "_rev": "rev", "_revisions": {"ids": ["ids"], "start": 1}, "_revs_info": [{"rev": "rev", "status": "available"}]}' - responses.add(responses.GET, - url, - body=mock_response, - content_type='application/json', - status=200) + mock_response = '{"_attachments": {"mapKey": {"content_type": "content_type", "data": "VGhpcyBpcyBhIG1vY2sgYnl0ZSBhcnJheSB2YWx1ZS4=", "digest": "digest", "encoded_length": 0, "encoding": "encoding", "follows": false, "length": 0, "revpos": 1, "stub": true}}, "_conflicts": ["conflicts"], "_deleted": true, "_deleted_conflicts": ["deleted_conflicts"], "_id": "_id", "_local_seq": "_local_seq", "_rev": "_rev", "_revisions": {"ids": ["ids"], "start": 1}, "_revs_info": [{"rev": "rev", "status": "available"}]}' + responses.add( + responses.GET, + url, + body=mock_response, + content_type='application/json', + status=200, + ) # Set up parameter values db = 'testString' @@ -12033,14 +12612,14 @@ def test_get_local_document_all_params(self): attachments=attachments, att_encoding_info=att_encoding_info, local_seq=local_seq, - headers={} + headers={}, ) # Check for correct operation assert len(responses.calls) == 1 assert response.status_code == 200 # Validate query params - query_string = responses.calls[0].request.url.split('?',1)[1] + query_string = responses.calls[0].request.url.split('?', 1)[1] query_string = urllib.parse.unquote_plus(query_string) assert 'attachments={}'.format('true' if attachments else 'false') in query_string assert 'att_encoding_info={}'.format('true' if att_encoding_info else 'false') in query_string @@ -12062,12 +12641,14 @@ def test_get_local_document_required_params(self): """ # Set up mock url = preprocess_url('/testString/_local/testString') - mock_response = '{"_attachments": {"mapKey": {"content_type": "content_type", "data": "VGhpcyBpcyBhbiBlbmNvZGVkIGJ5dGUgYXJyYXku", "digest": "digest", "encoded_length": 0, "encoding": "encoding", "follows": false, "length": 0, "revpos": 1, "stub": true}}, "_conflicts": ["conflicts"], "_deleted": false, "_deleted_conflicts": ["deleted_conflicts"], "_id": "id", "_local_seq": "local_seq", "_rev": "rev", "_revisions": {"ids": ["ids"], "start": 1}, "_revs_info": [{"rev": "rev", "status": "available"}]}' - responses.add(responses.GET, - url, - body=mock_response, - content_type='application/json', - status=200) + mock_response = '{"_attachments": {"mapKey": {"content_type": "content_type", "data": "VGhpcyBpcyBhIG1vY2sgYnl0ZSBhcnJheSB2YWx1ZS4=", "digest": "digest", "encoded_length": 0, "encoding": "encoding", "follows": false, "length": 0, "revpos": 1, "stub": true}}, "_conflicts": ["conflicts"], "_deleted": true, "_deleted_conflicts": ["deleted_conflicts"], "_id": "_id", "_local_seq": "_local_seq", "_rev": "_rev", "_revisions": {"ids": ["ids"], "start": 1}, "_revs_info": [{"rev": "rev", "status": "available"}]}' + responses.add( + responses.GET, + url, + body=mock_response, + content_type='application/json', + status=200, + ) # Set up parameter values db = 'testString' @@ -12077,7 +12658,7 @@ def test_get_local_document_required_params(self): response = _service.get_local_document( db, doc_id, - headers={} + headers={}, ) # Check for correct operation @@ -12100,12 +12681,14 @@ def test_get_local_document_value_error(self): """ # Set up mock url = preprocess_url('/testString/_local/testString') - mock_response = '{"_attachments": {"mapKey": {"content_type": "content_type", "data": "VGhpcyBpcyBhbiBlbmNvZGVkIGJ5dGUgYXJyYXku", "digest": "digest", "encoded_length": 0, "encoding": "encoding", "follows": false, "length": 0, "revpos": 1, "stub": true}}, "_conflicts": ["conflicts"], "_deleted": false, "_deleted_conflicts": ["deleted_conflicts"], "_id": "id", "_local_seq": "local_seq", "_rev": "rev", "_revisions": {"ids": ["ids"], "start": 1}, "_revs_info": [{"rev": "rev", "status": "available"}]}' - responses.add(responses.GET, - url, - body=mock_response, - content_type='application/json', - status=200) + mock_response = '{"_attachments": {"mapKey": {"content_type": "content_type", "data": "VGhpcyBpcyBhIG1vY2sgYnl0ZSBhcnJheSB2YWx1ZS4=", "digest": "digest", "encoded_length": 0, "encoding": "encoding", "follows": false, "length": 0, "revpos": 1, "stub": true}}, "_conflicts": ["conflicts"], "_deleted": true, "_deleted_conflicts": ["deleted_conflicts"], "_id": "_id", "_local_seq": "_local_seq", "_rev": "_rev", "_revisions": {"ids": ["ids"], "start": 1}, "_revs_info": [{"rev": "rev", "status": "available"}]}' + responses.add( + responses.GET, + url, + body=mock_response, + content_type='application/json', + status=200, + ) # Set up parameter values db = 'testString' @@ -12117,7 +12700,7 @@ def test_get_local_document_value_error(self): "doc_id": doc_id, } for param in req_param_dict.keys(): - req_copy = {key:val if key is not param else None for (key,val) in req_param_dict.items()} + req_copy = {key: val if key is not param else None for (key, val) in req_param_dict.items()} with pytest.raises(ValueError): _service.get_local_document(**req_copy) @@ -12130,7 +12713,8 @@ def test_get_local_document_value_error_with_retries(self): _service.disable_retries() self.test_get_local_document_value_error() -class TestPutLocalDocument(): + +class TestPutLocalDocument: """ Test Class for put_local_document """ @@ -12142,12 +12726,14 @@ def test_put_local_document_all_params(self): """ # Set up mock url = preprocess_url('/testString/_local/testString') - mock_response = '{"id": "id", "rev": "rev", "ok": true, "caused_by": "caused_by", "error": "error", "reason": "reason"}' - responses.add(responses.PUT, - url, - body=mock_response, - content_type='application/json', - status=201) + mock_response = '{"id": "id", "rev": "rev", "ok": true, "caused_by": "caused_by", "error": "error", "reason": "reason", "ref": 0}' + responses.add( + responses.PUT, + url, + body=mock_response, + content_type='application/json', + status=201, + ) # Construct a dict representation of a Attachment model attachment_model = {} @@ -12177,12 +12763,21 @@ def test_put_local_document_all_params(self): document_model['_conflicts'] = ['testString'] document_model['_deleted'] = True document_model['_deleted_conflicts'] = ['testString'] - document_model['_id'] = 'testString' + document_model['_id'] = 'exampleid' document_model['_local_seq'] = 'testString' document_model['_rev'] = 'testString' document_model['_revisions'] = revisions_model document_model['_revs_info'] = [document_revision_status_model] - document_model['foo'] = 'testString' + document_model['brand'] = 'Foo' + document_model['colours'] = '["red","green","black","blue"]' + document_model['description'] = 'Slim Colourful Design Electronic Cooking Appliance for ...' + document_model['image'] = 'assets/img/0gmsnghhew.jpg' + document_model['keywords'] = '["Foo","Scales","Weight","Digital","Kitchen"]' + document_model['name'] = 'Digital Kitchen Scales' + document_model['price'] = '14.99' + document_model['productId'] = '1000042' + document_model['taxonomy'] = '["Home","Kitchen","Small Appliances"]' + document_model['type'] = 'product' # Set up parameter values db = 'testString' @@ -12198,18 +12793,19 @@ def test_put_local_document_all_params(self): document, content_type=content_type, batch=batch, - headers={} + headers={}, ) # Check for correct operation assert len(responses.calls) == 1 assert response.status_code == 201 # Validate query params - query_string = responses.calls[0].request.url.split('?',1)[1] + query_string = responses.calls[0].request.url.split('?', 1)[1] query_string = urllib.parse.unquote_plus(query_string) assert 'batch={}'.format(batch) in query_string - # decompress gzip compressed request body - responses.calls[0].request.body = gzip.decompress(responses.calls[0].request.body) + # Decompress gzip compressed request body. + raw_body = responses.calls[0].request.body + responses.calls[0].request.body = gzip.decompress(raw_body.read() if isinstance(raw_body, io.IOBase) else raw_body) # Validate body params @@ -12229,12 +12825,14 @@ def test_put_local_document_required_params(self): """ # Set up mock url = preprocess_url('/testString/_local/testString') - mock_response = '{"id": "id", "rev": "rev", "ok": true, "caused_by": "caused_by", "error": "error", "reason": "reason"}' - responses.add(responses.PUT, - url, - body=mock_response, - content_type='application/json', - status=201) + mock_response = '{"id": "id", "rev": "rev", "ok": true, "caused_by": "caused_by", "error": "error", "reason": "reason", "ref": 0}' + responses.add( + responses.PUT, + url, + body=mock_response, + content_type='application/json', + status=201, + ) # Construct a dict representation of a Attachment model attachment_model = {} @@ -12264,12 +12862,21 @@ def test_put_local_document_required_params(self): document_model['_conflicts'] = ['testString'] document_model['_deleted'] = True document_model['_deleted_conflicts'] = ['testString'] - document_model['_id'] = 'testString' + document_model['_id'] = 'exampleid' document_model['_local_seq'] = 'testString' document_model['_rev'] = 'testString' document_model['_revisions'] = revisions_model document_model['_revs_info'] = [document_revision_status_model] - document_model['foo'] = 'testString' + document_model['brand'] = 'Foo' + document_model['colours'] = '["red","green","black","blue"]' + document_model['description'] = 'Slim Colourful Design Electronic Cooking Appliance for ...' + document_model['image'] = 'assets/img/0gmsnghhew.jpg' + document_model['keywords'] = '["Foo","Scales","Weight","Digital","Kitchen"]' + document_model['name'] = 'Digital Kitchen Scales' + document_model['price'] = '14.99' + document_model['productId'] = '1000042' + document_model['taxonomy'] = '["Home","Kitchen","Small Appliances"]' + document_model['type'] = 'product' # Set up parameter values db = 'testString' @@ -12281,14 +12888,15 @@ def test_put_local_document_required_params(self): db, doc_id, document, - headers={} + headers={}, ) # Check for correct operation assert len(responses.calls) == 1 assert response.status_code == 201 - # decompress gzip compressed request body - responses.calls[0].request.body = gzip.decompress(responses.calls[0].request.body) + # Decompress gzip compressed request body. + raw_body = responses.calls[0].request.body + responses.calls[0].request.body = gzip.decompress(raw_body.read() if isinstance(raw_body, io.IOBase) else raw_body) # Validate body params @@ -12308,12 +12916,14 @@ def test_put_local_document_value_error(self): """ # Set up mock url = preprocess_url('/testString/_local/testString') - mock_response = '{"id": "id", "rev": "rev", "ok": true, "caused_by": "caused_by", "error": "error", "reason": "reason"}' - responses.add(responses.PUT, - url, - body=mock_response, - content_type='application/json', - status=201) + mock_response = '{"id": "id", "rev": "rev", "ok": true, "caused_by": "caused_by", "error": "error", "reason": "reason", "ref": 0}' + responses.add( + responses.PUT, + url, + body=mock_response, + content_type='application/json', + status=201, + ) # Construct a dict representation of a Attachment model attachment_model = {} @@ -12343,12 +12953,21 @@ def test_put_local_document_value_error(self): document_model['_conflicts'] = ['testString'] document_model['_deleted'] = True document_model['_deleted_conflicts'] = ['testString'] - document_model['_id'] = 'testString' + document_model['_id'] = 'exampleid' document_model['_local_seq'] = 'testString' document_model['_rev'] = 'testString' document_model['_revisions'] = revisions_model document_model['_revs_info'] = [document_revision_status_model] - document_model['foo'] = 'testString' + document_model['brand'] = 'Foo' + document_model['colours'] = '["red","green","black","blue"]' + document_model['description'] = 'Slim Colourful Design Electronic Cooking Appliance for ...' + document_model['image'] = 'assets/img/0gmsnghhew.jpg' + document_model['keywords'] = '["Foo","Scales","Weight","Digital","Kitchen"]' + document_model['name'] = 'Digital Kitchen Scales' + document_model['price'] = '14.99' + document_model['productId'] = '1000042' + document_model['taxonomy'] = '["Home","Kitchen","Small Appliances"]' + document_model['type'] = 'product' # Set up parameter values db = 'testString' @@ -12362,7 +12981,7 @@ def test_put_local_document_value_error(self): "document": document, } for param in req_param_dict.keys(): - req_copy = {key:val if key is not param else None for (key,val) in req_param_dict.items()} + req_copy = {key: val if key is not param else None for (key, val) in req_param_dict.items()} with pytest.raises(ValueError): _service.put_local_document(**req_copy) @@ -12375,6 +12994,7 @@ def test_put_local_document_value_error_with_retries(self): _service.disable_retries() self.test_put_local_document_value_error() + # endregion ############################################################################## # End of Service: LocalDocuments @@ -12385,7 +13005,8 @@ def test_put_local_document_value_error_with_retries(self): ############################################################################## # region -class TestNewInstance(): + +class TestNewInstance: """ Test Class for new_instance """ @@ -12412,7 +13033,8 @@ def test_new_instance_without_authenticator(self): service_name='TEST_SERVICE_NOT_FOUND', ) -class TestPostRevsDiff(): + +class TestPostRevsDiff: """ Test Class for post_revs_diff """ @@ -12425,11 +13047,13 @@ def test_post_revs_diff_all_params(self): # Set up mock url = preprocess_url('/testString/_revs_diff') mock_response = '{"mapKey": {"missing": ["missing"], "possible_ancestors": ["possible_ancestors"]}}' - responses.add(responses.POST, - url, - body=mock_response, - content_type='application/json', - status=200) + responses.add( + responses.POST, + url, + body=mock_response, + content_type='application/json', + status=200, + ) # Set up parameter values db = 'testString' @@ -12439,14 +13063,15 @@ def test_post_revs_diff_all_params(self): response = _service.post_revs_diff( db, document_revisions, - headers={} + headers={}, ) # Check for correct operation assert len(responses.calls) == 1 assert response.status_code == 200 - # decompress gzip compressed request body - responses.calls[0].request.body = gzip.decompress(responses.calls[0].request.body) + # Decompress gzip compressed request body. + raw_body = responses.calls[0].request.body + responses.calls[0].request.body = gzip.decompress(raw_body.read() if isinstance(raw_body, io.IOBase) else raw_body) # Validate body params req_body = json.loads(str(responses.calls[0].request.body, 'utf-8')) @@ -12469,11 +13094,13 @@ def test_post_revs_diff_value_error(self): # Set up mock url = preprocess_url('/testString/_revs_diff') mock_response = '{"mapKey": {"missing": ["missing"], "possible_ancestors": ["possible_ancestors"]}}' - responses.add(responses.POST, - url, - body=mock_response, - content_type='application/json', - status=200) + responses.add( + responses.POST, + url, + body=mock_response, + content_type='application/json', + status=200, + ) # Set up parameter values db = 'testString' @@ -12485,7 +13112,7 @@ def test_post_revs_diff_value_error(self): "document_revisions": document_revisions, } for param in req_param_dict.keys(): - req_copy = {key:val if key is not param else None for (key,val) in req_param_dict.items()} + req_copy = {key: val if key is not param else None for (key, val) in req_param_dict.items()} with pytest.raises(ValueError): _service.post_revs_diff(**req_copy) @@ -12498,7 +13125,8 @@ def test_post_revs_diff_value_error_with_retries(self): _service.disable_retries() self.test_post_revs_diff_value_error() -class TestGetShardsInformation(): + +class TestGetShardsInformation: """ Test Class for get_shards_information """ @@ -12511,11 +13139,13 @@ def test_get_shards_information_all_params(self): # Set up mock url = preprocess_url('/testString/_shards') mock_response = '{"shards": {"mapKey": ["inner"]}}' - responses.add(responses.GET, - url, - body=mock_response, - content_type='application/json', - status=200) + responses.add( + responses.GET, + url, + body=mock_response, + content_type='application/json', + status=200, + ) # Set up parameter values db = 'testString' @@ -12523,7 +13153,7 @@ def test_get_shards_information_all_params(self): # Invoke method response = _service.get_shards_information( db, - headers={} + headers={}, ) # Check for correct operation @@ -12547,11 +13177,13 @@ def test_get_shards_information_value_error(self): # Set up mock url = preprocess_url('/testString/_shards') mock_response = '{"shards": {"mapKey": ["inner"]}}' - responses.add(responses.GET, - url, - body=mock_response, - content_type='application/json', - status=200) + responses.add( + responses.GET, + url, + body=mock_response, + content_type='application/json', + status=200, + ) # Set up parameter values db = 'testString' @@ -12561,7 +13193,7 @@ def test_get_shards_information_value_error(self): "db": db, } for param in req_param_dict.keys(): - req_copy = {key:val if key is not param else None for (key,val) in req_param_dict.items()} + req_copy = {key: val if key is not param else None for (key, val) in req_param_dict.items()} with pytest.raises(ValueError): _service.get_shards_information(**req_copy) @@ -12574,7 +13206,8 @@ def test_get_shards_information_value_error_with_retries(self): _service.disable_retries() self.test_get_shards_information_value_error() -class TestGetDocumentShardsInfo(): + +class TestGetDocumentShardsInfo: """ Test Class for get_document_shards_info """ @@ -12587,11 +13220,13 @@ def test_get_document_shards_info_all_params(self): # Set up mock url = preprocess_url('/testString/_shards/testString') mock_response = '{"nodes": ["nodes"], "range": "range"}' - responses.add(responses.GET, - url, - body=mock_response, - content_type='application/json', - status=200) + responses.add( + responses.GET, + url, + body=mock_response, + content_type='application/json', + status=200, + ) # Set up parameter values db = 'testString' @@ -12601,7 +13236,7 @@ def test_get_document_shards_info_all_params(self): response = _service.get_document_shards_info( db, doc_id, - headers={} + headers={}, ) # Check for correct operation @@ -12625,11 +13260,13 @@ def test_get_document_shards_info_value_error(self): # Set up mock url = preprocess_url('/testString/_shards/testString') mock_response = '{"nodes": ["nodes"], "range": "range"}' - responses.add(responses.GET, - url, - body=mock_response, - content_type='application/json', - status=200) + responses.add( + responses.GET, + url, + body=mock_response, + content_type='application/json', + status=200, + ) # Set up parameter values db = 'testString' @@ -12641,7 +13278,7 @@ def test_get_document_shards_info_value_error(self): "doc_id": doc_id, } for param in req_param_dict.keys(): - req_copy = {key:val if key is not param else None for (key,val) in req_param_dict.items()} + req_copy = {key: val if key is not param else None for (key, val) in req_param_dict.items()} with pytest.raises(ValueError): _service.get_document_shards_info(**req_copy) @@ -12654,6 +13291,7 @@ def test_get_document_shards_info_value_error_with_retries(self): _service.disable_retries() self.test_get_document_shards_info_value_error() + # endregion ############################################################################## # End of Service: DatabaseDetails @@ -12664,7 +13302,8 @@ def test_get_document_shards_info_value_error_with_retries(self): ############################################################################## # region -class TestNewInstance(): + +class TestNewInstance: """ Test Class for new_instance """ @@ -12691,7 +13330,8 @@ def test_new_instance_without_authenticator(self): service_name='TEST_SERVICE_NOT_FOUND', ) -class TestHeadUpInformation(): + +class TestHeadUpInformation: """ Test Class for head_up_information """ @@ -12703,14 +13343,15 @@ def test_head_up_information_all_params(self): """ # Set up mock url = preprocess_url('/_up') - responses.add(responses.HEAD, - url, - status=200) + responses.add( + responses.HEAD, + url, + status=200, + ) # Invoke method response = _service.head_up_information() - # Check for correct operation assert len(responses.calls) == 1 assert response.status_code == 200 @@ -12724,7 +13365,8 @@ def test_head_up_information_all_params_with_retries(self): _service.disable_retries() self.test_head_up_information_all_params() -class TestGetActiveTasks(): + +class TestGetActiveTasks: """ Test Class for get_active_tasks """ @@ -12736,17 +13378,18 @@ def test_get_active_tasks_all_params(self): """ # Set up mock url = preprocess_url('/_active_tasks') - mock_response = '[{"changes_done": 0, "database": "database", "node": "node", "pid": "pid", "progress": 0, "started_on": 0, "status": "status", "task": "task", "total_changes": 0, "type": "type", "updated_on": 0}]' - responses.add(responses.GET, - url, - body=mock_response, - content_type='application/json', - status=200) + mock_response = '[{"bulk_get_attempts": 0, "bulk_get_docs": 0, "changes_done": 0, "changes_pending": 0, "checkpoint_interval": 30000, "checkpointed_source_seq": "checkpointed_source_seq", "continuous": false, "database": "database", "design_document": "design_document", "doc_id": "doc_id", "doc_write_failures": 0, "docs_read": 0, "docs_written": 0, "index": "index", "indexer_pid": "indexer_pid", "missing_revisions_found": 0, "node": "node", "phase": "docid_sort", "pid": "pid", "process_status": "exiting", "progress": 0, "replication_id": "replication_id", "retry": false, "revisions_checked": 0, "source": "source", "source_seq": "source_seq", "started_on": 0, "target": "target", "through_seq": "through_seq", "total_changes": 0, "type": "database_compaction", "updated_on": 0, "user": "user", "view": 0}]' + responses.add( + responses.GET, + url, + body=mock_response, + content_type='application/json', + status=200, + ) # Invoke method response = _service.get_active_tasks() - # Check for correct operation assert len(responses.calls) == 1 assert response.status_code == 200 @@ -12760,195 +13403,322 @@ def test_get_active_tasks_all_params_with_retries(self): _service.disable_retries() self.test_get_active_tasks_all_params() -class TestGetUpInformation(): + +class TestGetActivityTrackerEvents: """ - Test Class for get_up_information + Test Class for get_activity_tracker_events """ @responses.activate - def test_get_up_information_all_params(self): + def test_get_activity_tracker_events_all_params(self): """ - get_up_information() + get_activity_tracker_events() """ # Set up mock - url = preprocess_url('/_up') - mock_response = '{"seeds": {"anyKey": "anyValue"}, "status": "maintenance_mode"}' - responses.add(responses.GET, - url, - body=mock_response, - content_type='application/json', - status=200) + url = preprocess_url('/_api/v2/user/activity_tracker/events') + mock_response = '{"types": ["management"]}' + responses.add( + responses.GET, + url, + body=mock_response, + content_type='application/json', + status=200, + ) # Invoke method - response = _service.get_up_information() - + response = _service.get_activity_tracker_events() # Check for correct operation assert len(responses.calls) == 1 assert response.status_code == 200 - def test_get_up_information_all_params_with_retries(self): - # Enable retries and run test_get_up_information_all_params. + def test_get_activity_tracker_events_all_params_with_retries(self): + # Enable retries and run test_get_activity_tracker_events_all_params. _service.enable_retries() - self.test_get_up_information_all_params() + self.test_get_activity_tracker_events_all_params() - # Disable retries and run test_get_up_information_all_params. + # Disable retries and run test_get_activity_tracker_events_all_params. _service.disable_retries() - self.test_get_up_information_all_params() + self.test_get_activity_tracker_events_all_params() -class TestGetActivityTrackerEvents(): + +class TestPostActivityTrackerEvents: """ - Test Class for get_activity_tracker_events + Test Class for post_activity_tracker_events """ @responses.activate - def test_get_activity_tracker_events_all_params(self): + def test_post_activity_tracker_events_all_params(self): """ - get_activity_tracker_events() + post_activity_tracker_events() """ # Set up mock url = preprocess_url('/_api/v2/user/activity_tracker/events') - mock_response = '{"types": ["management"]}' - responses.add(responses.GET, - url, - body=mock_response, - content_type='application/json', - status=200) + mock_response = '{"ok": true}' + responses.add( + responses.POST, + url, + body=mock_response, + content_type='application/json', + status=200, + ) + + # Set up parameter values + types = ['management', 'data'] # Invoke method - response = _service.get_activity_tracker_events() + response = _service.post_activity_tracker_events( + types, + headers={}, + ) + + # Check for correct operation + assert len(responses.calls) == 1 + assert response.status_code == 200 + # Decompress gzip compressed request body. + raw_body = responses.calls[0].request.body + responses.calls[0].request.body = gzip.decompress(raw_body.read() if isinstance(raw_body, io.IOBase) else raw_body) + + # Validate body params + req_body = json.loads(str(responses.calls[0].request.body, 'utf-8')) + assert req_body['types'] == ['management', 'data'] + + def test_post_activity_tracker_events_all_params_with_retries(self): + # Enable retries and run test_post_activity_tracker_events_all_params. + _service.enable_retries() + self.test_post_activity_tracker_events_all_params() + + # Disable retries and run test_post_activity_tracker_events_all_params. + _service.disable_retries() + self.test_post_activity_tracker_events_all_params() + + @responses.activate + def test_post_activity_tracker_events_value_error(self): + """ + test_post_activity_tracker_events_value_error() + """ + # Set up mock + url = preprocess_url('/_api/v2/user/activity_tracker/events') + mock_response = '{"ok": true}' + responses.add( + responses.POST, + url, + body=mock_response, + content_type='application/json', + status=200, + ) + # Set up parameter values + types = ['management', 'data'] + + # Pass in all but one required param and check for a ValueError + req_param_dict = { + "types": types, + } + for param in req_param_dict.keys(): + req_copy = {key: val if key is not param else None for (key, val) in req_param_dict.items()} + with pytest.raises(ValueError): + _service.post_activity_tracker_events(**req_copy) + + def test_post_activity_tracker_events_value_error_with_retries(self): + # Enable retries and run test_post_activity_tracker_events_value_error. + _service.enable_retries() + self.test_post_activity_tracker_events_value_error() + + # Disable retries and run test_post_activity_tracker_events_value_error. + _service.disable_retries() + self.test_post_activity_tracker_events_value_error() + + +class TestGetCapacityDatabasesInformation: + """ + Test Class for get_capacity_databases_information + """ + + @responses.activate + def test_get_capacity_databases_information_all_params(self): + """ + get_capacity_databases_information() + """ + # Set up mock + url = preprocess_url('/_api/v2/user/capacity/databases') + mock_response = '{"current": {"databases": {"total": 0}}}' + responses.add( + responses.GET, + url, + body=mock_response, + content_type='application/json', + status=200, + ) + + # Invoke method + response = _service.get_capacity_databases_information() # Check for correct operation assert len(responses.calls) == 1 assert response.status_code == 200 - def test_get_activity_tracker_events_all_params_with_retries(self): - # Enable retries and run test_get_activity_tracker_events_all_params. + def test_get_capacity_databases_information_all_params_with_retries(self): + # Enable retries and run test_get_capacity_databases_information_all_params. _service.enable_retries() - self.test_get_activity_tracker_events_all_params() + self.test_get_capacity_databases_information_all_params() - # Disable retries and run test_get_activity_tracker_events_all_params. + # Disable retries and run test_get_capacity_databases_information_all_params. _service.disable_retries() - self.test_get_activity_tracker_events_all_params() + self.test_get_capacity_databases_information_all_params() + -class TestPostActivityTrackerEvents(): +class TestGetCurrentDatabasesInformation: """ - Test Class for post_activity_tracker_events + Test Class for get_current_databases_information """ @responses.activate - def test_post_activity_tracker_events_all_params(self): + def test_get_current_databases_information_all_params(self): """ - post_activity_tracker_events() + get_current_databases_information() """ # Set up mock - url = preprocess_url('/_api/v2/user/activity_tracker/events') - mock_response = '{"ok": true}' - responses.add(responses.POST, - url, - body=mock_response, - content_type='application/json', - status=200) + url = preprocess_url('/_api/v2/user/current/databases') + mock_response = '{"databases": {"total": 0}}' + responses.add( + responses.GET, + url, + body=mock_response, + content_type='application/json', + status=200, + ) - # Set up parameter values - types = ['management'] + # Invoke method + response = _service.get_current_databases_information() + + # Check for correct operation + assert len(responses.calls) == 1 + assert response.status_code == 200 + + def test_get_current_databases_information_all_params_with_retries(self): + # Enable retries and run test_get_current_databases_information_all_params. + _service.enable_retries() + self.test_get_current_databases_information_all_params() + + # Disable retries and run test_get_current_databases_information_all_params. + _service.disable_retries() + self.test_get_current_databases_information_all_params() + + +class TestGetCurrentThroughputInformation: + """ + Test Class for get_current_throughput_information + """ + + @responses.activate + def test_get_current_throughput_information_all_params(self): + """ + get_current_throughput_information() + """ + # Set up mock + url = preprocess_url('/_api/v2/user/current/throughput') + mock_response = '{"throughput": {"query": 0, "read": 0, "write": 0}}' + responses.add( + responses.GET, + url, + body=mock_response, + content_type='application/json', + status=200, + ) # Invoke method - response = _service.post_activity_tracker_events( - types, - headers={} - ) + response = _service.get_current_throughput_information() # Check for correct operation assert len(responses.calls) == 1 assert response.status_code == 200 - # decompress gzip compressed request body - responses.calls[0].request.body = gzip.decompress(responses.calls[0].request.body) - - # Validate body params - req_body = json.loads(str(responses.calls[0].request.body, 'utf-8')) - assert req_body['types'] == ['management'] - def test_post_activity_tracker_events_all_params_with_retries(self): - # Enable retries and run test_post_activity_tracker_events_all_params. + def test_get_current_throughput_information_all_params_with_retries(self): + # Enable retries and run test_get_current_throughput_information_all_params. _service.enable_retries() - self.test_post_activity_tracker_events_all_params() + self.test_get_current_throughput_information_all_params() - # Disable retries and run test_post_activity_tracker_events_all_params. + # Disable retries and run test_get_current_throughput_information_all_params. _service.disable_retries() - self.test_post_activity_tracker_events_all_params() + self.test_get_current_throughput_information_all_params() + + +class TestGetMembershipInformation: + """ + Test Class for get_membership_information + """ @responses.activate - def test_post_activity_tracker_events_value_error(self): + def test_get_membership_information_all_params(self): """ - test_post_activity_tracker_events_value_error() + get_membership_information() """ # Set up mock - url = preprocess_url('/_api/v2/user/activity_tracker/events') - mock_response = '{"ok": true}' - responses.add(responses.POST, - url, - body=mock_response, - content_type='application/json', - status=200) + url = preprocess_url('/_membership') + mock_response = '{"all_nodes": ["all_nodes"], "cluster_nodes": ["cluster_nodes"]}' + responses.add( + responses.GET, + url, + body=mock_response, + content_type='application/json', + status=200, + ) - # Set up parameter values - types = ['management'] + # Invoke method + response = _service.get_membership_information() - # Pass in all but one required param and check for a ValueError - req_param_dict = { - "types": types, - } - for param in req_param_dict.keys(): - req_copy = {key:val if key is not param else None for (key,val) in req_param_dict.items()} - with pytest.raises(ValueError): - _service.post_activity_tracker_events(**req_copy) + # Check for correct operation + assert len(responses.calls) == 1 + assert response.status_code == 200 - def test_post_activity_tracker_events_value_error_with_retries(self): - # Enable retries and run test_post_activity_tracker_events_value_error. + def test_get_membership_information_all_params_with_retries(self): + # Enable retries and run test_get_membership_information_all_params. _service.enable_retries() - self.test_post_activity_tracker_events_value_error() + self.test_get_membership_information_all_params() - # Disable retries and run test_post_activity_tracker_events_value_error. + # Disable retries and run test_get_membership_information_all_params. _service.disable_retries() - self.test_post_activity_tracker_events_value_error() + self.test_get_membership_information_all_params() + -class TestGetCurrentThroughputInformation(): +class TestGetUpInformation: """ - Test Class for get_current_throughput_information + Test Class for get_up_information """ @responses.activate - def test_get_current_throughput_information_all_params(self): + def test_get_up_information_all_params(self): """ - get_current_throughput_information() + get_up_information() """ # Set up mock - url = preprocess_url('/_api/v2/user/current/throughput') - mock_response = '{"throughput": {"query": 0, "read": 0, "write": 0}}' - responses.add(responses.GET, - url, - body=mock_response, - content_type='application/json', - status=200) + url = preprocess_url('/_up') + mock_response = '{"cluster": "cluster", "seeds": {"anyKey": "anyValue"}, "status": "maintenance_mode"}' + responses.add( + responses.GET, + url, + body=mock_response, + content_type='application/json', + status=200, + ) # Invoke method - response = _service.get_current_throughput_information() - + response = _service.get_up_information() # Check for correct operation assert len(responses.calls) == 1 assert response.status_code == 200 - def test_get_current_throughput_information_all_params_with_retries(self): - # Enable retries and run test_get_current_throughput_information_all_params. + def test_get_up_information_all_params_with_retries(self): + # Enable retries and run test_get_up_information_all_params. _service.enable_retries() - self.test_get_current_throughput_information_all_params() + self.test_get_up_information_all_params() - # Disable retries and run test_get_current_throughput_information_all_params. + # Disable retries and run test_get_up_information_all_params. _service.disable_retries() - self.test_get_current_throughput_information_all_params() + self.test_get_up_information_all_params() + # endregion ############################################################################## @@ -12960,7 +13730,9 @@ def test_get_current_throughput_information_all_params_with_retries(self): # Start of Model Tests ############################################################################## # region -class TestModel_ActiveTask(): + + +class TestModel_ActiveTask: """ Test Class for ActiveTask """ @@ -12972,17 +13744,40 @@ def test_active_task_serialization(self): # Construct a json representation of a ActiveTask model active_task_model_json = {} + active_task_model_json['bulk_get_attempts'] = 0 + active_task_model_json['bulk_get_docs'] = 0 active_task_model_json['changes_done'] = 0 + active_task_model_json['changes_pending'] = 0 + active_task_model_json['checkpoint_interval'] = 30000 + active_task_model_json['checkpointed_source_seq'] = 'testString' + active_task_model_json['continuous'] = False active_task_model_json['database'] = 'testString' + active_task_model_json['design_document'] = 'testString' + active_task_model_json['doc_id'] = 'testString' + active_task_model_json['doc_write_failures'] = 0 + active_task_model_json['docs_read'] = 0 + active_task_model_json['docs_written'] = 0 + active_task_model_json['index'] = 'testString' + active_task_model_json['indexer_pid'] = 'testString' + active_task_model_json['missing_revisions_found'] = 0 active_task_model_json['node'] = 'testString' + active_task_model_json['phase'] = 'docid_sort' active_task_model_json['pid'] = 'testString' + active_task_model_json['process_status'] = 'exiting' active_task_model_json['progress'] = 0 + active_task_model_json['replication_id'] = 'testString' + active_task_model_json['retry'] = True + active_task_model_json['revisions_checked'] = 0 + active_task_model_json['source'] = 'testString' + active_task_model_json['source_seq'] = 'testString' active_task_model_json['started_on'] = 0 - active_task_model_json['status'] = 'testString' - active_task_model_json['task'] = 'testString' + active_task_model_json['target'] = 'testString' + active_task_model_json['through_seq'] = 'testString' active_task_model_json['total_changes'] = 0 - active_task_model_json['type'] = 'testString' + active_task_model_json['type'] = 'database_compaction' active_task_model_json['updated_on'] = 0 + active_task_model_json['user'] = 'testString' + active_task_model_json['view'] = 0 # Construct a model instance of ActiveTask by calling from_dict on the json representation active_task_model = ActiveTask.from_dict(active_task_model_json) @@ -12999,7 +13794,8 @@ def test_active_task_serialization(self): active_task_model_json2 = active_task_model.to_dict() assert active_task_model_json2 == active_task_model_json -class TestModel_ActivityTrackerEvents(): + +class TestModel_ActivityTrackerEvents: """ Test Class for ActivityTrackerEvents """ @@ -13028,7 +13824,8 @@ def test_activity_tracker_events_serialization(self): activity_tracker_events_model_json2 = activity_tracker_events_model.to_dict() assert activity_tracker_events_model_json2 == activity_tracker_events_model_json -class TestModel_AllDocsQueriesResult(): + +class TestModel_AllDocsQueriesResult: """ Test Class for AllDocsQueriesResult """ @@ -13040,7 +13837,7 @@ def test_all_docs_queries_result_serialization(self): # Construct dict forms of any model objects needed in order to build this model. - attachment_model = {} # Attachment + attachment_model = {} # Attachment attachment_model['content_type'] = 'testString' attachment_model['data'] = 'VGhpcyBpcyBhIG1vY2sgYnl0ZSBhcnJheSB2YWx1ZS4=' attachment_model['digest'] = 'testString' @@ -13051,15 +13848,15 @@ def test_all_docs_queries_result_serialization(self): attachment_model['revpos'] = 1 attachment_model['stub'] = True - revisions_model = {} # Revisions + revisions_model = {} # Revisions revisions_model['ids'] = ['testString'] revisions_model['start'] = 1 - document_revision_status_model = {} # DocumentRevisionStatus + document_revision_status_model = {} # DocumentRevisionStatus document_revision_status_model['rev'] = 'testString' document_revision_status_model['status'] = 'available' - document_model = {} # Document + document_model = {} # Document document_model['_attachments'] = {'key1': attachment_model} document_model['_conflicts'] = ['testString'] document_model['_deleted'] = True @@ -13071,19 +13868,21 @@ def test_all_docs_queries_result_serialization(self): document_model['_revs_info'] = [document_revision_status_model] document_model['foo'] = 'testString' - docs_result_row_value_model = {} # DocsResultRowValue + docs_result_row_value_model = {} # DocsResultRowValue + docs_result_row_value_model['deleted'] = True docs_result_row_value_model['rev'] = 'testString' - docs_result_row_model = {} # DocsResultRow + docs_result_row_model = {} # DocsResultRow docs_result_row_model['caused_by'] = 'testString' docs_result_row_model['error'] = 'testString' docs_result_row_model['reason'] = 'testString' + docs_result_row_model['ref'] = 0 docs_result_row_model['doc'] = document_model docs_result_row_model['id'] = 'testString' docs_result_row_model['key'] = 'testString' docs_result_row_model['value'] = docs_result_row_value_model - all_docs_result_model = {} # AllDocsResult + all_docs_result_model = {} # AllDocsResult all_docs_result_model['total_rows'] = 0 all_docs_result_model['rows'] = [docs_result_row_model] all_docs_result_model['update_seq'] = 'testString' @@ -13107,7 +13906,8 @@ def test_all_docs_queries_result_serialization(self): all_docs_queries_result_model_json2 = all_docs_queries_result_model.to_dict() assert all_docs_queries_result_model_json2 == all_docs_queries_result_model_json -class TestModel_AllDocsQuery(): + +class TestModel_AllDocsQuery: """ Test Class for AllDocsQuery """ @@ -13148,7 +13948,8 @@ def test_all_docs_query_serialization(self): all_docs_query_model_json2 = all_docs_query_model.to_dict() assert all_docs_query_model_json2 == all_docs_query_model_json -class TestModel_AllDocsResult(): + +class TestModel_AllDocsResult: """ Test Class for AllDocsResult """ @@ -13160,7 +13961,7 @@ def test_all_docs_result_serialization(self): # Construct dict forms of any model objects needed in order to build this model. - attachment_model = {} # Attachment + attachment_model = {} # Attachment attachment_model['content_type'] = 'testString' attachment_model['data'] = 'VGhpcyBpcyBhIG1vY2sgYnl0ZSBhcnJheSB2YWx1ZS4=' attachment_model['digest'] = 'testString' @@ -13171,15 +13972,15 @@ def test_all_docs_result_serialization(self): attachment_model['revpos'] = 1 attachment_model['stub'] = True - revisions_model = {} # Revisions + revisions_model = {} # Revisions revisions_model['ids'] = ['testString'] revisions_model['start'] = 1 - document_revision_status_model = {} # DocumentRevisionStatus + document_revision_status_model = {} # DocumentRevisionStatus document_revision_status_model['rev'] = 'testString' document_revision_status_model['status'] = 'available' - document_model = {} # Document + document_model = {} # Document document_model['_attachments'] = {'key1': attachment_model} document_model['_conflicts'] = ['testString'] document_model['_deleted'] = True @@ -13191,13 +13992,15 @@ def test_all_docs_result_serialization(self): document_model['_revs_info'] = [document_revision_status_model] document_model['foo'] = 'testString' - docs_result_row_value_model = {} # DocsResultRowValue + docs_result_row_value_model = {} # DocsResultRowValue + docs_result_row_value_model['deleted'] = True docs_result_row_value_model['rev'] = 'testString' - docs_result_row_model = {} # DocsResultRow + docs_result_row_model = {} # DocsResultRow docs_result_row_model['caused_by'] = 'testString' docs_result_row_model['error'] = 'testString' docs_result_row_model['reason'] = 'testString' + docs_result_row_model['ref'] = 0 docs_result_row_model['doc'] = document_model docs_result_row_model['id'] = 'testString' docs_result_row_model['key'] = 'testString' @@ -13224,7 +14027,8 @@ def test_all_docs_result_serialization(self): all_docs_result_model_json2 = all_docs_result_model.to_dict() assert all_docs_result_model_json2 == all_docs_result_model_json -class TestModel_Analyzer(): + +class TestModel_Analyzer: """ Test Class for Analyzer """ @@ -13254,7 +14058,8 @@ def test_analyzer_serialization(self): analyzer_model_json2 = analyzer_model.to_dict() assert analyzer_model_json2 == analyzer_model_json -class TestModel_AnalyzerConfiguration(): + +class TestModel_AnalyzerConfiguration: """ Test Class for AnalyzerConfiguration """ @@ -13266,7 +14071,7 @@ def test_analyzer_configuration_serialization(self): # Construct dict forms of any model objects needed in order to build this model. - analyzer_model = {} # Analyzer + analyzer_model = {} # Analyzer analyzer_model['name'] = 'classic' analyzer_model['stopwords'] = ['testString'] @@ -13274,6 +14079,7 @@ def test_analyzer_configuration_serialization(self): analyzer_configuration_model_json = {} analyzer_configuration_model_json['name'] = 'classic' analyzer_configuration_model_json['stopwords'] = ['testString'] + analyzer_configuration_model_json['default'] = analyzer_model analyzer_configuration_model_json['fields'] = {'key1': analyzer_model} # Construct a model instance of AnalyzerConfiguration by calling from_dict on the json representation @@ -13291,7 +14097,8 @@ def test_analyzer_configuration_serialization(self): analyzer_configuration_model_json2 = analyzer_configuration_model.to_dict() assert analyzer_configuration_model_json2 == analyzer_configuration_model_json -class TestModel_ApiKeysResult(): + +class TestModel_ApiKeysResult: """ Test Class for ApiKeysResult """ @@ -13322,7 +14129,8 @@ def test_api_keys_result_serialization(self): api_keys_result_model_json2 = api_keys_result_model.to_dict() assert api_keys_result_model_json2 == api_keys_result_model_json -class TestModel_Attachment(): + +class TestModel_Attachment: """ Test Class for Attachment """ @@ -13359,7 +14167,8 @@ def test_attachment_serialization(self): attachment_model_json2 = attachment_model.to_dict() assert attachment_model_json2 == attachment_model_json -class TestModel_BulkDocs(): + +class TestModel_BulkDocs: """ Test Class for BulkDocs """ @@ -13371,7 +14180,7 @@ def test_bulk_docs_serialization(self): # Construct dict forms of any model objects needed in order to build this model. - attachment_model = {} # Attachment + attachment_model = {} # Attachment attachment_model['content_type'] = 'testString' attachment_model['data'] = 'VGhpcyBpcyBhIG1vY2sgYnl0ZSBhcnJheSB2YWx1ZS4=' attachment_model['digest'] = 'testString' @@ -13382,15 +14191,15 @@ def test_bulk_docs_serialization(self): attachment_model['revpos'] = 1 attachment_model['stub'] = True - revisions_model = {} # Revisions + revisions_model = {} # Revisions revisions_model['ids'] = ['testString'] revisions_model['start'] = 1 - document_revision_status_model = {} # DocumentRevisionStatus + document_revision_status_model = {} # DocumentRevisionStatus document_revision_status_model['rev'] = 'testString' document_revision_status_model['status'] = 'available' - document_model = {} # Document + document_model = {} # Document document_model['_attachments'] = {'key1': attachment_model} document_model['_conflicts'] = ['testString'] document_model['_deleted'] = True @@ -13422,7 +14231,8 @@ def test_bulk_docs_serialization(self): bulk_docs_model_json2 = bulk_docs_model.to_dict() assert bulk_docs_model_json2 == bulk_docs_model_json -class TestModel_BulkGetQueryDocument(): + +class TestModel_BulkGetQueryDocument: """ Test Class for BulkGetQueryDocument """ @@ -13453,7 +14263,8 @@ def test_bulk_get_query_document_serialization(self): bulk_get_query_document_model_json2 = bulk_get_query_document_model.to_dict() assert bulk_get_query_document_model_json2 == bulk_get_query_document_model_json -class TestModel_BulkGetResult(): + +class TestModel_BulkGetResult: """ Test Class for BulkGetResult """ @@ -13465,15 +14276,16 @@ def test_bulk_get_result_serialization(self): # Construct dict forms of any model objects needed in order to build this model. - document_result_model = {} # DocumentResult + document_result_model = {} # DocumentResult document_result_model['id'] = 'testString' document_result_model['rev'] = 'testString' document_result_model['ok'] = True document_result_model['caused_by'] = 'testString' document_result_model['error'] = 'testString' document_result_model['reason'] = 'testString' + document_result_model['ref'] = 0 - attachment_model = {} # Attachment + attachment_model = {} # Attachment attachment_model['content_type'] = 'testString' attachment_model['data'] = 'VGhpcyBpcyBhIG1vY2sgYnl0ZSBhcnJheSB2YWx1ZS4=' attachment_model['digest'] = 'testString' @@ -13484,15 +14296,15 @@ def test_bulk_get_result_serialization(self): attachment_model['revpos'] = 1 attachment_model['stub'] = True - revisions_model = {} # Revisions + revisions_model = {} # Revisions revisions_model['ids'] = ['testString'] revisions_model['start'] = 1 - document_revision_status_model = {} # DocumentRevisionStatus + document_revision_status_model = {} # DocumentRevisionStatus document_revision_status_model['rev'] = 'testString' document_revision_status_model['status'] = 'available' - document_model = {} # Document + document_model = {} # Document document_model['_attachments'] = {'key1': attachment_model} document_model['_conflicts'] = ['testString'] document_model['_deleted'] = True @@ -13504,11 +14316,11 @@ def test_bulk_get_result_serialization(self): document_model['_revs_info'] = [document_revision_status_model] document_model['foo'] = 'testString' - bulk_get_result_document_model = {} # BulkGetResultDocument + bulk_get_result_document_model = {} # BulkGetResultDocument bulk_get_result_document_model['error'] = document_result_model bulk_get_result_document_model['ok'] = document_model - bulk_get_result_item_model = {} # BulkGetResultItem + bulk_get_result_item_model = {} # BulkGetResultItem bulk_get_result_item_model['docs'] = [bulk_get_result_document_model] bulk_get_result_item_model['id'] = 'testString' @@ -13531,7 +14343,8 @@ def test_bulk_get_result_serialization(self): bulk_get_result_model_json2 = bulk_get_result_model.to_dict() assert bulk_get_result_model_json2 == bulk_get_result_model_json -class TestModel_BulkGetResultDocument(): + +class TestModel_BulkGetResultDocument: """ Test Class for BulkGetResultDocument """ @@ -13543,15 +14356,16 @@ def test_bulk_get_result_document_serialization(self): # Construct dict forms of any model objects needed in order to build this model. - document_result_model = {} # DocumentResult + document_result_model = {} # DocumentResult document_result_model['id'] = 'testString' document_result_model['rev'] = 'testString' document_result_model['ok'] = True document_result_model['caused_by'] = 'testString' document_result_model['error'] = 'testString' document_result_model['reason'] = 'testString' + document_result_model['ref'] = 0 - attachment_model = {} # Attachment + attachment_model = {} # Attachment attachment_model['content_type'] = 'testString' attachment_model['data'] = 'VGhpcyBpcyBhIG1vY2sgYnl0ZSBhcnJheSB2YWx1ZS4=' attachment_model['digest'] = 'testString' @@ -13562,15 +14376,15 @@ def test_bulk_get_result_document_serialization(self): attachment_model['revpos'] = 1 attachment_model['stub'] = True - revisions_model = {} # Revisions + revisions_model = {} # Revisions revisions_model['ids'] = ['testString'] revisions_model['start'] = 1 - document_revision_status_model = {} # DocumentRevisionStatus + document_revision_status_model = {} # DocumentRevisionStatus document_revision_status_model['rev'] = 'testString' document_revision_status_model['status'] = 'available' - document_model = {} # Document + document_model = {} # Document document_model['_attachments'] = {'key1': attachment_model} document_model['_conflicts'] = ['testString'] document_model['_deleted'] = True @@ -13602,7 +14416,8 @@ def test_bulk_get_result_document_serialization(self): bulk_get_result_document_model_json2 = bulk_get_result_document_model.to_dict() assert bulk_get_result_document_model_json2 == bulk_get_result_document_model_json -class TestModel_BulkGetResultItem(): + +class TestModel_BulkGetResultItem: """ Test Class for BulkGetResultItem """ @@ -13614,15 +14429,16 @@ def test_bulk_get_result_item_serialization(self): # Construct dict forms of any model objects needed in order to build this model. - document_result_model = {} # DocumentResult + document_result_model = {} # DocumentResult document_result_model['id'] = 'testString' document_result_model['rev'] = 'testString' document_result_model['ok'] = True document_result_model['caused_by'] = 'testString' document_result_model['error'] = 'testString' document_result_model['reason'] = 'testString' + document_result_model['ref'] = 0 - attachment_model = {} # Attachment + attachment_model = {} # Attachment attachment_model['content_type'] = 'testString' attachment_model['data'] = 'VGhpcyBpcyBhIG1vY2sgYnl0ZSBhcnJheSB2YWx1ZS4=' attachment_model['digest'] = 'testString' @@ -13633,15 +14449,15 @@ def test_bulk_get_result_item_serialization(self): attachment_model['revpos'] = 1 attachment_model['stub'] = True - revisions_model = {} # Revisions + revisions_model = {} # Revisions revisions_model['ids'] = ['testString'] revisions_model['start'] = 1 - document_revision_status_model = {} # DocumentRevisionStatus + document_revision_status_model = {} # DocumentRevisionStatus document_revision_status_model['rev'] = 'testString' document_revision_status_model['status'] = 'available' - document_model = {} # Document + document_model = {} # Document document_model['_attachments'] = {'key1': attachment_model} document_model['_conflicts'] = ['testString'] document_model['_deleted'] = True @@ -13653,7 +14469,7 @@ def test_bulk_get_result_item_serialization(self): document_model['_revs_info'] = [document_revision_status_model] document_model['foo'] = 'testString' - bulk_get_result_document_model = {} # BulkGetResultDocument + bulk_get_result_document_model = {} # BulkGetResultDocument bulk_get_result_document_model['error'] = document_result_model bulk_get_result_document_model['ok'] = document_model @@ -13677,7 +14493,81 @@ def test_bulk_get_result_item_serialization(self): bulk_get_result_item_model_json2 = bulk_get_result_item_model.to_dict() assert bulk_get_result_item_model_json2 == bulk_get_result_item_model_json -class TestModel_CapacityThroughputInformation(): + +class TestModel_CapacityDatabasesInformation: + """ + Test Class for CapacityDatabasesInformation + """ + + def test_capacity_databases_information_serialization(self): + """ + Test serialization/deserialization for CapacityDatabasesInformation + """ + + # Construct dict forms of any model objects needed in order to build this model. + + databases_count_information_model = {} # DatabasesCountInformation + databases_count_information_model['total'] = 0 + + capacity_databases_information_current_model = {} # CapacityDatabasesInformationCurrent + capacity_databases_information_current_model['databases'] = databases_count_information_model + + # Construct a json representation of a CapacityDatabasesInformation model + capacity_databases_information_model_json = {} + capacity_databases_information_model_json['current'] = capacity_databases_information_current_model + + # Construct a model instance of CapacityDatabasesInformation by calling from_dict on the json representation + capacity_databases_information_model = CapacityDatabasesInformation.from_dict(capacity_databases_information_model_json) + assert capacity_databases_information_model != False + + # Construct a model instance of CapacityDatabasesInformation by calling from_dict on the json representation + capacity_databases_information_model_dict = CapacityDatabasesInformation.from_dict(capacity_databases_information_model_json).__dict__ + capacity_databases_information_model2 = CapacityDatabasesInformation(**capacity_databases_information_model_dict) + + # Verify the model instances are equivalent + assert capacity_databases_information_model == capacity_databases_information_model2 + + # Convert model instance back to dict and verify no loss of data + capacity_databases_information_model_json2 = capacity_databases_information_model.to_dict() + assert capacity_databases_information_model_json2 == capacity_databases_information_model_json + + +class TestModel_CapacityDatabasesInformationCurrent: + """ + Test Class for CapacityDatabasesInformationCurrent + """ + + def test_capacity_databases_information_current_serialization(self): + """ + Test serialization/deserialization for CapacityDatabasesInformationCurrent + """ + + # Construct dict forms of any model objects needed in order to build this model. + + databases_count_information_model = {} # DatabasesCountInformation + databases_count_information_model['total'] = 0 + + # Construct a json representation of a CapacityDatabasesInformationCurrent model + capacity_databases_information_current_model_json = {} + capacity_databases_information_current_model_json['databases'] = databases_count_information_model + + # Construct a model instance of CapacityDatabasesInformationCurrent by calling from_dict on the json representation + capacity_databases_information_current_model = CapacityDatabasesInformationCurrent.from_dict(capacity_databases_information_current_model_json) + assert capacity_databases_information_current_model != False + + # Construct a model instance of CapacityDatabasesInformationCurrent by calling from_dict on the json representation + capacity_databases_information_current_model_dict = CapacityDatabasesInformationCurrent.from_dict(capacity_databases_information_current_model_json).__dict__ + capacity_databases_information_current_model2 = CapacityDatabasesInformationCurrent(**capacity_databases_information_current_model_dict) + + # Verify the model instances are equivalent + assert capacity_databases_information_current_model == capacity_databases_information_current_model2 + + # Convert model instance back to dict and verify no loss of data + capacity_databases_information_current_model_json2 = capacity_databases_information_current_model.to_dict() + assert capacity_databases_information_current_model_json2 == capacity_databases_information_current_model_json + + +class TestModel_CapacityThroughputInformation: """ Test Class for CapacityThroughputInformation """ @@ -13689,16 +14579,16 @@ def test_capacity_throughput_information_serialization(self): # Construct dict forms of any model objects needed in order to build this model. - throughput_information_model = {} # ThroughputInformation - throughput_information_model['blocks'] = 0 + throughput_information_model = {} # ThroughputInformation + throughput_information_model['blocks'] = 1 throughput_information_model['query'] = 0 throughput_information_model['read'] = 0 throughput_information_model['write'] = 0 - capacity_throughput_information_current_model = {} # CapacityThroughputInformationCurrent + capacity_throughput_information_current_model = {} # CapacityThroughputInformationCurrent capacity_throughput_information_current_model['throughput'] = throughput_information_model - capacity_throughput_information_target_model = {} # CapacityThroughputInformationTarget + capacity_throughput_information_target_model = {} # CapacityThroughputInformationTarget capacity_throughput_information_target_model['throughput'] = throughput_information_model # Construct a json representation of a CapacityThroughputInformation model @@ -13721,7 +14611,8 @@ def test_capacity_throughput_information_serialization(self): capacity_throughput_information_model_json2 = capacity_throughput_information_model.to_dict() assert capacity_throughput_information_model_json2 == capacity_throughput_information_model_json -class TestModel_CapacityThroughputInformationCurrent(): + +class TestModel_CapacityThroughputInformationCurrent: """ Test Class for CapacityThroughputInformationCurrent """ @@ -13733,8 +14624,8 @@ def test_capacity_throughput_information_current_serialization(self): # Construct dict forms of any model objects needed in order to build this model. - throughput_information_model = {} # ThroughputInformation - throughput_information_model['blocks'] = 0 + throughput_information_model = {} # ThroughputInformation + throughput_information_model['blocks'] = 1 throughput_information_model['query'] = 0 throughput_information_model['read'] = 0 throughput_information_model['write'] = 0 @@ -13758,7 +14649,8 @@ def test_capacity_throughput_information_current_serialization(self): capacity_throughput_information_current_model_json2 = capacity_throughput_information_current_model.to_dict() assert capacity_throughput_information_current_model_json2 == capacity_throughput_information_current_model_json -class TestModel_CapacityThroughputInformationTarget(): + +class TestModel_CapacityThroughputInformationTarget: """ Test Class for CapacityThroughputInformationTarget """ @@ -13770,8 +14662,8 @@ def test_capacity_throughput_information_target_serialization(self): # Construct dict forms of any model objects needed in order to build this model. - throughput_information_model = {} # ThroughputInformation - throughput_information_model['blocks'] = 0 + throughput_information_model = {} # ThroughputInformation + throughput_information_model['blocks'] = 1 throughput_information_model['query'] = 0 throughput_information_model['read'] = 0 throughput_information_model['write'] = 0 @@ -13795,7 +14687,8 @@ def test_capacity_throughput_information_target_serialization(self): capacity_throughput_information_target_model_json2 = capacity_throughput_information_target_model.to_dict() assert capacity_throughput_information_target_model_json2 == capacity_throughput_information_target_model_json -class TestModel_Change(): + +class TestModel_Change: """ Test Class for Change """ @@ -13824,7 +14717,8 @@ def test_change_serialization(self): change_model_json2 = change_model.to_dict() assert change_model_json2 == change_model_json -class TestModel_ChangesResult(): + +class TestModel_ChangesResult: """ Test Class for ChangesResult """ @@ -13836,10 +14730,10 @@ def test_changes_result_serialization(self): # Construct dict forms of any model objects needed in order to build this model. - change_model = {} # Change + change_model = {} # Change change_model['rev'] = 'testString' - attachment_model = {} # Attachment + attachment_model = {} # Attachment attachment_model['content_type'] = 'testString' attachment_model['data'] = 'VGhpcyBpcyBhIG1vY2sgYnl0ZSBhcnJheSB2YWx1ZS4=' attachment_model['digest'] = 'testString' @@ -13850,15 +14744,15 @@ def test_changes_result_serialization(self): attachment_model['revpos'] = 1 attachment_model['stub'] = True - revisions_model = {} # Revisions + revisions_model = {} # Revisions revisions_model['ids'] = ['testString'] revisions_model['start'] = 1 - document_revision_status_model = {} # DocumentRevisionStatus + document_revision_status_model = {} # DocumentRevisionStatus document_revision_status_model['rev'] = 'testString' document_revision_status_model['status'] = 'available' - document_model = {} # Document + document_model = {} # Document document_model['_attachments'] = {'key1': attachment_model} document_model['_conflicts'] = ['testString'] document_model['_deleted'] = True @@ -13870,7 +14764,7 @@ def test_changes_result_serialization(self): document_model['_revs_info'] = [document_revision_status_model] document_model['foo'] = 'testString' - changes_result_item_model = {} # ChangesResultItem + changes_result_item_model = {} # ChangesResultItem changes_result_item_model['changes'] = [change_model] changes_result_item_model['deleted'] = True changes_result_item_model['doc'] = document_model @@ -13880,7 +14774,7 @@ def test_changes_result_serialization(self): # Construct a json representation of a ChangesResult model changes_result_model_json = {} changes_result_model_json['last_seq'] = 'testString' - changes_result_model_json['pending'] = 26 + changes_result_model_json['pending'] = 0 changes_result_model_json['results'] = [changes_result_item_model] # Construct a model instance of ChangesResult by calling from_dict on the json representation @@ -13898,7 +14792,8 @@ def test_changes_result_serialization(self): changes_result_model_json2 = changes_result_model.to_dict() assert changes_result_model_json2 == changes_result_model_json -class TestModel_ChangesResultItem(): + +class TestModel_ChangesResultItem: """ Test Class for ChangesResultItem """ @@ -13910,10 +14805,10 @@ def test_changes_result_item_serialization(self): # Construct dict forms of any model objects needed in order to build this model. - change_model = {} # Change + change_model = {} # Change change_model['rev'] = 'testString' - attachment_model = {} # Attachment + attachment_model = {} # Attachment attachment_model['content_type'] = 'testString' attachment_model['data'] = 'VGhpcyBpcyBhIG1vY2sgYnl0ZSBhcnJheSB2YWx1ZS4=' attachment_model['digest'] = 'testString' @@ -13924,15 +14819,15 @@ def test_changes_result_item_serialization(self): attachment_model['revpos'] = 1 attachment_model['stub'] = True - revisions_model = {} # Revisions + revisions_model = {} # Revisions revisions_model['ids'] = ['testString'] revisions_model['start'] = 1 - document_revision_status_model = {} # DocumentRevisionStatus + document_revision_status_model = {} # DocumentRevisionStatus document_revision_status_model['rev'] = 'testString' document_revision_status_model['status'] = 'available' - document_model = {} # Document + document_model = {} # Document document_model['_attachments'] = {'key1': attachment_model} document_model['_conflicts'] = ['testString'] document_model['_deleted'] = True @@ -13967,7 +14862,8 @@ def test_changes_result_item_serialization(self): changes_result_item_model_json2 = changes_result_item_model.to_dict() assert changes_result_item_model_json2 == changes_result_item_model_json -class TestModel_ContentInformationSizes(): + +class TestModel_ContentInformationSizes: """ Test Class for ContentInformationSizes """ @@ -13979,9 +14875,9 @@ def test_content_information_sizes_serialization(self): # Construct a json representation of a ContentInformationSizes model content_information_sizes_model_json = {} - content_information_sizes_model_json['active'] = 26 - content_information_sizes_model_json['external'] = 26 - content_information_sizes_model_json['file'] = 26 + content_information_sizes_model_json['active'] = 0 + content_information_sizes_model_json['external'] = 0 + content_information_sizes_model_json['file'] = 0 # Construct a model instance of ContentInformationSizes by calling from_dict on the json representation content_information_sizes_model = ContentInformationSizes.from_dict(content_information_sizes_model_json) @@ -13998,7 +14894,8 @@ def test_content_information_sizes_serialization(self): content_information_sizes_model_json2 = content_information_sizes_model.to_dict() assert content_information_sizes_model_json2 == content_information_sizes_model_json -class TestModel_CorsInformation(): + +class TestModel_CorsInformation: """ Test Class for CorsInformation """ @@ -14029,7 +14926,43 @@ def test_cors_information_serialization(self): cors_information_model_json2 = cors_information_model.to_dict() assert cors_information_model_json2 == cors_information_model_json -class TestModel_CurrentThroughputInformation(): + +class TestModel_CurrentDatabasesInformation: + """ + Test Class for CurrentDatabasesInformation + """ + + def test_current_databases_information_serialization(self): + """ + Test serialization/deserialization for CurrentDatabasesInformation + """ + + # Construct dict forms of any model objects needed in order to build this model. + + databases_count_information_model = {} # DatabasesCountInformation + databases_count_information_model['total'] = 0 + + # Construct a json representation of a CurrentDatabasesInformation model + current_databases_information_model_json = {} + current_databases_information_model_json['databases'] = databases_count_information_model + + # Construct a model instance of CurrentDatabasesInformation by calling from_dict on the json representation + current_databases_information_model = CurrentDatabasesInformation.from_dict(current_databases_information_model_json) + assert current_databases_information_model != False + + # Construct a model instance of CurrentDatabasesInformation by calling from_dict on the json representation + current_databases_information_model_dict = CurrentDatabasesInformation.from_dict(current_databases_information_model_json).__dict__ + current_databases_information_model2 = CurrentDatabasesInformation(**current_databases_information_model_dict) + + # Verify the model instances are equivalent + assert current_databases_information_model == current_databases_information_model2 + + # Convert model instance back to dict and verify no loss of data + current_databases_information_model_json2 = current_databases_information_model.to_dict() + assert current_databases_information_model_json2 == current_databases_information_model_json + + +class TestModel_CurrentThroughputInformation: """ Test Class for CurrentThroughputInformation """ @@ -14041,7 +14974,7 @@ def test_current_throughput_information_serialization(self): # Construct dict forms of any model objects needed in order to build this model. - current_throughput_information_throughput_model = {} # CurrentThroughputInformationThroughput + current_throughput_information_throughput_model = {} # CurrentThroughputInformationThroughput current_throughput_information_throughput_model['query'] = 0 current_throughput_information_throughput_model['read'] = 0 current_throughput_information_throughput_model['write'] = 0 @@ -14065,7 +14998,8 @@ def test_current_throughput_information_serialization(self): current_throughput_information_model_json2 = current_throughput_information_model.to_dict() assert current_throughput_information_model_json2 == current_throughput_information_model_json -class TestModel_CurrentThroughputInformationThroughput(): + +class TestModel_CurrentThroughputInformationThroughput: """ Test Class for CurrentThroughputInformationThroughput """ @@ -14096,7 +15030,8 @@ def test_current_throughput_information_throughput_serialization(self): current_throughput_information_throughput_model_json2 = current_throughput_information_throughput_model.to_dict() assert current_throughput_information_throughput_model_json2 == current_throughput_information_throughput_model_json -class TestModel_DatabaseInformation(): + +class TestModel_DatabaseInformation: """ Test Class for DatabaseInformation """ @@ -14108,19 +15043,28 @@ def test_database_information_serialization(self): # Construct dict forms of any model objects needed in order to build this model. - database_information_cluster_model = {} # DatabaseInformationCluster - database_information_cluster_model['n'] = 1 - database_information_cluster_model['q'] = 26 + database_information_cluster_model = {} # DatabaseInformationCluster + database_information_cluster_model['n'] = 3 + database_information_cluster_model['q'] = 16 database_information_cluster_model['r'] = 1 database_information_cluster_model['w'] = 1 - database_information_props_model = {} # DatabaseInformationProps + partitioned_indexes_detailed_information_model = {} # PartitionedIndexesDetailedInformation + partitioned_indexes_detailed_information_model['search'] = 0 + partitioned_indexes_detailed_information_model['view'] = 0 + + partitioned_indexes_information_model = {} # PartitionedIndexesInformation + partitioned_indexes_information_model['count'] = 0 + partitioned_indexes_information_model['indexes'] = partitioned_indexes_detailed_information_model + partitioned_indexes_information_model['limit'] = 10 + + database_information_props_model = {} # DatabaseInformationProps database_information_props_model['partitioned'] = True - content_information_sizes_model = {} # ContentInformationSizes - content_information_sizes_model['active'] = 26 - content_information_sizes_model['external'] = 26 - content_information_sizes_model['file'] = 26 + content_information_sizes_model = {} # ContentInformationSizes + content_information_sizes_model['active'] = 0 + content_information_sizes_model['external'] = 0 + content_information_sizes_model['file'] = 0 # Construct a json representation of a DatabaseInformation model database_information_model_json = {} @@ -14129,10 +15073,12 @@ def test_database_information_serialization(self): database_information_model_json['compact_running'] = True database_information_model_json['compacted_seq'] = 'testString' database_information_model_json['db_name'] = 'testString' - database_information_model_json['disk_format_version'] = 26 + database_information_model_json['disk_format_version'] = 1 database_information_model_json['doc_count'] = 0 database_information_model_json['doc_del_count'] = 0 database_information_model_json['engine'] = 'testString' + database_information_model_json['instance_start_time'] = 'testString' + database_information_model_json['partitioned_indexes'] = partitioned_indexes_information_model database_information_model_json['props'] = database_information_props_model database_information_model_json['sizes'] = content_information_sizes_model database_information_model_json['update_seq'] = 'testString' @@ -14153,7 +15099,8 @@ def test_database_information_serialization(self): database_information_model_json2 = database_information_model.to_dict() assert database_information_model_json2 == database_information_model_json -class TestModel_DatabaseInformationCluster(): + +class TestModel_DatabaseInformationCluster: """ Test Class for DatabaseInformationCluster """ @@ -14165,8 +15112,8 @@ def test_database_information_cluster_serialization(self): # Construct a json representation of a DatabaseInformationCluster model database_information_cluster_model_json = {} - database_information_cluster_model_json['n'] = 1 - database_information_cluster_model_json['q'] = 26 + database_information_cluster_model_json['n'] = 3 + database_information_cluster_model_json['q'] = 16 database_information_cluster_model_json['r'] = 1 database_information_cluster_model_json['w'] = 1 @@ -14185,7 +15132,8 @@ def test_database_information_cluster_serialization(self): database_information_cluster_model_json2 = database_information_cluster_model.to_dict() assert database_information_cluster_model_json2 == database_information_cluster_model_json -class TestModel_DatabaseInformationProps(): + +class TestModel_DatabaseInformationProps: """ Test Class for DatabaseInformationProps """ @@ -14214,7 +15162,38 @@ def test_database_information_props_serialization(self): database_information_props_model_json2 = database_information_props_model.to_dict() assert database_information_props_model_json2 == database_information_props_model_json -class TestModel_DbEvent(): + +class TestModel_DatabasesCountInformation: + """ + Test Class for DatabasesCountInformation + """ + + def test_databases_count_information_serialization(self): + """ + Test serialization/deserialization for DatabasesCountInformation + """ + + # Construct a json representation of a DatabasesCountInformation model + databases_count_information_model_json = {} + databases_count_information_model_json['total'] = 0 + + # Construct a model instance of DatabasesCountInformation by calling from_dict on the json representation + databases_count_information_model = DatabasesCountInformation.from_dict(databases_count_information_model_json) + assert databases_count_information_model != False + + # Construct a model instance of DatabasesCountInformation by calling from_dict on the json representation + databases_count_information_model_dict = DatabasesCountInformation.from_dict(databases_count_information_model_json).__dict__ + databases_count_information_model2 = DatabasesCountInformation(**databases_count_information_model_dict) + + # Verify the model instances are equivalent + assert databases_count_information_model == databases_count_information_model2 + + # Convert model instance back to dict and verify no loss of data + databases_count_information_model_json2 = databases_count_information_model.to_dict() + assert databases_count_information_model_json2 == databases_count_information_model_json + + +class TestModel_DbEvent: """ Test Class for DbEvent """ @@ -14245,7 +15224,8 @@ def test_db_event_serialization(self): db_event_model_json2 = db_event_model.to_dict() assert db_event_model_json2 == db_event_model_json -class TestModel_DbUpdates(): + +class TestModel_DbUpdates: """ Test Class for DbUpdates """ @@ -14257,7 +15237,7 @@ def test_db_updates_serialization(self): # Construct dict forms of any model objects needed in order to build this model. - db_event_model = {} # DbEvent + db_event_model = {} # DbEvent db_event_model['db_name'] = 'testString' db_event_model['seq'] = 'testString' db_event_model['type'] = 'created' @@ -14282,7 +15262,8 @@ def test_db_updates_serialization(self): db_updates_model_json2 = db_updates_model.to_dict() assert db_updates_model_json2 == db_updates_model_json -class TestModel_DbsInfoResult(): + +class TestModel_DbsInfoResult: """ Test Class for DbsInfoResult """ @@ -14294,30 +15275,41 @@ def test_dbs_info_result_serialization(self): # Construct dict forms of any model objects needed in order to build this model. - database_information_cluster_model = {} # DatabaseInformationCluster - database_information_cluster_model['n'] = 1 - database_information_cluster_model['q'] = 26 + database_information_cluster_model = {} # DatabaseInformationCluster + database_information_cluster_model['n'] = 3 + database_information_cluster_model['q'] = 16 database_information_cluster_model['r'] = 1 database_information_cluster_model['w'] = 1 - database_information_props_model = {} # DatabaseInformationProps + partitioned_indexes_detailed_information_model = {} # PartitionedIndexesDetailedInformation + partitioned_indexes_detailed_information_model['search'] = 0 + partitioned_indexes_detailed_information_model['view'] = 0 + + partitioned_indexes_information_model = {} # PartitionedIndexesInformation + partitioned_indexes_information_model['count'] = 0 + partitioned_indexes_information_model['indexes'] = partitioned_indexes_detailed_information_model + partitioned_indexes_information_model['limit'] = 10 + + database_information_props_model = {} # DatabaseInformationProps database_information_props_model['partitioned'] = True - content_information_sizes_model = {} # ContentInformationSizes - content_information_sizes_model['active'] = 26 - content_information_sizes_model['external'] = 26 - content_information_sizes_model['file'] = 26 + content_information_sizes_model = {} # ContentInformationSizes + content_information_sizes_model['active'] = 0 + content_information_sizes_model['external'] = 0 + content_information_sizes_model['file'] = 0 - database_information_model = {} # DatabaseInformation + database_information_model = {} # DatabaseInformation database_information_model['cluster'] = database_information_cluster_model database_information_model['committed_update_seq'] = 'testString' database_information_model['compact_running'] = True database_information_model['compacted_seq'] = 'testString' database_information_model['db_name'] = 'testString' - database_information_model['disk_format_version'] = 26 + database_information_model['disk_format_version'] = 1 database_information_model['doc_count'] = 0 database_information_model['doc_del_count'] = 0 database_information_model['engine'] = 'testString' + database_information_model['instance_start_time'] = 'testString' + database_information_model['partitioned_indexes'] = partitioned_indexes_information_model database_information_model['props'] = database_information_props_model database_information_model['sizes'] = content_information_sizes_model database_information_model['update_seq'] = 'testString' @@ -14344,7 +15336,8 @@ def test_dbs_info_result_serialization(self): dbs_info_result_model_json2 = dbs_info_result_model.to_dict() assert dbs_info_result_model_json2 == dbs_info_result_model_json -class TestModel_DesignDocument(): + +class TestModel_DesignDocument: """ Test Class for DesignDocument """ @@ -14356,7 +15349,7 @@ def test_design_document_serialization(self): # Construct dict forms of any model objects needed in order to build this model. - attachment_model = {} # Attachment + attachment_model = {} # Attachment attachment_model['content_type'] = 'testString' attachment_model['data'] = 'VGhpcyBpcyBhIG1vY2sgYnl0ZSBhcnJheSB2YWx1ZS4=' attachment_model['digest'] = 'testString' @@ -14367,37 +15360,35 @@ def test_design_document_serialization(self): attachment_model['revpos'] = 1 attachment_model['stub'] = True - revisions_model = {} # Revisions + revisions_model = {} # Revisions revisions_model['ids'] = ['testString'] revisions_model['start'] = 1 - document_revision_status_model = {} # DocumentRevisionStatus + document_revision_status_model = {} # DocumentRevisionStatus document_revision_status_model['rev'] = 'testString' document_revision_status_model['status'] = 'available' - analyzer_model = {} # Analyzer + analyzer_model = {} # Analyzer analyzer_model['name'] = 'classic' analyzer_model['stopwords'] = ['testString'] - analyzer_configuration_model = {} # AnalyzerConfiguration + analyzer_configuration_model = {} # AnalyzerConfiguration analyzer_configuration_model['name'] = 'classic' analyzer_configuration_model['stopwords'] = ['testString'] + analyzer_configuration_model['default'] = analyzer_model analyzer_configuration_model['fields'] = {'key1': analyzer_model} - search_index_definition_model = {} # SearchIndexDefinition + search_index_definition_model = {} # SearchIndexDefinition search_index_definition_model['analyzer'] = analyzer_configuration_model search_index_definition_model['index'] = 'testString' - design_document_options_model = {} # DesignDocumentOptions + design_document_options_model = {} # DesignDocumentOptions design_document_options_model['partitioned'] = True - design_document_views_map_reduce_model = {} # DesignDocumentViewsMapReduce + design_document_views_map_reduce_model = {} # DesignDocumentViewsMapReduce design_document_views_map_reduce_model['map'] = 'testString' design_document_views_map_reduce_model['reduce'] = 'testString' - geo_index_definition_model = {} # GeoIndexDefinition - geo_index_definition_model['index'] = 'testString' - # Construct a json representation of a DesignDocument model design_document_model_json = {} design_document_model_json['_attachments'] = {'key1': attachment_model} @@ -14416,7 +15407,6 @@ def test_design_document_serialization(self): design_document_model_json['options'] = design_document_options_model design_document_model_json['validate_doc_update'] = 'testString' design_document_model_json['views'] = {'key1': design_document_views_map_reduce_model} - design_document_model_json['st_indexes'] = {'key1': geo_index_definition_model} design_document_model_json['foo'] = 'testString' # Construct a model instance of DesignDocument by calling from_dict on the json representation @@ -14442,9 +15432,10 @@ def test_design_document_serialization(self): expected_dict = {'foo': 'testString'} design_document_model.set_properties(expected_dict) actual_dict = design_document_model.get_properties() - assert actual_dict == expected_dict + assert actual_dict.keys() == expected_dict.keys() + -class TestModel_DesignDocumentInformation(): +class TestModel_DesignDocumentInformation: """ Test Class for DesignDocumentInformation """ @@ -14456,18 +15447,24 @@ def test_design_document_information_serialization(self): # Construct dict forms of any model objects needed in order to build this model. - content_information_sizes_model = {} # ContentInformationSizes - content_information_sizes_model['active'] = 26 - content_information_sizes_model['external'] = 26 - content_information_sizes_model['file'] = 26 + content_information_sizes_model = {} # ContentInformationSizes + content_information_sizes_model['active'] = 0 + content_information_sizes_model['external'] = 0 + content_information_sizes_model['file'] = 0 - design_document_view_index_model = {} # DesignDocumentViewIndex + updates_pending_model = {} # UpdatesPending + updates_pending_model['minimum'] = 0 + updates_pending_model['preferred'] = 0 + updates_pending_model['total'] = 0 + + design_document_view_index_model = {} # DesignDocumentViewIndex design_document_view_index_model['collator_versions'] = ['testString'] design_document_view_index_model['compact_running'] = True design_document_view_index_model['language'] = 'testString' design_document_view_index_model['signature'] = 'testString' design_document_view_index_model['sizes'] = content_information_sizes_model design_document_view_index_model['updater_running'] = True + design_document_view_index_model['updates_pending'] = updates_pending_model design_document_view_index_model['waiting_clients'] = 0 design_document_view_index_model['waiting_commit'] = True @@ -14491,7 +15488,8 @@ def test_design_document_information_serialization(self): design_document_information_model_json2 = design_document_information_model.to_dict() assert design_document_information_model_json2 == design_document_information_model_json -class TestModel_DesignDocumentOptions(): + +class TestModel_DesignDocumentOptions: """ Test Class for DesignDocumentOptions """ @@ -14520,7 +15518,8 @@ def test_design_document_options_serialization(self): design_document_options_model_json2 = design_document_options_model.to_dict() assert design_document_options_model_json2 == design_document_options_model_json -class TestModel_DesignDocumentViewIndex(): + +class TestModel_DesignDocumentViewIndex: """ Test Class for DesignDocumentViewIndex """ @@ -14532,10 +15531,15 @@ def test_design_document_view_index_serialization(self): # Construct dict forms of any model objects needed in order to build this model. - content_information_sizes_model = {} # ContentInformationSizes - content_information_sizes_model['active'] = 26 - content_information_sizes_model['external'] = 26 - content_information_sizes_model['file'] = 26 + content_information_sizes_model = {} # ContentInformationSizes + content_information_sizes_model['active'] = 0 + content_information_sizes_model['external'] = 0 + content_information_sizes_model['file'] = 0 + + updates_pending_model = {} # UpdatesPending + updates_pending_model['minimum'] = 0 + updates_pending_model['preferred'] = 0 + updates_pending_model['total'] = 0 # Construct a json representation of a DesignDocumentViewIndex model design_document_view_index_model_json = {} @@ -14545,6 +15549,7 @@ def test_design_document_view_index_serialization(self): design_document_view_index_model_json['signature'] = 'testString' design_document_view_index_model_json['sizes'] = content_information_sizes_model design_document_view_index_model_json['updater_running'] = True + design_document_view_index_model_json['updates_pending'] = updates_pending_model design_document_view_index_model_json['waiting_clients'] = 0 design_document_view_index_model_json['waiting_commit'] = True @@ -14563,7 +15568,8 @@ def test_design_document_view_index_serialization(self): design_document_view_index_model_json2 = design_document_view_index_model.to_dict() assert design_document_view_index_model_json2 == design_document_view_index_model_json -class TestModel_DesignDocumentViewsMapReduce(): + +class TestModel_DesignDocumentViewsMapReduce: """ Test Class for DesignDocumentViewsMapReduce """ @@ -14593,7 +15599,8 @@ def test_design_document_views_map_reduce_serialization(self): design_document_views_map_reduce_model_json2 = design_document_views_map_reduce_model.to_dict() assert design_document_views_map_reduce_model_json2 == design_document_views_map_reduce_model_json -class TestModel_DocsResultRow(): + +class TestModel_DocsResultRow: """ Test Class for DocsResultRow """ @@ -14605,7 +15612,7 @@ def test_docs_result_row_serialization(self): # Construct dict forms of any model objects needed in order to build this model. - attachment_model = {} # Attachment + attachment_model = {} # Attachment attachment_model['content_type'] = 'testString' attachment_model['data'] = 'VGhpcyBpcyBhIG1vY2sgYnl0ZSBhcnJheSB2YWx1ZS4=' attachment_model['digest'] = 'testString' @@ -14616,15 +15623,15 @@ def test_docs_result_row_serialization(self): attachment_model['revpos'] = 1 attachment_model['stub'] = True - revisions_model = {} # Revisions + revisions_model = {} # Revisions revisions_model['ids'] = ['testString'] revisions_model['start'] = 1 - document_revision_status_model = {} # DocumentRevisionStatus + document_revision_status_model = {} # DocumentRevisionStatus document_revision_status_model['rev'] = 'testString' document_revision_status_model['status'] = 'available' - document_model = {} # Document + document_model = {} # Document document_model['_attachments'] = {'key1': attachment_model} document_model['_conflicts'] = ['testString'] document_model['_deleted'] = True @@ -14636,7 +15643,8 @@ def test_docs_result_row_serialization(self): document_model['_revs_info'] = [document_revision_status_model] document_model['foo'] = 'testString' - docs_result_row_value_model = {} # DocsResultRowValue + docs_result_row_value_model = {} # DocsResultRowValue + docs_result_row_value_model['deleted'] = True docs_result_row_value_model['rev'] = 'testString' # Construct a json representation of a DocsResultRow model @@ -14644,6 +15652,7 @@ def test_docs_result_row_serialization(self): docs_result_row_model_json['caused_by'] = 'testString' docs_result_row_model_json['error'] = 'testString' docs_result_row_model_json['reason'] = 'testString' + docs_result_row_model_json['ref'] = 0 docs_result_row_model_json['doc'] = document_model docs_result_row_model_json['id'] = 'testString' docs_result_row_model_json['key'] = 'testString' @@ -14664,7 +15673,8 @@ def test_docs_result_row_serialization(self): docs_result_row_model_json2 = docs_result_row_model.to_dict() assert docs_result_row_model_json2 == docs_result_row_model_json -class TestModel_DocsResultRowValue(): + +class TestModel_DocsResultRowValue: """ Test Class for DocsResultRowValue """ @@ -14676,6 +15686,7 @@ def test_docs_result_row_value_serialization(self): # Construct a json representation of a DocsResultRowValue model docs_result_row_value_model_json = {} + docs_result_row_value_model_json['deleted'] = True docs_result_row_value_model_json['rev'] = 'testString' # Construct a model instance of DocsResultRowValue by calling from_dict on the json representation @@ -14693,7 +15704,8 @@ def test_docs_result_row_value_serialization(self): docs_result_row_value_model_json2 = docs_result_row_value_model.to_dict() assert docs_result_row_value_model_json2 == docs_result_row_value_model_json -class TestModel_Document(): + +class TestModel_Document: """ Test Class for Document """ @@ -14705,7 +15717,7 @@ def test_document_serialization(self): # Construct dict forms of any model objects needed in order to build this model. - attachment_model = {} # Attachment + attachment_model = {} # Attachment attachment_model['content_type'] = 'testString' attachment_model['data'] = 'VGhpcyBpcyBhIG1vY2sgYnl0ZSBhcnJheSB2YWx1ZS4=' attachment_model['digest'] = 'testString' @@ -14716,11 +15728,11 @@ def test_document_serialization(self): attachment_model['revpos'] = 1 attachment_model['stub'] = True - revisions_model = {} # Revisions + revisions_model = {} # Revisions revisions_model['ids'] = ['testString'] revisions_model['start'] = 1 - document_revision_status_model = {} # DocumentRevisionStatus + document_revision_status_model = {} # DocumentRevisionStatus document_revision_status_model['rev'] = 'testString' document_revision_status_model['status'] = 'available' @@ -14760,9 +15772,10 @@ def test_document_serialization(self): expected_dict = {'foo': 'testString'} document_model.set_properties(expected_dict) actual_dict = document_model.get_properties() - assert actual_dict == expected_dict + assert actual_dict.keys() == expected_dict.keys() -class TestModel_DocumentResult(): + +class TestModel_DocumentResult: """ Test Class for DocumentResult """ @@ -14780,6 +15793,7 @@ def test_document_result_serialization(self): document_result_model_json['caused_by'] = 'testString' document_result_model_json['error'] = 'testString' document_result_model_json['reason'] = 'testString' + document_result_model_json['ref'] = 0 # Construct a model instance of DocumentResult by calling from_dict on the json representation document_result_model = DocumentResult.from_dict(document_result_model_json) @@ -14796,7 +15810,8 @@ def test_document_result_serialization(self): document_result_model_json2 = document_result_model.to_dict() assert document_result_model_json2 == document_result_model_json -class TestModel_DocumentRevisionStatus(): + +class TestModel_DocumentRevisionStatus: """ Test Class for DocumentRevisionStatus """ @@ -14826,7 +15841,8 @@ def test_document_revision_status_serialization(self): document_revision_status_model_json2 = document_revision_status_model.to_dict() assert document_revision_status_model_json2 == document_revision_status_model_json -class TestModel_DocumentShardInfo(): + +class TestModel_DocumentShardInfo: """ Test Class for DocumentShardInfo """ @@ -14856,7 +15872,8 @@ def test_document_shard_info_serialization(self): document_shard_info_model_json2 = document_shard_info_model.to_dict() assert document_shard_info_model_json2 == document_shard_info_model_json -class TestModel_ExecutionStats(): + +class TestModel_ExecutionStats: """ Test Class for ExecutionStats """ @@ -14889,7 +15906,8 @@ def test_execution_stats_serialization(self): execution_stats_model_json2 = execution_stats_model.to_dict() assert execution_stats_model_json2 == execution_stats_model_json -class TestModel_ExplainResult(): + +class TestModel_ExplainResult: """ Test Class for ExplainResult """ @@ -14901,45 +15919,91 @@ def test_explain_result_serialization(self): # Construct dict forms of any model objects needed in order to build this model. - analyzer_model = {} # Analyzer + analyzer_model = {} # Analyzer analyzer_model['name'] = 'classic' analyzer_model['stopwords'] = ['testString'] - index_text_operator_default_field_model = {} # IndexTextOperatorDefaultField + index_text_operator_default_field_model = {} # IndexTextOperatorDefaultField index_text_operator_default_field_model['analyzer'] = analyzer_model index_text_operator_default_field_model['enabled'] = True - index_field_model = {} # IndexField + index_field_model = {} # IndexField index_field_model['name'] = 'testString' index_field_model['type'] = 'boolean' index_field_model['foo'] = 'asc' - index_definition_model = {} # IndexDefinition + index_definition_model = {} # IndexDefinition index_definition_model['default_analyzer'] = analyzer_model index_definition_model['default_field'] = index_text_operator_default_field_model index_definition_model['fields'] = [index_field_model] index_definition_model['index_array_lengths'] = True - index_definition_model['partial_filter_selector'] = {'key1': 'testString'} + index_definition_model['partial_filter_selector'] = {'anyKey': 'anyValue'} - index_information_model = {} # IndexInformation + index_information_model = {} # IndexInformation index_information_model['ddoc'] = 'testString' index_information_model['def'] = index_definition_model index_information_model['name'] = 'testString' + index_information_model['partitioned'] = True index_information_model['type'] = 'json' - explain_result_range_model = {} # ExplainResultRange - explain_result_range_model['end_key'] = ['testString'] - explain_result_range_model['start_key'] = ['testString'] + index_analysis_exclusion_reason_model = {} # IndexAnalysisExclusionReason + index_analysis_exclusion_reason_model['name'] = 'alphabetically_comes_after' + + index_analysis_model = {} # IndexAnalysis + index_analysis_model['covering'] = True + index_analysis_model['ranking'] = 1 + index_analysis_model['reasons'] = [index_analysis_exclusion_reason_model] + index_analysis_model['usable'] = True + + index_candidate_model = {} # IndexCandidate + index_candidate_model['analysis'] = index_analysis_model + index_candidate_model['index'] = index_information_model + + explain_result_mr_args_model = {} # ExplainResultMrArgs + explain_result_mr_args_model['conflicts'] = 'testString' + explain_result_mr_args_model['direction'] = 'fwd' + explain_result_mr_args_model['end_key'] = 'testString' + explain_result_mr_args_model['include_docs'] = True + explain_result_mr_args_model['partition'] = 'testString' + explain_result_mr_args_model['reduce'] = True + explain_result_mr_args_model['stable'] = True + explain_result_mr_args_model['start_key'] = 'testString' + explain_result_mr_args_model['update'] = 'testString' + explain_result_mr_args_model['view_type'] = 'map' + + explain_result_opts_model = {} # ExplainResultOpts + explain_result_opts_model['bookmark'] = 'testString' + explain_result_opts_model['conflicts'] = False + explain_result_opts_model['execution_stats'] = False + explain_result_opts_model['fields'] = ['testString'] + explain_result_opts_model['limit'] = 25 + explain_result_opts_model['partition'] = 'testString' + explain_result_opts_model['r'] = 1 + explain_result_opts_model['skip'] = 0 + explain_result_opts_model['sort'] = 'testString' + explain_result_opts_model['stable'] = False + explain_result_opts_model['stale'] = False + explain_result_opts_model['update'] = True + explain_result_opts_model['use_index'] = ['testString'] + + selector_hint_model = {} # SelectorHint + selector_hint_model['indexable_fields'] = ['testString'] + selector_hint_model['type'] = 'json' + selector_hint_model['unindexable_fields'] = ['testString'] # Construct a json representation of a ExplainResult model explain_result_model_json = {} + explain_result_model_json['covering'] = True explain_result_model_json['dbname'] = 'testString' explain_result_model_json['fields'] = ['testString'] explain_result_model_json['index'] = index_information_model - explain_result_model_json['limit'] = 0 - explain_result_model_json['opts'] = {'key1': 'testString'} - explain_result_model_json['range'] = explain_result_range_model - explain_result_model_json['selector'] = {'key1': 'testString'} + explain_result_model_json['index_candidates'] = [index_candidate_model] + explain_result_model_json['limit'] = 25 + explain_result_model_json['mrargs'] = explain_result_mr_args_model + explain_result_model_json['opts'] = explain_result_opts_model + explain_result_model_json['partitioned'] = 'testString' + explain_result_model_json['selector'] = {'anyKey': 'anyValue'} + explain_result_model_json['selector_hints'] = [selector_hint_model] explain_result_model_json['skip'] = 0 # Construct a model instance of ExplainResult by calling from_dict on the json representation @@ -14957,37 +16021,89 @@ def test_explain_result_serialization(self): explain_result_model_json2 = explain_result_model.to_dict() assert explain_result_model_json2 == explain_result_model_json -class TestModel_ExplainResultRange(): + +class TestModel_ExplainResultMrArgs: + """ + Test Class for ExplainResultMrArgs + """ + + def test_explain_result_mr_args_serialization(self): + """ + Test serialization/deserialization for ExplainResultMrArgs + """ + + # Construct a json representation of a ExplainResultMrArgs model + explain_result_mr_args_model_json = {} + explain_result_mr_args_model_json['conflicts'] = 'testString' + explain_result_mr_args_model_json['direction'] = 'fwd' + explain_result_mr_args_model_json['end_key'] = 'testString' + explain_result_mr_args_model_json['include_docs'] = True + explain_result_mr_args_model_json['partition'] = 'testString' + explain_result_mr_args_model_json['reduce'] = True + explain_result_mr_args_model_json['stable'] = True + explain_result_mr_args_model_json['start_key'] = 'testString' + explain_result_mr_args_model_json['update'] = 'testString' + explain_result_mr_args_model_json['view_type'] = 'map' + + # Construct a model instance of ExplainResultMrArgs by calling from_dict on the json representation + explain_result_mr_args_model = ExplainResultMrArgs.from_dict(explain_result_mr_args_model_json) + assert explain_result_mr_args_model != False + + # Construct a model instance of ExplainResultMrArgs by calling from_dict on the json representation + explain_result_mr_args_model_dict = ExplainResultMrArgs.from_dict(explain_result_mr_args_model_json).__dict__ + explain_result_mr_args_model2 = ExplainResultMrArgs(**explain_result_mr_args_model_dict) + + # Verify the model instances are equivalent + assert explain_result_mr_args_model == explain_result_mr_args_model2 + + # Convert model instance back to dict and verify no loss of data + explain_result_mr_args_model_json2 = explain_result_mr_args_model.to_dict() + assert explain_result_mr_args_model_json2 == explain_result_mr_args_model_json + + +class TestModel_ExplainResultOpts: """ - Test Class for ExplainResultRange + Test Class for ExplainResultOpts """ - def test_explain_result_range_serialization(self): + def test_explain_result_opts_serialization(self): """ - Test serialization/deserialization for ExplainResultRange + Test serialization/deserialization for ExplainResultOpts """ - # Construct a json representation of a ExplainResultRange model - explain_result_range_model_json = {} - explain_result_range_model_json['end_key'] = ['testString'] - explain_result_range_model_json['start_key'] = ['testString'] + # Construct a json representation of a ExplainResultOpts model + explain_result_opts_model_json = {} + explain_result_opts_model_json['bookmark'] = 'testString' + explain_result_opts_model_json['conflicts'] = False + explain_result_opts_model_json['execution_stats'] = False + explain_result_opts_model_json['fields'] = ['testString'] + explain_result_opts_model_json['limit'] = 25 + explain_result_opts_model_json['partition'] = 'testString' + explain_result_opts_model_json['r'] = 1 + explain_result_opts_model_json['skip'] = 0 + explain_result_opts_model_json['sort'] = 'testString' + explain_result_opts_model_json['stable'] = False + explain_result_opts_model_json['stale'] = False + explain_result_opts_model_json['update'] = True + explain_result_opts_model_json['use_index'] = ['testString'] - # Construct a model instance of ExplainResultRange by calling from_dict on the json representation - explain_result_range_model = ExplainResultRange.from_dict(explain_result_range_model_json) - assert explain_result_range_model != False + # Construct a model instance of ExplainResultOpts by calling from_dict on the json representation + explain_result_opts_model = ExplainResultOpts.from_dict(explain_result_opts_model_json) + assert explain_result_opts_model != False - # Construct a model instance of ExplainResultRange by calling from_dict on the json representation - explain_result_range_model_dict = ExplainResultRange.from_dict(explain_result_range_model_json).__dict__ - explain_result_range_model2 = ExplainResultRange(**explain_result_range_model_dict) + # Construct a model instance of ExplainResultOpts by calling from_dict on the json representation + explain_result_opts_model_dict = ExplainResultOpts.from_dict(explain_result_opts_model_json).__dict__ + explain_result_opts_model2 = ExplainResultOpts(**explain_result_opts_model_dict) # Verify the model instances are equivalent - assert explain_result_range_model == explain_result_range_model2 + assert explain_result_opts_model == explain_result_opts_model2 # Convert model instance back to dict and verify no loss of data - explain_result_range_model_json2 = explain_result_range_model.to_dict() - assert explain_result_range_model_json2 == explain_result_range_model_json + explain_result_opts_model_json2 = explain_result_opts_model.to_dict() + assert explain_result_opts_model_json2 == explain_result_opts_model_json -class TestModel_FindResult(): + +class TestModel_FindResult: """ Test Class for FindResult """ @@ -14999,7 +16115,7 @@ def test_find_result_serialization(self): # Construct dict forms of any model objects needed in order to build this model. - attachment_model = {} # Attachment + attachment_model = {} # Attachment attachment_model['content_type'] = 'testString' attachment_model['data'] = 'VGhpcyBpcyBhIG1vY2sgYnl0ZSBhcnJheSB2YWx1ZS4=' attachment_model['digest'] = 'testString' @@ -15010,15 +16126,15 @@ def test_find_result_serialization(self): attachment_model['revpos'] = 1 attachment_model['stub'] = True - revisions_model = {} # Revisions + revisions_model = {} # Revisions revisions_model['ids'] = ['testString'] revisions_model['start'] = 1 - document_revision_status_model = {} # DocumentRevisionStatus + document_revision_status_model = {} # DocumentRevisionStatus document_revision_status_model['rev'] = 'testString' document_revision_status_model['status'] = 'available' - document_model = {} # Document + document_model = {} # Document document_model['_attachments'] = {'key1': attachment_model} document_model['_conflicts'] = ['testString'] document_model['_deleted'] = True @@ -15030,7 +16146,7 @@ def test_find_result_serialization(self): document_model['_revs_info'] = [document_revision_status_model] document_model['foo'] = 'testString' - execution_stats_model = {} # ExecutionStats + execution_stats_model = {} # ExecutionStats execution_stats_model['execution_time_ms'] = 72.5 execution_stats_model['results_returned'] = 0 execution_stats_model['total_docs_examined'] = 0 @@ -15057,314 +16173,147 @@ def test_find_result_serialization(self): # Convert model instance back to dict and verify no loss of data find_result_model_json2 = find_result_model.to_dict() - assert find_result_model_json2 == find_result_model_json - -class TestModel_GeoIndexDefinition(): - """ - Test Class for GeoIndexDefinition - """ - - def test_geo_index_definition_serialization(self): - """ - Test serialization/deserialization for GeoIndexDefinition - """ - - # Construct a json representation of a GeoIndexDefinition model - geo_index_definition_model_json = {} - geo_index_definition_model_json['index'] = 'testString' - - # Construct a model instance of GeoIndexDefinition by calling from_dict on the json representation - geo_index_definition_model = GeoIndexDefinition.from_dict(geo_index_definition_model_json) - assert geo_index_definition_model != False - - # Construct a model instance of GeoIndexDefinition by calling from_dict on the json representation - geo_index_definition_model_dict = GeoIndexDefinition.from_dict(geo_index_definition_model_json).__dict__ - geo_index_definition_model2 = GeoIndexDefinition(**geo_index_definition_model_dict) - - # Verify the model instances are equivalent - assert geo_index_definition_model == geo_index_definition_model2 - - # Convert model instance back to dict and verify no loss of data - geo_index_definition_model_json2 = geo_index_definition_model.to_dict() - assert geo_index_definition_model_json2 == geo_index_definition_model_json - -class TestModel_GeoIndexInformation(): - """ - Test Class for GeoIndexInformation - """ - - def test_geo_index_information_serialization(self): - """ - Test serialization/deserialization for GeoIndexInformation - """ - - # Construct dict forms of any model objects needed in order to build this model. - - geo_index_stats_model = {} # GeoIndexStats - geo_index_stats_model['data_size'] = 0 - geo_index_stats_model['disk_size'] = 0 - geo_index_stats_model['doc_count'] = 0 - - # Construct a json representation of a GeoIndexInformation model - geo_index_information_model_json = {} - geo_index_information_model_json['geo_index'] = geo_index_stats_model - geo_index_information_model_json['name'] = 'testString' - - # Construct a model instance of GeoIndexInformation by calling from_dict on the json representation - geo_index_information_model = GeoIndexInformation.from_dict(geo_index_information_model_json) - assert geo_index_information_model != False - - # Construct a model instance of GeoIndexInformation by calling from_dict on the json representation - geo_index_information_model_dict = GeoIndexInformation.from_dict(geo_index_information_model_json).__dict__ - geo_index_information_model2 = GeoIndexInformation(**geo_index_information_model_dict) - - # Verify the model instances are equivalent - assert geo_index_information_model == geo_index_information_model2 - - # Convert model instance back to dict and verify no loss of data - geo_index_information_model_json2 = geo_index_information_model.to_dict() - assert geo_index_information_model_json2 == geo_index_information_model_json - -class TestModel_GeoIndexStats(): - """ - Test Class for GeoIndexStats - """ - - def test_geo_index_stats_serialization(self): - """ - Test serialization/deserialization for GeoIndexStats - """ - - # Construct a json representation of a GeoIndexStats model - geo_index_stats_model_json = {} - geo_index_stats_model_json['data_size'] = 0 - geo_index_stats_model_json['disk_size'] = 0 - geo_index_stats_model_json['doc_count'] = 0 - - # Construct a model instance of GeoIndexStats by calling from_dict on the json representation - geo_index_stats_model = GeoIndexStats.from_dict(geo_index_stats_model_json) - assert geo_index_stats_model != False - - # Construct a model instance of GeoIndexStats by calling from_dict on the json representation - geo_index_stats_model_dict = GeoIndexStats.from_dict(geo_index_stats_model_json).__dict__ - geo_index_stats_model2 = GeoIndexStats(**geo_index_stats_model_dict) - - # Verify the model instances are equivalent - assert geo_index_stats_model == geo_index_stats_model2 + assert find_result_model_json2 == find_result_model_json - # Convert model instance back to dict and verify no loss of data - geo_index_stats_model_json2 = geo_index_stats_model.to_dict() - assert geo_index_stats_model_json2 == geo_index_stats_model_json -class TestModel_GeoJsonFeature(): +class TestModel_IndexAnalysis: """ - Test Class for GeoJsonFeature + Test Class for IndexAnalysis """ - def test_geo_json_feature_serialization(self): + def test_index_analysis_serialization(self): """ - Test serialization/deserialization for GeoJsonFeature + Test serialization/deserialization for IndexAnalysis """ # Construct dict forms of any model objects needed in order to build this model. - geo_json_geometry_object_model = {} # GeoJsonGeometry - geo_json_geometry_object_model['type'] = 'Point' - geo_json_geometry_object_model['coordinates'] = ['testString'] + index_analysis_exclusion_reason_model = {} # IndexAnalysisExclusionReason + index_analysis_exclusion_reason_model['name'] = 'alphabetically_comes_after' - # Construct a json representation of a GeoJsonFeature model - geo_json_feature_model_json = {} - geo_json_feature_model_json['_id'] = 'testString' - geo_json_feature_model_json['_rev'] = 'testString' - geo_json_feature_model_json['bbox'] = [72.5] - geo_json_feature_model_json['geometry'] = geo_json_geometry_object_model - geo_json_feature_model_json['properties'] = {'key1': 'testString'} - geo_json_feature_model_json['type'] = 'Feature' - geo_json_feature_model_json['foo'] = 'testString' + # Construct a json representation of a IndexAnalysis model + index_analysis_model_json = {} + index_analysis_model_json['covering'] = True + index_analysis_model_json['ranking'] = 1 + index_analysis_model_json['reasons'] = [index_analysis_exclusion_reason_model] + index_analysis_model_json['usable'] = True - # Construct a model instance of GeoJsonFeature by calling from_dict on the json representation - geo_json_feature_model = GeoJsonFeature.from_dict(geo_json_feature_model_json) - assert geo_json_feature_model != False + # Construct a model instance of IndexAnalysis by calling from_dict on the json representation + index_analysis_model = IndexAnalysis.from_dict(index_analysis_model_json) + assert index_analysis_model != False - # Construct a model instance of GeoJsonFeature by calling from_dict on the json representation - geo_json_feature_model_dict = GeoJsonFeature.from_dict(geo_json_feature_model_json).__dict__ - geo_json_feature_model2 = GeoJsonFeature(**geo_json_feature_model_dict) + # Construct a model instance of IndexAnalysis by calling from_dict on the json representation + index_analysis_model_dict = IndexAnalysis.from_dict(index_analysis_model_json).__dict__ + index_analysis_model2 = IndexAnalysis(**index_analysis_model_dict) # Verify the model instances are equivalent - assert geo_json_feature_model == geo_json_feature_model2 + assert index_analysis_model == index_analysis_model2 # Convert model instance back to dict and verify no loss of data - geo_json_feature_model_json2 = geo_json_feature_model.to_dict() - assert geo_json_feature_model_json2 == geo_json_feature_model_json - - # Test get_properties and set_properties methods. - geo_json_feature_model.set_properties({}) - actual_dict = geo_json_feature_model.get_properties() - assert actual_dict == {} + index_analysis_model_json2 = index_analysis_model.to_dict() + assert index_analysis_model_json2 == index_analysis_model_json - expected_dict = {'foo': 'testString'} - geo_json_feature_model.set_properties(expected_dict) - actual_dict = geo_json_feature_model.get_properties() - assert actual_dict == expected_dict -class TestModel_GeoResult(): +class TestModel_IndexAnalysisExclusionReason: """ - Test Class for GeoResult + Test Class for IndexAnalysisExclusionReason """ - def test_geo_result_serialization(self): + def test_index_analysis_exclusion_reason_serialization(self): """ - Test serialization/deserialization for GeoResult + Test serialization/deserialization for IndexAnalysisExclusionReason """ - # Construct dict forms of any model objects needed in order to build this model. - - geo_json_geometry_object_model = {} # GeoJsonGeometry - geo_json_geometry_object_model['type'] = 'Point' - geo_json_geometry_object_model['coordinates'] = ['testString'] - - geo_json_feature_model = {} # GeoJsonFeature - geo_json_feature_model['_id'] = 'testString' - geo_json_feature_model['_rev'] = 'testString' - geo_json_feature_model['bbox'] = [72.5] - geo_json_feature_model['geometry'] = geo_json_geometry_object_model - geo_json_feature_model['properties'] = {'key1': 'testString'} - geo_json_feature_model['type'] = 'Feature' - geo_json_feature_model['foo'] = 'testString' - - attachment_model = {} # Attachment - attachment_model['content_type'] = 'testString' - attachment_model['data'] = 'VGhpcyBpcyBhIG1vY2sgYnl0ZSBhcnJheSB2YWx1ZS4=' - attachment_model['digest'] = 'testString' - attachment_model['encoded_length'] = 0 - attachment_model['encoding'] = 'testString' - attachment_model['follows'] = True - attachment_model['length'] = 0 - attachment_model['revpos'] = 1 - attachment_model['stub'] = True - - revisions_model = {} # Revisions - revisions_model['ids'] = ['testString'] - revisions_model['start'] = 1 - - document_revision_status_model = {} # DocumentRevisionStatus - document_revision_status_model['rev'] = 'testString' - document_revision_status_model['status'] = 'available' - - document_model = {} # Document - document_model['_attachments'] = {'key1': attachment_model} - document_model['_conflicts'] = ['testString'] - document_model['_deleted'] = True - document_model['_deleted_conflicts'] = ['testString'] - document_model['_id'] = 'testString' - document_model['_local_seq'] = 'testString' - document_model['_rev'] = 'testString' - document_model['_revisions'] = revisions_model - document_model['_revs_info'] = [document_revision_status_model] - document_model['foo'] = 'testString' - - geo_json_geometry_model = {} # GeoJsonGeometry - geo_json_geometry_model['type'] = 'Point' - geo_json_geometry_model['coordinates'] = ['testString'] - - geo_result_row_model = {} # GeoResultRow - geo_result_row_model['doc'] = document_model - geo_result_row_model['geometry'] = geo_json_geometry_model - geo_result_row_model['id'] = 'testString' - geo_result_row_model['rev'] = 'testString' + # Construct a json representation of a IndexAnalysisExclusionReason model + index_analysis_exclusion_reason_model_json = {} + index_analysis_exclusion_reason_model_json['name'] = 'alphabetically_comes_after' - # Construct a json representation of a GeoResult model - geo_result_model_json = {} - geo_result_model_json['bookmark'] = 'testString' - geo_result_model_json['features'] = [geo_json_feature_model] - geo_result_model_json['rows'] = [geo_result_row_model] - geo_result_model_json['type'] = 'FeatureCollection' + # Construct a model instance of IndexAnalysisExclusionReason by calling from_dict on the json representation + index_analysis_exclusion_reason_model = IndexAnalysisExclusionReason.from_dict(index_analysis_exclusion_reason_model_json) + assert index_analysis_exclusion_reason_model != False - # Construct a model instance of GeoResult by calling from_dict on the json representation - geo_result_model = GeoResult.from_dict(geo_result_model_json) - assert geo_result_model != False - - # Construct a model instance of GeoResult by calling from_dict on the json representation - geo_result_model_dict = GeoResult.from_dict(geo_result_model_json).__dict__ - geo_result_model2 = GeoResult(**geo_result_model_dict) + # Construct a model instance of IndexAnalysisExclusionReason by calling from_dict on the json representation + index_analysis_exclusion_reason_model_dict = IndexAnalysisExclusionReason.from_dict(index_analysis_exclusion_reason_model_json).__dict__ + index_analysis_exclusion_reason_model2 = IndexAnalysisExclusionReason(**index_analysis_exclusion_reason_model_dict) # Verify the model instances are equivalent - assert geo_result_model == geo_result_model2 + assert index_analysis_exclusion_reason_model == index_analysis_exclusion_reason_model2 # Convert model instance back to dict and verify no loss of data - geo_result_model_json2 = geo_result_model.to_dict() - assert geo_result_model_json2 == geo_result_model_json + index_analysis_exclusion_reason_model_json2 = index_analysis_exclusion_reason_model.to_dict() + assert index_analysis_exclusion_reason_model_json2 == index_analysis_exclusion_reason_model_json + -class TestModel_GeoResultRow(): +class TestModel_IndexCandidate: """ - Test Class for GeoResultRow + Test Class for IndexCandidate """ - def test_geo_result_row_serialization(self): + def test_index_candidate_serialization(self): """ - Test serialization/deserialization for GeoResultRow + Test serialization/deserialization for IndexCandidate """ # Construct dict forms of any model objects needed in order to build this model. - attachment_model = {} # Attachment - attachment_model['content_type'] = 'testString' - attachment_model['data'] = 'VGhpcyBpcyBhIG1vY2sgYnl0ZSBhcnJheSB2YWx1ZS4=' - attachment_model['digest'] = 'testString' - attachment_model['encoded_length'] = 0 - attachment_model['encoding'] = 'testString' - attachment_model['follows'] = True - attachment_model['length'] = 0 - attachment_model['revpos'] = 1 - attachment_model['stub'] = True + index_analysis_exclusion_reason_model = {} # IndexAnalysisExclusionReason + index_analysis_exclusion_reason_model['name'] = 'alphabetically_comes_after' - revisions_model = {} # Revisions - revisions_model['ids'] = ['testString'] - revisions_model['start'] = 1 + index_analysis_model = {} # IndexAnalysis + index_analysis_model['covering'] = True + index_analysis_model['ranking'] = 1 + index_analysis_model['reasons'] = [index_analysis_exclusion_reason_model] + index_analysis_model['usable'] = True - document_revision_status_model = {} # DocumentRevisionStatus - document_revision_status_model['rev'] = 'testString' - document_revision_status_model['status'] = 'available' + analyzer_model = {} # Analyzer + analyzer_model['name'] = 'classic' + analyzer_model['stopwords'] = ['testString'] - document_model = {} # Document - document_model['_attachments'] = {'key1': attachment_model} - document_model['_conflicts'] = ['testString'] - document_model['_deleted'] = True - document_model['_deleted_conflicts'] = ['testString'] - document_model['_id'] = 'testString' - document_model['_local_seq'] = 'testString' - document_model['_rev'] = 'testString' - document_model['_revisions'] = revisions_model - document_model['_revs_info'] = [document_revision_status_model] - document_model['foo'] = 'testString' + index_text_operator_default_field_model = {} # IndexTextOperatorDefaultField + index_text_operator_default_field_model['analyzer'] = analyzer_model + index_text_operator_default_field_model['enabled'] = True + + index_field_model = {} # IndexField + index_field_model['name'] = 'testString' + index_field_model['type'] = 'boolean' + index_field_model['foo'] = 'asc' + + index_definition_model = {} # IndexDefinition + index_definition_model['default_analyzer'] = analyzer_model + index_definition_model['default_field'] = index_text_operator_default_field_model + index_definition_model['fields'] = [index_field_model] + index_definition_model['index_array_lengths'] = True + index_definition_model['partial_filter_selector'] = {'anyKey': 'anyValue'} - geo_json_geometry_model = {} # GeoJsonGeometry - geo_json_geometry_model['type'] = 'Point' - geo_json_geometry_model['coordinates'] = ['testString'] + index_information_model = {} # IndexInformation + index_information_model['ddoc'] = 'testString' + index_information_model['def'] = index_definition_model + index_information_model['name'] = 'testString' + index_information_model['partitioned'] = True + index_information_model['type'] = 'json' - # Construct a json representation of a GeoResultRow model - geo_result_row_model_json = {} - geo_result_row_model_json['doc'] = document_model - geo_result_row_model_json['geometry'] = geo_json_geometry_model - geo_result_row_model_json['id'] = 'testString' - geo_result_row_model_json['rev'] = 'testString' + # Construct a json representation of a IndexCandidate model + index_candidate_model_json = {} + index_candidate_model_json['analysis'] = index_analysis_model + index_candidate_model_json['index'] = index_information_model - # Construct a model instance of GeoResultRow by calling from_dict on the json representation - geo_result_row_model = GeoResultRow.from_dict(geo_result_row_model_json) - assert geo_result_row_model != False + # Construct a model instance of IndexCandidate by calling from_dict on the json representation + index_candidate_model = IndexCandidate.from_dict(index_candidate_model_json) + assert index_candidate_model != False - # Construct a model instance of GeoResultRow by calling from_dict on the json representation - geo_result_row_model_dict = GeoResultRow.from_dict(geo_result_row_model_json).__dict__ - geo_result_row_model2 = GeoResultRow(**geo_result_row_model_dict) + # Construct a model instance of IndexCandidate by calling from_dict on the json representation + index_candidate_model_dict = IndexCandidate.from_dict(index_candidate_model_json).__dict__ + index_candidate_model2 = IndexCandidate(**index_candidate_model_dict) # Verify the model instances are equivalent - assert geo_result_row_model == geo_result_row_model2 + assert index_candidate_model == index_candidate_model2 # Convert model instance back to dict and verify no loss of data - geo_result_row_model_json2 = geo_result_row_model.to_dict() - assert geo_result_row_model_json2 == geo_result_row_model_json + index_candidate_model_json2 = index_candidate_model.to_dict() + assert index_candidate_model_json2 == index_candidate_model_json -class TestModel_IndexDefinition(): + +class TestModel_IndexDefinition: """ Test Class for IndexDefinition """ @@ -15376,15 +16325,15 @@ def test_index_definition_serialization(self): # Construct dict forms of any model objects needed in order to build this model. - analyzer_model = {} # Analyzer + analyzer_model = {} # Analyzer analyzer_model['name'] = 'classic' analyzer_model['stopwords'] = ['testString'] - index_text_operator_default_field_model = {} # IndexTextOperatorDefaultField + index_text_operator_default_field_model = {} # IndexTextOperatorDefaultField index_text_operator_default_field_model['analyzer'] = analyzer_model index_text_operator_default_field_model['enabled'] = True - index_field_model = {} # IndexField + index_field_model = {} # IndexField index_field_model['name'] = 'testString' index_field_model['type'] = 'boolean' index_field_model['foo'] = 'asc' @@ -15395,7 +16344,7 @@ def test_index_definition_serialization(self): index_definition_model_json['default_field'] = index_text_operator_default_field_model index_definition_model_json['fields'] = [index_field_model] index_definition_model_json['index_array_lengths'] = True - index_definition_model_json['partial_filter_selector'] = {'key1': 'testString'} + index_definition_model_json['partial_filter_selector'] = {'anyKey': 'anyValue'} # Construct a model instance of IndexDefinition by calling from_dict on the json representation index_definition_model = IndexDefinition.from_dict(index_definition_model_json) @@ -15412,7 +16361,8 @@ def test_index_definition_serialization(self): index_definition_model_json2 = index_definition_model.to_dict() assert index_definition_model_json2 == index_definition_model_json -class TestModel_IndexField(): + +class TestModel_IndexField: """ Test Class for IndexField """ @@ -15451,9 +16401,10 @@ def test_index_field_serialization(self): expected_dict = {'foo': 'asc'} index_field_model.set_properties(expected_dict) actual_dict = index_field_model.get_properties() - assert actual_dict == expected_dict + assert actual_dict.keys() == expected_dict.keys() -class TestModel_IndexInformation(): + +class TestModel_IndexInformation: """ Test Class for IndexInformation """ @@ -15465,31 +16416,32 @@ def test_index_information_serialization(self): # Construct dict forms of any model objects needed in order to build this model. - analyzer_model = {} # Analyzer + analyzer_model = {} # Analyzer analyzer_model['name'] = 'classic' analyzer_model['stopwords'] = ['testString'] - index_text_operator_default_field_model = {} # IndexTextOperatorDefaultField + index_text_operator_default_field_model = {} # IndexTextOperatorDefaultField index_text_operator_default_field_model['analyzer'] = analyzer_model index_text_operator_default_field_model['enabled'] = True - index_field_model = {} # IndexField + index_field_model = {} # IndexField index_field_model['name'] = 'testString' index_field_model['type'] = 'boolean' index_field_model['foo'] = 'asc' - index_definition_model = {} # IndexDefinition + index_definition_model = {} # IndexDefinition index_definition_model['default_analyzer'] = analyzer_model index_definition_model['default_field'] = index_text_operator_default_field_model index_definition_model['fields'] = [index_field_model] index_definition_model['index_array_lengths'] = True - index_definition_model['partial_filter_selector'] = {'key1': 'testString'} + index_definition_model['partial_filter_selector'] = {'anyKey': 'anyValue'} # Construct a json representation of a IndexInformation model index_information_model_json = {} index_information_model_json['ddoc'] = 'testString' index_information_model_json['def'] = index_definition_model index_information_model_json['name'] = 'testString' + index_information_model_json['partitioned'] = True index_information_model_json['type'] = 'json' # Construct a model instance of IndexInformation by calling from_dict on the json representation @@ -15507,7 +16459,8 @@ def test_index_information_serialization(self): index_information_model_json2 = index_information_model.to_dict() assert index_information_model_json2 == index_information_model_json -class TestModel_IndexResult(): + +class TestModel_IndexResult: """ Test Class for IndexResult """ @@ -15538,7 +16491,8 @@ def test_index_result_serialization(self): index_result_model_json2 = index_result_model.to_dict() assert index_result_model_json2 == index_result_model_json -class TestModel_IndexTextOperatorDefaultField(): + +class TestModel_IndexTextOperatorDefaultField: """ Test Class for IndexTextOperatorDefaultField """ @@ -15550,7 +16504,7 @@ def test_index_text_operator_default_field_serialization(self): # Construct dict forms of any model objects needed in order to build this model. - analyzer_model = {} # Analyzer + analyzer_model = {} # Analyzer analyzer_model['name'] = 'classic' analyzer_model['stopwords'] = ['testString'] @@ -15574,7 +16528,8 @@ def test_index_text_operator_default_field_serialization(self): index_text_operator_default_field_model_json2 = index_text_operator_default_field_model.to_dict() assert index_text_operator_default_field_model_json2 == index_text_operator_default_field_model_json -class TestModel_IndexesInformation(): + +class TestModel_IndexesInformation: """ Test Class for IndexesInformation """ @@ -15586,30 +16541,31 @@ def test_indexes_information_serialization(self): # Construct dict forms of any model objects needed in order to build this model. - analyzer_model = {} # Analyzer + analyzer_model = {} # Analyzer analyzer_model['name'] = 'classic' analyzer_model['stopwords'] = ['testString'] - index_text_operator_default_field_model = {} # IndexTextOperatorDefaultField + index_text_operator_default_field_model = {} # IndexTextOperatorDefaultField index_text_operator_default_field_model['analyzer'] = analyzer_model index_text_operator_default_field_model['enabled'] = True - index_field_model = {} # IndexField + index_field_model = {} # IndexField index_field_model['name'] = 'testString' index_field_model['type'] = 'boolean' index_field_model['foo'] = 'asc' - index_definition_model = {} # IndexDefinition + index_definition_model = {} # IndexDefinition index_definition_model['default_analyzer'] = analyzer_model index_definition_model['default_field'] = index_text_operator_default_field_model index_definition_model['fields'] = [index_field_model] index_definition_model['index_array_lengths'] = True - index_definition_model['partial_filter_selector'] = {'key1': 'testString'} + index_definition_model['partial_filter_selector'] = {'anyKey': 'anyValue'} - index_information_model = {} # IndexInformation + index_information_model = {} # IndexInformation index_information_model['ddoc'] = 'testString' index_information_model['def'] = index_definition_model index_information_model['name'] = 'testString' + index_information_model['partitioned'] = True index_information_model['type'] = 'json' # Construct a json representation of a IndexesInformation model @@ -15632,7 +16588,8 @@ def test_indexes_information_serialization(self): indexes_information_model_json2 = indexes_information_model.to_dict() assert indexes_information_model_json2 == indexes_information_model_json -class TestModel_MembershipInformation(): + +class TestModel_MembershipInformation: """ Test Class for MembershipInformation """ @@ -15662,7 +16619,8 @@ def test_membership_information_serialization(self): membership_information_model_json2 = membership_information_model.to_dict() assert membership_information_model_json2 == membership_information_model_json -class TestModel_Ok(): + +class TestModel_Ok: """ Test Class for Ok """ @@ -15691,7 +16649,8 @@ def test_ok_serialization(self): ok_model_json2 = ok_model.to_dict() assert ok_model_json2 == ok_model_json -class TestModel_PartitionInformation(): + +class TestModel_PartitionInformation: """ Test Class for PartitionInformation """ @@ -15703,16 +16662,16 @@ def test_partition_information_serialization(self): # Construct dict forms of any model objects needed in order to build this model. - partition_information_indexes_indexes_model = {} # PartitionInformationIndexesIndexes + partition_information_indexes_indexes_model = {} # PartitionInformationIndexesIndexes partition_information_indexes_indexes_model['search'] = 0 partition_information_indexes_indexes_model['view'] = 0 - partition_information_indexes_model = {} # PartitionInformationIndexes + partition_information_indexes_model = {} # PartitionInformationIndexes partition_information_indexes_model['count'] = 0 partition_information_indexes_model['indexes'] = partition_information_indexes_indexes_model partition_information_indexes_model['limit'] = 0 - partition_information_sizes_model = {} # PartitionInformationSizes + partition_information_sizes_model = {} # PartitionInformationSizes partition_information_sizes_model['active'] = 0 partition_information_sizes_model['external'] = 0 @@ -15740,7 +16699,8 @@ def test_partition_information_serialization(self): partition_information_model_json2 = partition_information_model.to_dict() assert partition_information_model_json2 == partition_information_model_json -class TestModel_PartitionInformationIndexes(): + +class TestModel_PartitionInformationIndexes: """ Test Class for PartitionInformationIndexes """ @@ -15752,7 +16712,7 @@ def test_partition_information_indexes_serialization(self): # Construct dict forms of any model objects needed in order to build this model. - partition_information_indexes_indexes_model = {} # PartitionInformationIndexesIndexes + partition_information_indexes_indexes_model = {} # PartitionInformationIndexesIndexes partition_information_indexes_indexes_model['search'] = 0 partition_information_indexes_indexes_model['view'] = 0 @@ -15777,7 +16737,8 @@ def test_partition_information_indexes_serialization(self): partition_information_indexes_model_json2 = partition_information_indexes_model.to_dict() assert partition_information_indexes_model_json2 == partition_information_indexes_model_json -class TestModel_PartitionInformationIndexesIndexes(): + +class TestModel_PartitionInformationIndexesIndexes: """ Test Class for PartitionInformationIndexesIndexes """ @@ -15807,7 +16768,8 @@ def test_partition_information_indexes_indexes_serialization(self): partition_information_indexes_indexes_model_json2 = partition_information_indexes_indexes_model.to_dict() assert partition_information_indexes_indexes_model_json2 == partition_information_indexes_indexes_model_json -class TestModel_PartitionInformationSizes(): + +class TestModel_PartitionInformationSizes: """ Test Class for PartitionInformationSizes """ @@ -15837,7 +16799,77 @@ def test_partition_information_sizes_serialization(self): partition_information_sizes_model_json2 = partition_information_sizes_model.to_dict() assert partition_information_sizes_model_json2 == partition_information_sizes_model_json -class TestModel_ReplicationCreateTargetParameters(): + +class TestModel_PartitionedIndexesDetailedInformation: + """ + Test Class for PartitionedIndexesDetailedInformation + """ + + def test_partitioned_indexes_detailed_information_serialization(self): + """ + Test serialization/deserialization for PartitionedIndexesDetailedInformation + """ + + # Construct a json representation of a PartitionedIndexesDetailedInformation model + partitioned_indexes_detailed_information_model_json = {} + partitioned_indexes_detailed_information_model_json['search'] = 0 + partitioned_indexes_detailed_information_model_json['view'] = 0 + + # Construct a model instance of PartitionedIndexesDetailedInformation by calling from_dict on the json representation + partitioned_indexes_detailed_information_model = PartitionedIndexesDetailedInformation.from_dict(partitioned_indexes_detailed_information_model_json) + assert partitioned_indexes_detailed_information_model != False + + # Construct a model instance of PartitionedIndexesDetailedInformation by calling from_dict on the json representation + partitioned_indexes_detailed_information_model_dict = PartitionedIndexesDetailedInformation.from_dict(partitioned_indexes_detailed_information_model_json).__dict__ + partitioned_indexes_detailed_information_model2 = PartitionedIndexesDetailedInformation(**partitioned_indexes_detailed_information_model_dict) + + # Verify the model instances are equivalent + assert partitioned_indexes_detailed_information_model == partitioned_indexes_detailed_information_model2 + + # Convert model instance back to dict and verify no loss of data + partitioned_indexes_detailed_information_model_json2 = partitioned_indexes_detailed_information_model.to_dict() + assert partitioned_indexes_detailed_information_model_json2 == partitioned_indexes_detailed_information_model_json + + +class TestModel_PartitionedIndexesInformation: + """ + Test Class for PartitionedIndexesInformation + """ + + def test_partitioned_indexes_information_serialization(self): + """ + Test serialization/deserialization for PartitionedIndexesInformation + """ + + # Construct dict forms of any model objects needed in order to build this model. + + partitioned_indexes_detailed_information_model = {} # PartitionedIndexesDetailedInformation + partitioned_indexes_detailed_information_model['search'] = 0 + partitioned_indexes_detailed_information_model['view'] = 0 + + # Construct a json representation of a PartitionedIndexesInformation model + partitioned_indexes_information_model_json = {} + partitioned_indexes_information_model_json['count'] = 0 + partitioned_indexes_information_model_json['indexes'] = partitioned_indexes_detailed_information_model + partitioned_indexes_information_model_json['limit'] = 10 + + # Construct a model instance of PartitionedIndexesInformation by calling from_dict on the json representation + partitioned_indexes_information_model = PartitionedIndexesInformation.from_dict(partitioned_indexes_information_model_json) + assert partitioned_indexes_information_model != False + + # Construct a model instance of PartitionedIndexesInformation by calling from_dict on the json representation + partitioned_indexes_information_model_dict = PartitionedIndexesInformation.from_dict(partitioned_indexes_information_model_json).__dict__ + partitioned_indexes_information_model2 = PartitionedIndexesInformation(**partitioned_indexes_information_model_dict) + + # Verify the model instances are equivalent + assert partitioned_indexes_information_model == partitioned_indexes_information_model2 + + # Convert model instance back to dict and verify no loss of data + partitioned_indexes_information_model_json2 = partitioned_indexes_information_model.to_dict() + assert partitioned_indexes_information_model_json2 == partitioned_indexes_information_model_json + + +class TestModel_ReplicationCreateTargetParameters: """ Test Class for ReplicationCreateTargetParameters """ @@ -15849,9 +16881,9 @@ def test_replication_create_target_parameters_serialization(self): # Construct a json representation of a ReplicationCreateTargetParameters model replication_create_target_parameters_model_json = {} - replication_create_target_parameters_model_json['n'] = 1 + replication_create_target_parameters_model_json['n'] = 3 replication_create_target_parameters_model_json['partitioned'] = False - replication_create_target_parameters_model_json['q'] = 26 + replication_create_target_parameters_model_json['q'] = 16 # Construct a model instance of ReplicationCreateTargetParameters by calling from_dict on the json representation replication_create_target_parameters_model = ReplicationCreateTargetParameters.from_dict(replication_create_target_parameters_model_json) @@ -15868,7 +16900,8 @@ def test_replication_create_target_parameters_serialization(self): replication_create_target_parameters_model_json2 = replication_create_target_parameters_model.to_dict() assert replication_create_target_parameters_model_json2 == replication_create_target_parameters_model_json -class TestModel_ReplicationDatabase(): + +class TestModel_ReplicationDatabase: """ Test Class for ReplicationDatabase """ @@ -15880,14 +16913,14 @@ def test_replication_database_serialization(self): # Construct dict forms of any model objects needed in order to build this model. - replication_database_auth_basic_model = {} # ReplicationDatabaseAuthBasic + replication_database_auth_basic_model = {} # ReplicationDatabaseAuthBasic replication_database_auth_basic_model['password'] = 'testString' replication_database_auth_basic_model['username'] = 'testString' - replication_database_auth_iam_model = {} # ReplicationDatabaseAuthIam + replication_database_auth_iam_model = {} # ReplicationDatabaseAuthIam replication_database_auth_iam_model['api_key'] = 'testString' - replication_database_auth_model = {} # ReplicationDatabaseAuth + replication_database_auth_model = {} # ReplicationDatabaseAuth replication_database_auth_model['basic'] = replication_database_auth_basic_model replication_database_auth_model['iam'] = replication_database_auth_iam_model @@ -15912,7 +16945,8 @@ def test_replication_database_serialization(self): replication_database_model_json2 = replication_database_model.to_dict() assert replication_database_model_json2 == replication_database_model_json -class TestModel_ReplicationDatabaseAuth(): + +class TestModel_ReplicationDatabaseAuth: """ Test Class for ReplicationDatabaseAuth """ @@ -15924,11 +16958,11 @@ def test_replication_database_auth_serialization(self): # Construct dict forms of any model objects needed in order to build this model. - replication_database_auth_basic_model = {} # ReplicationDatabaseAuthBasic + replication_database_auth_basic_model = {} # ReplicationDatabaseAuthBasic replication_database_auth_basic_model['password'] = 'testString' replication_database_auth_basic_model['username'] = 'testString' - replication_database_auth_iam_model = {} # ReplicationDatabaseAuthIam + replication_database_auth_iam_model = {} # ReplicationDatabaseAuthIam replication_database_auth_iam_model['api_key'] = 'testString' # Construct a json representation of a ReplicationDatabaseAuth model @@ -15951,7 +16985,8 @@ def test_replication_database_auth_serialization(self): replication_database_auth_model_json2 = replication_database_auth_model.to_dict() assert replication_database_auth_model_json2 == replication_database_auth_model_json -class TestModel_ReplicationDatabaseAuthBasic(): + +class TestModel_ReplicationDatabaseAuthBasic: """ Test Class for ReplicationDatabaseAuthBasic """ @@ -15981,7 +17016,8 @@ def test_replication_database_auth_basic_serialization(self): replication_database_auth_basic_model_json2 = replication_database_auth_basic_model.to_dict() assert replication_database_auth_basic_model_json2 == replication_database_auth_basic_model_json -class TestModel_ReplicationDatabaseAuthIam(): + +class TestModel_ReplicationDatabaseAuthIam: """ Test Class for ReplicationDatabaseAuthIam """ @@ -16010,7 +17046,8 @@ def test_replication_database_auth_iam_serialization(self): replication_database_auth_iam_model_json2 = replication_database_auth_iam_model.to_dict() assert replication_database_auth_iam_model_json2 == replication_database_auth_iam_model_json -class TestModel_ReplicationDocument(): + +class TestModel_ReplicationDocument: """ Test Class for ReplicationDocument """ @@ -16022,7 +17059,7 @@ def test_replication_document_serialization(self): # Construct dict forms of any model objects needed in order to build this model. - attachment_model = {} # Attachment + attachment_model = {} # Attachment attachment_model['content_type'] = 'testString' attachment_model['data'] = 'VGhpcyBpcyBhIG1vY2sgYnl0ZSBhcnJheSB2YWx1ZS4=' attachment_model['digest'] = 'testString' @@ -16033,36 +17070,36 @@ def test_replication_document_serialization(self): attachment_model['revpos'] = 1 attachment_model['stub'] = True - revisions_model = {} # Revisions + revisions_model = {} # Revisions revisions_model['ids'] = ['testString'] revisions_model['start'] = 1 - document_revision_status_model = {} # DocumentRevisionStatus + document_revision_status_model = {} # DocumentRevisionStatus document_revision_status_model['rev'] = 'testString' document_revision_status_model['status'] = 'available' - replication_create_target_parameters_model = {} # ReplicationCreateTargetParameters - replication_create_target_parameters_model['n'] = 1 + replication_create_target_parameters_model = {} # ReplicationCreateTargetParameters + replication_create_target_parameters_model['n'] = 3 replication_create_target_parameters_model['partitioned'] = False - replication_create_target_parameters_model['q'] = 26 + replication_create_target_parameters_model['q'] = 16 - replication_database_auth_basic_model = {} # ReplicationDatabaseAuthBasic + replication_database_auth_basic_model = {} # ReplicationDatabaseAuthBasic replication_database_auth_basic_model['password'] = 'testString' replication_database_auth_basic_model['username'] = 'testString' - replication_database_auth_iam_model = {} # ReplicationDatabaseAuthIam + replication_database_auth_iam_model = {} # ReplicationDatabaseAuthIam replication_database_auth_iam_model['api_key'] = 'testString' - replication_database_auth_model = {} # ReplicationDatabaseAuth + replication_database_auth_model = {} # ReplicationDatabaseAuth replication_database_auth_model['basic'] = replication_database_auth_basic_model replication_database_auth_model['iam'] = replication_database_auth_iam_model - replication_database_model = {} # ReplicationDatabase + replication_database_model = {} # ReplicationDatabase replication_database_model['auth'] = replication_database_auth_model replication_database_model['headers'] = {'key1': 'testString'} replication_database_model['url'] = 'testString' - user_context_model = {} # UserContext + user_context_model = {} # UserContext user_context_model['db'] = 'testString' user_context_model['name'] = 'testString' user_context_model['roles'] = ['_reader'] @@ -16079,27 +17116,30 @@ def test_replication_document_serialization(self): replication_document_model_json['_revisions'] = revisions_model replication_document_model_json['_revs_info'] = [document_revision_status_model] replication_document_model_json['cancel'] = True - replication_document_model_json['checkpoint_interval'] = 0 - replication_document_model_json['connection_timeout'] = 0 + replication_document_model_json['checkpoint_interval'] = 30000 + replication_document_model_json['connection_timeout'] = 30000 replication_document_model_json['continuous'] = False replication_document_model_json['create_target'] = False replication_document_model_json['create_target_params'] = replication_create_target_parameters_model replication_document_model_json['doc_ids'] = ['testString'] replication_document_model_json['filter'] = 'testString' - replication_document_model_json['http_connections'] = 1 + replication_document_model_json['http_connections'] = 20 + replication_document_model_json['owner'] = 'testString' replication_document_model_json['query_params'] = {'key1': 'testString'} - replication_document_model_json['retries_per_request'] = 0 - replication_document_model_json['selector'] = {'key1': 'testString'} + replication_document_model_json['retries_per_request'] = 5 + replication_document_model_json['selector'] = {'anyKey': 'anyValue'} replication_document_model_json['since_seq'] = 'testString' replication_document_model_json['socket_options'] = 'testString' replication_document_model_json['source'] = replication_database_model replication_document_model_json['source_proxy'] = 'testString' replication_document_model_json['target'] = replication_database_model replication_document_model_json['target_proxy'] = 'testString' + replication_document_model_json['use_bulk_get'] = True replication_document_model_json['use_checkpoints'] = True replication_document_model_json['user_ctx'] = user_context_model - replication_document_model_json['worker_batch_size'] = 1 - replication_document_model_json['worker_processes'] = 1 + replication_document_model_json['winning_revs_only'] = False + replication_document_model_json['worker_batch_size'] = 500 + replication_document_model_json['worker_processes'] = 4 replication_document_model_json['foo'] = 'testString' # Construct a model instance of ReplicationDocument by calling from_dict on the json representation @@ -16125,9 +17165,10 @@ def test_replication_document_serialization(self): expected_dict = {'foo': 'testString'} replication_document_model.set_properties(expected_dict) actual_dict = replication_document_model.get_properties() - assert actual_dict == expected_dict + assert actual_dict.keys() == expected_dict.keys() + -class TestModel_Revisions(): +class TestModel_Revisions: """ Test Class for Revisions """ @@ -16157,7 +17198,8 @@ def test_revisions_serialization(self): revisions_model_json2 = revisions_model.to_dict() assert revisions_model_json2 == revisions_model_json -class TestModel_RevsDiff(): + +class TestModel_RevsDiff: """ Test Class for RevsDiff """ @@ -16187,7 +17229,8 @@ def test_revs_diff_serialization(self): revs_diff_model_json2 = revs_diff_model.to_dict() assert revs_diff_model_json2 == revs_diff_model_json -class TestModel_SchedulerDocsResult(): + +class TestModel_SchedulerDocsResult: """ Test Class for SchedulerDocsResult """ @@ -16199,7 +17242,7 @@ def test_scheduler_docs_result_serialization(self): # Construct dict forms of any model objects needed in order to build this model. - scheduler_info_model = {} # SchedulerInfo + scheduler_info_model = {} # SchedulerInfo scheduler_info_model['changes_pending'] = 0 scheduler_info_model['checkpointed_source_seq'] = 'testString' scheduler_info_model['doc_write_failures'] = 0 @@ -16211,7 +17254,7 @@ def test_scheduler_docs_result_serialization(self): scheduler_info_model['source_seq'] = 'testString' scheduler_info_model['through_seq'] = 'testString' - scheduler_document_model = {} # SchedulerDocument + scheduler_document_model = {} # SchedulerDocument scheduler_document_model['database'] = 'testString' scheduler_document_model['doc_id'] = 'testString' scheduler_document_model['error_count'] = 0 @@ -16246,7 +17289,8 @@ def test_scheduler_docs_result_serialization(self): scheduler_docs_result_model_json2 = scheduler_docs_result_model.to_dict() assert scheduler_docs_result_model_json2 == scheduler_docs_result_model_json -class TestModel_SchedulerDocument(): + +class TestModel_SchedulerDocument: """ Test Class for SchedulerDocument """ @@ -16258,7 +17302,7 @@ def test_scheduler_document_serialization(self): # Construct dict forms of any model objects needed in order to build this model. - scheduler_info_model = {} # SchedulerInfo + scheduler_info_model = {} # SchedulerInfo scheduler_info_model['changes_pending'] = 0 scheduler_info_model['checkpointed_source_seq'] = 'testString' scheduler_info_model['doc_write_failures'] = 0 @@ -16301,7 +17345,8 @@ def test_scheduler_document_serialization(self): scheduler_document_model_json2 = scheduler_document_model.to_dict() assert scheduler_document_model_json2 == scheduler_document_model_json -class TestModel_SchedulerInfo(): + +class TestModel_SchedulerInfo: """ Test Class for SchedulerInfo """ @@ -16339,7 +17384,8 @@ def test_scheduler_info_serialization(self): scheduler_info_model_json2 = scheduler_info_model.to_dict() assert scheduler_info_model_json2 == scheduler_info_model_json -class TestModel_SchedulerJob(): + +class TestModel_SchedulerJob: """ Test Class for SchedulerJob """ @@ -16351,12 +17397,12 @@ def test_scheduler_job_serialization(self): # Construct dict forms of any model objects needed in order to build this model. - scheduler_job_event_model = {} # SchedulerJobEvent + scheduler_job_event_model = {} # SchedulerJobEvent scheduler_job_event_model['reason'] = 'testString' scheduler_job_event_model['timestamp'] = '2019-01-01T12:00:00Z' scheduler_job_event_model['type'] = 'testString' - scheduler_info_model = {} # SchedulerInfo + scheduler_info_model = {} # SchedulerInfo scheduler_info_model['changes_pending'] = 0 scheduler_info_model['checkpointed_source_seq'] = 'testString' scheduler_info_model['doc_write_failures'] = 0 @@ -16397,7 +17443,8 @@ def test_scheduler_job_serialization(self): scheduler_job_model_json2 = scheduler_job_model.to_dict() assert scheduler_job_model_json2 == scheduler_job_model_json -class TestModel_SchedulerJobEvent(): + +class TestModel_SchedulerJobEvent: """ Test Class for SchedulerJobEvent """ @@ -16428,7 +17475,8 @@ def test_scheduler_job_event_serialization(self): scheduler_job_event_model_json2 = scheduler_job_event_model.to_dict() assert scheduler_job_event_model_json2 == scheduler_job_event_model_json -class TestModel_SchedulerJobsResult(): + +class TestModel_SchedulerJobsResult: """ Test Class for SchedulerJobsResult """ @@ -16440,12 +17488,12 @@ def test_scheduler_jobs_result_serialization(self): # Construct dict forms of any model objects needed in order to build this model. - scheduler_job_event_model = {} # SchedulerJobEvent + scheduler_job_event_model = {} # SchedulerJobEvent scheduler_job_event_model['reason'] = 'testString' scheduler_job_event_model['timestamp'] = '2019-01-01T12:00:00Z' scheduler_job_event_model['type'] = 'testString' - scheduler_info_model = {} # SchedulerInfo + scheduler_info_model = {} # SchedulerInfo scheduler_info_model['changes_pending'] = 0 scheduler_info_model['checkpointed_source_seq'] = 'testString' scheduler_info_model['doc_write_failures'] = 0 @@ -16457,7 +17505,7 @@ def test_scheduler_jobs_result_serialization(self): scheduler_info_model['source_seq'] = 'testString' scheduler_info_model['through_seq'] = 'testString' - scheduler_job_model = {} # SchedulerJob + scheduler_job_model = {} # SchedulerJob scheduler_job_model['database'] = 'testString' scheduler_job_model['doc_id'] = 'testString' scheduler_job_model['history'] = [scheduler_job_event_model] @@ -16490,7 +17538,8 @@ def test_scheduler_jobs_result_serialization(self): scheduler_jobs_result_model_json2 = scheduler_jobs_result_model.to_dict() assert scheduler_jobs_result_model_json2 == scheduler_jobs_result_model_json -class TestModel_SearchAnalyzeResult(): + +class TestModel_SearchAnalyzeResult: """ Test Class for SearchAnalyzeResult """ @@ -16519,7 +17568,44 @@ def test_search_analyze_result_serialization(self): search_analyze_result_model_json2 = search_analyze_result_model.to_dict() assert search_analyze_result_model_json2 == search_analyze_result_model_json -class TestModel_SearchIndexDefinition(): + +class TestModel_SearchDiskSizeInformation: + """ + Test Class for SearchDiskSizeInformation + """ + + def test_search_disk_size_information_serialization(self): + """ + Test serialization/deserialization for SearchDiskSizeInformation + """ + + # Construct dict forms of any model objects needed in order to build this model. + + search_index_disk_size_model = {} # SearchIndexDiskSize + search_index_disk_size_model['disk_size'] = 0 + + # Construct a json representation of a SearchDiskSizeInformation model + search_disk_size_information_model_json = {} + search_disk_size_information_model_json['name'] = 'testString' + search_disk_size_information_model_json['search_index'] = search_index_disk_size_model + + # Construct a model instance of SearchDiskSizeInformation by calling from_dict on the json representation + search_disk_size_information_model = SearchDiskSizeInformation.from_dict(search_disk_size_information_model_json) + assert search_disk_size_information_model != False + + # Construct a model instance of SearchDiskSizeInformation by calling from_dict on the json representation + search_disk_size_information_model_dict = SearchDiskSizeInformation.from_dict(search_disk_size_information_model_json).__dict__ + search_disk_size_information_model2 = SearchDiskSizeInformation(**search_disk_size_information_model_dict) + + # Verify the model instances are equivalent + assert search_disk_size_information_model == search_disk_size_information_model2 + + # Convert model instance back to dict and verify no loss of data + search_disk_size_information_model_json2 = search_disk_size_information_model.to_dict() + assert search_disk_size_information_model_json2 == search_disk_size_information_model_json + + +class TestModel_SearchIndexDefinition: """ Test Class for SearchIndexDefinition """ @@ -16531,13 +17617,14 @@ def test_search_index_definition_serialization(self): # Construct dict forms of any model objects needed in order to build this model. - analyzer_model = {} # Analyzer + analyzer_model = {} # Analyzer analyzer_model['name'] = 'classic' analyzer_model['stopwords'] = ['testString'] - analyzer_configuration_model = {} # AnalyzerConfiguration + analyzer_configuration_model = {} # AnalyzerConfiguration analyzer_configuration_model['name'] = 'classic' analyzer_configuration_model['stopwords'] = ['testString'] + analyzer_configuration_model['default'] = analyzer_model analyzer_configuration_model['fields'] = {'key1': analyzer_model} # Construct a json representation of a SearchIndexDefinition model @@ -16560,7 +17647,38 @@ def test_search_index_definition_serialization(self): search_index_definition_model_json2 = search_index_definition_model.to_dict() assert search_index_definition_model_json2 == search_index_definition_model_json -class TestModel_SearchIndexInfo(): + +class TestModel_SearchIndexDiskSize: + """ + Test Class for SearchIndexDiskSize + """ + + def test_search_index_disk_size_serialization(self): + """ + Test serialization/deserialization for SearchIndexDiskSize + """ + + # Construct a json representation of a SearchIndexDiskSize model + search_index_disk_size_model_json = {} + search_index_disk_size_model_json['disk_size'] = 0 + + # Construct a model instance of SearchIndexDiskSize by calling from_dict on the json representation + search_index_disk_size_model = SearchIndexDiskSize.from_dict(search_index_disk_size_model_json) + assert search_index_disk_size_model != False + + # Construct a model instance of SearchIndexDiskSize by calling from_dict on the json representation + search_index_disk_size_model_dict = SearchIndexDiskSize.from_dict(search_index_disk_size_model_json).__dict__ + search_index_disk_size_model2 = SearchIndexDiskSize(**search_index_disk_size_model_dict) + + # Verify the model instances are equivalent + assert search_index_disk_size_model == search_index_disk_size_model2 + + # Convert model instance back to dict and verify no loss of data + search_index_disk_size_model_json2 = search_index_disk_size_model.to_dict() + assert search_index_disk_size_model_json2 == search_index_disk_size_model_json + + +class TestModel_SearchIndexInfo: """ Test Class for SearchIndexInfo """ @@ -16572,11 +17690,11 @@ def test_search_index_info_serialization(self): # Construct a json representation of a SearchIndexInfo model search_index_info_model_json = {} - search_index_info_model_json['committed_seq'] = 26 + search_index_info_model_json['committed_seq'] = 0 search_index_info_model_json['disk_size'] = 0 search_index_info_model_json['doc_count'] = 0 search_index_info_model_json['doc_del_count'] = 0 - search_index_info_model_json['pending_seq'] = 26 + search_index_info_model_json['pending_seq'] = 0 search_index_info_model_json['signature'] = 'testString' # Construct a model instance of SearchIndexInfo by calling from_dict on the json representation @@ -16594,7 +17712,8 @@ def test_search_index_info_serialization(self): search_index_info_model_json2 = search_index_info_model.to_dict() assert search_index_info_model_json2 == search_index_info_model_json -class TestModel_SearchInfoResult(): + +class TestModel_SearchInfoResult: """ Test Class for SearchInfoResult """ @@ -16606,12 +17725,12 @@ def test_search_info_result_serialization(self): # Construct dict forms of any model objects needed in order to build this model. - search_index_info_model = {} # SearchIndexInfo - search_index_info_model['committed_seq'] = 26 + search_index_info_model = {} # SearchIndexInfo + search_index_info_model['committed_seq'] = 0 search_index_info_model['disk_size'] = 0 search_index_info_model['doc_count'] = 0 search_index_info_model['doc_del_count'] = 0 - search_index_info_model['pending_seq'] = 26 + search_index_info_model['pending_seq'] = 0 search_index_info_model['signature'] = 'testString' # Construct a json representation of a SearchInfoResult model @@ -16634,7 +17753,8 @@ def test_search_info_result_serialization(self): search_info_result_model_json2 = search_info_result_model.to_dict() assert search_info_result_model_json2 == search_info_result_model_json -class TestModel_SearchResult(): + +class TestModel_SearchResult: """ Test Class for SearchResult """ @@ -16646,7 +17766,7 @@ def test_search_result_serialization(self): # Construct dict forms of any model objects needed in order to build this model. - attachment_model = {} # Attachment + attachment_model = {} # Attachment attachment_model['content_type'] = 'testString' attachment_model['data'] = 'VGhpcyBpcyBhIG1vY2sgYnl0ZSBhcnJheSB2YWx1ZS4=' attachment_model['digest'] = 'testString' @@ -16657,15 +17777,15 @@ def test_search_result_serialization(self): attachment_model['revpos'] = 1 attachment_model['stub'] = True - revisions_model = {} # Revisions + revisions_model = {} # Revisions revisions_model['ids'] = ['testString'] revisions_model['start'] = 1 - document_revision_status_model = {} # DocumentRevisionStatus + document_revision_status_model = {} # DocumentRevisionStatus document_revision_status_model['rev'] = 'testString' document_revision_status_model['status'] = 'available' - document_model = {} # Document + document_model = {} # Document document_model['_attachments'] = {'key1': attachment_model} document_model['_conflicts'] = ['testString'] document_model['_deleted'] = True @@ -16677,13 +17797,13 @@ def test_search_result_serialization(self): document_model['_revs_info'] = [document_revision_status_model] document_model['foo'] = 'testString' - search_result_row_model = {} # SearchResultRow + search_result_row_model = {} # SearchResultRow search_result_row_model['doc'] = document_model - search_result_row_model['fields'] = {'key1': 'testString'} + search_result_row_model['fields'] = {'anyKey': 'anyValue'} search_result_row_model['highlights'] = {'key1': ['testString']} search_result_row_model['id'] = 'testString' - search_result_properties_model = {} # SearchResultProperties + search_result_properties_model = {} # SearchResultProperties search_result_properties_model['total_rows'] = 0 search_result_properties_model['bookmark'] = 'testString' search_result_properties_model['by'] = 'testString' @@ -16716,7 +17836,8 @@ def test_search_result_serialization(self): search_result_model_json2 = search_result_model.to_dict() assert search_result_model_json2 == search_result_model_json -class TestModel_SearchResultProperties(): + +class TestModel_SearchResultProperties: """ Test Class for SearchResultProperties """ @@ -16728,7 +17849,7 @@ def test_search_result_properties_serialization(self): # Construct dict forms of any model objects needed in order to build this model. - attachment_model = {} # Attachment + attachment_model = {} # Attachment attachment_model['content_type'] = 'testString' attachment_model['data'] = 'VGhpcyBpcyBhIG1vY2sgYnl0ZSBhcnJheSB2YWx1ZS4=' attachment_model['digest'] = 'testString' @@ -16739,15 +17860,15 @@ def test_search_result_properties_serialization(self): attachment_model['revpos'] = 1 attachment_model['stub'] = True - revisions_model = {} # Revisions + revisions_model = {} # Revisions revisions_model['ids'] = ['testString'] revisions_model['start'] = 1 - document_revision_status_model = {} # DocumentRevisionStatus + document_revision_status_model = {} # DocumentRevisionStatus document_revision_status_model['rev'] = 'testString' document_revision_status_model['status'] = 'available' - document_model = {} # Document + document_model = {} # Document document_model['_attachments'] = {'key1': attachment_model} document_model['_conflicts'] = ['testString'] document_model['_deleted'] = True @@ -16759,9 +17880,9 @@ def test_search_result_properties_serialization(self): document_model['_revs_info'] = [document_revision_status_model] document_model['foo'] = 'testString' - search_result_row_model = {} # SearchResultRow + search_result_row_model = {} # SearchResultRow search_result_row_model['doc'] = document_model - search_result_row_model['fields'] = {'key1': 'testString'} + search_result_row_model['fields'] = {'anyKey': 'anyValue'} search_result_row_model['highlights'] = {'key1': ['testString']} search_result_row_model['id'] = 'testString' @@ -16789,7 +17910,8 @@ def test_search_result_properties_serialization(self): search_result_properties_model_json2 = search_result_properties_model.to_dict() assert search_result_properties_model_json2 == search_result_properties_model_json -class TestModel_SearchResultRow(): + +class TestModel_SearchResultRow: """ Test Class for SearchResultRow """ @@ -16801,7 +17923,7 @@ def test_search_result_row_serialization(self): # Construct dict forms of any model objects needed in order to build this model. - attachment_model = {} # Attachment + attachment_model = {} # Attachment attachment_model['content_type'] = 'testString' attachment_model['data'] = 'VGhpcyBpcyBhIG1vY2sgYnl0ZSBhcnJheSB2YWx1ZS4=' attachment_model['digest'] = 'testString' @@ -16812,15 +17934,15 @@ def test_search_result_row_serialization(self): attachment_model['revpos'] = 1 attachment_model['stub'] = True - revisions_model = {} # Revisions + revisions_model = {} # Revisions revisions_model['ids'] = ['testString'] revisions_model['start'] = 1 - document_revision_status_model = {} # DocumentRevisionStatus + document_revision_status_model = {} # DocumentRevisionStatus document_revision_status_model['rev'] = 'testString' document_revision_status_model['status'] = 'available' - document_model = {} # Document + document_model = {} # Document document_model['_attachments'] = {'key1': attachment_model} document_model['_conflicts'] = ['testString'] document_model['_deleted'] = True @@ -16835,7 +17957,7 @@ def test_search_result_row_serialization(self): # Construct a json representation of a SearchResultRow model search_result_row_model_json = {} search_result_row_model_json['doc'] = document_model - search_result_row_model_json['fields'] = {'key1': 'testString'} + search_result_row_model_json['fields'] = {'anyKey': 'anyValue'} search_result_row_model_json['highlights'] = {'key1': ['testString']} search_result_row_model_json['id'] = 'testString' @@ -16854,7 +17976,8 @@ def test_search_result_row_serialization(self): search_result_row_model_json2 = search_result_row_model.to_dict() assert search_result_row_model_json2 == search_result_row_model_json -class TestModel_Security(): + +class TestModel_Security: """ Test Class for Security """ @@ -16866,16 +17989,16 @@ def test_security_serialization(self): # Construct dict forms of any model objects needed in order to build this model. - security_object_model = {} # SecurityObject + security_object_model = {} # SecurityObject security_object_model['names'] = ['testString'] security_object_model['roles'] = ['testString'] # Construct a json representation of a Security model security_model_json = {} security_model_json['admins'] = security_object_model - security_model_json['members'] = security_object_model security_model_json['cloudant'] = {'key1': ['_reader']} security_model_json['couchdb_auth_only'] = True + security_model_json['members'] = security_object_model # Construct a model instance of Security by calling from_dict on the json representation security_model = Security.from_dict(security_model_json) @@ -16892,7 +18015,8 @@ def test_security_serialization(self): security_model_json2 = security_model.to_dict() assert security_model_json2 == security_model_json -class TestModel_SecurityObject(): + +class TestModel_SecurityObject: """ Test Class for SecurityObject """ @@ -16922,7 +18046,40 @@ def test_security_object_serialization(self): security_object_model_json2 = security_object_model.to_dict() assert security_object_model_json2 == security_object_model_json -class TestModel_ServerInformation(): + +class TestModel_SelectorHint: + """ + Test Class for SelectorHint + """ + + def test_selector_hint_serialization(self): + """ + Test serialization/deserialization for SelectorHint + """ + + # Construct a json representation of a SelectorHint model + selector_hint_model_json = {} + selector_hint_model_json['indexable_fields'] = ['testString'] + selector_hint_model_json['type'] = 'json' + selector_hint_model_json['unindexable_fields'] = ['testString'] + + # Construct a model instance of SelectorHint by calling from_dict on the json representation + selector_hint_model = SelectorHint.from_dict(selector_hint_model_json) + assert selector_hint_model != False + + # Construct a model instance of SelectorHint by calling from_dict on the json representation + selector_hint_model_dict = SelectorHint.from_dict(selector_hint_model_json).__dict__ + selector_hint_model2 = SelectorHint(**selector_hint_model_dict) + + # Verify the model instances are equivalent + assert selector_hint_model == selector_hint_model2 + + # Convert model instance back to dict and verify no loss of data + selector_hint_model_json2 = selector_hint_model.to_dict() + assert selector_hint_model_json2 == selector_hint_model_json + + +class TestModel_ServerInformation: """ Test Class for ServerInformation """ @@ -16934,18 +18091,19 @@ def test_server_information_serialization(self): # Construct dict forms of any model objects needed in order to build this model. - server_vendor_model = {} # ServerVendor + server_vendor_model = {} # ServerVendor server_vendor_model['name'] = 'testString' - server_vendor_model['variant'] = 'testString' + server_vendor_model['variant'] = 'paas' server_vendor_model['version'] = 'testString' + server_vendor_model['foo'] = 'testString' # Construct a json representation of a ServerInformation model server_information_model_json = {} server_information_model_json['couchdb'] = 'testString' server_information_model_json['features'] = ['testString'] + server_information_model_json['features_flags'] = ['testString'] server_information_model_json['vendor'] = server_vendor_model server_information_model_json['version'] = 'testString' - server_information_model_json['features_flags'] = ['testString'] # Construct a model instance of ServerInformation by calling from_dict on the json representation server_information_model = ServerInformation.from_dict(server_information_model_json) @@ -16962,7 +18120,8 @@ def test_server_information_serialization(self): server_information_model_json2 = server_information_model.to_dict() assert server_information_model_json2 == server_information_model_json -class TestModel_ServerVendor(): + +class TestModel_ServerVendor: """ Test Class for ServerVendor """ @@ -16975,8 +18134,9 @@ def test_server_vendor_serialization(self): # Construct a json representation of a ServerVendor model server_vendor_model_json = {} server_vendor_model_json['name'] = 'testString' - server_vendor_model_json['variant'] = 'testString' + server_vendor_model_json['variant'] = 'paas' server_vendor_model_json['version'] = 'testString' + server_vendor_model_json['foo'] = 'testString' # Construct a model instance of ServerVendor by calling from_dict on the json representation server_vendor_model = ServerVendor.from_dict(server_vendor_model_json) @@ -16993,7 +18153,18 @@ def test_server_vendor_serialization(self): server_vendor_model_json2 = server_vendor_model.to_dict() assert server_vendor_model_json2 == server_vendor_model_json -class TestModel_SessionAuthentication(): + # Test get_properties and set_properties methods. + server_vendor_model.set_properties({}) + actual_dict = server_vendor_model.get_properties() + assert actual_dict == {} + + expected_dict = {'foo': 'testString'} + server_vendor_model.set_properties(expected_dict) + actual_dict = server_vendor_model.get_properties() + assert actual_dict.keys() == expected_dict.keys() + + +class TestModel_SessionAuthentication: """ Test Class for SessionAuthentication """ @@ -17024,7 +18195,8 @@ def test_session_authentication_serialization(self): session_authentication_model_json2 = session_authentication_model.to_dict() assert session_authentication_model_json2 == session_authentication_model_json -class TestModel_SessionInformation(): + +class TestModel_SessionInformation: """ Test Class for SessionInformation """ @@ -17036,12 +18208,12 @@ def test_session_information_serialization(self): # Construct dict forms of any model objects needed in order to build this model. - session_authentication_model = {} # SessionAuthentication + session_authentication_model = {} # SessionAuthentication session_authentication_model['authenticated'] = 'testString' session_authentication_model['authentication_db'] = 'testString' session_authentication_model['authentication_handlers'] = ['testString'] - user_context_model = {} # UserContext + user_context_model = {} # UserContext user_context_model['db'] = 'testString' user_context_model['name'] = 'testString' user_context_model['roles'] = ['_reader'] @@ -17067,7 +18239,8 @@ def test_session_information_serialization(self): session_information_model_json2 = session_information_model.to_dict() assert session_information_model_json2 == session_information_model_json -class TestModel_ShardsInformation(): + +class TestModel_ShardsInformation: """ Test Class for ShardsInformation """ @@ -17096,7 +18269,8 @@ def test_shards_information_serialization(self): shards_information_model_json2 = shards_information_model.to_dict() assert shards_information_model_json2 == shards_information_model_json -class TestModel_ThroughputInformation(): + +class TestModel_ThroughputInformation: """ Test Class for ThroughputInformation """ @@ -17108,7 +18282,7 @@ def test_throughput_information_serialization(self): # Construct a json representation of a ThroughputInformation model throughput_information_model_json = {} - throughput_information_model_json['blocks'] = 0 + throughput_information_model_json['blocks'] = 1 throughput_information_model_json['query'] = 0 throughput_information_model_json['read'] = 0 throughput_information_model_json['write'] = 0 @@ -17128,7 +18302,8 @@ def test_throughput_information_serialization(self): throughput_information_model_json2 = throughput_information_model.to_dict() assert throughput_information_model_json2 == throughput_information_model_json -class TestModel_UpInformation(): + +class TestModel_UpInformation: """ Test Class for UpInformation """ @@ -17140,7 +18315,8 @@ def test_up_information_serialization(self): # Construct a json representation of a UpInformation model up_information_model_json = {} - up_information_model_json['seeds'] = {'foo': 'bar'} + up_information_model_json['cluster'] = 'testString' + up_information_model_json['seeds'] = {'anyKey': 'anyValue'} up_information_model_json['status'] = 'maintenance_mode' # Construct a model instance of UpInformation by calling from_dict on the json representation @@ -17158,7 +18334,40 @@ def test_up_information_serialization(self): up_information_model_json2 = up_information_model.to_dict() assert up_information_model_json2 == up_information_model_json -class TestModel_UserContext(): + +class TestModel_UpdatesPending: + """ + Test Class for UpdatesPending + """ + + def test_updates_pending_serialization(self): + """ + Test serialization/deserialization for UpdatesPending + """ + + # Construct a json representation of a UpdatesPending model + updates_pending_model_json = {} + updates_pending_model_json['minimum'] = 0 + updates_pending_model_json['preferred'] = 0 + updates_pending_model_json['total'] = 0 + + # Construct a model instance of UpdatesPending by calling from_dict on the json representation + updates_pending_model = UpdatesPending.from_dict(updates_pending_model_json) + assert updates_pending_model != False + + # Construct a model instance of UpdatesPending by calling from_dict on the json representation + updates_pending_model_dict = UpdatesPending.from_dict(updates_pending_model_json).__dict__ + updates_pending_model2 = UpdatesPending(**updates_pending_model_dict) + + # Verify the model instances are equivalent + assert updates_pending_model == updates_pending_model2 + + # Convert model instance back to dict and verify no loss of data + updates_pending_model_json2 = updates_pending_model.to_dict() + assert updates_pending_model_json2 == updates_pending_model_json + + +class TestModel_UserContext: """ Test Class for UserContext """ @@ -17189,7 +18398,8 @@ def test_user_context_serialization(self): user_context_model_json2 = user_context_model.to_dict() assert user_context_model_json2 == user_context_model_json -class TestModel_UuidsResult(): + +class TestModel_UuidsResult: """ Test Class for UuidsResult """ @@ -17218,7 +18428,8 @@ def test_uuids_result_serialization(self): uuids_result_model_json2 = uuids_result_model.to_dict() assert uuids_result_model_json2 == uuids_result_model_json -class TestModel_ViewQueriesResult(): + +class TestModel_ViewQueriesResult: """ Test Class for ViewQueriesResult """ @@ -17230,7 +18441,7 @@ def test_view_queries_result_serialization(self): # Construct dict forms of any model objects needed in order to build this model. - attachment_model = {} # Attachment + attachment_model = {} # Attachment attachment_model['content_type'] = 'testString' attachment_model['data'] = 'VGhpcyBpcyBhIG1vY2sgYnl0ZSBhcnJheSB2YWx1ZS4=' attachment_model['digest'] = 'testString' @@ -17241,15 +18452,15 @@ def test_view_queries_result_serialization(self): attachment_model['revpos'] = 1 attachment_model['stub'] = True - revisions_model = {} # Revisions + revisions_model = {} # Revisions revisions_model['ids'] = ['testString'] revisions_model['start'] = 1 - document_revision_status_model = {} # DocumentRevisionStatus + document_revision_status_model = {} # DocumentRevisionStatus document_revision_status_model['rev'] = 'testString' document_revision_status_model['status'] = 'available' - document_model = {} # Document + document_model = {} # Document document_model['_attachments'] = {'key1': attachment_model} document_model['_conflicts'] = ['testString'] document_model['_deleted'] = True @@ -17261,16 +18472,17 @@ def test_view_queries_result_serialization(self): document_model['_revs_info'] = [document_revision_status_model] document_model['foo'] = 'testString' - view_result_row_model = {} # ViewResultRow + view_result_row_model = {} # ViewResultRow view_result_row_model['caused_by'] = 'testString' view_result_row_model['error'] = 'testString' view_result_row_model['reason'] = 'testString' + view_result_row_model['ref'] = 0 view_result_row_model['doc'] = document_model view_result_row_model['id'] = 'testString' view_result_row_model['key'] = 'testString' view_result_row_model['value'] = 'testString' - view_result_model = {} # ViewResult + view_result_model = {} # ViewResult view_result_model['total_rows'] = 0 view_result_model['update_seq'] = 'testString' view_result_model['rows'] = [view_result_row_model] @@ -17294,7 +18506,8 @@ def test_view_queries_result_serialization(self): view_queries_result_model_json2 = view_queries_result_model.to_dict() assert view_queries_result_model_json2 == view_queries_result_model_json -class TestModel_ViewQuery(): + +class TestModel_ViewQuery: """ Test Class for ViewQuery """ @@ -17342,7 +18555,8 @@ def test_view_query_serialization(self): view_query_model_json2 = view_query_model.to_dict() assert view_query_model_json2 == view_query_model_json -class TestModel_ViewResult(): + +class TestModel_ViewResult: """ Test Class for ViewResult """ @@ -17354,7 +18568,7 @@ def test_view_result_serialization(self): # Construct dict forms of any model objects needed in order to build this model. - attachment_model = {} # Attachment + attachment_model = {} # Attachment attachment_model['content_type'] = 'testString' attachment_model['data'] = 'VGhpcyBpcyBhIG1vY2sgYnl0ZSBhcnJheSB2YWx1ZS4=' attachment_model['digest'] = 'testString' @@ -17365,15 +18579,15 @@ def test_view_result_serialization(self): attachment_model['revpos'] = 1 attachment_model['stub'] = True - revisions_model = {} # Revisions + revisions_model = {} # Revisions revisions_model['ids'] = ['testString'] revisions_model['start'] = 1 - document_revision_status_model = {} # DocumentRevisionStatus + document_revision_status_model = {} # DocumentRevisionStatus document_revision_status_model['rev'] = 'testString' document_revision_status_model['status'] = 'available' - document_model = {} # Document + document_model = {} # Document document_model['_attachments'] = {'key1': attachment_model} document_model['_conflicts'] = ['testString'] document_model['_deleted'] = True @@ -17385,10 +18599,11 @@ def test_view_result_serialization(self): document_model['_revs_info'] = [document_revision_status_model] document_model['foo'] = 'testString' - view_result_row_model = {} # ViewResultRow + view_result_row_model = {} # ViewResultRow view_result_row_model['caused_by'] = 'testString' view_result_row_model['error'] = 'testString' view_result_row_model['reason'] = 'testString' + view_result_row_model['ref'] = 0 view_result_row_model['doc'] = document_model view_result_row_model['id'] = 'testString' view_result_row_model['key'] = 'testString' @@ -17415,7 +18630,8 @@ def test_view_result_serialization(self): view_result_model_json2 = view_result_model.to_dict() assert view_result_model_json2 == view_result_model_json -class TestModel_ViewResultRow(): + +class TestModel_ViewResultRow: """ Test Class for ViewResultRow """ @@ -17427,7 +18643,7 @@ def test_view_result_row_serialization(self): # Construct dict forms of any model objects needed in order to build this model. - attachment_model = {} # Attachment + attachment_model = {} # Attachment attachment_model['content_type'] = 'testString' attachment_model['data'] = 'VGhpcyBpcyBhIG1vY2sgYnl0ZSBhcnJheSB2YWx1ZS4=' attachment_model['digest'] = 'testString' @@ -17438,15 +18654,15 @@ def test_view_result_row_serialization(self): attachment_model['revpos'] = 1 attachment_model['stub'] = True - revisions_model = {} # Revisions + revisions_model = {} # Revisions revisions_model['ids'] = ['testString'] revisions_model['start'] = 1 - document_revision_status_model = {} # DocumentRevisionStatus + document_revision_status_model = {} # DocumentRevisionStatus document_revision_status_model['rev'] = 'testString' document_revision_status_model['status'] = 'available' - document_model = {} # Document + document_model = {} # Document document_model['_attachments'] = {'key1': attachment_model} document_model['_conflicts'] = ['testString'] document_model['_deleted'] = True @@ -17463,6 +18679,7 @@ def test_view_result_row_serialization(self): view_result_row_model_json['caused_by'] = 'testString' view_result_row_model_json['error'] = 'testString' view_result_row_model_json['reason'] = 'testString' + view_result_row_model_json['ref'] = 0 view_result_row_model_json['doc'] = document_model view_result_row_model_json['id'] = 'testString' view_result_row_model_json['key'] = 'testString' @@ -17483,72 +18700,6 @@ def test_view_result_row_serialization(self): view_result_row_model_json2 = view_result_row_model.to_dict() assert view_result_row_model_json2 == view_result_row_model_json -class TestModel_GeoJsonGeometry(): - """ - Test Class for GeoJsonGeometry - """ - - def test_geo_json_geometry_serialization(self): - """ - Test serialization/deserialization for GeoJsonGeometry - """ - - # Construct a json representation of a GeoJsonGeometry model - geo_json_geometry_model_json = {} - geo_json_geometry_model_json['type'] = 'Point' - geo_json_geometry_model_json['coordinates'] = ['testString'] - - # Construct a model instance of GeoJsonGeometry by calling from_dict on the json representation - geo_json_geometry_model = GeoJsonGeometry.from_dict(geo_json_geometry_model_json) - assert geo_json_geometry_model != False - - # Construct a model instance of GeoJsonGeometry by calling from_dict on the json representation - geo_json_geometry_model_dict = GeoJsonGeometry.from_dict(geo_json_geometry_model_json).__dict__ - geo_json_geometry_model2 = GeoJsonGeometry(**geo_json_geometry_model_dict) - - # Verify the model instances are equivalent - assert geo_json_geometry_model == geo_json_geometry_model2 - - # Convert model instance back to dict and verify no loss of data - geo_json_geometry_model_json2 = geo_json_geometry_model.to_dict() - assert geo_json_geometry_model_json2 == geo_json_geometry_model_json - -class TestModel_GeoJsonGeometryCollection(): - """ - Test Class for GeoJsonGeometryCollection - """ - - def test_geo_json_geometry_collection_serialization(self): - """ - Test serialization/deserialization for GeoJsonGeometryCollection - """ - - # Construct dict forms of any model objects needed in order to build this model. - - geo_json_geometry_model = {} # GeoJsonGeometry - geo_json_geometry_model['type'] = 'Point' - geo_json_geometry_model['coordinates'] = ['testString'] - - # Construct a json representation of a GeoJsonGeometryCollection model - geo_json_geometry_collection_model_json = {} - geo_json_geometry_collection_model_json['type'] = 'Point' - geo_json_geometry_collection_model_json['geometries'] = [geo_json_geometry_model] - - # Construct a model instance of GeoJsonGeometryCollection by calling from_dict on the json representation - geo_json_geometry_collection_model = GeoJsonGeometryCollection.from_dict(geo_json_geometry_collection_model_json) - assert geo_json_geometry_collection_model != False - - # Construct a model instance of GeoJsonGeometryCollection by calling from_dict on the json representation - geo_json_geometry_collection_model_dict = GeoJsonGeometryCollection.from_dict(geo_json_geometry_collection_model_json).__dict__ - geo_json_geometry_collection_model2 = GeoJsonGeometryCollection(**geo_json_geometry_collection_model_dict) - - # Verify the model instances are equivalent - assert geo_json_geometry_collection_model == geo_json_geometry_collection_model2 - - # Convert model instance back to dict and verify no loss of data - geo_json_geometry_collection_model_json2 = geo_json_geometry_collection_model.to_dict() - assert geo_json_geometry_collection_model_json2 == geo_json_geometry_collection_model_json - # endregion ############################################################################## diff --git a/test/unit/test_couchdb_session_auth.py b/test/unit/test_couchdb_session_auth.py index 1c42842c..9285d9d2 100644 --- a/test/unit/test_couchdb_session_auth.py +++ b/test/unit/test_couchdb_session_auth.py @@ -1,6 +1,6 @@ # coding: utf-8 -# © Copyright IBM Corporation 2020, 2021. +# © Copyright IBM Corporation 2020, 2022. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -108,6 +108,32 @@ def test_disable_ssl_verification_off(self): finally: self.client.set_http_client(original_http_client) + def test_valid_disable_ssl_verification_type(self): + authenticator = CouchDbSessionAuthenticator("adm", "pass") + self.assertFalse(authenticator.token_manager.disable_ssl_verification) + authenticator = CouchDbSessionAuthenticator( + "adm", + "pass", + disable_ssl_verification=False + ) + self.assertFalse(authenticator.token_manager.disable_ssl_verification) + authenticator = CouchDbSessionAuthenticator( + "adm", + "pass", + disable_ssl_verification=True + ) + self.assertTrue(authenticator.token_manager.disable_ssl_verification) + + def test_invalid_disable_ssl_verification_type(self): + with self.assertRaisesRegex( + TypeError, 'disable_ssl_verification must be a bool' + ): + CouchDbSessionAuthenticator( + "adm", + "pass", + disable_ssl_verification='True' + ) + @responses.activate def test_cookie_refresh(self): self.client.get_session_information() @@ -168,6 +194,10 @@ def test_set_service_url(self): class TestCouchDbSessionAuthPatch(unittest.TestCase): + def setUp(self) -> None: + for key in os.environ: + del os.environ[key] + def test_new_instance(self): os.environ['TEST_SERVICE_AUTH_TYPE'] = 'couchdb_session' os.environ['TEST_SERVICE_USERNAME'] = 'adm' @@ -176,6 +206,9 @@ def test_new_instance(self): self.assertIsNotNone(service) self.assertIsInstance(service, CloudantV1) self.assertEqual('COUCHDB_SESSION', service.authenticator.authentication_type()) + self.assertFalse(service.disable_ssl_verification) + self.assertFalse( + service.authenticator.token_manager.disable_ssl_verification) def test_new_instance_auth_alias(self): os.environ['TEST_SERVICE_AUTHTYPE'] = 'couchdb_session' @@ -185,3 +218,16 @@ def test_new_instance_auth_alias(self): self.assertIsNotNone(service) self.assertIsInstance(service, CloudantV1) self.assertEqual('COUCHDB_SESSION', service.authenticator.authentication_type()) + + def test_disable_ssl(self): + os.environ['TEST_SERVICE_AUTHTYPE'] = 'couchdb_session' + os.environ['TEST_SERVICE_USERNAME'] = 'adm' + os.environ['TEST_SERVICE_PASSWORD'] = 'pass' + os.environ['TEST_SERVICE_DISABLE_SSL'] = 'true' + service = CloudantV1.new_instance(service_name='TEST_SERVICE') + self.assertIsNotNone(service) + self.assertIsInstance(service, CloudantV1) + self.assertEqual('COUCHDB_SESSION', service.authenticator.authentication_type()) + self.assertTrue(service.disable_ssl_verification) + self.assertTrue( + service.authenticator.token_manager.disable_ssl_verification) diff --git a/tox.ini b/tox.ini deleted file mode 100644 index 9c65c5db..00000000 --- a/tox.ini +++ /dev/null @@ -1,25 +0,0 @@ -[tox] -envlist = py39-lint, py37, py38, py39, py310 - -[testenv:py39-lint] -basepython = python3.9 -deps = pylint -commands = pylint --rcfile=.pylintrc ibmcloudant test -setenv = PYTHONPATH = pylint/checkers - -[testenv] -passenv = TOXENV CI TRAVIS* -commands = - py.test --reruns 3 -o junit_family=xunit2 --junitxml=junitreports/junit-{envname}.xml --cov=ibmcloudant {posargs} -deps = - -r{toxinidir}/requirements.txt - -r{toxinidir}/requirements-dev.txt -usedevelop = True -exclude = .venv,.git,.tox,docs - -[testenv:docs] -description = invoke sphinx-build to build the HTML docs -deps = {[testenv]deps} -basepython = python3.10 -commands = sphinx-apidoc -o {toxinidir}/source ibmcloudant "**/common.py" "**/version.py" "**/cloudant_base_service.py" "**/couchdb_session_get_authenticator_patch.py" - sphinx-build -d "{toxinidir}/docs_doctree" {toxinidir}/source "{toxinidir}/apidocs" --color -bhtml