From 18d97c183b8062f9ed700547e019caca94fdc2f1 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Wed, 10 Dec 2025 10:17:16 +0000 Subject: [PATCH 01/62] chore: configure new SDK language --- .devcontainer/Dockerfile | 9 + .devcontainer/devcontainer.json | 43 + .github/workflows/ci.yml | 98 + .gitignore | 15 + .python-version | 1 + .stats.yml | 4 + .vscode/settings.json | 3 + Brewfile | 2 + CONTRIBUTING.md | 128 ++ LICENSE | 201 ++ README.md | 368 ++- SECURITY.md | 23 + api.md | 181 ++ bin/publish-pypi | 6 + examples/.keep | 4 + noxfile.py | 9 + pyproject.toml | 269 +++ requirements-dev.lock | 149 ++ requirements.lock | 76 + scripts/bootstrap | 27 + scripts/format | 8 + scripts/lint | 11 + scripts/mock | 41 + scripts/test | 61 + scripts/utils/ruffen-docs.py | 167 ++ scripts/utils/upload-artifact.sh | 27 + src/unlayer/__init__.py | 92 + src/unlayer/_base_client.py | 1995 +++++++++++++++++ src/unlayer/_client.py | 450 ++++ src/unlayer/_compat.py | 219 ++ src/unlayer/_constants.py | 14 + src/unlayer/_exceptions.py | 108 + src/unlayer/_files.py | 123 + src/unlayer/_models.py | 857 +++++++ src/unlayer/_qs.py | 150 ++ src/unlayer/_resource.py | 43 + src/unlayer/_response.py | 830 +++++++ src/unlayer/_streaming.py | 333 +++ src/unlayer/_types.py | 261 +++ src/unlayer/_utils/__init__.py | 64 + src/unlayer/_utils/_compat.py | 45 + src/unlayer/_utils/_datetime_parse.py | 136 ++ src/unlayer/_utils/_logs.py | 25 + src/unlayer/_utils/_proxy.py | 65 + src/unlayer/_utils/_reflection.py | 42 + src/unlayer/_utils/_resources_proxy.py | 24 + src/unlayer/_utils/_streams.py | 12 + src/unlayer/_utils/_sync.py | 58 + src/unlayer/_utils/_transform.py | 457 ++++ src/unlayer/_utils/_typing.py | 156 ++ src/unlayer/_utils/_utils.py | 421 ++++ src/unlayer/_version.py | 4 + src/unlayer/lib/.keep | 4 + src/unlayer/py.typed | 0 src/unlayer/resources/__init__.py | 117 + src/unlayer/resources/documents.py | 397 ++++ src/unlayer/resources/documents_v1.py | 397 ++++ src/unlayer/resources/emails.py | 504 +++++ src/unlayer/resources/emails_v1.py | 508 +++++ src/unlayer/resources/pages.py | 187 ++ src/unlayer/resources/pages_v1.py | 187 ++ src/unlayer/resources/project.py | 1378 ++++++++++++ src/unlayer/resources/project_v1.py | 1378 ++++++++++++ src/unlayer/types/__init__.py | 90 + .../document_documents_retrieve_response.py | 37 + .../types/document_generate_create_params.py | 27 + .../document_generate_create_response.py | 23 + ...ument_generate_template_template_params.py | 21 + ...ent_generate_template_template_response.py | 23 + ...ocuments_v1_documents_retrieve_response.py | 37 + .../documents_v1_generate_create_params.py | 27 + .../documents_v1_generate_create_response.py | 23 + ...ts_v1_generate_template_template_params.py | 21 + ..._v1_generate_template_template_response.py | 23 + .../types/email_emails_retrieve_response.py | 31 + .../types/email_render_create_params.py | 18 + .../types/email_render_create_response.py | 12 + src/unlayer/types/email_send_create_params.py | 27 + .../types/email_send_create_response.py | 17 + .../email_send_template_template_params.py | 24 + .../email_send_template_template_response.py | 17 + .../emails_v1_emails_retrieve_response.py | 31 + .../types/emails_v1_render_create_params.py | 18 + .../types/emails_v1_render_create_response.py | 12 + .../types/emails_v1_send_create_params.py | 27 + .../types/emails_v1_send_create_response.py | 17 + ...emails_v1_send_template_template_params.py | 24 + ...ails_v1_send_template_template_response.py | 17 + .../types/page_render_create_params.py | 18 + .../types/page_render_create_response.py | 12 + .../types/pages_v1_render_create_params.py | 18 + .../types/pages_v1_render_create_response.py | 12 + .../types/project_api_keys_create_params.py | 17 + .../types/project_api_keys_create_response.py | 28 + .../types/project_api_keys_list_response.py | 30 + .../project_api_keys_retrieve_response.py | 30 + .../types/project_api_keys_update_params.py | 20 + .../types/project_api_keys_update_response.py | 30 + .../types/project_current_list_response.py | 32 + .../types/project_domains_create_params.py | 12 + .../types/project_domains_create_response.py | 26 + .../types/project_domains_list_response.py | 27 + .../project_domains_retrieve_response.py | 26 + .../types/project_domains_update_params.py | 12 + .../types/project_domains_update_response.py | 26 + .../types/project_templates_create_params.py | 18 + .../project_templates_create_response.py | 28 + .../types/project_templates_list_response.py | 28 + .../project_templates_retrieve_response.py | 28 + .../types/project_templates_update_params.py | 18 + .../project_templates_update_response.py | 28 + .../project_v1_api_keys_create_params.py | 17 + .../project_v1_api_keys_create_response.py | 28 + .../project_v1_api_keys_list_response.py | 30 + .../project_v1_api_keys_retrieve_response.py | 30 + .../project_v1_api_keys_update_params.py | 20 + .../project_v1_api_keys_update_response.py | 30 + .../types/project_v1_current_list_response.py | 32 + .../types/project_v1_domains_create_params.py | 12 + .../project_v1_domains_create_response.py | 26 + .../types/project_v1_domains_list_response.py | 27 + .../project_v1_domains_retrieve_response.py | 26 + .../types/project_v1_domains_update_params.py | 12 + .../project_v1_domains_update_response.py | 26 + .../project_v1_templates_create_params.py | 18 + .../project_v1_templates_create_response.py | 28 + .../project_v1_templates_list_response.py | 28 + .../project_v1_templates_retrieve_response.py | 28 + .../project_v1_templates_update_params.py | 18 + .../project_v1_templates_update_response.py | 28 + tests/__init__.py | 1 + tests/api_resources/__init__.py | 1 + tests/api_resources/test_documents.py | 292 +++ tests/api_resources/test_documents_v1.py | 292 +++ tests/api_resources/test_emails.py | 409 ++++ tests/api_resources/test_emails_v1.py | 409 ++++ tests/api_resources/test_pages.py | 126 ++ tests/api_resources/test_pages_v1.py | 126 ++ tests/api_resources/test_project.py | 1198 ++++++++++ tests/api_resources/test_project_v1.py | 1198 ++++++++++ tests/conftest.py | 84 + tests/sample_file.txt | 1 + tests/test_client.py | 1717 ++++++++++++++ tests/test_deepcopy.py | 58 + tests/test_extract_files.py | 64 + tests/test_files.py | 51 + tests/test_models.py | 963 ++++++++ tests/test_qs.py | 78 + tests/test_required_args.py | 111 + tests/test_response.py | 277 +++ tests/test_streaming.py | 248 ++ tests/test_transform.py | 460 ++++ tests/test_utils/test_datetime_parse.py | 110 + tests/test_utils/test_proxy.py | 34 + tests/test_utils/test_typing.py | 73 + tests/utils.py | 167 ++ 156 files changed, 24151 insertions(+), 1 deletion(-) create mode 100644 .devcontainer/Dockerfile create mode 100644 .devcontainer/devcontainer.json create mode 100644 .github/workflows/ci.yml create mode 100644 .gitignore create mode 100644 .python-version create mode 100644 .stats.yml create mode 100644 .vscode/settings.json create mode 100644 Brewfile create mode 100644 CONTRIBUTING.md create mode 100644 LICENSE create mode 100644 SECURITY.md create mode 100644 api.md create mode 100644 bin/publish-pypi create mode 100644 examples/.keep create mode 100644 noxfile.py create mode 100644 pyproject.toml create mode 100644 requirements-dev.lock create mode 100644 requirements.lock create mode 100755 scripts/bootstrap create mode 100755 scripts/format create mode 100755 scripts/lint create mode 100755 scripts/mock create mode 100755 scripts/test create mode 100644 scripts/utils/ruffen-docs.py create mode 100755 scripts/utils/upload-artifact.sh create mode 100644 src/unlayer/__init__.py create mode 100644 src/unlayer/_base_client.py create mode 100644 src/unlayer/_client.py create mode 100644 src/unlayer/_compat.py create mode 100644 src/unlayer/_constants.py create mode 100644 src/unlayer/_exceptions.py create mode 100644 src/unlayer/_files.py create mode 100644 src/unlayer/_models.py create mode 100644 src/unlayer/_qs.py create mode 100644 src/unlayer/_resource.py create mode 100644 src/unlayer/_response.py create mode 100644 src/unlayer/_streaming.py create mode 100644 src/unlayer/_types.py create mode 100644 src/unlayer/_utils/__init__.py create mode 100644 src/unlayer/_utils/_compat.py create mode 100644 src/unlayer/_utils/_datetime_parse.py create mode 100644 src/unlayer/_utils/_logs.py create mode 100644 src/unlayer/_utils/_proxy.py create mode 100644 src/unlayer/_utils/_reflection.py create mode 100644 src/unlayer/_utils/_resources_proxy.py create mode 100644 src/unlayer/_utils/_streams.py create mode 100644 src/unlayer/_utils/_sync.py create mode 100644 src/unlayer/_utils/_transform.py create mode 100644 src/unlayer/_utils/_typing.py create mode 100644 src/unlayer/_utils/_utils.py create mode 100644 src/unlayer/_version.py create mode 100644 src/unlayer/lib/.keep create mode 100644 src/unlayer/py.typed create mode 100644 src/unlayer/resources/__init__.py create mode 100644 src/unlayer/resources/documents.py create mode 100644 src/unlayer/resources/documents_v1.py create mode 100644 src/unlayer/resources/emails.py create mode 100644 src/unlayer/resources/emails_v1.py create mode 100644 src/unlayer/resources/pages.py create mode 100644 src/unlayer/resources/pages_v1.py create mode 100644 src/unlayer/resources/project.py create mode 100644 src/unlayer/resources/project_v1.py create mode 100644 src/unlayer/types/__init__.py create mode 100644 src/unlayer/types/document_documents_retrieve_response.py create mode 100644 src/unlayer/types/document_generate_create_params.py create mode 100644 src/unlayer/types/document_generate_create_response.py create mode 100644 src/unlayer/types/document_generate_template_template_params.py create mode 100644 src/unlayer/types/document_generate_template_template_response.py create mode 100644 src/unlayer/types/documents_v1_documents_retrieve_response.py create mode 100644 src/unlayer/types/documents_v1_generate_create_params.py create mode 100644 src/unlayer/types/documents_v1_generate_create_response.py create mode 100644 src/unlayer/types/documents_v1_generate_template_template_params.py create mode 100644 src/unlayer/types/documents_v1_generate_template_template_response.py create mode 100644 src/unlayer/types/email_emails_retrieve_response.py create mode 100644 src/unlayer/types/email_render_create_params.py create mode 100644 src/unlayer/types/email_render_create_response.py create mode 100644 src/unlayer/types/email_send_create_params.py create mode 100644 src/unlayer/types/email_send_create_response.py create mode 100644 src/unlayer/types/email_send_template_template_params.py create mode 100644 src/unlayer/types/email_send_template_template_response.py create mode 100644 src/unlayer/types/emails_v1_emails_retrieve_response.py create mode 100644 src/unlayer/types/emails_v1_render_create_params.py create mode 100644 src/unlayer/types/emails_v1_render_create_response.py create mode 100644 src/unlayer/types/emails_v1_send_create_params.py create mode 100644 src/unlayer/types/emails_v1_send_create_response.py create mode 100644 src/unlayer/types/emails_v1_send_template_template_params.py create mode 100644 src/unlayer/types/emails_v1_send_template_template_response.py create mode 100644 src/unlayer/types/page_render_create_params.py create mode 100644 src/unlayer/types/page_render_create_response.py create mode 100644 src/unlayer/types/pages_v1_render_create_params.py create mode 100644 src/unlayer/types/pages_v1_render_create_response.py create mode 100644 src/unlayer/types/project_api_keys_create_params.py create mode 100644 src/unlayer/types/project_api_keys_create_response.py create mode 100644 src/unlayer/types/project_api_keys_list_response.py create mode 100644 src/unlayer/types/project_api_keys_retrieve_response.py create mode 100644 src/unlayer/types/project_api_keys_update_params.py create mode 100644 src/unlayer/types/project_api_keys_update_response.py create mode 100644 src/unlayer/types/project_current_list_response.py create mode 100644 src/unlayer/types/project_domains_create_params.py create mode 100644 src/unlayer/types/project_domains_create_response.py create mode 100644 src/unlayer/types/project_domains_list_response.py create mode 100644 src/unlayer/types/project_domains_retrieve_response.py create mode 100644 src/unlayer/types/project_domains_update_params.py create mode 100644 src/unlayer/types/project_domains_update_response.py create mode 100644 src/unlayer/types/project_templates_create_params.py create mode 100644 src/unlayer/types/project_templates_create_response.py create mode 100644 src/unlayer/types/project_templates_list_response.py create mode 100644 src/unlayer/types/project_templates_retrieve_response.py create mode 100644 src/unlayer/types/project_templates_update_params.py create mode 100644 src/unlayer/types/project_templates_update_response.py create mode 100644 src/unlayer/types/project_v1_api_keys_create_params.py create mode 100644 src/unlayer/types/project_v1_api_keys_create_response.py create mode 100644 src/unlayer/types/project_v1_api_keys_list_response.py create mode 100644 src/unlayer/types/project_v1_api_keys_retrieve_response.py create mode 100644 src/unlayer/types/project_v1_api_keys_update_params.py create mode 100644 src/unlayer/types/project_v1_api_keys_update_response.py create mode 100644 src/unlayer/types/project_v1_current_list_response.py create mode 100644 src/unlayer/types/project_v1_domains_create_params.py create mode 100644 src/unlayer/types/project_v1_domains_create_response.py create mode 100644 src/unlayer/types/project_v1_domains_list_response.py create mode 100644 src/unlayer/types/project_v1_domains_retrieve_response.py create mode 100644 src/unlayer/types/project_v1_domains_update_params.py create mode 100644 src/unlayer/types/project_v1_domains_update_response.py create mode 100644 src/unlayer/types/project_v1_templates_create_params.py create mode 100644 src/unlayer/types/project_v1_templates_create_response.py create mode 100644 src/unlayer/types/project_v1_templates_list_response.py create mode 100644 src/unlayer/types/project_v1_templates_retrieve_response.py create mode 100644 src/unlayer/types/project_v1_templates_update_params.py create mode 100644 src/unlayer/types/project_v1_templates_update_response.py create mode 100644 tests/__init__.py create mode 100644 tests/api_resources/__init__.py create mode 100644 tests/api_resources/test_documents.py create mode 100644 tests/api_resources/test_documents_v1.py create mode 100644 tests/api_resources/test_emails.py create mode 100644 tests/api_resources/test_emails_v1.py create mode 100644 tests/api_resources/test_pages.py create mode 100644 tests/api_resources/test_pages_v1.py create mode 100644 tests/api_resources/test_project.py create mode 100644 tests/api_resources/test_project_v1.py create mode 100644 tests/conftest.py create mode 100644 tests/sample_file.txt create mode 100644 tests/test_client.py create mode 100644 tests/test_deepcopy.py create mode 100644 tests/test_extract_files.py create mode 100644 tests/test_files.py create mode 100644 tests/test_models.py create mode 100644 tests/test_qs.py create mode 100644 tests/test_required_args.py create mode 100644 tests/test_response.py create mode 100644 tests/test_streaming.py create mode 100644 tests/test_transform.py create mode 100644 tests/test_utils/test_datetime_parse.py create mode 100644 tests/test_utils/test_proxy.py create mode 100644 tests/test_utils/test_typing.py create mode 100644 tests/utils.py diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile new file mode 100644 index 0000000..ff261ba --- /dev/null +++ b/.devcontainer/Dockerfile @@ -0,0 +1,9 @@ +ARG VARIANT="3.9" +FROM mcr.microsoft.com/vscode/devcontainers/python:0-${VARIANT} + +USER vscode + +RUN curl -sSf https://rye.astral.sh/get | RYE_VERSION="0.44.0" RYE_INSTALL_OPTION="--yes" bash +ENV PATH=/home/vscode/.rye/shims:$PATH + +RUN echo "[[ -d .venv ]] && source .venv/bin/activate || export PATH=\$PATH" >> /home/vscode/.bashrc diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json new file mode 100644 index 0000000..c17fdc1 --- /dev/null +++ b/.devcontainer/devcontainer.json @@ -0,0 +1,43 @@ +// For format details, see https://aka.ms/devcontainer.json. For config options, see the +// README at: https://github.com/devcontainers/templates/tree/main/src/debian +{ + "name": "Debian", + "build": { + "dockerfile": "Dockerfile", + "context": ".." + }, + + "postStartCommand": "rye sync --all-features", + + "customizations": { + "vscode": { + "extensions": [ + "ms-python.python" + ], + "settings": { + "terminal.integrated.shell.linux": "/bin/bash", + "python.pythonPath": ".venv/bin/python", + "python.defaultInterpreterPath": ".venv/bin/python", + "python.typeChecking": "basic", + "terminal.integrated.env.linux": { + "PATH": "/home/vscode/.rye/shims:${env:PATH}" + } + } + } + }, + "features": { + "ghcr.io/devcontainers/features/node:1": {} + } + + // Features to add to the dev container. More info: https://containers.dev/features. + // "features": {}, + + // Use 'forwardPorts' to make a list of ports inside the container available locally. + // "forwardPorts": [], + + // Configure tool-specific properties. + // "customizations": {}, + + // Uncomment to connect as root instead. More info: https://aka.ms/dev-containers-non-root. + // "remoteUser": "root" +} diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..66bf079 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,98 @@ +name: CI +on: + push: + branches-ignore: + - 'generated' + - 'codegen/**' + - 'integrated/**' + - 'stl-preview-head/**' + - 'stl-preview-base/**' + pull_request: + branches-ignore: + - 'stl-preview-head/**' + - 'stl-preview-base/**' + +jobs: + lint: + timeout-minutes: 10 + name: lint + runs-on: ${{ github.repository == 'stainless-sdks/unlayer-python' && 'depot-ubuntu-24.04' || 'ubuntu-latest' }} + if: github.event_name == 'push' || github.event.pull_request.head.repo.fork + steps: + - uses: actions/checkout@v4 + + - name: Install Rye + run: | + curl -sSf https://rye.astral.sh/get | bash + echo "$HOME/.rye/shims" >> $GITHUB_PATH + env: + RYE_VERSION: '0.44.0' + RYE_INSTALL_OPTION: '--yes' + + - name: Install dependencies + run: rye sync --all-features + + - name: Run lints + run: ./scripts/lint + + build: + if: github.event_name == 'push' || github.event.pull_request.head.repo.fork + timeout-minutes: 10 + name: build + permissions: + contents: read + id-token: write + runs-on: ${{ github.repository == 'stainless-sdks/unlayer-python' && 'depot-ubuntu-24.04' || 'ubuntu-latest' }} + steps: + - uses: actions/checkout@v4 + + - name: Install Rye + run: | + curl -sSf https://rye.astral.sh/get | bash + echo "$HOME/.rye/shims" >> $GITHUB_PATH + env: + RYE_VERSION: '0.44.0' + RYE_INSTALL_OPTION: '--yes' + + - name: Install dependencies + run: rye sync --all-features + + - name: Run build + run: rye build + + - name: Get GitHub OIDC Token + if: github.repository == 'stainless-sdks/unlayer-python' + id: github-oidc + uses: actions/github-script@v6 + with: + script: core.setOutput('github_token', await core.getIDToken()); + + - name: Upload tarball + if: github.repository == 'stainless-sdks/unlayer-python' + env: + URL: https://pkg.stainless.com/s + AUTH: ${{ steps.github-oidc.outputs.github_token }} + SHA: ${{ github.sha }} + run: ./scripts/utils/upload-artifact.sh + + test: + timeout-minutes: 10 + name: test + runs-on: ${{ github.repository == 'stainless-sdks/unlayer-python' && 'depot-ubuntu-24.04' || 'ubuntu-latest' }} + if: github.event_name == 'push' || github.event.pull_request.head.repo.fork + steps: + - uses: actions/checkout@v4 + + - name: Install Rye + run: | + curl -sSf https://rye.astral.sh/get | bash + echo "$HOME/.rye/shims" >> $GITHUB_PATH + env: + RYE_VERSION: '0.44.0' + RYE_INSTALL_OPTION: '--yes' + + - name: Bootstrap + run: ./scripts/bootstrap + + - name: Run tests + run: ./scripts/test diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..95ceb18 --- /dev/null +++ b/.gitignore @@ -0,0 +1,15 @@ +.prism.log +_dev + +__pycache__ +.mypy_cache + +dist + +.venv +.idea + +.env +.envrc +codegen.log +Brewfile.lock.json diff --git a/.python-version b/.python-version new file mode 100644 index 0000000..43077b2 --- /dev/null +++ b/.python-version @@ -0,0 +1 @@ +3.9.18 diff --git a/.stats.yml b/.stats.yml new file mode 100644 index 0000000..4b2f990 --- /dev/null +++ b/.stats.yml @@ -0,0 +1,4 @@ +configured_endpoints: 24 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/unlayer%2Funlayer-36e6b164f4fc145ef8b0068290d8ffa479dbfadb8fe11263d6ccd49ea0dd4a22.yml +openapi_spec_hash: b4022090026a2d0b27e2c2c19e6581b2 +config_hash: 256da0d3adcdb5fa292234ddd8247f58 diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..5b01030 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,3 @@ +{ + "python.analysis.importFormat": "relative", +} diff --git a/Brewfile b/Brewfile new file mode 100644 index 0000000..492ca37 --- /dev/null +++ b/Brewfile @@ -0,0 +1,2 @@ +brew "rye" + diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..8bc8eee --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,128 @@ +## Setting up the environment + +### With Rye + +We use [Rye](https://rye.astral.sh/) to manage dependencies because it will automatically provision a Python environment with the expected Python version. To set it up, run: + +```sh +$ ./scripts/bootstrap +``` + +Or [install Rye manually](https://rye.astral.sh/guide/installation/) and run: + +```sh +$ rye sync --all-features +``` + +You can then run scripts using `rye run python script.py` or by activating the virtual environment: + +```sh +# Activate the virtual environment - https://docs.python.org/3/library/venv.html#how-venvs-work +$ source .venv/bin/activate + +# now you can omit the `rye run` prefix +$ python script.py +``` + +### Without Rye + +Alternatively if you don't want to install `Rye`, you can stick with the standard `pip` setup by ensuring you have the Python version specified in `.python-version`, create a virtual environment however you desire and then install dependencies using this command: + +```sh +$ pip install -r requirements-dev.lock +``` + +## Modifying/Adding code + +Most of the SDK is generated code. Modifications to code will be persisted between generations, but may +result in merge conflicts between manual patches and changes from the generator. The generator will never +modify the contents of the `src/unlayer/lib/` and `examples/` directories. + +## Adding and running examples + +All files in the `examples/` directory are not modified by the generator and can be freely edited or added to. + +```py +# add an example to examples/.py + +#!/usr/bin/env -S rye run python +… +``` + +```sh +$ chmod +x examples/.py +# run the example against your api +$ ./examples/.py +``` + +## Using the repository from source + +If you’d like to use the repository from source, you can either install from git or link to a cloned repository: + +To install via git: + +```sh +$ pip install git+ssh://git@github.com/stainless-sdks/unlayer-python.git +``` + +Alternatively, you can build from source and install the wheel file: + +Building this package will create two files in the `dist/` directory, a `.tar.gz` containing the source files and a `.whl` that can be used to install the package efficiently. + +To create a distributable version of the library, all you have to do is run this command: + +```sh +$ rye build +# or +$ python -m build +``` + +Then to install: + +```sh +$ pip install ./path-to-wheel-file.whl +``` + +## Running tests + +Most tests require you to [set up a mock server](https://github.com/stoplightio/prism) against the OpenAPI spec to run the tests. + +```sh +# you will need npm installed +$ npx prism mock path/to/your/openapi.yml +``` + +```sh +$ ./scripts/test +``` + +## Linting and formatting + +This repository uses [ruff](https://github.com/astral-sh/ruff) and +[black](https://github.com/psf/black) to format the code in the repository. + +To lint: + +```sh +$ ./scripts/lint +``` + +To format and fix all ruff issues automatically: + +```sh +$ ./scripts/format +``` + +## Publishing and releases + +Changes made to this repository via the automated release PR pipeline should publish to PyPI automatically. If +the changes aren't made through the automated pipeline, you may want to make releases manually. + +### Publish with a GitHub workflow + +You can release to package managers by using [the `Publish PyPI` GitHub action](https://www.github.com/stainless-sdks/unlayer-python/actions/workflows/publish-pypi.yml). This requires a setup organization or repository secret to be set up. + +### Publish manually + +If you need to manually release a package, you can run the `bin/publish-pypi` script with a `PYPI_TOKEN` set on +the environment. diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..42d0669 --- /dev/null +++ b/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright 2025 Unlayer + + 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/README.md b/README.md index ef044cb..8c44d3b 100644 --- a/README.md +++ b/README.md @@ -1 +1,367 @@ -# unlayer-python \ No newline at end of file +# Unlayer Python API library + + +[![PyPI version](https://img.shields.io/pypi/v/unlayer.svg?label=pypi%20(stable))](https://pypi.org/project/unlayer/) + +The Unlayer Python library provides convenient access to the Unlayer REST API from any Python 3.9+ +application. The library includes type definitions for all request params and response fields, +and offers both synchronous and asynchronous clients powered by [httpx](https://github.com/encode/httpx). + +It is generated with [Stainless](https://www.stainless.com/). + +## Documentation + +The full API of this library can be found in [api.md](api.md). + +## Installation + +```sh +# install from this staging repo +pip install git+ssh://git@github.com/stainless-sdks/unlayer-python.git +``` + +> [!NOTE] +> Once this package is [published to PyPI](https://www.stainless.com/docs/guides/publish), this will become: `pip install unlayer` + +## Usage + +The full API of this library can be found in [api.md](api.md). + +```python +import os +from unlayer import Unlayer + +client = Unlayer( + api_key=os.environ.get("UNLAYER_API_KEY"), # This is the default and can be omitted +) + +response = client.project_v1.current_list() +print(response.data) +``` + +While you can provide an `api_key` keyword argument, +we recommend using [python-dotenv](https://pypi.org/project/python-dotenv/) +to add `UNLAYER_API_KEY="My API Key"` to your `.env` file +so that your API Key is not stored in source control. + +## Async usage + +Simply import `AsyncUnlayer` instead of `Unlayer` and use `await` with each API call: + +```python +import os +import asyncio +from unlayer import AsyncUnlayer + +client = AsyncUnlayer( + api_key=os.environ.get("UNLAYER_API_KEY"), # This is the default and can be omitted +) + + +async def main() -> None: + response = await client.project_v1.current_list() + print(response.data) + + +asyncio.run(main()) +``` + +Functionality between the synchronous and asynchronous clients is otherwise identical. + +### With aiohttp + +By default, the async client uses `httpx` for HTTP requests. However, for improved concurrency performance you may also use `aiohttp` as the HTTP backend. + +You can enable this by installing `aiohttp`: + +```sh +# install from this staging repo +pip install 'unlayer[aiohttp] @ git+ssh://git@github.com/stainless-sdks/unlayer-python.git' +``` + +Then you can enable it by instantiating the client with `http_client=DefaultAioHttpClient()`: + +```python +import os +import asyncio +from unlayer import DefaultAioHttpClient +from unlayer import AsyncUnlayer + + +async def main() -> None: + async with AsyncUnlayer( + api_key=os.environ.get("UNLAYER_API_KEY"), # This is the default and can be omitted + http_client=DefaultAioHttpClient(), + ) as client: + response = await client.project_v1.current_list() + print(response.data) + + +asyncio.run(main()) +``` + +## Using types + +Nested request parameters are [TypedDicts](https://docs.python.org/3/library/typing.html#typing.TypedDict). Responses are [Pydantic models](https://docs.pydantic.dev) which also provide helper methods for things like: + +- Serializing back into JSON, `model.to_json()` +- Converting to a dictionary, `model.to_dict()` + +Typed requests and responses provide autocomplete and documentation within your editor. If you would like to see type errors in VS Code to help catch bugs earlier, set `python.analysis.typeCheckingMode` to `basic`. + +## Handling errors + +When the library is unable to connect to the API (for example, due to network connection problems or a timeout), a subclass of `unlayer.APIConnectionError` is raised. + +When the API returns a non-success status code (that is, 4xx or 5xx +response), a subclass of `unlayer.APIStatusError` is raised, containing `status_code` and `response` properties. + +All errors inherit from `unlayer.APIError`. + +```python +import unlayer +from unlayer import Unlayer + +client = Unlayer() + +try: + client.project_v1.current_list() +except unlayer.APIConnectionError as e: + print("The server could not be reached") + print(e.__cause__) # an underlying Exception, likely raised within httpx. +except unlayer.RateLimitError as e: + print("A 429 status code was received; we should back off a bit.") +except unlayer.APIStatusError as e: + print("Another non-200-range status code was received") + print(e.status_code) + print(e.response) +``` + +Error codes are as follows: + +| Status Code | Error Type | +| ----------- | -------------------------- | +| 400 | `BadRequestError` | +| 401 | `AuthenticationError` | +| 403 | `PermissionDeniedError` | +| 404 | `NotFoundError` | +| 422 | `UnprocessableEntityError` | +| 429 | `RateLimitError` | +| >=500 | `InternalServerError` | +| N/A | `APIConnectionError` | + +### Retries + +Certain errors are automatically retried 2 times by default, with a short exponential backoff. +Connection errors (for example, due to a network connectivity problem), 408 Request Timeout, 409 Conflict, +429 Rate Limit, and >=500 Internal errors are all retried by default. + +You can use the `max_retries` option to configure or disable retry settings: + +```python +from unlayer import Unlayer + +# Configure the default for all requests: +client = Unlayer( + # default is 2 + max_retries=0, +) + +# Or, configure per-request: +client.with_options(max_retries=5).project_v1.current_list() +``` + +### Timeouts + +By default requests time out after 1 minute. You can configure this with a `timeout` option, +which accepts a float or an [`httpx.Timeout`](https://www.python-httpx.org/advanced/timeouts/#fine-tuning-the-configuration) object: + +```python +from unlayer import Unlayer + +# Configure the default for all requests: +client = Unlayer( + # 20 seconds (default is 1 minute) + timeout=20.0, +) + +# More granular control: +client = Unlayer( + timeout=httpx.Timeout(60.0, read=5.0, write=10.0, connect=2.0), +) + +# Override per-request: +client.with_options(timeout=5.0).project_v1.current_list() +``` + +On timeout, an `APITimeoutError` is thrown. + +Note that requests that time out are [retried twice by default](#retries). + +## Advanced + +### Logging + +We use the standard library [`logging`](https://docs.python.org/3/library/logging.html) module. + +You can enable logging by setting the environment variable `UNLAYER_LOG` to `info`. + +```shell +$ export UNLAYER_LOG=info +``` + +Or to `debug` for more verbose logging. + +### How to tell whether `None` means `null` or missing + +In an API response, a field may be explicitly `null`, or missing entirely; in either case, its value is `None` in this library. You can differentiate the two cases with `.model_fields_set`: + +```py +if response.my_field is None: + if 'my_field' not in response.model_fields_set: + print('Got json like {}, without a "my_field" key present at all.') + else: + print('Got json like {"my_field": null}.') +``` + +### Accessing raw response data (e.g. headers) + +The "raw" Response object can be accessed by prefixing `.with_raw_response.` to any HTTP method call, e.g., + +```py +from unlayer import Unlayer + +client = Unlayer() +response = client.project_v1.with_raw_response.current_list() +print(response.headers.get('X-My-Header')) + +project_v1 = response.parse() # get the object that `project_v1.current_list()` would have returned +print(project_v1.data) +``` + +These methods return an [`APIResponse`](https://github.com/stainless-sdks/unlayer-python/tree/main/src/unlayer/_response.py) object. + +The async client returns an [`AsyncAPIResponse`](https://github.com/stainless-sdks/unlayer-python/tree/main/src/unlayer/_response.py) with the same structure, the only difference being `await`able methods for reading the response content. + +#### `.with_streaming_response` + +The above interface eagerly reads the full response body when you make the request, which may not always be what you want. + +To stream the response body, use `.with_streaming_response` instead, which requires a context manager and only reads the response body once you call `.read()`, `.text()`, `.json()`, `.iter_bytes()`, `.iter_text()`, `.iter_lines()` or `.parse()`. In the async client, these are async methods. + +```python +with client.project_v1.with_streaming_response.current_list() as response: + print(response.headers.get("X-My-Header")) + + for line in response.iter_lines(): + print(line) +``` + +The context manager is required so that the response will reliably be closed. + +### Making custom/undocumented requests + +This library is typed for convenient access to the documented API. + +If you need to access undocumented endpoints, params, or response properties, the library can still be used. + +#### Undocumented endpoints + +To make requests to undocumented endpoints, you can make requests using `client.get`, `client.post`, and other +http verbs. Options on the client will be respected (such as retries) when making this request. + +```py +import httpx + +response = client.post( + "/foo", + cast_to=httpx.Response, + body={"my_param": True}, +) + +print(response.headers.get("x-foo")) +``` + +#### Undocumented request params + +If you want to explicitly send an extra param, you can do so with the `extra_query`, `extra_body`, and `extra_headers` request +options. + +#### Undocumented response properties + +To access undocumented response properties, you can access the extra fields like `response.unknown_prop`. You +can also get all the extra fields on the Pydantic model as a dict with +[`response.model_extra`](https://docs.pydantic.dev/latest/api/base_model/#pydantic.BaseModel.model_extra). + +### Configuring the HTTP client + +You can directly override the [httpx client](https://www.python-httpx.org/api/#client) to customize it for your use case, including: + +- Support for [proxies](https://www.python-httpx.org/advanced/proxies/) +- Custom [transports](https://www.python-httpx.org/advanced/transports/) +- Additional [advanced](https://www.python-httpx.org/advanced/clients/) functionality + +```python +import httpx +from unlayer import Unlayer, DefaultHttpxClient + +client = Unlayer( + # Or use the `UNLAYER_BASE_URL` env var + base_url="http://my.test.server.example.com:8083", + http_client=DefaultHttpxClient( + proxy="http://my.test.proxy.example.com", + transport=httpx.HTTPTransport(local_address="0.0.0.0"), + ), +) +``` + +You can also customize the client on a per-request basis by using `with_options()`: + +```python +client.with_options(http_client=DefaultHttpxClient(...)) +``` + +### Managing HTTP resources + +By default the library closes underlying HTTP connections whenever the client is [garbage collected](https://docs.python.org/3/reference/datamodel.html#object.__del__). You can manually close the client using the `.close()` method if desired, or with a context manager that closes when exiting. + +```py +from unlayer import Unlayer + +with Unlayer() as client: + # make requests here + ... + +# HTTP client is now closed +``` + +## Versioning + +This package generally follows [SemVer](https://semver.org/spec/v2.0.0.html) conventions, though certain backwards-incompatible changes may be released as minor versions: + +1. Changes that only affect static types, without breaking runtime behavior. +2. Changes to library internals which are technically public but not intended or documented for external use. _(Please open a GitHub issue to let us know if you are relying on such internals.)_ +3. Changes that we do not expect to impact the vast majority of users in practice. + +We take backwards-compatibility seriously and work hard to ensure you can rely on a smooth upgrade experience. + +We are keen for your feedback; please open an [issue](https://www.github.com/stainless-sdks/unlayer-python/issues) with questions, bugs, or suggestions. + +### Determining the installed version + +If you've upgraded to the latest version but aren't seeing any new features you were expecting then your python environment is likely still using an older version. + +You can determine the version that is being used at runtime with: + +```py +import unlayer +print(unlayer.__version__) +``` + +## Requirements + +Python 3.9 or higher. + +## Contributing + +See [the contributing documentation](./CONTRIBUTING.md). diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 0000000..d4bb777 --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,23 @@ +# Security Policy + +## Reporting Security Issues + +This SDK is generated by [Stainless Software Inc](http://stainless.com). Stainless takes security seriously, and encourages you to report any security vulnerability promptly so that appropriate action can be taken. + +To report a security issue, please contact the Stainless team at security@stainless.com. + +## Responsible Disclosure + +We appreciate the efforts of security researchers and individuals who help us maintain the security of +SDKs we generate. If you believe you have found a security vulnerability, please adhere to responsible +disclosure practices by allowing us a reasonable amount of time to investigate and address the issue +before making any information public. + +## Reporting Non-SDK Related Security Issues + +If you encounter security issues that are not directly related to SDKs but pertain to the services +or products provided by Unlayer, please follow the respective company's security reporting guidelines. + +--- + +Thank you for helping us keep the SDKs and systems they interact with secure. diff --git a/api.md b/api.md new file mode 100644 index 0000000..13c618d --- /dev/null +++ b/api.md @@ -0,0 +1,181 @@ +# ProjectV1 + +Types: + +```python +from unlayer.types import ( + ProjectV1APIKeysCreateResponse, + ProjectV1APIKeysListResponse, + ProjectV1APIKeysRetrieveResponse, + ProjectV1APIKeysUpdateResponse, + ProjectV1CurrentListResponse, + ProjectV1DomainsCreateResponse, + ProjectV1DomainsListResponse, + ProjectV1DomainsRetrieveResponse, + ProjectV1DomainsUpdateResponse, + ProjectV1TemplatesCreateResponse, + ProjectV1TemplatesListResponse, + ProjectV1TemplatesRetrieveResponse, + ProjectV1TemplatesUpdateResponse, +) +``` + +Methods: + +- client.project_v1.api_keys_create(\*\*params) -> ProjectV1APIKeysCreateResponse +- client.project_v1.api_keys_delete(id) -> None +- client.project_v1.api_keys_list() -> ProjectV1APIKeysListResponse +- client.project_v1.api_keys_retrieve(id) -> ProjectV1APIKeysRetrieveResponse +- client.project_v1.api_keys_update(id, \*\*params) -> ProjectV1APIKeysUpdateResponse +- client.project_v1.current_list() -> ProjectV1CurrentListResponse +- client.project_v1.domains_create(\*\*params) -> ProjectV1DomainsCreateResponse +- client.project_v1.domains_delete(id) -> None +- client.project_v1.domains_list() -> ProjectV1DomainsListResponse +- client.project_v1.domains_retrieve(id) -> ProjectV1DomainsRetrieveResponse +- client.project_v1.domains_update(id, \*\*params) -> ProjectV1DomainsUpdateResponse +- client.project_v1.templates_create(\*\*params) -> ProjectV1TemplatesCreateResponse +- client.project_v1.templates_delete(id) -> None +- client.project_v1.templates_list() -> ProjectV1TemplatesListResponse +- client.project_v1.templates_retrieve(id) -> ProjectV1TemplatesRetrieveResponse +- client.project_v1.templates_update(id, \*\*params) -> ProjectV1TemplatesUpdateResponse + +# Project + +Types: + +```python +from unlayer.types import ( + ProjectAPIKeysCreateResponse, + ProjectAPIKeysListResponse, + ProjectAPIKeysRetrieveResponse, + ProjectAPIKeysUpdateResponse, + ProjectCurrentListResponse, + ProjectDomainsCreateResponse, + ProjectDomainsListResponse, + ProjectDomainsRetrieveResponse, + ProjectDomainsUpdateResponse, + ProjectTemplatesCreateResponse, + ProjectTemplatesListResponse, + ProjectTemplatesRetrieveResponse, + ProjectTemplatesUpdateResponse, +) +``` + +Methods: + +- client.project.api_keys_create(\*\*params) -> ProjectAPIKeysCreateResponse +- client.project.api_keys_delete(id) -> None +- client.project.api_keys_list() -> ProjectAPIKeysListResponse +- client.project.api_keys_retrieve(id) -> ProjectAPIKeysRetrieveResponse +- client.project.api_keys_update(id, \*\*params) -> ProjectAPIKeysUpdateResponse +- client.project.current_list() -> ProjectCurrentListResponse +- client.project.domains_create(\*\*params) -> ProjectDomainsCreateResponse +- client.project.domains_delete(id) -> None +- client.project.domains_list() -> ProjectDomainsListResponse +- client.project.domains_retrieve(id) -> ProjectDomainsRetrieveResponse +- client.project.domains_update(id, \*\*params) -> ProjectDomainsUpdateResponse +- client.project.templates_create(\*\*params) -> ProjectTemplatesCreateResponse +- client.project.templates_delete(id) -> None +- client.project.templates_list() -> ProjectTemplatesListResponse +- client.project.templates_retrieve(id) -> ProjectTemplatesRetrieveResponse +- client.project.templates_update(id, \*\*params) -> ProjectTemplatesUpdateResponse + +# EmailsV1 + +Types: + +```python +from unlayer.types import ( + EmailsV1EmailsRetrieveResponse, + EmailsV1RenderCreateResponse, + EmailsV1SendCreateResponse, + EmailsV1SendTemplateTemplateResponse, +) +``` + +Methods: + +- client.emails_v1.emails_retrieve(id) -> EmailsV1EmailsRetrieveResponse +- client.emails_v1.render_create(\*\*params) -> EmailsV1RenderCreateResponse +- client.emails_v1.send_create(\*\*params) -> EmailsV1SendCreateResponse +- client.emails_v1.send_template_template(\*\*params) -> EmailsV1SendTemplateTemplateResponse + +# Emails + +Types: + +```python +from unlayer.types import ( + EmailEmailsRetrieveResponse, + EmailRenderCreateResponse, + EmailSendCreateResponse, + EmailSendTemplateTemplateResponse, +) +``` + +Methods: + +- client.emails.emails_retrieve(id) -> EmailEmailsRetrieveResponse +- client.emails.render_create(\*\*params) -> EmailRenderCreateResponse +- client.emails.send_create(\*\*params) -> EmailSendCreateResponse +- client.emails.send_template_template(\*\*params) -> EmailSendTemplateTemplateResponse + +# DocumentsV1 + +Types: + +```python +from unlayer.types import ( + DocumentsV1DocumentsRetrieveResponse, + DocumentsV1GenerateCreateResponse, + DocumentsV1GenerateTemplateTemplateResponse, +) +``` + +Methods: + +- client.documents_v1.documents_retrieve(id) -> DocumentsV1DocumentsRetrieveResponse +- client.documents_v1.generate_create(\*\*params) -> DocumentsV1GenerateCreateResponse +- client.documents_v1.generate_template_template(\*\*params) -> DocumentsV1GenerateTemplateTemplateResponse + +# Documents + +Types: + +```python +from unlayer.types import ( + DocumentDocumentsRetrieveResponse, + DocumentGenerateCreateResponse, + DocumentGenerateTemplateTemplateResponse, +) +``` + +Methods: + +- client.documents.documents_retrieve(id) -> DocumentDocumentsRetrieveResponse +- client.documents.generate_create(\*\*params) -> DocumentGenerateCreateResponse +- client.documents.generate_template_template(\*\*params) -> DocumentGenerateTemplateTemplateResponse + +# PagesV1 + +Types: + +```python +from unlayer.types import PagesV1RenderCreateResponse +``` + +Methods: + +- client.pages_v1.render_create(\*\*params) -> PagesV1RenderCreateResponse + +# Pages + +Types: + +```python +from unlayer.types import PageRenderCreateResponse +``` + +Methods: + +- client.pages.render_create(\*\*params) -> PageRenderCreateResponse diff --git a/bin/publish-pypi b/bin/publish-pypi new file mode 100644 index 0000000..826054e --- /dev/null +++ b/bin/publish-pypi @@ -0,0 +1,6 @@ +#!/usr/bin/env bash + +set -eux +mkdir -p dist +rye build --clean +rye publish --yes --token=$PYPI_TOKEN diff --git a/examples/.keep b/examples/.keep new file mode 100644 index 0000000..d8c73e9 --- /dev/null +++ b/examples/.keep @@ -0,0 +1,4 @@ +File generated from our OpenAPI spec by Stainless. + +This directory can be used to store example files demonstrating usage of this SDK. +It is ignored by Stainless code generation and its content (other than this keep file) won't be touched. \ No newline at end of file diff --git a/noxfile.py b/noxfile.py new file mode 100644 index 0000000..53bca7f --- /dev/null +++ b/noxfile.py @@ -0,0 +1,9 @@ +import nox + + +@nox.session(reuse_venv=True, name="test-pydantic-v1") +def test_pydantic_v1(session: nox.Session) -> None: + session.install("-r", "requirements-dev.lock") + session.install("pydantic<2") + + session.run("pytest", "--showlocals", "--ignore=tests/functional", *session.posargs) diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..b1d1c1c --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,269 @@ +[project] +name = "unlayer" +version = "0.0.1" +description = "The official Python library for the unlayer API" +dynamic = ["readme"] +license = "Apache-2.0" +authors = [ +{ name = "Unlayer", email = "contact@example.com" }, +] + +dependencies = [ + "httpx>=0.23.0, <1", + "pydantic>=1.9.0, <3", + "typing-extensions>=4.10, <5", + "anyio>=3.5.0, <5", + "distro>=1.7.0, <2", + "sniffio", +] + +requires-python = ">= 3.9" +classifiers = [ + "Typing :: Typed", + "Intended Audience :: Developers", + "Programming Language :: Python :: 3.9", + "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", + "Operating System :: OS Independent", + "Operating System :: POSIX", + "Operating System :: MacOS", + "Operating System :: POSIX :: Linux", + "Operating System :: Microsoft :: Windows", + "Topic :: Software Development :: Libraries :: Python Modules", + "License :: OSI Approved :: Apache Software License" +] + +[project.urls] +Homepage = "https://github.com/stainless-sdks/unlayer-python" +Repository = "https://github.com/stainless-sdks/unlayer-python" + +[project.optional-dependencies] +aiohttp = ["aiohttp", "httpx_aiohttp>=0.1.9"] + +[tool.rye] +managed = true +# version pins are in requirements-dev.lock +dev-dependencies = [ + "pyright==1.1.399", + "mypy==1.17", + "respx", + "pytest", + "pytest-asyncio", + "ruff", + "time-machine", + "nox", + "dirty-equals>=0.6.0", + "importlib-metadata>=6.7.0", + "rich>=13.7.1", + "pytest-xdist>=3.6.1", +] + +[tool.rye.scripts] +format = { chain = [ + "format:ruff", + "format:docs", + "fix:ruff", + # run formatting again to fix any inconsistencies when imports are stripped + "format:ruff", +]} +"format:docs" = "python scripts/utils/ruffen-docs.py README.md api.md" +"format:ruff" = "ruff format" + +"lint" = { chain = [ + "check:ruff", + "typecheck", + "check:importable", +]} +"check:ruff" = "ruff check ." +"fix:ruff" = "ruff check --fix ." + +"check:importable" = "python -c 'import unlayer'" + +typecheck = { chain = [ + "typecheck:pyright", + "typecheck:mypy" +]} +"typecheck:pyright" = "pyright" +"typecheck:verify-types" = "pyright --verifytypes unlayer --ignoreexternal" +"typecheck:mypy" = "mypy ." + +[build-system] +requires = ["hatchling==1.26.3", "hatch-fancy-pypi-readme"] +build-backend = "hatchling.build" + +[tool.hatch.build] +include = [ + "src/*" +] + +[tool.hatch.build.targets.wheel] +packages = ["src/unlayer"] + +[tool.hatch.build.targets.sdist] +# Basically everything except hidden files/directories (such as .github, .devcontainers, .python-version, etc) +include = [ + "/*.toml", + "/*.json", + "/*.lock", + "/*.md", + "/mypy.ini", + "/noxfile.py", + "bin/*", + "examples/*", + "src/*", + "tests/*", +] + +[tool.hatch.metadata.hooks.fancy-pypi-readme] +content-type = "text/markdown" + +[[tool.hatch.metadata.hooks.fancy-pypi-readme.fragments]] +path = "README.md" + +[[tool.hatch.metadata.hooks.fancy-pypi-readme.substitutions]] +# replace relative links with absolute links +pattern = '\[(.+?)\]\(((?!https?://)\S+?)\)' +replacement = '[\1](https://github.com/stainless-sdks/unlayer-python/tree/main/\g<2>)' + +[tool.pytest.ini_options] +testpaths = ["tests"] +addopts = "--tb=short -n auto" +xfail_strict = true +asyncio_mode = "auto" +asyncio_default_fixture_loop_scope = "session" +filterwarnings = [ + "error" +] + +[tool.pyright] +# this enables practically every flag given by pyright. +# there are a couple of flags that are still disabled by +# default in strict mode as they are experimental and niche. +typeCheckingMode = "strict" +pythonVersion = "3.9" + +exclude = [ + "_dev", + ".venv", + ".nox", + ".git", +] + +reportImplicitOverride = true +reportOverlappingOverload = false + +reportImportCycles = false +reportPrivateUsage = false + +[tool.mypy] +pretty = true +show_error_codes = true + +# Exclude _files.py because mypy isn't smart enough to apply +# the correct type narrowing and as this is an internal module +# it's fine to just use Pyright. +# +# We also exclude our `tests` as mypy doesn't always infer +# types correctly and Pyright will still catch any type errors. +exclude = ['src/unlayer/_files.py', '_dev/.*.py', 'tests/.*'] + +strict_equality = true +implicit_reexport = true +check_untyped_defs = true +no_implicit_optional = true + +warn_return_any = true +warn_unreachable = true +warn_unused_configs = true + +# Turn these options off as it could cause conflicts +# with the Pyright options. +warn_unused_ignores = false +warn_redundant_casts = false + +disallow_any_generics = true +disallow_untyped_defs = true +disallow_untyped_calls = true +disallow_subclassing_any = true +disallow_incomplete_defs = true +disallow_untyped_decorators = true +cache_fine_grained = true + +# By default, mypy reports an error if you assign a value to the result +# of a function call that doesn't return anything. We do this in our test +# cases: +# ``` +# result = ... +# assert result is None +# ``` +# Changing this codegen to make mypy happy would increase complexity +# and would not be worth it. +disable_error_code = "func-returns-value,overload-cannot-match" + +# https://github.com/python/mypy/issues/12162 +[[tool.mypy.overrides]] +module = "black.files.*" +ignore_errors = true +ignore_missing_imports = true + + +[tool.ruff] +line-length = 120 +output-format = "grouped" +target-version = "py38" + +[tool.ruff.format] +docstring-code-format = true + +[tool.ruff.lint] +select = [ + # isort + "I", + # bugbear rules + "B", + # remove unused imports + "F401", + # check for missing future annotations + "FA102", + # bare except statements + "E722", + # unused arguments + "ARG", + # print statements + "T201", + "T203", + # misuse of typing.TYPE_CHECKING + "TC004", + # import rules + "TID251", +] +ignore = [ + # mutable defaults + "B006", +] +unfixable = [ + # disable auto fix for print statements + "T201", + "T203", +] + +extend-safe-fixes = ["FA102"] + +[tool.ruff.lint.flake8-tidy-imports.banned-api] +"functools.lru_cache".msg = "This function does not retain type information for the wrapped function's arguments; The `lru_cache` function from `_utils` should be used instead" + +[tool.ruff.lint.isort] +length-sort = true +length-sort-straight = true +combine-as-imports = true +extra-standard-library = ["typing_extensions"] +known-first-party = ["unlayer", "tests"] + +[tool.ruff.lint.per-file-ignores] +"bin/**.py" = ["T201", "T203"] +"scripts/**.py" = ["T201", "T203"] +"tests/**.py" = ["T201", "T203"] +"examples/**.py" = ["T201", "T203"] diff --git a/requirements-dev.lock b/requirements-dev.lock new file mode 100644 index 0000000..0571b5a --- /dev/null +++ b/requirements-dev.lock @@ -0,0 +1,149 @@ +# generated by rye +# use `rye lock` or `rye sync` to update this lockfile +# +# last locked with the following flags: +# pre: false +# features: [] +# all-features: true +# with-sources: false +# generate-hashes: false +# universal: false + +-e file:. +aiohappyeyeballs==2.6.1 + # via aiohttp +aiohttp==3.13.2 + # via httpx-aiohttp + # via unlayer +aiosignal==1.4.0 + # via aiohttp +annotated-types==0.7.0 + # via pydantic +anyio==4.12.0 + # via httpx + # via unlayer +argcomplete==3.6.3 + # via nox +async-timeout==5.0.1 + # via aiohttp +attrs==25.4.0 + # via aiohttp + # via nox +backports-asyncio-runner==1.2.0 + # via pytest-asyncio +certifi==2025.11.12 + # via httpcore + # via httpx +colorlog==6.10.1 + # via nox +dependency-groups==1.3.1 + # via nox +dirty-equals==0.11 +distlib==0.4.0 + # via virtualenv +distro==1.9.0 + # via unlayer +exceptiongroup==1.3.1 + # via anyio + # via pytest +execnet==2.1.2 + # via pytest-xdist +filelock==3.19.1 + # via virtualenv +frozenlist==1.8.0 + # via aiohttp + # via aiosignal +h11==0.16.0 + # via httpcore +httpcore==1.0.9 + # via httpx +httpx==0.28.1 + # via httpx-aiohttp + # via respx + # via unlayer +httpx-aiohttp==0.1.9 + # via unlayer +humanize==4.13.0 + # via nox +idna==3.11 + # via anyio + # via httpx + # via yarl +importlib-metadata==8.7.0 +iniconfig==2.1.0 + # via pytest +markdown-it-py==3.0.0 + # via rich +mdurl==0.1.2 + # via markdown-it-py +multidict==6.7.0 + # via aiohttp + # via yarl +mypy==1.17.0 +mypy-extensions==1.1.0 + # via mypy +nodeenv==1.9.1 + # via pyright +nox==2025.11.12 +packaging==25.0 + # via dependency-groups + # via nox + # via pytest +pathspec==0.12.1 + # via mypy +platformdirs==4.4.0 + # via virtualenv +pluggy==1.6.0 + # via pytest +propcache==0.4.1 + # via aiohttp + # via yarl +pydantic==2.12.5 + # via unlayer +pydantic-core==2.41.5 + # via pydantic +pygments==2.19.2 + # via pytest + # via rich +pyright==1.1.399 +pytest==8.4.2 + # via pytest-asyncio + # via pytest-xdist +pytest-asyncio==1.2.0 +pytest-xdist==3.8.0 +python-dateutil==2.9.0.post0 + # via time-machine +respx==0.22.0 +rich==14.2.0 +ruff==0.14.7 +six==1.17.0 + # via python-dateutil +sniffio==1.3.1 + # via unlayer +time-machine==2.19.0 +tomli==2.3.0 + # via dependency-groups + # via mypy + # via nox + # via pytest +typing-extensions==4.15.0 + # via aiosignal + # via anyio + # via exceptiongroup + # via multidict + # via mypy + # via pydantic + # via pydantic-core + # via pyright + # via pytest-asyncio + # via typing-inspection + # via unlayer + # via virtualenv +typing-inspection==0.4.2 + # via pydantic +virtualenv==20.35.4 + # via nox +yarl==1.22.0 + # via aiohttp +zipp==3.23.0 + # via importlib-metadata diff --git a/requirements.lock b/requirements.lock new file mode 100644 index 0000000..32753cc --- /dev/null +++ b/requirements.lock @@ -0,0 +1,76 @@ +# generated by rye +# use `rye lock` or `rye sync` to update this lockfile +# +# last locked with the following flags: +# pre: false +# features: [] +# all-features: true +# with-sources: false +# generate-hashes: false +# universal: false + +-e file:. +aiohappyeyeballs==2.6.1 + # via aiohttp +aiohttp==3.13.2 + # via httpx-aiohttp + # via unlayer +aiosignal==1.4.0 + # via aiohttp +annotated-types==0.7.0 + # via pydantic +anyio==4.12.0 + # via httpx + # via unlayer +async-timeout==5.0.1 + # via aiohttp +attrs==25.4.0 + # via aiohttp +certifi==2025.11.12 + # via httpcore + # via httpx +distro==1.9.0 + # via unlayer +exceptiongroup==1.3.1 + # via anyio +frozenlist==1.8.0 + # via aiohttp + # via aiosignal +h11==0.16.0 + # via httpcore +httpcore==1.0.9 + # via httpx +httpx==0.28.1 + # via httpx-aiohttp + # via unlayer +httpx-aiohttp==0.1.9 + # via unlayer +idna==3.11 + # via anyio + # via httpx + # via yarl +multidict==6.7.0 + # via aiohttp + # via yarl +propcache==0.4.1 + # via aiohttp + # via yarl +pydantic==2.12.5 + # via unlayer +pydantic-core==2.41.5 + # via pydantic +sniffio==1.3.1 + # via unlayer +typing-extensions==4.15.0 + # via aiosignal + # via anyio + # via exceptiongroup + # via multidict + # via pydantic + # via pydantic-core + # via typing-inspection + # via unlayer +typing-inspection==0.4.2 + # via pydantic +yarl==1.22.0 + # via aiohttp diff --git a/scripts/bootstrap b/scripts/bootstrap new file mode 100755 index 0000000..b430fee --- /dev/null +++ b/scripts/bootstrap @@ -0,0 +1,27 @@ +#!/usr/bin/env bash + +set -e + +cd "$(dirname "$0")/.." + +if [ -f "Brewfile" ] && [ "$(uname -s)" = "Darwin" ] && [ "$SKIP_BREW" != "1" ] && [ -t 0 ]; then + brew bundle check >/dev/null 2>&1 || { + echo -n "==> Install Homebrew dependencies? (y/N): " + read -r response + case "$response" in + [yY][eE][sS]|[yY]) + brew bundle + ;; + *) + ;; + esac + echo + } +fi + +echo "==> Installing Python dependencies…" + +# experimental uv support makes installations significantly faster +rye config --set-bool behavior.use-uv=true + +rye sync --all-features diff --git a/scripts/format b/scripts/format new file mode 100755 index 0000000..667ec2d --- /dev/null +++ b/scripts/format @@ -0,0 +1,8 @@ +#!/usr/bin/env bash + +set -e + +cd "$(dirname "$0")/.." + +echo "==> Running formatters" +rye run format diff --git a/scripts/lint b/scripts/lint new file mode 100755 index 0000000..1b9382a --- /dev/null +++ b/scripts/lint @@ -0,0 +1,11 @@ +#!/usr/bin/env bash + +set -e + +cd "$(dirname "$0")/.." + +echo "==> Running lints" +rye run lint + +echo "==> Making sure it imports" +rye run python -c 'import unlayer' diff --git a/scripts/mock b/scripts/mock new file mode 100755 index 0000000..0b28f6e --- /dev/null +++ b/scripts/mock @@ -0,0 +1,41 @@ +#!/usr/bin/env bash + +set -e + +cd "$(dirname "$0")/.." + +if [[ -n "$1" && "$1" != '--'* ]]; then + URL="$1" + shift +else + URL="$(grep 'openapi_spec_url' .stats.yml | cut -d' ' -f2)" +fi + +# Check if the URL is empty +if [ -z "$URL" ]; then + echo "Error: No OpenAPI spec path/url provided or found in .stats.yml" + exit 1 +fi + +echo "==> Starting mock server with URL ${URL}" + +# Run prism mock on the given spec +if [ "$1" == "--daemon" ]; then + npm exec --package=@stainless-api/prism-cli@5.15.0 -- prism mock "$URL" &> .prism.log & + + # Wait for server to come online + echo -n "Waiting for server" + while ! grep -q "✖ fatal\|Prism is listening" ".prism.log" ; do + echo -n "." + sleep 0.1 + done + + if grep -q "✖ fatal" ".prism.log"; then + cat .prism.log + exit 1 + fi + + echo +else + npm exec --package=@stainless-api/prism-cli@5.15.0 -- prism mock "$URL" +fi diff --git a/scripts/test b/scripts/test new file mode 100755 index 0000000..dbeda2d --- /dev/null +++ b/scripts/test @@ -0,0 +1,61 @@ +#!/usr/bin/env bash + +set -e + +cd "$(dirname "$0")/.." + +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[0;33m' +NC='\033[0m' # No Color + +function prism_is_running() { + curl --silent "http://localhost:4010" >/dev/null 2>&1 +} + +kill_server_on_port() { + pids=$(lsof -t -i tcp:"$1" || echo "") + if [ "$pids" != "" ]; then + kill "$pids" + echo "Stopped $pids." + fi +} + +function is_overriding_api_base_url() { + [ -n "$TEST_API_BASE_URL" ] +} + +if ! is_overriding_api_base_url && ! prism_is_running ; then + # When we exit this script, make sure to kill the background mock server process + trap 'kill_server_on_port 4010' EXIT + + # Start the dev server + ./scripts/mock --daemon +fi + +if is_overriding_api_base_url ; then + echo -e "${GREEN}✔ Running tests against ${TEST_API_BASE_URL}${NC}" + echo +elif ! prism_is_running ; then + echo -e "${RED}ERROR:${NC} The test suite will not run without a mock Prism server" + echo -e "running against your OpenAPI spec." + echo + echo -e "To run the server, pass in the path or url of your OpenAPI" + echo -e "spec to the prism command:" + echo + echo -e " \$ ${YELLOW}npm exec --package=@stainless-api/prism-cli@5.15.0 -- prism mock path/to/your.openapi.yml${NC}" + echo + + exit 1 +else + echo -e "${GREEN}✔ Mock prism server is running with your OpenAPI spec${NC}" + echo +fi + +export DEFER_PYDANTIC_BUILD=false + +echo "==> Running tests" +rye run pytest "$@" + +echo "==> Running Pydantic v1 tests" +rye run nox -s test-pydantic-v1 -- "$@" diff --git a/scripts/utils/ruffen-docs.py b/scripts/utils/ruffen-docs.py new file mode 100644 index 0000000..0cf2bd2 --- /dev/null +++ b/scripts/utils/ruffen-docs.py @@ -0,0 +1,167 @@ +# fork of https://github.com/asottile/blacken-docs adapted for ruff +from __future__ import annotations + +import re +import sys +import argparse +import textwrap +import contextlib +import subprocess +from typing import Match, Optional, Sequence, Generator, NamedTuple, cast + +MD_RE = re.compile( + r"(?P^(?P *)```\s*python\n)" r"(?P.*?)" r"(?P^(?P=indent)```\s*$)", + re.DOTALL | re.MULTILINE, +) +MD_PYCON_RE = re.compile( + r"(?P^(?P *)```\s*pycon\n)" r"(?P.*?)" r"(?P^(?P=indent)```.*$)", + re.DOTALL | re.MULTILINE, +) +PYCON_PREFIX = ">>> " +PYCON_CONTINUATION_PREFIX = "..." +PYCON_CONTINUATION_RE = re.compile( + rf"^{re.escape(PYCON_CONTINUATION_PREFIX)}( |$)", +) +DEFAULT_LINE_LENGTH = 100 + + +class CodeBlockError(NamedTuple): + offset: int + exc: Exception + + +def format_str( + src: str, +) -> tuple[str, Sequence[CodeBlockError]]: + errors: list[CodeBlockError] = [] + + @contextlib.contextmanager + def _collect_error(match: Match[str]) -> Generator[None, None, None]: + try: + yield + except Exception as e: + errors.append(CodeBlockError(match.start(), e)) + + def _md_match(match: Match[str]) -> str: + code = textwrap.dedent(match["code"]) + with _collect_error(match): + code = format_code_block(code) + code = textwrap.indent(code, match["indent"]) + return f"{match['before']}{code}{match['after']}" + + def _pycon_match(match: Match[str]) -> str: + code = "" + fragment = cast(Optional[str], None) + + def finish_fragment() -> None: + nonlocal code + nonlocal fragment + + if fragment is not None: + with _collect_error(match): + fragment = format_code_block(fragment) + fragment_lines = fragment.splitlines() + code += f"{PYCON_PREFIX}{fragment_lines[0]}\n" + for line in fragment_lines[1:]: + # Skip blank lines to handle Black adding a blank above + # functions within blocks. A blank line would end the REPL + # continuation prompt. + # + # >>> if True: + # ... def f(): + # ... pass + # ... + if line: + code += f"{PYCON_CONTINUATION_PREFIX} {line}\n" + if fragment_lines[-1].startswith(" "): + code += f"{PYCON_CONTINUATION_PREFIX}\n" + fragment = None + + indentation = None + for line in match["code"].splitlines(): + orig_line, line = line, line.lstrip() + if indentation is None and line: + indentation = len(orig_line) - len(line) + continuation_match = PYCON_CONTINUATION_RE.match(line) + if continuation_match and fragment is not None: + fragment += line[continuation_match.end() :] + "\n" + else: + finish_fragment() + if line.startswith(PYCON_PREFIX): + fragment = line[len(PYCON_PREFIX) :] + "\n" + else: + code += orig_line[indentation:] + "\n" + finish_fragment() + return code + + def _md_pycon_match(match: Match[str]) -> str: + code = _pycon_match(match) + code = textwrap.indent(code, match["indent"]) + return f"{match['before']}{code}{match['after']}" + + src = MD_RE.sub(_md_match, src) + src = MD_PYCON_RE.sub(_md_pycon_match, src) + return src, errors + + +def format_code_block(code: str) -> str: + return subprocess.check_output( + [ + sys.executable, + "-m", + "ruff", + "format", + "--stdin-filename=script.py", + f"--line-length={DEFAULT_LINE_LENGTH}", + ], + encoding="utf-8", + input=code, + ) + + +def format_file( + filename: str, + skip_errors: bool, +) -> int: + with open(filename, encoding="UTF-8") as f: + contents = f.read() + new_contents, errors = format_str(contents) + for error in errors: + lineno = contents[: error.offset].count("\n") + 1 + print(f"{filename}:{lineno}: code block parse error {error.exc}") + if errors and not skip_errors: + return 1 + if contents != new_contents: + print(f"{filename}: Rewriting...") + with open(filename, "w", encoding="UTF-8") as f: + f.write(new_contents) + return 0 + else: + return 0 + + +def main(argv: Sequence[str] | None = None) -> int: + parser = argparse.ArgumentParser() + parser.add_argument( + "-l", + "--line-length", + type=int, + default=DEFAULT_LINE_LENGTH, + ) + parser.add_argument( + "-S", + "--skip-string-normalization", + action="store_true", + ) + parser.add_argument("-E", "--skip-errors", action="store_true") + parser.add_argument("filenames", nargs="*") + args = parser.parse_args(argv) + + retv = 0 + for filename in args.filenames: + retv |= format_file(filename, skip_errors=args.skip_errors) + return retv + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/scripts/utils/upload-artifact.sh b/scripts/utils/upload-artifact.sh new file mode 100755 index 0000000..0394855 --- /dev/null +++ b/scripts/utils/upload-artifact.sh @@ -0,0 +1,27 @@ +#!/usr/bin/env bash +set -exuo pipefail + +FILENAME=$(basename dist/*.whl) + +RESPONSE=$(curl -X POST "$URL?filename=$FILENAME" \ + -H "Authorization: Bearer $AUTH" \ + -H "Content-Type: application/json") + +SIGNED_URL=$(echo "$RESPONSE" | jq -r '.url') + +if [[ "$SIGNED_URL" == "null" ]]; then + echo -e "\033[31mFailed to get signed URL.\033[0m" + exit 1 +fi + +UPLOAD_RESPONSE=$(curl -v -X PUT \ + -H "Content-Type: binary/octet-stream" \ + --data-binary "@dist/$FILENAME" "$SIGNED_URL" 2>&1) + +if echo "$UPLOAD_RESPONSE" | grep -q "HTTP/[0-9.]* 200"; then + echo -e "\033[32mUploaded build to Stainless storage.\033[0m" + echo -e "\033[32mInstallation: pip install 'https://pkg.stainless.com/s/unlayer-python/$SHA/$FILENAME'\033[0m" +else + echo -e "\033[31mFailed to upload artifact.\033[0m" + exit 1 +fi diff --git a/src/unlayer/__init__.py b/src/unlayer/__init__.py new file mode 100644 index 0000000..37d9b74 --- /dev/null +++ b/src/unlayer/__init__.py @@ -0,0 +1,92 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +import typing as _t + +from . import types +from ._types import NOT_GIVEN, Omit, NoneType, NotGiven, Transport, ProxiesTypes, omit, not_given +from ._utils import file_from_path +from ._client import Client, Stream, Timeout, Unlayer, Transport, AsyncClient, AsyncStream, AsyncUnlayer, RequestOptions +from ._models import BaseModel +from ._version import __title__, __version__ +from ._response import APIResponse as APIResponse, AsyncAPIResponse as AsyncAPIResponse +from ._constants import DEFAULT_TIMEOUT, DEFAULT_MAX_RETRIES, DEFAULT_CONNECTION_LIMITS +from ._exceptions import ( + APIError, + UnlayerError, + ConflictError, + NotFoundError, + APIStatusError, + RateLimitError, + APITimeoutError, + BadRequestError, + APIConnectionError, + AuthenticationError, + InternalServerError, + PermissionDeniedError, + UnprocessableEntityError, + APIResponseValidationError, +) +from ._base_client import DefaultHttpxClient, DefaultAioHttpClient, DefaultAsyncHttpxClient +from ._utils._logs import setup_logging as _setup_logging + +__all__ = [ + "types", + "__version__", + "__title__", + "NoneType", + "Transport", + "ProxiesTypes", + "NotGiven", + "NOT_GIVEN", + "not_given", + "Omit", + "omit", + "UnlayerError", + "APIError", + "APIStatusError", + "APITimeoutError", + "APIConnectionError", + "APIResponseValidationError", + "BadRequestError", + "AuthenticationError", + "PermissionDeniedError", + "NotFoundError", + "ConflictError", + "UnprocessableEntityError", + "RateLimitError", + "InternalServerError", + "Timeout", + "RequestOptions", + "Client", + "AsyncClient", + "Stream", + "AsyncStream", + "Unlayer", + "AsyncUnlayer", + "file_from_path", + "BaseModel", + "DEFAULT_TIMEOUT", + "DEFAULT_MAX_RETRIES", + "DEFAULT_CONNECTION_LIMITS", + "DefaultHttpxClient", + "DefaultAsyncHttpxClient", + "DefaultAioHttpClient", +] + +if not _t.TYPE_CHECKING: + from ._utils._resources_proxy import resources as resources + +_setup_logging() + +# Update the __module__ attribute for exported symbols so that +# error messages point to this module instead of the module +# it was originally defined in, e.g. +# unlayer._exceptions.NotFoundError -> unlayer.NotFoundError +__locals = locals() +for __name in __all__: + if not __name.startswith("__"): + try: + __locals[__name].__module__ = "unlayer" + except (TypeError, AttributeError): + # Some of our exported symbols are builtins which we can't set attributes for. + pass diff --git a/src/unlayer/_base_client.py b/src/unlayer/_base_client.py new file mode 100644 index 0000000..f3883b2 --- /dev/null +++ b/src/unlayer/_base_client.py @@ -0,0 +1,1995 @@ +from __future__ import annotations + +import sys +import json +import time +import uuid +import email +import asyncio +import inspect +import logging +import platform +import email.utils +from types import TracebackType +from random import random +from typing import ( + TYPE_CHECKING, + Any, + Dict, + Type, + Union, + Generic, + Mapping, + TypeVar, + Iterable, + Iterator, + Optional, + Generator, + AsyncIterator, + cast, + overload, +) +from typing_extensions import Literal, override, get_origin + +import anyio +import httpx +import distro +import pydantic +from httpx import URL +from pydantic import PrivateAttr + +from . import _exceptions +from ._qs import Querystring +from ._files import to_httpx_files, async_to_httpx_files +from ._types import ( + Body, + Omit, + Query, + Headers, + Timeout, + NotGiven, + ResponseT, + AnyMapping, + PostParser, + RequestFiles, + HttpxSendArgs, + RequestOptions, + HttpxRequestFiles, + ModelBuilderProtocol, + not_given, +) +from ._utils import is_dict, is_list, asyncify, is_given, lru_cache, is_mapping +from ._compat import PYDANTIC_V1, model_copy, model_dump +from ._models import GenericModel, FinalRequestOptions, validate_type, construct_type +from ._response import ( + APIResponse, + BaseAPIResponse, + AsyncAPIResponse, + extract_response_type, +) +from ._constants import ( + DEFAULT_TIMEOUT, + MAX_RETRY_DELAY, + DEFAULT_MAX_RETRIES, + INITIAL_RETRY_DELAY, + RAW_RESPONSE_HEADER, + OVERRIDE_CAST_TO_HEADER, + DEFAULT_CONNECTION_LIMITS, +) +from ._streaming import Stream, SSEDecoder, AsyncStream, SSEBytesDecoder +from ._exceptions import ( + APIStatusError, + APITimeoutError, + APIConnectionError, + APIResponseValidationError, +) + +log: logging.Logger = logging.getLogger(__name__) + +# TODO: make base page type vars covariant +SyncPageT = TypeVar("SyncPageT", bound="BaseSyncPage[Any]") +AsyncPageT = TypeVar("AsyncPageT", bound="BaseAsyncPage[Any]") + + +_T = TypeVar("_T") +_T_co = TypeVar("_T_co", covariant=True) + +_StreamT = TypeVar("_StreamT", bound=Stream[Any]) +_AsyncStreamT = TypeVar("_AsyncStreamT", bound=AsyncStream[Any]) + +if TYPE_CHECKING: + from httpx._config import ( + DEFAULT_TIMEOUT_CONFIG, # pyright: ignore[reportPrivateImportUsage] + ) + + HTTPX_DEFAULT_TIMEOUT = DEFAULT_TIMEOUT_CONFIG +else: + try: + from httpx._config import DEFAULT_TIMEOUT_CONFIG as HTTPX_DEFAULT_TIMEOUT + except ImportError: + # taken from https://github.com/encode/httpx/blob/3ba5fe0d7ac70222590e759c31442b1cab263791/httpx/_config.py#L366 + HTTPX_DEFAULT_TIMEOUT = Timeout(5.0) + + +class PageInfo: + """Stores the necessary information to build the request to retrieve the next page. + + Either `url` or `params` must be set. + """ + + url: URL | NotGiven + params: Query | NotGiven + json: Body | NotGiven + + @overload + def __init__( + self, + *, + url: URL, + ) -> None: ... + + @overload + def __init__( + self, + *, + params: Query, + ) -> None: ... + + @overload + def __init__( + self, + *, + json: Body, + ) -> None: ... + + def __init__( + self, + *, + url: URL | NotGiven = not_given, + json: Body | NotGiven = not_given, + params: Query | NotGiven = not_given, + ) -> None: + self.url = url + self.json = json + self.params = params + + @override + def __repr__(self) -> str: + if self.url: + return f"{self.__class__.__name__}(url={self.url})" + if self.json: + return f"{self.__class__.__name__}(json={self.json})" + return f"{self.__class__.__name__}(params={self.params})" + + +class BasePage(GenericModel, Generic[_T]): + """ + Defines the core interface for pagination. + + Type Args: + ModelT: The pydantic model that represents an item in the response. + + Methods: + has_next_page(): Check if there is another page available + next_page_info(): Get the necessary information to make a request for the next page + """ + + _options: FinalRequestOptions = PrivateAttr() + _model: Type[_T] = PrivateAttr() + + def has_next_page(self) -> bool: + items = self._get_page_items() + if not items: + return False + return self.next_page_info() is not None + + def next_page_info(self) -> Optional[PageInfo]: ... + + def _get_page_items(self) -> Iterable[_T]: # type: ignore[empty-body] + ... + + def _params_from_url(self, url: URL) -> httpx.QueryParams: + # TODO: do we have to preprocess params here? + return httpx.QueryParams(cast(Any, self._options.params)).merge(url.params) + + def _info_to_options(self, info: PageInfo) -> FinalRequestOptions: + options = model_copy(self._options) + options._strip_raw_response_header() + + if not isinstance(info.params, NotGiven): + options.params = {**options.params, **info.params} + return options + + if not isinstance(info.url, NotGiven): + params = self._params_from_url(info.url) + url = info.url.copy_with(params=params) + options.params = dict(url.params) + options.url = str(url) + return options + + if not isinstance(info.json, NotGiven): + if not is_mapping(info.json): + raise TypeError("Pagination is only supported with mappings") + + if not options.json_data: + options.json_data = {**info.json} + else: + if not is_mapping(options.json_data): + raise TypeError("Pagination is only supported with mappings") + + options.json_data = {**options.json_data, **info.json} + return options + + raise ValueError("Unexpected PageInfo state") + + +class BaseSyncPage(BasePage[_T], Generic[_T]): + _client: SyncAPIClient = pydantic.PrivateAttr() + + def _set_private_attributes( + self, + client: SyncAPIClient, + model: Type[_T], + options: FinalRequestOptions, + ) -> None: + if (not PYDANTIC_V1) and getattr(self, "__pydantic_private__", None) is None: + self.__pydantic_private__ = {} + + self._model = model + self._client = client + self._options = options + + # Pydantic uses a custom `__iter__` method to support casting BaseModels + # to dictionaries. e.g. dict(model). + # As we want to support `for item in page`, this is inherently incompatible + # with the default pydantic behaviour. It is not possible to support both + # use cases at once. Fortunately, this is not a big deal as all other pydantic + # methods should continue to work as expected as there is an alternative method + # to cast a model to a dictionary, model.dict(), which is used internally + # by pydantic. + def __iter__(self) -> Iterator[_T]: # type: ignore + for page in self.iter_pages(): + for item in page._get_page_items(): + yield item + + def iter_pages(self: SyncPageT) -> Iterator[SyncPageT]: + page = self + while True: + yield page + if page.has_next_page(): + page = page.get_next_page() + else: + return + + def get_next_page(self: SyncPageT) -> SyncPageT: + info = self.next_page_info() + if not info: + raise RuntimeError( + "No next page expected; please check `.has_next_page()` before calling `.get_next_page()`." + ) + + options = self._info_to_options(info) + return self._client._request_api_list(self._model, page=self.__class__, options=options) + + +class AsyncPaginator(Generic[_T, AsyncPageT]): + def __init__( + self, + client: AsyncAPIClient, + options: FinalRequestOptions, + page_cls: Type[AsyncPageT], + model: Type[_T], + ) -> None: + self._model = model + self._client = client + self._options = options + self._page_cls = page_cls + + def __await__(self) -> Generator[Any, None, AsyncPageT]: + return self._get_page().__await__() + + async def _get_page(self) -> AsyncPageT: + def _parser(resp: AsyncPageT) -> AsyncPageT: + resp._set_private_attributes( + model=self._model, + options=self._options, + client=self._client, + ) + return resp + + self._options.post_parser = _parser + + return await self._client.request(self._page_cls, self._options) + + async def __aiter__(self) -> AsyncIterator[_T]: + # https://github.com/microsoft/pyright/issues/3464 + page = cast( + AsyncPageT, + await self, # type: ignore + ) + async for item in page: + yield item + + +class BaseAsyncPage(BasePage[_T], Generic[_T]): + _client: AsyncAPIClient = pydantic.PrivateAttr() + + def _set_private_attributes( + self, + model: Type[_T], + client: AsyncAPIClient, + options: FinalRequestOptions, + ) -> None: + if (not PYDANTIC_V1) and getattr(self, "__pydantic_private__", None) is None: + self.__pydantic_private__ = {} + + self._model = model + self._client = client + self._options = options + + async def __aiter__(self) -> AsyncIterator[_T]: + async for page in self.iter_pages(): + for item in page._get_page_items(): + yield item + + async def iter_pages(self: AsyncPageT) -> AsyncIterator[AsyncPageT]: + page = self + while True: + yield page + if page.has_next_page(): + page = await page.get_next_page() + else: + return + + async def get_next_page(self: AsyncPageT) -> AsyncPageT: + info = self.next_page_info() + if not info: + raise RuntimeError( + "No next page expected; please check `.has_next_page()` before calling `.get_next_page()`." + ) + + options = self._info_to_options(info) + return await self._client._request_api_list(self._model, page=self.__class__, options=options) + + +_HttpxClientT = TypeVar("_HttpxClientT", bound=Union[httpx.Client, httpx.AsyncClient]) +_DefaultStreamT = TypeVar("_DefaultStreamT", bound=Union[Stream[Any], AsyncStream[Any]]) + + +class BaseClient(Generic[_HttpxClientT, _DefaultStreamT]): + _client: _HttpxClientT + _version: str + _base_url: URL + max_retries: int + timeout: Union[float, Timeout, None] + _strict_response_validation: bool + _idempotency_header: str | None + _default_stream_cls: type[_DefaultStreamT] | None = None + + def __init__( + self, + *, + version: str, + base_url: str | URL, + _strict_response_validation: bool, + max_retries: int = DEFAULT_MAX_RETRIES, + timeout: float | Timeout | None = DEFAULT_TIMEOUT, + custom_headers: Mapping[str, str] | None = None, + custom_query: Mapping[str, object] | None = None, + ) -> None: + self._version = version + self._base_url = self._enforce_trailing_slash(URL(base_url)) + self.max_retries = max_retries + self.timeout = timeout + self._custom_headers = custom_headers or {} + self._custom_query = custom_query or {} + self._strict_response_validation = _strict_response_validation + self._idempotency_header = None + self._platform: Platform | None = None + + if max_retries is None: # pyright: ignore[reportUnnecessaryComparison] + raise TypeError( + "max_retries cannot be None. If you want to disable retries, pass `0`; if you want unlimited retries, pass `math.inf` or a very high number; if you want the default behavior, pass `unlayer.DEFAULT_MAX_RETRIES`" + ) + + def _enforce_trailing_slash(self, url: URL) -> URL: + if url.raw_path.endswith(b"/"): + return url + return url.copy_with(raw_path=url.raw_path + b"/") + + def _make_status_error_from_response( + self, + response: httpx.Response, + ) -> APIStatusError: + if response.is_closed and not response.is_stream_consumed: + # We can't read the response body as it has been closed + # before it was read. This can happen if an event hook + # raises a status error. + body = None + err_msg = f"Error code: {response.status_code}" + else: + err_text = response.text.strip() + body = err_text + + try: + body = json.loads(err_text) + err_msg = f"Error code: {response.status_code} - {body}" + except Exception: + err_msg = err_text or f"Error code: {response.status_code}" + + return self._make_status_error(err_msg, body=body, response=response) + + def _make_status_error( + self, + err_msg: str, + *, + body: object, + response: httpx.Response, + ) -> _exceptions.APIStatusError: + raise NotImplementedError() + + def _build_headers(self, options: FinalRequestOptions, *, retries_taken: int = 0) -> httpx.Headers: + custom_headers = options.headers or {} + headers_dict = _merge_mappings(self.default_headers, custom_headers) + self._validate_headers(headers_dict, custom_headers) + + # headers are case-insensitive while dictionaries are not. + headers = httpx.Headers(headers_dict) + + idempotency_header = self._idempotency_header + if idempotency_header and options.idempotency_key and idempotency_header not in headers: + headers[idempotency_header] = options.idempotency_key + + # Don't set these headers if they were already set or removed by the caller. We check + # `custom_headers`, which can contain `Omit()`, instead of `headers` to account for the removal case. + lower_custom_headers = [header.lower() for header in custom_headers] + if "x-stainless-retry-count" not in lower_custom_headers: + headers["x-stainless-retry-count"] = str(retries_taken) + if "x-stainless-read-timeout" not in lower_custom_headers: + timeout = self.timeout if isinstance(options.timeout, NotGiven) else options.timeout + if isinstance(timeout, Timeout): + timeout = timeout.read + if timeout is not None: + headers["x-stainless-read-timeout"] = str(timeout) + + return headers + + def _prepare_url(self, url: str) -> URL: + """ + Merge a URL argument together with any 'base_url' on the client, + to create the URL used for the outgoing request. + """ + # Copied from httpx's `_merge_url` method. + merge_url = URL(url) + if merge_url.is_relative_url: + merge_raw_path = self.base_url.raw_path + merge_url.raw_path.lstrip(b"/") + return self.base_url.copy_with(raw_path=merge_raw_path) + + return merge_url + + def _make_sse_decoder(self) -> SSEDecoder | SSEBytesDecoder: + return SSEDecoder() + + def _build_request( + self, + options: FinalRequestOptions, + *, + retries_taken: int = 0, + ) -> httpx.Request: + if log.isEnabledFor(logging.DEBUG): + log.debug("Request options: %s", model_dump(options, exclude_unset=True)) + + kwargs: dict[str, Any] = {} + + json_data = options.json_data + if options.extra_json is not None: + if json_data is None: + json_data = cast(Body, options.extra_json) + elif is_mapping(json_data): + json_data = _merge_mappings(json_data, options.extra_json) + else: + raise RuntimeError(f"Unexpected JSON data type, {type(json_data)}, cannot merge with `extra_body`") + + headers = self._build_headers(options, retries_taken=retries_taken) + params = _merge_mappings(self.default_query, options.params) + content_type = headers.get("Content-Type") + files = options.files + + # If the given Content-Type header is multipart/form-data then it + # has to be removed so that httpx can generate the header with + # additional information for us as it has to be in this form + # for the server to be able to correctly parse the request: + # multipart/form-data; boundary=---abc-- + if content_type is not None and content_type.startswith("multipart/form-data"): + if "boundary" not in content_type: + # only remove the header if the boundary hasn't been explicitly set + # as the caller doesn't want httpx to come up with their own boundary + headers.pop("Content-Type") + + # As we are now sending multipart/form-data instead of application/json + # we need to tell httpx to use it, https://www.python-httpx.org/advanced/clients/#multipart-file-encoding + if json_data: + if not is_dict(json_data): + raise TypeError( + f"Expected query input to be a dictionary for multipart requests but got {type(json_data)} instead." + ) + kwargs["data"] = self._serialize_multipartform(json_data) + + # httpx determines whether or not to send a "multipart/form-data" + # request based on the truthiness of the "files" argument. + # This gets around that issue by generating a dict value that + # evaluates to true. + # + # https://github.com/encode/httpx/discussions/2399#discussioncomment-3814186 + if not files: + files = cast(HttpxRequestFiles, ForceMultipartDict()) + + prepared_url = self._prepare_url(options.url) + if "_" in prepared_url.host: + # work around https://github.com/encode/httpx/discussions/2880 + kwargs["extensions"] = {"sni_hostname": prepared_url.host.replace("_", "-")} + + is_body_allowed = options.method.lower() != "get" + + if is_body_allowed: + if isinstance(json_data, bytes): + kwargs["content"] = json_data + else: + kwargs["json"] = json_data if is_given(json_data) else None + kwargs["files"] = files + else: + headers.pop("Content-Type", None) + kwargs.pop("data", None) + + # TODO: report this error to httpx + return self._client.build_request( # pyright: ignore[reportUnknownMemberType] + headers=headers, + timeout=self.timeout if isinstance(options.timeout, NotGiven) else options.timeout, + method=options.method, + url=prepared_url, + # the `Query` type that we use is incompatible with qs' + # `Params` type as it needs to be typed as `Mapping[str, object]` + # so that passing a `TypedDict` doesn't cause an error. + # https://github.com/microsoft/pyright/issues/3526#event-6715453066 + params=self.qs.stringify(cast(Mapping[str, Any], params)) if params else None, + **kwargs, + ) + + def _serialize_multipartform(self, data: Mapping[object, object]) -> dict[str, object]: + items = self.qs.stringify_items( + # TODO: type ignore is required as stringify_items is well typed but we can't be + # well typed without heavy validation. + data, # type: ignore + array_format="brackets", + ) + serialized: dict[str, object] = {} + for key, value in items: + existing = serialized.get(key) + + if not existing: + serialized[key] = value + continue + + # If a value has already been set for this key then that + # means we're sending data like `array[]=[1, 2, 3]` and we + # need to tell httpx that we want to send multiple values with + # the same key which is done by using a list or a tuple. + # + # Note: 2d arrays should never result in the same key at both + # levels so it's safe to assume that if the value is a list, + # it was because we changed it to be a list. + if is_list(existing): + existing.append(value) + else: + serialized[key] = [existing, value] + + return serialized + + def _maybe_override_cast_to(self, cast_to: type[ResponseT], options: FinalRequestOptions) -> type[ResponseT]: + if not is_given(options.headers): + return cast_to + + # make a copy of the headers so we don't mutate user-input + headers = dict(options.headers) + + # we internally support defining a temporary header to override the + # default `cast_to` type for use with `.with_raw_response` and `.with_streaming_response` + # see _response.py for implementation details + override_cast_to = headers.pop(OVERRIDE_CAST_TO_HEADER, not_given) + if is_given(override_cast_to): + options.headers = headers + return cast(Type[ResponseT], override_cast_to) + + return cast_to + + def _should_stream_response_body(self, request: httpx.Request) -> bool: + return request.headers.get(RAW_RESPONSE_HEADER) == "stream" # type: ignore[no-any-return] + + def _process_response_data( + self, + *, + data: object, + cast_to: type[ResponseT], + response: httpx.Response, + ) -> ResponseT: + if data is None: + return cast(ResponseT, None) + + if cast_to is object: + return cast(ResponseT, data) + + try: + if inspect.isclass(cast_to) and issubclass(cast_to, ModelBuilderProtocol): + return cast(ResponseT, cast_to.build(response=response, data=data)) + + if self._strict_response_validation: + return cast(ResponseT, validate_type(type_=cast_to, value=data)) + + return cast(ResponseT, construct_type(type_=cast_to, value=data)) + except pydantic.ValidationError as err: + raise APIResponseValidationError(response=response, body=data) from err + + @property + def qs(self) -> Querystring: + return Querystring() + + @property + def custom_auth(self) -> httpx.Auth | None: + return None + + @property + def auth_headers(self) -> dict[str, str]: + return {} + + @property + def default_headers(self) -> dict[str, str | Omit]: + return { + "Accept": "application/json", + "Content-Type": "application/json", + "User-Agent": self.user_agent, + **self.platform_headers(), + **self.auth_headers, + **self._custom_headers, + } + + @property + def default_query(self) -> dict[str, object]: + return { + **self._custom_query, + } + + def _validate_headers( + self, + headers: Headers, # noqa: ARG002 + custom_headers: Headers, # noqa: ARG002 + ) -> None: + """Validate the given default headers and custom headers. + + Does nothing by default. + """ + return + + @property + def user_agent(self) -> str: + return f"{self.__class__.__name__}/Python {self._version}" + + @property + def base_url(self) -> URL: + return self._base_url + + @base_url.setter + def base_url(self, url: URL | str) -> None: + self._base_url = self._enforce_trailing_slash(url if isinstance(url, URL) else URL(url)) + + def platform_headers(self) -> Dict[str, str]: + # the actual implementation is in a separate `lru_cache` decorated + # function because adding `lru_cache` to methods will leak memory + # https://github.com/python/cpython/issues/88476 + return platform_headers(self._version, platform=self._platform) + + def _parse_retry_after_header(self, response_headers: Optional[httpx.Headers] = None) -> float | None: + """Returns a float of the number of seconds (not milliseconds) to wait after retrying, or None if unspecified. + + About the Retry-After header: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Retry-After + See also https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Retry-After#syntax + """ + if response_headers is None: + return None + + # First, try the non-standard `retry-after-ms` header for milliseconds, + # which is more precise than integer-seconds `retry-after` + try: + retry_ms_header = response_headers.get("retry-after-ms", None) + return float(retry_ms_header) / 1000 + except (TypeError, ValueError): + pass + + # Next, try parsing `retry-after` header as seconds (allowing nonstandard floats). + retry_header = response_headers.get("retry-after") + try: + # note: the spec indicates that this should only ever be an integer + # but if someone sends a float there's no reason for us to not respect it + return float(retry_header) + except (TypeError, ValueError): + pass + + # Last, try parsing `retry-after` as a date. + retry_date_tuple = email.utils.parsedate_tz(retry_header) + if retry_date_tuple is None: + return None + + retry_date = email.utils.mktime_tz(retry_date_tuple) + return float(retry_date - time.time()) + + def _calculate_retry_timeout( + self, + remaining_retries: int, + options: FinalRequestOptions, + response_headers: Optional[httpx.Headers] = None, + ) -> float: + max_retries = options.get_max_retries(self.max_retries) + + # If the API asks us to wait a certain amount of time (and it's a reasonable amount), just do what it says. + retry_after = self._parse_retry_after_header(response_headers) + if retry_after is not None and 0 < retry_after <= 60: + return retry_after + + # Also cap retry count to 1000 to avoid any potential overflows with `pow` + nb_retries = min(max_retries - remaining_retries, 1000) + + # Apply exponential backoff, but not more than the max. + sleep_seconds = min(INITIAL_RETRY_DELAY * pow(2.0, nb_retries), MAX_RETRY_DELAY) + + # Apply some jitter, plus-or-minus half a second. + jitter = 1 - 0.25 * random() + timeout = sleep_seconds * jitter + return timeout if timeout >= 0 else 0 + + def _should_retry(self, response: httpx.Response) -> bool: + # Note: this is not a standard header + should_retry_header = response.headers.get("x-should-retry") + + # If the server explicitly says whether or not to retry, obey. + if should_retry_header == "true": + log.debug("Retrying as header `x-should-retry` is set to `true`") + return True + if should_retry_header == "false": + log.debug("Not retrying as header `x-should-retry` is set to `false`") + return False + + # Retry on request timeouts. + if response.status_code == 408: + log.debug("Retrying due to status code %i", response.status_code) + return True + + # Retry on lock timeouts. + if response.status_code == 409: + log.debug("Retrying due to status code %i", response.status_code) + return True + + # Retry on rate limits. + if response.status_code == 429: + log.debug("Retrying due to status code %i", response.status_code) + return True + + # Retry internal errors. + if response.status_code >= 500: + log.debug("Retrying due to status code %i", response.status_code) + return True + + log.debug("Not retrying") + return False + + def _idempotency_key(self) -> str: + return f"stainless-python-retry-{uuid.uuid4()}" + + +class _DefaultHttpxClient(httpx.Client): + def __init__(self, **kwargs: Any) -> None: + kwargs.setdefault("timeout", DEFAULT_TIMEOUT) + kwargs.setdefault("limits", DEFAULT_CONNECTION_LIMITS) + kwargs.setdefault("follow_redirects", True) + super().__init__(**kwargs) + + +if TYPE_CHECKING: + DefaultHttpxClient = httpx.Client + """An alias to `httpx.Client` that provides the same defaults that this SDK + uses internally. + + This is useful because overriding the `http_client` with your own instance of + `httpx.Client` will result in httpx's defaults being used, not ours. + """ +else: + DefaultHttpxClient = _DefaultHttpxClient + + +class SyncHttpxClientWrapper(DefaultHttpxClient): + def __del__(self) -> None: + if self.is_closed: + return + + try: + self.close() + except Exception: + pass + + +class SyncAPIClient(BaseClient[httpx.Client, Stream[Any]]): + _client: httpx.Client + _default_stream_cls: type[Stream[Any]] | None = None + + def __init__( + self, + *, + version: str, + base_url: str | URL, + max_retries: int = DEFAULT_MAX_RETRIES, + timeout: float | Timeout | None | NotGiven = not_given, + http_client: httpx.Client | None = None, + custom_headers: Mapping[str, str] | None = None, + custom_query: Mapping[str, object] | None = None, + _strict_response_validation: bool, + ) -> None: + if not is_given(timeout): + # if the user passed in a custom http client with a non-default + # timeout set then we use that timeout. + # + # note: there is an edge case here where the user passes in a client + # where they've explicitly set the timeout to match the default timeout + # as this check is structural, meaning that we'll think they didn't + # pass in a timeout and will ignore it + if http_client and http_client.timeout != HTTPX_DEFAULT_TIMEOUT: + timeout = http_client.timeout + else: + timeout = DEFAULT_TIMEOUT + + if http_client is not None and not isinstance(http_client, httpx.Client): # pyright: ignore[reportUnnecessaryIsInstance] + raise TypeError( + f"Invalid `http_client` argument; Expected an instance of `httpx.Client` but got {type(http_client)}" + ) + + super().__init__( + version=version, + # cast to a valid type because mypy doesn't understand our type narrowing + timeout=cast(Timeout, timeout), + base_url=base_url, + max_retries=max_retries, + custom_query=custom_query, + custom_headers=custom_headers, + _strict_response_validation=_strict_response_validation, + ) + self._client = http_client or SyncHttpxClientWrapper( + base_url=base_url, + # cast to a valid type because mypy doesn't understand our type narrowing + timeout=cast(Timeout, timeout), + ) + + def is_closed(self) -> bool: + return self._client.is_closed + + def close(self) -> None: + """Close the underlying HTTPX client. + + The client will *not* be usable after this. + """ + # If an error is thrown while constructing a client, self._client + # may not be present + if hasattr(self, "_client"): + self._client.close() + + def __enter__(self: _T) -> _T: + return self + + def __exit__( + self, + exc_type: type[BaseException] | None, + exc: BaseException | None, + exc_tb: TracebackType | None, + ) -> None: + self.close() + + def _prepare_options( + self, + options: FinalRequestOptions, # noqa: ARG002 + ) -> FinalRequestOptions: + """Hook for mutating the given options""" + return options + + def _prepare_request( + self, + request: httpx.Request, # noqa: ARG002 + ) -> None: + """This method is used as a callback for mutating the `Request` object + after it has been constructed. + This is useful for cases where you want to add certain headers based off of + the request properties, e.g. `url`, `method` etc. + """ + return None + + @overload + def request( + self, + cast_to: Type[ResponseT], + options: FinalRequestOptions, + *, + stream: Literal[True], + stream_cls: Type[_StreamT], + ) -> _StreamT: ... + + @overload + def request( + self, + cast_to: Type[ResponseT], + options: FinalRequestOptions, + *, + stream: Literal[False] = False, + ) -> ResponseT: ... + + @overload + def request( + self, + cast_to: Type[ResponseT], + options: FinalRequestOptions, + *, + stream: bool = False, + stream_cls: Type[_StreamT] | None = None, + ) -> ResponseT | _StreamT: ... + + def request( + self, + cast_to: Type[ResponseT], + options: FinalRequestOptions, + *, + stream: bool = False, + stream_cls: type[_StreamT] | None = None, + ) -> ResponseT | _StreamT: + cast_to = self._maybe_override_cast_to(cast_to, options) + + # create a copy of the options we were given so that if the + # options are mutated later & we then retry, the retries are + # given the original options + input_options = model_copy(options) + if input_options.idempotency_key is None and input_options.method.lower() != "get": + # ensure the idempotency key is reused between requests + input_options.idempotency_key = self._idempotency_key() + + response: httpx.Response | None = None + max_retries = input_options.get_max_retries(self.max_retries) + + retries_taken = 0 + for retries_taken in range(max_retries + 1): + options = model_copy(input_options) + options = self._prepare_options(options) + + remaining_retries = max_retries - retries_taken + request = self._build_request(options, retries_taken=retries_taken) + self._prepare_request(request) + + kwargs: HttpxSendArgs = {} + if self.custom_auth is not None: + kwargs["auth"] = self.custom_auth + + if options.follow_redirects is not None: + kwargs["follow_redirects"] = options.follow_redirects + + log.debug("Sending HTTP Request: %s %s", request.method, request.url) + + response = None + try: + response = self._client.send( + request, + stream=stream or self._should_stream_response_body(request=request), + **kwargs, + ) + except httpx.TimeoutException as err: + log.debug("Encountered httpx.TimeoutException", exc_info=True) + + if remaining_retries > 0: + self._sleep_for_retry( + retries_taken=retries_taken, + max_retries=max_retries, + options=input_options, + response=None, + ) + continue + + log.debug("Raising timeout error") + raise APITimeoutError(request=request) from err + except Exception as err: + log.debug("Encountered Exception", exc_info=True) + + if remaining_retries > 0: + self._sleep_for_retry( + retries_taken=retries_taken, + max_retries=max_retries, + options=input_options, + response=None, + ) + continue + + log.debug("Raising connection error") + raise APIConnectionError(request=request) from err + + log.debug( + 'HTTP Response: %s %s "%i %s" %s', + request.method, + request.url, + response.status_code, + response.reason_phrase, + response.headers, + ) + + try: + response.raise_for_status() + except httpx.HTTPStatusError as err: # thrown on 4xx and 5xx status code + log.debug("Encountered httpx.HTTPStatusError", exc_info=True) + + if remaining_retries > 0 and self._should_retry(err.response): + err.response.close() + self._sleep_for_retry( + retries_taken=retries_taken, + max_retries=max_retries, + options=input_options, + response=response, + ) + continue + + # If the response is streamed then we need to explicitly read the response + # to completion before attempting to access the response text. + if not err.response.is_closed: + err.response.read() + + log.debug("Re-raising status error") + raise self._make_status_error_from_response(err.response) from None + + break + + assert response is not None, "could not resolve response (should never happen)" + return self._process_response( + cast_to=cast_to, + options=options, + response=response, + stream=stream, + stream_cls=stream_cls, + retries_taken=retries_taken, + ) + + def _sleep_for_retry( + self, *, retries_taken: int, max_retries: int, options: FinalRequestOptions, response: httpx.Response | None + ) -> None: + remaining_retries = max_retries - retries_taken + if remaining_retries == 1: + log.debug("1 retry left") + else: + log.debug("%i retries left", remaining_retries) + + timeout = self._calculate_retry_timeout(remaining_retries, options, response.headers if response else None) + log.info("Retrying request to %s in %f seconds", options.url, timeout) + + time.sleep(timeout) + + def _process_response( + self, + *, + cast_to: Type[ResponseT], + options: FinalRequestOptions, + response: httpx.Response, + stream: bool, + stream_cls: type[Stream[Any]] | type[AsyncStream[Any]] | None, + retries_taken: int = 0, + ) -> ResponseT: + origin = get_origin(cast_to) or cast_to + + if ( + inspect.isclass(origin) + and issubclass(origin, BaseAPIResponse) + # we only want to actually return the custom BaseAPIResponse class if we're + # returning the raw response, or if we're not streaming SSE, as if we're streaming + # SSE then `cast_to` doesn't actively reflect the type we need to parse into + and (not stream or bool(response.request.headers.get(RAW_RESPONSE_HEADER))) + ): + if not issubclass(origin, APIResponse): + raise TypeError(f"API Response types must subclass {APIResponse}; Received {origin}") + + response_cls = cast("type[BaseAPIResponse[Any]]", cast_to) + return cast( + ResponseT, + response_cls( + raw=response, + client=self, + cast_to=extract_response_type(response_cls), + stream=stream, + stream_cls=stream_cls, + options=options, + retries_taken=retries_taken, + ), + ) + + if cast_to == httpx.Response: + return cast(ResponseT, response) + + api_response = APIResponse( + raw=response, + client=self, + cast_to=cast("type[ResponseT]", cast_to), # pyright: ignore[reportUnnecessaryCast] + stream=stream, + stream_cls=stream_cls, + options=options, + retries_taken=retries_taken, + ) + if bool(response.request.headers.get(RAW_RESPONSE_HEADER)): + return cast(ResponseT, api_response) + + return api_response.parse() + + def _request_api_list( + self, + model: Type[object], + page: Type[SyncPageT], + options: FinalRequestOptions, + ) -> SyncPageT: + def _parser(resp: SyncPageT) -> SyncPageT: + resp._set_private_attributes( + client=self, + model=model, + options=options, + ) + return resp + + options.post_parser = _parser + + return self.request(page, options, stream=False) + + @overload + def get( + self, + path: str, + *, + cast_to: Type[ResponseT], + options: RequestOptions = {}, + stream: Literal[False] = False, + ) -> ResponseT: ... + + @overload + def get( + self, + path: str, + *, + cast_to: Type[ResponseT], + options: RequestOptions = {}, + stream: Literal[True], + stream_cls: type[_StreamT], + ) -> _StreamT: ... + + @overload + def get( + self, + path: str, + *, + cast_to: Type[ResponseT], + options: RequestOptions = {}, + stream: bool, + stream_cls: type[_StreamT] | None = None, + ) -> ResponseT | _StreamT: ... + + def get( + self, + path: str, + *, + cast_to: Type[ResponseT], + options: RequestOptions = {}, + stream: bool = False, + stream_cls: type[_StreamT] | None = None, + ) -> ResponseT | _StreamT: + opts = FinalRequestOptions.construct(method="get", url=path, **options) + # cast is required because mypy complains about returning Any even though + # it understands the type variables + return cast(ResponseT, self.request(cast_to, opts, stream=stream, stream_cls=stream_cls)) + + @overload + def post( + self, + path: str, + *, + cast_to: Type[ResponseT], + body: Body | None = None, + options: RequestOptions = {}, + files: RequestFiles | None = None, + stream: Literal[False] = False, + ) -> ResponseT: ... + + @overload + def post( + self, + path: str, + *, + cast_to: Type[ResponseT], + body: Body | None = None, + options: RequestOptions = {}, + files: RequestFiles | None = None, + stream: Literal[True], + stream_cls: type[_StreamT], + ) -> _StreamT: ... + + @overload + def post( + self, + path: str, + *, + cast_to: Type[ResponseT], + body: Body | None = None, + options: RequestOptions = {}, + files: RequestFiles | None = None, + stream: bool, + stream_cls: type[_StreamT] | None = None, + ) -> ResponseT | _StreamT: ... + + def post( + self, + path: str, + *, + cast_to: Type[ResponseT], + body: Body | None = None, + options: RequestOptions = {}, + files: RequestFiles | None = None, + stream: bool = False, + stream_cls: type[_StreamT] | None = None, + ) -> ResponseT | _StreamT: + opts = FinalRequestOptions.construct( + method="post", url=path, json_data=body, files=to_httpx_files(files), **options + ) + return cast(ResponseT, self.request(cast_to, opts, stream=stream, stream_cls=stream_cls)) + + def patch( + self, + path: str, + *, + cast_to: Type[ResponseT], + body: Body | None = None, + options: RequestOptions = {}, + ) -> ResponseT: + opts = FinalRequestOptions.construct(method="patch", url=path, json_data=body, **options) + return self.request(cast_to, opts) + + def put( + self, + path: str, + *, + cast_to: Type[ResponseT], + body: Body | None = None, + files: RequestFiles | None = None, + options: RequestOptions = {}, + ) -> ResponseT: + opts = FinalRequestOptions.construct( + method="put", url=path, json_data=body, files=to_httpx_files(files), **options + ) + return self.request(cast_to, opts) + + def delete( + self, + path: str, + *, + cast_to: Type[ResponseT], + body: Body | None = None, + options: RequestOptions = {}, + ) -> ResponseT: + opts = FinalRequestOptions.construct(method="delete", url=path, json_data=body, **options) + return self.request(cast_to, opts) + + def get_api_list( + self, + path: str, + *, + model: Type[object], + page: Type[SyncPageT], + body: Body | None = None, + options: RequestOptions = {}, + method: str = "get", + ) -> SyncPageT: + opts = FinalRequestOptions.construct(method=method, url=path, json_data=body, **options) + return self._request_api_list(model, page, opts) + + +class _DefaultAsyncHttpxClient(httpx.AsyncClient): + def __init__(self, **kwargs: Any) -> None: + kwargs.setdefault("timeout", DEFAULT_TIMEOUT) + kwargs.setdefault("limits", DEFAULT_CONNECTION_LIMITS) + kwargs.setdefault("follow_redirects", True) + super().__init__(**kwargs) + + +try: + import httpx_aiohttp +except ImportError: + + class _DefaultAioHttpClient(httpx.AsyncClient): + def __init__(self, **_kwargs: Any) -> None: + raise RuntimeError("To use the aiohttp client you must have installed the package with the `aiohttp` extra") +else: + + class _DefaultAioHttpClient(httpx_aiohttp.HttpxAiohttpClient): # type: ignore + def __init__(self, **kwargs: Any) -> None: + kwargs.setdefault("timeout", DEFAULT_TIMEOUT) + kwargs.setdefault("limits", DEFAULT_CONNECTION_LIMITS) + kwargs.setdefault("follow_redirects", True) + + super().__init__(**kwargs) + + +if TYPE_CHECKING: + DefaultAsyncHttpxClient = httpx.AsyncClient + """An alias to `httpx.AsyncClient` that provides the same defaults that this SDK + uses internally. + + This is useful because overriding the `http_client` with your own instance of + `httpx.AsyncClient` will result in httpx's defaults being used, not ours. + """ + + DefaultAioHttpClient = httpx.AsyncClient + """An alias to `httpx.AsyncClient` that changes the default HTTP transport to `aiohttp`.""" +else: + DefaultAsyncHttpxClient = _DefaultAsyncHttpxClient + DefaultAioHttpClient = _DefaultAioHttpClient + + +class AsyncHttpxClientWrapper(DefaultAsyncHttpxClient): + def __del__(self) -> None: + if self.is_closed: + return + + try: + # TODO(someday): support non asyncio runtimes here + asyncio.get_running_loop().create_task(self.aclose()) + except Exception: + pass + + +class AsyncAPIClient(BaseClient[httpx.AsyncClient, AsyncStream[Any]]): + _client: httpx.AsyncClient + _default_stream_cls: type[AsyncStream[Any]] | None = None + + def __init__( + self, + *, + version: str, + base_url: str | URL, + _strict_response_validation: bool, + max_retries: int = DEFAULT_MAX_RETRIES, + timeout: float | Timeout | None | NotGiven = not_given, + http_client: httpx.AsyncClient | None = None, + custom_headers: Mapping[str, str] | None = None, + custom_query: Mapping[str, object] | None = None, + ) -> None: + if not is_given(timeout): + # if the user passed in a custom http client with a non-default + # timeout set then we use that timeout. + # + # note: there is an edge case here where the user passes in a client + # where they've explicitly set the timeout to match the default timeout + # as this check is structural, meaning that we'll think they didn't + # pass in a timeout and will ignore it + if http_client and http_client.timeout != HTTPX_DEFAULT_TIMEOUT: + timeout = http_client.timeout + else: + timeout = DEFAULT_TIMEOUT + + if http_client is not None and not isinstance(http_client, httpx.AsyncClient): # pyright: ignore[reportUnnecessaryIsInstance] + raise TypeError( + f"Invalid `http_client` argument; Expected an instance of `httpx.AsyncClient` but got {type(http_client)}" + ) + + super().__init__( + version=version, + base_url=base_url, + # cast to a valid type because mypy doesn't understand our type narrowing + timeout=cast(Timeout, timeout), + max_retries=max_retries, + custom_query=custom_query, + custom_headers=custom_headers, + _strict_response_validation=_strict_response_validation, + ) + self._client = http_client or AsyncHttpxClientWrapper( + base_url=base_url, + # cast to a valid type because mypy doesn't understand our type narrowing + timeout=cast(Timeout, timeout), + ) + + def is_closed(self) -> bool: + return self._client.is_closed + + async def close(self) -> None: + """Close the underlying HTTPX client. + + The client will *not* be usable after this. + """ + await self._client.aclose() + + async def __aenter__(self: _T) -> _T: + return self + + async def __aexit__( + self, + exc_type: type[BaseException] | None, + exc: BaseException | None, + exc_tb: TracebackType | None, + ) -> None: + await self.close() + + async def _prepare_options( + self, + options: FinalRequestOptions, # noqa: ARG002 + ) -> FinalRequestOptions: + """Hook for mutating the given options""" + return options + + async def _prepare_request( + self, + request: httpx.Request, # noqa: ARG002 + ) -> None: + """This method is used as a callback for mutating the `Request` object + after it has been constructed. + This is useful for cases where you want to add certain headers based off of + the request properties, e.g. `url`, `method` etc. + """ + return None + + @overload + async def request( + self, + cast_to: Type[ResponseT], + options: FinalRequestOptions, + *, + stream: Literal[False] = False, + ) -> ResponseT: ... + + @overload + async def request( + self, + cast_to: Type[ResponseT], + options: FinalRequestOptions, + *, + stream: Literal[True], + stream_cls: type[_AsyncStreamT], + ) -> _AsyncStreamT: ... + + @overload + async def request( + self, + cast_to: Type[ResponseT], + options: FinalRequestOptions, + *, + stream: bool, + stream_cls: type[_AsyncStreamT] | None = None, + ) -> ResponseT | _AsyncStreamT: ... + + async def request( + self, + cast_to: Type[ResponseT], + options: FinalRequestOptions, + *, + stream: bool = False, + stream_cls: type[_AsyncStreamT] | None = None, + ) -> ResponseT | _AsyncStreamT: + if self._platform is None: + # `get_platform` can make blocking IO calls so we + # execute it earlier while we are in an async context + self._platform = await asyncify(get_platform)() + + cast_to = self._maybe_override_cast_to(cast_to, options) + + # create a copy of the options we were given so that if the + # options are mutated later & we then retry, the retries are + # given the original options + input_options = model_copy(options) + if input_options.idempotency_key is None and input_options.method.lower() != "get": + # ensure the idempotency key is reused between requests + input_options.idempotency_key = self._idempotency_key() + + response: httpx.Response | None = None + max_retries = input_options.get_max_retries(self.max_retries) + + retries_taken = 0 + for retries_taken in range(max_retries + 1): + options = model_copy(input_options) + options = await self._prepare_options(options) + + remaining_retries = max_retries - retries_taken + request = self._build_request(options, retries_taken=retries_taken) + await self._prepare_request(request) + + kwargs: HttpxSendArgs = {} + if self.custom_auth is not None: + kwargs["auth"] = self.custom_auth + + if options.follow_redirects is not None: + kwargs["follow_redirects"] = options.follow_redirects + + log.debug("Sending HTTP Request: %s %s", request.method, request.url) + + response = None + try: + response = await self._client.send( + request, + stream=stream or self._should_stream_response_body(request=request), + **kwargs, + ) + except httpx.TimeoutException as err: + log.debug("Encountered httpx.TimeoutException", exc_info=True) + + if remaining_retries > 0: + await self._sleep_for_retry( + retries_taken=retries_taken, + max_retries=max_retries, + options=input_options, + response=None, + ) + continue + + log.debug("Raising timeout error") + raise APITimeoutError(request=request) from err + except Exception as err: + log.debug("Encountered Exception", exc_info=True) + + if remaining_retries > 0: + await self._sleep_for_retry( + retries_taken=retries_taken, + max_retries=max_retries, + options=input_options, + response=None, + ) + continue + + log.debug("Raising connection error") + raise APIConnectionError(request=request) from err + + log.debug( + 'HTTP Response: %s %s "%i %s" %s', + request.method, + request.url, + response.status_code, + response.reason_phrase, + response.headers, + ) + + try: + response.raise_for_status() + except httpx.HTTPStatusError as err: # thrown on 4xx and 5xx status code + log.debug("Encountered httpx.HTTPStatusError", exc_info=True) + + if remaining_retries > 0 and self._should_retry(err.response): + await err.response.aclose() + await self._sleep_for_retry( + retries_taken=retries_taken, + max_retries=max_retries, + options=input_options, + response=response, + ) + continue + + # If the response is streamed then we need to explicitly read the response + # to completion before attempting to access the response text. + if not err.response.is_closed: + await err.response.aread() + + log.debug("Re-raising status error") + raise self._make_status_error_from_response(err.response) from None + + break + + assert response is not None, "could not resolve response (should never happen)" + return await self._process_response( + cast_to=cast_to, + options=options, + response=response, + stream=stream, + stream_cls=stream_cls, + retries_taken=retries_taken, + ) + + async def _sleep_for_retry( + self, *, retries_taken: int, max_retries: int, options: FinalRequestOptions, response: httpx.Response | None + ) -> None: + remaining_retries = max_retries - retries_taken + if remaining_retries == 1: + log.debug("1 retry left") + else: + log.debug("%i retries left", remaining_retries) + + timeout = self._calculate_retry_timeout(remaining_retries, options, response.headers if response else None) + log.info("Retrying request to %s in %f seconds", options.url, timeout) + + await anyio.sleep(timeout) + + async def _process_response( + self, + *, + cast_to: Type[ResponseT], + options: FinalRequestOptions, + response: httpx.Response, + stream: bool, + stream_cls: type[Stream[Any]] | type[AsyncStream[Any]] | None, + retries_taken: int = 0, + ) -> ResponseT: + origin = get_origin(cast_to) or cast_to + + if ( + inspect.isclass(origin) + and issubclass(origin, BaseAPIResponse) + # we only want to actually return the custom BaseAPIResponse class if we're + # returning the raw response, or if we're not streaming SSE, as if we're streaming + # SSE then `cast_to` doesn't actively reflect the type we need to parse into + and (not stream or bool(response.request.headers.get(RAW_RESPONSE_HEADER))) + ): + if not issubclass(origin, AsyncAPIResponse): + raise TypeError(f"API Response types must subclass {AsyncAPIResponse}; Received {origin}") + + response_cls = cast("type[BaseAPIResponse[Any]]", cast_to) + return cast( + "ResponseT", + response_cls( + raw=response, + client=self, + cast_to=extract_response_type(response_cls), + stream=stream, + stream_cls=stream_cls, + options=options, + retries_taken=retries_taken, + ), + ) + + if cast_to == httpx.Response: + return cast(ResponseT, response) + + api_response = AsyncAPIResponse( + raw=response, + client=self, + cast_to=cast("type[ResponseT]", cast_to), # pyright: ignore[reportUnnecessaryCast] + stream=stream, + stream_cls=stream_cls, + options=options, + retries_taken=retries_taken, + ) + if bool(response.request.headers.get(RAW_RESPONSE_HEADER)): + return cast(ResponseT, api_response) + + return await api_response.parse() + + def _request_api_list( + self, + model: Type[_T], + page: Type[AsyncPageT], + options: FinalRequestOptions, + ) -> AsyncPaginator[_T, AsyncPageT]: + return AsyncPaginator(client=self, options=options, page_cls=page, model=model) + + @overload + async def get( + self, + path: str, + *, + cast_to: Type[ResponseT], + options: RequestOptions = {}, + stream: Literal[False] = False, + ) -> ResponseT: ... + + @overload + async def get( + self, + path: str, + *, + cast_to: Type[ResponseT], + options: RequestOptions = {}, + stream: Literal[True], + stream_cls: type[_AsyncStreamT], + ) -> _AsyncStreamT: ... + + @overload + async def get( + self, + path: str, + *, + cast_to: Type[ResponseT], + options: RequestOptions = {}, + stream: bool, + stream_cls: type[_AsyncStreamT] | None = None, + ) -> ResponseT | _AsyncStreamT: ... + + async def get( + self, + path: str, + *, + cast_to: Type[ResponseT], + options: RequestOptions = {}, + stream: bool = False, + stream_cls: type[_AsyncStreamT] | None = None, + ) -> ResponseT | _AsyncStreamT: + opts = FinalRequestOptions.construct(method="get", url=path, **options) + return await self.request(cast_to, opts, stream=stream, stream_cls=stream_cls) + + @overload + async def post( + self, + path: str, + *, + cast_to: Type[ResponseT], + body: Body | None = None, + files: RequestFiles | None = None, + options: RequestOptions = {}, + stream: Literal[False] = False, + ) -> ResponseT: ... + + @overload + async def post( + self, + path: str, + *, + cast_to: Type[ResponseT], + body: Body | None = None, + files: RequestFiles | None = None, + options: RequestOptions = {}, + stream: Literal[True], + stream_cls: type[_AsyncStreamT], + ) -> _AsyncStreamT: ... + + @overload + async def post( + self, + path: str, + *, + cast_to: Type[ResponseT], + body: Body | None = None, + files: RequestFiles | None = None, + options: RequestOptions = {}, + stream: bool, + stream_cls: type[_AsyncStreamT] | None = None, + ) -> ResponseT | _AsyncStreamT: ... + + async def post( + self, + path: str, + *, + cast_to: Type[ResponseT], + body: Body | None = None, + files: RequestFiles | None = None, + options: RequestOptions = {}, + stream: bool = False, + stream_cls: type[_AsyncStreamT] | None = None, + ) -> ResponseT | _AsyncStreamT: + opts = FinalRequestOptions.construct( + method="post", url=path, json_data=body, files=await async_to_httpx_files(files), **options + ) + return await self.request(cast_to, opts, stream=stream, stream_cls=stream_cls) + + async def patch( + self, + path: str, + *, + cast_to: Type[ResponseT], + body: Body | None = None, + options: RequestOptions = {}, + ) -> ResponseT: + opts = FinalRequestOptions.construct(method="patch", url=path, json_data=body, **options) + return await self.request(cast_to, opts) + + async def put( + self, + path: str, + *, + cast_to: Type[ResponseT], + body: Body | None = None, + files: RequestFiles | None = None, + options: RequestOptions = {}, + ) -> ResponseT: + opts = FinalRequestOptions.construct( + method="put", url=path, json_data=body, files=await async_to_httpx_files(files), **options + ) + return await self.request(cast_to, opts) + + async def delete( + self, + path: str, + *, + cast_to: Type[ResponseT], + body: Body | None = None, + options: RequestOptions = {}, + ) -> ResponseT: + opts = FinalRequestOptions.construct(method="delete", url=path, json_data=body, **options) + return await self.request(cast_to, opts) + + def get_api_list( + self, + path: str, + *, + model: Type[_T], + page: Type[AsyncPageT], + body: Body | None = None, + options: RequestOptions = {}, + method: str = "get", + ) -> AsyncPaginator[_T, AsyncPageT]: + opts = FinalRequestOptions.construct(method=method, url=path, json_data=body, **options) + return self._request_api_list(model, page, opts) + + +def make_request_options( + *, + query: Query | None = None, + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + idempotency_key: str | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + post_parser: PostParser | NotGiven = not_given, +) -> RequestOptions: + """Create a dict of type RequestOptions without keys of NotGiven values.""" + options: RequestOptions = {} + if extra_headers is not None: + options["headers"] = extra_headers + + if extra_body is not None: + options["extra_json"] = cast(AnyMapping, extra_body) + + if query is not None: + options["params"] = query + + if extra_query is not None: + options["params"] = {**options.get("params", {}), **extra_query} + + if not isinstance(timeout, NotGiven): + options["timeout"] = timeout + + if idempotency_key is not None: + options["idempotency_key"] = idempotency_key + + if is_given(post_parser): + # internal + options["post_parser"] = post_parser # type: ignore + + return options + + +class ForceMultipartDict(Dict[str, None]): + def __bool__(self) -> bool: + return True + + +class OtherPlatform: + def __init__(self, name: str) -> None: + self.name = name + + @override + def __str__(self) -> str: + return f"Other:{self.name}" + + +Platform = Union[ + OtherPlatform, + Literal[ + "MacOS", + "Linux", + "Windows", + "FreeBSD", + "OpenBSD", + "iOS", + "Android", + "Unknown", + ], +] + + +def get_platform() -> Platform: + try: + system = platform.system().lower() + platform_name = platform.platform().lower() + except Exception: + return "Unknown" + + if "iphone" in platform_name or "ipad" in platform_name: + # Tested using Python3IDE on an iPhone 11 and Pythonista on an iPad 7 + # system is Darwin and platform_name is a string like: + # - Darwin-21.6.0-iPhone12,1-64bit + # - Darwin-21.6.0-iPad7,11-64bit + return "iOS" + + if system == "darwin": + return "MacOS" + + if system == "windows": + return "Windows" + + if "android" in platform_name: + # Tested using Pydroid 3 + # system is Linux and platform_name is a string like 'Linux-5.10.81-android12-9-00001-geba40aecb3b7-ab8534902-aarch64-with-libc' + return "Android" + + if system == "linux": + # https://distro.readthedocs.io/en/latest/#distro.id + distro_id = distro.id() + if distro_id == "freebsd": + return "FreeBSD" + + if distro_id == "openbsd": + return "OpenBSD" + + return "Linux" + + if platform_name: + return OtherPlatform(platform_name) + + return "Unknown" + + +@lru_cache(maxsize=None) +def platform_headers(version: str, *, platform: Platform | None) -> Dict[str, str]: + return { + "X-Stainless-Lang": "python", + "X-Stainless-Package-Version": version, + "X-Stainless-OS": str(platform or get_platform()), + "X-Stainless-Arch": str(get_architecture()), + "X-Stainless-Runtime": get_python_runtime(), + "X-Stainless-Runtime-Version": get_python_version(), + } + + +class OtherArch: + def __init__(self, name: str) -> None: + self.name = name + + @override + def __str__(self) -> str: + return f"other:{self.name}" + + +Arch = Union[OtherArch, Literal["x32", "x64", "arm", "arm64", "unknown"]] + + +def get_python_runtime() -> str: + try: + return platform.python_implementation() + except Exception: + return "unknown" + + +def get_python_version() -> str: + try: + return platform.python_version() + except Exception: + return "unknown" + + +def get_architecture() -> Arch: + try: + machine = platform.machine().lower() + except Exception: + return "unknown" + + if machine in ("arm64", "aarch64"): + return "arm64" + + # TODO: untested + if machine == "arm": + return "arm" + + if machine == "x86_64": + return "x64" + + # TODO: untested + if sys.maxsize <= 2**32: + return "x32" + + if machine: + return OtherArch(machine) + + return "unknown" + + +def _merge_mappings( + obj1: Mapping[_T_co, Union[_T, Omit]], + obj2: Mapping[_T_co, Union[_T, Omit]], +) -> Dict[_T_co, _T]: + """Merge two mappings of the same type, removing any values that are instances of `Omit`. + + In cases with duplicate keys the second mapping takes precedence. + """ + merged = {**obj1, **obj2} + return {key: value for key, value in merged.items() if not isinstance(value, Omit)} diff --git a/src/unlayer/_client.py b/src/unlayer/_client.py new file mode 100644 index 0000000..94b3089 --- /dev/null +++ b/src/unlayer/_client.py @@ -0,0 +1,450 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import os +from typing import Any, Mapping +from typing_extensions import Self, override + +import httpx + +from . import _exceptions +from ._qs import Querystring +from ._types import ( + Omit, + Timeout, + NotGiven, + Transport, + ProxiesTypes, + RequestOptions, + not_given, +) +from ._utils import is_given, get_async_library +from ._version import __version__ +from .resources import pages, emails, project, pages_v1, documents, emails_v1, project_v1, documents_v1 +from ._streaming import Stream as Stream, AsyncStream as AsyncStream +from ._exceptions import UnlayerError, APIStatusError +from ._base_client import ( + DEFAULT_MAX_RETRIES, + SyncAPIClient, + AsyncAPIClient, +) + +__all__ = ["Timeout", "Transport", "ProxiesTypes", "RequestOptions", "Unlayer", "AsyncUnlayer", "Client", "AsyncClient"] + + +class Unlayer(SyncAPIClient): + project_v1: project_v1.ProjectV1Resource + project: project.ProjectResource + emails_v1: emails_v1.EmailsV1Resource + emails: emails.EmailsResource + documents_v1: documents_v1.DocumentsV1Resource + documents: documents.DocumentsResource + pages_v1: pages_v1.PagesV1Resource + pages: pages.PagesResource + with_raw_response: UnlayerWithRawResponse + with_streaming_response: UnlayerWithStreamedResponse + + # client options + api_key: str + + def __init__( + self, + *, + api_key: str | None = None, + base_url: str | httpx.URL | None = None, + timeout: float | Timeout | None | NotGiven = not_given, + max_retries: int = DEFAULT_MAX_RETRIES, + default_headers: Mapping[str, str] | None = None, + default_query: Mapping[str, object] | None = None, + # Configure a custom httpx client. + # We provide a `DefaultHttpxClient` class that you can pass to retain the default values we use for `limits`, `timeout` & `follow_redirects`. + # See the [httpx documentation](https://www.python-httpx.org/api/#client) for more details. + http_client: httpx.Client | None = None, + # Enable or disable schema validation for data returned by the API. + # When enabled an error APIResponseValidationError is raised + # if the API responds with invalid data for the expected schema. + # + # This parameter may be removed or changed in the future. + # If you rely on this feature, please open a GitHub issue + # outlining your use-case to help us decide if it should be + # part of our public interface in the future. + _strict_response_validation: bool = False, + ) -> None: + """Construct a new synchronous Unlayer client instance. + + This automatically infers the `api_key` argument from the `UNLAYER_API_KEY` environment variable if it is not provided. + """ + if api_key is None: + api_key = os.environ.get("UNLAYER_API_KEY") + if api_key is None: + raise UnlayerError( + "The api_key client option must be set either by passing api_key to the client or by setting the UNLAYER_API_KEY environment variable" + ) + self.api_key = api_key + + if base_url is None: + base_url = os.environ.get("UNLAYER_BASE_URL") + if base_url is None: + base_url = f"https://api.unlayer.com" + + super().__init__( + version=__version__, + base_url=base_url, + max_retries=max_retries, + timeout=timeout, + http_client=http_client, + custom_headers=default_headers, + custom_query=default_query, + _strict_response_validation=_strict_response_validation, + ) + + self.project_v1 = project_v1.ProjectV1Resource(self) + self.project = project.ProjectResource(self) + self.emails_v1 = emails_v1.EmailsV1Resource(self) + self.emails = emails.EmailsResource(self) + self.documents_v1 = documents_v1.DocumentsV1Resource(self) + self.documents = documents.DocumentsResource(self) + self.pages_v1 = pages_v1.PagesV1Resource(self) + self.pages = pages.PagesResource(self) + self.with_raw_response = UnlayerWithRawResponse(self) + self.with_streaming_response = UnlayerWithStreamedResponse(self) + + @property + @override + def qs(self) -> Querystring: + return Querystring(array_format="comma") + + @property + @override + def auth_headers(self) -> dict[str, str]: + api_key = self.api_key + return {"Authorization": f"Bearer {api_key}"} + + @property + @override + def default_headers(self) -> dict[str, str | Omit]: + return { + **super().default_headers, + "X-Stainless-Async": "false", + **self._custom_headers, + } + + def copy( + self, + *, + api_key: str | None = None, + base_url: str | httpx.URL | None = None, + timeout: float | Timeout | None | NotGiven = not_given, + http_client: httpx.Client | None = None, + max_retries: int | NotGiven = not_given, + default_headers: Mapping[str, str] | None = None, + set_default_headers: Mapping[str, str] | None = None, + default_query: Mapping[str, object] | None = None, + set_default_query: Mapping[str, object] | None = None, + _extra_kwargs: Mapping[str, Any] = {}, + ) -> Self: + """ + Create a new client instance re-using the same options given to the current client with optional overriding. + """ + if default_headers is not None and set_default_headers is not None: + raise ValueError("The `default_headers` and `set_default_headers` arguments are mutually exclusive") + + if default_query is not None and set_default_query is not None: + raise ValueError("The `default_query` and `set_default_query` arguments are mutually exclusive") + + headers = self._custom_headers + if default_headers is not None: + headers = {**headers, **default_headers} + elif set_default_headers is not None: + headers = set_default_headers + + params = self._custom_query + if default_query is not None: + params = {**params, **default_query} + elif set_default_query is not None: + params = set_default_query + + http_client = http_client or self._client + return self.__class__( + api_key=api_key or self.api_key, + base_url=base_url or self.base_url, + timeout=self.timeout if isinstance(timeout, NotGiven) else timeout, + http_client=http_client, + max_retries=max_retries if is_given(max_retries) else self.max_retries, + default_headers=headers, + default_query=params, + **_extra_kwargs, + ) + + # Alias for `copy` for nicer inline usage, e.g. + # client.with_options(timeout=10).foo.create(...) + with_options = copy + + @override + def _make_status_error( + self, + err_msg: str, + *, + body: object, + response: httpx.Response, + ) -> APIStatusError: + if response.status_code == 400: + return _exceptions.BadRequestError(err_msg, response=response, body=body) + + if response.status_code == 401: + return _exceptions.AuthenticationError(err_msg, response=response, body=body) + + if response.status_code == 403: + return _exceptions.PermissionDeniedError(err_msg, response=response, body=body) + + if response.status_code == 404: + return _exceptions.NotFoundError(err_msg, response=response, body=body) + + if response.status_code == 409: + return _exceptions.ConflictError(err_msg, response=response, body=body) + + if response.status_code == 422: + return _exceptions.UnprocessableEntityError(err_msg, response=response, body=body) + + if response.status_code == 429: + return _exceptions.RateLimitError(err_msg, response=response, body=body) + + if response.status_code >= 500: + return _exceptions.InternalServerError(err_msg, response=response, body=body) + return APIStatusError(err_msg, response=response, body=body) + + +class AsyncUnlayer(AsyncAPIClient): + project_v1: project_v1.AsyncProjectV1Resource + project: project.AsyncProjectResource + emails_v1: emails_v1.AsyncEmailsV1Resource + emails: emails.AsyncEmailsResource + documents_v1: documents_v1.AsyncDocumentsV1Resource + documents: documents.AsyncDocumentsResource + pages_v1: pages_v1.AsyncPagesV1Resource + pages: pages.AsyncPagesResource + with_raw_response: AsyncUnlayerWithRawResponse + with_streaming_response: AsyncUnlayerWithStreamedResponse + + # client options + api_key: str + + def __init__( + self, + *, + api_key: str | None = None, + base_url: str | httpx.URL | None = None, + timeout: float | Timeout | None | NotGiven = not_given, + max_retries: int = DEFAULT_MAX_RETRIES, + default_headers: Mapping[str, str] | None = None, + default_query: Mapping[str, object] | None = None, + # Configure a custom httpx client. + # We provide a `DefaultAsyncHttpxClient` class that you can pass to retain the default values we use for `limits`, `timeout` & `follow_redirects`. + # See the [httpx documentation](https://www.python-httpx.org/api/#asyncclient) for more details. + http_client: httpx.AsyncClient | None = None, + # Enable or disable schema validation for data returned by the API. + # When enabled an error APIResponseValidationError is raised + # if the API responds with invalid data for the expected schema. + # + # This parameter may be removed or changed in the future. + # If you rely on this feature, please open a GitHub issue + # outlining your use-case to help us decide if it should be + # part of our public interface in the future. + _strict_response_validation: bool = False, + ) -> None: + """Construct a new async AsyncUnlayer client instance. + + This automatically infers the `api_key` argument from the `UNLAYER_API_KEY` environment variable if it is not provided. + """ + if api_key is None: + api_key = os.environ.get("UNLAYER_API_KEY") + if api_key is None: + raise UnlayerError( + "The api_key client option must be set either by passing api_key to the client or by setting the UNLAYER_API_KEY environment variable" + ) + self.api_key = api_key + + if base_url is None: + base_url = os.environ.get("UNLAYER_BASE_URL") + if base_url is None: + base_url = f"https://api.unlayer.com" + + super().__init__( + version=__version__, + base_url=base_url, + max_retries=max_retries, + timeout=timeout, + http_client=http_client, + custom_headers=default_headers, + custom_query=default_query, + _strict_response_validation=_strict_response_validation, + ) + + self.project_v1 = project_v1.AsyncProjectV1Resource(self) + self.project = project.AsyncProjectResource(self) + self.emails_v1 = emails_v1.AsyncEmailsV1Resource(self) + self.emails = emails.AsyncEmailsResource(self) + self.documents_v1 = documents_v1.AsyncDocumentsV1Resource(self) + self.documents = documents.AsyncDocumentsResource(self) + self.pages_v1 = pages_v1.AsyncPagesV1Resource(self) + self.pages = pages.AsyncPagesResource(self) + self.with_raw_response = AsyncUnlayerWithRawResponse(self) + self.with_streaming_response = AsyncUnlayerWithStreamedResponse(self) + + @property + @override + def qs(self) -> Querystring: + return Querystring(array_format="comma") + + @property + @override + def auth_headers(self) -> dict[str, str]: + api_key = self.api_key + return {"Authorization": f"Bearer {api_key}"} + + @property + @override + def default_headers(self) -> dict[str, str | Omit]: + return { + **super().default_headers, + "X-Stainless-Async": f"async:{get_async_library()}", + **self._custom_headers, + } + + def copy( + self, + *, + api_key: str | None = None, + base_url: str | httpx.URL | None = None, + timeout: float | Timeout | None | NotGiven = not_given, + http_client: httpx.AsyncClient | None = None, + max_retries: int | NotGiven = not_given, + default_headers: Mapping[str, str] | None = None, + set_default_headers: Mapping[str, str] | None = None, + default_query: Mapping[str, object] | None = None, + set_default_query: Mapping[str, object] | None = None, + _extra_kwargs: Mapping[str, Any] = {}, + ) -> Self: + """ + Create a new client instance re-using the same options given to the current client with optional overriding. + """ + if default_headers is not None and set_default_headers is not None: + raise ValueError("The `default_headers` and `set_default_headers` arguments are mutually exclusive") + + if default_query is not None and set_default_query is not None: + raise ValueError("The `default_query` and `set_default_query` arguments are mutually exclusive") + + headers = self._custom_headers + if default_headers is not None: + headers = {**headers, **default_headers} + elif set_default_headers is not None: + headers = set_default_headers + + params = self._custom_query + if default_query is not None: + params = {**params, **default_query} + elif set_default_query is not None: + params = set_default_query + + http_client = http_client or self._client + return self.__class__( + api_key=api_key or self.api_key, + base_url=base_url or self.base_url, + timeout=self.timeout if isinstance(timeout, NotGiven) else timeout, + http_client=http_client, + max_retries=max_retries if is_given(max_retries) else self.max_retries, + default_headers=headers, + default_query=params, + **_extra_kwargs, + ) + + # Alias for `copy` for nicer inline usage, e.g. + # client.with_options(timeout=10).foo.create(...) + with_options = copy + + @override + def _make_status_error( + self, + err_msg: str, + *, + body: object, + response: httpx.Response, + ) -> APIStatusError: + if response.status_code == 400: + return _exceptions.BadRequestError(err_msg, response=response, body=body) + + if response.status_code == 401: + return _exceptions.AuthenticationError(err_msg, response=response, body=body) + + if response.status_code == 403: + return _exceptions.PermissionDeniedError(err_msg, response=response, body=body) + + if response.status_code == 404: + return _exceptions.NotFoundError(err_msg, response=response, body=body) + + if response.status_code == 409: + return _exceptions.ConflictError(err_msg, response=response, body=body) + + if response.status_code == 422: + return _exceptions.UnprocessableEntityError(err_msg, response=response, body=body) + + if response.status_code == 429: + return _exceptions.RateLimitError(err_msg, response=response, body=body) + + if response.status_code >= 500: + return _exceptions.InternalServerError(err_msg, response=response, body=body) + return APIStatusError(err_msg, response=response, body=body) + + +class UnlayerWithRawResponse: + def __init__(self, client: Unlayer) -> None: + self.project_v1 = project_v1.ProjectV1ResourceWithRawResponse(client.project_v1) + self.project = project.ProjectResourceWithRawResponse(client.project) + self.emails_v1 = emails_v1.EmailsV1ResourceWithRawResponse(client.emails_v1) + self.emails = emails.EmailsResourceWithRawResponse(client.emails) + self.documents_v1 = documents_v1.DocumentsV1ResourceWithRawResponse(client.documents_v1) + self.documents = documents.DocumentsResourceWithRawResponse(client.documents) + self.pages_v1 = pages_v1.PagesV1ResourceWithRawResponse(client.pages_v1) + self.pages = pages.PagesResourceWithRawResponse(client.pages) + + +class AsyncUnlayerWithRawResponse: + def __init__(self, client: AsyncUnlayer) -> None: + self.project_v1 = project_v1.AsyncProjectV1ResourceWithRawResponse(client.project_v1) + self.project = project.AsyncProjectResourceWithRawResponse(client.project) + self.emails_v1 = emails_v1.AsyncEmailsV1ResourceWithRawResponse(client.emails_v1) + self.emails = emails.AsyncEmailsResourceWithRawResponse(client.emails) + self.documents_v1 = documents_v1.AsyncDocumentsV1ResourceWithRawResponse(client.documents_v1) + self.documents = documents.AsyncDocumentsResourceWithRawResponse(client.documents) + self.pages_v1 = pages_v1.AsyncPagesV1ResourceWithRawResponse(client.pages_v1) + self.pages = pages.AsyncPagesResourceWithRawResponse(client.pages) + + +class UnlayerWithStreamedResponse: + def __init__(self, client: Unlayer) -> None: + self.project_v1 = project_v1.ProjectV1ResourceWithStreamingResponse(client.project_v1) + self.project = project.ProjectResourceWithStreamingResponse(client.project) + self.emails_v1 = emails_v1.EmailsV1ResourceWithStreamingResponse(client.emails_v1) + self.emails = emails.EmailsResourceWithStreamingResponse(client.emails) + self.documents_v1 = documents_v1.DocumentsV1ResourceWithStreamingResponse(client.documents_v1) + self.documents = documents.DocumentsResourceWithStreamingResponse(client.documents) + self.pages_v1 = pages_v1.PagesV1ResourceWithStreamingResponse(client.pages_v1) + self.pages = pages.PagesResourceWithStreamingResponse(client.pages) + + +class AsyncUnlayerWithStreamedResponse: + def __init__(self, client: AsyncUnlayer) -> None: + self.project_v1 = project_v1.AsyncProjectV1ResourceWithStreamingResponse(client.project_v1) + self.project = project.AsyncProjectResourceWithStreamingResponse(client.project) + self.emails_v1 = emails_v1.AsyncEmailsV1ResourceWithStreamingResponse(client.emails_v1) + self.emails = emails.AsyncEmailsResourceWithStreamingResponse(client.emails) + self.documents_v1 = documents_v1.AsyncDocumentsV1ResourceWithStreamingResponse(client.documents_v1) + self.documents = documents.AsyncDocumentsResourceWithStreamingResponse(client.documents) + self.pages_v1 = pages_v1.AsyncPagesV1ResourceWithStreamingResponse(client.pages_v1) + self.pages = pages.AsyncPagesResourceWithStreamingResponse(client.pages) + + +Client = Unlayer + +AsyncClient = AsyncUnlayer diff --git a/src/unlayer/_compat.py b/src/unlayer/_compat.py new file mode 100644 index 0000000..bdef67f --- /dev/null +++ b/src/unlayer/_compat.py @@ -0,0 +1,219 @@ +from __future__ import annotations + +from typing import TYPE_CHECKING, Any, Union, Generic, TypeVar, Callable, cast, overload +from datetime import date, datetime +from typing_extensions import Self, Literal + +import pydantic +from pydantic.fields import FieldInfo + +from ._types import IncEx, StrBytesIntFloat + +_T = TypeVar("_T") +_ModelT = TypeVar("_ModelT", bound=pydantic.BaseModel) + +# --------------- Pydantic v2, v3 compatibility --------------- + +# Pyright incorrectly reports some of our functions as overriding a method when they don't +# pyright: reportIncompatibleMethodOverride=false + +PYDANTIC_V1 = pydantic.VERSION.startswith("1.") + +if TYPE_CHECKING: + + def parse_date(value: date | StrBytesIntFloat) -> date: # noqa: ARG001 + ... + + def parse_datetime(value: Union[datetime, StrBytesIntFloat]) -> datetime: # noqa: ARG001 + ... + + def get_args(t: type[Any]) -> tuple[Any, ...]: # noqa: ARG001 + ... + + def is_union(tp: type[Any] | None) -> bool: # noqa: ARG001 + ... + + def get_origin(t: type[Any]) -> type[Any] | None: # noqa: ARG001 + ... + + def is_literal_type(type_: type[Any]) -> bool: # noqa: ARG001 + ... + + def is_typeddict(type_: type[Any]) -> bool: # noqa: ARG001 + ... + +else: + # v1 re-exports + if PYDANTIC_V1: + from pydantic.typing import ( + get_args as get_args, + is_union as is_union, + get_origin as get_origin, + is_typeddict as is_typeddict, + is_literal_type as is_literal_type, + ) + from pydantic.datetime_parse import parse_date as parse_date, parse_datetime as parse_datetime + else: + from ._utils import ( + get_args as get_args, + is_union as is_union, + get_origin as get_origin, + parse_date as parse_date, + is_typeddict as is_typeddict, + parse_datetime as parse_datetime, + is_literal_type as is_literal_type, + ) + + +# refactored config +if TYPE_CHECKING: + from pydantic import ConfigDict as ConfigDict +else: + if PYDANTIC_V1: + # TODO: provide an error message here? + ConfigDict = None + else: + from pydantic import ConfigDict as ConfigDict + + +# renamed methods / properties +def parse_obj(model: type[_ModelT], value: object) -> _ModelT: + if PYDANTIC_V1: + return cast(_ModelT, model.parse_obj(value)) # pyright: ignore[reportDeprecated, reportUnnecessaryCast] + else: + return model.model_validate(value) + + +def field_is_required(field: FieldInfo) -> bool: + if PYDANTIC_V1: + return field.required # type: ignore + return field.is_required() + + +def field_get_default(field: FieldInfo) -> Any: + value = field.get_default() + if PYDANTIC_V1: + return value + from pydantic_core import PydanticUndefined + + if value == PydanticUndefined: + return None + return value + + +def field_outer_type(field: FieldInfo) -> Any: + if PYDANTIC_V1: + return field.outer_type_ # type: ignore + return field.annotation + + +def get_model_config(model: type[pydantic.BaseModel]) -> Any: + if PYDANTIC_V1: + return model.__config__ # type: ignore + return model.model_config + + +def get_model_fields(model: type[pydantic.BaseModel]) -> dict[str, FieldInfo]: + if PYDANTIC_V1: + return model.__fields__ # type: ignore + return model.model_fields + + +def model_copy(model: _ModelT, *, deep: bool = False) -> _ModelT: + if PYDANTIC_V1: + return model.copy(deep=deep) # type: ignore + return model.model_copy(deep=deep) + + +def model_json(model: pydantic.BaseModel, *, indent: int | None = None) -> str: + if PYDANTIC_V1: + return model.json(indent=indent) # type: ignore + return model.model_dump_json(indent=indent) + + +def model_dump( + model: pydantic.BaseModel, + *, + exclude: IncEx | None = None, + exclude_unset: bool = False, + exclude_defaults: bool = False, + warnings: bool = True, + mode: Literal["json", "python"] = "python", +) -> dict[str, Any]: + if (not PYDANTIC_V1) or hasattr(model, "model_dump"): + return model.model_dump( + mode=mode, + exclude=exclude, + exclude_unset=exclude_unset, + exclude_defaults=exclude_defaults, + # warnings are not supported in Pydantic v1 + warnings=True if PYDANTIC_V1 else warnings, + ) + return cast( + "dict[str, Any]", + model.dict( # pyright: ignore[reportDeprecated, reportUnnecessaryCast] + exclude=exclude, + exclude_unset=exclude_unset, + exclude_defaults=exclude_defaults, + ), + ) + + +def model_parse(model: type[_ModelT], data: Any) -> _ModelT: + if PYDANTIC_V1: + return model.parse_obj(data) # pyright: ignore[reportDeprecated] + return model.model_validate(data) + + +# generic models +if TYPE_CHECKING: + + class GenericModel(pydantic.BaseModel): ... + +else: + if PYDANTIC_V1: + import pydantic.generics + + class GenericModel(pydantic.generics.GenericModel, pydantic.BaseModel): ... + else: + # there no longer needs to be a distinction in v2 but + # we still have to create our own subclass to avoid + # inconsistent MRO ordering errors + class GenericModel(pydantic.BaseModel): ... + + +# cached properties +if TYPE_CHECKING: + cached_property = property + + # we define a separate type (copied from typeshed) + # that represents that `cached_property` is `set`able + # at runtime, which differs from `@property`. + # + # this is a separate type as editors likely special case + # `@property` and we don't want to cause issues just to have + # more helpful internal types. + + class typed_cached_property(Generic[_T]): + func: Callable[[Any], _T] + attrname: str | None + + def __init__(self, func: Callable[[Any], _T]) -> None: ... + + @overload + def __get__(self, instance: None, owner: type[Any] | None = None) -> Self: ... + + @overload + def __get__(self, instance: object, owner: type[Any] | None = None) -> _T: ... + + def __get__(self, instance: object, owner: type[Any] | None = None) -> _T | Self: + raise NotImplementedError() + + def __set_name__(self, owner: type[Any], name: str) -> None: ... + + # __set__ is not defined at runtime, but @cached_property is designed to be settable + def __set__(self, instance: object, value: _T) -> None: ... +else: + from functools import cached_property as cached_property + + typed_cached_property = cached_property diff --git a/src/unlayer/_constants.py b/src/unlayer/_constants.py new file mode 100644 index 0000000..6ddf2c7 --- /dev/null +++ b/src/unlayer/_constants.py @@ -0,0 +1,14 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +import httpx + +RAW_RESPONSE_HEADER = "X-Stainless-Raw-Response" +OVERRIDE_CAST_TO_HEADER = "____stainless_override_cast_to" + +# default timeout is 1 minute +DEFAULT_TIMEOUT = httpx.Timeout(timeout=60, connect=5.0) +DEFAULT_MAX_RETRIES = 2 +DEFAULT_CONNECTION_LIMITS = httpx.Limits(max_connections=100, max_keepalive_connections=20) + +INITIAL_RETRY_DELAY = 0.5 +MAX_RETRY_DELAY = 8.0 diff --git a/src/unlayer/_exceptions.py b/src/unlayer/_exceptions.py new file mode 100644 index 0000000..19220a2 --- /dev/null +++ b/src/unlayer/_exceptions.py @@ -0,0 +1,108 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal + +import httpx + +__all__ = [ + "BadRequestError", + "AuthenticationError", + "PermissionDeniedError", + "NotFoundError", + "ConflictError", + "UnprocessableEntityError", + "RateLimitError", + "InternalServerError", +] + + +class UnlayerError(Exception): + pass + + +class APIError(UnlayerError): + message: str + request: httpx.Request + + body: object | None + """The API response body. + + If the API responded with a valid JSON structure then this property will be the + decoded result. + + If it isn't a valid JSON structure then this will be the raw response. + + If there was no response associated with this error then it will be `None`. + """ + + def __init__(self, message: str, request: httpx.Request, *, body: object | None) -> None: # noqa: ARG002 + super().__init__(message) + self.request = request + self.message = message + self.body = body + + +class APIResponseValidationError(APIError): + response: httpx.Response + status_code: int + + def __init__(self, response: httpx.Response, body: object | None, *, message: str | None = None) -> None: + super().__init__(message or "Data returned by API invalid for expected schema.", response.request, body=body) + self.response = response + self.status_code = response.status_code + + +class APIStatusError(APIError): + """Raised when an API response has a status code of 4xx or 5xx.""" + + response: httpx.Response + status_code: int + + def __init__(self, message: str, *, response: httpx.Response, body: object | None) -> None: + super().__init__(message, response.request, body=body) + self.response = response + self.status_code = response.status_code + + +class APIConnectionError(APIError): + def __init__(self, *, message: str = "Connection error.", request: httpx.Request) -> None: + super().__init__(message, request, body=None) + + +class APITimeoutError(APIConnectionError): + def __init__(self, request: httpx.Request) -> None: + super().__init__(message="Request timed out.", request=request) + + +class BadRequestError(APIStatusError): + status_code: Literal[400] = 400 # pyright: ignore[reportIncompatibleVariableOverride] + + +class AuthenticationError(APIStatusError): + status_code: Literal[401] = 401 # pyright: ignore[reportIncompatibleVariableOverride] + + +class PermissionDeniedError(APIStatusError): + status_code: Literal[403] = 403 # pyright: ignore[reportIncompatibleVariableOverride] + + +class NotFoundError(APIStatusError): + status_code: Literal[404] = 404 # pyright: ignore[reportIncompatibleVariableOverride] + + +class ConflictError(APIStatusError): + status_code: Literal[409] = 409 # pyright: ignore[reportIncompatibleVariableOverride] + + +class UnprocessableEntityError(APIStatusError): + status_code: Literal[422] = 422 # pyright: ignore[reportIncompatibleVariableOverride] + + +class RateLimitError(APIStatusError): + status_code: Literal[429] = 429 # pyright: ignore[reportIncompatibleVariableOverride] + + +class InternalServerError(APIStatusError): + pass diff --git a/src/unlayer/_files.py b/src/unlayer/_files.py new file mode 100644 index 0000000..cc14c14 --- /dev/null +++ b/src/unlayer/_files.py @@ -0,0 +1,123 @@ +from __future__ import annotations + +import io +import os +import pathlib +from typing import overload +from typing_extensions import TypeGuard + +import anyio + +from ._types import ( + FileTypes, + FileContent, + RequestFiles, + HttpxFileTypes, + Base64FileInput, + HttpxFileContent, + HttpxRequestFiles, +) +from ._utils import is_tuple_t, is_mapping_t, is_sequence_t + + +def is_base64_file_input(obj: object) -> TypeGuard[Base64FileInput]: + return isinstance(obj, io.IOBase) or isinstance(obj, os.PathLike) + + +def is_file_content(obj: object) -> TypeGuard[FileContent]: + return ( + isinstance(obj, bytes) or isinstance(obj, tuple) or isinstance(obj, io.IOBase) or isinstance(obj, os.PathLike) + ) + + +def assert_is_file_content(obj: object, *, key: str | None = None) -> None: + if not is_file_content(obj): + prefix = f"Expected entry at `{key}`" if key is not None else f"Expected file input `{obj!r}`" + raise RuntimeError( + f"{prefix} to be bytes, an io.IOBase instance, PathLike or a tuple but received {type(obj)} instead." + ) from None + + +@overload +def to_httpx_files(files: None) -> None: ... + + +@overload +def to_httpx_files(files: RequestFiles) -> HttpxRequestFiles: ... + + +def to_httpx_files(files: RequestFiles | None) -> HttpxRequestFiles | None: + if files is None: + return None + + if is_mapping_t(files): + files = {key: _transform_file(file) for key, file in files.items()} + elif is_sequence_t(files): + files = [(key, _transform_file(file)) for key, file in files] + else: + raise TypeError(f"Unexpected file type input {type(files)}, expected mapping or sequence") + + return files + + +def _transform_file(file: FileTypes) -> HttpxFileTypes: + if is_file_content(file): + if isinstance(file, os.PathLike): + path = pathlib.Path(file) + return (path.name, path.read_bytes()) + + return file + + if is_tuple_t(file): + return (file[0], read_file_content(file[1]), *file[2:]) + + raise TypeError(f"Expected file types input to be a FileContent type or to be a tuple") + + +def read_file_content(file: FileContent) -> HttpxFileContent: + if isinstance(file, os.PathLike): + return pathlib.Path(file).read_bytes() + return file + + +@overload +async def async_to_httpx_files(files: None) -> None: ... + + +@overload +async def async_to_httpx_files(files: RequestFiles) -> HttpxRequestFiles: ... + + +async def async_to_httpx_files(files: RequestFiles | None) -> HttpxRequestFiles | None: + if files is None: + return None + + if is_mapping_t(files): + files = {key: await _async_transform_file(file) for key, file in files.items()} + elif is_sequence_t(files): + files = [(key, await _async_transform_file(file)) for key, file in files] + else: + raise TypeError("Unexpected file type input {type(files)}, expected mapping or sequence") + + return files + + +async def _async_transform_file(file: FileTypes) -> HttpxFileTypes: + if is_file_content(file): + if isinstance(file, os.PathLike): + path = anyio.Path(file) + return (path.name, await path.read_bytes()) + + return file + + if is_tuple_t(file): + return (file[0], await async_read_file_content(file[1]), *file[2:]) + + raise TypeError(f"Expected file types input to be a FileContent type or to be a tuple") + + +async def async_read_file_content(file: FileContent) -> HttpxFileContent: + if isinstance(file, os.PathLike): + return await anyio.Path(file).read_bytes() + + return file diff --git a/src/unlayer/_models.py b/src/unlayer/_models.py new file mode 100644 index 0000000..ca9500b --- /dev/null +++ b/src/unlayer/_models.py @@ -0,0 +1,857 @@ +from __future__ import annotations + +import os +import inspect +import weakref +from typing import TYPE_CHECKING, Any, Type, Union, Generic, TypeVar, Callable, Optional, cast +from datetime import date, datetime +from typing_extensions import ( + List, + Unpack, + Literal, + ClassVar, + Protocol, + Required, + ParamSpec, + TypedDict, + TypeGuard, + final, + override, + runtime_checkable, +) + +import pydantic +from pydantic.fields import FieldInfo + +from ._types import ( + Body, + IncEx, + Query, + ModelT, + Headers, + Timeout, + NotGiven, + AnyMapping, + HttpxRequestFiles, +) +from ._utils import ( + PropertyInfo, + is_list, + is_given, + json_safe, + lru_cache, + is_mapping, + parse_date, + coerce_boolean, + parse_datetime, + strip_not_given, + extract_type_arg, + is_annotated_type, + is_type_alias_type, + strip_annotated_type, +) +from ._compat import ( + PYDANTIC_V1, + ConfigDict, + GenericModel as BaseGenericModel, + get_args, + is_union, + parse_obj, + get_origin, + is_literal_type, + get_model_config, + get_model_fields, + field_get_default, +) +from ._constants import RAW_RESPONSE_HEADER + +if TYPE_CHECKING: + from pydantic_core.core_schema import ModelField, ModelSchema, LiteralSchema, ModelFieldsSchema + +__all__ = ["BaseModel", "GenericModel"] + +_T = TypeVar("_T") +_BaseModelT = TypeVar("_BaseModelT", bound="BaseModel") + +P = ParamSpec("P") + + +@runtime_checkable +class _ConfigProtocol(Protocol): + allow_population_by_field_name: bool + + +class BaseModel(pydantic.BaseModel): + if PYDANTIC_V1: + + @property + @override + def model_fields_set(self) -> set[str]: + # a forwards-compat shim for pydantic v2 + return self.__fields_set__ # type: ignore + + class Config(pydantic.BaseConfig): # pyright: ignore[reportDeprecated] + extra: Any = pydantic.Extra.allow # type: ignore + else: + model_config: ClassVar[ConfigDict] = ConfigDict( + extra="allow", defer_build=coerce_boolean(os.environ.get("DEFER_PYDANTIC_BUILD", "true")) + ) + + def to_dict( + self, + *, + mode: Literal["json", "python"] = "python", + use_api_names: bool = True, + exclude_unset: bool = True, + exclude_defaults: bool = False, + exclude_none: bool = False, + warnings: bool = True, + ) -> dict[str, object]: + """Recursively generate a dictionary representation of the model, optionally specifying which fields to include or exclude. + + By default, fields that were not set by the API will not be included, + and keys will match the API response, *not* the property names from the model. + + For example, if the API responds with `"fooBar": true` but we've defined a `foo_bar: bool` property, + the output will use the `"fooBar"` key (unless `use_api_names=False` is passed). + + Args: + mode: + If mode is 'json', the dictionary will only contain JSON serializable types. e.g. `datetime` will be turned into a string, `"2024-3-22T18:11:19.117000Z"`. + If mode is 'python', the dictionary may contain any Python objects. e.g. `datetime(2024, 3, 22)` + + use_api_names: Whether to use the key that the API responded with or the property name. Defaults to `True`. + exclude_unset: Whether to exclude fields that have not been explicitly set. + exclude_defaults: Whether to exclude fields that are set to their default value from the output. + exclude_none: Whether to exclude fields that have a value of `None` from the output. + warnings: Whether to log warnings when invalid fields are encountered. This is only supported in Pydantic v2. + """ + return self.model_dump( + mode=mode, + by_alias=use_api_names, + exclude_unset=exclude_unset, + exclude_defaults=exclude_defaults, + exclude_none=exclude_none, + warnings=warnings, + ) + + def to_json( + self, + *, + indent: int | None = 2, + use_api_names: bool = True, + exclude_unset: bool = True, + exclude_defaults: bool = False, + exclude_none: bool = False, + warnings: bool = True, + ) -> str: + """Generates a JSON string representing this model as it would be received from or sent to the API (but with indentation). + + By default, fields that were not set by the API will not be included, + and keys will match the API response, *not* the property names from the model. + + For example, if the API responds with `"fooBar": true` but we've defined a `foo_bar: bool` property, + the output will use the `"fooBar"` key (unless `use_api_names=False` is passed). + + Args: + indent: Indentation to use in the JSON output. If `None` is passed, the output will be compact. Defaults to `2` + use_api_names: Whether to use the key that the API responded with or the property name. Defaults to `True`. + exclude_unset: Whether to exclude fields that have not been explicitly set. + exclude_defaults: Whether to exclude fields that have the default value. + exclude_none: Whether to exclude fields that have a value of `None`. + warnings: Whether to show any warnings that occurred during serialization. This is only supported in Pydantic v2. + """ + return self.model_dump_json( + indent=indent, + by_alias=use_api_names, + exclude_unset=exclude_unset, + exclude_defaults=exclude_defaults, + exclude_none=exclude_none, + warnings=warnings, + ) + + @override + def __str__(self) -> str: + # mypy complains about an invalid self arg + return f"{self.__repr_name__()}({self.__repr_str__(', ')})" # type: ignore[misc] + + # Override the 'construct' method in a way that supports recursive parsing without validation. + # Based on https://github.com/samuelcolvin/pydantic/issues/1168#issuecomment-817742836. + @classmethod + @override + def construct( # pyright: ignore[reportIncompatibleMethodOverride] + __cls: Type[ModelT], + _fields_set: set[str] | None = None, + **values: object, + ) -> ModelT: + m = __cls.__new__(__cls) + fields_values: dict[str, object] = {} + + config = get_model_config(__cls) + populate_by_name = ( + config.allow_population_by_field_name + if isinstance(config, _ConfigProtocol) + else config.get("populate_by_name") + ) + + if _fields_set is None: + _fields_set = set() + + model_fields = get_model_fields(__cls) + for name, field in model_fields.items(): + key = field.alias + if key is None or (key not in values and populate_by_name): + key = name + + if key in values: + fields_values[name] = _construct_field(value=values[key], field=field, key=key) + _fields_set.add(name) + else: + fields_values[name] = field_get_default(field) + + extra_field_type = _get_extra_fields_type(__cls) + + _extra = {} + for key, value in values.items(): + if key not in model_fields: + parsed = construct_type(value=value, type_=extra_field_type) if extra_field_type is not None else value + + if PYDANTIC_V1: + _fields_set.add(key) + fields_values[key] = parsed + else: + _extra[key] = parsed + + object.__setattr__(m, "__dict__", fields_values) + + if PYDANTIC_V1: + # init_private_attributes() does not exist in v2 + m._init_private_attributes() # type: ignore + + # copied from Pydantic v1's `construct()` method + object.__setattr__(m, "__fields_set__", _fields_set) + else: + # these properties are copied from Pydantic's `model_construct()` method + object.__setattr__(m, "__pydantic_private__", None) + object.__setattr__(m, "__pydantic_extra__", _extra) + object.__setattr__(m, "__pydantic_fields_set__", _fields_set) + + return m + + if not TYPE_CHECKING: + # type checkers incorrectly complain about this assignment + # because the type signatures are technically different + # although not in practice + model_construct = construct + + if PYDANTIC_V1: + # we define aliases for some of the new pydantic v2 methods so + # that we can just document these methods without having to specify + # a specific pydantic version as some users may not know which + # pydantic version they are currently using + + @override + def model_dump( + self, + *, + mode: Literal["json", "python"] | str = "python", + include: IncEx | None = None, + exclude: IncEx | None = None, + context: Any | None = None, + by_alias: bool | None = None, + exclude_unset: bool = False, + exclude_defaults: bool = False, + exclude_none: bool = False, + exclude_computed_fields: bool = False, + round_trip: bool = False, + warnings: bool | Literal["none", "warn", "error"] = True, + fallback: Callable[[Any], Any] | None = None, + serialize_as_any: bool = False, + ) -> dict[str, Any]: + """Usage docs: https://docs.pydantic.dev/2.4/concepts/serialization/#modelmodel_dump + + Generate a dictionary representation of the model, optionally specifying which fields to include or exclude. + + Args: + mode: The mode in which `to_python` should run. + If mode is 'json', the output will only contain JSON serializable types. + If mode is 'python', the output may contain non-JSON-serializable Python objects. + include: A set of fields to include in the output. + exclude: A set of fields to exclude from the output. + context: Additional context to pass to the serializer. + by_alias: Whether to use the field's alias in the dictionary key if defined. + exclude_unset: Whether to exclude fields that have not been explicitly set. + exclude_defaults: Whether to exclude fields that are set to their default value. + exclude_none: Whether to exclude fields that have a value of `None`. + exclude_computed_fields: Whether to exclude computed fields. + While this can be useful for round-tripping, it is usually recommended to use the dedicated + `round_trip` parameter instead. + round_trip: If True, dumped values should be valid as input for non-idempotent types such as Json[T]. + warnings: How to handle serialization errors. False/"none" ignores them, True/"warn" logs errors, + "error" raises a [`PydanticSerializationError`][pydantic_core.PydanticSerializationError]. + fallback: A function to call when an unknown value is encountered. If not provided, + a [`PydanticSerializationError`][pydantic_core.PydanticSerializationError] error is raised. + serialize_as_any: Whether to serialize fields with duck-typing serialization behavior. + + Returns: + A dictionary representation of the model. + """ + if mode not in {"json", "python"}: + raise ValueError("mode must be either 'json' or 'python'") + if round_trip != False: + raise ValueError("round_trip is only supported in Pydantic v2") + if warnings != True: + raise ValueError("warnings is only supported in Pydantic v2") + if context is not None: + raise ValueError("context is only supported in Pydantic v2") + if serialize_as_any != False: + raise ValueError("serialize_as_any is only supported in Pydantic v2") + if fallback is not None: + raise ValueError("fallback is only supported in Pydantic v2") + if exclude_computed_fields != False: + raise ValueError("exclude_computed_fields is only supported in Pydantic v2") + dumped = super().dict( # pyright: ignore[reportDeprecated] + include=include, + exclude=exclude, + by_alias=by_alias if by_alias is not None else False, + exclude_unset=exclude_unset, + exclude_defaults=exclude_defaults, + exclude_none=exclude_none, + ) + + return cast("dict[str, Any]", json_safe(dumped)) if mode == "json" else dumped + + @override + def model_dump_json( + self, + *, + indent: int | None = None, + ensure_ascii: bool = False, + include: IncEx | None = None, + exclude: IncEx | None = None, + context: Any | None = None, + by_alias: bool | None = None, + exclude_unset: bool = False, + exclude_defaults: bool = False, + exclude_none: bool = False, + exclude_computed_fields: bool = False, + round_trip: bool = False, + warnings: bool | Literal["none", "warn", "error"] = True, + fallback: Callable[[Any], Any] | None = None, + serialize_as_any: bool = False, + ) -> str: + """Usage docs: https://docs.pydantic.dev/2.4/concepts/serialization/#modelmodel_dump_json + + Generates a JSON representation of the model using Pydantic's `to_json` method. + + Args: + indent: Indentation to use in the JSON output. If None is passed, the output will be compact. + include: Field(s) to include in the JSON output. Can take either a string or set of strings. + exclude: Field(s) to exclude from the JSON output. Can take either a string or set of strings. + by_alias: Whether to serialize using field aliases. + exclude_unset: Whether to exclude fields that have not been explicitly set. + exclude_defaults: Whether to exclude fields that have the default value. + exclude_none: Whether to exclude fields that have a value of `None`. + round_trip: Whether to use serialization/deserialization between JSON and class instance. + warnings: Whether to show any warnings that occurred during serialization. + + Returns: + A JSON string representation of the model. + """ + if round_trip != False: + raise ValueError("round_trip is only supported in Pydantic v2") + if warnings != True: + raise ValueError("warnings is only supported in Pydantic v2") + if context is not None: + raise ValueError("context is only supported in Pydantic v2") + if serialize_as_any != False: + raise ValueError("serialize_as_any is only supported in Pydantic v2") + if fallback is not None: + raise ValueError("fallback is only supported in Pydantic v2") + if ensure_ascii != False: + raise ValueError("ensure_ascii is only supported in Pydantic v2") + if exclude_computed_fields != False: + raise ValueError("exclude_computed_fields is only supported in Pydantic v2") + return super().json( # type: ignore[reportDeprecated] + indent=indent, + include=include, + exclude=exclude, + by_alias=by_alias if by_alias is not None else False, + exclude_unset=exclude_unset, + exclude_defaults=exclude_defaults, + exclude_none=exclude_none, + ) + + +def _construct_field(value: object, field: FieldInfo, key: str) -> object: + if value is None: + return field_get_default(field) + + if PYDANTIC_V1: + type_ = cast(type, field.outer_type_) # type: ignore + else: + type_ = field.annotation # type: ignore + + if type_ is None: + raise RuntimeError(f"Unexpected field type is None for {key}") + + return construct_type(value=value, type_=type_, metadata=getattr(field, "metadata", None)) + + +def _get_extra_fields_type(cls: type[pydantic.BaseModel]) -> type | None: + if PYDANTIC_V1: + # TODO + return None + + schema = cls.__pydantic_core_schema__ + if schema["type"] == "model": + fields = schema["schema"] + if fields["type"] == "model-fields": + extras = fields.get("extras_schema") + if extras and "cls" in extras: + # mypy can't narrow the type + return extras["cls"] # type: ignore[no-any-return] + + return None + + +def is_basemodel(type_: type) -> bool: + """Returns whether or not the given type is either a `BaseModel` or a union of `BaseModel`""" + if is_union(type_): + for variant in get_args(type_): + if is_basemodel(variant): + return True + + return False + + return is_basemodel_type(type_) + + +def is_basemodel_type(type_: type) -> TypeGuard[type[BaseModel] | type[GenericModel]]: + origin = get_origin(type_) or type_ + if not inspect.isclass(origin): + return False + return issubclass(origin, BaseModel) or issubclass(origin, GenericModel) + + +def build( + base_model_cls: Callable[P, _BaseModelT], + *args: P.args, + **kwargs: P.kwargs, +) -> _BaseModelT: + """Construct a BaseModel class without validation. + + This is useful for cases where you need to instantiate a `BaseModel` + from an API response as this provides type-safe params which isn't supported + by helpers like `construct_type()`. + + ```py + build(MyModel, my_field_a="foo", my_field_b=123) + ``` + """ + if args: + raise TypeError( + "Received positional arguments which are not supported; Keyword arguments must be used instead", + ) + + return cast(_BaseModelT, construct_type(type_=base_model_cls, value=kwargs)) + + +def construct_type_unchecked(*, value: object, type_: type[_T]) -> _T: + """Loose coercion to the expected type with construction of nested values. + + Note: the returned value from this function is not guaranteed to match the + given type. + """ + return cast(_T, construct_type(value=value, type_=type_)) + + +def construct_type(*, value: object, type_: object, metadata: Optional[List[Any]] = None) -> object: + """Loose coercion to the expected type with construction of nested values. + + If the given value does not match the expected type then it is returned as-is. + """ + + # store a reference to the original type we were given before we extract any inner + # types so that we can properly resolve forward references in `TypeAliasType` annotations + original_type = None + + # we allow `object` as the input type because otherwise, passing things like + # `Literal['value']` will be reported as a type error by type checkers + type_ = cast("type[object]", type_) + if is_type_alias_type(type_): + original_type = type_ # type: ignore[unreachable] + type_ = type_.__value__ # type: ignore[unreachable] + + # unwrap `Annotated[T, ...]` -> `T` + if metadata is not None and len(metadata) > 0: + meta: tuple[Any, ...] = tuple(metadata) + elif is_annotated_type(type_): + meta = get_args(type_)[1:] + type_ = extract_type_arg(type_, 0) + else: + meta = tuple() + + # we need to use the origin class for any types that are subscripted generics + # e.g. Dict[str, object] + origin = get_origin(type_) or type_ + args = get_args(type_) + + if is_union(origin): + try: + return validate_type(type_=cast("type[object]", original_type or type_), value=value) + except Exception: + pass + + # if the type is a discriminated union then we want to construct the right variant + # in the union, even if the data doesn't match exactly, otherwise we'd break code + # that relies on the constructed class types, e.g. + # + # class FooType: + # kind: Literal['foo'] + # value: str + # + # class BarType: + # kind: Literal['bar'] + # value: int + # + # without this block, if the data we get is something like `{'kind': 'bar', 'value': 'foo'}` then + # we'd end up constructing `FooType` when it should be `BarType`. + discriminator = _build_discriminated_union_meta(union=type_, meta_annotations=meta) + if discriminator and is_mapping(value): + variant_value = value.get(discriminator.field_alias_from or discriminator.field_name) + if variant_value and isinstance(variant_value, str): + variant_type = discriminator.mapping.get(variant_value) + if variant_type: + return construct_type(type_=variant_type, value=value) + + # if the data is not valid, use the first variant that doesn't fail while deserializing + for variant in args: + try: + return construct_type(value=value, type_=variant) + except Exception: + continue + + raise RuntimeError(f"Could not convert data into a valid instance of {type_}") + + if origin == dict: + if not is_mapping(value): + return value + + _, items_type = get_args(type_) # Dict[_, items_type] + return {key: construct_type(value=item, type_=items_type) for key, item in value.items()} + + if ( + not is_literal_type(type_) + and inspect.isclass(origin) + and (issubclass(origin, BaseModel) or issubclass(origin, GenericModel)) + ): + if is_list(value): + return [cast(Any, type_).construct(**entry) if is_mapping(entry) else entry for entry in value] + + if is_mapping(value): + if issubclass(type_, BaseModel): + return type_.construct(**value) # type: ignore[arg-type] + + return cast(Any, type_).construct(**value) + + if origin == list: + if not is_list(value): + return value + + inner_type = args[0] # List[inner_type] + return [construct_type(value=entry, type_=inner_type) for entry in value] + + if origin == float: + if isinstance(value, int): + coerced = float(value) + if coerced != value: + return value + return coerced + + return value + + if type_ == datetime: + try: + return parse_datetime(value) # type: ignore + except Exception: + return value + + if type_ == date: + try: + return parse_date(value) # type: ignore + except Exception: + return value + + return value + + +@runtime_checkable +class CachedDiscriminatorType(Protocol): + __discriminator__: DiscriminatorDetails + + +DISCRIMINATOR_CACHE: weakref.WeakKeyDictionary[type, DiscriminatorDetails] = weakref.WeakKeyDictionary() + + +class DiscriminatorDetails: + field_name: str + """The name of the discriminator field in the variant class, e.g. + + ```py + class Foo(BaseModel): + type: Literal['foo'] + ``` + + Will result in field_name='type' + """ + + field_alias_from: str | None + """The name of the discriminator field in the API response, e.g. + + ```py + class Foo(BaseModel): + type: Literal['foo'] = Field(alias='type_from_api') + ``` + + Will result in field_alias_from='type_from_api' + """ + + mapping: dict[str, type] + """Mapping of discriminator value to variant type, e.g. + + {'foo': FooVariant, 'bar': BarVariant} + """ + + def __init__( + self, + *, + mapping: dict[str, type], + discriminator_field: str, + discriminator_alias: str | None, + ) -> None: + self.mapping = mapping + self.field_name = discriminator_field + self.field_alias_from = discriminator_alias + + +def _build_discriminated_union_meta(*, union: type, meta_annotations: tuple[Any, ...]) -> DiscriminatorDetails | None: + cached = DISCRIMINATOR_CACHE.get(union) + if cached is not None: + return cached + + discriminator_field_name: str | None = None + + for annotation in meta_annotations: + if isinstance(annotation, PropertyInfo) and annotation.discriminator is not None: + discriminator_field_name = annotation.discriminator + break + + if not discriminator_field_name: + return None + + mapping: dict[str, type] = {} + discriminator_alias: str | None = None + + for variant in get_args(union): + variant = strip_annotated_type(variant) + if is_basemodel_type(variant): + if PYDANTIC_V1: + field_info = cast("dict[str, FieldInfo]", variant.__fields__).get(discriminator_field_name) # pyright: ignore[reportDeprecated, reportUnnecessaryCast] + if not field_info: + continue + + # Note: if one variant defines an alias then they all should + discriminator_alias = field_info.alias + + if (annotation := getattr(field_info, "annotation", None)) and is_literal_type(annotation): + for entry in get_args(annotation): + if isinstance(entry, str): + mapping[entry] = variant + else: + field = _extract_field_schema_pv2(variant, discriminator_field_name) + if not field: + continue + + # Note: if one variant defines an alias then they all should + discriminator_alias = field.get("serialization_alias") + + field_schema = field["schema"] + + if field_schema["type"] == "literal": + for entry in cast("LiteralSchema", field_schema)["expected"]: + if isinstance(entry, str): + mapping[entry] = variant + + if not mapping: + return None + + details = DiscriminatorDetails( + mapping=mapping, + discriminator_field=discriminator_field_name, + discriminator_alias=discriminator_alias, + ) + DISCRIMINATOR_CACHE.setdefault(union, details) + return details + + +def _extract_field_schema_pv2(model: type[BaseModel], field_name: str) -> ModelField | None: + schema = model.__pydantic_core_schema__ + if schema["type"] == "definitions": + schema = schema["schema"] + + if schema["type"] != "model": + return None + + schema = cast("ModelSchema", schema) + fields_schema = schema["schema"] + if fields_schema["type"] != "model-fields": + return None + + fields_schema = cast("ModelFieldsSchema", fields_schema) + field = fields_schema["fields"].get(field_name) + if not field: + return None + + return cast("ModelField", field) # pyright: ignore[reportUnnecessaryCast] + + +def validate_type(*, type_: type[_T], value: object) -> _T: + """Strict validation that the given value matches the expected type""" + if inspect.isclass(type_) and issubclass(type_, pydantic.BaseModel): + return cast(_T, parse_obj(type_, value)) + + return cast(_T, _validate_non_model_type(type_=type_, value=value)) + + +def set_pydantic_config(typ: Any, config: pydantic.ConfigDict) -> None: + """Add a pydantic config for the given type. + + Note: this is a no-op on Pydantic v1. + """ + setattr(typ, "__pydantic_config__", config) # noqa: B010 + + +# our use of subclassing here causes weirdness for type checkers, +# so we just pretend that we don't subclass +if TYPE_CHECKING: + GenericModel = BaseModel +else: + + class GenericModel(BaseGenericModel, BaseModel): + pass + + +if not PYDANTIC_V1: + from pydantic import TypeAdapter as _TypeAdapter + + _CachedTypeAdapter = cast("TypeAdapter[object]", lru_cache(maxsize=None)(_TypeAdapter)) + + if TYPE_CHECKING: + from pydantic import TypeAdapter + else: + TypeAdapter = _CachedTypeAdapter + + def _validate_non_model_type(*, type_: type[_T], value: object) -> _T: + return TypeAdapter(type_).validate_python(value) + +elif not TYPE_CHECKING: # TODO: condition is weird + + class RootModel(GenericModel, Generic[_T]): + """Used as a placeholder to easily convert runtime types to a Pydantic format + to provide validation. + + For example: + ```py + validated = RootModel[int](__root__="5").__root__ + # validated: 5 + ``` + """ + + __root__: _T + + def _validate_non_model_type(*, type_: type[_T], value: object) -> _T: + model = _create_pydantic_model(type_).validate(value) + return cast(_T, model.__root__) + + def _create_pydantic_model(type_: _T) -> Type[RootModel[_T]]: + return RootModel[type_] # type: ignore + + +class FinalRequestOptionsInput(TypedDict, total=False): + method: Required[str] + url: Required[str] + params: Query + headers: Headers + max_retries: int + timeout: float | Timeout | None + files: HttpxRequestFiles | None + idempotency_key: str + json_data: Body + extra_json: AnyMapping + follow_redirects: bool + + +@final +class FinalRequestOptions(pydantic.BaseModel): + method: str + url: str + params: Query = {} + headers: Union[Headers, NotGiven] = NotGiven() + max_retries: Union[int, NotGiven] = NotGiven() + timeout: Union[float, Timeout, None, NotGiven] = NotGiven() + files: Union[HttpxRequestFiles, None] = None + idempotency_key: Union[str, None] = None + post_parser: Union[Callable[[Any], Any], NotGiven] = NotGiven() + follow_redirects: Union[bool, None] = None + + # It should be noted that we cannot use `json` here as that would override + # a BaseModel method in an incompatible fashion. + json_data: Union[Body, None] = None + extra_json: Union[AnyMapping, None] = None + + if PYDANTIC_V1: + + class Config(pydantic.BaseConfig): # pyright: ignore[reportDeprecated] + arbitrary_types_allowed: bool = True + else: + model_config: ClassVar[ConfigDict] = ConfigDict(arbitrary_types_allowed=True) + + def get_max_retries(self, max_retries: int) -> int: + if isinstance(self.max_retries, NotGiven): + return max_retries + return self.max_retries + + def _strip_raw_response_header(self) -> None: + if not is_given(self.headers): + return + + if self.headers.get(RAW_RESPONSE_HEADER): + self.headers = {**self.headers} + self.headers.pop(RAW_RESPONSE_HEADER) + + # override the `construct` method so that we can run custom transformations. + # this is necessary as we don't want to do any actual runtime type checking + # (which means we can't use validators) but we do want to ensure that `NotGiven` + # values are not present + # + # type ignore required because we're adding explicit types to `**values` + @classmethod + def construct( # type: ignore + cls, + _fields_set: set[str] | None = None, + **values: Unpack[FinalRequestOptionsInput], + ) -> FinalRequestOptions: + kwargs: dict[str, Any] = { + # we unconditionally call `strip_not_given` on any value + # as it will just ignore any non-mapping types + key: strip_not_given(value) + for key, value in values.items() + } + if PYDANTIC_V1: + return cast(FinalRequestOptions, super().construct(_fields_set, **kwargs)) # pyright: ignore[reportDeprecated] + return super().model_construct(_fields_set, **kwargs) + + if not TYPE_CHECKING: + # type checkers incorrectly complain about this assignment + model_construct = construct diff --git a/src/unlayer/_qs.py b/src/unlayer/_qs.py new file mode 100644 index 0000000..ada6fd3 --- /dev/null +++ b/src/unlayer/_qs.py @@ -0,0 +1,150 @@ +from __future__ import annotations + +from typing import Any, List, Tuple, Union, Mapping, TypeVar +from urllib.parse import parse_qs, urlencode +from typing_extensions import Literal, get_args + +from ._types import NotGiven, not_given +from ._utils import flatten + +_T = TypeVar("_T") + + +ArrayFormat = Literal["comma", "repeat", "indices", "brackets"] +NestedFormat = Literal["dots", "brackets"] + +PrimitiveData = Union[str, int, float, bool, None] +# this should be Data = Union[PrimitiveData, "List[Data]", "Tuple[Data]", "Mapping[str, Data]"] +# https://github.com/microsoft/pyright/issues/3555 +Data = Union[PrimitiveData, List[Any], Tuple[Any], "Mapping[str, Any]"] +Params = Mapping[str, Data] + + +class Querystring: + array_format: ArrayFormat + nested_format: NestedFormat + + def __init__( + self, + *, + array_format: ArrayFormat = "repeat", + nested_format: NestedFormat = "brackets", + ) -> None: + self.array_format = array_format + self.nested_format = nested_format + + def parse(self, query: str) -> Mapping[str, object]: + # Note: custom format syntax is not supported yet + return parse_qs(query) + + def stringify( + self, + params: Params, + *, + array_format: ArrayFormat | NotGiven = not_given, + nested_format: NestedFormat | NotGiven = not_given, + ) -> str: + return urlencode( + self.stringify_items( + params, + array_format=array_format, + nested_format=nested_format, + ) + ) + + def stringify_items( + self, + params: Params, + *, + array_format: ArrayFormat | NotGiven = not_given, + nested_format: NestedFormat | NotGiven = not_given, + ) -> list[tuple[str, str]]: + opts = Options( + qs=self, + array_format=array_format, + nested_format=nested_format, + ) + return flatten([self._stringify_item(key, value, opts) for key, value in params.items()]) + + def _stringify_item( + self, + key: str, + value: Data, + opts: Options, + ) -> list[tuple[str, str]]: + if isinstance(value, Mapping): + items: list[tuple[str, str]] = [] + nested_format = opts.nested_format + for subkey, subvalue in value.items(): + items.extend( + self._stringify_item( + # TODO: error if unknown format + f"{key}.{subkey}" if nested_format == "dots" else f"{key}[{subkey}]", + subvalue, + opts, + ) + ) + return items + + if isinstance(value, (list, tuple)): + array_format = opts.array_format + if array_format == "comma": + return [ + ( + key, + ",".join(self._primitive_value_to_str(item) for item in value if item is not None), + ), + ] + elif array_format == "repeat": + items = [] + for item in value: + items.extend(self._stringify_item(key, item, opts)) + return items + elif array_format == "indices": + raise NotImplementedError("The array indices format is not supported yet") + elif array_format == "brackets": + items = [] + key = key + "[]" + for item in value: + items.extend(self._stringify_item(key, item, opts)) + return items + else: + raise NotImplementedError( + f"Unknown array_format value: {array_format}, choose from {', '.join(get_args(ArrayFormat))}" + ) + + serialised = self._primitive_value_to_str(value) + if not serialised: + return [] + return [(key, serialised)] + + def _primitive_value_to_str(self, value: PrimitiveData) -> str: + # copied from httpx + if value is True: + return "true" + elif value is False: + return "false" + elif value is None: + return "" + return str(value) + + +_qs = Querystring() +parse = _qs.parse +stringify = _qs.stringify +stringify_items = _qs.stringify_items + + +class Options: + array_format: ArrayFormat + nested_format: NestedFormat + + def __init__( + self, + qs: Querystring = _qs, + *, + array_format: ArrayFormat | NotGiven = not_given, + nested_format: NestedFormat | NotGiven = not_given, + ) -> None: + self.array_format = qs.array_format if isinstance(array_format, NotGiven) else array_format + self.nested_format = qs.nested_format if isinstance(nested_format, NotGiven) else nested_format diff --git a/src/unlayer/_resource.py b/src/unlayer/_resource.py new file mode 100644 index 0000000..284cdf1 --- /dev/null +++ b/src/unlayer/_resource.py @@ -0,0 +1,43 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import time +from typing import TYPE_CHECKING + +import anyio + +if TYPE_CHECKING: + from ._client import Unlayer, AsyncUnlayer + + +class SyncAPIResource: + _client: Unlayer + + def __init__(self, client: Unlayer) -> None: + self._client = client + self._get = client.get + self._post = client.post + self._patch = client.patch + self._put = client.put + self._delete = client.delete + self._get_api_list = client.get_api_list + + def _sleep(self, seconds: float) -> None: + time.sleep(seconds) + + +class AsyncAPIResource: + _client: AsyncUnlayer + + def __init__(self, client: AsyncUnlayer) -> None: + self._client = client + self._get = client.get + self._post = client.post + self._patch = client.patch + self._put = client.put + self._delete = client.delete + self._get_api_list = client.get_api_list + + async def _sleep(self, seconds: float) -> None: + await anyio.sleep(seconds) diff --git a/src/unlayer/_response.py b/src/unlayer/_response.py new file mode 100644 index 0000000..9a45370 --- /dev/null +++ b/src/unlayer/_response.py @@ -0,0 +1,830 @@ +from __future__ import annotations + +import os +import inspect +import logging +import datetime +import functools +from types import TracebackType +from typing import ( + TYPE_CHECKING, + Any, + Union, + Generic, + TypeVar, + Callable, + Iterator, + AsyncIterator, + cast, + overload, +) +from typing_extensions import Awaitable, ParamSpec, override, get_origin + +import anyio +import httpx +import pydantic + +from ._types import NoneType +from ._utils import is_given, extract_type_arg, is_annotated_type, is_type_alias_type, extract_type_var_from_base +from ._models import BaseModel, is_basemodel +from ._constants import RAW_RESPONSE_HEADER, OVERRIDE_CAST_TO_HEADER +from ._streaming import Stream, AsyncStream, is_stream_class_type, extract_stream_chunk_type +from ._exceptions import UnlayerError, APIResponseValidationError + +if TYPE_CHECKING: + from ._models import FinalRequestOptions + from ._base_client import BaseClient + + +P = ParamSpec("P") +R = TypeVar("R") +_T = TypeVar("_T") +_APIResponseT = TypeVar("_APIResponseT", bound="APIResponse[Any]") +_AsyncAPIResponseT = TypeVar("_AsyncAPIResponseT", bound="AsyncAPIResponse[Any]") + +log: logging.Logger = logging.getLogger(__name__) + + +class BaseAPIResponse(Generic[R]): + _cast_to: type[R] + _client: BaseClient[Any, Any] + _parsed_by_type: dict[type[Any], Any] + _is_sse_stream: bool + _stream_cls: type[Stream[Any]] | type[AsyncStream[Any]] | None + _options: FinalRequestOptions + + http_response: httpx.Response + + retries_taken: int + """The number of retries made. If no retries happened this will be `0`""" + + def __init__( + self, + *, + raw: httpx.Response, + cast_to: type[R], + client: BaseClient[Any, Any], + stream: bool, + stream_cls: type[Stream[Any]] | type[AsyncStream[Any]] | None, + options: FinalRequestOptions, + retries_taken: int = 0, + ) -> None: + self._cast_to = cast_to + self._client = client + self._parsed_by_type = {} + self._is_sse_stream = stream + self._stream_cls = stream_cls + self._options = options + self.http_response = raw + self.retries_taken = retries_taken + + @property + def headers(self) -> httpx.Headers: + return self.http_response.headers + + @property + def http_request(self) -> httpx.Request: + """Returns the httpx Request instance associated with the current response.""" + return self.http_response.request + + @property + def status_code(self) -> int: + return self.http_response.status_code + + @property + def url(self) -> httpx.URL: + """Returns the URL for which the request was made.""" + return self.http_response.url + + @property + def method(self) -> str: + return self.http_request.method + + @property + def http_version(self) -> str: + return self.http_response.http_version + + @property + def elapsed(self) -> datetime.timedelta: + """The time taken for the complete request/response cycle to complete.""" + return self.http_response.elapsed + + @property + def is_closed(self) -> bool: + """Whether or not the response body has been closed. + + If this is False then there is response data that has not been read yet. + You must either fully consume the response body or call `.close()` + before discarding the response to prevent resource leaks. + """ + return self.http_response.is_closed + + @override + def __repr__(self) -> str: + return ( + f"<{self.__class__.__name__} [{self.status_code} {self.http_response.reason_phrase}] type={self._cast_to}>" + ) + + def _parse(self, *, to: type[_T] | None = None) -> R | _T: + cast_to = to if to is not None else self._cast_to + + # unwrap `TypeAlias('Name', T)` -> `T` + if is_type_alias_type(cast_to): + cast_to = cast_to.__value__ # type: ignore[unreachable] + + # unwrap `Annotated[T, ...]` -> `T` + if cast_to and is_annotated_type(cast_to): + cast_to = extract_type_arg(cast_to, 0) + + origin = get_origin(cast_to) or cast_to + + if self._is_sse_stream: + if to: + if not is_stream_class_type(to): + raise TypeError(f"Expected custom parse type to be a subclass of {Stream} or {AsyncStream}") + + return cast( + _T, + to( + cast_to=extract_stream_chunk_type( + to, + failure_message="Expected custom stream type to be passed with a type argument, e.g. Stream[ChunkType]", + ), + response=self.http_response, + client=cast(Any, self._client), + ), + ) + + if self._stream_cls: + return cast( + R, + self._stream_cls( + cast_to=extract_stream_chunk_type(self._stream_cls), + response=self.http_response, + client=cast(Any, self._client), + ), + ) + + stream_cls = cast("type[Stream[Any]] | type[AsyncStream[Any]] | None", self._client._default_stream_cls) + if stream_cls is None: + raise MissingStreamClassError() + + return cast( + R, + stream_cls( + cast_to=cast_to, + response=self.http_response, + client=cast(Any, self._client), + ), + ) + + if cast_to is NoneType: + return cast(R, None) + + response = self.http_response + if cast_to == str: + return cast(R, response.text) + + if cast_to == bytes: + return cast(R, response.content) + + if cast_to == int: + return cast(R, int(response.text)) + + if cast_to == float: + return cast(R, float(response.text)) + + if cast_to == bool: + return cast(R, response.text.lower() == "true") + + if origin == APIResponse: + raise RuntimeError("Unexpected state - cast_to is `APIResponse`") + + if inspect.isclass(origin) and issubclass(origin, httpx.Response): + # Because of the invariance of our ResponseT TypeVar, users can subclass httpx.Response + # and pass that class to our request functions. We cannot change the variance to be either + # covariant or contravariant as that makes our usage of ResponseT illegal. We could construct + # the response class ourselves but that is something that should be supported directly in httpx + # as it would be easy to incorrectly construct the Response object due to the multitude of arguments. + if cast_to != httpx.Response: + raise ValueError(f"Subclasses of httpx.Response cannot be passed to `cast_to`") + return cast(R, response) + + if ( + inspect.isclass( + origin # pyright: ignore[reportUnknownArgumentType] + ) + and not issubclass(origin, BaseModel) + and issubclass(origin, pydantic.BaseModel) + ): + raise TypeError("Pydantic models must subclass our base model type, e.g. `from unlayer import BaseModel`") + + if ( + cast_to is not object + and not origin is list + and not origin is dict + and not origin is Union + and not issubclass(origin, BaseModel) + ): + raise RuntimeError( + f"Unsupported type, expected {cast_to} to be a subclass of {BaseModel}, {dict}, {list}, {Union}, {NoneType}, {str} or {httpx.Response}." + ) + + # split is required to handle cases where additional information is included + # in the response, e.g. application/json; charset=utf-8 + content_type, *_ = response.headers.get("content-type", "*").split(";") + if not content_type.endswith("json"): + if is_basemodel(cast_to): + try: + data = response.json() + except Exception as exc: + log.debug("Could not read JSON from response data due to %s - %s", type(exc), exc) + else: + return self._client._process_response_data( + data=data, + cast_to=cast_to, # type: ignore + response=response, + ) + + if self._client._strict_response_validation: + raise APIResponseValidationError( + response=response, + message=f"Expected Content-Type response header to be `application/json` but received `{content_type}` instead.", + body=response.text, + ) + + # If the API responds with content that isn't JSON then we just return + # the (decoded) text without performing any parsing so that you can still + # handle the response however you need to. + return response.text # type: ignore + + data = response.json() + + return self._client._process_response_data( + data=data, + cast_to=cast_to, # type: ignore + response=response, + ) + + +class APIResponse(BaseAPIResponse[R]): + @overload + def parse(self, *, to: type[_T]) -> _T: ... + + @overload + def parse(self) -> R: ... + + def parse(self, *, to: type[_T] | None = None) -> R | _T: + """Returns the rich python representation of this response's data. + + For lower-level control, see `.read()`, `.json()`, `.iter_bytes()`. + + You can customise the type that the response is parsed into through + the `to` argument, e.g. + + ```py + from unlayer import BaseModel + + + class MyModel(BaseModel): + foo: str + + + obj = response.parse(to=MyModel) + print(obj.foo) + ``` + + We support parsing: + - `BaseModel` + - `dict` + - `list` + - `Union` + - `str` + - `int` + - `float` + - `httpx.Response` + """ + cache_key = to if to is not None else self._cast_to + cached = self._parsed_by_type.get(cache_key) + if cached is not None: + return cached # type: ignore[no-any-return] + + if not self._is_sse_stream: + self.read() + + parsed = self._parse(to=to) + if is_given(self._options.post_parser): + parsed = self._options.post_parser(parsed) + + self._parsed_by_type[cache_key] = parsed + return parsed + + def read(self) -> bytes: + """Read and return the binary response content.""" + try: + return self.http_response.read() + except httpx.StreamConsumed as exc: + # The default error raised by httpx isn't very + # helpful in our case so we re-raise it with + # a different error message. + raise StreamAlreadyConsumed() from exc + + def text(self) -> str: + """Read and decode the response content into a string.""" + self.read() + return self.http_response.text + + def json(self) -> object: + """Read and decode the JSON response content.""" + self.read() + return self.http_response.json() + + def close(self) -> None: + """Close the response and release the connection. + + Automatically called if the response body is read to completion. + """ + self.http_response.close() + + def iter_bytes(self, chunk_size: int | None = None) -> Iterator[bytes]: + """ + A byte-iterator over the decoded response content. + + This automatically handles gzip, deflate and brotli encoded responses. + """ + for chunk in self.http_response.iter_bytes(chunk_size): + yield chunk + + def iter_text(self, chunk_size: int | None = None) -> Iterator[str]: + """A str-iterator over the decoded response content + that handles both gzip, deflate, etc but also detects the content's + string encoding. + """ + for chunk in self.http_response.iter_text(chunk_size): + yield chunk + + def iter_lines(self) -> Iterator[str]: + """Like `iter_text()` but will only yield chunks for each line""" + for chunk in self.http_response.iter_lines(): + yield chunk + + +class AsyncAPIResponse(BaseAPIResponse[R]): + @overload + async def parse(self, *, to: type[_T]) -> _T: ... + + @overload + async def parse(self) -> R: ... + + async def parse(self, *, to: type[_T] | None = None) -> R | _T: + """Returns the rich python representation of this response's data. + + For lower-level control, see `.read()`, `.json()`, `.iter_bytes()`. + + You can customise the type that the response is parsed into through + the `to` argument, e.g. + + ```py + from unlayer import BaseModel + + + class MyModel(BaseModel): + foo: str + + + obj = response.parse(to=MyModel) + print(obj.foo) + ``` + + We support parsing: + - `BaseModel` + - `dict` + - `list` + - `Union` + - `str` + - `httpx.Response` + """ + cache_key = to if to is not None else self._cast_to + cached = self._parsed_by_type.get(cache_key) + if cached is not None: + return cached # type: ignore[no-any-return] + + if not self._is_sse_stream: + await self.read() + + parsed = self._parse(to=to) + if is_given(self._options.post_parser): + parsed = self._options.post_parser(parsed) + + self._parsed_by_type[cache_key] = parsed + return parsed + + async def read(self) -> bytes: + """Read and return the binary response content.""" + try: + return await self.http_response.aread() + except httpx.StreamConsumed as exc: + # the default error raised by httpx isn't very + # helpful in our case so we re-raise it with + # a different error message + raise StreamAlreadyConsumed() from exc + + async def text(self) -> str: + """Read and decode the response content into a string.""" + await self.read() + return self.http_response.text + + async def json(self) -> object: + """Read and decode the JSON response content.""" + await self.read() + return self.http_response.json() + + async def close(self) -> None: + """Close the response and release the connection. + + Automatically called if the response body is read to completion. + """ + await self.http_response.aclose() + + async def iter_bytes(self, chunk_size: int | None = None) -> AsyncIterator[bytes]: + """ + A byte-iterator over the decoded response content. + + This automatically handles gzip, deflate and brotli encoded responses. + """ + async for chunk in self.http_response.aiter_bytes(chunk_size): + yield chunk + + async def iter_text(self, chunk_size: int | None = None) -> AsyncIterator[str]: + """A str-iterator over the decoded response content + that handles both gzip, deflate, etc but also detects the content's + string encoding. + """ + async for chunk in self.http_response.aiter_text(chunk_size): + yield chunk + + async def iter_lines(self) -> AsyncIterator[str]: + """Like `iter_text()` but will only yield chunks for each line""" + async for chunk in self.http_response.aiter_lines(): + yield chunk + + +class BinaryAPIResponse(APIResponse[bytes]): + """Subclass of APIResponse providing helpers for dealing with binary data. + + Note: If you want to stream the response data instead of eagerly reading it + all at once then you should use `.with_streaming_response` when making + the API request, e.g. `.with_streaming_response.get_binary_response()` + """ + + def write_to_file( + self, + file: str | os.PathLike[str], + ) -> None: + """Write the output to the given file. + + Accepts a filename or any path-like object, e.g. pathlib.Path + + Note: if you want to stream the data to the file instead of writing + all at once then you should use `.with_streaming_response` when making + the API request, e.g. `.with_streaming_response.get_binary_response()` + """ + with open(file, mode="wb") as f: + for data in self.iter_bytes(): + f.write(data) + + +class AsyncBinaryAPIResponse(AsyncAPIResponse[bytes]): + """Subclass of APIResponse providing helpers for dealing with binary data. + + Note: If you want to stream the response data instead of eagerly reading it + all at once then you should use `.with_streaming_response` when making + the API request, e.g. `.with_streaming_response.get_binary_response()` + """ + + async def write_to_file( + self, + file: str | os.PathLike[str], + ) -> None: + """Write the output to the given file. + + Accepts a filename or any path-like object, e.g. pathlib.Path + + Note: if you want to stream the data to the file instead of writing + all at once then you should use `.with_streaming_response` when making + the API request, e.g. `.with_streaming_response.get_binary_response()` + """ + path = anyio.Path(file) + async with await path.open(mode="wb") as f: + async for data in self.iter_bytes(): + await f.write(data) + + +class StreamedBinaryAPIResponse(APIResponse[bytes]): + def stream_to_file( + self, + file: str | os.PathLike[str], + *, + chunk_size: int | None = None, + ) -> None: + """Streams the output to the given file. + + Accepts a filename or any path-like object, e.g. pathlib.Path + """ + with open(file, mode="wb") as f: + for data in self.iter_bytes(chunk_size): + f.write(data) + + +class AsyncStreamedBinaryAPIResponse(AsyncAPIResponse[bytes]): + async def stream_to_file( + self, + file: str | os.PathLike[str], + *, + chunk_size: int | None = None, + ) -> None: + """Streams the output to the given file. + + Accepts a filename or any path-like object, e.g. pathlib.Path + """ + path = anyio.Path(file) + async with await path.open(mode="wb") as f: + async for data in self.iter_bytes(chunk_size): + await f.write(data) + + +class MissingStreamClassError(TypeError): + def __init__(self) -> None: + super().__init__( + "The `stream` argument was set to `True` but the `stream_cls` argument was not given. See `unlayer._streaming` for reference", + ) + + +class StreamAlreadyConsumed(UnlayerError): + """ + Attempted to read or stream content, but the content has already + been streamed. + + This can happen if you use a method like `.iter_lines()` and then attempt + to read th entire response body afterwards, e.g. + + ```py + response = await client.post(...) + async for line in response.iter_lines(): + ... # do something with `line` + + content = await response.read() + # ^ error + ``` + + If you want this behaviour you'll need to either manually accumulate the response + content or call `await response.read()` before iterating over the stream. + """ + + def __init__(self) -> None: + message = ( + "Attempted to read or stream some content, but the content has " + "already been streamed. " + "This could be due to attempting to stream the response " + "content more than once." + "\n\n" + "You can fix this by manually accumulating the response content while streaming " + "or by calling `.read()` before starting to stream." + ) + super().__init__(message) + + +class ResponseContextManager(Generic[_APIResponseT]): + """Context manager for ensuring that a request is not made + until it is entered and that the response will always be closed + when the context manager exits + """ + + def __init__(self, request_func: Callable[[], _APIResponseT]) -> None: + self._request_func = request_func + self.__response: _APIResponseT | None = None + + def __enter__(self) -> _APIResponseT: + self.__response = self._request_func() + return self.__response + + def __exit__( + self, + exc_type: type[BaseException] | None, + exc: BaseException | None, + exc_tb: TracebackType | None, + ) -> None: + if self.__response is not None: + self.__response.close() + + +class AsyncResponseContextManager(Generic[_AsyncAPIResponseT]): + """Context manager for ensuring that a request is not made + until it is entered and that the response will always be closed + when the context manager exits + """ + + def __init__(self, api_request: Awaitable[_AsyncAPIResponseT]) -> None: + self._api_request = api_request + self.__response: _AsyncAPIResponseT | None = None + + async def __aenter__(self) -> _AsyncAPIResponseT: + self.__response = await self._api_request + return self.__response + + async def __aexit__( + self, + exc_type: type[BaseException] | None, + exc: BaseException | None, + exc_tb: TracebackType | None, + ) -> None: + if self.__response is not None: + await self.__response.close() + + +def to_streamed_response_wrapper(func: Callable[P, R]) -> Callable[P, ResponseContextManager[APIResponse[R]]]: + """Higher order function that takes one of our bound API methods and wraps it + to support streaming and returning the raw `APIResponse` object directly. + """ + + @functools.wraps(func) + def wrapped(*args: P.args, **kwargs: P.kwargs) -> ResponseContextManager[APIResponse[R]]: + extra_headers: dict[str, str] = {**(cast(Any, kwargs.get("extra_headers")) or {})} + extra_headers[RAW_RESPONSE_HEADER] = "stream" + + kwargs["extra_headers"] = extra_headers + + make_request = functools.partial(func, *args, **kwargs) + + return ResponseContextManager(cast(Callable[[], APIResponse[R]], make_request)) + + return wrapped + + +def async_to_streamed_response_wrapper( + func: Callable[P, Awaitable[R]], +) -> Callable[P, AsyncResponseContextManager[AsyncAPIResponse[R]]]: + """Higher order function that takes one of our bound API methods and wraps it + to support streaming and returning the raw `APIResponse` object directly. + """ + + @functools.wraps(func) + def wrapped(*args: P.args, **kwargs: P.kwargs) -> AsyncResponseContextManager[AsyncAPIResponse[R]]: + extra_headers: dict[str, str] = {**(cast(Any, kwargs.get("extra_headers")) or {})} + extra_headers[RAW_RESPONSE_HEADER] = "stream" + + kwargs["extra_headers"] = extra_headers + + make_request = func(*args, **kwargs) + + return AsyncResponseContextManager(cast(Awaitable[AsyncAPIResponse[R]], make_request)) + + return wrapped + + +def to_custom_streamed_response_wrapper( + func: Callable[P, object], + response_cls: type[_APIResponseT], +) -> Callable[P, ResponseContextManager[_APIResponseT]]: + """Higher order function that takes one of our bound API methods and an `APIResponse` class + and wraps the method to support streaming and returning the given response class directly. + + Note: the given `response_cls` *must* be concrete, e.g. `class BinaryAPIResponse(APIResponse[bytes])` + """ + + @functools.wraps(func) + def wrapped(*args: P.args, **kwargs: P.kwargs) -> ResponseContextManager[_APIResponseT]: + extra_headers: dict[str, Any] = {**(cast(Any, kwargs.get("extra_headers")) or {})} + extra_headers[RAW_RESPONSE_HEADER] = "stream" + extra_headers[OVERRIDE_CAST_TO_HEADER] = response_cls + + kwargs["extra_headers"] = extra_headers + + make_request = functools.partial(func, *args, **kwargs) + + return ResponseContextManager(cast(Callable[[], _APIResponseT], make_request)) + + return wrapped + + +def async_to_custom_streamed_response_wrapper( + func: Callable[P, Awaitable[object]], + response_cls: type[_AsyncAPIResponseT], +) -> Callable[P, AsyncResponseContextManager[_AsyncAPIResponseT]]: + """Higher order function that takes one of our bound API methods and an `APIResponse` class + and wraps the method to support streaming and returning the given response class directly. + + Note: the given `response_cls` *must* be concrete, e.g. `class BinaryAPIResponse(APIResponse[bytes])` + """ + + @functools.wraps(func) + def wrapped(*args: P.args, **kwargs: P.kwargs) -> AsyncResponseContextManager[_AsyncAPIResponseT]: + extra_headers: dict[str, Any] = {**(cast(Any, kwargs.get("extra_headers")) or {})} + extra_headers[RAW_RESPONSE_HEADER] = "stream" + extra_headers[OVERRIDE_CAST_TO_HEADER] = response_cls + + kwargs["extra_headers"] = extra_headers + + make_request = func(*args, **kwargs) + + return AsyncResponseContextManager(cast(Awaitable[_AsyncAPIResponseT], make_request)) + + return wrapped + + +def to_raw_response_wrapper(func: Callable[P, R]) -> Callable[P, APIResponse[R]]: + """Higher order function that takes one of our bound API methods and wraps it + to support returning the raw `APIResponse` object directly. + """ + + @functools.wraps(func) + def wrapped(*args: P.args, **kwargs: P.kwargs) -> APIResponse[R]: + extra_headers: dict[str, str] = {**(cast(Any, kwargs.get("extra_headers")) or {})} + extra_headers[RAW_RESPONSE_HEADER] = "raw" + + kwargs["extra_headers"] = extra_headers + + return cast(APIResponse[R], func(*args, **kwargs)) + + return wrapped + + +def async_to_raw_response_wrapper(func: Callable[P, Awaitable[R]]) -> Callable[P, Awaitable[AsyncAPIResponse[R]]]: + """Higher order function that takes one of our bound API methods and wraps it + to support returning the raw `APIResponse` object directly. + """ + + @functools.wraps(func) + async def wrapped(*args: P.args, **kwargs: P.kwargs) -> AsyncAPIResponse[R]: + extra_headers: dict[str, str] = {**(cast(Any, kwargs.get("extra_headers")) or {})} + extra_headers[RAW_RESPONSE_HEADER] = "raw" + + kwargs["extra_headers"] = extra_headers + + return cast(AsyncAPIResponse[R], await func(*args, **kwargs)) + + return wrapped + + +def to_custom_raw_response_wrapper( + func: Callable[P, object], + response_cls: type[_APIResponseT], +) -> Callable[P, _APIResponseT]: + """Higher order function that takes one of our bound API methods and an `APIResponse` class + and wraps the method to support returning the given response class directly. + + Note: the given `response_cls` *must* be concrete, e.g. `class BinaryAPIResponse(APIResponse[bytes])` + """ + + @functools.wraps(func) + def wrapped(*args: P.args, **kwargs: P.kwargs) -> _APIResponseT: + extra_headers: dict[str, Any] = {**(cast(Any, kwargs.get("extra_headers")) or {})} + extra_headers[RAW_RESPONSE_HEADER] = "raw" + extra_headers[OVERRIDE_CAST_TO_HEADER] = response_cls + + kwargs["extra_headers"] = extra_headers + + return cast(_APIResponseT, func(*args, **kwargs)) + + return wrapped + + +def async_to_custom_raw_response_wrapper( + func: Callable[P, Awaitable[object]], + response_cls: type[_AsyncAPIResponseT], +) -> Callable[P, Awaitable[_AsyncAPIResponseT]]: + """Higher order function that takes one of our bound API methods and an `APIResponse` class + and wraps the method to support returning the given response class directly. + + Note: the given `response_cls` *must* be concrete, e.g. `class BinaryAPIResponse(APIResponse[bytes])` + """ + + @functools.wraps(func) + def wrapped(*args: P.args, **kwargs: P.kwargs) -> Awaitable[_AsyncAPIResponseT]: + extra_headers: dict[str, Any] = {**(cast(Any, kwargs.get("extra_headers")) or {})} + extra_headers[RAW_RESPONSE_HEADER] = "raw" + extra_headers[OVERRIDE_CAST_TO_HEADER] = response_cls + + kwargs["extra_headers"] = extra_headers + + return cast(Awaitable[_AsyncAPIResponseT], func(*args, **kwargs)) + + return wrapped + + +def extract_response_type(typ: type[BaseAPIResponse[Any]]) -> type: + """Given a type like `APIResponse[T]`, returns the generic type variable `T`. + + This also handles the case where a concrete subclass is given, e.g. + ```py + class MyResponse(APIResponse[bytes]): + ... + + extract_response_type(MyResponse) -> bytes + ``` + """ + return extract_type_var_from_base( + typ, + generic_bases=cast("tuple[type, ...]", (BaseAPIResponse, APIResponse, AsyncAPIResponse)), + index=0, + ) diff --git a/src/unlayer/_streaming.py b/src/unlayer/_streaming.py new file mode 100644 index 0000000..d6a6f4d --- /dev/null +++ b/src/unlayer/_streaming.py @@ -0,0 +1,333 @@ +# Note: initially copied from https://github.com/florimondmanca/httpx-sse/blob/master/src/httpx_sse/_decoders.py +from __future__ import annotations + +import json +import inspect +from types import TracebackType +from typing import TYPE_CHECKING, Any, Generic, TypeVar, Iterator, AsyncIterator, cast +from typing_extensions import Self, Protocol, TypeGuard, override, get_origin, runtime_checkable + +import httpx + +from ._utils import extract_type_var_from_base + +if TYPE_CHECKING: + from ._client import Unlayer, AsyncUnlayer + + +_T = TypeVar("_T") + + +class Stream(Generic[_T]): + """Provides the core interface to iterate over a synchronous stream response.""" + + response: httpx.Response + + _decoder: SSEBytesDecoder + + def __init__( + self, + *, + cast_to: type[_T], + response: httpx.Response, + client: Unlayer, + ) -> None: + self.response = response + self._cast_to = cast_to + self._client = client + self._decoder = client._make_sse_decoder() + self._iterator = self.__stream__() + + def __next__(self) -> _T: + return self._iterator.__next__() + + def __iter__(self) -> Iterator[_T]: + for item in self._iterator: + yield item + + def _iter_events(self) -> Iterator[ServerSentEvent]: + yield from self._decoder.iter_bytes(self.response.iter_bytes()) + + def __stream__(self) -> Iterator[_T]: + cast_to = cast(Any, self._cast_to) + response = self.response + process_data = self._client._process_response_data + iterator = self._iter_events() + + try: + for sse in iterator: + yield process_data(data=sse.json(), cast_to=cast_to, response=response) + finally: + # Ensure the response is closed even if the consumer doesn't read all data + response.close() + + def __enter__(self) -> Self: + return self + + def __exit__( + self, + exc_type: type[BaseException] | None, + exc: BaseException | None, + exc_tb: TracebackType | None, + ) -> None: + self.close() + + def close(self) -> None: + """ + Close the response and release the connection. + + Automatically called if the response body is read to completion. + """ + self.response.close() + + +class AsyncStream(Generic[_T]): + """Provides the core interface to iterate over an asynchronous stream response.""" + + response: httpx.Response + + _decoder: SSEDecoder | SSEBytesDecoder + + def __init__( + self, + *, + cast_to: type[_T], + response: httpx.Response, + client: AsyncUnlayer, + ) -> None: + self.response = response + self._cast_to = cast_to + self._client = client + self._decoder = client._make_sse_decoder() + self._iterator = self.__stream__() + + async def __anext__(self) -> _T: + return await self._iterator.__anext__() + + async def __aiter__(self) -> AsyncIterator[_T]: + async for item in self._iterator: + yield item + + async def _iter_events(self) -> AsyncIterator[ServerSentEvent]: + async for sse in self._decoder.aiter_bytes(self.response.aiter_bytes()): + yield sse + + async def __stream__(self) -> AsyncIterator[_T]: + cast_to = cast(Any, self._cast_to) + response = self.response + process_data = self._client._process_response_data + iterator = self._iter_events() + + try: + async for sse in iterator: + yield process_data(data=sse.json(), cast_to=cast_to, response=response) + finally: + # Ensure the response is closed even if the consumer doesn't read all data + await response.aclose() + + async def __aenter__(self) -> Self: + return self + + async def __aexit__( + self, + exc_type: type[BaseException] | None, + exc: BaseException | None, + exc_tb: TracebackType | None, + ) -> None: + await self.close() + + async def close(self) -> None: + """ + Close the response and release the connection. + + Automatically called if the response body is read to completion. + """ + await self.response.aclose() + + +class ServerSentEvent: + def __init__( + self, + *, + event: str | None = None, + data: str | None = None, + id: str | None = None, + retry: int | None = None, + ) -> None: + if data is None: + data = "" + + self._id = id + self._data = data + self._event = event or None + self._retry = retry + + @property + def event(self) -> str | None: + return self._event + + @property + def id(self) -> str | None: + return self._id + + @property + def retry(self) -> int | None: + return self._retry + + @property + def data(self) -> str: + return self._data + + def json(self) -> Any: + return json.loads(self.data) + + @override + def __repr__(self) -> str: + return f"ServerSentEvent(event={self.event}, data={self.data}, id={self.id}, retry={self.retry})" + + +class SSEDecoder: + _data: list[str] + _event: str | None + _retry: int | None + _last_event_id: str | None + + def __init__(self) -> None: + self._event = None + self._data = [] + self._last_event_id = None + self._retry = None + + def iter_bytes(self, iterator: Iterator[bytes]) -> Iterator[ServerSentEvent]: + """Given an iterator that yields raw binary data, iterate over it & yield every event encountered""" + for chunk in self._iter_chunks(iterator): + # Split before decoding so splitlines() only uses \r and \n + for raw_line in chunk.splitlines(): + line = raw_line.decode("utf-8") + sse = self.decode(line) + if sse: + yield sse + + def _iter_chunks(self, iterator: Iterator[bytes]) -> Iterator[bytes]: + """Given an iterator that yields raw binary data, iterate over it and yield individual SSE chunks""" + data = b"" + for chunk in iterator: + for line in chunk.splitlines(keepends=True): + data += line + if data.endswith((b"\r\r", b"\n\n", b"\r\n\r\n")): + yield data + data = b"" + if data: + yield data + + async def aiter_bytes(self, iterator: AsyncIterator[bytes]) -> AsyncIterator[ServerSentEvent]: + """Given an iterator that yields raw binary data, iterate over it & yield every event encountered""" + async for chunk in self._aiter_chunks(iterator): + # Split before decoding so splitlines() only uses \r and \n + for raw_line in chunk.splitlines(): + line = raw_line.decode("utf-8") + sse = self.decode(line) + if sse: + yield sse + + async def _aiter_chunks(self, iterator: AsyncIterator[bytes]) -> AsyncIterator[bytes]: + """Given an iterator that yields raw binary data, iterate over it and yield individual SSE chunks""" + data = b"" + async for chunk in iterator: + for line in chunk.splitlines(keepends=True): + data += line + if data.endswith((b"\r\r", b"\n\n", b"\r\n\r\n")): + yield data + data = b"" + if data: + yield data + + def decode(self, line: str) -> ServerSentEvent | None: + # See: https://html.spec.whatwg.org/multipage/server-sent-events.html#event-stream-interpretation # noqa: E501 + + if not line: + if not self._event and not self._data and not self._last_event_id and self._retry is None: + return None + + sse = ServerSentEvent( + event=self._event, + data="\n".join(self._data), + id=self._last_event_id, + retry=self._retry, + ) + + # NOTE: as per the SSE spec, do not reset last_event_id. + self._event = None + self._data = [] + self._retry = None + + return sse + + if line.startswith(":"): + return None + + fieldname, _, value = line.partition(":") + + if value.startswith(" "): + value = value[1:] + + if fieldname == "event": + self._event = value + elif fieldname == "data": + self._data.append(value) + elif fieldname == "id": + if "\0" in value: + pass + else: + self._last_event_id = value + elif fieldname == "retry": + try: + self._retry = int(value) + except (TypeError, ValueError): + pass + else: + pass # Field is ignored. + + return None + + +@runtime_checkable +class SSEBytesDecoder(Protocol): + def iter_bytes(self, iterator: Iterator[bytes]) -> Iterator[ServerSentEvent]: + """Given an iterator that yields raw binary data, iterate over it & yield every event encountered""" + ... + + def aiter_bytes(self, iterator: AsyncIterator[bytes]) -> AsyncIterator[ServerSentEvent]: + """Given an async iterator that yields raw binary data, iterate over it & yield every event encountered""" + ... + + +def is_stream_class_type(typ: type) -> TypeGuard[type[Stream[object]] | type[AsyncStream[object]]]: + """TypeGuard for determining whether or not the given type is a subclass of `Stream` / `AsyncStream`""" + origin = get_origin(typ) or typ + return inspect.isclass(origin) and issubclass(origin, (Stream, AsyncStream)) + + +def extract_stream_chunk_type( + stream_cls: type, + *, + failure_message: str | None = None, +) -> type: + """Given a type like `Stream[T]`, returns the generic type variable `T`. + + This also handles the case where a concrete subclass is given, e.g. + ```py + class MyStream(Stream[bytes]): + ... + + extract_stream_chunk_type(MyStream) -> bytes + ``` + """ + from ._base_client import Stream, AsyncStream + + return extract_type_var_from_base( + stream_cls, + index=0, + generic_bases=cast("tuple[type, ...]", (Stream, AsyncStream)), + failure_message=failure_message, + ) diff --git a/src/unlayer/_types.py b/src/unlayer/_types.py new file mode 100644 index 0000000..067c3d4 --- /dev/null +++ b/src/unlayer/_types.py @@ -0,0 +1,261 @@ +from __future__ import annotations + +from os import PathLike +from typing import ( + IO, + TYPE_CHECKING, + Any, + Dict, + List, + Type, + Tuple, + Union, + Mapping, + TypeVar, + Callable, + Iterator, + Optional, + Sequence, +) +from typing_extensions import ( + Set, + Literal, + Protocol, + TypeAlias, + TypedDict, + SupportsIndex, + overload, + override, + runtime_checkable, +) + +import httpx +import pydantic +from httpx import URL, Proxy, Timeout, Response, BaseTransport, AsyncBaseTransport + +if TYPE_CHECKING: + from ._models import BaseModel + from ._response import APIResponse, AsyncAPIResponse + +Transport = BaseTransport +AsyncTransport = AsyncBaseTransport +Query = Mapping[str, object] +Body = object +AnyMapping = Mapping[str, object] +ModelT = TypeVar("ModelT", bound=pydantic.BaseModel) +_T = TypeVar("_T") + + +# Approximates httpx internal ProxiesTypes and RequestFiles types +# while adding support for `PathLike` instances +ProxiesDict = Dict["str | URL", Union[None, str, URL, Proxy]] +ProxiesTypes = Union[str, Proxy, ProxiesDict] +if TYPE_CHECKING: + Base64FileInput = Union[IO[bytes], PathLike[str]] + FileContent = Union[IO[bytes], bytes, PathLike[str]] +else: + Base64FileInput = Union[IO[bytes], PathLike] + FileContent = Union[IO[bytes], bytes, PathLike] # PathLike is not subscriptable in Python 3.8. +FileTypes = Union[ + # file (or bytes) + FileContent, + # (filename, file (or bytes)) + Tuple[Optional[str], FileContent], + # (filename, file (or bytes), content_type) + Tuple[Optional[str], FileContent, Optional[str]], + # (filename, file (or bytes), content_type, headers) + Tuple[Optional[str], FileContent, Optional[str], Mapping[str, str]], +] +RequestFiles = Union[Mapping[str, FileTypes], Sequence[Tuple[str, FileTypes]]] + +# duplicate of the above but without our custom file support +HttpxFileContent = Union[IO[bytes], bytes] +HttpxFileTypes = Union[ + # file (or bytes) + HttpxFileContent, + # (filename, file (or bytes)) + Tuple[Optional[str], HttpxFileContent], + # (filename, file (or bytes), content_type) + Tuple[Optional[str], HttpxFileContent, Optional[str]], + # (filename, file (or bytes), content_type, headers) + Tuple[Optional[str], HttpxFileContent, Optional[str], Mapping[str, str]], +] +HttpxRequestFiles = Union[Mapping[str, HttpxFileTypes], Sequence[Tuple[str, HttpxFileTypes]]] + +# Workaround to support (cast_to: Type[ResponseT]) -> ResponseT +# where ResponseT includes `None`. In order to support directly +# passing `None`, overloads would have to be defined for every +# method that uses `ResponseT` which would lead to an unacceptable +# amount of code duplication and make it unreadable. See _base_client.py +# for example usage. +# +# This unfortunately means that you will either have +# to import this type and pass it explicitly: +# +# from unlayer import NoneType +# client.get('/foo', cast_to=NoneType) +# +# or build it yourself: +# +# client.get('/foo', cast_to=type(None)) +if TYPE_CHECKING: + NoneType: Type[None] +else: + NoneType = type(None) + + +class RequestOptions(TypedDict, total=False): + headers: Headers + max_retries: int + timeout: float | Timeout | None + params: Query + extra_json: AnyMapping + idempotency_key: str + follow_redirects: bool + + +# Sentinel class used until PEP 0661 is accepted +class NotGiven: + """ + For parameters with a meaningful None value, we need to distinguish between + the user explicitly passing None, and the user not passing the parameter at + all. + + User code shouldn't need to use not_given directly. + + For example: + + ```py + def create(timeout: Timeout | None | NotGiven = not_given): ... + + + create(timeout=1) # 1s timeout + create(timeout=None) # No timeout + create() # Default timeout behavior + ``` + """ + + def __bool__(self) -> Literal[False]: + return False + + @override + def __repr__(self) -> str: + return "NOT_GIVEN" + + +not_given = NotGiven() +# for backwards compatibility: +NOT_GIVEN = NotGiven() + + +class Omit: + """ + To explicitly omit something from being sent in a request, use `omit`. + + ```py + # as the default `Content-Type` header is `application/json` that will be sent + client.post("/upload/files", files={"file": b"my raw file content"}) + + # you can't explicitly override the header as it has to be dynamically generated + # to look something like: 'multipart/form-data; boundary=0d8382fcf5f8c3be01ca2e11002d2983' + client.post(..., headers={"Content-Type": "multipart/form-data"}) + + # instead you can remove the default `application/json` header by passing omit + client.post(..., headers={"Content-Type": omit}) + ``` + """ + + def __bool__(self) -> Literal[False]: + return False + + +omit = Omit() + + +@runtime_checkable +class ModelBuilderProtocol(Protocol): + @classmethod + def build( + cls: type[_T], + *, + response: Response, + data: object, + ) -> _T: ... + + +Headers = Mapping[str, Union[str, Omit]] + + +class HeadersLikeProtocol(Protocol): + def get(self, __key: str) -> str | None: ... + + +HeadersLike = Union[Headers, HeadersLikeProtocol] + +ResponseT = TypeVar( + "ResponseT", + bound=Union[ + object, + str, + None, + "BaseModel", + List[Any], + Dict[str, Any], + Response, + ModelBuilderProtocol, + "APIResponse[Any]", + "AsyncAPIResponse[Any]", + ], +) + +StrBytesIntFloat = Union[str, bytes, int, float] + +# Note: copied from Pydantic +# https://github.com/pydantic/pydantic/blob/6f31f8f68ef011f84357330186f603ff295312fd/pydantic/main.py#L79 +IncEx: TypeAlias = Union[Set[int], Set[str], Mapping[int, Union["IncEx", bool]], Mapping[str, Union["IncEx", bool]]] + +PostParser = Callable[[Any], Any] + + +@runtime_checkable +class InheritsGeneric(Protocol): + """Represents a type that has inherited from `Generic` + + The `__orig_bases__` property can be used to determine the resolved + type variable for a given base class. + """ + + __orig_bases__: tuple[_GenericAlias] + + +class _GenericAlias(Protocol): + __origin__: type[object] + + +class HttpxSendArgs(TypedDict, total=False): + auth: httpx.Auth + follow_redirects: bool + + +_T_co = TypeVar("_T_co", covariant=True) + + +if TYPE_CHECKING: + # This works because str.__contains__ does not accept object (either in typeshed or at runtime) + # https://github.com/hauntsaninja/useful_types/blob/5e9710f3875107d068e7679fd7fec9cfab0eff3b/useful_types/__init__.py#L285 + # + # Note: index() and count() methods are intentionally omitted to allow pyright to properly + # infer TypedDict types when dict literals are used in lists assigned to SequenceNotStr. + class SequenceNotStr(Protocol[_T_co]): + @overload + def __getitem__(self, index: SupportsIndex, /) -> _T_co: ... + @overload + def __getitem__(self, index: slice, /) -> Sequence[_T_co]: ... + def __contains__(self, value: object, /) -> bool: ... + def __len__(self) -> int: ... + def __iter__(self) -> Iterator[_T_co]: ... + def __reversed__(self) -> Iterator[_T_co]: ... +else: + # just point this to a normal `Sequence` at runtime to avoid having to special case + # deserializing our custom sequence type + SequenceNotStr = Sequence diff --git a/src/unlayer/_utils/__init__.py b/src/unlayer/_utils/__init__.py new file mode 100644 index 0000000..dc64e29 --- /dev/null +++ b/src/unlayer/_utils/__init__.py @@ -0,0 +1,64 @@ +from ._sync import asyncify as asyncify +from ._proxy import LazyProxy as LazyProxy +from ._utils import ( + flatten as flatten, + is_dict as is_dict, + is_list as is_list, + is_given as is_given, + is_tuple as is_tuple, + json_safe as json_safe, + lru_cache as lru_cache, + is_mapping as is_mapping, + is_tuple_t as is_tuple_t, + is_iterable as is_iterable, + is_sequence as is_sequence, + coerce_float as coerce_float, + is_mapping_t as is_mapping_t, + removeprefix as removeprefix, + removesuffix as removesuffix, + extract_files as extract_files, + is_sequence_t as is_sequence_t, + required_args as required_args, + coerce_boolean as coerce_boolean, + coerce_integer as coerce_integer, + file_from_path as file_from_path, + strip_not_given as strip_not_given, + deepcopy_minimal as deepcopy_minimal, + get_async_library as get_async_library, + maybe_coerce_float as maybe_coerce_float, + get_required_header as get_required_header, + maybe_coerce_boolean as maybe_coerce_boolean, + maybe_coerce_integer as maybe_coerce_integer, +) +from ._compat import ( + get_args as get_args, + is_union as is_union, + get_origin as get_origin, + is_typeddict as is_typeddict, + is_literal_type as is_literal_type, +) +from ._typing import ( + is_list_type as is_list_type, + is_union_type as is_union_type, + extract_type_arg as extract_type_arg, + is_iterable_type as is_iterable_type, + is_required_type as is_required_type, + is_sequence_type as is_sequence_type, + is_annotated_type as is_annotated_type, + is_type_alias_type as is_type_alias_type, + strip_annotated_type as strip_annotated_type, + extract_type_var_from_base as extract_type_var_from_base, +) +from ._streams import consume_sync_iterator as consume_sync_iterator, consume_async_iterator as consume_async_iterator +from ._transform import ( + PropertyInfo as PropertyInfo, + transform as transform, + async_transform as async_transform, + maybe_transform as maybe_transform, + async_maybe_transform as async_maybe_transform, +) +from ._reflection import ( + function_has_argument as function_has_argument, + assert_signatures_in_sync as assert_signatures_in_sync, +) +from ._datetime_parse import parse_date as parse_date, parse_datetime as parse_datetime diff --git a/src/unlayer/_utils/_compat.py b/src/unlayer/_utils/_compat.py new file mode 100644 index 0000000..dd70323 --- /dev/null +++ b/src/unlayer/_utils/_compat.py @@ -0,0 +1,45 @@ +from __future__ import annotations + +import sys +import typing_extensions +from typing import Any, Type, Union, Literal, Optional +from datetime import date, datetime +from typing_extensions import get_args as _get_args, get_origin as _get_origin + +from .._types import StrBytesIntFloat +from ._datetime_parse import parse_date as _parse_date, parse_datetime as _parse_datetime + +_LITERAL_TYPES = {Literal, typing_extensions.Literal} + + +def get_args(tp: type[Any]) -> tuple[Any, ...]: + return _get_args(tp) + + +def get_origin(tp: type[Any]) -> type[Any] | None: + return _get_origin(tp) + + +def is_union(tp: Optional[Type[Any]]) -> bool: + if sys.version_info < (3, 10): + return tp is Union # type: ignore[comparison-overlap] + else: + import types + + return tp is Union or tp is types.UnionType + + +def is_typeddict(tp: Type[Any]) -> bool: + return typing_extensions.is_typeddict(tp) + + +def is_literal_type(tp: Type[Any]) -> bool: + return get_origin(tp) in _LITERAL_TYPES + + +def parse_date(value: Union[date, StrBytesIntFloat]) -> date: + return _parse_date(value) + + +def parse_datetime(value: Union[datetime, StrBytesIntFloat]) -> datetime: + return _parse_datetime(value) diff --git a/src/unlayer/_utils/_datetime_parse.py b/src/unlayer/_utils/_datetime_parse.py new file mode 100644 index 0000000..7cb9d9e --- /dev/null +++ b/src/unlayer/_utils/_datetime_parse.py @@ -0,0 +1,136 @@ +""" +This file contains code from https://github.com/pydantic/pydantic/blob/main/pydantic/v1/datetime_parse.py +without the Pydantic v1 specific errors. +""" + +from __future__ import annotations + +import re +from typing import Dict, Union, Optional +from datetime import date, datetime, timezone, timedelta + +from .._types import StrBytesIntFloat + +date_expr = r"(?P\d{4})-(?P\d{1,2})-(?P\d{1,2})" +time_expr = ( + r"(?P\d{1,2}):(?P\d{1,2})" + r"(?::(?P\d{1,2})(?:\.(?P\d{1,6})\d{0,6})?)?" + r"(?PZ|[+-]\d{2}(?::?\d{2})?)?$" +) + +date_re = re.compile(f"{date_expr}$") +datetime_re = re.compile(f"{date_expr}[T ]{time_expr}") + + +EPOCH = datetime(1970, 1, 1) +# if greater than this, the number is in ms, if less than or equal it's in seconds +# (in seconds this is 11th October 2603, in ms it's 20th August 1970) +MS_WATERSHED = int(2e10) +# slightly more than datetime.max in ns - (datetime.max - EPOCH).total_seconds() * 1e9 +MAX_NUMBER = int(3e20) + + +def _get_numeric(value: StrBytesIntFloat, native_expected_type: str) -> Union[None, int, float]: + if isinstance(value, (int, float)): + return value + try: + return float(value) + except ValueError: + return None + except TypeError: + raise TypeError(f"invalid type; expected {native_expected_type}, string, bytes, int or float") from None + + +def _from_unix_seconds(seconds: Union[int, float]) -> datetime: + if seconds > MAX_NUMBER: + return datetime.max + elif seconds < -MAX_NUMBER: + return datetime.min + + while abs(seconds) > MS_WATERSHED: + seconds /= 1000 + dt = EPOCH + timedelta(seconds=seconds) + return dt.replace(tzinfo=timezone.utc) + + +def _parse_timezone(value: Optional[str]) -> Union[None, int, timezone]: + if value == "Z": + return timezone.utc + elif value is not None: + offset_mins = int(value[-2:]) if len(value) > 3 else 0 + offset = 60 * int(value[1:3]) + offset_mins + if value[0] == "-": + offset = -offset + return timezone(timedelta(minutes=offset)) + else: + return None + + +def parse_datetime(value: Union[datetime, StrBytesIntFloat]) -> datetime: + """ + Parse a datetime/int/float/string and return a datetime.datetime. + + This function supports time zone offsets. When the input contains one, + the output uses a timezone with a fixed offset from UTC. + + Raise ValueError if the input is well formatted but not a valid datetime. + Raise ValueError if the input isn't well formatted. + """ + if isinstance(value, datetime): + return value + + number = _get_numeric(value, "datetime") + if number is not None: + return _from_unix_seconds(number) + + if isinstance(value, bytes): + value = value.decode() + + assert not isinstance(value, (float, int)) + + match = datetime_re.match(value) + if match is None: + raise ValueError("invalid datetime format") + + kw = match.groupdict() + if kw["microsecond"]: + kw["microsecond"] = kw["microsecond"].ljust(6, "0") + + tzinfo = _parse_timezone(kw.pop("tzinfo")) + kw_: Dict[str, Union[None, int, timezone]] = {k: int(v) for k, v in kw.items() if v is not None} + kw_["tzinfo"] = tzinfo + + return datetime(**kw_) # type: ignore + + +def parse_date(value: Union[date, StrBytesIntFloat]) -> date: + """ + Parse a date/int/float/string and return a datetime.date. + + Raise ValueError if the input is well formatted but not a valid date. + Raise ValueError if the input isn't well formatted. + """ + if isinstance(value, date): + if isinstance(value, datetime): + return value.date() + else: + return value + + number = _get_numeric(value, "date") + if number is not None: + return _from_unix_seconds(number).date() + + if isinstance(value, bytes): + value = value.decode() + + assert not isinstance(value, (float, int)) + match = date_re.match(value) + if match is None: + raise ValueError("invalid date format") + + kw = {k: int(v) for k, v in match.groupdict().items()} + + try: + return date(**kw) + except ValueError: + raise ValueError("invalid date format") from None diff --git a/src/unlayer/_utils/_logs.py b/src/unlayer/_utils/_logs.py new file mode 100644 index 0000000..6d0f6f6 --- /dev/null +++ b/src/unlayer/_utils/_logs.py @@ -0,0 +1,25 @@ +import os +import logging + +logger: logging.Logger = logging.getLogger("unlayer") +httpx_logger: logging.Logger = logging.getLogger("httpx") + + +def _basic_config() -> None: + # e.g. [2023-10-05 14:12:26 - unlayer._base_client:818 - DEBUG] HTTP Request: POST http://127.0.0.1:4010/foo/bar "200 OK" + logging.basicConfig( + format="[%(asctime)s - %(name)s:%(lineno)d - %(levelname)s] %(message)s", + datefmt="%Y-%m-%d %H:%M:%S", + ) + + +def setup_logging() -> None: + env = os.environ.get("UNLAYER_LOG") + if env == "debug": + _basic_config() + logger.setLevel(logging.DEBUG) + httpx_logger.setLevel(logging.DEBUG) + elif env == "info": + _basic_config() + logger.setLevel(logging.INFO) + httpx_logger.setLevel(logging.INFO) diff --git a/src/unlayer/_utils/_proxy.py b/src/unlayer/_utils/_proxy.py new file mode 100644 index 0000000..0f239a3 --- /dev/null +++ b/src/unlayer/_utils/_proxy.py @@ -0,0 +1,65 @@ +from __future__ import annotations + +from abc import ABC, abstractmethod +from typing import Generic, TypeVar, Iterable, cast +from typing_extensions import override + +T = TypeVar("T") + + +class LazyProxy(Generic[T], ABC): + """Implements data methods to pretend that an instance is another instance. + + This includes forwarding attribute access and other methods. + """ + + # Note: we have to special case proxies that themselves return proxies + # to support using a proxy as a catch-all for any random access, e.g. `proxy.foo.bar.baz` + + def __getattr__(self, attr: str) -> object: + proxied = self.__get_proxied__() + if isinstance(proxied, LazyProxy): + return proxied # pyright: ignore + return getattr(proxied, attr) + + @override + def __repr__(self) -> str: + proxied = self.__get_proxied__() + if isinstance(proxied, LazyProxy): + return proxied.__class__.__name__ + return repr(self.__get_proxied__()) + + @override + def __str__(self) -> str: + proxied = self.__get_proxied__() + if isinstance(proxied, LazyProxy): + return proxied.__class__.__name__ + return str(proxied) + + @override + def __dir__(self) -> Iterable[str]: + proxied = self.__get_proxied__() + if isinstance(proxied, LazyProxy): + return [] + return proxied.__dir__() + + @property # type: ignore + @override + def __class__(self) -> type: # pyright: ignore + try: + proxied = self.__get_proxied__() + except Exception: + return type(self) + if issubclass(type(proxied), LazyProxy): + return type(proxied) + return proxied.__class__ + + def __get_proxied__(self) -> T: + return self.__load__() + + def __as_proxied__(self) -> T: + """Helper method that returns the current proxy, typed as the loaded object""" + return cast(T, self) + + @abstractmethod + def __load__(self) -> T: ... diff --git a/src/unlayer/_utils/_reflection.py b/src/unlayer/_utils/_reflection.py new file mode 100644 index 0000000..89aa712 --- /dev/null +++ b/src/unlayer/_utils/_reflection.py @@ -0,0 +1,42 @@ +from __future__ import annotations + +import inspect +from typing import Any, Callable + + +def function_has_argument(func: Callable[..., Any], arg_name: str) -> bool: + """Returns whether or not the given function has a specific parameter""" + sig = inspect.signature(func) + return arg_name in sig.parameters + + +def assert_signatures_in_sync( + source_func: Callable[..., Any], + check_func: Callable[..., Any], + *, + exclude_params: set[str] = set(), +) -> None: + """Ensure that the signature of the second function matches the first.""" + + check_sig = inspect.signature(check_func) + source_sig = inspect.signature(source_func) + + errors: list[str] = [] + + for name, source_param in source_sig.parameters.items(): + if name in exclude_params: + continue + + custom_param = check_sig.parameters.get(name) + if not custom_param: + errors.append(f"the `{name}` param is missing") + continue + + if custom_param.annotation != source_param.annotation: + errors.append( + f"types for the `{name}` param are do not match; source={repr(source_param.annotation)} checking={repr(custom_param.annotation)}" + ) + continue + + if errors: + raise AssertionError(f"{len(errors)} errors encountered when comparing signatures:\n\n" + "\n\n".join(errors)) diff --git a/src/unlayer/_utils/_resources_proxy.py b/src/unlayer/_utils/_resources_proxy.py new file mode 100644 index 0000000..8e0e948 --- /dev/null +++ b/src/unlayer/_utils/_resources_proxy.py @@ -0,0 +1,24 @@ +from __future__ import annotations + +from typing import Any +from typing_extensions import override + +from ._proxy import LazyProxy + + +class ResourcesProxy(LazyProxy[Any]): + """A proxy for the `unlayer.resources` module. + + This is used so that we can lazily import `unlayer.resources` only when + needed *and* so that users can just import `unlayer` and reference `unlayer.resources` + """ + + @override + def __load__(self) -> Any: + import importlib + + mod = importlib.import_module("unlayer.resources") + return mod + + +resources = ResourcesProxy().__as_proxied__() diff --git a/src/unlayer/_utils/_streams.py b/src/unlayer/_utils/_streams.py new file mode 100644 index 0000000..f4a0208 --- /dev/null +++ b/src/unlayer/_utils/_streams.py @@ -0,0 +1,12 @@ +from typing import Any +from typing_extensions import Iterator, AsyncIterator + + +def consume_sync_iterator(iterator: Iterator[Any]) -> None: + for _ in iterator: + ... + + +async def consume_async_iterator(iterator: AsyncIterator[Any]) -> None: + async for _ in iterator: + ... diff --git a/src/unlayer/_utils/_sync.py b/src/unlayer/_utils/_sync.py new file mode 100644 index 0000000..f6027c1 --- /dev/null +++ b/src/unlayer/_utils/_sync.py @@ -0,0 +1,58 @@ +from __future__ import annotations + +import asyncio +import functools +from typing import TypeVar, Callable, Awaitable +from typing_extensions import ParamSpec + +import anyio +import sniffio +import anyio.to_thread + +T_Retval = TypeVar("T_Retval") +T_ParamSpec = ParamSpec("T_ParamSpec") + + +async def to_thread( + func: Callable[T_ParamSpec, T_Retval], /, *args: T_ParamSpec.args, **kwargs: T_ParamSpec.kwargs +) -> T_Retval: + if sniffio.current_async_library() == "asyncio": + return await asyncio.to_thread(func, *args, **kwargs) + + return await anyio.to_thread.run_sync( + functools.partial(func, *args, **kwargs), + ) + + +# inspired by `asyncer`, https://github.com/tiangolo/asyncer +def asyncify(function: Callable[T_ParamSpec, T_Retval]) -> Callable[T_ParamSpec, Awaitable[T_Retval]]: + """ + Take a blocking function and create an async one that receives the same + positional and keyword arguments. + + Usage: + + ```python + def blocking_func(arg1, arg2, kwarg1=None): + # blocking code + return result + + + result = asyncify(blocking_function)(arg1, arg2, kwarg1=value1) + ``` + + ## Arguments + + `function`: a blocking regular callable (e.g. a function) + + ## Return + + An async function that takes the same positional and keyword arguments as the + original one, that when called runs the same original function in a thread worker + and returns the result. + """ + + async def wrapper(*args: T_ParamSpec.args, **kwargs: T_ParamSpec.kwargs) -> T_Retval: + return await to_thread(function, *args, **kwargs) + + return wrapper diff --git a/src/unlayer/_utils/_transform.py b/src/unlayer/_utils/_transform.py new file mode 100644 index 0000000..5207549 --- /dev/null +++ b/src/unlayer/_utils/_transform.py @@ -0,0 +1,457 @@ +from __future__ import annotations + +import io +import base64 +import pathlib +from typing import Any, Mapping, TypeVar, cast +from datetime import date, datetime +from typing_extensions import Literal, get_args, override, get_type_hints as _get_type_hints + +import anyio +import pydantic + +from ._utils import ( + is_list, + is_given, + lru_cache, + is_mapping, + is_iterable, + is_sequence, +) +from .._files import is_base64_file_input +from ._compat import get_origin, is_typeddict +from ._typing import ( + is_list_type, + is_union_type, + extract_type_arg, + is_iterable_type, + is_required_type, + is_sequence_type, + is_annotated_type, + strip_annotated_type, +) + +_T = TypeVar("_T") + + +# TODO: support for drilling globals() and locals() +# TODO: ensure works correctly with forward references in all cases + + +PropertyFormat = Literal["iso8601", "base64", "custom"] + + +class PropertyInfo: + """Metadata class to be used in Annotated types to provide information about a given type. + + For example: + + class MyParams(TypedDict): + account_holder_name: Annotated[str, PropertyInfo(alias='accountHolderName')] + + This means that {'account_holder_name': 'Robert'} will be transformed to {'accountHolderName': 'Robert'} before being sent to the API. + """ + + alias: str | None + format: PropertyFormat | None + format_template: str | None + discriminator: str | None + + def __init__( + self, + *, + alias: str | None = None, + format: PropertyFormat | None = None, + format_template: str | None = None, + discriminator: str | None = None, + ) -> None: + self.alias = alias + self.format = format + self.format_template = format_template + self.discriminator = discriminator + + @override + def __repr__(self) -> str: + return f"{self.__class__.__name__}(alias='{self.alias}', format={self.format}, format_template='{self.format_template}', discriminator='{self.discriminator}')" + + +def maybe_transform( + data: object, + expected_type: object, +) -> Any | None: + """Wrapper over `transform()` that allows `None` to be passed. + + See `transform()` for more details. + """ + if data is None: + return None + return transform(data, expected_type) + + +# Wrapper over _transform_recursive providing fake types +def transform( + data: _T, + expected_type: object, +) -> _T: + """Transform dictionaries based off of type information from the given type, for example: + + ```py + class Params(TypedDict, total=False): + card_id: Required[Annotated[str, PropertyInfo(alias="cardID")]] + + + transformed = transform({"card_id": ""}, Params) + # {'cardID': ''} + ``` + + Any keys / data that does not have type information given will be included as is. + + It should be noted that the transformations that this function does are not represented in the type system. + """ + transformed = _transform_recursive(data, annotation=cast(type, expected_type)) + return cast(_T, transformed) + + +@lru_cache(maxsize=8096) +def _get_annotated_type(type_: type) -> type | None: + """If the given type is an `Annotated` type then it is returned, if not `None` is returned. + + This also unwraps the type when applicable, e.g. `Required[Annotated[T, ...]]` + """ + if is_required_type(type_): + # Unwrap `Required[Annotated[T, ...]]` to `Annotated[T, ...]` + type_ = get_args(type_)[0] + + if is_annotated_type(type_): + return type_ + + return None + + +def _maybe_transform_key(key: str, type_: type) -> str: + """Transform the given `data` based on the annotations provided in `type_`. + + Note: this function only looks at `Annotated` types that contain `PropertyInfo` metadata. + """ + annotated_type = _get_annotated_type(type_) + if annotated_type is None: + # no `Annotated` definition for this type, no transformation needed + return key + + # ignore the first argument as it is the actual type + annotations = get_args(annotated_type)[1:] + for annotation in annotations: + if isinstance(annotation, PropertyInfo) and annotation.alias is not None: + return annotation.alias + + return key + + +def _no_transform_needed(annotation: type) -> bool: + return annotation == float or annotation == int + + +def _transform_recursive( + data: object, + *, + annotation: type, + inner_type: type | None = None, +) -> object: + """Transform the given data against the expected type. + + Args: + annotation: The direct type annotation given to the particular piece of data. + This may or may not be wrapped in metadata types, e.g. `Required[T]`, `Annotated[T, ...]` etc + + inner_type: If applicable, this is the "inside" type. This is useful in certain cases where the outside type + is a container type such as `List[T]`. In that case `inner_type` should be set to `T` so that each entry in + the list can be transformed using the metadata from the container type. + + Defaults to the same value as the `annotation` argument. + """ + from .._compat import model_dump + + if inner_type is None: + inner_type = annotation + + stripped_type = strip_annotated_type(inner_type) + origin = get_origin(stripped_type) or stripped_type + if is_typeddict(stripped_type) and is_mapping(data): + return _transform_typeddict(data, stripped_type) + + if origin == dict and is_mapping(data): + items_type = get_args(stripped_type)[1] + return {key: _transform_recursive(value, annotation=items_type) for key, value in data.items()} + + if ( + # List[T] + (is_list_type(stripped_type) and is_list(data)) + # Iterable[T] + or (is_iterable_type(stripped_type) and is_iterable(data) and not isinstance(data, str)) + # Sequence[T] + or (is_sequence_type(stripped_type) and is_sequence(data) and not isinstance(data, str)) + ): + # dicts are technically iterable, but it is an iterable on the keys of the dict and is not usually + # intended as an iterable, so we don't transform it. + if isinstance(data, dict): + return cast(object, data) + + inner_type = extract_type_arg(stripped_type, 0) + if _no_transform_needed(inner_type): + # for some types there is no need to transform anything, so we can get a small + # perf boost from skipping that work. + # + # but we still need to convert to a list to ensure the data is json-serializable + if is_list(data): + return data + return list(data) + + return [_transform_recursive(d, annotation=annotation, inner_type=inner_type) for d in data] + + if is_union_type(stripped_type): + # For union types we run the transformation against all subtypes to ensure that everything is transformed. + # + # TODO: there may be edge cases where the same normalized field name will transform to two different names + # in different subtypes. + for subtype in get_args(stripped_type): + data = _transform_recursive(data, annotation=annotation, inner_type=subtype) + return data + + if isinstance(data, pydantic.BaseModel): + return model_dump(data, exclude_unset=True, mode="json") + + annotated_type = _get_annotated_type(annotation) + if annotated_type is None: + return data + + # ignore the first argument as it is the actual type + annotations = get_args(annotated_type)[1:] + for annotation in annotations: + if isinstance(annotation, PropertyInfo) and annotation.format is not None: + return _format_data(data, annotation.format, annotation.format_template) + + return data + + +def _format_data(data: object, format_: PropertyFormat, format_template: str | None) -> object: + if isinstance(data, (date, datetime)): + if format_ == "iso8601": + return data.isoformat() + + if format_ == "custom" and format_template is not None: + return data.strftime(format_template) + + if format_ == "base64" and is_base64_file_input(data): + binary: str | bytes | None = None + + if isinstance(data, pathlib.Path): + binary = data.read_bytes() + elif isinstance(data, io.IOBase): + binary = data.read() + + if isinstance(binary, str): # type: ignore[unreachable] + binary = binary.encode() + + if not isinstance(binary, bytes): + raise RuntimeError(f"Could not read bytes from {data}; Received {type(binary)}") + + return base64.b64encode(binary).decode("ascii") + + return data + + +def _transform_typeddict( + data: Mapping[str, object], + expected_type: type, +) -> Mapping[str, object]: + result: dict[str, object] = {} + annotations = get_type_hints(expected_type, include_extras=True) + for key, value in data.items(): + if not is_given(value): + # we don't need to include omitted values here as they'll + # be stripped out before the request is sent anyway + continue + + type_ = annotations.get(key) + if type_ is None: + # we do not have a type annotation for this field, leave it as is + result[key] = value + else: + result[_maybe_transform_key(key, type_)] = _transform_recursive(value, annotation=type_) + return result + + +async def async_maybe_transform( + data: object, + expected_type: object, +) -> Any | None: + """Wrapper over `async_transform()` that allows `None` to be passed. + + See `async_transform()` for more details. + """ + if data is None: + return None + return await async_transform(data, expected_type) + + +async def async_transform( + data: _T, + expected_type: object, +) -> _T: + """Transform dictionaries based off of type information from the given type, for example: + + ```py + class Params(TypedDict, total=False): + card_id: Required[Annotated[str, PropertyInfo(alias="cardID")]] + + + transformed = transform({"card_id": ""}, Params) + # {'cardID': ''} + ``` + + Any keys / data that does not have type information given will be included as is. + + It should be noted that the transformations that this function does are not represented in the type system. + """ + transformed = await _async_transform_recursive(data, annotation=cast(type, expected_type)) + return cast(_T, transformed) + + +async def _async_transform_recursive( + data: object, + *, + annotation: type, + inner_type: type | None = None, +) -> object: + """Transform the given data against the expected type. + + Args: + annotation: The direct type annotation given to the particular piece of data. + This may or may not be wrapped in metadata types, e.g. `Required[T]`, `Annotated[T, ...]` etc + + inner_type: If applicable, this is the "inside" type. This is useful in certain cases where the outside type + is a container type such as `List[T]`. In that case `inner_type` should be set to `T` so that each entry in + the list can be transformed using the metadata from the container type. + + Defaults to the same value as the `annotation` argument. + """ + from .._compat import model_dump + + if inner_type is None: + inner_type = annotation + + stripped_type = strip_annotated_type(inner_type) + origin = get_origin(stripped_type) or stripped_type + if is_typeddict(stripped_type) and is_mapping(data): + return await _async_transform_typeddict(data, stripped_type) + + if origin == dict and is_mapping(data): + items_type = get_args(stripped_type)[1] + return {key: _transform_recursive(value, annotation=items_type) for key, value in data.items()} + + if ( + # List[T] + (is_list_type(stripped_type) and is_list(data)) + # Iterable[T] + or (is_iterable_type(stripped_type) and is_iterable(data) and not isinstance(data, str)) + # Sequence[T] + or (is_sequence_type(stripped_type) and is_sequence(data) and not isinstance(data, str)) + ): + # dicts are technically iterable, but it is an iterable on the keys of the dict and is not usually + # intended as an iterable, so we don't transform it. + if isinstance(data, dict): + return cast(object, data) + + inner_type = extract_type_arg(stripped_type, 0) + if _no_transform_needed(inner_type): + # for some types there is no need to transform anything, so we can get a small + # perf boost from skipping that work. + # + # but we still need to convert to a list to ensure the data is json-serializable + if is_list(data): + return data + return list(data) + + return [await _async_transform_recursive(d, annotation=annotation, inner_type=inner_type) for d in data] + + if is_union_type(stripped_type): + # For union types we run the transformation against all subtypes to ensure that everything is transformed. + # + # TODO: there may be edge cases where the same normalized field name will transform to two different names + # in different subtypes. + for subtype in get_args(stripped_type): + data = await _async_transform_recursive(data, annotation=annotation, inner_type=subtype) + return data + + if isinstance(data, pydantic.BaseModel): + return model_dump(data, exclude_unset=True, mode="json") + + annotated_type = _get_annotated_type(annotation) + if annotated_type is None: + return data + + # ignore the first argument as it is the actual type + annotations = get_args(annotated_type)[1:] + for annotation in annotations: + if isinstance(annotation, PropertyInfo) and annotation.format is not None: + return await _async_format_data(data, annotation.format, annotation.format_template) + + return data + + +async def _async_format_data(data: object, format_: PropertyFormat, format_template: str | None) -> object: + if isinstance(data, (date, datetime)): + if format_ == "iso8601": + return data.isoformat() + + if format_ == "custom" and format_template is not None: + return data.strftime(format_template) + + if format_ == "base64" and is_base64_file_input(data): + binary: str | bytes | None = None + + if isinstance(data, pathlib.Path): + binary = await anyio.Path(data).read_bytes() + elif isinstance(data, io.IOBase): + binary = data.read() + + if isinstance(binary, str): # type: ignore[unreachable] + binary = binary.encode() + + if not isinstance(binary, bytes): + raise RuntimeError(f"Could not read bytes from {data}; Received {type(binary)}") + + return base64.b64encode(binary).decode("ascii") + + return data + + +async def _async_transform_typeddict( + data: Mapping[str, object], + expected_type: type, +) -> Mapping[str, object]: + result: dict[str, object] = {} + annotations = get_type_hints(expected_type, include_extras=True) + for key, value in data.items(): + if not is_given(value): + # we don't need to include omitted values here as they'll + # be stripped out before the request is sent anyway + continue + + type_ = annotations.get(key) + if type_ is None: + # we do not have a type annotation for this field, leave it as is + result[key] = value + else: + result[_maybe_transform_key(key, type_)] = await _async_transform_recursive(value, annotation=type_) + return result + + +@lru_cache(maxsize=8096) +def get_type_hints( + obj: Any, + globalns: dict[str, Any] | None = None, + localns: Mapping[str, Any] | None = None, + include_extras: bool = False, +) -> dict[str, Any]: + return _get_type_hints(obj, globalns=globalns, localns=localns, include_extras=include_extras) diff --git a/src/unlayer/_utils/_typing.py b/src/unlayer/_utils/_typing.py new file mode 100644 index 0000000..193109f --- /dev/null +++ b/src/unlayer/_utils/_typing.py @@ -0,0 +1,156 @@ +from __future__ import annotations + +import sys +import typing +import typing_extensions +from typing import Any, TypeVar, Iterable, cast +from collections import abc as _c_abc +from typing_extensions import ( + TypeIs, + Required, + Annotated, + get_args, + get_origin, +) + +from ._utils import lru_cache +from .._types import InheritsGeneric +from ._compat import is_union as _is_union + + +def is_annotated_type(typ: type) -> bool: + return get_origin(typ) == Annotated + + +def is_list_type(typ: type) -> bool: + return (get_origin(typ) or typ) == list + + +def is_sequence_type(typ: type) -> bool: + origin = get_origin(typ) or typ + return origin == typing_extensions.Sequence or origin == typing.Sequence or origin == _c_abc.Sequence + + +def is_iterable_type(typ: type) -> bool: + """If the given type is `typing.Iterable[T]`""" + origin = get_origin(typ) or typ + return origin == Iterable or origin == _c_abc.Iterable + + +def is_union_type(typ: type) -> bool: + return _is_union(get_origin(typ)) + + +def is_required_type(typ: type) -> bool: + return get_origin(typ) == Required + + +def is_typevar(typ: type) -> bool: + # type ignore is required because type checkers + # think this expression will always return False + return type(typ) == TypeVar # type: ignore + + +_TYPE_ALIAS_TYPES: tuple[type[typing_extensions.TypeAliasType], ...] = (typing_extensions.TypeAliasType,) +if sys.version_info >= (3, 12): + _TYPE_ALIAS_TYPES = (*_TYPE_ALIAS_TYPES, typing.TypeAliasType) + + +def is_type_alias_type(tp: Any, /) -> TypeIs[typing_extensions.TypeAliasType]: + """Return whether the provided argument is an instance of `TypeAliasType`. + + ```python + type Int = int + is_type_alias_type(Int) + # > True + Str = TypeAliasType("Str", str) + is_type_alias_type(Str) + # > True + ``` + """ + return isinstance(tp, _TYPE_ALIAS_TYPES) + + +# Extracts T from Annotated[T, ...] or from Required[Annotated[T, ...]] +@lru_cache(maxsize=8096) +def strip_annotated_type(typ: type) -> type: + if is_required_type(typ) or is_annotated_type(typ): + return strip_annotated_type(cast(type, get_args(typ)[0])) + + return typ + + +def extract_type_arg(typ: type, index: int) -> type: + args = get_args(typ) + try: + return cast(type, args[index]) + except IndexError as err: + raise RuntimeError(f"Expected type {typ} to have a type argument at index {index} but it did not") from err + + +def extract_type_var_from_base( + typ: type, + *, + generic_bases: tuple[type, ...], + index: int, + failure_message: str | None = None, +) -> type: + """Given a type like `Foo[T]`, returns the generic type variable `T`. + + This also handles the case where a concrete subclass is given, e.g. + ```py + class MyResponse(Foo[bytes]): + ... + + extract_type_var(MyResponse, bases=(Foo,), index=0) -> bytes + ``` + + And where a generic subclass is given: + ```py + _T = TypeVar('_T') + class MyResponse(Foo[_T]): + ... + + extract_type_var(MyResponse[bytes], bases=(Foo,), index=0) -> bytes + ``` + """ + cls = cast(object, get_origin(typ) or typ) + if cls in generic_bases: # pyright: ignore[reportUnnecessaryContains] + # we're given the class directly + return extract_type_arg(typ, index) + + # if a subclass is given + # --- + # this is needed as __orig_bases__ is not present in the typeshed stubs + # because it is intended to be for internal use only, however there does + # not seem to be a way to resolve generic TypeVars for inherited subclasses + # without using it. + if isinstance(cls, InheritsGeneric): + target_base_class: Any | None = None + for base in cls.__orig_bases__: + if base.__origin__ in generic_bases: + target_base_class = base + break + + if target_base_class is None: + raise RuntimeError( + "Could not find the generic base class;\n" + "This should never happen;\n" + f"Does {cls} inherit from one of {generic_bases} ?" + ) + + extracted = extract_type_arg(target_base_class, index) + if is_typevar(extracted): + # If the extracted type argument is itself a type variable + # then that means the subclass itself is generic, so we have + # to resolve the type argument from the class itself, not + # the base class. + # + # Note: if there is more than 1 type argument, the subclass could + # change the ordering of the type arguments, this is not currently + # supported. + return extract_type_arg(typ, index) + + return extracted + + raise RuntimeError(failure_message or f"Could not resolve inner type variable at index {index} for {typ}") diff --git a/src/unlayer/_utils/_utils.py b/src/unlayer/_utils/_utils.py new file mode 100644 index 0000000..eec7f4a --- /dev/null +++ b/src/unlayer/_utils/_utils.py @@ -0,0 +1,421 @@ +from __future__ import annotations + +import os +import re +import inspect +import functools +from typing import ( + Any, + Tuple, + Mapping, + TypeVar, + Callable, + Iterable, + Sequence, + cast, + overload, +) +from pathlib import Path +from datetime import date, datetime +from typing_extensions import TypeGuard + +import sniffio + +from .._types import Omit, NotGiven, FileTypes, HeadersLike + +_T = TypeVar("_T") +_TupleT = TypeVar("_TupleT", bound=Tuple[object, ...]) +_MappingT = TypeVar("_MappingT", bound=Mapping[str, object]) +_SequenceT = TypeVar("_SequenceT", bound=Sequence[object]) +CallableT = TypeVar("CallableT", bound=Callable[..., Any]) + + +def flatten(t: Iterable[Iterable[_T]]) -> list[_T]: + return [item for sublist in t for item in sublist] + + +def extract_files( + # TODO: this needs to take Dict but variance issues..... + # create protocol type ? + query: Mapping[str, object], + *, + paths: Sequence[Sequence[str]], +) -> list[tuple[str, FileTypes]]: + """Recursively extract files from the given dictionary based on specified paths. + + A path may look like this ['foo', 'files', '', 'data']. + + Note: this mutates the given dictionary. + """ + files: list[tuple[str, FileTypes]] = [] + for path in paths: + files.extend(_extract_items(query, path, index=0, flattened_key=None)) + return files + + +def _extract_items( + obj: object, + path: Sequence[str], + *, + index: int, + flattened_key: str | None, +) -> list[tuple[str, FileTypes]]: + try: + key = path[index] + except IndexError: + if not is_given(obj): + # no value was provided - we can safely ignore + return [] + + # cyclical import + from .._files import assert_is_file_content + + # We have exhausted the path, return the entry we found. + assert flattened_key is not None + + if is_list(obj): + files: list[tuple[str, FileTypes]] = [] + for entry in obj: + assert_is_file_content(entry, key=flattened_key + "[]" if flattened_key else "") + files.append((flattened_key + "[]", cast(FileTypes, entry))) + return files + + assert_is_file_content(obj, key=flattened_key) + return [(flattened_key, cast(FileTypes, obj))] + + index += 1 + if is_dict(obj): + try: + # We are at the last entry in the path so we must remove the field + if (len(path)) == index: + item = obj.pop(key) + else: + item = obj[key] + except KeyError: + # Key was not present in the dictionary, this is not indicative of an error + # as the given path may not point to a required field. We also do not want + # to enforce required fields as the API may differ from the spec in some cases. + return [] + if flattened_key is None: + flattened_key = key + else: + flattened_key += f"[{key}]" + return _extract_items( + item, + path, + index=index, + flattened_key=flattened_key, + ) + elif is_list(obj): + if key != "": + return [] + + return flatten( + [ + _extract_items( + item, + path, + index=index, + flattened_key=flattened_key + "[]" if flattened_key is not None else "[]", + ) + for item in obj + ] + ) + + # Something unexpected was passed, just ignore it. + return [] + + +def is_given(obj: _T | NotGiven | Omit) -> TypeGuard[_T]: + return not isinstance(obj, NotGiven) and not isinstance(obj, Omit) + + +# Type safe methods for narrowing types with TypeVars. +# The default narrowing for isinstance(obj, dict) is dict[unknown, unknown], +# however this cause Pyright to rightfully report errors. As we know we don't +# care about the contained types we can safely use `object` in its place. +# +# There are two separate functions defined, `is_*` and `is_*_t` for different use cases. +# `is_*` is for when you're dealing with an unknown input +# `is_*_t` is for when you're narrowing a known union type to a specific subset + + +def is_tuple(obj: object) -> TypeGuard[tuple[object, ...]]: + return isinstance(obj, tuple) + + +def is_tuple_t(obj: _TupleT | object) -> TypeGuard[_TupleT]: + return isinstance(obj, tuple) + + +def is_sequence(obj: object) -> TypeGuard[Sequence[object]]: + return isinstance(obj, Sequence) + + +def is_sequence_t(obj: _SequenceT | object) -> TypeGuard[_SequenceT]: + return isinstance(obj, Sequence) + + +def is_mapping(obj: object) -> TypeGuard[Mapping[str, object]]: + return isinstance(obj, Mapping) + + +def is_mapping_t(obj: _MappingT | object) -> TypeGuard[_MappingT]: + return isinstance(obj, Mapping) + + +def is_dict(obj: object) -> TypeGuard[dict[object, object]]: + return isinstance(obj, dict) + + +def is_list(obj: object) -> TypeGuard[list[object]]: + return isinstance(obj, list) + + +def is_iterable(obj: object) -> TypeGuard[Iterable[object]]: + return isinstance(obj, Iterable) + + +def deepcopy_minimal(item: _T) -> _T: + """Minimal reimplementation of copy.deepcopy() that will only copy certain object types: + + - mappings, e.g. `dict` + - list + + This is done for performance reasons. + """ + if is_mapping(item): + return cast(_T, {k: deepcopy_minimal(v) for k, v in item.items()}) + if is_list(item): + return cast(_T, [deepcopy_minimal(entry) for entry in item]) + return item + + +# copied from https://github.com/Rapptz/RoboDanny +def human_join(seq: Sequence[str], *, delim: str = ", ", final: str = "or") -> str: + size = len(seq) + if size == 0: + return "" + + if size == 1: + return seq[0] + + if size == 2: + return f"{seq[0]} {final} {seq[1]}" + + return delim.join(seq[:-1]) + f" {final} {seq[-1]}" + + +def quote(string: str) -> str: + """Add single quotation marks around the given string. Does *not* do any escaping.""" + return f"'{string}'" + + +def required_args(*variants: Sequence[str]) -> Callable[[CallableT], CallableT]: + """Decorator to enforce a given set of arguments or variants of arguments are passed to the decorated function. + + Useful for enforcing runtime validation of overloaded functions. + + Example usage: + ```py + @overload + def foo(*, a: str) -> str: ... + + + @overload + def foo(*, b: bool) -> str: ... + + + # This enforces the same constraints that a static type checker would + # i.e. that either a or b must be passed to the function + @required_args(["a"], ["b"]) + def foo(*, a: str | None = None, b: bool | None = None) -> str: ... + ``` + """ + + def inner(func: CallableT) -> CallableT: + params = inspect.signature(func).parameters + positional = [ + name + for name, param in params.items() + if param.kind + in { + param.POSITIONAL_ONLY, + param.POSITIONAL_OR_KEYWORD, + } + ] + + @functools.wraps(func) + def wrapper(*args: object, **kwargs: object) -> object: + given_params: set[str] = set() + for i, _ in enumerate(args): + try: + given_params.add(positional[i]) + except IndexError: + raise TypeError( + f"{func.__name__}() takes {len(positional)} argument(s) but {len(args)} were given" + ) from None + + for key in kwargs.keys(): + given_params.add(key) + + for variant in variants: + matches = all((param in given_params for param in variant)) + if matches: + break + else: # no break + if len(variants) > 1: + variations = human_join( + ["(" + human_join([quote(arg) for arg in variant], final="and") + ")" for variant in variants] + ) + msg = f"Missing required arguments; Expected either {variations} arguments to be given" + else: + assert len(variants) > 0 + + # TODO: this error message is not deterministic + missing = list(set(variants[0]) - given_params) + if len(missing) > 1: + msg = f"Missing required arguments: {human_join([quote(arg) for arg in missing])}" + else: + msg = f"Missing required argument: {quote(missing[0])}" + raise TypeError(msg) + return func(*args, **kwargs) + + return wrapper # type: ignore + + return inner + + +_K = TypeVar("_K") +_V = TypeVar("_V") + + +@overload +def strip_not_given(obj: None) -> None: ... + + +@overload +def strip_not_given(obj: Mapping[_K, _V | NotGiven]) -> dict[_K, _V]: ... + + +@overload +def strip_not_given(obj: object) -> object: ... + + +def strip_not_given(obj: object | None) -> object: + """Remove all top-level keys where their values are instances of `NotGiven`""" + if obj is None: + return None + + if not is_mapping(obj): + return obj + + return {key: value for key, value in obj.items() if not isinstance(value, NotGiven)} + + +def coerce_integer(val: str) -> int: + return int(val, base=10) + + +def coerce_float(val: str) -> float: + return float(val) + + +def coerce_boolean(val: str) -> bool: + return val == "true" or val == "1" or val == "on" + + +def maybe_coerce_integer(val: str | None) -> int | None: + if val is None: + return None + return coerce_integer(val) + + +def maybe_coerce_float(val: str | None) -> float | None: + if val is None: + return None + return coerce_float(val) + + +def maybe_coerce_boolean(val: str | None) -> bool | None: + if val is None: + return None + return coerce_boolean(val) + + +def removeprefix(string: str, prefix: str) -> str: + """Remove a prefix from a string. + + Backport of `str.removeprefix` for Python < 3.9 + """ + if string.startswith(prefix): + return string[len(prefix) :] + return string + + +def removesuffix(string: str, suffix: str) -> str: + """Remove a suffix from a string. + + Backport of `str.removesuffix` for Python < 3.9 + """ + if string.endswith(suffix): + return string[: -len(suffix)] + return string + + +def file_from_path(path: str) -> FileTypes: + contents = Path(path).read_bytes() + file_name = os.path.basename(path) + return (file_name, contents) + + +def get_required_header(headers: HeadersLike, header: str) -> str: + lower_header = header.lower() + if is_mapping_t(headers): + # mypy doesn't understand the type narrowing here + for k, v in headers.items(): # type: ignore + if k.lower() == lower_header and isinstance(v, str): + return v + + # to deal with the case where the header looks like Stainless-Event-Id + intercaps_header = re.sub(r"([^\w])(\w)", lambda pat: pat.group(1) + pat.group(2).upper(), header.capitalize()) + + for normalized_header in [header, lower_header, header.upper(), intercaps_header]: + value = headers.get(normalized_header) + if value: + return value + + raise ValueError(f"Could not find {header} header") + + +def get_async_library() -> str: + try: + return sniffio.current_async_library() + except Exception: + return "false" + + +def lru_cache(*, maxsize: int | None = 128) -> Callable[[CallableT], CallableT]: + """A version of functools.lru_cache that retains the type signature + for the wrapped function arguments. + """ + wrapper = functools.lru_cache( # noqa: TID251 + maxsize=maxsize, + ) + return cast(Any, wrapper) # type: ignore[no-any-return] + + +def json_safe(data: object) -> object: + """Translates a mapping / sequence recursively in the same fashion + as `pydantic` v2's `model_dump(mode="json")`. + """ + if is_mapping(data): + return {json_safe(key): json_safe(value) for key, value in data.items()} + + if is_iterable(data) and not isinstance(data, (str, bytes, bytearray)): + return [json_safe(item) for item in data] + + if isinstance(data, (datetime, date)): + return data.isoformat() + + return data diff --git a/src/unlayer/_version.py b/src/unlayer/_version.py new file mode 100644 index 0000000..c9e1c88 --- /dev/null +++ b/src/unlayer/_version.py @@ -0,0 +1,4 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +__title__ = "unlayer" +__version__ = "0.0.1" diff --git a/src/unlayer/lib/.keep b/src/unlayer/lib/.keep new file mode 100644 index 0000000..5e2c99f --- /dev/null +++ b/src/unlayer/lib/.keep @@ -0,0 +1,4 @@ +File generated from our OpenAPI spec by Stainless. + +This directory can be used to store custom files to expand the SDK. +It is ignored by Stainless code generation and its content (other than this keep file) won't be touched. \ No newline at end of file diff --git a/src/unlayer/py.typed b/src/unlayer/py.typed new file mode 100644 index 0000000..e69de29 diff --git a/src/unlayer/resources/__init__.py b/src/unlayer/resources/__init__.py new file mode 100644 index 0000000..8ddd0c6 --- /dev/null +++ b/src/unlayer/resources/__init__.py @@ -0,0 +1,117 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from .pages import ( + PagesResource, + AsyncPagesResource, + PagesResourceWithRawResponse, + AsyncPagesResourceWithRawResponse, + PagesResourceWithStreamingResponse, + AsyncPagesResourceWithStreamingResponse, +) +from .emails import ( + EmailsResource, + AsyncEmailsResource, + EmailsResourceWithRawResponse, + AsyncEmailsResourceWithRawResponse, + EmailsResourceWithStreamingResponse, + AsyncEmailsResourceWithStreamingResponse, +) +from .project import ( + ProjectResource, + AsyncProjectResource, + ProjectResourceWithRawResponse, + AsyncProjectResourceWithRawResponse, + ProjectResourceWithStreamingResponse, + AsyncProjectResourceWithStreamingResponse, +) +from .pages_v1 import ( + PagesV1Resource, + AsyncPagesV1Resource, + PagesV1ResourceWithRawResponse, + AsyncPagesV1ResourceWithRawResponse, + PagesV1ResourceWithStreamingResponse, + AsyncPagesV1ResourceWithStreamingResponse, +) +from .documents import ( + DocumentsResource, + AsyncDocumentsResource, + DocumentsResourceWithRawResponse, + AsyncDocumentsResourceWithRawResponse, + DocumentsResourceWithStreamingResponse, + AsyncDocumentsResourceWithStreamingResponse, +) +from .emails_v1 import ( + EmailsV1Resource, + AsyncEmailsV1Resource, + EmailsV1ResourceWithRawResponse, + AsyncEmailsV1ResourceWithRawResponse, + EmailsV1ResourceWithStreamingResponse, + AsyncEmailsV1ResourceWithStreamingResponse, +) +from .project_v1 import ( + ProjectV1Resource, + AsyncProjectV1Resource, + ProjectV1ResourceWithRawResponse, + AsyncProjectV1ResourceWithRawResponse, + ProjectV1ResourceWithStreamingResponse, + AsyncProjectV1ResourceWithStreamingResponse, +) +from .documents_v1 import ( + DocumentsV1Resource, + AsyncDocumentsV1Resource, + DocumentsV1ResourceWithRawResponse, + AsyncDocumentsV1ResourceWithRawResponse, + DocumentsV1ResourceWithStreamingResponse, + AsyncDocumentsV1ResourceWithStreamingResponse, +) + +__all__ = [ + "ProjectV1Resource", + "AsyncProjectV1Resource", + "ProjectV1ResourceWithRawResponse", + "AsyncProjectV1ResourceWithRawResponse", + "ProjectV1ResourceWithStreamingResponse", + "AsyncProjectV1ResourceWithStreamingResponse", + "ProjectResource", + "AsyncProjectResource", + "ProjectResourceWithRawResponse", + "AsyncProjectResourceWithRawResponse", + "ProjectResourceWithStreamingResponse", + "AsyncProjectResourceWithStreamingResponse", + "EmailsV1Resource", + "AsyncEmailsV1Resource", + "EmailsV1ResourceWithRawResponse", + "AsyncEmailsV1ResourceWithRawResponse", + "EmailsV1ResourceWithStreamingResponse", + "AsyncEmailsV1ResourceWithStreamingResponse", + "EmailsResource", + "AsyncEmailsResource", + "EmailsResourceWithRawResponse", + "AsyncEmailsResourceWithRawResponse", + "EmailsResourceWithStreamingResponse", + "AsyncEmailsResourceWithStreamingResponse", + "DocumentsV1Resource", + "AsyncDocumentsV1Resource", + "DocumentsV1ResourceWithRawResponse", + "AsyncDocumentsV1ResourceWithRawResponse", + "DocumentsV1ResourceWithStreamingResponse", + "AsyncDocumentsV1ResourceWithStreamingResponse", + "DocumentsResource", + "AsyncDocumentsResource", + "DocumentsResourceWithRawResponse", + "AsyncDocumentsResourceWithRawResponse", + "DocumentsResourceWithStreamingResponse", + "AsyncDocumentsResourceWithStreamingResponse", + "PagesV1Resource", + "AsyncPagesV1Resource", + "PagesV1ResourceWithRawResponse", + "AsyncPagesV1ResourceWithRawResponse", + "PagesV1ResourceWithStreamingResponse", + "AsyncPagesV1ResourceWithStreamingResponse", + "PagesResource", + "AsyncPagesResource", + "PagesResourceWithRawResponse", + "AsyncPagesResourceWithRawResponse", + "PagesResourceWithStreamingResponse", + "AsyncPagesResourceWithStreamingResponse", +] diff --git a/src/unlayer/resources/documents.py b/src/unlayer/resources/documents.py new file mode 100644 index 0000000..803d2f9 --- /dev/null +++ b/src/unlayer/resources/documents.py @@ -0,0 +1,397 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Dict + +import httpx + +from ..types import document_generate_create_params, document_generate_template_template_params +from .._types import Body, Omit, Query, Headers, NotGiven, omit, not_given +from .._utils import maybe_transform, async_maybe_transform +from .._compat import cached_property +from .._resource import SyncAPIResource, AsyncAPIResource +from .._response import ( + to_raw_response_wrapper, + to_streamed_response_wrapper, + async_to_raw_response_wrapper, + async_to_streamed_response_wrapper, +) +from .._base_client import make_request_options +from ..types.document_generate_create_response import DocumentGenerateCreateResponse +from ..types.document_documents_retrieve_response import DocumentDocumentsRetrieveResponse +from ..types.document_generate_template_template_response import DocumentGenerateTemplateTemplateResponse + +__all__ = ["DocumentsResource", "AsyncDocumentsResource"] + + +class DocumentsResource(SyncAPIResource): + @cached_property + def with_raw_response(self) -> DocumentsResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/stainless-sdks/unlayer-python#accessing-raw-response-data-eg-headers + """ + return DocumentsResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> DocumentsResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/stainless-sdks/unlayer-python#with_streaming_response + """ + return DocumentsResourceWithStreamingResponse(self) + + def documents_retrieve( + self, + id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> DocumentDocumentsRetrieveResponse: + """ + Retrieve details of a previously generated document. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not id: + raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") + return self._get( + f"/documents/v1/documents/{id}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=DocumentDocumentsRetrieveResponse, + ) + + def generate_create( + self, + *, + design: Dict[str, object], + filename: str | Omit = omit, + html: str | Omit = omit, + merge_tags: Dict[str, str] | Omit = omit, + url: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> DocumentGenerateCreateResponse: + """ + Generate PDF document from JSON design, HTML content, or URL. + + Args: + design: Proprietary design format JSON + + filename: Optional filename for the generated PDF + + html: HTML content to convert to PDF + + merge_tags: Optional merge tags for personalization + + url: URL to convert to PDF + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._post( + "/documents/v1/generate", + body=maybe_transform( + { + "design": design, + "filename": filename, + "html": html, + "merge_tags": merge_tags, + "url": url, + }, + document_generate_create_params.DocumentGenerateCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=DocumentGenerateCreateResponse, + ) + + def generate_template_template( + self, + *, + template_id: str, + filename: str | Omit = omit, + merge_tags: Dict[str, str] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> DocumentGenerateTemplateTemplateResponse: + """ + Generate PDF document from an existing template with merge tags. + + Args: + template_id: ID of the template to use for generation + + filename: Optional filename for the generated PDF + + merge_tags: Optional merge tags for personalization + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._post( + "/documents/v1/generate/template", + body=maybe_transform( + { + "template_id": template_id, + "filename": filename, + "merge_tags": merge_tags, + }, + document_generate_template_template_params.DocumentGenerateTemplateTemplateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=DocumentGenerateTemplateTemplateResponse, + ) + + +class AsyncDocumentsResource(AsyncAPIResource): + @cached_property + def with_raw_response(self) -> AsyncDocumentsResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/stainless-sdks/unlayer-python#accessing-raw-response-data-eg-headers + """ + return AsyncDocumentsResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncDocumentsResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/stainless-sdks/unlayer-python#with_streaming_response + """ + return AsyncDocumentsResourceWithStreamingResponse(self) + + async def documents_retrieve( + self, + id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> DocumentDocumentsRetrieveResponse: + """ + Retrieve details of a previously generated document. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not id: + raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") + return await self._get( + f"/documents/v1/documents/{id}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=DocumentDocumentsRetrieveResponse, + ) + + async def generate_create( + self, + *, + design: Dict[str, object], + filename: str | Omit = omit, + html: str | Omit = omit, + merge_tags: Dict[str, str] | Omit = omit, + url: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> DocumentGenerateCreateResponse: + """ + Generate PDF document from JSON design, HTML content, or URL. + + Args: + design: Proprietary design format JSON + + filename: Optional filename for the generated PDF + + html: HTML content to convert to PDF + + merge_tags: Optional merge tags for personalization + + url: URL to convert to PDF + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return await self._post( + "/documents/v1/generate", + body=await async_maybe_transform( + { + "design": design, + "filename": filename, + "html": html, + "merge_tags": merge_tags, + "url": url, + }, + document_generate_create_params.DocumentGenerateCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=DocumentGenerateCreateResponse, + ) + + async def generate_template_template( + self, + *, + template_id: str, + filename: str | Omit = omit, + merge_tags: Dict[str, str] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> DocumentGenerateTemplateTemplateResponse: + """ + Generate PDF document from an existing template with merge tags. + + Args: + template_id: ID of the template to use for generation + + filename: Optional filename for the generated PDF + + merge_tags: Optional merge tags for personalization + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return await self._post( + "/documents/v1/generate/template", + body=await async_maybe_transform( + { + "template_id": template_id, + "filename": filename, + "merge_tags": merge_tags, + }, + document_generate_template_template_params.DocumentGenerateTemplateTemplateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=DocumentGenerateTemplateTemplateResponse, + ) + + +class DocumentsResourceWithRawResponse: + def __init__(self, documents: DocumentsResource) -> None: + self._documents = documents + + self.documents_retrieve = to_raw_response_wrapper( + documents.documents_retrieve, + ) + self.generate_create = to_raw_response_wrapper( + documents.generate_create, + ) + self.generate_template_template = to_raw_response_wrapper( + documents.generate_template_template, + ) + + +class AsyncDocumentsResourceWithRawResponse: + def __init__(self, documents: AsyncDocumentsResource) -> None: + self._documents = documents + + self.documents_retrieve = async_to_raw_response_wrapper( + documents.documents_retrieve, + ) + self.generate_create = async_to_raw_response_wrapper( + documents.generate_create, + ) + self.generate_template_template = async_to_raw_response_wrapper( + documents.generate_template_template, + ) + + +class DocumentsResourceWithStreamingResponse: + def __init__(self, documents: DocumentsResource) -> None: + self._documents = documents + + self.documents_retrieve = to_streamed_response_wrapper( + documents.documents_retrieve, + ) + self.generate_create = to_streamed_response_wrapper( + documents.generate_create, + ) + self.generate_template_template = to_streamed_response_wrapper( + documents.generate_template_template, + ) + + +class AsyncDocumentsResourceWithStreamingResponse: + def __init__(self, documents: AsyncDocumentsResource) -> None: + self._documents = documents + + self.documents_retrieve = async_to_streamed_response_wrapper( + documents.documents_retrieve, + ) + self.generate_create = async_to_streamed_response_wrapper( + documents.generate_create, + ) + self.generate_template_template = async_to_streamed_response_wrapper( + documents.generate_template_template, + ) diff --git a/src/unlayer/resources/documents_v1.py b/src/unlayer/resources/documents_v1.py new file mode 100644 index 0000000..1a2d68e --- /dev/null +++ b/src/unlayer/resources/documents_v1.py @@ -0,0 +1,397 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Dict + +import httpx + +from ..types import documents_v1_generate_create_params, documents_v1_generate_template_template_params +from .._types import Body, Omit, Query, Headers, NotGiven, omit, not_given +from .._utils import maybe_transform, async_maybe_transform +from .._compat import cached_property +from .._resource import SyncAPIResource, AsyncAPIResource +from .._response import ( + to_raw_response_wrapper, + to_streamed_response_wrapper, + async_to_raw_response_wrapper, + async_to_streamed_response_wrapper, +) +from .._base_client import make_request_options +from ..types.documents_v1_generate_create_response import DocumentsV1GenerateCreateResponse +from ..types.documents_v1_documents_retrieve_response import DocumentsV1DocumentsRetrieveResponse +from ..types.documents_v1_generate_template_template_response import DocumentsV1GenerateTemplateTemplateResponse + +__all__ = ["DocumentsV1Resource", "AsyncDocumentsV1Resource"] + + +class DocumentsV1Resource(SyncAPIResource): + @cached_property + def with_raw_response(self) -> DocumentsV1ResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/stainless-sdks/unlayer-python#accessing-raw-response-data-eg-headers + """ + return DocumentsV1ResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> DocumentsV1ResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/stainless-sdks/unlayer-python#with_streaming_response + """ + return DocumentsV1ResourceWithStreamingResponse(self) + + def documents_retrieve( + self, + id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> DocumentsV1DocumentsRetrieveResponse: + """ + Retrieve details of a previously generated document. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not id: + raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") + return self._get( + f"/documents/v1/documents/{id}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=DocumentsV1DocumentsRetrieveResponse, + ) + + def generate_create( + self, + *, + design: Dict[str, object], + filename: str | Omit = omit, + html: str | Omit = omit, + merge_tags: Dict[str, str] | Omit = omit, + url: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> DocumentsV1GenerateCreateResponse: + """ + Generate PDF document from JSON design, HTML content, or URL. + + Args: + design: Proprietary design format JSON + + filename: Optional filename for the generated PDF + + html: HTML content to convert to PDF + + merge_tags: Optional merge tags for personalization + + url: URL to convert to PDF + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._post( + "/documents/v1/generate", + body=maybe_transform( + { + "design": design, + "filename": filename, + "html": html, + "merge_tags": merge_tags, + "url": url, + }, + documents_v1_generate_create_params.DocumentsV1GenerateCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=DocumentsV1GenerateCreateResponse, + ) + + def generate_template_template( + self, + *, + template_id: str, + filename: str | Omit = omit, + merge_tags: Dict[str, str] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> DocumentsV1GenerateTemplateTemplateResponse: + """ + Generate PDF document from an existing template with merge tags. + + Args: + template_id: ID of the template to use for generation + + filename: Optional filename for the generated PDF + + merge_tags: Optional merge tags for personalization + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._post( + "/documents/v1/generate/template", + body=maybe_transform( + { + "template_id": template_id, + "filename": filename, + "merge_tags": merge_tags, + }, + documents_v1_generate_template_template_params.DocumentsV1GenerateTemplateTemplateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=DocumentsV1GenerateTemplateTemplateResponse, + ) + + +class AsyncDocumentsV1Resource(AsyncAPIResource): + @cached_property + def with_raw_response(self) -> AsyncDocumentsV1ResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/stainless-sdks/unlayer-python#accessing-raw-response-data-eg-headers + """ + return AsyncDocumentsV1ResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncDocumentsV1ResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/stainless-sdks/unlayer-python#with_streaming_response + """ + return AsyncDocumentsV1ResourceWithStreamingResponse(self) + + async def documents_retrieve( + self, + id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> DocumentsV1DocumentsRetrieveResponse: + """ + Retrieve details of a previously generated document. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not id: + raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") + return await self._get( + f"/documents/v1/documents/{id}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=DocumentsV1DocumentsRetrieveResponse, + ) + + async def generate_create( + self, + *, + design: Dict[str, object], + filename: str | Omit = omit, + html: str | Omit = omit, + merge_tags: Dict[str, str] | Omit = omit, + url: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> DocumentsV1GenerateCreateResponse: + """ + Generate PDF document from JSON design, HTML content, or URL. + + Args: + design: Proprietary design format JSON + + filename: Optional filename for the generated PDF + + html: HTML content to convert to PDF + + merge_tags: Optional merge tags for personalization + + url: URL to convert to PDF + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return await self._post( + "/documents/v1/generate", + body=await async_maybe_transform( + { + "design": design, + "filename": filename, + "html": html, + "merge_tags": merge_tags, + "url": url, + }, + documents_v1_generate_create_params.DocumentsV1GenerateCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=DocumentsV1GenerateCreateResponse, + ) + + async def generate_template_template( + self, + *, + template_id: str, + filename: str | Omit = omit, + merge_tags: Dict[str, str] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> DocumentsV1GenerateTemplateTemplateResponse: + """ + Generate PDF document from an existing template with merge tags. + + Args: + template_id: ID of the template to use for generation + + filename: Optional filename for the generated PDF + + merge_tags: Optional merge tags for personalization + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return await self._post( + "/documents/v1/generate/template", + body=await async_maybe_transform( + { + "template_id": template_id, + "filename": filename, + "merge_tags": merge_tags, + }, + documents_v1_generate_template_template_params.DocumentsV1GenerateTemplateTemplateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=DocumentsV1GenerateTemplateTemplateResponse, + ) + + +class DocumentsV1ResourceWithRawResponse: + def __init__(self, documents_v1: DocumentsV1Resource) -> None: + self._documents_v1 = documents_v1 + + self.documents_retrieve = to_raw_response_wrapper( + documents_v1.documents_retrieve, + ) + self.generate_create = to_raw_response_wrapper( + documents_v1.generate_create, + ) + self.generate_template_template = to_raw_response_wrapper( + documents_v1.generate_template_template, + ) + + +class AsyncDocumentsV1ResourceWithRawResponse: + def __init__(self, documents_v1: AsyncDocumentsV1Resource) -> None: + self._documents_v1 = documents_v1 + + self.documents_retrieve = async_to_raw_response_wrapper( + documents_v1.documents_retrieve, + ) + self.generate_create = async_to_raw_response_wrapper( + documents_v1.generate_create, + ) + self.generate_template_template = async_to_raw_response_wrapper( + documents_v1.generate_template_template, + ) + + +class DocumentsV1ResourceWithStreamingResponse: + def __init__(self, documents_v1: DocumentsV1Resource) -> None: + self._documents_v1 = documents_v1 + + self.documents_retrieve = to_streamed_response_wrapper( + documents_v1.documents_retrieve, + ) + self.generate_create = to_streamed_response_wrapper( + documents_v1.generate_create, + ) + self.generate_template_template = to_streamed_response_wrapper( + documents_v1.generate_template_template, + ) + + +class AsyncDocumentsV1ResourceWithStreamingResponse: + def __init__(self, documents_v1: AsyncDocumentsV1Resource) -> None: + self._documents_v1 = documents_v1 + + self.documents_retrieve = async_to_streamed_response_wrapper( + documents_v1.documents_retrieve, + ) + self.generate_create = async_to_streamed_response_wrapper( + documents_v1.generate_create, + ) + self.generate_template_template = async_to_streamed_response_wrapper( + documents_v1.generate_template_template, + ) diff --git a/src/unlayer/resources/emails.py b/src/unlayer/resources/emails.py new file mode 100644 index 0000000..1f2ca5c --- /dev/null +++ b/src/unlayer/resources/emails.py @@ -0,0 +1,504 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Dict + +import httpx + +from ..types import email_send_create_params, email_render_create_params, email_send_template_template_params +from .._types import Body, Omit, Query, Headers, NotGiven, omit, not_given +from .._utils import maybe_transform, async_maybe_transform +from .._compat import cached_property +from .._resource import SyncAPIResource, AsyncAPIResource +from .._response import ( + to_raw_response_wrapper, + to_streamed_response_wrapper, + async_to_raw_response_wrapper, + async_to_streamed_response_wrapper, +) +from .._base_client import make_request_options +from ..types.email_send_create_response import EmailSendCreateResponse +from ..types.email_render_create_response import EmailRenderCreateResponse +from ..types.email_emails_retrieve_response import EmailEmailsRetrieveResponse +from ..types.email_send_template_template_response import EmailSendTemplateTemplateResponse + +__all__ = ["EmailsResource", "AsyncEmailsResource"] + + +class EmailsResource(SyncAPIResource): + @cached_property + def with_raw_response(self) -> EmailsResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/stainless-sdks/unlayer-python#accessing-raw-response-data-eg-headers + """ + return EmailsResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> EmailsResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/stainless-sdks/unlayer-python#with_streaming_response + """ + return EmailsResourceWithStreamingResponse(self) + + def emails_retrieve( + self, + id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> EmailEmailsRetrieveResponse: + """ + Retrieve details of a previously sent email. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not id: + raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") + return self._get( + f"/emails/v1/emails/{id}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=EmailEmailsRetrieveResponse, + ) + + def render_create( + self, + *, + design: Dict[str, object], + merge_tags: Dict[str, str] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> EmailRenderCreateResponse: + """ + Convert design JSON to HTML with optional merge tags. + + Args: + design: Proprietary design format JSON + + merge_tags: Optional merge tags for personalization + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._post( + "/emails/v1/render", + body=maybe_transform( + { + "design": design, + "merge_tags": merge_tags, + }, + email_render_create_params.EmailRenderCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=EmailRenderCreateResponse, + ) + + def send_create( + self, + *, + design: Dict[str, object], + to: str, + html: str | Omit = omit, + merge_tags: Dict[str, str] | Omit = omit, + subject: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> EmailSendCreateResponse: + """ + Send email with design JSON or HTML content. + + Args: + design: Proprietary design format JSON + + to: Recipient email address + + html: HTML content to send + + merge_tags: Optional merge tags for personalization + + subject: Email subject line + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._post( + "/emails/v1/send", + body=maybe_transform( + { + "design": design, + "to": to, + "html": html, + "merge_tags": merge_tags, + "subject": subject, + }, + email_send_create_params.EmailSendCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=EmailSendCreateResponse, + ) + + def send_template_template( + self, + *, + template_id: str, + to: str, + merge_tags: Dict[str, str] | Omit = omit, + subject: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> EmailSendTemplateTemplateResponse: + """ + Send email using an existing template with merge tags. + + Args: + template_id: ID of the template to use + + to: Recipient email address + + merge_tags: Optional merge tags for personalization + + subject: Email subject line (optional, uses template default if not provided) + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._post( + "/emails/v1/send/template", + body=maybe_transform( + { + "template_id": template_id, + "to": to, + "merge_tags": merge_tags, + "subject": subject, + }, + email_send_template_template_params.EmailSendTemplateTemplateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=EmailSendTemplateTemplateResponse, + ) + + +class AsyncEmailsResource(AsyncAPIResource): + @cached_property + def with_raw_response(self) -> AsyncEmailsResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/stainless-sdks/unlayer-python#accessing-raw-response-data-eg-headers + """ + return AsyncEmailsResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncEmailsResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/stainless-sdks/unlayer-python#with_streaming_response + """ + return AsyncEmailsResourceWithStreamingResponse(self) + + async def emails_retrieve( + self, + id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> EmailEmailsRetrieveResponse: + """ + Retrieve details of a previously sent email. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not id: + raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") + return await self._get( + f"/emails/v1/emails/{id}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=EmailEmailsRetrieveResponse, + ) + + async def render_create( + self, + *, + design: Dict[str, object], + merge_tags: Dict[str, str] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> EmailRenderCreateResponse: + """ + Convert design JSON to HTML with optional merge tags. + + Args: + design: Proprietary design format JSON + + merge_tags: Optional merge tags for personalization + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return await self._post( + "/emails/v1/render", + body=await async_maybe_transform( + { + "design": design, + "merge_tags": merge_tags, + }, + email_render_create_params.EmailRenderCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=EmailRenderCreateResponse, + ) + + async def send_create( + self, + *, + design: Dict[str, object], + to: str, + html: str | Omit = omit, + merge_tags: Dict[str, str] | Omit = omit, + subject: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> EmailSendCreateResponse: + """ + Send email with design JSON or HTML content. + + Args: + design: Proprietary design format JSON + + to: Recipient email address + + html: HTML content to send + + merge_tags: Optional merge tags for personalization + + subject: Email subject line + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return await self._post( + "/emails/v1/send", + body=await async_maybe_transform( + { + "design": design, + "to": to, + "html": html, + "merge_tags": merge_tags, + "subject": subject, + }, + email_send_create_params.EmailSendCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=EmailSendCreateResponse, + ) + + async def send_template_template( + self, + *, + template_id: str, + to: str, + merge_tags: Dict[str, str] | Omit = omit, + subject: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> EmailSendTemplateTemplateResponse: + """ + Send email using an existing template with merge tags. + + Args: + template_id: ID of the template to use + + to: Recipient email address + + merge_tags: Optional merge tags for personalization + + subject: Email subject line (optional, uses template default if not provided) + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return await self._post( + "/emails/v1/send/template", + body=await async_maybe_transform( + { + "template_id": template_id, + "to": to, + "merge_tags": merge_tags, + "subject": subject, + }, + email_send_template_template_params.EmailSendTemplateTemplateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=EmailSendTemplateTemplateResponse, + ) + + +class EmailsResourceWithRawResponse: + def __init__(self, emails: EmailsResource) -> None: + self._emails = emails + + self.emails_retrieve = to_raw_response_wrapper( + emails.emails_retrieve, + ) + self.render_create = to_raw_response_wrapper( + emails.render_create, + ) + self.send_create = to_raw_response_wrapper( + emails.send_create, + ) + self.send_template_template = to_raw_response_wrapper( + emails.send_template_template, + ) + + +class AsyncEmailsResourceWithRawResponse: + def __init__(self, emails: AsyncEmailsResource) -> None: + self._emails = emails + + self.emails_retrieve = async_to_raw_response_wrapper( + emails.emails_retrieve, + ) + self.render_create = async_to_raw_response_wrapper( + emails.render_create, + ) + self.send_create = async_to_raw_response_wrapper( + emails.send_create, + ) + self.send_template_template = async_to_raw_response_wrapper( + emails.send_template_template, + ) + + +class EmailsResourceWithStreamingResponse: + def __init__(self, emails: EmailsResource) -> None: + self._emails = emails + + self.emails_retrieve = to_streamed_response_wrapper( + emails.emails_retrieve, + ) + self.render_create = to_streamed_response_wrapper( + emails.render_create, + ) + self.send_create = to_streamed_response_wrapper( + emails.send_create, + ) + self.send_template_template = to_streamed_response_wrapper( + emails.send_template_template, + ) + + +class AsyncEmailsResourceWithStreamingResponse: + def __init__(self, emails: AsyncEmailsResource) -> None: + self._emails = emails + + self.emails_retrieve = async_to_streamed_response_wrapper( + emails.emails_retrieve, + ) + self.render_create = async_to_streamed_response_wrapper( + emails.render_create, + ) + self.send_create = async_to_streamed_response_wrapper( + emails.send_create, + ) + self.send_template_template = async_to_streamed_response_wrapper( + emails.send_template_template, + ) diff --git a/src/unlayer/resources/emails_v1.py b/src/unlayer/resources/emails_v1.py new file mode 100644 index 0000000..7744a72 --- /dev/null +++ b/src/unlayer/resources/emails_v1.py @@ -0,0 +1,508 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Dict + +import httpx + +from ..types import ( + emails_v1_send_create_params, + emails_v1_render_create_params, + emails_v1_send_template_template_params, +) +from .._types import Body, Omit, Query, Headers, NotGiven, omit, not_given +from .._utils import maybe_transform, async_maybe_transform +from .._compat import cached_property +from .._resource import SyncAPIResource, AsyncAPIResource +from .._response import ( + to_raw_response_wrapper, + to_streamed_response_wrapper, + async_to_raw_response_wrapper, + async_to_streamed_response_wrapper, +) +from .._base_client import make_request_options +from ..types.emails_v1_send_create_response import EmailsV1SendCreateResponse +from ..types.emails_v1_render_create_response import EmailsV1RenderCreateResponse +from ..types.emails_v1_emails_retrieve_response import EmailsV1EmailsRetrieveResponse +from ..types.emails_v1_send_template_template_response import EmailsV1SendTemplateTemplateResponse + +__all__ = ["EmailsV1Resource", "AsyncEmailsV1Resource"] + + +class EmailsV1Resource(SyncAPIResource): + @cached_property + def with_raw_response(self) -> EmailsV1ResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/stainless-sdks/unlayer-python#accessing-raw-response-data-eg-headers + """ + return EmailsV1ResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> EmailsV1ResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/stainless-sdks/unlayer-python#with_streaming_response + """ + return EmailsV1ResourceWithStreamingResponse(self) + + def emails_retrieve( + self, + id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> EmailsV1EmailsRetrieveResponse: + """ + Retrieve details of a previously sent email. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not id: + raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") + return self._get( + f"/emails/v1/emails/{id}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=EmailsV1EmailsRetrieveResponse, + ) + + def render_create( + self, + *, + design: Dict[str, object], + merge_tags: Dict[str, str] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> EmailsV1RenderCreateResponse: + """ + Convert design JSON to HTML with optional merge tags. + + Args: + design: Proprietary design format JSON + + merge_tags: Optional merge tags for personalization + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._post( + "/emails/v1/render", + body=maybe_transform( + { + "design": design, + "merge_tags": merge_tags, + }, + emails_v1_render_create_params.EmailsV1RenderCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=EmailsV1RenderCreateResponse, + ) + + def send_create( + self, + *, + design: Dict[str, object], + to: str, + html: str | Omit = omit, + merge_tags: Dict[str, str] | Omit = omit, + subject: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> EmailsV1SendCreateResponse: + """ + Send email with design JSON or HTML content. + + Args: + design: Proprietary design format JSON + + to: Recipient email address + + html: HTML content to send + + merge_tags: Optional merge tags for personalization + + subject: Email subject line + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._post( + "/emails/v1/send", + body=maybe_transform( + { + "design": design, + "to": to, + "html": html, + "merge_tags": merge_tags, + "subject": subject, + }, + emails_v1_send_create_params.EmailsV1SendCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=EmailsV1SendCreateResponse, + ) + + def send_template_template( + self, + *, + template_id: str, + to: str, + merge_tags: Dict[str, str] | Omit = omit, + subject: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> EmailsV1SendTemplateTemplateResponse: + """ + Send email using an existing template with merge tags. + + Args: + template_id: ID of the template to use + + to: Recipient email address + + merge_tags: Optional merge tags for personalization + + subject: Email subject line (optional, uses template default if not provided) + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._post( + "/emails/v1/send/template", + body=maybe_transform( + { + "template_id": template_id, + "to": to, + "merge_tags": merge_tags, + "subject": subject, + }, + emails_v1_send_template_template_params.EmailsV1SendTemplateTemplateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=EmailsV1SendTemplateTemplateResponse, + ) + + +class AsyncEmailsV1Resource(AsyncAPIResource): + @cached_property + def with_raw_response(self) -> AsyncEmailsV1ResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/stainless-sdks/unlayer-python#accessing-raw-response-data-eg-headers + """ + return AsyncEmailsV1ResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncEmailsV1ResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/stainless-sdks/unlayer-python#with_streaming_response + """ + return AsyncEmailsV1ResourceWithStreamingResponse(self) + + async def emails_retrieve( + self, + id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> EmailsV1EmailsRetrieveResponse: + """ + Retrieve details of a previously sent email. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not id: + raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") + return await self._get( + f"/emails/v1/emails/{id}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=EmailsV1EmailsRetrieveResponse, + ) + + async def render_create( + self, + *, + design: Dict[str, object], + merge_tags: Dict[str, str] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> EmailsV1RenderCreateResponse: + """ + Convert design JSON to HTML with optional merge tags. + + Args: + design: Proprietary design format JSON + + merge_tags: Optional merge tags for personalization + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return await self._post( + "/emails/v1/render", + body=await async_maybe_transform( + { + "design": design, + "merge_tags": merge_tags, + }, + emails_v1_render_create_params.EmailsV1RenderCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=EmailsV1RenderCreateResponse, + ) + + async def send_create( + self, + *, + design: Dict[str, object], + to: str, + html: str | Omit = omit, + merge_tags: Dict[str, str] | Omit = omit, + subject: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> EmailsV1SendCreateResponse: + """ + Send email with design JSON or HTML content. + + Args: + design: Proprietary design format JSON + + to: Recipient email address + + html: HTML content to send + + merge_tags: Optional merge tags for personalization + + subject: Email subject line + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return await self._post( + "/emails/v1/send", + body=await async_maybe_transform( + { + "design": design, + "to": to, + "html": html, + "merge_tags": merge_tags, + "subject": subject, + }, + emails_v1_send_create_params.EmailsV1SendCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=EmailsV1SendCreateResponse, + ) + + async def send_template_template( + self, + *, + template_id: str, + to: str, + merge_tags: Dict[str, str] | Omit = omit, + subject: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> EmailsV1SendTemplateTemplateResponse: + """ + Send email using an existing template with merge tags. + + Args: + template_id: ID of the template to use + + to: Recipient email address + + merge_tags: Optional merge tags for personalization + + subject: Email subject line (optional, uses template default if not provided) + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return await self._post( + "/emails/v1/send/template", + body=await async_maybe_transform( + { + "template_id": template_id, + "to": to, + "merge_tags": merge_tags, + "subject": subject, + }, + emails_v1_send_template_template_params.EmailsV1SendTemplateTemplateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=EmailsV1SendTemplateTemplateResponse, + ) + + +class EmailsV1ResourceWithRawResponse: + def __init__(self, emails_v1: EmailsV1Resource) -> None: + self._emails_v1 = emails_v1 + + self.emails_retrieve = to_raw_response_wrapper( + emails_v1.emails_retrieve, + ) + self.render_create = to_raw_response_wrapper( + emails_v1.render_create, + ) + self.send_create = to_raw_response_wrapper( + emails_v1.send_create, + ) + self.send_template_template = to_raw_response_wrapper( + emails_v1.send_template_template, + ) + + +class AsyncEmailsV1ResourceWithRawResponse: + def __init__(self, emails_v1: AsyncEmailsV1Resource) -> None: + self._emails_v1 = emails_v1 + + self.emails_retrieve = async_to_raw_response_wrapper( + emails_v1.emails_retrieve, + ) + self.render_create = async_to_raw_response_wrapper( + emails_v1.render_create, + ) + self.send_create = async_to_raw_response_wrapper( + emails_v1.send_create, + ) + self.send_template_template = async_to_raw_response_wrapper( + emails_v1.send_template_template, + ) + + +class EmailsV1ResourceWithStreamingResponse: + def __init__(self, emails_v1: EmailsV1Resource) -> None: + self._emails_v1 = emails_v1 + + self.emails_retrieve = to_streamed_response_wrapper( + emails_v1.emails_retrieve, + ) + self.render_create = to_streamed_response_wrapper( + emails_v1.render_create, + ) + self.send_create = to_streamed_response_wrapper( + emails_v1.send_create, + ) + self.send_template_template = to_streamed_response_wrapper( + emails_v1.send_template_template, + ) + + +class AsyncEmailsV1ResourceWithStreamingResponse: + def __init__(self, emails_v1: AsyncEmailsV1Resource) -> None: + self._emails_v1 = emails_v1 + + self.emails_retrieve = async_to_streamed_response_wrapper( + emails_v1.emails_retrieve, + ) + self.render_create = async_to_streamed_response_wrapper( + emails_v1.render_create, + ) + self.send_create = async_to_streamed_response_wrapper( + emails_v1.send_create, + ) + self.send_template_template = async_to_streamed_response_wrapper( + emails_v1.send_template_template, + ) diff --git a/src/unlayer/resources/pages.py b/src/unlayer/resources/pages.py new file mode 100644 index 0000000..158cd79 --- /dev/null +++ b/src/unlayer/resources/pages.py @@ -0,0 +1,187 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Dict + +import httpx + +from ..types import page_render_create_params +from .._types import Body, Omit, Query, Headers, NotGiven, omit, not_given +from .._utils import maybe_transform, async_maybe_transform +from .._compat import cached_property +from .._resource import SyncAPIResource, AsyncAPIResource +from .._response import ( + to_raw_response_wrapper, + to_streamed_response_wrapper, + async_to_raw_response_wrapper, + async_to_streamed_response_wrapper, +) +from .._base_client import make_request_options +from ..types.page_render_create_response import PageRenderCreateResponse + +__all__ = ["PagesResource", "AsyncPagesResource"] + + +class PagesResource(SyncAPIResource): + @cached_property + def with_raw_response(self) -> PagesResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/stainless-sdks/unlayer-python#accessing-raw-response-data-eg-headers + """ + return PagesResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> PagesResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/stainless-sdks/unlayer-python#with_streaming_response + """ + return PagesResourceWithStreamingResponse(self) + + def render_create( + self, + *, + design: Dict[str, object], + merge_tags: Dict[str, str] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> PageRenderCreateResponse: + """ + Convert page design JSON to HTML with optional merge tags. + + Args: + design: Proprietary design format JSON + + merge_tags: Optional merge tags for personalization + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._post( + "/pages/v1/render", + body=maybe_transform( + { + "design": design, + "merge_tags": merge_tags, + }, + page_render_create_params.PageRenderCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=PageRenderCreateResponse, + ) + + +class AsyncPagesResource(AsyncAPIResource): + @cached_property + def with_raw_response(self) -> AsyncPagesResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/stainless-sdks/unlayer-python#accessing-raw-response-data-eg-headers + """ + return AsyncPagesResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncPagesResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/stainless-sdks/unlayer-python#with_streaming_response + """ + return AsyncPagesResourceWithStreamingResponse(self) + + async def render_create( + self, + *, + design: Dict[str, object], + merge_tags: Dict[str, str] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> PageRenderCreateResponse: + """ + Convert page design JSON to HTML with optional merge tags. + + Args: + design: Proprietary design format JSON + + merge_tags: Optional merge tags for personalization + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return await self._post( + "/pages/v1/render", + body=await async_maybe_transform( + { + "design": design, + "merge_tags": merge_tags, + }, + page_render_create_params.PageRenderCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=PageRenderCreateResponse, + ) + + +class PagesResourceWithRawResponse: + def __init__(self, pages: PagesResource) -> None: + self._pages = pages + + self.render_create = to_raw_response_wrapper( + pages.render_create, + ) + + +class AsyncPagesResourceWithRawResponse: + def __init__(self, pages: AsyncPagesResource) -> None: + self._pages = pages + + self.render_create = async_to_raw_response_wrapper( + pages.render_create, + ) + + +class PagesResourceWithStreamingResponse: + def __init__(self, pages: PagesResource) -> None: + self._pages = pages + + self.render_create = to_streamed_response_wrapper( + pages.render_create, + ) + + +class AsyncPagesResourceWithStreamingResponse: + def __init__(self, pages: AsyncPagesResource) -> None: + self._pages = pages + + self.render_create = async_to_streamed_response_wrapper( + pages.render_create, + ) diff --git a/src/unlayer/resources/pages_v1.py b/src/unlayer/resources/pages_v1.py new file mode 100644 index 0000000..17c64ed --- /dev/null +++ b/src/unlayer/resources/pages_v1.py @@ -0,0 +1,187 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Dict + +import httpx + +from ..types import pages_v1_render_create_params +from .._types import Body, Omit, Query, Headers, NotGiven, omit, not_given +from .._utils import maybe_transform, async_maybe_transform +from .._compat import cached_property +from .._resource import SyncAPIResource, AsyncAPIResource +from .._response import ( + to_raw_response_wrapper, + to_streamed_response_wrapper, + async_to_raw_response_wrapper, + async_to_streamed_response_wrapper, +) +from .._base_client import make_request_options +from ..types.pages_v1_render_create_response import PagesV1RenderCreateResponse + +__all__ = ["PagesV1Resource", "AsyncPagesV1Resource"] + + +class PagesV1Resource(SyncAPIResource): + @cached_property + def with_raw_response(self) -> PagesV1ResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/stainless-sdks/unlayer-python#accessing-raw-response-data-eg-headers + """ + return PagesV1ResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> PagesV1ResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/stainless-sdks/unlayer-python#with_streaming_response + """ + return PagesV1ResourceWithStreamingResponse(self) + + def render_create( + self, + *, + design: Dict[str, object], + merge_tags: Dict[str, str] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> PagesV1RenderCreateResponse: + """ + Convert page design JSON to HTML with optional merge tags. + + Args: + design: Proprietary design format JSON + + merge_tags: Optional merge tags for personalization + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._post( + "/pages/v1/render", + body=maybe_transform( + { + "design": design, + "merge_tags": merge_tags, + }, + pages_v1_render_create_params.PagesV1RenderCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=PagesV1RenderCreateResponse, + ) + + +class AsyncPagesV1Resource(AsyncAPIResource): + @cached_property + def with_raw_response(self) -> AsyncPagesV1ResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/stainless-sdks/unlayer-python#accessing-raw-response-data-eg-headers + """ + return AsyncPagesV1ResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncPagesV1ResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/stainless-sdks/unlayer-python#with_streaming_response + """ + return AsyncPagesV1ResourceWithStreamingResponse(self) + + async def render_create( + self, + *, + design: Dict[str, object], + merge_tags: Dict[str, str] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> PagesV1RenderCreateResponse: + """ + Convert page design JSON to HTML with optional merge tags. + + Args: + design: Proprietary design format JSON + + merge_tags: Optional merge tags for personalization + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return await self._post( + "/pages/v1/render", + body=await async_maybe_transform( + { + "design": design, + "merge_tags": merge_tags, + }, + pages_v1_render_create_params.PagesV1RenderCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=PagesV1RenderCreateResponse, + ) + + +class PagesV1ResourceWithRawResponse: + def __init__(self, pages_v1: PagesV1Resource) -> None: + self._pages_v1 = pages_v1 + + self.render_create = to_raw_response_wrapper( + pages_v1.render_create, + ) + + +class AsyncPagesV1ResourceWithRawResponse: + def __init__(self, pages_v1: AsyncPagesV1Resource) -> None: + self._pages_v1 = pages_v1 + + self.render_create = async_to_raw_response_wrapper( + pages_v1.render_create, + ) + + +class PagesV1ResourceWithStreamingResponse: + def __init__(self, pages_v1: PagesV1Resource) -> None: + self._pages_v1 = pages_v1 + + self.render_create = to_streamed_response_wrapper( + pages_v1.render_create, + ) + + +class AsyncPagesV1ResourceWithStreamingResponse: + def __init__(self, pages_v1: AsyncPagesV1Resource) -> None: + self._pages_v1 = pages_v1 + + self.render_create = async_to_streamed_response_wrapper( + pages_v1.render_create, + ) diff --git a/src/unlayer/resources/project.py b/src/unlayer/resources/project.py new file mode 100644 index 0000000..bea717d --- /dev/null +++ b/src/unlayer/resources/project.py @@ -0,0 +1,1378 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import httpx + +from ..types import ( + project_domains_create_params, + project_domains_update_params, + project_api_keys_create_params, + project_api_keys_update_params, + project_templates_create_params, + project_templates_update_params, +) +from .._types import Body, Omit, Query, Headers, NoneType, NotGiven, SequenceNotStr, omit, not_given +from .._utils import maybe_transform, async_maybe_transform +from .._compat import cached_property +from .._resource import SyncAPIResource, AsyncAPIResource +from .._response import ( + to_raw_response_wrapper, + to_streamed_response_wrapper, + async_to_raw_response_wrapper, + async_to_streamed_response_wrapper, +) +from .._base_client import make_request_options +from ..types.project_current_list_response import ProjectCurrentListResponse +from ..types.project_domains_list_response import ProjectDomainsListResponse +from ..types.project_api_keys_list_response import ProjectAPIKeysListResponse +from ..types.project_domains_create_response import ProjectDomainsCreateResponse +from ..types.project_domains_update_response import ProjectDomainsUpdateResponse +from ..types.project_templates_list_response import ProjectTemplatesListResponse +from ..types.project_api_keys_create_response import ProjectAPIKeysCreateResponse +from ..types.project_api_keys_update_response import ProjectAPIKeysUpdateResponse +from ..types.project_domains_retrieve_response import ProjectDomainsRetrieveResponse +from ..types.project_templates_create_response import ProjectTemplatesCreateResponse +from ..types.project_templates_update_response import ProjectTemplatesUpdateResponse +from ..types.project_api_keys_retrieve_response import ProjectAPIKeysRetrieveResponse +from ..types.project_templates_retrieve_response import ProjectTemplatesRetrieveResponse + +__all__ = ["ProjectResource", "AsyncProjectResource"] + + +class ProjectResource(SyncAPIResource): + @cached_property + def with_raw_response(self) -> ProjectResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/stainless-sdks/unlayer-python#accessing-raw-response-data-eg-headers + """ + return ProjectResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> ProjectResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/stainless-sdks/unlayer-python#with_streaming_response + """ + return ProjectResourceWithStreamingResponse(self) + + def api_keys_create( + self, + *, + name: str, + domains: SequenceNotStr[str] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> ProjectAPIKeysCreateResponse: + """ + Create a new API key for the project. + + Args: + name: Name for the API key + + domains: Allowed domains for this API key + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._post( + "/project/v1/api-keys", + body=maybe_transform( + { + "name": name, + "domains": domains, + }, + project_api_keys_create_params.ProjectAPIKeysCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=ProjectAPIKeysCreateResponse, + ) + + def api_keys_delete( + self, + id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> None: + """ + Revoke API key. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not id: + raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") + extra_headers = {"Accept": "*/*", **(extra_headers or {})} + return self._delete( + f"/project/v1/api-keys/{id}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=NoneType, + ) + + def api_keys_list( + self, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> ProjectAPIKeysListResponse: + """List all API keys for the project.""" + return self._get( + "/project/v1/api-keys", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=ProjectAPIKeysListResponse, + ) + + def api_keys_retrieve( + self, + id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> ProjectAPIKeysRetrieveResponse: + """ + Get API key details by ID. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not id: + raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") + return self._get( + f"/project/v1/api-keys/{id}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=ProjectAPIKeysRetrieveResponse, + ) + + def api_keys_update( + self, + id: str, + *, + active: bool | Omit = omit, + domains: SequenceNotStr[str] | Omit = omit, + name: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> ProjectAPIKeysUpdateResponse: + """ + Update API key settings. + + Args: + active: Whether the API key is active + + domains: Updated allowed domains + + name: Updated name for the API key + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not id: + raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") + return self._put( + f"/project/v1/api-keys/{id}", + body=maybe_transform( + { + "active": active, + "domains": domains, + "name": name, + }, + project_api_keys_update_params.ProjectAPIKeysUpdateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=ProjectAPIKeysUpdateResponse, + ) + + def current_list( + self, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> ProjectCurrentListResponse: + """Get project details for the authenticated project.""" + return self._get( + "/project/v1/current", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=ProjectCurrentListResponse, + ) + + def domains_create( + self, + *, + domain: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> ProjectDomainsCreateResponse: + """ + Add a new domain to the project. + + Args: + domain: Domain name to add + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._post( + "/project/v1/domains", + body=maybe_transform({"domain": domain}, project_domains_create_params.ProjectDomainsCreateParams), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=ProjectDomainsCreateResponse, + ) + + def domains_delete( + self, + id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> None: + """ + Remove domain from project. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not id: + raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") + extra_headers = {"Accept": "*/*", **(extra_headers or {})} + return self._delete( + f"/project/v1/domains/{id}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=NoneType, + ) + + def domains_list( + self, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> ProjectDomainsListResponse: + """List all domains for the project.""" + return self._get( + "/project/v1/domains", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=ProjectDomainsListResponse, + ) + + def domains_retrieve( + self, + id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> ProjectDomainsRetrieveResponse: + """ + Get domain details by ID. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not id: + raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") + return self._get( + f"/project/v1/domains/{id}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=ProjectDomainsRetrieveResponse, + ) + + def domains_update( + self, + id: str, + *, + domain: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> ProjectDomainsUpdateResponse: + """ + Update domain settings. + + Args: + domain: Updated domain name + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not id: + raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") + return self._put( + f"/project/v1/domains/{id}", + body=maybe_transform({"domain": domain}, project_domains_update_params.ProjectDomainsUpdateParams), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=ProjectDomainsUpdateResponse, + ) + + def templates_create( + self, + *, + name: str, + body: str | Omit = omit, + subject: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> ProjectTemplatesCreateResponse: + """ + Create a new project template. + + Args: + name: Template name + + body: Email body content + + subject: Email subject line + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._post( + "/project/v1/templates", + body=maybe_transform( + { + "name": name, + "body": body, + "subject": subject, + }, + project_templates_create_params.ProjectTemplatesCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=ProjectTemplatesCreateResponse, + ) + + def templates_delete( + self, + id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> None: + """ + Delete project template. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not id: + raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") + extra_headers = {"Accept": "*/*", **(extra_headers or {})} + return self._delete( + f"/project/v1/templates/{id}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=NoneType, + ) + + def templates_list( + self, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> ProjectTemplatesListResponse: + """Get all project templates.""" + return self._get( + "/project/v1/templates", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=ProjectTemplatesListResponse, + ) + + def templates_retrieve( + self, + id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> ProjectTemplatesRetrieveResponse: + """ + Get project template by ID. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not id: + raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") + return self._get( + f"/project/v1/templates/{id}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=ProjectTemplatesRetrieveResponse, + ) + + def templates_update( + self, + id: str, + *, + body: str | Omit = omit, + name: str | Omit = omit, + subject: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> ProjectTemplatesUpdateResponse: + """ + Update project template. + + Args: + body: Updated email body content + + name: Updated template name + + subject: Updated email subject line + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not id: + raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") + return self._put( + f"/project/v1/templates/{id}", + body=maybe_transform( + { + "body": body, + "name": name, + "subject": subject, + }, + project_templates_update_params.ProjectTemplatesUpdateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=ProjectTemplatesUpdateResponse, + ) + + +class AsyncProjectResource(AsyncAPIResource): + @cached_property + def with_raw_response(self) -> AsyncProjectResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/stainless-sdks/unlayer-python#accessing-raw-response-data-eg-headers + """ + return AsyncProjectResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncProjectResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/stainless-sdks/unlayer-python#with_streaming_response + """ + return AsyncProjectResourceWithStreamingResponse(self) + + async def api_keys_create( + self, + *, + name: str, + domains: SequenceNotStr[str] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> ProjectAPIKeysCreateResponse: + """ + Create a new API key for the project. + + Args: + name: Name for the API key + + domains: Allowed domains for this API key + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return await self._post( + "/project/v1/api-keys", + body=await async_maybe_transform( + { + "name": name, + "domains": domains, + }, + project_api_keys_create_params.ProjectAPIKeysCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=ProjectAPIKeysCreateResponse, + ) + + async def api_keys_delete( + self, + id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> None: + """ + Revoke API key. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not id: + raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") + extra_headers = {"Accept": "*/*", **(extra_headers or {})} + return await self._delete( + f"/project/v1/api-keys/{id}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=NoneType, + ) + + async def api_keys_list( + self, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> ProjectAPIKeysListResponse: + """List all API keys for the project.""" + return await self._get( + "/project/v1/api-keys", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=ProjectAPIKeysListResponse, + ) + + async def api_keys_retrieve( + self, + id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> ProjectAPIKeysRetrieveResponse: + """ + Get API key details by ID. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not id: + raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") + return await self._get( + f"/project/v1/api-keys/{id}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=ProjectAPIKeysRetrieveResponse, + ) + + async def api_keys_update( + self, + id: str, + *, + active: bool | Omit = omit, + domains: SequenceNotStr[str] | Omit = omit, + name: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> ProjectAPIKeysUpdateResponse: + """ + Update API key settings. + + Args: + active: Whether the API key is active + + domains: Updated allowed domains + + name: Updated name for the API key + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not id: + raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") + return await self._put( + f"/project/v1/api-keys/{id}", + body=await async_maybe_transform( + { + "active": active, + "domains": domains, + "name": name, + }, + project_api_keys_update_params.ProjectAPIKeysUpdateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=ProjectAPIKeysUpdateResponse, + ) + + async def current_list( + self, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> ProjectCurrentListResponse: + """Get project details for the authenticated project.""" + return await self._get( + "/project/v1/current", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=ProjectCurrentListResponse, + ) + + async def domains_create( + self, + *, + domain: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> ProjectDomainsCreateResponse: + """ + Add a new domain to the project. + + Args: + domain: Domain name to add + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return await self._post( + "/project/v1/domains", + body=await async_maybe_transform( + {"domain": domain}, project_domains_create_params.ProjectDomainsCreateParams + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=ProjectDomainsCreateResponse, + ) + + async def domains_delete( + self, + id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> None: + """ + Remove domain from project. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not id: + raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") + extra_headers = {"Accept": "*/*", **(extra_headers or {})} + return await self._delete( + f"/project/v1/domains/{id}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=NoneType, + ) + + async def domains_list( + self, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> ProjectDomainsListResponse: + """List all domains for the project.""" + return await self._get( + "/project/v1/domains", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=ProjectDomainsListResponse, + ) + + async def domains_retrieve( + self, + id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> ProjectDomainsRetrieveResponse: + """ + Get domain details by ID. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not id: + raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") + return await self._get( + f"/project/v1/domains/{id}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=ProjectDomainsRetrieveResponse, + ) + + async def domains_update( + self, + id: str, + *, + domain: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> ProjectDomainsUpdateResponse: + """ + Update domain settings. + + Args: + domain: Updated domain name + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not id: + raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") + return await self._put( + f"/project/v1/domains/{id}", + body=await async_maybe_transform( + {"domain": domain}, project_domains_update_params.ProjectDomainsUpdateParams + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=ProjectDomainsUpdateResponse, + ) + + async def templates_create( + self, + *, + name: str, + body: str | Omit = omit, + subject: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> ProjectTemplatesCreateResponse: + """ + Create a new project template. + + Args: + name: Template name + + body: Email body content + + subject: Email subject line + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return await self._post( + "/project/v1/templates", + body=await async_maybe_transform( + { + "name": name, + "body": body, + "subject": subject, + }, + project_templates_create_params.ProjectTemplatesCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=ProjectTemplatesCreateResponse, + ) + + async def templates_delete( + self, + id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> None: + """ + Delete project template. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not id: + raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") + extra_headers = {"Accept": "*/*", **(extra_headers or {})} + return await self._delete( + f"/project/v1/templates/{id}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=NoneType, + ) + + async def templates_list( + self, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> ProjectTemplatesListResponse: + """Get all project templates.""" + return await self._get( + "/project/v1/templates", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=ProjectTemplatesListResponse, + ) + + async def templates_retrieve( + self, + id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> ProjectTemplatesRetrieveResponse: + """ + Get project template by ID. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not id: + raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") + return await self._get( + f"/project/v1/templates/{id}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=ProjectTemplatesRetrieveResponse, + ) + + async def templates_update( + self, + id: str, + *, + body: str | Omit = omit, + name: str | Omit = omit, + subject: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> ProjectTemplatesUpdateResponse: + """ + Update project template. + + Args: + body: Updated email body content + + name: Updated template name + + subject: Updated email subject line + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not id: + raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") + return await self._put( + f"/project/v1/templates/{id}", + body=await async_maybe_transform( + { + "body": body, + "name": name, + "subject": subject, + }, + project_templates_update_params.ProjectTemplatesUpdateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=ProjectTemplatesUpdateResponse, + ) + + +class ProjectResourceWithRawResponse: + def __init__(self, project: ProjectResource) -> None: + self._project = project + + self.api_keys_create = to_raw_response_wrapper( + project.api_keys_create, + ) + self.api_keys_delete = to_raw_response_wrapper( + project.api_keys_delete, + ) + self.api_keys_list = to_raw_response_wrapper( + project.api_keys_list, + ) + self.api_keys_retrieve = to_raw_response_wrapper( + project.api_keys_retrieve, + ) + self.api_keys_update = to_raw_response_wrapper( + project.api_keys_update, + ) + self.current_list = to_raw_response_wrapper( + project.current_list, + ) + self.domains_create = to_raw_response_wrapper( + project.domains_create, + ) + self.domains_delete = to_raw_response_wrapper( + project.domains_delete, + ) + self.domains_list = to_raw_response_wrapper( + project.domains_list, + ) + self.domains_retrieve = to_raw_response_wrapper( + project.domains_retrieve, + ) + self.domains_update = to_raw_response_wrapper( + project.domains_update, + ) + self.templates_create = to_raw_response_wrapper( + project.templates_create, + ) + self.templates_delete = to_raw_response_wrapper( + project.templates_delete, + ) + self.templates_list = to_raw_response_wrapper( + project.templates_list, + ) + self.templates_retrieve = to_raw_response_wrapper( + project.templates_retrieve, + ) + self.templates_update = to_raw_response_wrapper( + project.templates_update, + ) + + +class AsyncProjectResourceWithRawResponse: + def __init__(self, project: AsyncProjectResource) -> None: + self._project = project + + self.api_keys_create = async_to_raw_response_wrapper( + project.api_keys_create, + ) + self.api_keys_delete = async_to_raw_response_wrapper( + project.api_keys_delete, + ) + self.api_keys_list = async_to_raw_response_wrapper( + project.api_keys_list, + ) + self.api_keys_retrieve = async_to_raw_response_wrapper( + project.api_keys_retrieve, + ) + self.api_keys_update = async_to_raw_response_wrapper( + project.api_keys_update, + ) + self.current_list = async_to_raw_response_wrapper( + project.current_list, + ) + self.domains_create = async_to_raw_response_wrapper( + project.domains_create, + ) + self.domains_delete = async_to_raw_response_wrapper( + project.domains_delete, + ) + self.domains_list = async_to_raw_response_wrapper( + project.domains_list, + ) + self.domains_retrieve = async_to_raw_response_wrapper( + project.domains_retrieve, + ) + self.domains_update = async_to_raw_response_wrapper( + project.domains_update, + ) + self.templates_create = async_to_raw_response_wrapper( + project.templates_create, + ) + self.templates_delete = async_to_raw_response_wrapper( + project.templates_delete, + ) + self.templates_list = async_to_raw_response_wrapper( + project.templates_list, + ) + self.templates_retrieve = async_to_raw_response_wrapper( + project.templates_retrieve, + ) + self.templates_update = async_to_raw_response_wrapper( + project.templates_update, + ) + + +class ProjectResourceWithStreamingResponse: + def __init__(self, project: ProjectResource) -> None: + self._project = project + + self.api_keys_create = to_streamed_response_wrapper( + project.api_keys_create, + ) + self.api_keys_delete = to_streamed_response_wrapper( + project.api_keys_delete, + ) + self.api_keys_list = to_streamed_response_wrapper( + project.api_keys_list, + ) + self.api_keys_retrieve = to_streamed_response_wrapper( + project.api_keys_retrieve, + ) + self.api_keys_update = to_streamed_response_wrapper( + project.api_keys_update, + ) + self.current_list = to_streamed_response_wrapper( + project.current_list, + ) + self.domains_create = to_streamed_response_wrapper( + project.domains_create, + ) + self.domains_delete = to_streamed_response_wrapper( + project.domains_delete, + ) + self.domains_list = to_streamed_response_wrapper( + project.domains_list, + ) + self.domains_retrieve = to_streamed_response_wrapper( + project.domains_retrieve, + ) + self.domains_update = to_streamed_response_wrapper( + project.domains_update, + ) + self.templates_create = to_streamed_response_wrapper( + project.templates_create, + ) + self.templates_delete = to_streamed_response_wrapper( + project.templates_delete, + ) + self.templates_list = to_streamed_response_wrapper( + project.templates_list, + ) + self.templates_retrieve = to_streamed_response_wrapper( + project.templates_retrieve, + ) + self.templates_update = to_streamed_response_wrapper( + project.templates_update, + ) + + +class AsyncProjectResourceWithStreamingResponse: + def __init__(self, project: AsyncProjectResource) -> None: + self._project = project + + self.api_keys_create = async_to_streamed_response_wrapper( + project.api_keys_create, + ) + self.api_keys_delete = async_to_streamed_response_wrapper( + project.api_keys_delete, + ) + self.api_keys_list = async_to_streamed_response_wrapper( + project.api_keys_list, + ) + self.api_keys_retrieve = async_to_streamed_response_wrapper( + project.api_keys_retrieve, + ) + self.api_keys_update = async_to_streamed_response_wrapper( + project.api_keys_update, + ) + self.current_list = async_to_streamed_response_wrapper( + project.current_list, + ) + self.domains_create = async_to_streamed_response_wrapper( + project.domains_create, + ) + self.domains_delete = async_to_streamed_response_wrapper( + project.domains_delete, + ) + self.domains_list = async_to_streamed_response_wrapper( + project.domains_list, + ) + self.domains_retrieve = async_to_streamed_response_wrapper( + project.domains_retrieve, + ) + self.domains_update = async_to_streamed_response_wrapper( + project.domains_update, + ) + self.templates_create = async_to_streamed_response_wrapper( + project.templates_create, + ) + self.templates_delete = async_to_streamed_response_wrapper( + project.templates_delete, + ) + self.templates_list = async_to_streamed_response_wrapper( + project.templates_list, + ) + self.templates_retrieve = async_to_streamed_response_wrapper( + project.templates_retrieve, + ) + self.templates_update = async_to_streamed_response_wrapper( + project.templates_update, + ) diff --git a/src/unlayer/resources/project_v1.py b/src/unlayer/resources/project_v1.py new file mode 100644 index 0000000..5752a5c --- /dev/null +++ b/src/unlayer/resources/project_v1.py @@ -0,0 +1,1378 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import httpx + +from ..types import ( + project_v1_domains_create_params, + project_v1_domains_update_params, + project_v1_api_keys_create_params, + project_v1_api_keys_update_params, + project_v1_templates_create_params, + project_v1_templates_update_params, +) +from .._types import Body, Omit, Query, Headers, NoneType, NotGiven, SequenceNotStr, omit, not_given +from .._utils import maybe_transform, async_maybe_transform +from .._compat import cached_property +from .._resource import SyncAPIResource, AsyncAPIResource +from .._response import ( + to_raw_response_wrapper, + to_streamed_response_wrapper, + async_to_raw_response_wrapper, + async_to_streamed_response_wrapper, +) +from .._base_client import make_request_options +from ..types.project_v1_current_list_response import ProjectV1CurrentListResponse +from ..types.project_v1_domains_list_response import ProjectV1DomainsListResponse +from ..types.project_v1_api_keys_list_response import ProjectV1APIKeysListResponse +from ..types.project_v1_domains_create_response import ProjectV1DomainsCreateResponse +from ..types.project_v1_domains_update_response import ProjectV1DomainsUpdateResponse +from ..types.project_v1_templates_list_response import ProjectV1TemplatesListResponse +from ..types.project_v1_api_keys_create_response import ProjectV1APIKeysCreateResponse +from ..types.project_v1_api_keys_update_response import ProjectV1APIKeysUpdateResponse +from ..types.project_v1_domains_retrieve_response import ProjectV1DomainsRetrieveResponse +from ..types.project_v1_templates_create_response import ProjectV1TemplatesCreateResponse +from ..types.project_v1_templates_update_response import ProjectV1TemplatesUpdateResponse +from ..types.project_v1_api_keys_retrieve_response import ProjectV1APIKeysRetrieveResponse +from ..types.project_v1_templates_retrieve_response import ProjectV1TemplatesRetrieveResponse + +__all__ = ["ProjectV1Resource", "AsyncProjectV1Resource"] + + +class ProjectV1Resource(SyncAPIResource): + @cached_property + def with_raw_response(self) -> ProjectV1ResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/stainless-sdks/unlayer-python#accessing-raw-response-data-eg-headers + """ + return ProjectV1ResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> ProjectV1ResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/stainless-sdks/unlayer-python#with_streaming_response + """ + return ProjectV1ResourceWithStreamingResponse(self) + + def api_keys_create( + self, + *, + name: str, + domains: SequenceNotStr[str] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> ProjectV1APIKeysCreateResponse: + """ + Create a new API key for the project. + + Args: + name: Name for the API key + + domains: Allowed domains for this API key + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._post( + "/project/v1/api-keys", + body=maybe_transform( + { + "name": name, + "domains": domains, + }, + project_v1_api_keys_create_params.ProjectV1APIKeysCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=ProjectV1APIKeysCreateResponse, + ) + + def api_keys_delete( + self, + id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> None: + """ + Revoke API key. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not id: + raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") + extra_headers = {"Accept": "*/*", **(extra_headers or {})} + return self._delete( + f"/project/v1/api-keys/{id}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=NoneType, + ) + + def api_keys_list( + self, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> ProjectV1APIKeysListResponse: + """List all API keys for the project.""" + return self._get( + "/project/v1/api-keys", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=ProjectV1APIKeysListResponse, + ) + + def api_keys_retrieve( + self, + id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> ProjectV1APIKeysRetrieveResponse: + """ + Get API key details by ID. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not id: + raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") + return self._get( + f"/project/v1/api-keys/{id}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=ProjectV1APIKeysRetrieveResponse, + ) + + def api_keys_update( + self, + id: str, + *, + active: bool | Omit = omit, + domains: SequenceNotStr[str] | Omit = omit, + name: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> ProjectV1APIKeysUpdateResponse: + """ + Update API key settings. + + Args: + active: Whether the API key is active + + domains: Updated allowed domains + + name: Updated name for the API key + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not id: + raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") + return self._put( + f"/project/v1/api-keys/{id}", + body=maybe_transform( + { + "active": active, + "domains": domains, + "name": name, + }, + project_v1_api_keys_update_params.ProjectV1APIKeysUpdateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=ProjectV1APIKeysUpdateResponse, + ) + + def current_list( + self, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> ProjectV1CurrentListResponse: + """Get project details for the authenticated project.""" + return self._get( + "/project/v1/current", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=ProjectV1CurrentListResponse, + ) + + def domains_create( + self, + *, + domain: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> ProjectV1DomainsCreateResponse: + """ + Add a new domain to the project. + + Args: + domain: Domain name to add + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._post( + "/project/v1/domains", + body=maybe_transform({"domain": domain}, project_v1_domains_create_params.ProjectV1DomainsCreateParams), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=ProjectV1DomainsCreateResponse, + ) + + def domains_delete( + self, + id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> None: + """ + Remove domain from project. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not id: + raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") + extra_headers = {"Accept": "*/*", **(extra_headers or {})} + return self._delete( + f"/project/v1/domains/{id}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=NoneType, + ) + + def domains_list( + self, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> ProjectV1DomainsListResponse: + """List all domains for the project.""" + return self._get( + "/project/v1/domains", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=ProjectV1DomainsListResponse, + ) + + def domains_retrieve( + self, + id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> ProjectV1DomainsRetrieveResponse: + """ + Get domain details by ID. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not id: + raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") + return self._get( + f"/project/v1/domains/{id}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=ProjectV1DomainsRetrieveResponse, + ) + + def domains_update( + self, + id: str, + *, + domain: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> ProjectV1DomainsUpdateResponse: + """ + Update domain settings. + + Args: + domain: Updated domain name + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not id: + raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") + return self._put( + f"/project/v1/domains/{id}", + body=maybe_transform({"domain": domain}, project_v1_domains_update_params.ProjectV1DomainsUpdateParams), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=ProjectV1DomainsUpdateResponse, + ) + + def templates_create( + self, + *, + name: str, + body: str | Omit = omit, + subject: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> ProjectV1TemplatesCreateResponse: + """ + Create a new project template. + + Args: + name: Template name + + body: Email body content + + subject: Email subject line + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._post( + "/project/v1/templates", + body=maybe_transform( + { + "name": name, + "body": body, + "subject": subject, + }, + project_v1_templates_create_params.ProjectV1TemplatesCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=ProjectV1TemplatesCreateResponse, + ) + + def templates_delete( + self, + id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> None: + """ + Delete project template. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not id: + raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") + extra_headers = {"Accept": "*/*", **(extra_headers or {})} + return self._delete( + f"/project/v1/templates/{id}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=NoneType, + ) + + def templates_list( + self, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> ProjectV1TemplatesListResponse: + """Get all project templates.""" + return self._get( + "/project/v1/templates", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=ProjectV1TemplatesListResponse, + ) + + def templates_retrieve( + self, + id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> ProjectV1TemplatesRetrieveResponse: + """ + Get project template by ID. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not id: + raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") + return self._get( + f"/project/v1/templates/{id}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=ProjectV1TemplatesRetrieveResponse, + ) + + def templates_update( + self, + id: str, + *, + body: str | Omit = omit, + name: str | Omit = omit, + subject: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> ProjectV1TemplatesUpdateResponse: + """ + Update project template. + + Args: + body: Updated email body content + + name: Updated template name + + subject: Updated email subject line + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not id: + raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") + return self._put( + f"/project/v1/templates/{id}", + body=maybe_transform( + { + "body": body, + "name": name, + "subject": subject, + }, + project_v1_templates_update_params.ProjectV1TemplatesUpdateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=ProjectV1TemplatesUpdateResponse, + ) + + +class AsyncProjectV1Resource(AsyncAPIResource): + @cached_property + def with_raw_response(self) -> AsyncProjectV1ResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/stainless-sdks/unlayer-python#accessing-raw-response-data-eg-headers + """ + return AsyncProjectV1ResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncProjectV1ResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/stainless-sdks/unlayer-python#with_streaming_response + """ + return AsyncProjectV1ResourceWithStreamingResponse(self) + + async def api_keys_create( + self, + *, + name: str, + domains: SequenceNotStr[str] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> ProjectV1APIKeysCreateResponse: + """ + Create a new API key for the project. + + Args: + name: Name for the API key + + domains: Allowed domains for this API key + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return await self._post( + "/project/v1/api-keys", + body=await async_maybe_transform( + { + "name": name, + "domains": domains, + }, + project_v1_api_keys_create_params.ProjectV1APIKeysCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=ProjectV1APIKeysCreateResponse, + ) + + async def api_keys_delete( + self, + id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> None: + """ + Revoke API key. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not id: + raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") + extra_headers = {"Accept": "*/*", **(extra_headers or {})} + return await self._delete( + f"/project/v1/api-keys/{id}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=NoneType, + ) + + async def api_keys_list( + self, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> ProjectV1APIKeysListResponse: + """List all API keys for the project.""" + return await self._get( + "/project/v1/api-keys", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=ProjectV1APIKeysListResponse, + ) + + async def api_keys_retrieve( + self, + id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> ProjectV1APIKeysRetrieveResponse: + """ + Get API key details by ID. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not id: + raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") + return await self._get( + f"/project/v1/api-keys/{id}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=ProjectV1APIKeysRetrieveResponse, + ) + + async def api_keys_update( + self, + id: str, + *, + active: bool | Omit = omit, + domains: SequenceNotStr[str] | Omit = omit, + name: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> ProjectV1APIKeysUpdateResponse: + """ + Update API key settings. + + Args: + active: Whether the API key is active + + domains: Updated allowed domains + + name: Updated name for the API key + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not id: + raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") + return await self._put( + f"/project/v1/api-keys/{id}", + body=await async_maybe_transform( + { + "active": active, + "domains": domains, + "name": name, + }, + project_v1_api_keys_update_params.ProjectV1APIKeysUpdateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=ProjectV1APIKeysUpdateResponse, + ) + + async def current_list( + self, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> ProjectV1CurrentListResponse: + """Get project details for the authenticated project.""" + return await self._get( + "/project/v1/current", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=ProjectV1CurrentListResponse, + ) + + async def domains_create( + self, + *, + domain: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> ProjectV1DomainsCreateResponse: + """ + Add a new domain to the project. + + Args: + domain: Domain name to add + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return await self._post( + "/project/v1/domains", + body=await async_maybe_transform( + {"domain": domain}, project_v1_domains_create_params.ProjectV1DomainsCreateParams + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=ProjectV1DomainsCreateResponse, + ) + + async def domains_delete( + self, + id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> None: + """ + Remove domain from project. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not id: + raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") + extra_headers = {"Accept": "*/*", **(extra_headers or {})} + return await self._delete( + f"/project/v1/domains/{id}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=NoneType, + ) + + async def domains_list( + self, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> ProjectV1DomainsListResponse: + """List all domains for the project.""" + return await self._get( + "/project/v1/domains", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=ProjectV1DomainsListResponse, + ) + + async def domains_retrieve( + self, + id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> ProjectV1DomainsRetrieveResponse: + """ + Get domain details by ID. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not id: + raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") + return await self._get( + f"/project/v1/domains/{id}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=ProjectV1DomainsRetrieveResponse, + ) + + async def domains_update( + self, + id: str, + *, + domain: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> ProjectV1DomainsUpdateResponse: + """ + Update domain settings. + + Args: + domain: Updated domain name + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not id: + raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") + return await self._put( + f"/project/v1/domains/{id}", + body=await async_maybe_transform( + {"domain": domain}, project_v1_domains_update_params.ProjectV1DomainsUpdateParams + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=ProjectV1DomainsUpdateResponse, + ) + + async def templates_create( + self, + *, + name: str, + body: str | Omit = omit, + subject: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> ProjectV1TemplatesCreateResponse: + """ + Create a new project template. + + Args: + name: Template name + + body: Email body content + + subject: Email subject line + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return await self._post( + "/project/v1/templates", + body=await async_maybe_transform( + { + "name": name, + "body": body, + "subject": subject, + }, + project_v1_templates_create_params.ProjectV1TemplatesCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=ProjectV1TemplatesCreateResponse, + ) + + async def templates_delete( + self, + id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> None: + """ + Delete project template. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not id: + raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") + extra_headers = {"Accept": "*/*", **(extra_headers or {})} + return await self._delete( + f"/project/v1/templates/{id}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=NoneType, + ) + + async def templates_list( + self, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> ProjectV1TemplatesListResponse: + """Get all project templates.""" + return await self._get( + "/project/v1/templates", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=ProjectV1TemplatesListResponse, + ) + + async def templates_retrieve( + self, + id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> ProjectV1TemplatesRetrieveResponse: + """ + Get project template by ID. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not id: + raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") + return await self._get( + f"/project/v1/templates/{id}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=ProjectV1TemplatesRetrieveResponse, + ) + + async def templates_update( + self, + id: str, + *, + body: str | Omit = omit, + name: str | Omit = omit, + subject: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> ProjectV1TemplatesUpdateResponse: + """ + Update project template. + + Args: + body: Updated email body content + + name: Updated template name + + subject: Updated email subject line + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not id: + raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") + return await self._put( + f"/project/v1/templates/{id}", + body=await async_maybe_transform( + { + "body": body, + "name": name, + "subject": subject, + }, + project_v1_templates_update_params.ProjectV1TemplatesUpdateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=ProjectV1TemplatesUpdateResponse, + ) + + +class ProjectV1ResourceWithRawResponse: + def __init__(self, project_v1: ProjectV1Resource) -> None: + self._project_v1 = project_v1 + + self.api_keys_create = to_raw_response_wrapper( + project_v1.api_keys_create, + ) + self.api_keys_delete = to_raw_response_wrapper( + project_v1.api_keys_delete, + ) + self.api_keys_list = to_raw_response_wrapper( + project_v1.api_keys_list, + ) + self.api_keys_retrieve = to_raw_response_wrapper( + project_v1.api_keys_retrieve, + ) + self.api_keys_update = to_raw_response_wrapper( + project_v1.api_keys_update, + ) + self.current_list = to_raw_response_wrapper( + project_v1.current_list, + ) + self.domains_create = to_raw_response_wrapper( + project_v1.domains_create, + ) + self.domains_delete = to_raw_response_wrapper( + project_v1.domains_delete, + ) + self.domains_list = to_raw_response_wrapper( + project_v1.domains_list, + ) + self.domains_retrieve = to_raw_response_wrapper( + project_v1.domains_retrieve, + ) + self.domains_update = to_raw_response_wrapper( + project_v1.domains_update, + ) + self.templates_create = to_raw_response_wrapper( + project_v1.templates_create, + ) + self.templates_delete = to_raw_response_wrapper( + project_v1.templates_delete, + ) + self.templates_list = to_raw_response_wrapper( + project_v1.templates_list, + ) + self.templates_retrieve = to_raw_response_wrapper( + project_v1.templates_retrieve, + ) + self.templates_update = to_raw_response_wrapper( + project_v1.templates_update, + ) + + +class AsyncProjectV1ResourceWithRawResponse: + def __init__(self, project_v1: AsyncProjectV1Resource) -> None: + self._project_v1 = project_v1 + + self.api_keys_create = async_to_raw_response_wrapper( + project_v1.api_keys_create, + ) + self.api_keys_delete = async_to_raw_response_wrapper( + project_v1.api_keys_delete, + ) + self.api_keys_list = async_to_raw_response_wrapper( + project_v1.api_keys_list, + ) + self.api_keys_retrieve = async_to_raw_response_wrapper( + project_v1.api_keys_retrieve, + ) + self.api_keys_update = async_to_raw_response_wrapper( + project_v1.api_keys_update, + ) + self.current_list = async_to_raw_response_wrapper( + project_v1.current_list, + ) + self.domains_create = async_to_raw_response_wrapper( + project_v1.domains_create, + ) + self.domains_delete = async_to_raw_response_wrapper( + project_v1.domains_delete, + ) + self.domains_list = async_to_raw_response_wrapper( + project_v1.domains_list, + ) + self.domains_retrieve = async_to_raw_response_wrapper( + project_v1.domains_retrieve, + ) + self.domains_update = async_to_raw_response_wrapper( + project_v1.domains_update, + ) + self.templates_create = async_to_raw_response_wrapper( + project_v1.templates_create, + ) + self.templates_delete = async_to_raw_response_wrapper( + project_v1.templates_delete, + ) + self.templates_list = async_to_raw_response_wrapper( + project_v1.templates_list, + ) + self.templates_retrieve = async_to_raw_response_wrapper( + project_v1.templates_retrieve, + ) + self.templates_update = async_to_raw_response_wrapper( + project_v1.templates_update, + ) + + +class ProjectV1ResourceWithStreamingResponse: + def __init__(self, project_v1: ProjectV1Resource) -> None: + self._project_v1 = project_v1 + + self.api_keys_create = to_streamed_response_wrapper( + project_v1.api_keys_create, + ) + self.api_keys_delete = to_streamed_response_wrapper( + project_v1.api_keys_delete, + ) + self.api_keys_list = to_streamed_response_wrapper( + project_v1.api_keys_list, + ) + self.api_keys_retrieve = to_streamed_response_wrapper( + project_v1.api_keys_retrieve, + ) + self.api_keys_update = to_streamed_response_wrapper( + project_v1.api_keys_update, + ) + self.current_list = to_streamed_response_wrapper( + project_v1.current_list, + ) + self.domains_create = to_streamed_response_wrapper( + project_v1.domains_create, + ) + self.domains_delete = to_streamed_response_wrapper( + project_v1.domains_delete, + ) + self.domains_list = to_streamed_response_wrapper( + project_v1.domains_list, + ) + self.domains_retrieve = to_streamed_response_wrapper( + project_v1.domains_retrieve, + ) + self.domains_update = to_streamed_response_wrapper( + project_v1.domains_update, + ) + self.templates_create = to_streamed_response_wrapper( + project_v1.templates_create, + ) + self.templates_delete = to_streamed_response_wrapper( + project_v1.templates_delete, + ) + self.templates_list = to_streamed_response_wrapper( + project_v1.templates_list, + ) + self.templates_retrieve = to_streamed_response_wrapper( + project_v1.templates_retrieve, + ) + self.templates_update = to_streamed_response_wrapper( + project_v1.templates_update, + ) + + +class AsyncProjectV1ResourceWithStreamingResponse: + def __init__(self, project_v1: AsyncProjectV1Resource) -> None: + self._project_v1 = project_v1 + + self.api_keys_create = async_to_streamed_response_wrapper( + project_v1.api_keys_create, + ) + self.api_keys_delete = async_to_streamed_response_wrapper( + project_v1.api_keys_delete, + ) + self.api_keys_list = async_to_streamed_response_wrapper( + project_v1.api_keys_list, + ) + self.api_keys_retrieve = async_to_streamed_response_wrapper( + project_v1.api_keys_retrieve, + ) + self.api_keys_update = async_to_streamed_response_wrapper( + project_v1.api_keys_update, + ) + self.current_list = async_to_streamed_response_wrapper( + project_v1.current_list, + ) + self.domains_create = async_to_streamed_response_wrapper( + project_v1.domains_create, + ) + self.domains_delete = async_to_streamed_response_wrapper( + project_v1.domains_delete, + ) + self.domains_list = async_to_streamed_response_wrapper( + project_v1.domains_list, + ) + self.domains_retrieve = async_to_streamed_response_wrapper( + project_v1.domains_retrieve, + ) + self.domains_update = async_to_streamed_response_wrapper( + project_v1.domains_update, + ) + self.templates_create = async_to_streamed_response_wrapper( + project_v1.templates_create, + ) + self.templates_delete = async_to_streamed_response_wrapper( + project_v1.templates_delete, + ) + self.templates_list = async_to_streamed_response_wrapper( + project_v1.templates_list, + ) + self.templates_retrieve = async_to_streamed_response_wrapper( + project_v1.templates_retrieve, + ) + self.templates_update = async_to_streamed_response_wrapper( + project_v1.templates_update, + ) diff --git a/src/unlayer/types/__init__.py b/src/unlayer/types/__init__.py new file mode 100644 index 0000000..9e6dfd4 --- /dev/null +++ b/src/unlayer/types/__init__.py @@ -0,0 +1,90 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from .email_send_create_params import EmailSendCreateParams as EmailSendCreateParams +from .page_render_create_params import PageRenderCreateParams as PageRenderCreateParams +from .email_render_create_params import EmailRenderCreateParams as EmailRenderCreateParams +from .email_send_create_response import EmailSendCreateResponse as EmailSendCreateResponse +from .page_render_create_response import PageRenderCreateResponse as PageRenderCreateResponse +from .email_render_create_response import EmailRenderCreateResponse as EmailRenderCreateResponse +from .emails_v1_send_create_params import EmailsV1SendCreateParams as EmailsV1SendCreateParams +from .pages_v1_render_create_params import PagesV1RenderCreateParams as PagesV1RenderCreateParams +from .project_current_list_response import ProjectCurrentListResponse as ProjectCurrentListResponse +from .project_domains_create_params import ProjectDomainsCreateParams as ProjectDomainsCreateParams +from .project_domains_list_response import ProjectDomainsListResponse as ProjectDomainsListResponse +from .project_domains_update_params import ProjectDomainsUpdateParams as ProjectDomainsUpdateParams +from .email_emails_retrieve_response import EmailEmailsRetrieveResponse as EmailEmailsRetrieveResponse +from .emails_v1_render_create_params import EmailsV1RenderCreateParams as EmailsV1RenderCreateParams +from .emails_v1_send_create_response import EmailsV1SendCreateResponse as EmailsV1SendCreateResponse +from .project_api_keys_create_params import ProjectAPIKeysCreateParams as ProjectAPIKeysCreateParams +from .project_api_keys_list_response import ProjectAPIKeysListResponse as ProjectAPIKeysListResponse +from .project_api_keys_update_params import ProjectAPIKeysUpdateParams as ProjectAPIKeysUpdateParams +from .document_generate_create_params import DocumentGenerateCreateParams as DocumentGenerateCreateParams +from .pages_v1_render_create_response import PagesV1RenderCreateResponse as PagesV1RenderCreateResponse +from .project_domains_create_response import ProjectDomainsCreateResponse as ProjectDomainsCreateResponse +from .project_domains_update_response import ProjectDomainsUpdateResponse as ProjectDomainsUpdateResponse +from .project_templates_create_params import ProjectTemplatesCreateParams as ProjectTemplatesCreateParams +from .project_templates_list_response import ProjectTemplatesListResponse as ProjectTemplatesListResponse +from .project_templates_update_params import ProjectTemplatesUpdateParams as ProjectTemplatesUpdateParams +from .emails_v1_render_create_response import EmailsV1RenderCreateResponse as EmailsV1RenderCreateResponse +from .project_api_keys_create_response import ProjectAPIKeysCreateResponse as ProjectAPIKeysCreateResponse +from .project_api_keys_update_response import ProjectAPIKeysUpdateResponse as ProjectAPIKeysUpdateResponse +from .project_v1_current_list_response import ProjectV1CurrentListResponse as ProjectV1CurrentListResponse +from .project_v1_domains_create_params import ProjectV1DomainsCreateParams as ProjectV1DomainsCreateParams +from .project_v1_domains_list_response import ProjectV1DomainsListResponse as ProjectV1DomainsListResponse +from .project_v1_domains_update_params import ProjectV1DomainsUpdateParams as ProjectV1DomainsUpdateParams +from .document_generate_create_response import DocumentGenerateCreateResponse as DocumentGenerateCreateResponse +from .project_domains_retrieve_response import ProjectDomainsRetrieveResponse as ProjectDomainsRetrieveResponse +from .project_templates_create_response import ProjectTemplatesCreateResponse as ProjectTemplatesCreateResponse +from .project_templates_update_response import ProjectTemplatesUpdateResponse as ProjectTemplatesUpdateResponse +from .project_v1_api_keys_create_params import ProjectV1APIKeysCreateParams as ProjectV1APIKeysCreateParams +from .project_v1_api_keys_list_response import ProjectV1APIKeysListResponse as ProjectV1APIKeysListResponse +from .project_v1_api_keys_update_params import ProjectV1APIKeysUpdateParams as ProjectV1APIKeysUpdateParams +from .emails_v1_emails_retrieve_response import EmailsV1EmailsRetrieveResponse as EmailsV1EmailsRetrieveResponse +from .project_api_keys_retrieve_response import ProjectAPIKeysRetrieveResponse as ProjectAPIKeysRetrieveResponse +from .project_v1_domains_create_response import ProjectV1DomainsCreateResponse as ProjectV1DomainsCreateResponse +from .project_v1_domains_update_response import ProjectV1DomainsUpdateResponse as ProjectV1DomainsUpdateResponse +from .project_v1_templates_create_params import ProjectV1TemplatesCreateParams as ProjectV1TemplatesCreateParams +from .project_v1_templates_list_response import ProjectV1TemplatesListResponse as ProjectV1TemplatesListResponse +from .project_v1_templates_update_params import ProjectV1TemplatesUpdateParams as ProjectV1TemplatesUpdateParams +from .documents_v1_generate_create_params import DocumentsV1GenerateCreateParams as DocumentsV1GenerateCreateParams +from .email_send_template_template_params import EmailSendTemplateTemplateParams as EmailSendTemplateTemplateParams +from .project_templates_retrieve_response import ProjectTemplatesRetrieveResponse as ProjectTemplatesRetrieveResponse +from .project_v1_api_keys_create_response import ProjectV1APIKeysCreateResponse as ProjectV1APIKeysCreateResponse +from .project_v1_api_keys_update_response import ProjectV1APIKeysUpdateResponse as ProjectV1APIKeysUpdateResponse +from .document_documents_retrieve_response import DocumentDocumentsRetrieveResponse as DocumentDocumentsRetrieveResponse +from .project_v1_domains_retrieve_response import ProjectV1DomainsRetrieveResponse as ProjectV1DomainsRetrieveResponse +from .project_v1_templates_create_response import ProjectV1TemplatesCreateResponse as ProjectV1TemplatesCreateResponse +from .project_v1_templates_update_response import ProjectV1TemplatesUpdateResponse as ProjectV1TemplatesUpdateResponse +from .documents_v1_generate_create_response import ( + DocumentsV1GenerateCreateResponse as DocumentsV1GenerateCreateResponse, +) +from .email_send_template_template_response import ( + EmailSendTemplateTemplateResponse as EmailSendTemplateTemplateResponse, +) +from .project_v1_api_keys_retrieve_response import ProjectV1APIKeysRetrieveResponse as ProjectV1APIKeysRetrieveResponse +from .project_v1_templates_retrieve_response import ( + ProjectV1TemplatesRetrieveResponse as ProjectV1TemplatesRetrieveResponse, +) +from .emails_v1_send_template_template_params import ( + EmailsV1SendTemplateTemplateParams as EmailsV1SendTemplateTemplateParams, +) +from .documents_v1_documents_retrieve_response import ( + DocumentsV1DocumentsRetrieveResponse as DocumentsV1DocumentsRetrieveResponse, +) +from .emails_v1_send_template_template_response import ( + EmailsV1SendTemplateTemplateResponse as EmailsV1SendTemplateTemplateResponse, +) +from .document_generate_template_template_params import ( + DocumentGenerateTemplateTemplateParams as DocumentGenerateTemplateTemplateParams, +) +from .document_generate_template_template_response import ( + DocumentGenerateTemplateTemplateResponse as DocumentGenerateTemplateTemplateResponse, +) +from .documents_v1_generate_template_template_params import ( + DocumentsV1GenerateTemplateTemplateParams as DocumentsV1GenerateTemplateTemplateParams, +) +from .documents_v1_generate_template_template_response import ( + DocumentsV1GenerateTemplateTemplateResponse as DocumentsV1GenerateTemplateTemplateResponse, +) diff --git a/src/unlayer/types/document_documents_retrieve_response.py b/src/unlayer/types/document_documents_retrieve_response.py new file mode 100644 index 0000000..68c5360 --- /dev/null +++ b/src/unlayer/types/document_documents_retrieve_response.py @@ -0,0 +1,37 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional +from datetime import datetime +from typing_extensions import Literal + +from pydantic import Field as FieldInfo + +from .._models import BaseModel + +__all__ = ["DocumentDocumentsRetrieveResponse"] + + +class DocumentDocumentsRetrieveResponse(BaseModel): + id: Optional[str] = None + """Document ID""" + + completed_at: Optional[datetime] = FieldInfo(alias="completedAt", default=None) + """When the document generation was completed""" + + created_at: Optional[datetime] = FieldInfo(alias="createdAt", default=None) + """When the document was created""" + + filename: Optional[str] = None + """Generated filename""" + + file_size: Optional[float] = FieldInfo(alias="fileSize", default=None) + """File size in bytes""" + + page_count: Optional[float] = FieldInfo(alias="pageCount", default=None) + """Number of pages in the PDF""" + + pdf_url: Optional[str] = FieldInfo(alias="pdfUrl", default=None) + """URL to download the PDF""" + + status: Optional[Literal["generating", "completed", "failed"]] = None + """Current document status""" diff --git a/src/unlayer/types/document_generate_create_params.py b/src/unlayer/types/document_generate_create_params.py new file mode 100644 index 0000000..012f007 --- /dev/null +++ b/src/unlayer/types/document_generate_create_params.py @@ -0,0 +1,27 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Dict +from typing_extensions import Required, Annotated, TypedDict + +from .._utils import PropertyInfo + +__all__ = ["DocumentGenerateCreateParams"] + + +class DocumentGenerateCreateParams(TypedDict, total=False): + design: Required[Dict[str, object]] + """Proprietary design format JSON""" + + filename: str + """Optional filename for the generated PDF""" + + html: str + """HTML content to convert to PDF""" + + merge_tags: Annotated[Dict[str, str], PropertyInfo(alias="mergeTags")] + """Optional merge tags for personalization""" + + url: str + """URL to convert to PDF""" diff --git a/src/unlayer/types/document_generate_create_response.py b/src/unlayer/types/document_generate_create_response.py new file mode 100644 index 0000000..60919cc --- /dev/null +++ b/src/unlayer/types/document_generate_create_response.py @@ -0,0 +1,23 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from pydantic import Field as FieldInfo + +from .._models import BaseModel + +__all__ = ["DocumentGenerateCreateResponse"] + + +class DocumentGenerateCreateResponse(BaseModel): + document_id: Optional[str] = FieldInfo(alias="documentId", default=None) + """Unique document identifier""" + + filename: Optional[str] = None + """Generated filename""" + + pdf_url: Optional[str] = FieldInfo(alias="pdfUrl", default=None) + """URL to download the generated PDF""" + + status: Optional[Literal["generating", "completed", "failed"]] = None diff --git a/src/unlayer/types/document_generate_template_template_params.py b/src/unlayer/types/document_generate_template_template_params.py new file mode 100644 index 0000000..6e2a307 --- /dev/null +++ b/src/unlayer/types/document_generate_template_template_params.py @@ -0,0 +1,21 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Dict +from typing_extensions import Required, Annotated, TypedDict + +from .._utils import PropertyInfo + +__all__ = ["DocumentGenerateTemplateTemplateParams"] + + +class DocumentGenerateTemplateTemplateParams(TypedDict, total=False): + template_id: Required[Annotated[str, PropertyInfo(alias="templateId")]] + """ID of the template to use for generation""" + + filename: str + """Optional filename for the generated PDF""" + + merge_tags: Annotated[Dict[str, str], PropertyInfo(alias="mergeTags")] + """Optional merge tags for personalization""" diff --git a/src/unlayer/types/document_generate_template_template_response.py b/src/unlayer/types/document_generate_template_template_response.py new file mode 100644 index 0000000..fcf7073 --- /dev/null +++ b/src/unlayer/types/document_generate_template_template_response.py @@ -0,0 +1,23 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from pydantic import Field as FieldInfo + +from .._models import BaseModel + +__all__ = ["DocumentGenerateTemplateTemplateResponse"] + + +class DocumentGenerateTemplateTemplateResponse(BaseModel): + document_id: Optional[str] = FieldInfo(alias="documentId", default=None) + """Unique document identifier""" + + filename: Optional[str] = None + """Generated filename""" + + pdf_url: Optional[str] = FieldInfo(alias="pdfUrl", default=None) + """URL to download the generated PDF""" + + status: Optional[Literal["generating", "completed", "failed"]] = None diff --git a/src/unlayer/types/documents_v1_documents_retrieve_response.py b/src/unlayer/types/documents_v1_documents_retrieve_response.py new file mode 100644 index 0000000..88ba0d4 --- /dev/null +++ b/src/unlayer/types/documents_v1_documents_retrieve_response.py @@ -0,0 +1,37 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional +from datetime import datetime +from typing_extensions import Literal + +from pydantic import Field as FieldInfo + +from .._models import BaseModel + +__all__ = ["DocumentsV1DocumentsRetrieveResponse"] + + +class DocumentsV1DocumentsRetrieveResponse(BaseModel): + id: Optional[str] = None + """Document ID""" + + completed_at: Optional[datetime] = FieldInfo(alias="completedAt", default=None) + """When the document generation was completed""" + + created_at: Optional[datetime] = FieldInfo(alias="createdAt", default=None) + """When the document was created""" + + filename: Optional[str] = None + """Generated filename""" + + file_size: Optional[float] = FieldInfo(alias="fileSize", default=None) + """File size in bytes""" + + page_count: Optional[float] = FieldInfo(alias="pageCount", default=None) + """Number of pages in the PDF""" + + pdf_url: Optional[str] = FieldInfo(alias="pdfUrl", default=None) + """URL to download the PDF""" + + status: Optional[Literal["generating", "completed", "failed"]] = None + """Current document status""" diff --git a/src/unlayer/types/documents_v1_generate_create_params.py b/src/unlayer/types/documents_v1_generate_create_params.py new file mode 100644 index 0000000..fb63fdc --- /dev/null +++ b/src/unlayer/types/documents_v1_generate_create_params.py @@ -0,0 +1,27 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Dict +from typing_extensions import Required, Annotated, TypedDict + +from .._utils import PropertyInfo + +__all__ = ["DocumentsV1GenerateCreateParams"] + + +class DocumentsV1GenerateCreateParams(TypedDict, total=False): + design: Required[Dict[str, object]] + """Proprietary design format JSON""" + + filename: str + """Optional filename for the generated PDF""" + + html: str + """HTML content to convert to PDF""" + + merge_tags: Annotated[Dict[str, str], PropertyInfo(alias="mergeTags")] + """Optional merge tags for personalization""" + + url: str + """URL to convert to PDF""" diff --git a/src/unlayer/types/documents_v1_generate_create_response.py b/src/unlayer/types/documents_v1_generate_create_response.py new file mode 100644 index 0000000..0c162cc --- /dev/null +++ b/src/unlayer/types/documents_v1_generate_create_response.py @@ -0,0 +1,23 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from pydantic import Field as FieldInfo + +from .._models import BaseModel + +__all__ = ["DocumentsV1GenerateCreateResponse"] + + +class DocumentsV1GenerateCreateResponse(BaseModel): + document_id: Optional[str] = FieldInfo(alias="documentId", default=None) + """Unique document identifier""" + + filename: Optional[str] = None + """Generated filename""" + + pdf_url: Optional[str] = FieldInfo(alias="pdfUrl", default=None) + """URL to download the generated PDF""" + + status: Optional[Literal["generating", "completed", "failed"]] = None diff --git a/src/unlayer/types/documents_v1_generate_template_template_params.py b/src/unlayer/types/documents_v1_generate_template_template_params.py new file mode 100644 index 0000000..8fdc116 --- /dev/null +++ b/src/unlayer/types/documents_v1_generate_template_template_params.py @@ -0,0 +1,21 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Dict +from typing_extensions import Required, Annotated, TypedDict + +from .._utils import PropertyInfo + +__all__ = ["DocumentsV1GenerateTemplateTemplateParams"] + + +class DocumentsV1GenerateTemplateTemplateParams(TypedDict, total=False): + template_id: Required[Annotated[str, PropertyInfo(alias="templateId")]] + """ID of the template to use for generation""" + + filename: str + """Optional filename for the generated PDF""" + + merge_tags: Annotated[Dict[str, str], PropertyInfo(alias="mergeTags")] + """Optional merge tags for personalization""" diff --git a/src/unlayer/types/documents_v1_generate_template_template_response.py b/src/unlayer/types/documents_v1_generate_template_template_response.py new file mode 100644 index 0000000..e73822c --- /dev/null +++ b/src/unlayer/types/documents_v1_generate_template_template_response.py @@ -0,0 +1,23 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from pydantic import Field as FieldInfo + +from .._models import BaseModel + +__all__ = ["DocumentsV1GenerateTemplateTemplateResponse"] + + +class DocumentsV1GenerateTemplateTemplateResponse(BaseModel): + document_id: Optional[str] = FieldInfo(alias="documentId", default=None) + """Unique document identifier""" + + filename: Optional[str] = None + """Generated filename""" + + pdf_url: Optional[str] = FieldInfo(alias="pdfUrl", default=None) + """URL to download the generated PDF""" + + status: Optional[Literal["generating", "completed", "failed"]] = None diff --git a/src/unlayer/types/email_emails_retrieve_response.py b/src/unlayer/types/email_emails_retrieve_response.py new file mode 100644 index 0000000..1fed895 --- /dev/null +++ b/src/unlayer/types/email_emails_retrieve_response.py @@ -0,0 +1,31 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional +from datetime import datetime +from typing_extensions import Literal + +from pydantic import Field as FieldInfo + +from .._models import BaseModel + +__all__ = ["EmailEmailsRetrieveResponse"] + + +class EmailEmailsRetrieveResponse(BaseModel): + id: Optional[str] = None + """Email message ID""" + + html: Optional[str] = None + """HTML content of the email (optional)""" + + sent_at: Optional[datetime] = FieldInfo(alias="sentAt", default=None) + """When the email was sent""" + + status: Optional[Literal["sent", "delivered", "opened", "clicked", "bounced", "failed"]] = None + """Current email status""" + + subject: Optional[str] = None + """Email subject line""" + + to: Optional[str] = None + """Recipient email address""" diff --git a/src/unlayer/types/email_render_create_params.py b/src/unlayer/types/email_render_create_params.py new file mode 100644 index 0000000..2ece897 --- /dev/null +++ b/src/unlayer/types/email_render_create_params.py @@ -0,0 +1,18 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Dict +from typing_extensions import Required, Annotated, TypedDict + +from .._utils import PropertyInfo + +__all__ = ["EmailRenderCreateParams"] + + +class EmailRenderCreateParams(TypedDict, total=False): + design: Required[Dict[str, object]] + """Proprietary design format JSON""" + + merge_tags: Annotated[Dict[str, str], PropertyInfo(alias="mergeTags")] + """Optional merge tags for personalization""" diff --git a/src/unlayer/types/email_render_create_response.py b/src/unlayer/types/email_render_create_response.py new file mode 100644 index 0000000..e41d301 --- /dev/null +++ b/src/unlayer/types/email_render_create_response.py @@ -0,0 +1,12 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional + +from .._models import BaseModel + +__all__ = ["EmailRenderCreateResponse"] + + +class EmailRenderCreateResponse(BaseModel): + html: Optional[str] = None + """Rendered HTML content""" diff --git a/src/unlayer/types/email_send_create_params.py b/src/unlayer/types/email_send_create_params.py new file mode 100644 index 0000000..073951c --- /dev/null +++ b/src/unlayer/types/email_send_create_params.py @@ -0,0 +1,27 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Dict +from typing_extensions import Required, Annotated, TypedDict + +from .._utils import PropertyInfo + +__all__ = ["EmailSendCreateParams"] + + +class EmailSendCreateParams(TypedDict, total=False): + design: Required[Dict[str, object]] + """Proprietary design format JSON""" + + to: Required[str] + """Recipient email address""" + + html: str + """HTML content to send""" + + merge_tags: Annotated[Dict[str, str], PropertyInfo(alias="mergeTags")] + """Optional merge tags for personalization""" + + subject: str + """Email subject line""" diff --git a/src/unlayer/types/email_send_create_response.py b/src/unlayer/types/email_send_create_response.py new file mode 100644 index 0000000..97c2daf --- /dev/null +++ b/src/unlayer/types/email_send_create_response.py @@ -0,0 +1,17 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from pydantic import Field as FieldInfo + +from .._models import BaseModel + +__all__ = ["EmailSendCreateResponse"] + + +class EmailSendCreateResponse(BaseModel): + message_id: Optional[str] = FieldInfo(alias="messageId", default=None) + """Unique message identifier""" + + status: Optional[Literal["sent", "queued", "failed"]] = None diff --git a/src/unlayer/types/email_send_template_template_params.py b/src/unlayer/types/email_send_template_template_params.py new file mode 100644 index 0000000..856309e --- /dev/null +++ b/src/unlayer/types/email_send_template_template_params.py @@ -0,0 +1,24 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Dict +from typing_extensions import Required, Annotated, TypedDict + +from .._utils import PropertyInfo + +__all__ = ["EmailSendTemplateTemplateParams"] + + +class EmailSendTemplateTemplateParams(TypedDict, total=False): + template_id: Required[Annotated[str, PropertyInfo(alias="templateId")]] + """ID of the template to use""" + + to: Required[str] + """Recipient email address""" + + merge_tags: Annotated[Dict[str, str], PropertyInfo(alias="mergeTags")] + """Optional merge tags for personalization""" + + subject: str + """Email subject line (optional, uses template default if not provided)""" diff --git a/src/unlayer/types/email_send_template_template_response.py b/src/unlayer/types/email_send_template_template_response.py new file mode 100644 index 0000000..03b91aa --- /dev/null +++ b/src/unlayer/types/email_send_template_template_response.py @@ -0,0 +1,17 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from pydantic import Field as FieldInfo + +from .._models import BaseModel + +__all__ = ["EmailSendTemplateTemplateResponse"] + + +class EmailSendTemplateTemplateResponse(BaseModel): + message_id: Optional[str] = FieldInfo(alias="messageId", default=None) + """Unique message identifier""" + + status: Optional[Literal["sent", "queued", "failed"]] = None diff --git a/src/unlayer/types/emails_v1_emails_retrieve_response.py b/src/unlayer/types/emails_v1_emails_retrieve_response.py new file mode 100644 index 0000000..3bba362 --- /dev/null +++ b/src/unlayer/types/emails_v1_emails_retrieve_response.py @@ -0,0 +1,31 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional +from datetime import datetime +from typing_extensions import Literal + +from pydantic import Field as FieldInfo + +from .._models import BaseModel + +__all__ = ["EmailsV1EmailsRetrieveResponse"] + + +class EmailsV1EmailsRetrieveResponse(BaseModel): + id: Optional[str] = None + """Email message ID""" + + html: Optional[str] = None + """HTML content of the email (optional)""" + + sent_at: Optional[datetime] = FieldInfo(alias="sentAt", default=None) + """When the email was sent""" + + status: Optional[Literal["sent", "delivered", "opened", "clicked", "bounced", "failed"]] = None + """Current email status""" + + subject: Optional[str] = None + """Email subject line""" + + to: Optional[str] = None + """Recipient email address""" diff --git a/src/unlayer/types/emails_v1_render_create_params.py b/src/unlayer/types/emails_v1_render_create_params.py new file mode 100644 index 0000000..5f6f577 --- /dev/null +++ b/src/unlayer/types/emails_v1_render_create_params.py @@ -0,0 +1,18 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Dict +from typing_extensions import Required, Annotated, TypedDict + +from .._utils import PropertyInfo + +__all__ = ["EmailsV1RenderCreateParams"] + + +class EmailsV1RenderCreateParams(TypedDict, total=False): + design: Required[Dict[str, object]] + """Proprietary design format JSON""" + + merge_tags: Annotated[Dict[str, str], PropertyInfo(alias="mergeTags")] + """Optional merge tags for personalization""" diff --git a/src/unlayer/types/emails_v1_render_create_response.py b/src/unlayer/types/emails_v1_render_create_response.py new file mode 100644 index 0000000..35cdf03 --- /dev/null +++ b/src/unlayer/types/emails_v1_render_create_response.py @@ -0,0 +1,12 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional + +from .._models import BaseModel + +__all__ = ["EmailsV1RenderCreateResponse"] + + +class EmailsV1RenderCreateResponse(BaseModel): + html: Optional[str] = None + """Rendered HTML content""" diff --git a/src/unlayer/types/emails_v1_send_create_params.py b/src/unlayer/types/emails_v1_send_create_params.py new file mode 100644 index 0000000..e9a82ba --- /dev/null +++ b/src/unlayer/types/emails_v1_send_create_params.py @@ -0,0 +1,27 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Dict +from typing_extensions import Required, Annotated, TypedDict + +from .._utils import PropertyInfo + +__all__ = ["EmailsV1SendCreateParams"] + + +class EmailsV1SendCreateParams(TypedDict, total=False): + design: Required[Dict[str, object]] + """Proprietary design format JSON""" + + to: Required[str] + """Recipient email address""" + + html: str + """HTML content to send""" + + merge_tags: Annotated[Dict[str, str], PropertyInfo(alias="mergeTags")] + """Optional merge tags for personalization""" + + subject: str + """Email subject line""" diff --git a/src/unlayer/types/emails_v1_send_create_response.py b/src/unlayer/types/emails_v1_send_create_response.py new file mode 100644 index 0000000..1ff2c5d --- /dev/null +++ b/src/unlayer/types/emails_v1_send_create_response.py @@ -0,0 +1,17 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from pydantic import Field as FieldInfo + +from .._models import BaseModel + +__all__ = ["EmailsV1SendCreateResponse"] + + +class EmailsV1SendCreateResponse(BaseModel): + message_id: Optional[str] = FieldInfo(alias="messageId", default=None) + """Unique message identifier""" + + status: Optional[Literal["sent", "queued", "failed"]] = None diff --git a/src/unlayer/types/emails_v1_send_template_template_params.py b/src/unlayer/types/emails_v1_send_template_template_params.py new file mode 100644 index 0000000..d6afa22 --- /dev/null +++ b/src/unlayer/types/emails_v1_send_template_template_params.py @@ -0,0 +1,24 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Dict +from typing_extensions import Required, Annotated, TypedDict + +from .._utils import PropertyInfo + +__all__ = ["EmailsV1SendTemplateTemplateParams"] + + +class EmailsV1SendTemplateTemplateParams(TypedDict, total=False): + template_id: Required[Annotated[str, PropertyInfo(alias="templateId")]] + """ID of the template to use""" + + to: Required[str] + """Recipient email address""" + + merge_tags: Annotated[Dict[str, str], PropertyInfo(alias="mergeTags")] + """Optional merge tags for personalization""" + + subject: str + """Email subject line (optional, uses template default if not provided)""" diff --git a/src/unlayer/types/emails_v1_send_template_template_response.py b/src/unlayer/types/emails_v1_send_template_template_response.py new file mode 100644 index 0000000..66ef594 --- /dev/null +++ b/src/unlayer/types/emails_v1_send_template_template_response.py @@ -0,0 +1,17 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional +from typing_extensions import Literal + +from pydantic import Field as FieldInfo + +from .._models import BaseModel + +__all__ = ["EmailsV1SendTemplateTemplateResponse"] + + +class EmailsV1SendTemplateTemplateResponse(BaseModel): + message_id: Optional[str] = FieldInfo(alias="messageId", default=None) + """Unique message identifier""" + + status: Optional[Literal["sent", "queued", "failed"]] = None diff --git a/src/unlayer/types/page_render_create_params.py b/src/unlayer/types/page_render_create_params.py new file mode 100644 index 0000000..956be96 --- /dev/null +++ b/src/unlayer/types/page_render_create_params.py @@ -0,0 +1,18 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Dict +from typing_extensions import Required, Annotated, TypedDict + +from .._utils import PropertyInfo + +__all__ = ["PageRenderCreateParams"] + + +class PageRenderCreateParams(TypedDict, total=False): + design: Required[Dict[str, object]] + """Proprietary design format JSON""" + + merge_tags: Annotated[Dict[str, str], PropertyInfo(alias="mergeTags")] + """Optional merge tags for personalization""" diff --git a/src/unlayer/types/page_render_create_response.py b/src/unlayer/types/page_render_create_response.py new file mode 100644 index 0000000..385ed69 --- /dev/null +++ b/src/unlayer/types/page_render_create_response.py @@ -0,0 +1,12 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional + +from .._models import BaseModel + +__all__ = ["PageRenderCreateResponse"] + + +class PageRenderCreateResponse(BaseModel): + html: Optional[str] = None + """Rendered HTML content""" diff --git a/src/unlayer/types/pages_v1_render_create_params.py b/src/unlayer/types/pages_v1_render_create_params.py new file mode 100644 index 0000000..605f9f7 --- /dev/null +++ b/src/unlayer/types/pages_v1_render_create_params.py @@ -0,0 +1,18 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Dict +from typing_extensions import Required, Annotated, TypedDict + +from .._utils import PropertyInfo + +__all__ = ["PagesV1RenderCreateParams"] + + +class PagesV1RenderCreateParams(TypedDict, total=False): + design: Required[Dict[str, object]] + """Proprietary design format JSON""" + + merge_tags: Annotated[Dict[str, str], PropertyInfo(alias="mergeTags")] + """Optional merge tags for personalization""" diff --git a/src/unlayer/types/pages_v1_render_create_response.py b/src/unlayer/types/pages_v1_render_create_response.py new file mode 100644 index 0000000..3c8e001 --- /dev/null +++ b/src/unlayer/types/pages_v1_render_create_response.py @@ -0,0 +1,12 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional + +from .._models import BaseModel + +__all__ = ["PagesV1RenderCreateResponse"] + + +class PagesV1RenderCreateResponse(BaseModel): + html: Optional[str] = None + """Rendered HTML content""" diff --git a/src/unlayer/types/project_api_keys_create_params.py b/src/unlayer/types/project_api_keys_create_params.py new file mode 100644 index 0000000..7cece83 --- /dev/null +++ b/src/unlayer/types/project_api_keys_create_params.py @@ -0,0 +1,17 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Required, TypedDict + +from .._types import SequenceNotStr + +__all__ = ["ProjectAPIKeysCreateParams"] + + +class ProjectAPIKeysCreateParams(TypedDict, total=False): + name: Required[str] + """Name for the API key""" + + domains: SequenceNotStr[str] + """Allowed domains for this API key""" diff --git a/src/unlayer/types/project_api_keys_create_response.py b/src/unlayer/types/project_api_keys_create_response.py new file mode 100644 index 0000000..ac406c2 --- /dev/null +++ b/src/unlayer/types/project_api_keys_create_response.py @@ -0,0 +1,28 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List, Optional +from datetime import datetime + +from pydantic import Field as FieldInfo + +from .._models import BaseModel + +__all__ = ["ProjectAPIKeysCreateResponse", "Data"] + + +class Data(BaseModel): + id: Optional[str] = None + + active: Optional[bool] = None + + created_at: Optional[datetime] = FieldInfo(alias="createdAt", default=None) + + domains: Optional[List[str]] = None + + key: Optional[str] = None + + name: Optional[str] = None + + +class ProjectAPIKeysCreateResponse(BaseModel): + data: Optional[Data] = None diff --git a/src/unlayer/types/project_api_keys_list_response.py b/src/unlayer/types/project_api_keys_list_response.py new file mode 100644 index 0000000..515b854 --- /dev/null +++ b/src/unlayer/types/project_api_keys_list_response.py @@ -0,0 +1,30 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List, Optional +from datetime import datetime + +from pydantic import Field as FieldInfo + +from .._models import BaseModel + +__all__ = ["ProjectAPIKeysListResponse", "Data"] + + +class Data(BaseModel): + id: Optional[str] = None + + active: Optional[bool] = None + + created_at: Optional[datetime] = FieldInfo(alias="createdAt", default=None) + + domains: Optional[List[str]] = None + + key: Optional[str] = None + + last_used: Optional[datetime] = FieldInfo(alias="lastUsed", default=None) + + name: Optional[str] = None + + +class ProjectAPIKeysListResponse(BaseModel): + data: Optional[List[Data]] = None diff --git a/src/unlayer/types/project_api_keys_retrieve_response.py b/src/unlayer/types/project_api_keys_retrieve_response.py new file mode 100644 index 0000000..f03855f --- /dev/null +++ b/src/unlayer/types/project_api_keys_retrieve_response.py @@ -0,0 +1,30 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List, Optional +from datetime import datetime + +from pydantic import Field as FieldInfo + +from .._models import BaseModel + +__all__ = ["ProjectAPIKeysRetrieveResponse", "Data"] + + +class Data(BaseModel): + id: Optional[str] = None + + active: Optional[bool] = None + + created_at: Optional[datetime] = FieldInfo(alias="createdAt", default=None) + + domains: Optional[List[str]] = None + + key: Optional[str] = None + + last_used: Optional[datetime] = FieldInfo(alias="lastUsed", default=None) + + name: Optional[str] = None + + +class ProjectAPIKeysRetrieveResponse(BaseModel): + data: Optional[Data] = None diff --git a/src/unlayer/types/project_api_keys_update_params.py b/src/unlayer/types/project_api_keys_update_params.py new file mode 100644 index 0000000..a9e2b65 --- /dev/null +++ b/src/unlayer/types/project_api_keys_update_params.py @@ -0,0 +1,20 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import TypedDict + +from .._types import SequenceNotStr + +__all__ = ["ProjectAPIKeysUpdateParams"] + + +class ProjectAPIKeysUpdateParams(TypedDict, total=False): + active: bool + """Whether the API key is active""" + + domains: SequenceNotStr[str] + """Updated allowed domains""" + + name: str + """Updated name for the API key""" diff --git a/src/unlayer/types/project_api_keys_update_response.py b/src/unlayer/types/project_api_keys_update_response.py new file mode 100644 index 0000000..437ccb7 --- /dev/null +++ b/src/unlayer/types/project_api_keys_update_response.py @@ -0,0 +1,30 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List, Optional +from datetime import datetime + +from pydantic import Field as FieldInfo + +from .._models import BaseModel + +__all__ = ["ProjectAPIKeysUpdateResponse", "Data"] + + +class Data(BaseModel): + id: Optional[str] = None + + active: Optional[bool] = None + + created_at: Optional[datetime] = FieldInfo(alias="createdAt", default=None) + + domains: Optional[List[str]] = None + + key: Optional[str] = None + + last_used: Optional[datetime] = FieldInfo(alias="lastUsed", default=None) + + name: Optional[str] = None + + +class ProjectAPIKeysUpdateResponse(BaseModel): + data: Optional[Data] = None diff --git a/src/unlayer/types/project_current_list_response.py b/src/unlayer/types/project_current_list_response.py new file mode 100644 index 0000000..17aaf94 --- /dev/null +++ b/src/unlayer/types/project_current_list_response.py @@ -0,0 +1,32 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional +from datetime import datetime + +from pydantic import Field as FieldInfo + +from .._models import BaseModel + +__all__ = ["ProjectCurrentListResponse", "Data", "DataWorkspace"] + + +class DataWorkspace(BaseModel): + id: Optional[float] = None + + name: Optional[str] = None + + +class Data(BaseModel): + id: Optional[float] = None + + created_at: Optional[datetime] = FieldInfo(alias="createdAt", default=None) + + name: Optional[str] = None + + status: Optional[str] = None + + workspace: Optional[DataWorkspace] = None + + +class ProjectCurrentListResponse(BaseModel): + data: Optional[Data] = None diff --git a/src/unlayer/types/project_domains_create_params.py b/src/unlayer/types/project_domains_create_params.py new file mode 100644 index 0000000..f20e9d3 --- /dev/null +++ b/src/unlayer/types/project_domains_create_params.py @@ -0,0 +1,12 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Required, TypedDict + +__all__ = ["ProjectDomainsCreateParams"] + + +class ProjectDomainsCreateParams(TypedDict, total=False): + domain: Required[str] + """Domain name to add""" diff --git a/src/unlayer/types/project_domains_create_response.py b/src/unlayer/types/project_domains_create_response.py new file mode 100644 index 0000000..530a679 --- /dev/null +++ b/src/unlayer/types/project_domains_create_response.py @@ -0,0 +1,26 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional +from datetime import datetime + +from pydantic import Field as FieldInfo + +from .._models import BaseModel + +__all__ = ["ProjectDomainsCreateResponse", "Data"] + + +class Data(BaseModel): + id: Optional[str] = None + + created_at: Optional[datetime] = FieldInfo(alias="createdAt", default=None) + + domain: Optional[str] = None + + status: Optional[str] = None + + verified: Optional[bool] = None + + +class ProjectDomainsCreateResponse(BaseModel): + data: Optional[Data] = None diff --git a/src/unlayer/types/project_domains_list_response.py b/src/unlayer/types/project_domains_list_response.py new file mode 100644 index 0000000..c0b0d06 --- /dev/null +++ b/src/unlayer/types/project_domains_list_response.py @@ -0,0 +1,27 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List, Optional +from datetime import datetime +from typing_extensions import Literal + +from pydantic import Field as FieldInfo + +from .._models import BaseModel + +__all__ = ["ProjectDomainsListResponse", "Data"] + + +class Data(BaseModel): + id: Optional[str] = None + + created_at: Optional[datetime] = FieldInfo(alias="createdAt", default=None) + + domain: Optional[str] = None + + status: Optional[Literal["active", "pending", "failed"]] = None + + verified: Optional[bool] = None + + +class ProjectDomainsListResponse(BaseModel): + data: Optional[List[Data]] = None diff --git a/src/unlayer/types/project_domains_retrieve_response.py b/src/unlayer/types/project_domains_retrieve_response.py new file mode 100644 index 0000000..1d446b7 --- /dev/null +++ b/src/unlayer/types/project_domains_retrieve_response.py @@ -0,0 +1,26 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional +from datetime import datetime + +from pydantic import Field as FieldInfo + +from .._models import BaseModel + +__all__ = ["ProjectDomainsRetrieveResponse", "Data"] + + +class Data(BaseModel): + id: Optional[str] = None + + created_at: Optional[datetime] = FieldInfo(alias="createdAt", default=None) + + domain: Optional[str] = None + + status: Optional[str] = None + + verified: Optional[bool] = None + + +class ProjectDomainsRetrieveResponse(BaseModel): + data: Optional[Data] = None diff --git a/src/unlayer/types/project_domains_update_params.py b/src/unlayer/types/project_domains_update_params.py new file mode 100644 index 0000000..a9f7599 --- /dev/null +++ b/src/unlayer/types/project_domains_update_params.py @@ -0,0 +1,12 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import TypedDict + +__all__ = ["ProjectDomainsUpdateParams"] + + +class ProjectDomainsUpdateParams(TypedDict, total=False): + domain: str + """Updated domain name""" diff --git a/src/unlayer/types/project_domains_update_response.py b/src/unlayer/types/project_domains_update_response.py new file mode 100644 index 0000000..0bb78ed --- /dev/null +++ b/src/unlayer/types/project_domains_update_response.py @@ -0,0 +1,26 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional +from datetime import datetime + +from pydantic import Field as FieldInfo + +from .._models import BaseModel + +__all__ = ["ProjectDomainsUpdateResponse", "Data"] + + +class Data(BaseModel): + id: Optional[str] = None + + created_at: Optional[datetime] = FieldInfo(alias="createdAt", default=None) + + domain: Optional[str] = None + + status: Optional[str] = None + + verified: Optional[bool] = None + + +class ProjectDomainsUpdateResponse(BaseModel): + data: Optional[Data] = None diff --git a/src/unlayer/types/project_templates_create_params.py b/src/unlayer/types/project_templates_create_params.py new file mode 100644 index 0000000..2ae8024 --- /dev/null +++ b/src/unlayer/types/project_templates_create_params.py @@ -0,0 +1,18 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Required, TypedDict + +__all__ = ["ProjectTemplatesCreateParams"] + + +class ProjectTemplatesCreateParams(TypedDict, total=False): + name: Required[str] + """Template name""" + + body: str + """Email body content""" + + subject: str + """Email subject line""" diff --git a/src/unlayer/types/project_templates_create_response.py b/src/unlayer/types/project_templates_create_response.py new file mode 100644 index 0000000..b5b309f --- /dev/null +++ b/src/unlayer/types/project_templates_create_response.py @@ -0,0 +1,28 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional +from datetime import datetime + +from pydantic import Field as FieldInfo + +from .._models import BaseModel + +__all__ = ["ProjectTemplatesCreateResponse", "Data"] + + +class Data(BaseModel): + id: Optional[str] = None + + body: Optional[str] = None + + created_at: Optional[datetime] = FieldInfo(alias="createdAt", default=None) + + name: Optional[str] = None + + subject: Optional[str] = None + + updated_at: Optional[datetime] = FieldInfo(alias="updatedAt", default=None) + + +class ProjectTemplatesCreateResponse(BaseModel): + data: Optional[Data] = None diff --git a/src/unlayer/types/project_templates_list_response.py b/src/unlayer/types/project_templates_list_response.py new file mode 100644 index 0000000..72ab756 --- /dev/null +++ b/src/unlayer/types/project_templates_list_response.py @@ -0,0 +1,28 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List, Optional +from datetime import datetime + +from pydantic import Field as FieldInfo + +from .._models import BaseModel + +__all__ = ["ProjectTemplatesListResponse", "Data"] + + +class Data(BaseModel): + id: Optional[str] = None + + body: Optional[str] = None + + created_at: Optional[datetime] = FieldInfo(alias="createdAt", default=None) + + name: Optional[str] = None + + subject: Optional[str] = None + + updated_at: Optional[datetime] = FieldInfo(alias="updatedAt", default=None) + + +class ProjectTemplatesListResponse(BaseModel): + data: Optional[List[Data]] = None diff --git a/src/unlayer/types/project_templates_retrieve_response.py b/src/unlayer/types/project_templates_retrieve_response.py new file mode 100644 index 0000000..25fd87b --- /dev/null +++ b/src/unlayer/types/project_templates_retrieve_response.py @@ -0,0 +1,28 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional +from datetime import datetime + +from pydantic import Field as FieldInfo + +from .._models import BaseModel + +__all__ = ["ProjectTemplatesRetrieveResponse", "Data"] + + +class Data(BaseModel): + id: Optional[str] = None + + body: Optional[str] = None + + created_at: Optional[datetime] = FieldInfo(alias="createdAt", default=None) + + name: Optional[str] = None + + subject: Optional[str] = None + + updated_at: Optional[datetime] = FieldInfo(alias="updatedAt", default=None) + + +class ProjectTemplatesRetrieveResponse(BaseModel): + data: Optional[Data] = None diff --git a/src/unlayer/types/project_templates_update_params.py b/src/unlayer/types/project_templates_update_params.py new file mode 100644 index 0000000..f539067 --- /dev/null +++ b/src/unlayer/types/project_templates_update_params.py @@ -0,0 +1,18 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import TypedDict + +__all__ = ["ProjectTemplatesUpdateParams"] + + +class ProjectTemplatesUpdateParams(TypedDict, total=False): + body: str + """Updated email body content""" + + name: str + """Updated template name""" + + subject: str + """Updated email subject line""" diff --git a/src/unlayer/types/project_templates_update_response.py b/src/unlayer/types/project_templates_update_response.py new file mode 100644 index 0000000..565e90c --- /dev/null +++ b/src/unlayer/types/project_templates_update_response.py @@ -0,0 +1,28 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional +from datetime import datetime + +from pydantic import Field as FieldInfo + +from .._models import BaseModel + +__all__ = ["ProjectTemplatesUpdateResponse", "Data"] + + +class Data(BaseModel): + id: Optional[str] = None + + body: Optional[str] = None + + created_at: Optional[datetime] = FieldInfo(alias="createdAt", default=None) + + name: Optional[str] = None + + subject: Optional[str] = None + + updated_at: Optional[datetime] = FieldInfo(alias="updatedAt", default=None) + + +class ProjectTemplatesUpdateResponse(BaseModel): + data: Optional[Data] = None diff --git a/src/unlayer/types/project_v1_api_keys_create_params.py b/src/unlayer/types/project_v1_api_keys_create_params.py new file mode 100644 index 0000000..f1111f3 --- /dev/null +++ b/src/unlayer/types/project_v1_api_keys_create_params.py @@ -0,0 +1,17 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Required, TypedDict + +from .._types import SequenceNotStr + +__all__ = ["ProjectV1APIKeysCreateParams"] + + +class ProjectV1APIKeysCreateParams(TypedDict, total=False): + name: Required[str] + """Name for the API key""" + + domains: SequenceNotStr[str] + """Allowed domains for this API key""" diff --git a/src/unlayer/types/project_v1_api_keys_create_response.py b/src/unlayer/types/project_v1_api_keys_create_response.py new file mode 100644 index 0000000..3983423 --- /dev/null +++ b/src/unlayer/types/project_v1_api_keys_create_response.py @@ -0,0 +1,28 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List, Optional +from datetime import datetime + +from pydantic import Field as FieldInfo + +from .._models import BaseModel + +__all__ = ["ProjectV1APIKeysCreateResponse", "Data"] + + +class Data(BaseModel): + id: Optional[str] = None + + active: Optional[bool] = None + + created_at: Optional[datetime] = FieldInfo(alias="createdAt", default=None) + + domains: Optional[List[str]] = None + + key: Optional[str] = None + + name: Optional[str] = None + + +class ProjectV1APIKeysCreateResponse(BaseModel): + data: Optional[Data] = None diff --git a/src/unlayer/types/project_v1_api_keys_list_response.py b/src/unlayer/types/project_v1_api_keys_list_response.py new file mode 100644 index 0000000..8dcaa2f --- /dev/null +++ b/src/unlayer/types/project_v1_api_keys_list_response.py @@ -0,0 +1,30 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List, Optional +from datetime import datetime + +from pydantic import Field as FieldInfo + +from .._models import BaseModel + +__all__ = ["ProjectV1APIKeysListResponse", "Data"] + + +class Data(BaseModel): + id: Optional[str] = None + + active: Optional[bool] = None + + created_at: Optional[datetime] = FieldInfo(alias="createdAt", default=None) + + domains: Optional[List[str]] = None + + key: Optional[str] = None + + last_used: Optional[datetime] = FieldInfo(alias="lastUsed", default=None) + + name: Optional[str] = None + + +class ProjectV1APIKeysListResponse(BaseModel): + data: Optional[List[Data]] = None diff --git a/src/unlayer/types/project_v1_api_keys_retrieve_response.py b/src/unlayer/types/project_v1_api_keys_retrieve_response.py new file mode 100644 index 0000000..b27fbf6 --- /dev/null +++ b/src/unlayer/types/project_v1_api_keys_retrieve_response.py @@ -0,0 +1,30 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List, Optional +from datetime import datetime + +from pydantic import Field as FieldInfo + +from .._models import BaseModel + +__all__ = ["ProjectV1APIKeysRetrieveResponse", "Data"] + + +class Data(BaseModel): + id: Optional[str] = None + + active: Optional[bool] = None + + created_at: Optional[datetime] = FieldInfo(alias="createdAt", default=None) + + domains: Optional[List[str]] = None + + key: Optional[str] = None + + last_used: Optional[datetime] = FieldInfo(alias="lastUsed", default=None) + + name: Optional[str] = None + + +class ProjectV1APIKeysRetrieveResponse(BaseModel): + data: Optional[Data] = None diff --git a/src/unlayer/types/project_v1_api_keys_update_params.py b/src/unlayer/types/project_v1_api_keys_update_params.py new file mode 100644 index 0000000..16cf1af --- /dev/null +++ b/src/unlayer/types/project_v1_api_keys_update_params.py @@ -0,0 +1,20 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import TypedDict + +from .._types import SequenceNotStr + +__all__ = ["ProjectV1APIKeysUpdateParams"] + + +class ProjectV1APIKeysUpdateParams(TypedDict, total=False): + active: bool + """Whether the API key is active""" + + domains: SequenceNotStr[str] + """Updated allowed domains""" + + name: str + """Updated name for the API key""" diff --git a/src/unlayer/types/project_v1_api_keys_update_response.py b/src/unlayer/types/project_v1_api_keys_update_response.py new file mode 100644 index 0000000..75f61e3 --- /dev/null +++ b/src/unlayer/types/project_v1_api_keys_update_response.py @@ -0,0 +1,30 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List, Optional +from datetime import datetime + +from pydantic import Field as FieldInfo + +from .._models import BaseModel + +__all__ = ["ProjectV1APIKeysUpdateResponse", "Data"] + + +class Data(BaseModel): + id: Optional[str] = None + + active: Optional[bool] = None + + created_at: Optional[datetime] = FieldInfo(alias="createdAt", default=None) + + domains: Optional[List[str]] = None + + key: Optional[str] = None + + last_used: Optional[datetime] = FieldInfo(alias="lastUsed", default=None) + + name: Optional[str] = None + + +class ProjectV1APIKeysUpdateResponse(BaseModel): + data: Optional[Data] = None diff --git a/src/unlayer/types/project_v1_current_list_response.py b/src/unlayer/types/project_v1_current_list_response.py new file mode 100644 index 0000000..bec0ada --- /dev/null +++ b/src/unlayer/types/project_v1_current_list_response.py @@ -0,0 +1,32 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional +from datetime import datetime + +from pydantic import Field as FieldInfo + +from .._models import BaseModel + +__all__ = ["ProjectV1CurrentListResponse", "Data", "DataWorkspace"] + + +class DataWorkspace(BaseModel): + id: Optional[float] = None + + name: Optional[str] = None + + +class Data(BaseModel): + id: Optional[float] = None + + created_at: Optional[datetime] = FieldInfo(alias="createdAt", default=None) + + name: Optional[str] = None + + status: Optional[str] = None + + workspace: Optional[DataWorkspace] = None + + +class ProjectV1CurrentListResponse(BaseModel): + data: Optional[Data] = None diff --git a/src/unlayer/types/project_v1_domains_create_params.py b/src/unlayer/types/project_v1_domains_create_params.py new file mode 100644 index 0000000..8ba07c6 --- /dev/null +++ b/src/unlayer/types/project_v1_domains_create_params.py @@ -0,0 +1,12 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Required, TypedDict + +__all__ = ["ProjectV1DomainsCreateParams"] + + +class ProjectV1DomainsCreateParams(TypedDict, total=False): + domain: Required[str] + """Domain name to add""" diff --git a/src/unlayer/types/project_v1_domains_create_response.py b/src/unlayer/types/project_v1_domains_create_response.py new file mode 100644 index 0000000..31325f3 --- /dev/null +++ b/src/unlayer/types/project_v1_domains_create_response.py @@ -0,0 +1,26 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional +from datetime import datetime + +from pydantic import Field as FieldInfo + +from .._models import BaseModel + +__all__ = ["ProjectV1DomainsCreateResponse", "Data"] + + +class Data(BaseModel): + id: Optional[str] = None + + created_at: Optional[datetime] = FieldInfo(alias="createdAt", default=None) + + domain: Optional[str] = None + + status: Optional[str] = None + + verified: Optional[bool] = None + + +class ProjectV1DomainsCreateResponse(BaseModel): + data: Optional[Data] = None diff --git a/src/unlayer/types/project_v1_domains_list_response.py b/src/unlayer/types/project_v1_domains_list_response.py new file mode 100644 index 0000000..134a6ab --- /dev/null +++ b/src/unlayer/types/project_v1_domains_list_response.py @@ -0,0 +1,27 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List, Optional +from datetime import datetime +from typing_extensions import Literal + +from pydantic import Field as FieldInfo + +from .._models import BaseModel + +__all__ = ["ProjectV1DomainsListResponse", "Data"] + + +class Data(BaseModel): + id: Optional[str] = None + + created_at: Optional[datetime] = FieldInfo(alias="createdAt", default=None) + + domain: Optional[str] = None + + status: Optional[Literal["active", "pending", "failed"]] = None + + verified: Optional[bool] = None + + +class ProjectV1DomainsListResponse(BaseModel): + data: Optional[List[Data]] = None diff --git a/src/unlayer/types/project_v1_domains_retrieve_response.py b/src/unlayer/types/project_v1_domains_retrieve_response.py new file mode 100644 index 0000000..cd3fbf2 --- /dev/null +++ b/src/unlayer/types/project_v1_domains_retrieve_response.py @@ -0,0 +1,26 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional +from datetime import datetime + +from pydantic import Field as FieldInfo + +from .._models import BaseModel + +__all__ = ["ProjectV1DomainsRetrieveResponse", "Data"] + + +class Data(BaseModel): + id: Optional[str] = None + + created_at: Optional[datetime] = FieldInfo(alias="createdAt", default=None) + + domain: Optional[str] = None + + status: Optional[str] = None + + verified: Optional[bool] = None + + +class ProjectV1DomainsRetrieveResponse(BaseModel): + data: Optional[Data] = None diff --git a/src/unlayer/types/project_v1_domains_update_params.py b/src/unlayer/types/project_v1_domains_update_params.py new file mode 100644 index 0000000..851bef7 --- /dev/null +++ b/src/unlayer/types/project_v1_domains_update_params.py @@ -0,0 +1,12 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import TypedDict + +__all__ = ["ProjectV1DomainsUpdateParams"] + + +class ProjectV1DomainsUpdateParams(TypedDict, total=False): + domain: str + """Updated domain name""" diff --git a/src/unlayer/types/project_v1_domains_update_response.py b/src/unlayer/types/project_v1_domains_update_response.py new file mode 100644 index 0000000..23cbe30 --- /dev/null +++ b/src/unlayer/types/project_v1_domains_update_response.py @@ -0,0 +1,26 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional +from datetime import datetime + +from pydantic import Field as FieldInfo + +from .._models import BaseModel + +__all__ = ["ProjectV1DomainsUpdateResponse", "Data"] + + +class Data(BaseModel): + id: Optional[str] = None + + created_at: Optional[datetime] = FieldInfo(alias="createdAt", default=None) + + domain: Optional[str] = None + + status: Optional[str] = None + + verified: Optional[bool] = None + + +class ProjectV1DomainsUpdateResponse(BaseModel): + data: Optional[Data] = None diff --git a/src/unlayer/types/project_v1_templates_create_params.py b/src/unlayer/types/project_v1_templates_create_params.py new file mode 100644 index 0000000..eeeb406 --- /dev/null +++ b/src/unlayer/types/project_v1_templates_create_params.py @@ -0,0 +1,18 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Required, TypedDict + +__all__ = ["ProjectV1TemplatesCreateParams"] + + +class ProjectV1TemplatesCreateParams(TypedDict, total=False): + name: Required[str] + """Template name""" + + body: str + """Email body content""" + + subject: str + """Email subject line""" diff --git a/src/unlayer/types/project_v1_templates_create_response.py b/src/unlayer/types/project_v1_templates_create_response.py new file mode 100644 index 0000000..787e4a2 --- /dev/null +++ b/src/unlayer/types/project_v1_templates_create_response.py @@ -0,0 +1,28 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional +from datetime import datetime + +from pydantic import Field as FieldInfo + +from .._models import BaseModel + +__all__ = ["ProjectV1TemplatesCreateResponse", "Data"] + + +class Data(BaseModel): + id: Optional[str] = None + + body: Optional[str] = None + + created_at: Optional[datetime] = FieldInfo(alias="createdAt", default=None) + + name: Optional[str] = None + + subject: Optional[str] = None + + updated_at: Optional[datetime] = FieldInfo(alias="updatedAt", default=None) + + +class ProjectV1TemplatesCreateResponse(BaseModel): + data: Optional[Data] = None diff --git a/src/unlayer/types/project_v1_templates_list_response.py b/src/unlayer/types/project_v1_templates_list_response.py new file mode 100644 index 0000000..da39fa4 --- /dev/null +++ b/src/unlayer/types/project_v1_templates_list_response.py @@ -0,0 +1,28 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List, Optional +from datetime import datetime + +from pydantic import Field as FieldInfo + +from .._models import BaseModel + +__all__ = ["ProjectV1TemplatesListResponse", "Data"] + + +class Data(BaseModel): + id: Optional[str] = None + + body: Optional[str] = None + + created_at: Optional[datetime] = FieldInfo(alias="createdAt", default=None) + + name: Optional[str] = None + + subject: Optional[str] = None + + updated_at: Optional[datetime] = FieldInfo(alias="updatedAt", default=None) + + +class ProjectV1TemplatesListResponse(BaseModel): + data: Optional[List[Data]] = None diff --git a/src/unlayer/types/project_v1_templates_retrieve_response.py b/src/unlayer/types/project_v1_templates_retrieve_response.py new file mode 100644 index 0000000..15c6171 --- /dev/null +++ b/src/unlayer/types/project_v1_templates_retrieve_response.py @@ -0,0 +1,28 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional +from datetime import datetime + +from pydantic import Field as FieldInfo + +from .._models import BaseModel + +__all__ = ["ProjectV1TemplatesRetrieveResponse", "Data"] + + +class Data(BaseModel): + id: Optional[str] = None + + body: Optional[str] = None + + created_at: Optional[datetime] = FieldInfo(alias="createdAt", default=None) + + name: Optional[str] = None + + subject: Optional[str] = None + + updated_at: Optional[datetime] = FieldInfo(alias="updatedAt", default=None) + + +class ProjectV1TemplatesRetrieveResponse(BaseModel): + data: Optional[Data] = None diff --git a/src/unlayer/types/project_v1_templates_update_params.py b/src/unlayer/types/project_v1_templates_update_params.py new file mode 100644 index 0000000..f0caf44 --- /dev/null +++ b/src/unlayer/types/project_v1_templates_update_params.py @@ -0,0 +1,18 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import TypedDict + +__all__ = ["ProjectV1TemplatesUpdateParams"] + + +class ProjectV1TemplatesUpdateParams(TypedDict, total=False): + body: str + """Updated email body content""" + + name: str + """Updated template name""" + + subject: str + """Updated email subject line""" diff --git a/src/unlayer/types/project_v1_templates_update_response.py b/src/unlayer/types/project_v1_templates_update_response.py new file mode 100644 index 0000000..5445311 --- /dev/null +++ b/src/unlayer/types/project_v1_templates_update_response.py @@ -0,0 +1,28 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional +from datetime import datetime + +from pydantic import Field as FieldInfo + +from .._models import BaseModel + +__all__ = ["ProjectV1TemplatesUpdateResponse", "Data"] + + +class Data(BaseModel): + id: Optional[str] = None + + body: Optional[str] = None + + created_at: Optional[datetime] = FieldInfo(alias="createdAt", default=None) + + name: Optional[str] = None + + subject: Optional[str] = None + + updated_at: Optional[datetime] = FieldInfo(alias="updatedAt", default=None) + + +class ProjectV1TemplatesUpdateResponse(BaseModel): + data: Optional[Data] = None diff --git a/tests/__init__.py b/tests/__init__.py new file mode 100644 index 0000000..fd8019a --- /dev/null +++ b/tests/__init__.py @@ -0,0 +1 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. diff --git a/tests/api_resources/__init__.py b/tests/api_resources/__init__.py new file mode 100644 index 0000000..fd8019a --- /dev/null +++ b/tests/api_resources/__init__.py @@ -0,0 +1 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. diff --git a/tests/api_resources/test_documents.py b/tests/api_resources/test_documents.py new file mode 100644 index 0000000..cc5084c --- /dev/null +++ b/tests/api_resources/test_documents.py @@ -0,0 +1,292 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import os +from typing import Any, cast + +import pytest + +from unlayer import Unlayer, AsyncUnlayer +from tests.utils import assert_matches_type +from unlayer.types import ( + DocumentGenerateCreateResponse, + DocumentDocumentsRetrieveResponse, + DocumentGenerateTemplateTemplateResponse, +) + +base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") + + +class TestDocuments: + parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) + + @parametrize + def test_method_documents_retrieve(self, client: Unlayer) -> None: + document = client.documents.documents_retrieve( + "id", + ) + assert_matches_type(DocumentDocumentsRetrieveResponse, document, path=["response"]) + + @parametrize + def test_raw_response_documents_retrieve(self, client: Unlayer) -> None: + response = client.documents.with_raw_response.documents_retrieve( + "id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + document = response.parse() + assert_matches_type(DocumentDocumentsRetrieveResponse, document, path=["response"]) + + @parametrize + def test_streaming_response_documents_retrieve(self, client: Unlayer) -> None: + with client.documents.with_streaming_response.documents_retrieve( + "id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + document = response.parse() + assert_matches_type(DocumentDocumentsRetrieveResponse, document, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_path_params_documents_retrieve(self, client: Unlayer) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): + client.documents.with_raw_response.documents_retrieve( + "", + ) + + @parametrize + def test_method_generate_create(self, client: Unlayer) -> None: + document = client.documents.generate_create( + design={ + "counters": "bar", + "body": "bar", + }, + ) + assert_matches_type(DocumentGenerateCreateResponse, document, path=["response"]) + + @parametrize + def test_method_generate_create_with_all_params(self, client: Unlayer) -> None: + document = client.documents.generate_create( + design={ + "counters": "bar", + "body": "bar", + }, + filename="filename", + html="html", + merge_tags={"foo": "string"}, + url="https://example.com", + ) + assert_matches_type(DocumentGenerateCreateResponse, document, path=["response"]) + + @parametrize + def test_raw_response_generate_create(self, client: Unlayer) -> None: + response = client.documents.with_raw_response.generate_create( + design={ + "counters": "bar", + "body": "bar", + }, + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + document = response.parse() + assert_matches_type(DocumentGenerateCreateResponse, document, path=["response"]) + + @parametrize + def test_streaming_response_generate_create(self, client: Unlayer) -> None: + with client.documents.with_streaming_response.generate_create( + design={ + "counters": "bar", + "body": "bar", + }, + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + document = response.parse() + assert_matches_type(DocumentGenerateCreateResponse, document, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_method_generate_template_template(self, client: Unlayer) -> None: + document = client.documents.generate_template_template( + template_id="templateId", + ) + assert_matches_type(DocumentGenerateTemplateTemplateResponse, document, path=["response"]) + + @parametrize + def test_method_generate_template_template_with_all_params(self, client: Unlayer) -> None: + document = client.documents.generate_template_template( + template_id="templateId", + filename="filename", + merge_tags={"foo": "string"}, + ) + assert_matches_type(DocumentGenerateTemplateTemplateResponse, document, path=["response"]) + + @parametrize + def test_raw_response_generate_template_template(self, client: Unlayer) -> None: + response = client.documents.with_raw_response.generate_template_template( + template_id="templateId", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + document = response.parse() + assert_matches_type(DocumentGenerateTemplateTemplateResponse, document, path=["response"]) + + @parametrize + def test_streaming_response_generate_template_template(self, client: Unlayer) -> None: + with client.documents.with_streaming_response.generate_template_template( + template_id="templateId", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + document = response.parse() + assert_matches_type(DocumentGenerateTemplateTemplateResponse, document, path=["response"]) + + assert cast(Any, response.is_closed) is True + + +class TestAsyncDocuments: + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) + + @parametrize + async def test_method_documents_retrieve(self, async_client: AsyncUnlayer) -> None: + document = await async_client.documents.documents_retrieve( + "id", + ) + assert_matches_type(DocumentDocumentsRetrieveResponse, document, path=["response"]) + + @parametrize + async def test_raw_response_documents_retrieve(self, async_client: AsyncUnlayer) -> None: + response = await async_client.documents.with_raw_response.documents_retrieve( + "id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + document = await response.parse() + assert_matches_type(DocumentDocumentsRetrieveResponse, document, path=["response"]) + + @parametrize + async def test_streaming_response_documents_retrieve(self, async_client: AsyncUnlayer) -> None: + async with async_client.documents.with_streaming_response.documents_retrieve( + "id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + document = await response.parse() + assert_matches_type(DocumentDocumentsRetrieveResponse, document, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_path_params_documents_retrieve(self, async_client: AsyncUnlayer) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): + await async_client.documents.with_raw_response.documents_retrieve( + "", + ) + + @parametrize + async def test_method_generate_create(self, async_client: AsyncUnlayer) -> None: + document = await async_client.documents.generate_create( + design={ + "counters": "bar", + "body": "bar", + }, + ) + assert_matches_type(DocumentGenerateCreateResponse, document, path=["response"]) + + @parametrize + async def test_method_generate_create_with_all_params(self, async_client: AsyncUnlayer) -> None: + document = await async_client.documents.generate_create( + design={ + "counters": "bar", + "body": "bar", + }, + filename="filename", + html="html", + merge_tags={"foo": "string"}, + url="https://example.com", + ) + assert_matches_type(DocumentGenerateCreateResponse, document, path=["response"]) + + @parametrize + async def test_raw_response_generate_create(self, async_client: AsyncUnlayer) -> None: + response = await async_client.documents.with_raw_response.generate_create( + design={ + "counters": "bar", + "body": "bar", + }, + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + document = await response.parse() + assert_matches_type(DocumentGenerateCreateResponse, document, path=["response"]) + + @parametrize + async def test_streaming_response_generate_create(self, async_client: AsyncUnlayer) -> None: + async with async_client.documents.with_streaming_response.generate_create( + design={ + "counters": "bar", + "body": "bar", + }, + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + document = await response.parse() + assert_matches_type(DocumentGenerateCreateResponse, document, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_method_generate_template_template(self, async_client: AsyncUnlayer) -> None: + document = await async_client.documents.generate_template_template( + template_id="templateId", + ) + assert_matches_type(DocumentGenerateTemplateTemplateResponse, document, path=["response"]) + + @parametrize + async def test_method_generate_template_template_with_all_params(self, async_client: AsyncUnlayer) -> None: + document = await async_client.documents.generate_template_template( + template_id="templateId", + filename="filename", + merge_tags={"foo": "string"}, + ) + assert_matches_type(DocumentGenerateTemplateTemplateResponse, document, path=["response"]) + + @parametrize + async def test_raw_response_generate_template_template(self, async_client: AsyncUnlayer) -> None: + response = await async_client.documents.with_raw_response.generate_template_template( + template_id="templateId", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + document = await response.parse() + assert_matches_type(DocumentGenerateTemplateTemplateResponse, document, path=["response"]) + + @parametrize + async def test_streaming_response_generate_template_template(self, async_client: AsyncUnlayer) -> None: + async with async_client.documents.with_streaming_response.generate_template_template( + template_id="templateId", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + document = await response.parse() + assert_matches_type(DocumentGenerateTemplateTemplateResponse, document, path=["response"]) + + assert cast(Any, response.is_closed) is True diff --git a/tests/api_resources/test_documents_v1.py b/tests/api_resources/test_documents_v1.py new file mode 100644 index 0000000..6c94054 --- /dev/null +++ b/tests/api_resources/test_documents_v1.py @@ -0,0 +1,292 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import os +from typing import Any, cast + +import pytest + +from unlayer import Unlayer, AsyncUnlayer +from tests.utils import assert_matches_type +from unlayer.types import ( + DocumentsV1GenerateCreateResponse, + DocumentsV1DocumentsRetrieveResponse, + DocumentsV1GenerateTemplateTemplateResponse, +) + +base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") + + +class TestDocumentsV1: + parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) + + @parametrize + def test_method_documents_retrieve(self, client: Unlayer) -> None: + documents_v1 = client.documents_v1.documents_retrieve( + "id", + ) + assert_matches_type(DocumentsV1DocumentsRetrieveResponse, documents_v1, path=["response"]) + + @parametrize + def test_raw_response_documents_retrieve(self, client: Unlayer) -> None: + response = client.documents_v1.with_raw_response.documents_retrieve( + "id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + documents_v1 = response.parse() + assert_matches_type(DocumentsV1DocumentsRetrieveResponse, documents_v1, path=["response"]) + + @parametrize + def test_streaming_response_documents_retrieve(self, client: Unlayer) -> None: + with client.documents_v1.with_streaming_response.documents_retrieve( + "id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + documents_v1 = response.parse() + assert_matches_type(DocumentsV1DocumentsRetrieveResponse, documents_v1, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_path_params_documents_retrieve(self, client: Unlayer) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): + client.documents_v1.with_raw_response.documents_retrieve( + "", + ) + + @parametrize + def test_method_generate_create(self, client: Unlayer) -> None: + documents_v1 = client.documents_v1.generate_create( + design={ + "counters": "bar", + "body": "bar", + }, + ) + assert_matches_type(DocumentsV1GenerateCreateResponse, documents_v1, path=["response"]) + + @parametrize + def test_method_generate_create_with_all_params(self, client: Unlayer) -> None: + documents_v1 = client.documents_v1.generate_create( + design={ + "counters": "bar", + "body": "bar", + }, + filename="filename", + html="html", + merge_tags={"foo": "string"}, + url="https://example.com", + ) + assert_matches_type(DocumentsV1GenerateCreateResponse, documents_v1, path=["response"]) + + @parametrize + def test_raw_response_generate_create(self, client: Unlayer) -> None: + response = client.documents_v1.with_raw_response.generate_create( + design={ + "counters": "bar", + "body": "bar", + }, + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + documents_v1 = response.parse() + assert_matches_type(DocumentsV1GenerateCreateResponse, documents_v1, path=["response"]) + + @parametrize + def test_streaming_response_generate_create(self, client: Unlayer) -> None: + with client.documents_v1.with_streaming_response.generate_create( + design={ + "counters": "bar", + "body": "bar", + }, + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + documents_v1 = response.parse() + assert_matches_type(DocumentsV1GenerateCreateResponse, documents_v1, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_method_generate_template_template(self, client: Unlayer) -> None: + documents_v1 = client.documents_v1.generate_template_template( + template_id="templateId", + ) + assert_matches_type(DocumentsV1GenerateTemplateTemplateResponse, documents_v1, path=["response"]) + + @parametrize + def test_method_generate_template_template_with_all_params(self, client: Unlayer) -> None: + documents_v1 = client.documents_v1.generate_template_template( + template_id="templateId", + filename="filename", + merge_tags={"foo": "string"}, + ) + assert_matches_type(DocumentsV1GenerateTemplateTemplateResponse, documents_v1, path=["response"]) + + @parametrize + def test_raw_response_generate_template_template(self, client: Unlayer) -> None: + response = client.documents_v1.with_raw_response.generate_template_template( + template_id="templateId", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + documents_v1 = response.parse() + assert_matches_type(DocumentsV1GenerateTemplateTemplateResponse, documents_v1, path=["response"]) + + @parametrize + def test_streaming_response_generate_template_template(self, client: Unlayer) -> None: + with client.documents_v1.with_streaming_response.generate_template_template( + template_id="templateId", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + documents_v1 = response.parse() + assert_matches_type(DocumentsV1GenerateTemplateTemplateResponse, documents_v1, path=["response"]) + + assert cast(Any, response.is_closed) is True + + +class TestAsyncDocumentsV1: + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) + + @parametrize + async def test_method_documents_retrieve(self, async_client: AsyncUnlayer) -> None: + documents_v1 = await async_client.documents_v1.documents_retrieve( + "id", + ) + assert_matches_type(DocumentsV1DocumentsRetrieveResponse, documents_v1, path=["response"]) + + @parametrize + async def test_raw_response_documents_retrieve(self, async_client: AsyncUnlayer) -> None: + response = await async_client.documents_v1.with_raw_response.documents_retrieve( + "id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + documents_v1 = await response.parse() + assert_matches_type(DocumentsV1DocumentsRetrieveResponse, documents_v1, path=["response"]) + + @parametrize + async def test_streaming_response_documents_retrieve(self, async_client: AsyncUnlayer) -> None: + async with async_client.documents_v1.with_streaming_response.documents_retrieve( + "id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + documents_v1 = await response.parse() + assert_matches_type(DocumentsV1DocumentsRetrieveResponse, documents_v1, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_path_params_documents_retrieve(self, async_client: AsyncUnlayer) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): + await async_client.documents_v1.with_raw_response.documents_retrieve( + "", + ) + + @parametrize + async def test_method_generate_create(self, async_client: AsyncUnlayer) -> None: + documents_v1 = await async_client.documents_v1.generate_create( + design={ + "counters": "bar", + "body": "bar", + }, + ) + assert_matches_type(DocumentsV1GenerateCreateResponse, documents_v1, path=["response"]) + + @parametrize + async def test_method_generate_create_with_all_params(self, async_client: AsyncUnlayer) -> None: + documents_v1 = await async_client.documents_v1.generate_create( + design={ + "counters": "bar", + "body": "bar", + }, + filename="filename", + html="html", + merge_tags={"foo": "string"}, + url="https://example.com", + ) + assert_matches_type(DocumentsV1GenerateCreateResponse, documents_v1, path=["response"]) + + @parametrize + async def test_raw_response_generate_create(self, async_client: AsyncUnlayer) -> None: + response = await async_client.documents_v1.with_raw_response.generate_create( + design={ + "counters": "bar", + "body": "bar", + }, + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + documents_v1 = await response.parse() + assert_matches_type(DocumentsV1GenerateCreateResponse, documents_v1, path=["response"]) + + @parametrize + async def test_streaming_response_generate_create(self, async_client: AsyncUnlayer) -> None: + async with async_client.documents_v1.with_streaming_response.generate_create( + design={ + "counters": "bar", + "body": "bar", + }, + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + documents_v1 = await response.parse() + assert_matches_type(DocumentsV1GenerateCreateResponse, documents_v1, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_method_generate_template_template(self, async_client: AsyncUnlayer) -> None: + documents_v1 = await async_client.documents_v1.generate_template_template( + template_id="templateId", + ) + assert_matches_type(DocumentsV1GenerateTemplateTemplateResponse, documents_v1, path=["response"]) + + @parametrize + async def test_method_generate_template_template_with_all_params(self, async_client: AsyncUnlayer) -> None: + documents_v1 = await async_client.documents_v1.generate_template_template( + template_id="templateId", + filename="filename", + merge_tags={"foo": "string"}, + ) + assert_matches_type(DocumentsV1GenerateTemplateTemplateResponse, documents_v1, path=["response"]) + + @parametrize + async def test_raw_response_generate_template_template(self, async_client: AsyncUnlayer) -> None: + response = await async_client.documents_v1.with_raw_response.generate_template_template( + template_id="templateId", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + documents_v1 = await response.parse() + assert_matches_type(DocumentsV1GenerateTemplateTemplateResponse, documents_v1, path=["response"]) + + @parametrize + async def test_streaming_response_generate_template_template(self, async_client: AsyncUnlayer) -> None: + async with async_client.documents_v1.with_streaming_response.generate_template_template( + template_id="templateId", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + documents_v1 = await response.parse() + assert_matches_type(DocumentsV1GenerateTemplateTemplateResponse, documents_v1, path=["response"]) + + assert cast(Any, response.is_closed) is True diff --git a/tests/api_resources/test_emails.py b/tests/api_resources/test_emails.py new file mode 100644 index 0000000..4fc6a86 --- /dev/null +++ b/tests/api_resources/test_emails.py @@ -0,0 +1,409 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import os +from typing import Any, cast + +import pytest + +from unlayer import Unlayer, AsyncUnlayer +from tests.utils import assert_matches_type +from unlayer.types import ( + EmailSendCreateResponse, + EmailRenderCreateResponse, + EmailEmailsRetrieveResponse, + EmailSendTemplateTemplateResponse, +) + +base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") + + +class TestEmails: + parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) + + @parametrize + def test_method_emails_retrieve(self, client: Unlayer) -> None: + email = client.emails.emails_retrieve( + "id", + ) + assert_matches_type(EmailEmailsRetrieveResponse, email, path=["response"]) + + @parametrize + def test_raw_response_emails_retrieve(self, client: Unlayer) -> None: + response = client.emails.with_raw_response.emails_retrieve( + "id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + email = response.parse() + assert_matches_type(EmailEmailsRetrieveResponse, email, path=["response"]) + + @parametrize + def test_streaming_response_emails_retrieve(self, client: Unlayer) -> None: + with client.emails.with_streaming_response.emails_retrieve( + "id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + email = response.parse() + assert_matches_type(EmailEmailsRetrieveResponse, email, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_path_params_emails_retrieve(self, client: Unlayer) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): + client.emails.with_raw_response.emails_retrieve( + "", + ) + + @parametrize + def test_method_render_create(self, client: Unlayer) -> None: + email = client.emails.render_create( + design={ + "counters": "bar", + "body": "bar", + }, + ) + assert_matches_type(EmailRenderCreateResponse, email, path=["response"]) + + @parametrize + def test_method_render_create_with_all_params(self, client: Unlayer) -> None: + email = client.emails.render_create( + design={ + "counters": "bar", + "body": "bar", + }, + merge_tags={"foo": "string"}, + ) + assert_matches_type(EmailRenderCreateResponse, email, path=["response"]) + + @parametrize + def test_raw_response_render_create(self, client: Unlayer) -> None: + response = client.emails.with_raw_response.render_create( + design={ + "counters": "bar", + "body": "bar", + }, + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + email = response.parse() + assert_matches_type(EmailRenderCreateResponse, email, path=["response"]) + + @parametrize + def test_streaming_response_render_create(self, client: Unlayer) -> None: + with client.emails.with_streaming_response.render_create( + design={ + "counters": "bar", + "body": "bar", + }, + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + email = response.parse() + assert_matches_type(EmailRenderCreateResponse, email, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_method_send_create(self, client: Unlayer) -> None: + email = client.emails.send_create( + design={ + "counters": "bar", + "body": "bar", + }, + to="test@example.com", + ) + assert_matches_type(EmailSendCreateResponse, email, path=["response"]) + + @parametrize + def test_method_send_create_with_all_params(self, client: Unlayer) -> None: + email = client.emails.send_create( + design={ + "counters": "bar", + "body": "bar", + }, + to="test@example.com", + html="html", + merge_tags={"foo": "string"}, + subject="Test Email", + ) + assert_matches_type(EmailSendCreateResponse, email, path=["response"]) + + @parametrize + def test_raw_response_send_create(self, client: Unlayer) -> None: + response = client.emails.with_raw_response.send_create( + design={ + "counters": "bar", + "body": "bar", + }, + to="test@example.com", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + email = response.parse() + assert_matches_type(EmailSendCreateResponse, email, path=["response"]) + + @parametrize + def test_streaming_response_send_create(self, client: Unlayer) -> None: + with client.emails.with_streaming_response.send_create( + design={ + "counters": "bar", + "body": "bar", + }, + to="test@example.com", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + email = response.parse() + assert_matches_type(EmailSendCreateResponse, email, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_method_send_template_template(self, client: Unlayer) -> None: + email = client.emails.send_template_template( + template_id="templateId", + to="dev@stainless.com", + ) + assert_matches_type(EmailSendTemplateTemplateResponse, email, path=["response"]) + + @parametrize + def test_method_send_template_template_with_all_params(self, client: Unlayer) -> None: + email = client.emails.send_template_template( + template_id="templateId", + to="dev@stainless.com", + merge_tags={"foo": "string"}, + subject="subject", + ) + assert_matches_type(EmailSendTemplateTemplateResponse, email, path=["response"]) + + @parametrize + def test_raw_response_send_template_template(self, client: Unlayer) -> None: + response = client.emails.with_raw_response.send_template_template( + template_id="templateId", + to="dev@stainless.com", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + email = response.parse() + assert_matches_type(EmailSendTemplateTemplateResponse, email, path=["response"]) + + @parametrize + def test_streaming_response_send_template_template(self, client: Unlayer) -> None: + with client.emails.with_streaming_response.send_template_template( + template_id="templateId", + to="dev@stainless.com", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + email = response.parse() + assert_matches_type(EmailSendTemplateTemplateResponse, email, path=["response"]) + + assert cast(Any, response.is_closed) is True + + +class TestAsyncEmails: + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) + + @parametrize + async def test_method_emails_retrieve(self, async_client: AsyncUnlayer) -> None: + email = await async_client.emails.emails_retrieve( + "id", + ) + assert_matches_type(EmailEmailsRetrieveResponse, email, path=["response"]) + + @parametrize + async def test_raw_response_emails_retrieve(self, async_client: AsyncUnlayer) -> None: + response = await async_client.emails.with_raw_response.emails_retrieve( + "id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + email = await response.parse() + assert_matches_type(EmailEmailsRetrieveResponse, email, path=["response"]) + + @parametrize + async def test_streaming_response_emails_retrieve(self, async_client: AsyncUnlayer) -> None: + async with async_client.emails.with_streaming_response.emails_retrieve( + "id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + email = await response.parse() + assert_matches_type(EmailEmailsRetrieveResponse, email, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_path_params_emails_retrieve(self, async_client: AsyncUnlayer) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): + await async_client.emails.with_raw_response.emails_retrieve( + "", + ) + + @parametrize + async def test_method_render_create(self, async_client: AsyncUnlayer) -> None: + email = await async_client.emails.render_create( + design={ + "counters": "bar", + "body": "bar", + }, + ) + assert_matches_type(EmailRenderCreateResponse, email, path=["response"]) + + @parametrize + async def test_method_render_create_with_all_params(self, async_client: AsyncUnlayer) -> None: + email = await async_client.emails.render_create( + design={ + "counters": "bar", + "body": "bar", + }, + merge_tags={"foo": "string"}, + ) + assert_matches_type(EmailRenderCreateResponse, email, path=["response"]) + + @parametrize + async def test_raw_response_render_create(self, async_client: AsyncUnlayer) -> None: + response = await async_client.emails.with_raw_response.render_create( + design={ + "counters": "bar", + "body": "bar", + }, + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + email = await response.parse() + assert_matches_type(EmailRenderCreateResponse, email, path=["response"]) + + @parametrize + async def test_streaming_response_render_create(self, async_client: AsyncUnlayer) -> None: + async with async_client.emails.with_streaming_response.render_create( + design={ + "counters": "bar", + "body": "bar", + }, + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + email = await response.parse() + assert_matches_type(EmailRenderCreateResponse, email, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_method_send_create(self, async_client: AsyncUnlayer) -> None: + email = await async_client.emails.send_create( + design={ + "counters": "bar", + "body": "bar", + }, + to="test@example.com", + ) + assert_matches_type(EmailSendCreateResponse, email, path=["response"]) + + @parametrize + async def test_method_send_create_with_all_params(self, async_client: AsyncUnlayer) -> None: + email = await async_client.emails.send_create( + design={ + "counters": "bar", + "body": "bar", + }, + to="test@example.com", + html="html", + merge_tags={"foo": "string"}, + subject="Test Email", + ) + assert_matches_type(EmailSendCreateResponse, email, path=["response"]) + + @parametrize + async def test_raw_response_send_create(self, async_client: AsyncUnlayer) -> None: + response = await async_client.emails.with_raw_response.send_create( + design={ + "counters": "bar", + "body": "bar", + }, + to="test@example.com", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + email = await response.parse() + assert_matches_type(EmailSendCreateResponse, email, path=["response"]) + + @parametrize + async def test_streaming_response_send_create(self, async_client: AsyncUnlayer) -> None: + async with async_client.emails.with_streaming_response.send_create( + design={ + "counters": "bar", + "body": "bar", + }, + to="test@example.com", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + email = await response.parse() + assert_matches_type(EmailSendCreateResponse, email, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_method_send_template_template(self, async_client: AsyncUnlayer) -> None: + email = await async_client.emails.send_template_template( + template_id="templateId", + to="dev@stainless.com", + ) + assert_matches_type(EmailSendTemplateTemplateResponse, email, path=["response"]) + + @parametrize + async def test_method_send_template_template_with_all_params(self, async_client: AsyncUnlayer) -> None: + email = await async_client.emails.send_template_template( + template_id="templateId", + to="dev@stainless.com", + merge_tags={"foo": "string"}, + subject="subject", + ) + assert_matches_type(EmailSendTemplateTemplateResponse, email, path=["response"]) + + @parametrize + async def test_raw_response_send_template_template(self, async_client: AsyncUnlayer) -> None: + response = await async_client.emails.with_raw_response.send_template_template( + template_id="templateId", + to="dev@stainless.com", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + email = await response.parse() + assert_matches_type(EmailSendTemplateTemplateResponse, email, path=["response"]) + + @parametrize + async def test_streaming_response_send_template_template(self, async_client: AsyncUnlayer) -> None: + async with async_client.emails.with_streaming_response.send_template_template( + template_id="templateId", + to="dev@stainless.com", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + email = await response.parse() + assert_matches_type(EmailSendTemplateTemplateResponse, email, path=["response"]) + + assert cast(Any, response.is_closed) is True diff --git a/tests/api_resources/test_emails_v1.py b/tests/api_resources/test_emails_v1.py new file mode 100644 index 0000000..d728ca3 --- /dev/null +++ b/tests/api_resources/test_emails_v1.py @@ -0,0 +1,409 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import os +from typing import Any, cast + +import pytest + +from unlayer import Unlayer, AsyncUnlayer +from tests.utils import assert_matches_type +from unlayer.types import ( + EmailsV1SendCreateResponse, + EmailsV1RenderCreateResponse, + EmailsV1EmailsRetrieveResponse, + EmailsV1SendTemplateTemplateResponse, +) + +base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") + + +class TestEmailsV1: + parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) + + @parametrize + def test_method_emails_retrieve(self, client: Unlayer) -> None: + emails_v1 = client.emails_v1.emails_retrieve( + "id", + ) + assert_matches_type(EmailsV1EmailsRetrieveResponse, emails_v1, path=["response"]) + + @parametrize + def test_raw_response_emails_retrieve(self, client: Unlayer) -> None: + response = client.emails_v1.with_raw_response.emails_retrieve( + "id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + emails_v1 = response.parse() + assert_matches_type(EmailsV1EmailsRetrieveResponse, emails_v1, path=["response"]) + + @parametrize + def test_streaming_response_emails_retrieve(self, client: Unlayer) -> None: + with client.emails_v1.with_streaming_response.emails_retrieve( + "id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + emails_v1 = response.parse() + assert_matches_type(EmailsV1EmailsRetrieveResponse, emails_v1, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_path_params_emails_retrieve(self, client: Unlayer) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): + client.emails_v1.with_raw_response.emails_retrieve( + "", + ) + + @parametrize + def test_method_render_create(self, client: Unlayer) -> None: + emails_v1 = client.emails_v1.render_create( + design={ + "counters": "bar", + "body": "bar", + }, + ) + assert_matches_type(EmailsV1RenderCreateResponse, emails_v1, path=["response"]) + + @parametrize + def test_method_render_create_with_all_params(self, client: Unlayer) -> None: + emails_v1 = client.emails_v1.render_create( + design={ + "counters": "bar", + "body": "bar", + }, + merge_tags={"foo": "string"}, + ) + assert_matches_type(EmailsV1RenderCreateResponse, emails_v1, path=["response"]) + + @parametrize + def test_raw_response_render_create(self, client: Unlayer) -> None: + response = client.emails_v1.with_raw_response.render_create( + design={ + "counters": "bar", + "body": "bar", + }, + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + emails_v1 = response.parse() + assert_matches_type(EmailsV1RenderCreateResponse, emails_v1, path=["response"]) + + @parametrize + def test_streaming_response_render_create(self, client: Unlayer) -> None: + with client.emails_v1.with_streaming_response.render_create( + design={ + "counters": "bar", + "body": "bar", + }, + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + emails_v1 = response.parse() + assert_matches_type(EmailsV1RenderCreateResponse, emails_v1, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_method_send_create(self, client: Unlayer) -> None: + emails_v1 = client.emails_v1.send_create( + design={ + "counters": "bar", + "body": "bar", + }, + to="test@example.com", + ) + assert_matches_type(EmailsV1SendCreateResponse, emails_v1, path=["response"]) + + @parametrize + def test_method_send_create_with_all_params(self, client: Unlayer) -> None: + emails_v1 = client.emails_v1.send_create( + design={ + "counters": "bar", + "body": "bar", + }, + to="test@example.com", + html="html", + merge_tags={"foo": "string"}, + subject="Test Email", + ) + assert_matches_type(EmailsV1SendCreateResponse, emails_v1, path=["response"]) + + @parametrize + def test_raw_response_send_create(self, client: Unlayer) -> None: + response = client.emails_v1.with_raw_response.send_create( + design={ + "counters": "bar", + "body": "bar", + }, + to="test@example.com", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + emails_v1 = response.parse() + assert_matches_type(EmailsV1SendCreateResponse, emails_v1, path=["response"]) + + @parametrize + def test_streaming_response_send_create(self, client: Unlayer) -> None: + with client.emails_v1.with_streaming_response.send_create( + design={ + "counters": "bar", + "body": "bar", + }, + to="test@example.com", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + emails_v1 = response.parse() + assert_matches_type(EmailsV1SendCreateResponse, emails_v1, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_method_send_template_template(self, client: Unlayer) -> None: + emails_v1 = client.emails_v1.send_template_template( + template_id="templateId", + to="dev@stainless.com", + ) + assert_matches_type(EmailsV1SendTemplateTemplateResponse, emails_v1, path=["response"]) + + @parametrize + def test_method_send_template_template_with_all_params(self, client: Unlayer) -> None: + emails_v1 = client.emails_v1.send_template_template( + template_id="templateId", + to="dev@stainless.com", + merge_tags={"foo": "string"}, + subject="subject", + ) + assert_matches_type(EmailsV1SendTemplateTemplateResponse, emails_v1, path=["response"]) + + @parametrize + def test_raw_response_send_template_template(self, client: Unlayer) -> None: + response = client.emails_v1.with_raw_response.send_template_template( + template_id="templateId", + to="dev@stainless.com", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + emails_v1 = response.parse() + assert_matches_type(EmailsV1SendTemplateTemplateResponse, emails_v1, path=["response"]) + + @parametrize + def test_streaming_response_send_template_template(self, client: Unlayer) -> None: + with client.emails_v1.with_streaming_response.send_template_template( + template_id="templateId", + to="dev@stainless.com", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + emails_v1 = response.parse() + assert_matches_type(EmailsV1SendTemplateTemplateResponse, emails_v1, path=["response"]) + + assert cast(Any, response.is_closed) is True + + +class TestAsyncEmailsV1: + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) + + @parametrize + async def test_method_emails_retrieve(self, async_client: AsyncUnlayer) -> None: + emails_v1 = await async_client.emails_v1.emails_retrieve( + "id", + ) + assert_matches_type(EmailsV1EmailsRetrieveResponse, emails_v1, path=["response"]) + + @parametrize + async def test_raw_response_emails_retrieve(self, async_client: AsyncUnlayer) -> None: + response = await async_client.emails_v1.with_raw_response.emails_retrieve( + "id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + emails_v1 = await response.parse() + assert_matches_type(EmailsV1EmailsRetrieveResponse, emails_v1, path=["response"]) + + @parametrize + async def test_streaming_response_emails_retrieve(self, async_client: AsyncUnlayer) -> None: + async with async_client.emails_v1.with_streaming_response.emails_retrieve( + "id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + emails_v1 = await response.parse() + assert_matches_type(EmailsV1EmailsRetrieveResponse, emails_v1, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_path_params_emails_retrieve(self, async_client: AsyncUnlayer) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): + await async_client.emails_v1.with_raw_response.emails_retrieve( + "", + ) + + @parametrize + async def test_method_render_create(self, async_client: AsyncUnlayer) -> None: + emails_v1 = await async_client.emails_v1.render_create( + design={ + "counters": "bar", + "body": "bar", + }, + ) + assert_matches_type(EmailsV1RenderCreateResponse, emails_v1, path=["response"]) + + @parametrize + async def test_method_render_create_with_all_params(self, async_client: AsyncUnlayer) -> None: + emails_v1 = await async_client.emails_v1.render_create( + design={ + "counters": "bar", + "body": "bar", + }, + merge_tags={"foo": "string"}, + ) + assert_matches_type(EmailsV1RenderCreateResponse, emails_v1, path=["response"]) + + @parametrize + async def test_raw_response_render_create(self, async_client: AsyncUnlayer) -> None: + response = await async_client.emails_v1.with_raw_response.render_create( + design={ + "counters": "bar", + "body": "bar", + }, + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + emails_v1 = await response.parse() + assert_matches_type(EmailsV1RenderCreateResponse, emails_v1, path=["response"]) + + @parametrize + async def test_streaming_response_render_create(self, async_client: AsyncUnlayer) -> None: + async with async_client.emails_v1.with_streaming_response.render_create( + design={ + "counters": "bar", + "body": "bar", + }, + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + emails_v1 = await response.parse() + assert_matches_type(EmailsV1RenderCreateResponse, emails_v1, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_method_send_create(self, async_client: AsyncUnlayer) -> None: + emails_v1 = await async_client.emails_v1.send_create( + design={ + "counters": "bar", + "body": "bar", + }, + to="test@example.com", + ) + assert_matches_type(EmailsV1SendCreateResponse, emails_v1, path=["response"]) + + @parametrize + async def test_method_send_create_with_all_params(self, async_client: AsyncUnlayer) -> None: + emails_v1 = await async_client.emails_v1.send_create( + design={ + "counters": "bar", + "body": "bar", + }, + to="test@example.com", + html="html", + merge_tags={"foo": "string"}, + subject="Test Email", + ) + assert_matches_type(EmailsV1SendCreateResponse, emails_v1, path=["response"]) + + @parametrize + async def test_raw_response_send_create(self, async_client: AsyncUnlayer) -> None: + response = await async_client.emails_v1.with_raw_response.send_create( + design={ + "counters": "bar", + "body": "bar", + }, + to="test@example.com", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + emails_v1 = await response.parse() + assert_matches_type(EmailsV1SendCreateResponse, emails_v1, path=["response"]) + + @parametrize + async def test_streaming_response_send_create(self, async_client: AsyncUnlayer) -> None: + async with async_client.emails_v1.with_streaming_response.send_create( + design={ + "counters": "bar", + "body": "bar", + }, + to="test@example.com", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + emails_v1 = await response.parse() + assert_matches_type(EmailsV1SendCreateResponse, emails_v1, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_method_send_template_template(self, async_client: AsyncUnlayer) -> None: + emails_v1 = await async_client.emails_v1.send_template_template( + template_id="templateId", + to="dev@stainless.com", + ) + assert_matches_type(EmailsV1SendTemplateTemplateResponse, emails_v1, path=["response"]) + + @parametrize + async def test_method_send_template_template_with_all_params(self, async_client: AsyncUnlayer) -> None: + emails_v1 = await async_client.emails_v1.send_template_template( + template_id="templateId", + to="dev@stainless.com", + merge_tags={"foo": "string"}, + subject="subject", + ) + assert_matches_type(EmailsV1SendTemplateTemplateResponse, emails_v1, path=["response"]) + + @parametrize + async def test_raw_response_send_template_template(self, async_client: AsyncUnlayer) -> None: + response = await async_client.emails_v1.with_raw_response.send_template_template( + template_id="templateId", + to="dev@stainless.com", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + emails_v1 = await response.parse() + assert_matches_type(EmailsV1SendTemplateTemplateResponse, emails_v1, path=["response"]) + + @parametrize + async def test_streaming_response_send_template_template(self, async_client: AsyncUnlayer) -> None: + async with async_client.emails_v1.with_streaming_response.send_template_template( + template_id="templateId", + to="dev@stainless.com", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + emails_v1 = await response.parse() + assert_matches_type(EmailsV1SendTemplateTemplateResponse, emails_v1, path=["response"]) + + assert cast(Any, response.is_closed) is True diff --git a/tests/api_resources/test_pages.py b/tests/api_resources/test_pages.py new file mode 100644 index 0000000..b300fca --- /dev/null +++ b/tests/api_resources/test_pages.py @@ -0,0 +1,126 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import os +from typing import Any, cast + +import pytest + +from unlayer import Unlayer, AsyncUnlayer +from tests.utils import assert_matches_type +from unlayer.types import PageRenderCreateResponse + +base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") + + +class TestPages: + parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) + + @parametrize + def test_method_render_create(self, client: Unlayer) -> None: + page = client.pages.render_create( + design={ + "counters": "bar", + "body": "bar", + }, + ) + assert_matches_type(PageRenderCreateResponse, page, path=["response"]) + + @parametrize + def test_method_render_create_with_all_params(self, client: Unlayer) -> None: + page = client.pages.render_create( + design={ + "counters": "bar", + "body": "bar", + }, + merge_tags={"foo": "string"}, + ) + assert_matches_type(PageRenderCreateResponse, page, path=["response"]) + + @parametrize + def test_raw_response_render_create(self, client: Unlayer) -> None: + response = client.pages.with_raw_response.render_create( + design={ + "counters": "bar", + "body": "bar", + }, + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + page = response.parse() + assert_matches_type(PageRenderCreateResponse, page, path=["response"]) + + @parametrize + def test_streaming_response_render_create(self, client: Unlayer) -> None: + with client.pages.with_streaming_response.render_create( + design={ + "counters": "bar", + "body": "bar", + }, + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + page = response.parse() + assert_matches_type(PageRenderCreateResponse, page, path=["response"]) + + assert cast(Any, response.is_closed) is True + + +class TestAsyncPages: + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) + + @parametrize + async def test_method_render_create(self, async_client: AsyncUnlayer) -> None: + page = await async_client.pages.render_create( + design={ + "counters": "bar", + "body": "bar", + }, + ) + assert_matches_type(PageRenderCreateResponse, page, path=["response"]) + + @parametrize + async def test_method_render_create_with_all_params(self, async_client: AsyncUnlayer) -> None: + page = await async_client.pages.render_create( + design={ + "counters": "bar", + "body": "bar", + }, + merge_tags={"foo": "string"}, + ) + assert_matches_type(PageRenderCreateResponse, page, path=["response"]) + + @parametrize + async def test_raw_response_render_create(self, async_client: AsyncUnlayer) -> None: + response = await async_client.pages.with_raw_response.render_create( + design={ + "counters": "bar", + "body": "bar", + }, + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + page = await response.parse() + assert_matches_type(PageRenderCreateResponse, page, path=["response"]) + + @parametrize + async def test_streaming_response_render_create(self, async_client: AsyncUnlayer) -> None: + async with async_client.pages.with_streaming_response.render_create( + design={ + "counters": "bar", + "body": "bar", + }, + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + page = await response.parse() + assert_matches_type(PageRenderCreateResponse, page, path=["response"]) + + assert cast(Any, response.is_closed) is True diff --git a/tests/api_resources/test_pages_v1.py b/tests/api_resources/test_pages_v1.py new file mode 100644 index 0000000..d55cff5 --- /dev/null +++ b/tests/api_resources/test_pages_v1.py @@ -0,0 +1,126 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import os +from typing import Any, cast + +import pytest + +from unlayer import Unlayer, AsyncUnlayer +from tests.utils import assert_matches_type +from unlayer.types import PagesV1RenderCreateResponse + +base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") + + +class TestPagesV1: + parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) + + @parametrize + def test_method_render_create(self, client: Unlayer) -> None: + pages_v1 = client.pages_v1.render_create( + design={ + "counters": "bar", + "body": "bar", + }, + ) + assert_matches_type(PagesV1RenderCreateResponse, pages_v1, path=["response"]) + + @parametrize + def test_method_render_create_with_all_params(self, client: Unlayer) -> None: + pages_v1 = client.pages_v1.render_create( + design={ + "counters": "bar", + "body": "bar", + }, + merge_tags={"foo": "string"}, + ) + assert_matches_type(PagesV1RenderCreateResponse, pages_v1, path=["response"]) + + @parametrize + def test_raw_response_render_create(self, client: Unlayer) -> None: + response = client.pages_v1.with_raw_response.render_create( + design={ + "counters": "bar", + "body": "bar", + }, + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + pages_v1 = response.parse() + assert_matches_type(PagesV1RenderCreateResponse, pages_v1, path=["response"]) + + @parametrize + def test_streaming_response_render_create(self, client: Unlayer) -> None: + with client.pages_v1.with_streaming_response.render_create( + design={ + "counters": "bar", + "body": "bar", + }, + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + pages_v1 = response.parse() + assert_matches_type(PagesV1RenderCreateResponse, pages_v1, path=["response"]) + + assert cast(Any, response.is_closed) is True + + +class TestAsyncPagesV1: + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) + + @parametrize + async def test_method_render_create(self, async_client: AsyncUnlayer) -> None: + pages_v1 = await async_client.pages_v1.render_create( + design={ + "counters": "bar", + "body": "bar", + }, + ) + assert_matches_type(PagesV1RenderCreateResponse, pages_v1, path=["response"]) + + @parametrize + async def test_method_render_create_with_all_params(self, async_client: AsyncUnlayer) -> None: + pages_v1 = await async_client.pages_v1.render_create( + design={ + "counters": "bar", + "body": "bar", + }, + merge_tags={"foo": "string"}, + ) + assert_matches_type(PagesV1RenderCreateResponse, pages_v1, path=["response"]) + + @parametrize + async def test_raw_response_render_create(self, async_client: AsyncUnlayer) -> None: + response = await async_client.pages_v1.with_raw_response.render_create( + design={ + "counters": "bar", + "body": "bar", + }, + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + pages_v1 = await response.parse() + assert_matches_type(PagesV1RenderCreateResponse, pages_v1, path=["response"]) + + @parametrize + async def test_streaming_response_render_create(self, async_client: AsyncUnlayer) -> None: + async with async_client.pages_v1.with_streaming_response.render_create( + design={ + "counters": "bar", + "body": "bar", + }, + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + pages_v1 = await response.parse() + assert_matches_type(PagesV1RenderCreateResponse, pages_v1, path=["response"]) + + assert cast(Any, response.is_closed) is True diff --git a/tests/api_resources/test_project.py b/tests/api_resources/test_project.py new file mode 100644 index 0000000..24deaa8 --- /dev/null +++ b/tests/api_resources/test_project.py @@ -0,0 +1,1198 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import os +from typing import Any, cast + +import pytest + +from unlayer import Unlayer, AsyncUnlayer +from tests.utils import assert_matches_type +from unlayer.types import ( + ProjectAPIKeysListResponse, + ProjectCurrentListResponse, + ProjectDomainsListResponse, + ProjectAPIKeysCreateResponse, + ProjectAPIKeysUpdateResponse, + ProjectDomainsCreateResponse, + ProjectDomainsUpdateResponse, + ProjectTemplatesListResponse, + ProjectAPIKeysRetrieveResponse, + ProjectDomainsRetrieveResponse, + ProjectTemplatesCreateResponse, + ProjectTemplatesUpdateResponse, + ProjectTemplatesRetrieveResponse, +) + +base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") + + +class TestProject: + parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) + + @parametrize + def test_method_api_keys_create(self, client: Unlayer) -> None: + project = client.project.api_keys_create( + name="name", + ) + assert_matches_type(ProjectAPIKeysCreateResponse, project, path=["response"]) + + @parametrize + def test_method_api_keys_create_with_all_params(self, client: Unlayer) -> None: + project = client.project.api_keys_create( + name="name", + domains=["string"], + ) + assert_matches_type(ProjectAPIKeysCreateResponse, project, path=["response"]) + + @parametrize + def test_raw_response_api_keys_create(self, client: Unlayer) -> None: + response = client.project.with_raw_response.api_keys_create( + name="name", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + project = response.parse() + assert_matches_type(ProjectAPIKeysCreateResponse, project, path=["response"]) + + @parametrize + def test_streaming_response_api_keys_create(self, client: Unlayer) -> None: + with client.project.with_streaming_response.api_keys_create( + name="name", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + project = response.parse() + assert_matches_type(ProjectAPIKeysCreateResponse, project, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_method_api_keys_delete(self, client: Unlayer) -> None: + project = client.project.api_keys_delete( + "id", + ) + assert project is None + + @parametrize + def test_raw_response_api_keys_delete(self, client: Unlayer) -> None: + response = client.project.with_raw_response.api_keys_delete( + "id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + project = response.parse() + assert project is None + + @parametrize + def test_streaming_response_api_keys_delete(self, client: Unlayer) -> None: + with client.project.with_streaming_response.api_keys_delete( + "id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + project = response.parse() + assert project is None + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_path_params_api_keys_delete(self, client: Unlayer) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): + client.project.with_raw_response.api_keys_delete( + "", + ) + + @parametrize + def test_method_api_keys_list(self, client: Unlayer) -> None: + project = client.project.api_keys_list() + assert_matches_type(ProjectAPIKeysListResponse, project, path=["response"]) + + @parametrize + def test_raw_response_api_keys_list(self, client: Unlayer) -> None: + response = client.project.with_raw_response.api_keys_list() + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + project = response.parse() + assert_matches_type(ProjectAPIKeysListResponse, project, path=["response"]) + + @parametrize + def test_streaming_response_api_keys_list(self, client: Unlayer) -> None: + with client.project.with_streaming_response.api_keys_list() as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + project = response.parse() + assert_matches_type(ProjectAPIKeysListResponse, project, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_method_api_keys_retrieve(self, client: Unlayer) -> None: + project = client.project.api_keys_retrieve( + "id", + ) + assert_matches_type(ProjectAPIKeysRetrieveResponse, project, path=["response"]) + + @parametrize + def test_raw_response_api_keys_retrieve(self, client: Unlayer) -> None: + response = client.project.with_raw_response.api_keys_retrieve( + "id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + project = response.parse() + assert_matches_type(ProjectAPIKeysRetrieveResponse, project, path=["response"]) + + @parametrize + def test_streaming_response_api_keys_retrieve(self, client: Unlayer) -> None: + with client.project.with_streaming_response.api_keys_retrieve( + "id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + project = response.parse() + assert_matches_type(ProjectAPIKeysRetrieveResponse, project, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_path_params_api_keys_retrieve(self, client: Unlayer) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): + client.project.with_raw_response.api_keys_retrieve( + "", + ) + + @parametrize + def test_method_api_keys_update(self, client: Unlayer) -> None: + project = client.project.api_keys_update( + id="id", + ) + assert_matches_type(ProjectAPIKeysUpdateResponse, project, path=["response"]) + + @parametrize + def test_method_api_keys_update_with_all_params(self, client: Unlayer) -> None: + project = client.project.api_keys_update( + id="id", + active=True, + domains=["string"], + name="name", + ) + assert_matches_type(ProjectAPIKeysUpdateResponse, project, path=["response"]) + + @parametrize + def test_raw_response_api_keys_update(self, client: Unlayer) -> None: + response = client.project.with_raw_response.api_keys_update( + id="id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + project = response.parse() + assert_matches_type(ProjectAPIKeysUpdateResponse, project, path=["response"]) + + @parametrize + def test_streaming_response_api_keys_update(self, client: Unlayer) -> None: + with client.project.with_streaming_response.api_keys_update( + id="id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + project = response.parse() + assert_matches_type(ProjectAPIKeysUpdateResponse, project, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_path_params_api_keys_update(self, client: Unlayer) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): + client.project.with_raw_response.api_keys_update( + id="", + ) + + @parametrize + def test_method_current_list(self, client: Unlayer) -> None: + project = client.project.current_list() + assert_matches_type(ProjectCurrentListResponse, project, path=["response"]) + + @parametrize + def test_raw_response_current_list(self, client: Unlayer) -> None: + response = client.project.with_raw_response.current_list() + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + project = response.parse() + assert_matches_type(ProjectCurrentListResponse, project, path=["response"]) + + @parametrize + def test_streaming_response_current_list(self, client: Unlayer) -> None: + with client.project.with_streaming_response.current_list() as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + project = response.parse() + assert_matches_type(ProjectCurrentListResponse, project, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_method_domains_create(self, client: Unlayer) -> None: + project = client.project.domains_create( + domain="domain", + ) + assert_matches_type(ProjectDomainsCreateResponse, project, path=["response"]) + + @parametrize + def test_raw_response_domains_create(self, client: Unlayer) -> None: + response = client.project.with_raw_response.domains_create( + domain="domain", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + project = response.parse() + assert_matches_type(ProjectDomainsCreateResponse, project, path=["response"]) + + @parametrize + def test_streaming_response_domains_create(self, client: Unlayer) -> None: + with client.project.with_streaming_response.domains_create( + domain="domain", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + project = response.parse() + assert_matches_type(ProjectDomainsCreateResponse, project, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_method_domains_delete(self, client: Unlayer) -> None: + project = client.project.domains_delete( + "id", + ) + assert project is None + + @parametrize + def test_raw_response_domains_delete(self, client: Unlayer) -> None: + response = client.project.with_raw_response.domains_delete( + "id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + project = response.parse() + assert project is None + + @parametrize + def test_streaming_response_domains_delete(self, client: Unlayer) -> None: + with client.project.with_streaming_response.domains_delete( + "id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + project = response.parse() + assert project is None + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_path_params_domains_delete(self, client: Unlayer) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): + client.project.with_raw_response.domains_delete( + "", + ) + + @parametrize + def test_method_domains_list(self, client: Unlayer) -> None: + project = client.project.domains_list() + assert_matches_type(ProjectDomainsListResponse, project, path=["response"]) + + @parametrize + def test_raw_response_domains_list(self, client: Unlayer) -> None: + response = client.project.with_raw_response.domains_list() + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + project = response.parse() + assert_matches_type(ProjectDomainsListResponse, project, path=["response"]) + + @parametrize + def test_streaming_response_domains_list(self, client: Unlayer) -> None: + with client.project.with_streaming_response.domains_list() as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + project = response.parse() + assert_matches_type(ProjectDomainsListResponse, project, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_method_domains_retrieve(self, client: Unlayer) -> None: + project = client.project.domains_retrieve( + "id", + ) + assert_matches_type(ProjectDomainsRetrieveResponse, project, path=["response"]) + + @parametrize + def test_raw_response_domains_retrieve(self, client: Unlayer) -> None: + response = client.project.with_raw_response.domains_retrieve( + "id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + project = response.parse() + assert_matches_type(ProjectDomainsRetrieveResponse, project, path=["response"]) + + @parametrize + def test_streaming_response_domains_retrieve(self, client: Unlayer) -> None: + with client.project.with_streaming_response.domains_retrieve( + "id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + project = response.parse() + assert_matches_type(ProjectDomainsRetrieveResponse, project, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_path_params_domains_retrieve(self, client: Unlayer) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): + client.project.with_raw_response.domains_retrieve( + "", + ) + + @parametrize + def test_method_domains_update(self, client: Unlayer) -> None: + project = client.project.domains_update( + id="id", + ) + assert_matches_type(ProjectDomainsUpdateResponse, project, path=["response"]) + + @parametrize + def test_method_domains_update_with_all_params(self, client: Unlayer) -> None: + project = client.project.domains_update( + id="id", + domain="domain", + ) + assert_matches_type(ProjectDomainsUpdateResponse, project, path=["response"]) + + @parametrize + def test_raw_response_domains_update(self, client: Unlayer) -> None: + response = client.project.with_raw_response.domains_update( + id="id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + project = response.parse() + assert_matches_type(ProjectDomainsUpdateResponse, project, path=["response"]) + + @parametrize + def test_streaming_response_domains_update(self, client: Unlayer) -> None: + with client.project.with_streaming_response.domains_update( + id="id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + project = response.parse() + assert_matches_type(ProjectDomainsUpdateResponse, project, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_path_params_domains_update(self, client: Unlayer) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): + client.project.with_raw_response.domains_update( + id="", + ) + + @parametrize + def test_method_templates_create(self, client: Unlayer) -> None: + project = client.project.templates_create( + name="name", + ) + assert_matches_type(ProjectTemplatesCreateResponse, project, path=["response"]) + + @parametrize + def test_method_templates_create_with_all_params(self, client: Unlayer) -> None: + project = client.project.templates_create( + name="name", + body="body", + subject="subject", + ) + assert_matches_type(ProjectTemplatesCreateResponse, project, path=["response"]) + + @parametrize + def test_raw_response_templates_create(self, client: Unlayer) -> None: + response = client.project.with_raw_response.templates_create( + name="name", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + project = response.parse() + assert_matches_type(ProjectTemplatesCreateResponse, project, path=["response"]) + + @parametrize + def test_streaming_response_templates_create(self, client: Unlayer) -> None: + with client.project.with_streaming_response.templates_create( + name="name", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + project = response.parse() + assert_matches_type(ProjectTemplatesCreateResponse, project, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_method_templates_delete(self, client: Unlayer) -> None: + project = client.project.templates_delete( + "id", + ) + assert project is None + + @parametrize + def test_raw_response_templates_delete(self, client: Unlayer) -> None: + response = client.project.with_raw_response.templates_delete( + "id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + project = response.parse() + assert project is None + + @parametrize + def test_streaming_response_templates_delete(self, client: Unlayer) -> None: + with client.project.with_streaming_response.templates_delete( + "id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + project = response.parse() + assert project is None + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_path_params_templates_delete(self, client: Unlayer) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): + client.project.with_raw_response.templates_delete( + "", + ) + + @parametrize + def test_method_templates_list(self, client: Unlayer) -> None: + project = client.project.templates_list() + assert_matches_type(ProjectTemplatesListResponse, project, path=["response"]) + + @parametrize + def test_raw_response_templates_list(self, client: Unlayer) -> None: + response = client.project.with_raw_response.templates_list() + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + project = response.parse() + assert_matches_type(ProjectTemplatesListResponse, project, path=["response"]) + + @parametrize + def test_streaming_response_templates_list(self, client: Unlayer) -> None: + with client.project.with_streaming_response.templates_list() as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + project = response.parse() + assert_matches_type(ProjectTemplatesListResponse, project, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_method_templates_retrieve(self, client: Unlayer) -> None: + project = client.project.templates_retrieve( + "id", + ) + assert_matches_type(ProjectTemplatesRetrieveResponse, project, path=["response"]) + + @parametrize + def test_raw_response_templates_retrieve(self, client: Unlayer) -> None: + response = client.project.with_raw_response.templates_retrieve( + "id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + project = response.parse() + assert_matches_type(ProjectTemplatesRetrieveResponse, project, path=["response"]) + + @parametrize + def test_streaming_response_templates_retrieve(self, client: Unlayer) -> None: + with client.project.with_streaming_response.templates_retrieve( + "id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + project = response.parse() + assert_matches_type(ProjectTemplatesRetrieveResponse, project, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_path_params_templates_retrieve(self, client: Unlayer) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): + client.project.with_raw_response.templates_retrieve( + "", + ) + + @parametrize + def test_method_templates_update(self, client: Unlayer) -> None: + project = client.project.templates_update( + id="id", + ) + assert_matches_type(ProjectTemplatesUpdateResponse, project, path=["response"]) + + @parametrize + def test_method_templates_update_with_all_params(self, client: Unlayer) -> None: + project = client.project.templates_update( + id="id", + body="body", + name="name", + subject="subject", + ) + assert_matches_type(ProjectTemplatesUpdateResponse, project, path=["response"]) + + @parametrize + def test_raw_response_templates_update(self, client: Unlayer) -> None: + response = client.project.with_raw_response.templates_update( + id="id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + project = response.parse() + assert_matches_type(ProjectTemplatesUpdateResponse, project, path=["response"]) + + @parametrize + def test_streaming_response_templates_update(self, client: Unlayer) -> None: + with client.project.with_streaming_response.templates_update( + id="id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + project = response.parse() + assert_matches_type(ProjectTemplatesUpdateResponse, project, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_path_params_templates_update(self, client: Unlayer) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): + client.project.with_raw_response.templates_update( + id="", + ) + + +class TestAsyncProject: + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) + + @parametrize + async def test_method_api_keys_create(self, async_client: AsyncUnlayer) -> None: + project = await async_client.project.api_keys_create( + name="name", + ) + assert_matches_type(ProjectAPIKeysCreateResponse, project, path=["response"]) + + @parametrize + async def test_method_api_keys_create_with_all_params(self, async_client: AsyncUnlayer) -> None: + project = await async_client.project.api_keys_create( + name="name", + domains=["string"], + ) + assert_matches_type(ProjectAPIKeysCreateResponse, project, path=["response"]) + + @parametrize + async def test_raw_response_api_keys_create(self, async_client: AsyncUnlayer) -> None: + response = await async_client.project.with_raw_response.api_keys_create( + name="name", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + project = await response.parse() + assert_matches_type(ProjectAPIKeysCreateResponse, project, path=["response"]) + + @parametrize + async def test_streaming_response_api_keys_create(self, async_client: AsyncUnlayer) -> None: + async with async_client.project.with_streaming_response.api_keys_create( + name="name", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + project = await response.parse() + assert_matches_type(ProjectAPIKeysCreateResponse, project, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_method_api_keys_delete(self, async_client: AsyncUnlayer) -> None: + project = await async_client.project.api_keys_delete( + "id", + ) + assert project is None + + @parametrize + async def test_raw_response_api_keys_delete(self, async_client: AsyncUnlayer) -> None: + response = await async_client.project.with_raw_response.api_keys_delete( + "id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + project = await response.parse() + assert project is None + + @parametrize + async def test_streaming_response_api_keys_delete(self, async_client: AsyncUnlayer) -> None: + async with async_client.project.with_streaming_response.api_keys_delete( + "id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + project = await response.parse() + assert project is None + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_path_params_api_keys_delete(self, async_client: AsyncUnlayer) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): + await async_client.project.with_raw_response.api_keys_delete( + "", + ) + + @parametrize + async def test_method_api_keys_list(self, async_client: AsyncUnlayer) -> None: + project = await async_client.project.api_keys_list() + assert_matches_type(ProjectAPIKeysListResponse, project, path=["response"]) + + @parametrize + async def test_raw_response_api_keys_list(self, async_client: AsyncUnlayer) -> None: + response = await async_client.project.with_raw_response.api_keys_list() + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + project = await response.parse() + assert_matches_type(ProjectAPIKeysListResponse, project, path=["response"]) + + @parametrize + async def test_streaming_response_api_keys_list(self, async_client: AsyncUnlayer) -> None: + async with async_client.project.with_streaming_response.api_keys_list() as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + project = await response.parse() + assert_matches_type(ProjectAPIKeysListResponse, project, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_method_api_keys_retrieve(self, async_client: AsyncUnlayer) -> None: + project = await async_client.project.api_keys_retrieve( + "id", + ) + assert_matches_type(ProjectAPIKeysRetrieveResponse, project, path=["response"]) + + @parametrize + async def test_raw_response_api_keys_retrieve(self, async_client: AsyncUnlayer) -> None: + response = await async_client.project.with_raw_response.api_keys_retrieve( + "id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + project = await response.parse() + assert_matches_type(ProjectAPIKeysRetrieveResponse, project, path=["response"]) + + @parametrize + async def test_streaming_response_api_keys_retrieve(self, async_client: AsyncUnlayer) -> None: + async with async_client.project.with_streaming_response.api_keys_retrieve( + "id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + project = await response.parse() + assert_matches_type(ProjectAPIKeysRetrieveResponse, project, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_path_params_api_keys_retrieve(self, async_client: AsyncUnlayer) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): + await async_client.project.with_raw_response.api_keys_retrieve( + "", + ) + + @parametrize + async def test_method_api_keys_update(self, async_client: AsyncUnlayer) -> None: + project = await async_client.project.api_keys_update( + id="id", + ) + assert_matches_type(ProjectAPIKeysUpdateResponse, project, path=["response"]) + + @parametrize + async def test_method_api_keys_update_with_all_params(self, async_client: AsyncUnlayer) -> None: + project = await async_client.project.api_keys_update( + id="id", + active=True, + domains=["string"], + name="name", + ) + assert_matches_type(ProjectAPIKeysUpdateResponse, project, path=["response"]) + + @parametrize + async def test_raw_response_api_keys_update(self, async_client: AsyncUnlayer) -> None: + response = await async_client.project.with_raw_response.api_keys_update( + id="id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + project = await response.parse() + assert_matches_type(ProjectAPIKeysUpdateResponse, project, path=["response"]) + + @parametrize + async def test_streaming_response_api_keys_update(self, async_client: AsyncUnlayer) -> None: + async with async_client.project.with_streaming_response.api_keys_update( + id="id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + project = await response.parse() + assert_matches_type(ProjectAPIKeysUpdateResponse, project, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_path_params_api_keys_update(self, async_client: AsyncUnlayer) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): + await async_client.project.with_raw_response.api_keys_update( + id="", + ) + + @parametrize + async def test_method_current_list(self, async_client: AsyncUnlayer) -> None: + project = await async_client.project.current_list() + assert_matches_type(ProjectCurrentListResponse, project, path=["response"]) + + @parametrize + async def test_raw_response_current_list(self, async_client: AsyncUnlayer) -> None: + response = await async_client.project.with_raw_response.current_list() + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + project = await response.parse() + assert_matches_type(ProjectCurrentListResponse, project, path=["response"]) + + @parametrize + async def test_streaming_response_current_list(self, async_client: AsyncUnlayer) -> None: + async with async_client.project.with_streaming_response.current_list() as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + project = await response.parse() + assert_matches_type(ProjectCurrentListResponse, project, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_method_domains_create(self, async_client: AsyncUnlayer) -> None: + project = await async_client.project.domains_create( + domain="domain", + ) + assert_matches_type(ProjectDomainsCreateResponse, project, path=["response"]) + + @parametrize + async def test_raw_response_domains_create(self, async_client: AsyncUnlayer) -> None: + response = await async_client.project.with_raw_response.domains_create( + domain="domain", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + project = await response.parse() + assert_matches_type(ProjectDomainsCreateResponse, project, path=["response"]) + + @parametrize + async def test_streaming_response_domains_create(self, async_client: AsyncUnlayer) -> None: + async with async_client.project.with_streaming_response.domains_create( + domain="domain", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + project = await response.parse() + assert_matches_type(ProjectDomainsCreateResponse, project, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_method_domains_delete(self, async_client: AsyncUnlayer) -> None: + project = await async_client.project.domains_delete( + "id", + ) + assert project is None + + @parametrize + async def test_raw_response_domains_delete(self, async_client: AsyncUnlayer) -> None: + response = await async_client.project.with_raw_response.domains_delete( + "id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + project = await response.parse() + assert project is None + + @parametrize + async def test_streaming_response_domains_delete(self, async_client: AsyncUnlayer) -> None: + async with async_client.project.with_streaming_response.domains_delete( + "id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + project = await response.parse() + assert project is None + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_path_params_domains_delete(self, async_client: AsyncUnlayer) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): + await async_client.project.with_raw_response.domains_delete( + "", + ) + + @parametrize + async def test_method_domains_list(self, async_client: AsyncUnlayer) -> None: + project = await async_client.project.domains_list() + assert_matches_type(ProjectDomainsListResponse, project, path=["response"]) + + @parametrize + async def test_raw_response_domains_list(self, async_client: AsyncUnlayer) -> None: + response = await async_client.project.with_raw_response.domains_list() + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + project = await response.parse() + assert_matches_type(ProjectDomainsListResponse, project, path=["response"]) + + @parametrize + async def test_streaming_response_domains_list(self, async_client: AsyncUnlayer) -> None: + async with async_client.project.with_streaming_response.domains_list() as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + project = await response.parse() + assert_matches_type(ProjectDomainsListResponse, project, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_method_domains_retrieve(self, async_client: AsyncUnlayer) -> None: + project = await async_client.project.domains_retrieve( + "id", + ) + assert_matches_type(ProjectDomainsRetrieveResponse, project, path=["response"]) + + @parametrize + async def test_raw_response_domains_retrieve(self, async_client: AsyncUnlayer) -> None: + response = await async_client.project.with_raw_response.domains_retrieve( + "id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + project = await response.parse() + assert_matches_type(ProjectDomainsRetrieveResponse, project, path=["response"]) + + @parametrize + async def test_streaming_response_domains_retrieve(self, async_client: AsyncUnlayer) -> None: + async with async_client.project.with_streaming_response.domains_retrieve( + "id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + project = await response.parse() + assert_matches_type(ProjectDomainsRetrieveResponse, project, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_path_params_domains_retrieve(self, async_client: AsyncUnlayer) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): + await async_client.project.with_raw_response.domains_retrieve( + "", + ) + + @parametrize + async def test_method_domains_update(self, async_client: AsyncUnlayer) -> None: + project = await async_client.project.domains_update( + id="id", + ) + assert_matches_type(ProjectDomainsUpdateResponse, project, path=["response"]) + + @parametrize + async def test_method_domains_update_with_all_params(self, async_client: AsyncUnlayer) -> None: + project = await async_client.project.domains_update( + id="id", + domain="domain", + ) + assert_matches_type(ProjectDomainsUpdateResponse, project, path=["response"]) + + @parametrize + async def test_raw_response_domains_update(self, async_client: AsyncUnlayer) -> None: + response = await async_client.project.with_raw_response.domains_update( + id="id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + project = await response.parse() + assert_matches_type(ProjectDomainsUpdateResponse, project, path=["response"]) + + @parametrize + async def test_streaming_response_domains_update(self, async_client: AsyncUnlayer) -> None: + async with async_client.project.with_streaming_response.domains_update( + id="id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + project = await response.parse() + assert_matches_type(ProjectDomainsUpdateResponse, project, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_path_params_domains_update(self, async_client: AsyncUnlayer) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): + await async_client.project.with_raw_response.domains_update( + id="", + ) + + @parametrize + async def test_method_templates_create(self, async_client: AsyncUnlayer) -> None: + project = await async_client.project.templates_create( + name="name", + ) + assert_matches_type(ProjectTemplatesCreateResponse, project, path=["response"]) + + @parametrize + async def test_method_templates_create_with_all_params(self, async_client: AsyncUnlayer) -> None: + project = await async_client.project.templates_create( + name="name", + body="body", + subject="subject", + ) + assert_matches_type(ProjectTemplatesCreateResponse, project, path=["response"]) + + @parametrize + async def test_raw_response_templates_create(self, async_client: AsyncUnlayer) -> None: + response = await async_client.project.with_raw_response.templates_create( + name="name", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + project = await response.parse() + assert_matches_type(ProjectTemplatesCreateResponse, project, path=["response"]) + + @parametrize + async def test_streaming_response_templates_create(self, async_client: AsyncUnlayer) -> None: + async with async_client.project.with_streaming_response.templates_create( + name="name", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + project = await response.parse() + assert_matches_type(ProjectTemplatesCreateResponse, project, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_method_templates_delete(self, async_client: AsyncUnlayer) -> None: + project = await async_client.project.templates_delete( + "id", + ) + assert project is None + + @parametrize + async def test_raw_response_templates_delete(self, async_client: AsyncUnlayer) -> None: + response = await async_client.project.with_raw_response.templates_delete( + "id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + project = await response.parse() + assert project is None + + @parametrize + async def test_streaming_response_templates_delete(self, async_client: AsyncUnlayer) -> None: + async with async_client.project.with_streaming_response.templates_delete( + "id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + project = await response.parse() + assert project is None + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_path_params_templates_delete(self, async_client: AsyncUnlayer) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): + await async_client.project.with_raw_response.templates_delete( + "", + ) + + @parametrize + async def test_method_templates_list(self, async_client: AsyncUnlayer) -> None: + project = await async_client.project.templates_list() + assert_matches_type(ProjectTemplatesListResponse, project, path=["response"]) + + @parametrize + async def test_raw_response_templates_list(self, async_client: AsyncUnlayer) -> None: + response = await async_client.project.with_raw_response.templates_list() + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + project = await response.parse() + assert_matches_type(ProjectTemplatesListResponse, project, path=["response"]) + + @parametrize + async def test_streaming_response_templates_list(self, async_client: AsyncUnlayer) -> None: + async with async_client.project.with_streaming_response.templates_list() as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + project = await response.parse() + assert_matches_type(ProjectTemplatesListResponse, project, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_method_templates_retrieve(self, async_client: AsyncUnlayer) -> None: + project = await async_client.project.templates_retrieve( + "id", + ) + assert_matches_type(ProjectTemplatesRetrieveResponse, project, path=["response"]) + + @parametrize + async def test_raw_response_templates_retrieve(self, async_client: AsyncUnlayer) -> None: + response = await async_client.project.with_raw_response.templates_retrieve( + "id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + project = await response.parse() + assert_matches_type(ProjectTemplatesRetrieveResponse, project, path=["response"]) + + @parametrize + async def test_streaming_response_templates_retrieve(self, async_client: AsyncUnlayer) -> None: + async with async_client.project.with_streaming_response.templates_retrieve( + "id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + project = await response.parse() + assert_matches_type(ProjectTemplatesRetrieveResponse, project, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_path_params_templates_retrieve(self, async_client: AsyncUnlayer) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): + await async_client.project.with_raw_response.templates_retrieve( + "", + ) + + @parametrize + async def test_method_templates_update(self, async_client: AsyncUnlayer) -> None: + project = await async_client.project.templates_update( + id="id", + ) + assert_matches_type(ProjectTemplatesUpdateResponse, project, path=["response"]) + + @parametrize + async def test_method_templates_update_with_all_params(self, async_client: AsyncUnlayer) -> None: + project = await async_client.project.templates_update( + id="id", + body="body", + name="name", + subject="subject", + ) + assert_matches_type(ProjectTemplatesUpdateResponse, project, path=["response"]) + + @parametrize + async def test_raw_response_templates_update(self, async_client: AsyncUnlayer) -> None: + response = await async_client.project.with_raw_response.templates_update( + id="id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + project = await response.parse() + assert_matches_type(ProjectTemplatesUpdateResponse, project, path=["response"]) + + @parametrize + async def test_streaming_response_templates_update(self, async_client: AsyncUnlayer) -> None: + async with async_client.project.with_streaming_response.templates_update( + id="id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + project = await response.parse() + assert_matches_type(ProjectTemplatesUpdateResponse, project, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_path_params_templates_update(self, async_client: AsyncUnlayer) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): + await async_client.project.with_raw_response.templates_update( + id="", + ) diff --git a/tests/api_resources/test_project_v1.py b/tests/api_resources/test_project_v1.py new file mode 100644 index 0000000..16dcd69 --- /dev/null +++ b/tests/api_resources/test_project_v1.py @@ -0,0 +1,1198 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import os +from typing import Any, cast + +import pytest + +from unlayer import Unlayer, AsyncUnlayer +from tests.utils import assert_matches_type +from unlayer.types import ( + ProjectV1APIKeysListResponse, + ProjectV1CurrentListResponse, + ProjectV1DomainsListResponse, + ProjectV1APIKeysCreateResponse, + ProjectV1APIKeysUpdateResponse, + ProjectV1DomainsCreateResponse, + ProjectV1DomainsUpdateResponse, + ProjectV1TemplatesListResponse, + ProjectV1APIKeysRetrieveResponse, + ProjectV1DomainsRetrieveResponse, + ProjectV1TemplatesCreateResponse, + ProjectV1TemplatesUpdateResponse, + ProjectV1TemplatesRetrieveResponse, +) + +base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") + + +class TestProjectV1: + parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) + + @parametrize + def test_method_api_keys_create(self, client: Unlayer) -> None: + project_v1 = client.project_v1.api_keys_create( + name="name", + ) + assert_matches_type(ProjectV1APIKeysCreateResponse, project_v1, path=["response"]) + + @parametrize + def test_method_api_keys_create_with_all_params(self, client: Unlayer) -> None: + project_v1 = client.project_v1.api_keys_create( + name="name", + domains=["string"], + ) + assert_matches_type(ProjectV1APIKeysCreateResponse, project_v1, path=["response"]) + + @parametrize + def test_raw_response_api_keys_create(self, client: Unlayer) -> None: + response = client.project_v1.with_raw_response.api_keys_create( + name="name", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + project_v1 = response.parse() + assert_matches_type(ProjectV1APIKeysCreateResponse, project_v1, path=["response"]) + + @parametrize + def test_streaming_response_api_keys_create(self, client: Unlayer) -> None: + with client.project_v1.with_streaming_response.api_keys_create( + name="name", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + project_v1 = response.parse() + assert_matches_type(ProjectV1APIKeysCreateResponse, project_v1, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_method_api_keys_delete(self, client: Unlayer) -> None: + project_v1 = client.project_v1.api_keys_delete( + "id", + ) + assert project_v1 is None + + @parametrize + def test_raw_response_api_keys_delete(self, client: Unlayer) -> None: + response = client.project_v1.with_raw_response.api_keys_delete( + "id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + project_v1 = response.parse() + assert project_v1 is None + + @parametrize + def test_streaming_response_api_keys_delete(self, client: Unlayer) -> None: + with client.project_v1.with_streaming_response.api_keys_delete( + "id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + project_v1 = response.parse() + assert project_v1 is None + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_path_params_api_keys_delete(self, client: Unlayer) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): + client.project_v1.with_raw_response.api_keys_delete( + "", + ) + + @parametrize + def test_method_api_keys_list(self, client: Unlayer) -> None: + project_v1 = client.project_v1.api_keys_list() + assert_matches_type(ProjectV1APIKeysListResponse, project_v1, path=["response"]) + + @parametrize + def test_raw_response_api_keys_list(self, client: Unlayer) -> None: + response = client.project_v1.with_raw_response.api_keys_list() + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + project_v1 = response.parse() + assert_matches_type(ProjectV1APIKeysListResponse, project_v1, path=["response"]) + + @parametrize + def test_streaming_response_api_keys_list(self, client: Unlayer) -> None: + with client.project_v1.with_streaming_response.api_keys_list() as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + project_v1 = response.parse() + assert_matches_type(ProjectV1APIKeysListResponse, project_v1, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_method_api_keys_retrieve(self, client: Unlayer) -> None: + project_v1 = client.project_v1.api_keys_retrieve( + "id", + ) + assert_matches_type(ProjectV1APIKeysRetrieveResponse, project_v1, path=["response"]) + + @parametrize + def test_raw_response_api_keys_retrieve(self, client: Unlayer) -> None: + response = client.project_v1.with_raw_response.api_keys_retrieve( + "id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + project_v1 = response.parse() + assert_matches_type(ProjectV1APIKeysRetrieveResponse, project_v1, path=["response"]) + + @parametrize + def test_streaming_response_api_keys_retrieve(self, client: Unlayer) -> None: + with client.project_v1.with_streaming_response.api_keys_retrieve( + "id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + project_v1 = response.parse() + assert_matches_type(ProjectV1APIKeysRetrieveResponse, project_v1, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_path_params_api_keys_retrieve(self, client: Unlayer) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): + client.project_v1.with_raw_response.api_keys_retrieve( + "", + ) + + @parametrize + def test_method_api_keys_update(self, client: Unlayer) -> None: + project_v1 = client.project_v1.api_keys_update( + id="id", + ) + assert_matches_type(ProjectV1APIKeysUpdateResponse, project_v1, path=["response"]) + + @parametrize + def test_method_api_keys_update_with_all_params(self, client: Unlayer) -> None: + project_v1 = client.project_v1.api_keys_update( + id="id", + active=True, + domains=["string"], + name="name", + ) + assert_matches_type(ProjectV1APIKeysUpdateResponse, project_v1, path=["response"]) + + @parametrize + def test_raw_response_api_keys_update(self, client: Unlayer) -> None: + response = client.project_v1.with_raw_response.api_keys_update( + id="id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + project_v1 = response.parse() + assert_matches_type(ProjectV1APIKeysUpdateResponse, project_v1, path=["response"]) + + @parametrize + def test_streaming_response_api_keys_update(self, client: Unlayer) -> None: + with client.project_v1.with_streaming_response.api_keys_update( + id="id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + project_v1 = response.parse() + assert_matches_type(ProjectV1APIKeysUpdateResponse, project_v1, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_path_params_api_keys_update(self, client: Unlayer) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): + client.project_v1.with_raw_response.api_keys_update( + id="", + ) + + @parametrize + def test_method_current_list(self, client: Unlayer) -> None: + project_v1 = client.project_v1.current_list() + assert_matches_type(ProjectV1CurrentListResponse, project_v1, path=["response"]) + + @parametrize + def test_raw_response_current_list(self, client: Unlayer) -> None: + response = client.project_v1.with_raw_response.current_list() + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + project_v1 = response.parse() + assert_matches_type(ProjectV1CurrentListResponse, project_v1, path=["response"]) + + @parametrize + def test_streaming_response_current_list(self, client: Unlayer) -> None: + with client.project_v1.with_streaming_response.current_list() as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + project_v1 = response.parse() + assert_matches_type(ProjectV1CurrentListResponse, project_v1, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_method_domains_create(self, client: Unlayer) -> None: + project_v1 = client.project_v1.domains_create( + domain="domain", + ) + assert_matches_type(ProjectV1DomainsCreateResponse, project_v1, path=["response"]) + + @parametrize + def test_raw_response_domains_create(self, client: Unlayer) -> None: + response = client.project_v1.with_raw_response.domains_create( + domain="domain", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + project_v1 = response.parse() + assert_matches_type(ProjectV1DomainsCreateResponse, project_v1, path=["response"]) + + @parametrize + def test_streaming_response_domains_create(self, client: Unlayer) -> None: + with client.project_v1.with_streaming_response.domains_create( + domain="domain", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + project_v1 = response.parse() + assert_matches_type(ProjectV1DomainsCreateResponse, project_v1, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_method_domains_delete(self, client: Unlayer) -> None: + project_v1 = client.project_v1.domains_delete( + "id", + ) + assert project_v1 is None + + @parametrize + def test_raw_response_domains_delete(self, client: Unlayer) -> None: + response = client.project_v1.with_raw_response.domains_delete( + "id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + project_v1 = response.parse() + assert project_v1 is None + + @parametrize + def test_streaming_response_domains_delete(self, client: Unlayer) -> None: + with client.project_v1.with_streaming_response.domains_delete( + "id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + project_v1 = response.parse() + assert project_v1 is None + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_path_params_domains_delete(self, client: Unlayer) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): + client.project_v1.with_raw_response.domains_delete( + "", + ) + + @parametrize + def test_method_domains_list(self, client: Unlayer) -> None: + project_v1 = client.project_v1.domains_list() + assert_matches_type(ProjectV1DomainsListResponse, project_v1, path=["response"]) + + @parametrize + def test_raw_response_domains_list(self, client: Unlayer) -> None: + response = client.project_v1.with_raw_response.domains_list() + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + project_v1 = response.parse() + assert_matches_type(ProjectV1DomainsListResponse, project_v1, path=["response"]) + + @parametrize + def test_streaming_response_domains_list(self, client: Unlayer) -> None: + with client.project_v1.with_streaming_response.domains_list() as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + project_v1 = response.parse() + assert_matches_type(ProjectV1DomainsListResponse, project_v1, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_method_domains_retrieve(self, client: Unlayer) -> None: + project_v1 = client.project_v1.domains_retrieve( + "id", + ) + assert_matches_type(ProjectV1DomainsRetrieveResponse, project_v1, path=["response"]) + + @parametrize + def test_raw_response_domains_retrieve(self, client: Unlayer) -> None: + response = client.project_v1.with_raw_response.domains_retrieve( + "id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + project_v1 = response.parse() + assert_matches_type(ProjectV1DomainsRetrieveResponse, project_v1, path=["response"]) + + @parametrize + def test_streaming_response_domains_retrieve(self, client: Unlayer) -> None: + with client.project_v1.with_streaming_response.domains_retrieve( + "id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + project_v1 = response.parse() + assert_matches_type(ProjectV1DomainsRetrieveResponse, project_v1, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_path_params_domains_retrieve(self, client: Unlayer) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): + client.project_v1.with_raw_response.domains_retrieve( + "", + ) + + @parametrize + def test_method_domains_update(self, client: Unlayer) -> None: + project_v1 = client.project_v1.domains_update( + id="id", + ) + assert_matches_type(ProjectV1DomainsUpdateResponse, project_v1, path=["response"]) + + @parametrize + def test_method_domains_update_with_all_params(self, client: Unlayer) -> None: + project_v1 = client.project_v1.domains_update( + id="id", + domain="domain", + ) + assert_matches_type(ProjectV1DomainsUpdateResponse, project_v1, path=["response"]) + + @parametrize + def test_raw_response_domains_update(self, client: Unlayer) -> None: + response = client.project_v1.with_raw_response.domains_update( + id="id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + project_v1 = response.parse() + assert_matches_type(ProjectV1DomainsUpdateResponse, project_v1, path=["response"]) + + @parametrize + def test_streaming_response_domains_update(self, client: Unlayer) -> None: + with client.project_v1.with_streaming_response.domains_update( + id="id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + project_v1 = response.parse() + assert_matches_type(ProjectV1DomainsUpdateResponse, project_v1, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_path_params_domains_update(self, client: Unlayer) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): + client.project_v1.with_raw_response.domains_update( + id="", + ) + + @parametrize + def test_method_templates_create(self, client: Unlayer) -> None: + project_v1 = client.project_v1.templates_create( + name="name", + ) + assert_matches_type(ProjectV1TemplatesCreateResponse, project_v1, path=["response"]) + + @parametrize + def test_method_templates_create_with_all_params(self, client: Unlayer) -> None: + project_v1 = client.project_v1.templates_create( + name="name", + body="body", + subject="subject", + ) + assert_matches_type(ProjectV1TemplatesCreateResponse, project_v1, path=["response"]) + + @parametrize + def test_raw_response_templates_create(self, client: Unlayer) -> None: + response = client.project_v1.with_raw_response.templates_create( + name="name", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + project_v1 = response.parse() + assert_matches_type(ProjectV1TemplatesCreateResponse, project_v1, path=["response"]) + + @parametrize + def test_streaming_response_templates_create(self, client: Unlayer) -> None: + with client.project_v1.with_streaming_response.templates_create( + name="name", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + project_v1 = response.parse() + assert_matches_type(ProjectV1TemplatesCreateResponse, project_v1, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_method_templates_delete(self, client: Unlayer) -> None: + project_v1 = client.project_v1.templates_delete( + "id", + ) + assert project_v1 is None + + @parametrize + def test_raw_response_templates_delete(self, client: Unlayer) -> None: + response = client.project_v1.with_raw_response.templates_delete( + "id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + project_v1 = response.parse() + assert project_v1 is None + + @parametrize + def test_streaming_response_templates_delete(self, client: Unlayer) -> None: + with client.project_v1.with_streaming_response.templates_delete( + "id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + project_v1 = response.parse() + assert project_v1 is None + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_path_params_templates_delete(self, client: Unlayer) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): + client.project_v1.with_raw_response.templates_delete( + "", + ) + + @parametrize + def test_method_templates_list(self, client: Unlayer) -> None: + project_v1 = client.project_v1.templates_list() + assert_matches_type(ProjectV1TemplatesListResponse, project_v1, path=["response"]) + + @parametrize + def test_raw_response_templates_list(self, client: Unlayer) -> None: + response = client.project_v1.with_raw_response.templates_list() + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + project_v1 = response.parse() + assert_matches_type(ProjectV1TemplatesListResponse, project_v1, path=["response"]) + + @parametrize + def test_streaming_response_templates_list(self, client: Unlayer) -> None: + with client.project_v1.with_streaming_response.templates_list() as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + project_v1 = response.parse() + assert_matches_type(ProjectV1TemplatesListResponse, project_v1, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_method_templates_retrieve(self, client: Unlayer) -> None: + project_v1 = client.project_v1.templates_retrieve( + "id", + ) + assert_matches_type(ProjectV1TemplatesRetrieveResponse, project_v1, path=["response"]) + + @parametrize + def test_raw_response_templates_retrieve(self, client: Unlayer) -> None: + response = client.project_v1.with_raw_response.templates_retrieve( + "id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + project_v1 = response.parse() + assert_matches_type(ProjectV1TemplatesRetrieveResponse, project_v1, path=["response"]) + + @parametrize + def test_streaming_response_templates_retrieve(self, client: Unlayer) -> None: + with client.project_v1.with_streaming_response.templates_retrieve( + "id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + project_v1 = response.parse() + assert_matches_type(ProjectV1TemplatesRetrieveResponse, project_v1, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_path_params_templates_retrieve(self, client: Unlayer) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): + client.project_v1.with_raw_response.templates_retrieve( + "", + ) + + @parametrize + def test_method_templates_update(self, client: Unlayer) -> None: + project_v1 = client.project_v1.templates_update( + id="id", + ) + assert_matches_type(ProjectV1TemplatesUpdateResponse, project_v1, path=["response"]) + + @parametrize + def test_method_templates_update_with_all_params(self, client: Unlayer) -> None: + project_v1 = client.project_v1.templates_update( + id="id", + body="body", + name="name", + subject="subject", + ) + assert_matches_type(ProjectV1TemplatesUpdateResponse, project_v1, path=["response"]) + + @parametrize + def test_raw_response_templates_update(self, client: Unlayer) -> None: + response = client.project_v1.with_raw_response.templates_update( + id="id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + project_v1 = response.parse() + assert_matches_type(ProjectV1TemplatesUpdateResponse, project_v1, path=["response"]) + + @parametrize + def test_streaming_response_templates_update(self, client: Unlayer) -> None: + with client.project_v1.with_streaming_response.templates_update( + id="id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + project_v1 = response.parse() + assert_matches_type(ProjectV1TemplatesUpdateResponse, project_v1, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_path_params_templates_update(self, client: Unlayer) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): + client.project_v1.with_raw_response.templates_update( + id="", + ) + + +class TestAsyncProjectV1: + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) + + @parametrize + async def test_method_api_keys_create(self, async_client: AsyncUnlayer) -> None: + project_v1 = await async_client.project_v1.api_keys_create( + name="name", + ) + assert_matches_type(ProjectV1APIKeysCreateResponse, project_v1, path=["response"]) + + @parametrize + async def test_method_api_keys_create_with_all_params(self, async_client: AsyncUnlayer) -> None: + project_v1 = await async_client.project_v1.api_keys_create( + name="name", + domains=["string"], + ) + assert_matches_type(ProjectV1APIKeysCreateResponse, project_v1, path=["response"]) + + @parametrize + async def test_raw_response_api_keys_create(self, async_client: AsyncUnlayer) -> None: + response = await async_client.project_v1.with_raw_response.api_keys_create( + name="name", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + project_v1 = await response.parse() + assert_matches_type(ProjectV1APIKeysCreateResponse, project_v1, path=["response"]) + + @parametrize + async def test_streaming_response_api_keys_create(self, async_client: AsyncUnlayer) -> None: + async with async_client.project_v1.with_streaming_response.api_keys_create( + name="name", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + project_v1 = await response.parse() + assert_matches_type(ProjectV1APIKeysCreateResponse, project_v1, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_method_api_keys_delete(self, async_client: AsyncUnlayer) -> None: + project_v1 = await async_client.project_v1.api_keys_delete( + "id", + ) + assert project_v1 is None + + @parametrize + async def test_raw_response_api_keys_delete(self, async_client: AsyncUnlayer) -> None: + response = await async_client.project_v1.with_raw_response.api_keys_delete( + "id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + project_v1 = await response.parse() + assert project_v1 is None + + @parametrize + async def test_streaming_response_api_keys_delete(self, async_client: AsyncUnlayer) -> None: + async with async_client.project_v1.with_streaming_response.api_keys_delete( + "id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + project_v1 = await response.parse() + assert project_v1 is None + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_path_params_api_keys_delete(self, async_client: AsyncUnlayer) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): + await async_client.project_v1.with_raw_response.api_keys_delete( + "", + ) + + @parametrize + async def test_method_api_keys_list(self, async_client: AsyncUnlayer) -> None: + project_v1 = await async_client.project_v1.api_keys_list() + assert_matches_type(ProjectV1APIKeysListResponse, project_v1, path=["response"]) + + @parametrize + async def test_raw_response_api_keys_list(self, async_client: AsyncUnlayer) -> None: + response = await async_client.project_v1.with_raw_response.api_keys_list() + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + project_v1 = await response.parse() + assert_matches_type(ProjectV1APIKeysListResponse, project_v1, path=["response"]) + + @parametrize + async def test_streaming_response_api_keys_list(self, async_client: AsyncUnlayer) -> None: + async with async_client.project_v1.with_streaming_response.api_keys_list() as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + project_v1 = await response.parse() + assert_matches_type(ProjectV1APIKeysListResponse, project_v1, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_method_api_keys_retrieve(self, async_client: AsyncUnlayer) -> None: + project_v1 = await async_client.project_v1.api_keys_retrieve( + "id", + ) + assert_matches_type(ProjectV1APIKeysRetrieveResponse, project_v1, path=["response"]) + + @parametrize + async def test_raw_response_api_keys_retrieve(self, async_client: AsyncUnlayer) -> None: + response = await async_client.project_v1.with_raw_response.api_keys_retrieve( + "id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + project_v1 = await response.parse() + assert_matches_type(ProjectV1APIKeysRetrieveResponse, project_v1, path=["response"]) + + @parametrize + async def test_streaming_response_api_keys_retrieve(self, async_client: AsyncUnlayer) -> None: + async with async_client.project_v1.with_streaming_response.api_keys_retrieve( + "id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + project_v1 = await response.parse() + assert_matches_type(ProjectV1APIKeysRetrieveResponse, project_v1, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_path_params_api_keys_retrieve(self, async_client: AsyncUnlayer) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): + await async_client.project_v1.with_raw_response.api_keys_retrieve( + "", + ) + + @parametrize + async def test_method_api_keys_update(self, async_client: AsyncUnlayer) -> None: + project_v1 = await async_client.project_v1.api_keys_update( + id="id", + ) + assert_matches_type(ProjectV1APIKeysUpdateResponse, project_v1, path=["response"]) + + @parametrize + async def test_method_api_keys_update_with_all_params(self, async_client: AsyncUnlayer) -> None: + project_v1 = await async_client.project_v1.api_keys_update( + id="id", + active=True, + domains=["string"], + name="name", + ) + assert_matches_type(ProjectV1APIKeysUpdateResponse, project_v1, path=["response"]) + + @parametrize + async def test_raw_response_api_keys_update(self, async_client: AsyncUnlayer) -> None: + response = await async_client.project_v1.with_raw_response.api_keys_update( + id="id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + project_v1 = await response.parse() + assert_matches_type(ProjectV1APIKeysUpdateResponse, project_v1, path=["response"]) + + @parametrize + async def test_streaming_response_api_keys_update(self, async_client: AsyncUnlayer) -> None: + async with async_client.project_v1.with_streaming_response.api_keys_update( + id="id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + project_v1 = await response.parse() + assert_matches_type(ProjectV1APIKeysUpdateResponse, project_v1, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_path_params_api_keys_update(self, async_client: AsyncUnlayer) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): + await async_client.project_v1.with_raw_response.api_keys_update( + id="", + ) + + @parametrize + async def test_method_current_list(self, async_client: AsyncUnlayer) -> None: + project_v1 = await async_client.project_v1.current_list() + assert_matches_type(ProjectV1CurrentListResponse, project_v1, path=["response"]) + + @parametrize + async def test_raw_response_current_list(self, async_client: AsyncUnlayer) -> None: + response = await async_client.project_v1.with_raw_response.current_list() + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + project_v1 = await response.parse() + assert_matches_type(ProjectV1CurrentListResponse, project_v1, path=["response"]) + + @parametrize + async def test_streaming_response_current_list(self, async_client: AsyncUnlayer) -> None: + async with async_client.project_v1.with_streaming_response.current_list() as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + project_v1 = await response.parse() + assert_matches_type(ProjectV1CurrentListResponse, project_v1, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_method_domains_create(self, async_client: AsyncUnlayer) -> None: + project_v1 = await async_client.project_v1.domains_create( + domain="domain", + ) + assert_matches_type(ProjectV1DomainsCreateResponse, project_v1, path=["response"]) + + @parametrize + async def test_raw_response_domains_create(self, async_client: AsyncUnlayer) -> None: + response = await async_client.project_v1.with_raw_response.domains_create( + domain="domain", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + project_v1 = await response.parse() + assert_matches_type(ProjectV1DomainsCreateResponse, project_v1, path=["response"]) + + @parametrize + async def test_streaming_response_domains_create(self, async_client: AsyncUnlayer) -> None: + async with async_client.project_v1.with_streaming_response.domains_create( + domain="domain", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + project_v1 = await response.parse() + assert_matches_type(ProjectV1DomainsCreateResponse, project_v1, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_method_domains_delete(self, async_client: AsyncUnlayer) -> None: + project_v1 = await async_client.project_v1.domains_delete( + "id", + ) + assert project_v1 is None + + @parametrize + async def test_raw_response_domains_delete(self, async_client: AsyncUnlayer) -> None: + response = await async_client.project_v1.with_raw_response.domains_delete( + "id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + project_v1 = await response.parse() + assert project_v1 is None + + @parametrize + async def test_streaming_response_domains_delete(self, async_client: AsyncUnlayer) -> None: + async with async_client.project_v1.with_streaming_response.domains_delete( + "id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + project_v1 = await response.parse() + assert project_v1 is None + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_path_params_domains_delete(self, async_client: AsyncUnlayer) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): + await async_client.project_v1.with_raw_response.domains_delete( + "", + ) + + @parametrize + async def test_method_domains_list(self, async_client: AsyncUnlayer) -> None: + project_v1 = await async_client.project_v1.domains_list() + assert_matches_type(ProjectV1DomainsListResponse, project_v1, path=["response"]) + + @parametrize + async def test_raw_response_domains_list(self, async_client: AsyncUnlayer) -> None: + response = await async_client.project_v1.with_raw_response.domains_list() + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + project_v1 = await response.parse() + assert_matches_type(ProjectV1DomainsListResponse, project_v1, path=["response"]) + + @parametrize + async def test_streaming_response_domains_list(self, async_client: AsyncUnlayer) -> None: + async with async_client.project_v1.with_streaming_response.domains_list() as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + project_v1 = await response.parse() + assert_matches_type(ProjectV1DomainsListResponse, project_v1, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_method_domains_retrieve(self, async_client: AsyncUnlayer) -> None: + project_v1 = await async_client.project_v1.domains_retrieve( + "id", + ) + assert_matches_type(ProjectV1DomainsRetrieveResponse, project_v1, path=["response"]) + + @parametrize + async def test_raw_response_domains_retrieve(self, async_client: AsyncUnlayer) -> None: + response = await async_client.project_v1.with_raw_response.domains_retrieve( + "id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + project_v1 = await response.parse() + assert_matches_type(ProjectV1DomainsRetrieveResponse, project_v1, path=["response"]) + + @parametrize + async def test_streaming_response_domains_retrieve(self, async_client: AsyncUnlayer) -> None: + async with async_client.project_v1.with_streaming_response.domains_retrieve( + "id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + project_v1 = await response.parse() + assert_matches_type(ProjectV1DomainsRetrieveResponse, project_v1, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_path_params_domains_retrieve(self, async_client: AsyncUnlayer) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): + await async_client.project_v1.with_raw_response.domains_retrieve( + "", + ) + + @parametrize + async def test_method_domains_update(self, async_client: AsyncUnlayer) -> None: + project_v1 = await async_client.project_v1.domains_update( + id="id", + ) + assert_matches_type(ProjectV1DomainsUpdateResponse, project_v1, path=["response"]) + + @parametrize + async def test_method_domains_update_with_all_params(self, async_client: AsyncUnlayer) -> None: + project_v1 = await async_client.project_v1.domains_update( + id="id", + domain="domain", + ) + assert_matches_type(ProjectV1DomainsUpdateResponse, project_v1, path=["response"]) + + @parametrize + async def test_raw_response_domains_update(self, async_client: AsyncUnlayer) -> None: + response = await async_client.project_v1.with_raw_response.domains_update( + id="id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + project_v1 = await response.parse() + assert_matches_type(ProjectV1DomainsUpdateResponse, project_v1, path=["response"]) + + @parametrize + async def test_streaming_response_domains_update(self, async_client: AsyncUnlayer) -> None: + async with async_client.project_v1.with_streaming_response.domains_update( + id="id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + project_v1 = await response.parse() + assert_matches_type(ProjectV1DomainsUpdateResponse, project_v1, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_path_params_domains_update(self, async_client: AsyncUnlayer) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): + await async_client.project_v1.with_raw_response.domains_update( + id="", + ) + + @parametrize + async def test_method_templates_create(self, async_client: AsyncUnlayer) -> None: + project_v1 = await async_client.project_v1.templates_create( + name="name", + ) + assert_matches_type(ProjectV1TemplatesCreateResponse, project_v1, path=["response"]) + + @parametrize + async def test_method_templates_create_with_all_params(self, async_client: AsyncUnlayer) -> None: + project_v1 = await async_client.project_v1.templates_create( + name="name", + body="body", + subject="subject", + ) + assert_matches_type(ProjectV1TemplatesCreateResponse, project_v1, path=["response"]) + + @parametrize + async def test_raw_response_templates_create(self, async_client: AsyncUnlayer) -> None: + response = await async_client.project_v1.with_raw_response.templates_create( + name="name", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + project_v1 = await response.parse() + assert_matches_type(ProjectV1TemplatesCreateResponse, project_v1, path=["response"]) + + @parametrize + async def test_streaming_response_templates_create(self, async_client: AsyncUnlayer) -> None: + async with async_client.project_v1.with_streaming_response.templates_create( + name="name", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + project_v1 = await response.parse() + assert_matches_type(ProjectV1TemplatesCreateResponse, project_v1, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_method_templates_delete(self, async_client: AsyncUnlayer) -> None: + project_v1 = await async_client.project_v1.templates_delete( + "id", + ) + assert project_v1 is None + + @parametrize + async def test_raw_response_templates_delete(self, async_client: AsyncUnlayer) -> None: + response = await async_client.project_v1.with_raw_response.templates_delete( + "id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + project_v1 = await response.parse() + assert project_v1 is None + + @parametrize + async def test_streaming_response_templates_delete(self, async_client: AsyncUnlayer) -> None: + async with async_client.project_v1.with_streaming_response.templates_delete( + "id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + project_v1 = await response.parse() + assert project_v1 is None + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_path_params_templates_delete(self, async_client: AsyncUnlayer) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): + await async_client.project_v1.with_raw_response.templates_delete( + "", + ) + + @parametrize + async def test_method_templates_list(self, async_client: AsyncUnlayer) -> None: + project_v1 = await async_client.project_v1.templates_list() + assert_matches_type(ProjectV1TemplatesListResponse, project_v1, path=["response"]) + + @parametrize + async def test_raw_response_templates_list(self, async_client: AsyncUnlayer) -> None: + response = await async_client.project_v1.with_raw_response.templates_list() + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + project_v1 = await response.parse() + assert_matches_type(ProjectV1TemplatesListResponse, project_v1, path=["response"]) + + @parametrize + async def test_streaming_response_templates_list(self, async_client: AsyncUnlayer) -> None: + async with async_client.project_v1.with_streaming_response.templates_list() as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + project_v1 = await response.parse() + assert_matches_type(ProjectV1TemplatesListResponse, project_v1, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_method_templates_retrieve(self, async_client: AsyncUnlayer) -> None: + project_v1 = await async_client.project_v1.templates_retrieve( + "id", + ) + assert_matches_type(ProjectV1TemplatesRetrieveResponse, project_v1, path=["response"]) + + @parametrize + async def test_raw_response_templates_retrieve(self, async_client: AsyncUnlayer) -> None: + response = await async_client.project_v1.with_raw_response.templates_retrieve( + "id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + project_v1 = await response.parse() + assert_matches_type(ProjectV1TemplatesRetrieveResponse, project_v1, path=["response"]) + + @parametrize + async def test_streaming_response_templates_retrieve(self, async_client: AsyncUnlayer) -> None: + async with async_client.project_v1.with_streaming_response.templates_retrieve( + "id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + project_v1 = await response.parse() + assert_matches_type(ProjectV1TemplatesRetrieveResponse, project_v1, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_path_params_templates_retrieve(self, async_client: AsyncUnlayer) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): + await async_client.project_v1.with_raw_response.templates_retrieve( + "", + ) + + @parametrize + async def test_method_templates_update(self, async_client: AsyncUnlayer) -> None: + project_v1 = await async_client.project_v1.templates_update( + id="id", + ) + assert_matches_type(ProjectV1TemplatesUpdateResponse, project_v1, path=["response"]) + + @parametrize + async def test_method_templates_update_with_all_params(self, async_client: AsyncUnlayer) -> None: + project_v1 = await async_client.project_v1.templates_update( + id="id", + body="body", + name="name", + subject="subject", + ) + assert_matches_type(ProjectV1TemplatesUpdateResponse, project_v1, path=["response"]) + + @parametrize + async def test_raw_response_templates_update(self, async_client: AsyncUnlayer) -> None: + response = await async_client.project_v1.with_raw_response.templates_update( + id="id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + project_v1 = await response.parse() + assert_matches_type(ProjectV1TemplatesUpdateResponse, project_v1, path=["response"]) + + @parametrize + async def test_streaming_response_templates_update(self, async_client: AsyncUnlayer) -> None: + async with async_client.project_v1.with_streaming_response.templates_update( + id="id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + project_v1 = await response.parse() + assert_matches_type(ProjectV1TemplatesUpdateResponse, project_v1, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_path_params_templates_update(self, async_client: AsyncUnlayer) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): + await async_client.project_v1.with_raw_response.templates_update( + id="", + ) diff --git a/tests/conftest.py b/tests/conftest.py new file mode 100644 index 0000000..fc6ba41 --- /dev/null +++ b/tests/conftest.py @@ -0,0 +1,84 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import os +import logging +from typing import TYPE_CHECKING, Iterator, AsyncIterator + +import httpx +import pytest +from pytest_asyncio import is_async_test + +from unlayer import Unlayer, AsyncUnlayer, DefaultAioHttpClient +from unlayer._utils import is_dict + +if TYPE_CHECKING: + from _pytest.fixtures import FixtureRequest # pyright: ignore[reportPrivateImportUsage] + +pytest.register_assert_rewrite("tests.utils") + +logging.getLogger("unlayer").setLevel(logging.DEBUG) + + +# automatically add `pytest.mark.asyncio()` to all of our async tests +# so we don't have to add that boilerplate everywhere +def pytest_collection_modifyitems(items: list[pytest.Function]) -> None: + pytest_asyncio_tests = (item for item in items if is_async_test(item)) + session_scope_marker = pytest.mark.asyncio(loop_scope="session") + for async_test in pytest_asyncio_tests: + async_test.add_marker(session_scope_marker, append=False) + + # We skip tests that use both the aiohttp client and respx_mock as respx_mock + # doesn't support custom transports. + for item in items: + if "async_client" not in item.fixturenames or "respx_mock" not in item.fixturenames: + continue + + if not hasattr(item, "callspec"): + continue + + async_client_param = item.callspec.params.get("async_client") + if is_dict(async_client_param) and async_client_param.get("http_client") == "aiohttp": + item.add_marker(pytest.mark.skip(reason="aiohttp client is not compatible with respx_mock")) + + +base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") + +api_key = "My API Key" + + +@pytest.fixture(scope="session") +def client(request: FixtureRequest) -> Iterator[Unlayer]: + strict = getattr(request, "param", True) + if not isinstance(strict, bool): + raise TypeError(f"Unexpected fixture parameter type {type(strict)}, expected {bool}") + + with Unlayer(base_url=base_url, api_key=api_key, _strict_response_validation=strict) as client: + yield client + + +@pytest.fixture(scope="session") +async def async_client(request: FixtureRequest) -> AsyncIterator[AsyncUnlayer]: + param = getattr(request, "param", True) + + # defaults + strict = True + http_client: None | httpx.AsyncClient = None + + if isinstance(param, bool): + strict = param + elif is_dict(param): + strict = param.get("strict", True) + assert isinstance(strict, bool) + + http_client_type = param.get("http_client", "httpx") + if http_client_type == "aiohttp": + http_client = DefaultAioHttpClient() + else: + raise TypeError(f"Unexpected fixture parameter type {type(param)}, expected bool or dict") + + async with AsyncUnlayer( + base_url=base_url, api_key=api_key, _strict_response_validation=strict, http_client=http_client + ) as client: + yield client diff --git a/tests/sample_file.txt b/tests/sample_file.txt new file mode 100644 index 0000000..af5626b --- /dev/null +++ b/tests/sample_file.txt @@ -0,0 +1 @@ +Hello, world! diff --git a/tests/test_client.py b/tests/test_client.py new file mode 100644 index 0000000..f8e2095 --- /dev/null +++ b/tests/test_client.py @@ -0,0 +1,1717 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import gc +import os +import sys +import json +import asyncio +import inspect +import tracemalloc +from typing import Any, Union, cast +from unittest import mock +from typing_extensions import Literal + +import httpx +import pytest +from respx import MockRouter +from pydantic import ValidationError + +from unlayer import Unlayer, AsyncUnlayer, APIResponseValidationError +from unlayer._types import Omit +from unlayer._utils import asyncify +from unlayer._models import BaseModel, FinalRequestOptions +from unlayer._exceptions import UnlayerError, APIStatusError, APITimeoutError, APIResponseValidationError +from unlayer._base_client import ( + DEFAULT_TIMEOUT, + HTTPX_DEFAULT_TIMEOUT, + BaseClient, + OtherPlatform, + DefaultHttpxClient, + DefaultAsyncHttpxClient, + get_platform, + make_request_options, +) + +from .utils import update_env + +base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") +api_key = "My API Key" + + +def _get_params(client: BaseClient[Any, Any]) -> dict[str, str]: + request = client._build_request(FinalRequestOptions(method="get", url="/foo")) + url = httpx.URL(request.url) + return dict(url.params) + + +def _low_retry_timeout(*_args: Any, **_kwargs: Any) -> float: + return 0.1 + + +def _get_open_connections(client: Unlayer | AsyncUnlayer) -> int: + transport = client._client._transport + assert isinstance(transport, httpx.HTTPTransport) or isinstance(transport, httpx.AsyncHTTPTransport) + + pool = transport._pool + return len(pool._requests) + + +class TestUnlayer: + @pytest.mark.respx(base_url=base_url) + def test_raw_response(self, respx_mock: MockRouter, client: Unlayer) -> None: + respx_mock.post("/foo").mock(return_value=httpx.Response(200, json={"foo": "bar"})) + + response = client.post("/foo", cast_to=httpx.Response) + assert response.status_code == 200 + assert isinstance(response, httpx.Response) + assert response.json() == {"foo": "bar"} + + @pytest.mark.respx(base_url=base_url) + def test_raw_response_for_binary(self, respx_mock: MockRouter, client: Unlayer) -> None: + respx_mock.post("/foo").mock( + return_value=httpx.Response(200, headers={"Content-Type": "application/binary"}, content='{"foo": "bar"}') + ) + + response = client.post("/foo", cast_to=httpx.Response) + assert response.status_code == 200 + assert isinstance(response, httpx.Response) + assert response.json() == {"foo": "bar"} + + def test_copy(self, client: Unlayer) -> None: + copied = client.copy() + assert id(copied) != id(client) + + copied = client.copy(api_key="another My API Key") + assert copied.api_key == "another My API Key" + assert client.api_key == "My API Key" + + def test_copy_default_options(self, client: Unlayer) -> None: + # options that have a default are overridden correctly + copied = client.copy(max_retries=7) + assert copied.max_retries == 7 + assert client.max_retries == 2 + + copied2 = copied.copy(max_retries=6) + assert copied2.max_retries == 6 + assert copied.max_retries == 7 + + # timeout + assert isinstance(client.timeout, httpx.Timeout) + copied = client.copy(timeout=None) + assert copied.timeout is None + assert isinstance(client.timeout, httpx.Timeout) + + def test_copy_default_headers(self) -> None: + client = Unlayer( + base_url=base_url, api_key=api_key, _strict_response_validation=True, default_headers={"X-Foo": "bar"} + ) + assert client.default_headers["X-Foo"] == "bar" + + # does not override the already given value when not specified + copied = client.copy() + assert copied.default_headers["X-Foo"] == "bar" + + # merges already given headers + copied = client.copy(default_headers={"X-Bar": "stainless"}) + assert copied.default_headers["X-Foo"] == "bar" + assert copied.default_headers["X-Bar"] == "stainless" + + # uses new values for any already given headers + copied = client.copy(default_headers={"X-Foo": "stainless"}) + assert copied.default_headers["X-Foo"] == "stainless" + + # set_default_headers + + # completely overrides already set values + copied = client.copy(set_default_headers={}) + assert copied.default_headers.get("X-Foo") is None + + copied = client.copy(set_default_headers={"X-Bar": "Robert"}) + assert copied.default_headers["X-Bar"] == "Robert" + + with pytest.raises( + ValueError, + match="`default_headers` and `set_default_headers` arguments are mutually exclusive", + ): + client.copy(set_default_headers={}, default_headers={"X-Foo": "Bar"}) + client.close() + + def test_copy_default_query(self) -> None: + client = Unlayer( + base_url=base_url, api_key=api_key, _strict_response_validation=True, default_query={"foo": "bar"} + ) + assert _get_params(client)["foo"] == "bar" + + # does not override the already given value when not specified + copied = client.copy() + assert _get_params(copied)["foo"] == "bar" + + # merges already given params + copied = client.copy(default_query={"bar": "stainless"}) + params = _get_params(copied) + assert params["foo"] == "bar" + assert params["bar"] == "stainless" + + # uses new values for any already given headers + copied = client.copy(default_query={"foo": "stainless"}) + assert _get_params(copied)["foo"] == "stainless" + + # set_default_query + + # completely overrides already set values + copied = client.copy(set_default_query={}) + assert _get_params(copied) == {} + + copied = client.copy(set_default_query={"bar": "Robert"}) + assert _get_params(copied)["bar"] == "Robert" + + with pytest.raises( + ValueError, + # TODO: update + match="`default_query` and `set_default_query` arguments are mutually exclusive", + ): + client.copy(set_default_query={}, default_query={"foo": "Bar"}) + + client.close() + + def test_copy_signature(self, client: Unlayer) -> None: + # ensure the same parameters that can be passed to the client are defined in the `.copy()` method + init_signature = inspect.signature( + # mypy doesn't like that we access the `__init__` property. + client.__init__, # type: ignore[misc] + ) + copy_signature = inspect.signature(client.copy) + exclude_params = {"transport", "proxies", "_strict_response_validation"} + + for name in init_signature.parameters.keys(): + if name in exclude_params: + continue + + copy_param = copy_signature.parameters.get(name) + assert copy_param is not None, f"copy() signature is missing the {name} param" + + @pytest.mark.skipif(sys.version_info >= (3, 10), reason="fails because of a memory leak that started from 3.12") + def test_copy_build_request(self, client: Unlayer) -> None: + options = FinalRequestOptions(method="get", url="/foo") + + def build_request(options: FinalRequestOptions) -> None: + client_copy = client.copy() + client_copy._build_request(options) + + # ensure that the machinery is warmed up before tracing starts. + build_request(options) + gc.collect() + + tracemalloc.start(1000) + + snapshot_before = tracemalloc.take_snapshot() + + ITERATIONS = 10 + for _ in range(ITERATIONS): + build_request(options) + + gc.collect() + snapshot_after = tracemalloc.take_snapshot() + + tracemalloc.stop() + + def add_leak(leaks: list[tracemalloc.StatisticDiff], diff: tracemalloc.StatisticDiff) -> None: + if diff.count == 0: + # Avoid false positives by considering only leaks (i.e. allocations that persist). + return + + if diff.count % ITERATIONS != 0: + # Avoid false positives by considering only leaks that appear per iteration. + return + + for frame in diff.traceback: + if any( + frame.filename.endswith(fragment) + for fragment in [ + # to_raw_response_wrapper leaks through the @functools.wraps() decorator. + # + # removing the decorator fixes the leak for reasons we don't understand. + "unlayer/_legacy_response.py", + "unlayer/_response.py", + # pydantic.BaseModel.model_dump || pydantic.BaseModel.dict leak memory for some reason. + "unlayer/_compat.py", + # Standard library leaks we don't care about. + "/logging/__init__.py", + ] + ): + return + + leaks.append(diff) + + leaks: list[tracemalloc.StatisticDiff] = [] + for diff in snapshot_after.compare_to(snapshot_before, "traceback"): + add_leak(leaks, diff) + if leaks: + for leak in leaks: + print("MEMORY LEAK:", leak) + for frame in leak.traceback: + print(frame) + raise AssertionError() + + def test_request_timeout(self, client: Unlayer) -> None: + request = client._build_request(FinalRequestOptions(method="get", url="/foo")) + timeout = httpx.Timeout(**request.extensions["timeout"]) # type: ignore + assert timeout == DEFAULT_TIMEOUT + + request = client._build_request(FinalRequestOptions(method="get", url="/foo", timeout=httpx.Timeout(100.0))) + timeout = httpx.Timeout(**request.extensions["timeout"]) # type: ignore + assert timeout == httpx.Timeout(100.0) + + def test_client_timeout_option(self) -> None: + client = Unlayer(base_url=base_url, api_key=api_key, _strict_response_validation=True, timeout=httpx.Timeout(0)) + + request = client._build_request(FinalRequestOptions(method="get", url="/foo")) + timeout = httpx.Timeout(**request.extensions["timeout"]) # type: ignore + assert timeout == httpx.Timeout(0) + + client.close() + + def test_http_client_timeout_option(self) -> None: + # custom timeout given to the httpx client should be used + with httpx.Client(timeout=None) as http_client: + client = Unlayer( + base_url=base_url, api_key=api_key, _strict_response_validation=True, http_client=http_client + ) + + request = client._build_request(FinalRequestOptions(method="get", url="/foo")) + timeout = httpx.Timeout(**request.extensions["timeout"]) # type: ignore + assert timeout == httpx.Timeout(None) + + client.close() + + # no timeout given to the httpx client should not use the httpx default + with httpx.Client() as http_client: + client = Unlayer( + base_url=base_url, api_key=api_key, _strict_response_validation=True, http_client=http_client + ) + + request = client._build_request(FinalRequestOptions(method="get", url="/foo")) + timeout = httpx.Timeout(**request.extensions["timeout"]) # type: ignore + assert timeout == DEFAULT_TIMEOUT + + client.close() + + # explicitly passing the default timeout currently results in it being ignored + with httpx.Client(timeout=HTTPX_DEFAULT_TIMEOUT) as http_client: + client = Unlayer( + base_url=base_url, api_key=api_key, _strict_response_validation=True, http_client=http_client + ) + + request = client._build_request(FinalRequestOptions(method="get", url="/foo")) + timeout = httpx.Timeout(**request.extensions["timeout"]) # type: ignore + assert timeout == DEFAULT_TIMEOUT # our default + + client.close() + + async def test_invalid_http_client(self) -> None: + with pytest.raises(TypeError, match="Invalid `http_client` arg"): + async with httpx.AsyncClient() as http_client: + Unlayer( + base_url=base_url, + api_key=api_key, + _strict_response_validation=True, + http_client=cast(Any, http_client), + ) + + def test_default_headers_option(self) -> None: + test_client = Unlayer( + base_url=base_url, api_key=api_key, _strict_response_validation=True, default_headers={"X-Foo": "bar"} + ) + request = test_client._build_request(FinalRequestOptions(method="get", url="/foo")) + assert request.headers.get("x-foo") == "bar" + assert request.headers.get("x-stainless-lang") == "python" + + test_client2 = Unlayer( + base_url=base_url, + api_key=api_key, + _strict_response_validation=True, + default_headers={ + "X-Foo": "stainless", + "X-Stainless-Lang": "my-overriding-header", + }, + ) + request = test_client2._build_request(FinalRequestOptions(method="get", url="/foo")) + assert request.headers.get("x-foo") == "stainless" + assert request.headers.get("x-stainless-lang") == "my-overriding-header" + + test_client.close() + test_client2.close() + + def test_validate_headers(self) -> None: + client = Unlayer(base_url=base_url, api_key=api_key, _strict_response_validation=True) + request = client._build_request(FinalRequestOptions(method="get", url="/foo")) + assert request.headers.get("Authorization") == f"Bearer {api_key}" + + with pytest.raises(UnlayerError): + with update_env(**{"UNLAYER_API_KEY": Omit()}): + client2 = Unlayer(base_url=base_url, api_key=None, _strict_response_validation=True) + _ = client2 + + def test_default_query_option(self) -> None: + client = Unlayer( + base_url=base_url, api_key=api_key, _strict_response_validation=True, default_query={"query_param": "bar"} + ) + request = client._build_request(FinalRequestOptions(method="get", url="/foo")) + url = httpx.URL(request.url) + assert dict(url.params) == {"query_param": "bar"} + + request = client._build_request( + FinalRequestOptions( + method="get", + url="/foo", + params={"foo": "baz", "query_param": "overridden"}, + ) + ) + url = httpx.URL(request.url) + assert dict(url.params) == {"foo": "baz", "query_param": "overridden"} + + client.close() + + def test_request_extra_json(self, client: Unlayer) -> None: + request = client._build_request( + FinalRequestOptions( + method="post", + url="/foo", + json_data={"foo": "bar"}, + extra_json={"baz": False}, + ), + ) + data = json.loads(request.content.decode("utf-8")) + assert data == {"foo": "bar", "baz": False} + + request = client._build_request( + FinalRequestOptions( + method="post", + url="/foo", + extra_json={"baz": False}, + ), + ) + data = json.loads(request.content.decode("utf-8")) + assert data == {"baz": False} + + # `extra_json` takes priority over `json_data` when keys clash + request = client._build_request( + FinalRequestOptions( + method="post", + url="/foo", + json_data={"foo": "bar", "baz": True}, + extra_json={"baz": None}, + ), + ) + data = json.loads(request.content.decode("utf-8")) + assert data == {"foo": "bar", "baz": None} + + def test_request_extra_headers(self, client: Unlayer) -> None: + request = client._build_request( + FinalRequestOptions( + method="post", + url="/foo", + **make_request_options(extra_headers={"X-Foo": "Foo"}), + ), + ) + assert request.headers.get("X-Foo") == "Foo" + + # `extra_headers` takes priority over `default_headers` when keys clash + request = client.with_options(default_headers={"X-Bar": "true"})._build_request( + FinalRequestOptions( + method="post", + url="/foo", + **make_request_options( + extra_headers={"X-Bar": "false"}, + ), + ), + ) + assert request.headers.get("X-Bar") == "false" + + def test_request_extra_query(self, client: Unlayer) -> None: + request = client._build_request( + FinalRequestOptions( + method="post", + url="/foo", + **make_request_options( + extra_query={"my_query_param": "Foo"}, + ), + ), + ) + params = dict(request.url.params) + assert params == {"my_query_param": "Foo"} + + # if both `query` and `extra_query` are given, they are merged + request = client._build_request( + FinalRequestOptions( + method="post", + url="/foo", + **make_request_options( + query={"bar": "1"}, + extra_query={"foo": "2"}, + ), + ), + ) + params = dict(request.url.params) + assert params == {"bar": "1", "foo": "2"} + + # `extra_query` takes priority over `query` when keys clash + request = client._build_request( + FinalRequestOptions( + method="post", + url="/foo", + **make_request_options( + query={"foo": "1"}, + extra_query={"foo": "2"}, + ), + ), + ) + params = dict(request.url.params) + assert params == {"foo": "2"} + + def test_multipart_repeating_array(self, client: Unlayer) -> None: + request = client._build_request( + FinalRequestOptions.construct( + method="post", + url="/foo", + headers={"Content-Type": "multipart/form-data; boundary=6b7ba517decee4a450543ea6ae821c82"}, + json_data={"array": ["foo", "bar"]}, + files=[("foo.txt", b"hello world")], + ) + ) + + assert request.read().split(b"\r\n") == [ + b"--6b7ba517decee4a450543ea6ae821c82", + b'Content-Disposition: form-data; name="array[]"', + b"", + b"foo", + b"--6b7ba517decee4a450543ea6ae821c82", + b'Content-Disposition: form-data; name="array[]"', + b"", + b"bar", + b"--6b7ba517decee4a450543ea6ae821c82", + b'Content-Disposition: form-data; name="foo.txt"; filename="upload"', + b"Content-Type: application/octet-stream", + b"", + b"hello world", + b"--6b7ba517decee4a450543ea6ae821c82--", + b"", + ] + + @pytest.mark.respx(base_url=base_url) + def test_basic_union_response(self, respx_mock: MockRouter, client: Unlayer) -> None: + class Model1(BaseModel): + name: str + + class Model2(BaseModel): + foo: str + + respx_mock.get("/foo").mock(return_value=httpx.Response(200, json={"foo": "bar"})) + + response = client.get("/foo", cast_to=cast(Any, Union[Model1, Model2])) + assert isinstance(response, Model2) + assert response.foo == "bar" + + @pytest.mark.respx(base_url=base_url) + def test_union_response_different_types(self, respx_mock: MockRouter, client: Unlayer) -> None: + """Union of objects with the same field name using a different type""" + + class Model1(BaseModel): + foo: int + + class Model2(BaseModel): + foo: str + + respx_mock.get("/foo").mock(return_value=httpx.Response(200, json={"foo": "bar"})) + + response = client.get("/foo", cast_to=cast(Any, Union[Model1, Model2])) + assert isinstance(response, Model2) + assert response.foo == "bar" + + respx_mock.get("/foo").mock(return_value=httpx.Response(200, json={"foo": 1})) + + response = client.get("/foo", cast_to=cast(Any, Union[Model1, Model2])) + assert isinstance(response, Model1) + assert response.foo == 1 + + @pytest.mark.respx(base_url=base_url) + def test_non_application_json_content_type_for_json_data(self, respx_mock: MockRouter, client: Unlayer) -> None: + """ + Response that sets Content-Type to something other than application/json but returns json data + """ + + class Model(BaseModel): + foo: int + + respx_mock.get("/foo").mock( + return_value=httpx.Response( + 200, + content=json.dumps({"foo": 2}), + headers={"Content-Type": "application/text"}, + ) + ) + + response = client.get("/foo", cast_to=Model) + assert isinstance(response, Model) + assert response.foo == 2 + + def test_base_url_setter(self) -> None: + client = Unlayer(base_url="https://example.com/from_init", api_key=api_key, _strict_response_validation=True) + assert client.base_url == "https://example.com/from_init/" + + client.base_url = "https://example.com/from_setter" # type: ignore[assignment] + + assert client.base_url == "https://example.com/from_setter/" + + client.close() + + def test_base_url_env(self) -> None: + with update_env(UNLAYER_BASE_URL="http://localhost:5000/from/env"): + client = Unlayer(api_key=api_key, _strict_response_validation=True) + assert client.base_url == "http://localhost:5000/from/env/" + + @pytest.mark.parametrize( + "client", + [ + Unlayer(base_url="http://localhost:5000/custom/path/", api_key=api_key, _strict_response_validation=True), + Unlayer( + base_url="http://localhost:5000/custom/path/", + api_key=api_key, + _strict_response_validation=True, + http_client=httpx.Client(), + ), + ], + ids=["standard", "custom http client"], + ) + def test_base_url_trailing_slash(self, client: Unlayer) -> None: + request = client._build_request( + FinalRequestOptions( + method="post", + url="/foo", + json_data={"foo": "bar"}, + ), + ) + assert request.url == "http://localhost:5000/custom/path/foo" + client.close() + + @pytest.mark.parametrize( + "client", + [ + Unlayer(base_url="http://localhost:5000/custom/path/", api_key=api_key, _strict_response_validation=True), + Unlayer( + base_url="http://localhost:5000/custom/path/", + api_key=api_key, + _strict_response_validation=True, + http_client=httpx.Client(), + ), + ], + ids=["standard", "custom http client"], + ) + def test_base_url_no_trailing_slash(self, client: Unlayer) -> None: + request = client._build_request( + FinalRequestOptions( + method="post", + url="/foo", + json_data={"foo": "bar"}, + ), + ) + assert request.url == "http://localhost:5000/custom/path/foo" + client.close() + + @pytest.mark.parametrize( + "client", + [ + Unlayer(base_url="http://localhost:5000/custom/path/", api_key=api_key, _strict_response_validation=True), + Unlayer( + base_url="http://localhost:5000/custom/path/", + api_key=api_key, + _strict_response_validation=True, + http_client=httpx.Client(), + ), + ], + ids=["standard", "custom http client"], + ) + def test_absolute_request_url(self, client: Unlayer) -> None: + request = client._build_request( + FinalRequestOptions( + method="post", + url="https://myapi.com/foo", + json_data={"foo": "bar"}, + ), + ) + assert request.url == "https://myapi.com/foo" + client.close() + + def test_copied_client_does_not_close_http(self) -> None: + test_client = Unlayer(base_url=base_url, api_key=api_key, _strict_response_validation=True) + assert not test_client.is_closed() + + copied = test_client.copy() + assert copied is not test_client + + del copied + + assert not test_client.is_closed() + + def test_client_context_manager(self) -> None: + test_client = Unlayer(base_url=base_url, api_key=api_key, _strict_response_validation=True) + with test_client as c2: + assert c2 is test_client + assert not c2.is_closed() + assert not test_client.is_closed() + assert test_client.is_closed() + + @pytest.mark.respx(base_url=base_url) + def test_client_response_validation_error(self, respx_mock: MockRouter, client: Unlayer) -> None: + class Model(BaseModel): + foo: str + + respx_mock.get("/foo").mock(return_value=httpx.Response(200, json={"foo": {"invalid": True}})) + + with pytest.raises(APIResponseValidationError) as exc: + client.get("/foo", cast_to=Model) + + assert isinstance(exc.value.__cause__, ValidationError) + + def test_client_max_retries_validation(self) -> None: + with pytest.raises(TypeError, match=r"max_retries cannot be None"): + Unlayer(base_url=base_url, api_key=api_key, _strict_response_validation=True, max_retries=cast(Any, None)) + + @pytest.mark.respx(base_url=base_url) + def test_received_text_for_expected_json(self, respx_mock: MockRouter) -> None: + class Model(BaseModel): + name: str + + respx_mock.get("/foo").mock(return_value=httpx.Response(200, text="my-custom-format")) + + strict_client = Unlayer(base_url=base_url, api_key=api_key, _strict_response_validation=True) + + with pytest.raises(APIResponseValidationError): + strict_client.get("/foo", cast_to=Model) + + non_strict_client = Unlayer(base_url=base_url, api_key=api_key, _strict_response_validation=False) + + response = non_strict_client.get("/foo", cast_to=Model) + assert isinstance(response, str) # type: ignore[unreachable] + + strict_client.close() + non_strict_client.close() + + @pytest.mark.parametrize( + "remaining_retries,retry_after,timeout", + [ + [3, "20", 20], + [3, "0", 0.5], + [3, "-10", 0.5], + [3, "60", 60], + [3, "61", 0.5], + [3, "Fri, 29 Sep 2023 16:26:57 GMT", 20], + [3, "Fri, 29 Sep 2023 16:26:37 GMT", 0.5], + [3, "Fri, 29 Sep 2023 16:26:27 GMT", 0.5], + [3, "Fri, 29 Sep 2023 16:27:37 GMT", 60], + [3, "Fri, 29 Sep 2023 16:27:38 GMT", 0.5], + [3, "99999999999999999999999999999999999", 0.5], + [3, "Zun, 29 Sep 2023 16:26:27 GMT", 0.5], + [3, "", 0.5], + [2, "", 0.5 * 2.0], + [1, "", 0.5 * 4.0], + [-1100, "", 8], # test large number potentially overflowing + ], + ) + @mock.patch("time.time", mock.MagicMock(return_value=1696004797)) + def test_parse_retry_after_header( + self, remaining_retries: int, retry_after: str, timeout: float, client: Unlayer + ) -> None: + headers = httpx.Headers({"retry-after": retry_after}) + options = FinalRequestOptions(method="get", url="/foo", max_retries=3) + calculated = client._calculate_retry_timeout(remaining_retries, options, headers) + assert calculated == pytest.approx(timeout, 0.5 * 0.875) # pyright: ignore[reportUnknownMemberType] + + @mock.patch("unlayer._base_client.BaseClient._calculate_retry_timeout", _low_retry_timeout) + @pytest.mark.respx(base_url=base_url) + def test_retrying_timeout_errors_doesnt_leak(self, respx_mock: MockRouter, client: Unlayer) -> None: + respx_mock.get("/project/v1/current").mock(side_effect=httpx.TimeoutException("Test timeout error")) + + with pytest.raises(APITimeoutError): + client.project_v1.with_streaming_response.current_list().__enter__() + + assert _get_open_connections(client) == 0 + + @mock.patch("unlayer._base_client.BaseClient._calculate_retry_timeout", _low_retry_timeout) + @pytest.mark.respx(base_url=base_url) + def test_retrying_status_errors_doesnt_leak(self, respx_mock: MockRouter, client: Unlayer) -> None: + respx_mock.get("/project/v1/current").mock(return_value=httpx.Response(500)) + + with pytest.raises(APIStatusError): + client.project_v1.with_streaming_response.current_list().__enter__() + assert _get_open_connections(client) == 0 + + @pytest.mark.parametrize("failures_before_success", [0, 2, 4]) + @mock.patch("unlayer._base_client.BaseClient._calculate_retry_timeout", _low_retry_timeout) + @pytest.mark.respx(base_url=base_url) + @pytest.mark.parametrize("failure_mode", ["status", "exception"]) + def test_retries_taken( + self, + client: Unlayer, + failures_before_success: int, + failure_mode: Literal["status", "exception"], + respx_mock: MockRouter, + ) -> None: + client = client.with_options(max_retries=4) + + nb_retries = 0 + + def retry_handler(_request: httpx.Request) -> httpx.Response: + nonlocal nb_retries + if nb_retries < failures_before_success: + nb_retries += 1 + if failure_mode == "exception": + raise RuntimeError("oops") + return httpx.Response(500) + return httpx.Response(200) + + respx_mock.get("/project/v1/current").mock(side_effect=retry_handler) + + response = client.project_v1.with_raw_response.current_list() + + assert response.retries_taken == failures_before_success + assert int(response.http_request.headers.get("x-stainless-retry-count")) == failures_before_success + + @pytest.mark.parametrize("failures_before_success", [0, 2, 4]) + @mock.patch("unlayer._base_client.BaseClient._calculate_retry_timeout", _low_retry_timeout) + @pytest.mark.respx(base_url=base_url) + def test_omit_retry_count_header( + self, client: Unlayer, failures_before_success: int, respx_mock: MockRouter + ) -> None: + client = client.with_options(max_retries=4) + + nb_retries = 0 + + def retry_handler(_request: httpx.Request) -> httpx.Response: + nonlocal nb_retries + if nb_retries < failures_before_success: + nb_retries += 1 + return httpx.Response(500) + return httpx.Response(200) + + respx_mock.get("/project/v1/current").mock(side_effect=retry_handler) + + response = client.project_v1.with_raw_response.current_list(extra_headers={"x-stainless-retry-count": Omit()}) + + assert len(response.http_request.headers.get_list("x-stainless-retry-count")) == 0 + + @pytest.mark.parametrize("failures_before_success", [0, 2, 4]) + @mock.patch("unlayer._base_client.BaseClient._calculate_retry_timeout", _low_retry_timeout) + @pytest.mark.respx(base_url=base_url) + def test_overwrite_retry_count_header( + self, client: Unlayer, failures_before_success: int, respx_mock: MockRouter + ) -> None: + client = client.with_options(max_retries=4) + + nb_retries = 0 + + def retry_handler(_request: httpx.Request) -> httpx.Response: + nonlocal nb_retries + if nb_retries < failures_before_success: + nb_retries += 1 + return httpx.Response(500) + return httpx.Response(200) + + respx_mock.get("/project/v1/current").mock(side_effect=retry_handler) + + response = client.project_v1.with_raw_response.current_list(extra_headers={"x-stainless-retry-count": "42"}) + + assert response.http_request.headers.get("x-stainless-retry-count") == "42" + + def test_proxy_environment_variables(self, monkeypatch: pytest.MonkeyPatch) -> None: + # Test that the proxy environment variables are set correctly + monkeypatch.setenv("HTTPS_PROXY", "https://example.org") + + client = DefaultHttpxClient() + + mounts = tuple(client._mounts.items()) + assert len(mounts) == 1 + assert mounts[0][0].pattern == "https://" + + @pytest.mark.filterwarnings("ignore:.*deprecated.*:DeprecationWarning") + def test_default_client_creation(self) -> None: + # Ensure that the client can be initialized without any exceptions + DefaultHttpxClient( + verify=True, + cert=None, + trust_env=True, + http1=True, + http2=False, + limits=httpx.Limits(max_connections=100, max_keepalive_connections=20), + ) + + @pytest.mark.respx(base_url=base_url) + def test_follow_redirects(self, respx_mock: MockRouter, client: Unlayer) -> None: + # Test that the default follow_redirects=True allows following redirects + respx_mock.post("/redirect").mock( + return_value=httpx.Response(302, headers={"Location": f"{base_url}/redirected"}) + ) + respx_mock.get("/redirected").mock(return_value=httpx.Response(200, json={"status": "ok"})) + + response = client.post("/redirect", body={"key": "value"}, cast_to=httpx.Response) + assert response.status_code == 200 + assert response.json() == {"status": "ok"} + + @pytest.mark.respx(base_url=base_url) + def test_follow_redirects_disabled(self, respx_mock: MockRouter, client: Unlayer) -> None: + # Test that follow_redirects=False prevents following redirects + respx_mock.post("/redirect").mock( + return_value=httpx.Response(302, headers={"Location": f"{base_url}/redirected"}) + ) + + with pytest.raises(APIStatusError) as exc_info: + client.post("/redirect", body={"key": "value"}, options={"follow_redirects": False}, cast_to=httpx.Response) + + assert exc_info.value.response.status_code == 302 + assert exc_info.value.response.headers["Location"] == f"{base_url}/redirected" + + +class TestAsyncUnlayer: + @pytest.mark.respx(base_url=base_url) + async def test_raw_response(self, respx_mock: MockRouter, async_client: AsyncUnlayer) -> None: + respx_mock.post("/foo").mock(return_value=httpx.Response(200, json={"foo": "bar"})) + + response = await async_client.post("/foo", cast_to=httpx.Response) + assert response.status_code == 200 + assert isinstance(response, httpx.Response) + assert response.json() == {"foo": "bar"} + + @pytest.mark.respx(base_url=base_url) + async def test_raw_response_for_binary(self, respx_mock: MockRouter, async_client: AsyncUnlayer) -> None: + respx_mock.post("/foo").mock( + return_value=httpx.Response(200, headers={"Content-Type": "application/binary"}, content='{"foo": "bar"}') + ) + + response = await async_client.post("/foo", cast_to=httpx.Response) + assert response.status_code == 200 + assert isinstance(response, httpx.Response) + assert response.json() == {"foo": "bar"} + + def test_copy(self, async_client: AsyncUnlayer) -> None: + copied = async_client.copy() + assert id(copied) != id(async_client) + + copied = async_client.copy(api_key="another My API Key") + assert copied.api_key == "another My API Key" + assert async_client.api_key == "My API Key" + + def test_copy_default_options(self, async_client: AsyncUnlayer) -> None: + # options that have a default are overridden correctly + copied = async_client.copy(max_retries=7) + assert copied.max_retries == 7 + assert async_client.max_retries == 2 + + copied2 = copied.copy(max_retries=6) + assert copied2.max_retries == 6 + assert copied.max_retries == 7 + + # timeout + assert isinstance(async_client.timeout, httpx.Timeout) + copied = async_client.copy(timeout=None) + assert copied.timeout is None + assert isinstance(async_client.timeout, httpx.Timeout) + + async def test_copy_default_headers(self) -> None: + client = AsyncUnlayer( + base_url=base_url, api_key=api_key, _strict_response_validation=True, default_headers={"X-Foo": "bar"} + ) + assert client.default_headers["X-Foo"] == "bar" + + # does not override the already given value when not specified + copied = client.copy() + assert copied.default_headers["X-Foo"] == "bar" + + # merges already given headers + copied = client.copy(default_headers={"X-Bar": "stainless"}) + assert copied.default_headers["X-Foo"] == "bar" + assert copied.default_headers["X-Bar"] == "stainless" + + # uses new values for any already given headers + copied = client.copy(default_headers={"X-Foo": "stainless"}) + assert copied.default_headers["X-Foo"] == "stainless" + + # set_default_headers + + # completely overrides already set values + copied = client.copy(set_default_headers={}) + assert copied.default_headers.get("X-Foo") is None + + copied = client.copy(set_default_headers={"X-Bar": "Robert"}) + assert copied.default_headers["X-Bar"] == "Robert" + + with pytest.raises( + ValueError, + match="`default_headers` and `set_default_headers` arguments are mutually exclusive", + ): + client.copy(set_default_headers={}, default_headers={"X-Foo": "Bar"}) + await client.close() + + async def test_copy_default_query(self) -> None: + client = AsyncUnlayer( + base_url=base_url, api_key=api_key, _strict_response_validation=True, default_query={"foo": "bar"} + ) + assert _get_params(client)["foo"] == "bar" + + # does not override the already given value when not specified + copied = client.copy() + assert _get_params(copied)["foo"] == "bar" + + # merges already given params + copied = client.copy(default_query={"bar": "stainless"}) + params = _get_params(copied) + assert params["foo"] == "bar" + assert params["bar"] == "stainless" + + # uses new values for any already given headers + copied = client.copy(default_query={"foo": "stainless"}) + assert _get_params(copied)["foo"] == "stainless" + + # set_default_query + + # completely overrides already set values + copied = client.copy(set_default_query={}) + assert _get_params(copied) == {} + + copied = client.copy(set_default_query={"bar": "Robert"}) + assert _get_params(copied)["bar"] == "Robert" + + with pytest.raises( + ValueError, + # TODO: update + match="`default_query` and `set_default_query` arguments are mutually exclusive", + ): + client.copy(set_default_query={}, default_query={"foo": "Bar"}) + + await client.close() + + def test_copy_signature(self, async_client: AsyncUnlayer) -> None: + # ensure the same parameters that can be passed to the client are defined in the `.copy()` method + init_signature = inspect.signature( + # mypy doesn't like that we access the `__init__` property. + async_client.__init__, # type: ignore[misc] + ) + copy_signature = inspect.signature(async_client.copy) + exclude_params = {"transport", "proxies", "_strict_response_validation"} + + for name in init_signature.parameters.keys(): + if name in exclude_params: + continue + + copy_param = copy_signature.parameters.get(name) + assert copy_param is not None, f"copy() signature is missing the {name} param" + + @pytest.mark.skipif(sys.version_info >= (3, 10), reason="fails because of a memory leak that started from 3.12") + def test_copy_build_request(self, async_client: AsyncUnlayer) -> None: + options = FinalRequestOptions(method="get", url="/foo") + + def build_request(options: FinalRequestOptions) -> None: + client_copy = async_client.copy() + client_copy._build_request(options) + + # ensure that the machinery is warmed up before tracing starts. + build_request(options) + gc.collect() + + tracemalloc.start(1000) + + snapshot_before = tracemalloc.take_snapshot() + + ITERATIONS = 10 + for _ in range(ITERATIONS): + build_request(options) + + gc.collect() + snapshot_after = tracemalloc.take_snapshot() + + tracemalloc.stop() + + def add_leak(leaks: list[tracemalloc.StatisticDiff], diff: tracemalloc.StatisticDiff) -> None: + if diff.count == 0: + # Avoid false positives by considering only leaks (i.e. allocations that persist). + return + + if diff.count % ITERATIONS != 0: + # Avoid false positives by considering only leaks that appear per iteration. + return + + for frame in diff.traceback: + if any( + frame.filename.endswith(fragment) + for fragment in [ + # to_raw_response_wrapper leaks through the @functools.wraps() decorator. + # + # removing the decorator fixes the leak for reasons we don't understand. + "unlayer/_legacy_response.py", + "unlayer/_response.py", + # pydantic.BaseModel.model_dump || pydantic.BaseModel.dict leak memory for some reason. + "unlayer/_compat.py", + # Standard library leaks we don't care about. + "/logging/__init__.py", + ] + ): + return + + leaks.append(diff) + + leaks: list[tracemalloc.StatisticDiff] = [] + for diff in snapshot_after.compare_to(snapshot_before, "traceback"): + add_leak(leaks, diff) + if leaks: + for leak in leaks: + print("MEMORY LEAK:", leak) + for frame in leak.traceback: + print(frame) + raise AssertionError() + + async def test_request_timeout(self, async_client: AsyncUnlayer) -> None: + request = async_client._build_request(FinalRequestOptions(method="get", url="/foo")) + timeout = httpx.Timeout(**request.extensions["timeout"]) # type: ignore + assert timeout == DEFAULT_TIMEOUT + + request = async_client._build_request( + FinalRequestOptions(method="get", url="/foo", timeout=httpx.Timeout(100.0)) + ) + timeout = httpx.Timeout(**request.extensions["timeout"]) # type: ignore + assert timeout == httpx.Timeout(100.0) + + async def test_client_timeout_option(self) -> None: + client = AsyncUnlayer( + base_url=base_url, api_key=api_key, _strict_response_validation=True, timeout=httpx.Timeout(0) + ) + + request = client._build_request(FinalRequestOptions(method="get", url="/foo")) + timeout = httpx.Timeout(**request.extensions["timeout"]) # type: ignore + assert timeout == httpx.Timeout(0) + + await client.close() + + async def test_http_client_timeout_option(self) -> None: + # custom timeout given to the httpx client should be used + async with httpx.AsyncClient(timeout=None) as http_client: + client = AsyncUnlayer( + base_url=base_url, api_key=api_key, _strict_response_validation=True, http_client=http_client + ) + + request = client._build_request(FinalRequestOptions(method="get", url="/foo")) + timeout = httpx.Timeout(**request.extensions["timeout"]) # type: ignore + assert timeout == httpx.Timeout(None) + + await client.close() + + # no timeout given to the httpx client should not use the httpx default + async with httpx.AsyncClient() as http_client: + client = AsyncUnlayer( + base_url=base_url, api_key=api_key, _strict_response_validation=True, http_client=http_client + ) + + request = client._build_request(FinalRequestOptions(method="get", url="/foo")) + timeout = httpx.Timeout(**request.extensions["timeout"]) # type: ignore + assert timeout == DEFAULT_TIMEOUT + + await client.close() + + # explicitly passing the default timeout currently results in it being ignored + async with httpx.AsyncClient(timeout=HTTPX_DEFAULT_TIMEOUT) as http_client: + client = AsyncUnlayer( + base_url=base_url, api_key=api_key, _strict_response_validation=True, http_client=http_client + ) + + request = client._build_request(FinalRequestOptions(method="get", url="/foo")) + timeout = httpx.Timeout(**request.extensions["timeout"]) # type: ignore + assert timeout == DEFAULT_TIMEOUT # our default + + await client.close() + + def test_invalid_http_client(self) -> None: + with pytest.raises(TypeError, match="Invalid `http_client` arg"): + with httpx.Client() as http_client: + AsyncUnlayer( + base_url=base_url, + api_key=api_key, + _strict_response_validation=True, + http_client=cast(Any, http_client), + ) + + async def test_default_headers_option(self) -> None: + test_client = AsyncUnlayer( + base_url=base_url, api_key=api_key, _strict_response_validation=True, default_headers={"X-Foo": "bar"} + ) + request = test_client._build_request(FinalRequestOptions(method="get", url="/foo")) + assert request.headers.get("x-foo") == "bar" + assert request.headers.get("x-stainless-lang") == "python" + + test_client2 = AsyncUnlayer( + base_url=base_url, + api_key=api_key, + _strict_response_validation=True, + default_headers={ + "X-Foo": "stainless", + "X-Stainless-Lang": "my-overriding-header", + }, + ) + request = test_client2._build_request(FinalRequestOptions(method="get", url="/foo")) + assert request.headers.get("x-foo") == "stainless" + assert request.headers.get("x-stainless-lang") == "my-overriding-header" + + await test_client.close() + await test_client2.close() + + def test_validate_headers(self) -> None: + client = AsyncUnlayer(base_url=base_url, api_key=api_key, _strict_response_validation=True) + request = client._build_request(FinalRequestOptions(method="get", url="/foo")) + assert request.headers.get("Authorization") == f"Bearer {api_key}" + + with pytest.raises(UnlayerError): + with update_env(**{"UNLAYER_API_KEY": Omit()}): + client2 = AsyncUnlayer(base_url=base_url, api_key=None, _strict_response_validation=True) + _ = client2 + + async def test_default_query_option(self) -> None: + client = AsyncUnlayer( + base_url=base_url, api_key=api_key, _strict_response_validation=True, default_query={"query_param": "bar"} + ) + request = client._build_request(FinalRequestOptions(method="get", url="/foo")) + url = httpx.URL(request.url) + assert dict(url.params) == {"query_param": "bar"} + + request = client._build_request( + FinalRequestOptions( + method="get", + url="/foo", + params={"foo": "baz", "query_param": "overridden"}, + ) + ) + url = httpx.URL(request.url) + assert dict(url.params) == {"foo": "baz", "query_param": "overridden"} + + await client.close() + + def test_request_extra_json(self, client: Unlayer) -> None: + request = client._build_request( + FinalRequestOptions( + method="post", + url="/foo", + json_data={"foo": "bar"}, + extra_json={"baz": False}, + ), + ) + data = json.loads(request.content.decode("utf-8")) + assert data == {"foo": "bar", "baz": False} + + request = client._build_request( + FinalRequestOptions( + method="post", + url="/foo", + extra_json={"baz": False}, + ), + ) + data = json.loads(request.content.decode("utf-8")) + assert data == {"baz": False} + + # `extra_json` takes priority over `json_data` when keys clash + request = client._build_request( + FinalRequestOptions( + method="post", + url="/foo", + json_data={"foo": "bar", "baz": True}, + extra_json={"baz": None}, + ), + ) + data = json.loads(request.content.decode("utf-8")) + assert data == {"foo": "bar", "baz": None} + + def test_request_extra_headers(self, client: Unlayer) -> None: + request = client._build_request( + FinalRequestOptions( + method="post", + url="/foo", + **make_request_options(extra_headers={"X-Foo": "Foo"}), + ), + ) + assert request.headers.get("X-Foo") == "Foo" + + # `extra_headers` takes priority over `default_headers` when keys clash + request = client.with_options(default_headers={"X-Bar": "true"})._build_request( + FinalRequestOptions( + method="post", + url="/foo", + **make_request_options( + extra_headers={"X-Bar": "false"}, + ), + ), + ) + assert request.headers.get("X-Bar") == "false" + + def test_request_extra_query(self, client: Unlayer) -> None: + request = client._build_request( + FinalRequestOptions( + method="post", + url="/foo", + **make_request_options( + extra_query={"my_query_param": "Foo"}, + ), + ), + ) + params = dict(request.url.params) + assert params == {"my_query_param": "Foo"} + + # if both `query` and `extra_query` are given, they are merged + request = client._build_request( + FinalRequestOptions( + method="post", + url="/foo", + **make_request_options( + query={"bar": "1"}, + extra_query={"foo": "2"}, + ), + ), + ) + params = dict(request.url.params) + assert params == {"bar": "1", "foo": "2"} + + # `extra_query` takes priority over `query` when keys clash + request = client._build_request( + FinalRequestOptions( + method="post", + url="/foo", + **make_request_options( + query={"foo": "1"}, + extra_query={"foo": "2"}, + ), + ), + ) + params = dict(request.url.params) + assert params == {"foo": "2"} + + def test_multipart_repeating_array(self, async_client: AsyncUnlayer) -> None: + request = async_client._build_request( + FinalRequestOptions.construct( + method="post", + url="/foo", + headers={"Content-Type": "multipart/form-data; boundary=6b7ba517decee4a450543ea6ae821c82"}, + json_data={"array": ["foo", "bar"]}, + files=[("foo.txt", b"hello world")], + ) + ) + + assert request.read().split(b"\r\n") == [ + b"--6b7ba517decee4a450543ea6ae821c82", + b'Content-Disposition: form-data; name="array[]"', + b"", + b"foo", + b"--6b7ba517decee4a450543ea6ae821c82", + b'Content-Disposition: form-data; name="array[]"', + b"", + b"bar", + b"--6b7ba517decee4a450543ea6ae821c82", + b'Content-Disposition: form-data; name="foo.txt"; filename="upload"', + b"Content-Type: application/octet-stream", + b"", + b"hello world", + b"--6b7ba517decee4a450543ea6ae821c82--", + b"", + ] + + @pytest.mark.respx(base_url=base_url) + async def test_basic_union_response(self, respx_mock: MockRouter, async_client: AsyncUnlayer) -> None: + class Model1(BaseModel): + name: str + + class Model2(BaseModel): + foo: str + + respx_mock.get("/foo").mock(return_value=httpx.Response(200, json={"foo": "bar"})) + + response = await async_client.get("/foo", cast_to=cast(Any, Union[Model1, Model2])) + assert isinstance(response, Model2) + assert response.foo == "bar" + + @pytest.mark.respx(base_url=base_url) + async def test_union_response_different_types(self, respx_mock: MockRouter, async_client: AsyncUnlayer) -> None: + """Union of objects with the same field name using a different type""" + + class Model1(BaseModel): + foo: int + + class Model2(BaseModel): + foo: str + + respx_mock.get("/foo").mock(return_value=httpx.Response(200, json={"foo": "bar"})) + + response = await async_client.get("/foo", cast_to=cast(Any, Union[Model1, Model2])) + assert isinstance(response, Model2) + assert response.foo == "bar" + + respx_mock.get("/foo").mock(return_value=httpx.Response(200, json={"foo": 1})) + + response = await async_client.get("/foo", cast_to=cast(Any, Union[Model1, Model2])) + assert isinstance(response, Model1) + assert response.foo == 1 + + @pytest.mark.respx(base_url=base_url) + async def test_non_application_json_content_type_for_json_data( + self, respx_mock: MockRouter, async_client: AsyncUnlayer + ) -> None: + """ + Response that sets Content-Type to something other than application/json but returns json data + """ + + class Model(BaseModel): + foo: int + + respx_mock.get("/foo").mock( + return_value=httpx.Response( + 200, + content=json.dumps({"foo": 2}), + headers={"Content-Type": "application/text"}, + ) + ) + + response = await async_client.get("/foo", cast_to=Model) + assert isinstance(response, Model) + assert response.foo == 2 + + async def test_base_url_setter(self) -> None: + client = AsyncUnlayer( + base_url="https://example.com/from_init", api_key=api_key, _strict_response_validation=True + ) + assert client.base_url == "https://example.com/from_init/" + + client.base_url = "https://example.com/from_setter" # type: ignore[assignment] + + assert client.base_url == "https://example.com/from_setter/" + + await client.close() + + async def test_base_url_env(self) -> None: + with update_env(UNLAYER_BASE_URL="http://localhost:5000/from/env"): + client = AsyncUnlayer(api_key=api_key, _strict_response_validation=True) + assert client.base_url == "http://localhost:5000/from/env/" + + @pytest.mark.parametrize( + "client", + [ + AsyncUnlayer( + base_url="http://localhost:5000/custom/path/", api_key=api_key, _strict_response_validation=True + ), + AsyncUnlayer( + base_url="http://localhost:5000/custom/path/", + api_key=api_key, + _strict_response_validation=True, + http_client=httpx.AsyncClient(), + ), + ], + ids=["standard", "custom http client"], + ) + async def test_base_url_trailing_slash(self, client: AsyncUnlayer) -> None: + request = client._build_request( + FinalRequestOptions( + method="post", + url="/foo", + json_data={"foo": "bar"}, + ), + ) + assert request.url == "http://localhost:5000/custom/path/foo" + await client.close() + + @pytest.mark.parametrize( + "client", + [ + AsyncUnlayer( + base_url="http://localhost:5000/custom/path/", api_key=api_key, _strict_response_validation=True + ), + AsyncUnlayer( + base_url="http://localhost:5000/custom/path/", + api_key=api_key, + _strict_response_validation=True, + http_client=httpx.AsyncClient(), + ), + ], + ids=["standard", "custom http client"], + ) + async def test_base_url_no_trailing_slash(self, client: AsyncUnlayer) -> None: + request = client._build_request( + FinalRequestOptions( + method="post", + url="/foo", + json_data={"foo": "bar"}, + ), + ) + assert request.url == "http://localhost:5000/custom/path/foo" + await client.close() + + @pytest.mark.parametrize( + "client", + [ + AsyncUnlayer( + base_url="http://localhost:5000/custom/path/", api_key=api_key, _strict_response_validation=True + ), + AsyncUnlayer( + base_url="http://localhost:5000/custom/path/", + api_key=api_key, + _strict_response_validation=True, + http_client=httpx.AsyncClient(), + ), + ], + ids=["standard", "custom http client"], + ) + async def test_absolute_request_url(self, client: AsyncUnlayer) -> None: + request = client._build_request( + FinalRequestOptions( + method="post", + url="https://myapi.com/foo", + json_data={"foo": "bar"}, + ), + ) + assert request.url == "https://myapi.com/foo" + await client.close() + + async def test_copied_client_does_not_close_http(self) -> None: + test_client = AsyncUnlayer(base_url=base_url, api_key=api_key, _strict_response_validation=True) + assert not test_client.is_closed() + + copied = test_client.copy() + assert copied is not test_client + + del copied + + await asyncio.sleep(0.2) + assert not test_client.is_closed() + + async def test_client_context_manager(self) -> None: + test_client = AsyncUnlayer(base_url=base_url, api_key=api_key, _strict_response_validation=True) + async with test_client as c2: + assert c2 is test_client + assert not c2.is_closed() + assert not test_client.is_closed() + assert test_client.is_closed() + + @pytest.mark.respx(base_url=base_url) + async def test_client_response_validation_error(self, respx_mock: MockRouter, async_client: AsyncUnlayer) -> None: + class Model(BaseModel): + foo: str + + respx_mock.get("/foo").mock(return_value=httpx.Response(200, json={"foo": {"invalid": True}})) + + with pytest.raises(APIResponseValidationError) as exc: + await async_client.get("/foo", cast_to=Model) + + assert isinstance(exc.value.__cause__, ValidationError) + + async def test_client_max_retries_validation(self) -> None: + with pytest.raises(TypeError, match=r"max_retries cannot be None"): + AsyncUnlayer( + base_url=base_url, api_key=api_key, _strict_response_validation=True, max_retries=cast(Any, None) + ) + + @pytest.mark.respx(base_url=base_url) + async def test_received_text_for_expected_json(self, respx_mock: MockRouter) -> None: + class Model(BaseModel): + name: str + + respx_mock.get("/foo").mock(return_value=httpx.Response(200, text="my-custom-format")) + + strict_client = AsyncUnlayer(base_url=base_url, api_key=api_key, _strict_response_validation=True) + + with pytest.raises(APIResponseValidationError): + await strict_client.get("/foo", cast_to=Model) + + non_strict_client = AsyncUnlayer(base_url=base_url, api_key=api_key, _strict_response_validation=False) + + response = await non_strict_client.get("/foo", cast_to=Model) + assert isinstance(response, str) # type: ignore[unreachable] + + await strict_client.close() + await non_strict_client.close() + + @pytest.mark.parametrize( + "remaining_retries,retry_after,timeout", + [ + [3, "20", 20], + [3, "0", 0.5], + [3, "-10", 0.5], + [3, "60", 60], + [3, "61", 0.5], + [3, "Fri, 29 Sep 2023 16:26:57 GMT", 20], + [3, "Fri, 29 Sep 2023 16:26:37 GMT", 0.5], + [3, "Fri, 29 Sep 2023 16:26:27 GMT", 0.5], + [3, "Fri, 29 Sep 2023 16:27:37 GMT", 60], + [3, "Fri, 29 Sep 2023 16:27:38 GMT", 0.5], + [3, "99999999999999999999999999999999999", 0.5], + [3, "Zun, 29 Sep 2023 16:26:27 GMT", 0.5], + [3, "", 0.5], + [2, "", 0.5 * 2.0], + [1, "", 0.5 * 4.0], + [-1100, "", 8], # test large number potentially overflowing + ], + ) + @mock.patch("time.time", mock.MagicMock(return_value=1696004797)) + async def test_parse_retry_after_header( + self, remaining_retries: int, retry_after: str, timeout: float, async_client: AsyncUnlayer + ) -> None: + headers = httpx.Headers({"retry-after": retry_after}) + options = FinalRequestOptions(method="get", url="/foo", max_retries=3) + calculated = async_client._calculate_retry_timeout(remaining_retries, options, headers) + assert calculated == pytest.approx(timeout, 0.5 * 0.875) # pyright: ignore[reportUnknownMemberType] + + @mock.patch("unlayer._base_client.BaseClient._calculate_retry_timeout", _low_retry_timeout) + @pytest.mark.respx(base_url=base_url) + async def test_retrying_timeout_errors_doesnt_leak( + self, respx_mock: MockRouter, async_client: AsyncUnlayer + ) -> None: + respx_mock.get("/project/v1/current").mock(side_effect=httpx.TimeoutException("Test timeout error")) + + with pytest.raises(APITimeoutError): + await async_client.project_v1.with_streaming_response.current_list().__aenter__() + + assert _get_open_connections(async_client) == 0 + + @mock.patch("unlayer._base_client.BaseClient._calculate_retry_timeout", _low_retry_timeout) + @pytest.mark.respx(base_url=base_url) + async def test_retrying_status_errors_doesnt_leak(self, respx_mock: MockRouter, async_client: AsyncUnlayer) -> None: + respx_mock.get("/project/v1/current").mock(return_value=httpx.Response(500)) + + with pytest.raises(APIStatusError): + await async_client.project_v1.with_streaming_response.current_list().__aenter__() + assert _get_open_connections(async_client) == 0 + + @pytest.mark.parametrize("failures_before_success", [0, 2, 4]) + @mock.patch("unlayer._base_client.BaseClient._calculate_retry_timeout", _low_retry_timeout) + @pytest.mark.respx(base_url=base_url) + @pytest.mark.parametrize("failure_mode", ["status", "exception"]) + async def test_retries_taken( + self, + async_client: AsyncUnlayer, + failures_before_success: int, + failure_mode: Literal["status", "exception"], + respx_mock: MockRouter, + ) -> None: + client = async_client.with_options(max_retries=4) + + nb_retries = 0 + + def retry_handler(_request: httpx.Request) -> httpx.Response: + nonlocal nb_retries + if nb_retries < failures_before_success: + nb_retries += 1 + if failure_mode == "exception": + raise RuntimeError("oops") + return httpx.Response(500) + return httpx.Response(200) + + respx_mock.get("/project/v1/current").mock(side_effect=retry_handler) + + response = await client.project_v1.with_raw_response.current_list() + + assert response.retries_taken == failures_before_success + assert int(response.http_request.headers.get("x-stainless-retry-count")) == failures_before_success + + @pytest.mark.parametrize("failures_before_success", [0, 2, 4]) + @mock.patch("unlayer._base_client.BaseClient._calculate_retry_timeout", _low_retry_timeout) + @pytest.mark.respx(base_url=base_url) + async def test_omit_retry_count_header( + self, async_client: AsyncUnlayer, failures_before_success: int, respx_mock: MockRouter + ) -> None: + client = async_client.with_options(max_retries=4) + + nb_retries = 0 + + def retry_handler(_request: httpx.Request) -> httpx.Response: + nonlocal nb_retries + if nb_retries < failures_before_success: + nb_retries += 1 + return httpx.Response(500) + return httpx.Response(200) + + respx_mock.get("/project/v1/current").mock(side_effect=retry_handler) + + response = await client.project_v1.with_raw_response.current_list( + extra_headers={"x-stainless-retry-count": Omit()} + ) + + assert len(response.http_request.headers.get_list("x-stainless-retry-count")) == 0 + + @pytest.mark.parametrize("failures_before_success", [0, 2, 4]) + @mock.patch("unlayer._base_client.BaseClient._calculate_retry_timeout", _low_retry_timeout) + @pytest.mark.respx(base_url=base_url) + async def test_overwrite_retry_count_header( + self, async_client: AsyncUnlayer, failures_before_success: int, respx_mock: MockRouter + ) -> None: + client = async_client.with_options(max_retries=4) + + nb_retries = 0 + + def retry_handler(_request: httpx.Request) -> httpx.Response: + nonlocal nb_retries + if nb_retries < failures_before_success: + nb_retries += 1 + return httpx.Response(500) + return httpx.Response(200) + + respx_mock.get("/project/v1/current").mock(side_effect=retry_handler) + + response = await client.project_v1.with_raw_response.current_list( + extra_headers={"x-stainless-retry-count": "42"} + ) + + assert response.http_request.headers.get("x-stainless-retry-count") == "42" + + async def test_get_platform(self) -> None: + platform = await asyncify(get_platform)() + assert isinstance(platform, (str, OtherPlatform)) + + async def test_proxy_environment_variables(self, monkeypatch: pytest.MonkeyPatch) -> None: + # Test that the proxy environment variables are set correctly + monkeypatch.setenv("HTTPS_PROXY", "https://example.org") + + client = DefaultAsyncHttpxClient() + + mounts = tuple(client._mounts.items()) + assert len(mounts) == 1 + assert mounts[0][0].pattern == "https://" + + @pytest.mark.filterwarnings("ignore:.*deprecated.*:DeprecationWarning") + async def test_default_client_creation(self) -> None: + # Ensure that the client can be initialized without any exceptions + DefaultAsyncHttpxClient( + verify=True, + cert=None, + trust_env=True, + http1=True, + http2=False, + limits=httpx.Limits(max_connections=100, max_keepalive_connections=20), + ) + + @pytest.mark.respx(base_url=base_url) + async def test_follow_redirects(self, respx_mock: MockRouter, async_client: AsyncUnlayer) -> None: + # Test that the default follow_redirects=True allows following redirects + respx_mock.post("/redirect").mock( + return_value=httpx.Response(302, headers={"Location": f"{base_url}/redirected"}) + ) + respx_mock.get("/redirected").mock(return_value=httpx.Response(200, json={"status": "ok"})) + + response = await async_client.post("/redirect", body={"key": "value"}, cast_to=httpx.Response) + assert response.status_code == 200 + assert response.json() == {"status": "ok"} + + @pytest.mark.respx(base_url=base_url) + async def test_follow_redirects_disabled(self, respx_mock: MockRouter, async_client: AsyncUnlayer) -> None: + # Test that follow_redirects=False prevents following redirects + respx_mock.post("/redirect").mock( + return_value=httpx.Response(302, headers={"Location": f"{base_url}/redirected"}) + ) + + with pytest.raises(APIStatusError) as exc_info: + await async_client.post( + "/redirect", body={"key": "value"}, options={"follow_redirects": False}, cast_to=httpx.Response + ) + + assert exc_info.value.response.status_code == 302 + assert exc_info.value.response.headers["Location"] == f"{base_url}/redirected" diff --git a/tests/test_deepcopy.py b/tests/test_deepcopy.py new file mode 100644 index 0000000..f308857 --- /dev/null +++ b/tests/test_deepcopy.py @@ -0,0 +1,58 @@ +from unlayer._utils import deepcopy_minimal + + +def assert_different_identities(obj1: object, obj2: object) -> None: + assert obj1 == obj2 + assert id(obj1) != id(obj2) + + +def test_simple_dict() -> None: + obj1 = {"foo": "bar"} + obj2 = deepcopy_minimal(obj1) + assert_different_identities(obj1, obj2) + + +def test_nested_dict() -> None: + obj1 = {"foo": {"bar": True}} + obj2 = deepcopy_minimal(obj1) + assert_different_identities(obj1, obj2) + assert_different_identities(obj1["foo"], obj2["foo"]) + + +def test_complex_nested_dict() -> None: + obj1 = {"foo": {"bar": [{"hello": "world"}]}} + obj2 = deepcopy_minimal(obj1) + assert_different_identities(obj1, obj2) + assert_different_identities(obj1["foo"], obj2["foo"]) + assert_different_identities(obj1["foo"]["bar"], obj2["foo"]["bar"]) + assert_different_identities(obj1["foo"]["bar"][0], obj2["foo"]["bar"][0]) + + +def test_simple_list() -> None: + obj1 = ["a", "b", "c"] + obj2 = deepcopy_minimal(obj1) + assert_different_identities(obj1, obj2) + + +def test_nested_list() -> None: + obj1 = ["a", [1, 2, 3]] + obj2 = deepcopy_minimal(obj1) + assert_different_identities(obj1, obj2) + assert_different_identities(obj1[1], obj2[1]) + + +class MyObject: ... + + +def test_ignores_other_types() -> None: + # custom classes + my_obj = MyObject() + obj1 = {"foo": my_obj} + obj2 = deepcopy_minimal(obj1) + assert_different_identities(obj1, obj2) + assert obj1["foo"] is my_obj + + # tuples + obj3 = ("a", "b") + obj4 = deepcopy_minimal(obj3) + assert obj3 is obj4 diff --git a/tests/test_extract_files.py b/tests/test_extract_files.py new file mode 100644 index 0000000..9f2cae2 --- /dev/null +++ b/tests/test_extract_files.py @@ -0,0 +1,64 @@ +from __future__ import annotations + +from typing import Sequence + +import pytest + +from unlayer._types import FileTypes +from unlayer._utils import extract_files + + +def test_removes_files_from_input() -> None: + query = {"foo": "bar"} + assert extract_files(query, paths=[]) == [] + assert query == {"foo": "bar"} + + query2 = {"foo": b"Bar", "hello": "world"} + assert extract_files(query2, paths=[["foo"]]) == [("foo", b"Bar")] + assert query2 == {"hello": "world"} + + query3 = {"foo": {"foo": {"bar": b"Bar"}}, "hello": "world"} + assert extract_files(query3, paths=[["foo", "foo", "bar"]]) == [("foo[foo][bar]", b"Bar")] + assert query3 == {"foo": {"foo": {}}, "hello": "world"} + + query4 = {"foo": {"bar": b"Bar", "baz": "foo"}, "hello": "world"} + assert extract_files(query4, paths=[["foo", "bar"]]) == [("foo[bar]", b"Bar")] + assert query4 == {"hello": "world", "foo": {"baz": "foo"}} + + +def test_multiple_files() -> None: + query = {"documents": [{"file": b"My first file"}, {"file": b"My second file"}]} + assert extract_files(query, paths=[["documents", "", "file"]]) == [ + ("documents[][file]", b"My first file"), + ("documents[][file]", b"My second file"), + ] + assert query == {"documents": [{}, {}]} + + +@pytest.mark.parametrize( + "query,paths,expected", + [ + [ + {"foo": {"bar": "baz"}}, + [["foo", "", "bar"]], + [], + ], + [ + {"foo": ["bar", "baz"]}, + [["foo", "bar"]], + [], + ], + [ + {"foo": {"bar": "baz"}}, + [["foo", "foo"]], + [], + ], + ], + ids=["dict expecting array", "array expecting dict", "unknown keys"], +) +def test_ignores_incorrect_paths( + query: dict[str, object], + paths: Sequence[Sequence[str]], + expected: list[tuple[str, FileTypes]], +) -> None: + assert extract_files(query, paths=paths) == expected diff --git a/tests/test_files.py b/tests/test_files.py new file mode 100644 index 0000000..f1e9f25 --- /dev/null +++ b/tests/test_files.py @@ -0,0 +1,51 @@ +from pathlib import Path + +import anyio +import pytest +from dirty_equals import IsDict, IsList, IsBytes, IsTuple + +from unlayer._files import to_httpx_files, async_to_httpx_files + +readme_path = Path(__file__).parent.parent.joinpath("README.md") + + +def test_pathlib_includes_file_name() -> None: + result = to_httpx_files({"file": readme_path}) + print(result) + assert result == IsDict({"file": IsTuple("README.md", IsBytes())}) + + +def test_tuple_input() -> None: + result = to_httpx_files([("file", readme_path)]) + print(result) + assert result == IsList(IsTuple("file", IsTuple("README.md", IsBytes()))) + + +@pytest.mark.asyncio +async def test_async_pathlib_includes_file_name() -> None: + result = await async_to_httpx_files({"file": readme_path}) + print(result) + assert result == IsDict({"file": IsTuple("README.md", IsBytes())}) + + +@pytest.mark.asyncio +async def test_async_supports_anyio_path() -> None: + result = await async_to_httpx_files({"file": anyio.Path(readme_path)}) + print(result) + assert result == IsDict({"file": IsTuple("README.md", IsBytes())}) + + +@pytest.mark.asyncio +async def test_async_tuple_input() -> None: + result = await async_to_httpx_files([("file", readme_path)]) + print(result) + assert result == IsList(IsTuple("file", IsTuple("README.md", IsBytes()))) + + +def test_string_not_allowed() -> None: + with pytest.raises(TypeError, match="Expected file types input to be a FileContent type or to be a tuple"): + to_httpx_files( + { + "file": "foo", # type: ignore + } + ) diff --git a/tests/test_models.py b/tests/test_models.py new file mode 100644 index 0000000..f6bd612 --- /dev/null +++ b/tests/test_models.py @@ -0,0 +1,963 @@ +import json +from typing import TYPE_CHECKING, Any, Dict, List, Union, Optional, cast +from datetime import datetime, timezone +from typing_extensions import Literal, Annotated, TypeAliasType + +import pytest +import pydantic +from pydantic import Field + +from unlayer._utils import PropertyInfo +from unlayer._compat import PYDANTIC_V1, parse_obj, model_dump, model_json +from unlayer._models import DISCRIMINATOR_CACHE, BaseModel, construct_type + + +class BasicModel(BaseModel): + foo: str + + +@pytest.mark.parametrize("value", ["hello", 1], ids=["correct type", "mismatched"]) +def test_basic(value: object) -> None: + m = BasicModel.construct(foo=value) + assert m.foo == value + + +def test_directly_nested_model() -> None: + class NestedModel(BaseModel): + nested: BasicModel + + m = NestedModel.construct(nested={"foo": "Foo!"}) + assert m.nested.foo == "Foo!" + + # mismatched types + m = NestedModel.construct(nested="hello!") + assert cast(Any, m.nested) == "hello!" + + +def test_optional_nested_model() -> None: + class NestedModel(BaseModel): + nested: Optional[BasicModel] + + m1 = NestedModel.construct(nested=None) + assert m1.nested is None + + m2 = NestedModel.construct(nested={"foo": "bar"}) + assert m2.nested is not None + assert m2.nested.foo == "bar" + + # mismatched types + m3 = NestedModel.construct(nested={"foo"}) + assert isinstance(cast(Any, m3.nested), set) + assert cast(Any, m3.nested) == {"foo"} + + +def test_list_nested_model() -> None: + class NestedModel(BaseModel): + nested: List[BasicModel] + + m = NestedModel.construct(nested=[{"foo": "bar"}, {"foo": "2"}]) + assert m.nested is not None + assert isinstance(m.nested, list) + assert len(m.nested) == 2 + assert m.nested[0].foo == "bar" + assert m.nested[1].foo == "2" + + # mismatched types + m = NestedModel.construct(nested=True) + assert cast(Any, m.nested) is True + + m = NestedModel.construct(nested=[False]) + assert cast(Any, m.nested) == [False] + + +def test_optional_list_nested_model() -> None: + class NestedModel(BaseModel): + nested: Optional[List[BasicModel]] + + m1 = NestedModel.construct(nested=[{"foo": "bar"}, {"foo": "2"}]) + assert m1.nested is not None + assert isinstance(m1.nested, list) + assert len(m1.nested) == 2 + assert m1.nested[0].foo == "bar" + assert m1.nested[1].foo == "2" + + m2 = NestedModel.construct(nested=None) + assert m2.nested is None + + # mismatched types + m3 = NestedModel.construct(nested={1}) + assert cast(Any, m3.nested) == {1} + + m4 = NestedModel.construct(nested=[False]) + assert cast(Any, m4.nested) == [False] + + +def test_list_optional_items_nested_model() -> None: + class NestedModel(BaseModel): + nested: List[Optional[BasicModel]] + + m = NestedModel.construct(nested=[None, {"foo": "bar"}]) + assert m.nested is not None + assert isinstance(m.nested, list) + assert len(m.nested) == 2 + assert m.nested[0] is None + assert m.nested[1] is not None + assert m.nested[1].foo == "bar" + + # mismatched types + m3 = NestedModel.construct(nested="foo") + assert cast(Any, m3.nested) == "foo" + + m4 = NestedModel.construct(nested=[False]) + assert cast(Any, m4.nested) == [False] + + +def test_list_mismatched_type() -> None: + class NestedModel(BaseModel): + nested: List[str] + + m = NestedModel.construct(nested=False) + assert cast(Any, m.nested) is False + + +def test_raw_dictionary() -> None: + class NestedModel(BaseModel): + nested: Dict[str, str] + + m = NestedModel.construct(nested={"hello": "world"}) + assert m.nested == {"hello": "world"} + + # mismatched types + m = NestedModel.construct(nested=False) + assert cast(Any, m.nested) is False + + +def test_nested_dictionary_model() -> None: + class NestedModel(BaseModel): + nested: Dict[str, BasicModel] + + m = NestedModel.construct(nested={"hello": {"foo": "bar"}}) + assert isinstance(m.nested, dict) + assert m.nested["hello"].foo == "bar" + + # mismatched types + m = NestedModel.construct(nested={"hello": False}) + assert cast(Any, m.nested["hello"]) is False + + +def test_unknown_fields() -> None: + m1 = BasicModel.construct(foo="foo", unknown=1) + assert m1.foo == "foo" + assert cast(Any, m1).unknown == 1 + + m2 = BasicModel.construct(foo="foo", unknown={"foo_bar": True}) + assert m2.foo == "foo" + assert cast(Any, m2).unknown == {"foo_bar": True} + + assert model_dump(m2) == {"foo": "foo", "unknown": {"foo_bar": True}} + + +def test_strict_validation_unknown_fields() -> None: + class Model(BaseModel): + foo: str + + model = parse_obj(Model, dict(foo="hello!", user="Robert")) + assert model.foo == "hello!" + assert cast(Any, model).user == "Robert" + + assert model_dump(model) == {"foo": "hello!", "user": "Robert"} + + +def test_aliases() -> None: + class Model(BaseModel): + my_field: int = Field(alias="myField") + + m = Model.construct(myField=1) + assert m.my_field == 1 + + # mismatched types + m = Model.construct(myField={"hello": False}) + assert cast(Any, m.my_field) == {"hello": False} + + +def test_repr() -> None: + model = BasicModel(foo="bar") + assert str(model) == "BasicModel(foo='bar')" + assert repr(model) == "BasicModel(foo='bar')" + + +def test_repr_nested_model() -> None: + class Child(BaseModel): + name: str + age: int + + class Parent(BaseModel): + name: str + child: Child + + model = Parent(name="Robert", child=Child(name="Foo", age=5)) + assert str(model) == "Parent(name='Robert', child=Child(name='Foo', age=5))" + assert repr(model) == "Parent(name='Robert', child=Child(name='Foo', age=5))" + + +def test_optional_list() -> None: + class Submodel(BaseModel): + name: str + + class Model(BaseModel): + items: Optional[List[Submodel]] + + m = Model.construct(items=None) + assert m.items is None + + m = Model.construct(items=[]) + assert m.items == [] + + m = Model.construct(items=[{"name": "Robert"}]) + assert m.items is not None + assert len(m.items) == 1 + assert m.items[0].name == "Robert" + + +def test_nested_union_of_models() -> None: + class Submodel1(BaseModel): + bar: bool + + class Submodel2(BaseModel): + thing: str + + class Model(BaseModel): + foo: Union[Submodel1, Submodel2] + + m = Model.construct(foo={"thing": "hello"}) + assert isinstance(m.foo, Submodel2) + assert m.foo.thing == "hello" + + +def test_nested_union_of_mixed_types() -> None: + class Submodel1(BaseModel): + bar: bool + + class Model(BaseModel): + foo: Union[Submodel1, Literal[True], Literal["CARD_HOLDER"]] + + m = Model.construct(foo=True) + assert m.foo is True + + m = Model.construct(foo="CARD_HOLDER") + assert m.foo == "CARD_HOLDER" + + m = Model.construct(foo={"bar": False}) + assert isinstance(m.foo, Submodel1) + assert m.foo.bar is False + + +def test_nested_union_multiple_variants() -> None: + class Submodel1(BaseModel): + bar: bool + + class Submodel2(BaseModel): + thing: str + + class Submodel3(BaseModel): + foo: int + + class Model(BaseModel): + foo: Union[Submodel1, Submodel2, None, Submodel3] + + m = Model.construct(foo={"thing": "hello"}) + assert isinstance(m.foo, Submodel2) + assert m.foo.thing == "hello" + + m = Model.construct(foo=None) + assert m.foo is None + + m = Model.construct() + assert m.foo is None + + m = Model.construct(foo={"foo": "1"}) + assert isinstance(m.foo, Submodel3) + assert m.foo.foo == 1 + + +def test_nested_union_invalid_data() -> None: + class Submodel1(BaseModel): + level: int + + class Submodel2(BaseModel): + name: str + + class Model(BaseModel): + foo: Union[Submodel1, Submodel2] + + m = Model.construct(foo=True) + assert cast(bool, m.foo) is True + + m = Model.construct(foo={"name": 3}) + if PYDANTIC_V1: + assert isinstance(m.foo, Submodel2) + assert m.foo.name == "3" + else: + assert isinstance(m.foo, Submodel1) + assert m.foo.name == 3 # type: ignore + + +def test_list_of_unions() -> None: + class Submodel1(BaseModel): + level: int + + class Submodel2(BaseModel): + name: str + + class Model(BaseModel): + items: List[Union[Submodel1, Submodel2]] + + m = Model.construct(items=[{"level": 1}, {"name": "Robert"}]) + assert len(m.items) == 2 + assert isinstance(m.items[0], Submodel1) + assert m.items[0].level == 1 + assert isinstance(m.items[1], Submodel2) + assert m.items[1].name == "Robert" + + m = Model.construct(items=[{"level": -1}, 156]) + assert len(m.items) == 2 + assert isinstance(m.items[0], Submodel1) + assert m.items[0].level == -1 + assert cast(Any, m.items[1]) == 156 + + +def test_union_of_lists() -> None: + class SubModel1(BaseModel): + level: int + + class SubModel2(BaseModel): + name: str + + class Model(BaseModel): + items: Union[List[SubModel1], List[SubModel2]] + + # with one valid entry + m = Model.construct(items=[{"name": "Robert"}]) + assert len(m.items) == 1 + assert isinstance(m.items[0], SubModel2) + assert m.items[0].name == "Robert" + + # with two entries pointing to different types + m = Model.construct(items=[{"level": 1}, {"name": "Robert"}]) + assert len(m.items) == 2 + assert isinstance(m.items[0], SubModel1) + assert m.items[0].level == 1 + assert isinstance(m.items[1], SubModel1) + assert cast(Any, m.items[1]).name == "Robert" + + # with two entries pointing to *completely* different types + m = Model.construct(items=[{"level": -1}, 156]) + assert len(m.items) == 2 + assert isinstance(m.items[0], SubModel1) + assert m.items[0].level == -1 + assert cast(Any, m.items[1]) == 156 + + +def test_dict_of_union() -> None: + class SubModel1(BaseModel): + name: str + + class SubModel2(BaseModel): + foo: str + + class Model(BaseModel): + data: Dict[str, Union[SubModel1, SubModel2]] + + m = Model.construct(data={"hello": {"name": "there"}, "foo": {"foo": "bar"}}) + assert len(list(m.data.keys())) == 2 + assert isinstance(m.data["hello"], SubModel1) + assert m.data["hello"].name == "there" + assert isinstance(m.data["foo"], SubModel2) + assert m.data["foo"].foo == "bar" + + # TODO: test mismatched type + + +def test_double_nested_union() -> None: + class SubModel1(BaseModel): + name: str + + class SubModel2(BaseModel): + bar: str + + class Model(BaseModel): + data: Dict[str, List[Union[SubModel1, SubModel2]]] + + m = Model.construct(data={"foo": [{"bar": "baz"}, {"name": "Robert"}]}) + assert len(m.data["foo"]) == 2 + + entry1 = m.data["foo"][0] + assert isinstance(entry1, SubModel2) + assert entry1.bar == "baz" + + entry2 = m.data["foo"][1] + assert isinstance(entry2, SubModel1) + assert entry2.name == "Robert" + + # TODO: test mismatched type + + +def test_union_of_dict() -> None: + class SubModel1(BaseModel): + name: str + + class SubModel2(BaseModel): + foo: str + + class Model(BaseModel): + data: Union[Dict[str, SubModel1], Dict[str, SubModel2]] + + m = Model.construct(data={"hello": {"name": "there"}, "foo": {"foo": "bar"}}) + assert len(list(m.data.keys())) == 2 + assert isinstance(m.data["hello"], SubModel1) + assert m.data["hello"].name == "there" + assert isinstance(m.data["foo"], SubModel1) + assert cast(Any, m.data["foo"]).foo == "bar" + + +def test_iso8601_datetime() -> None: + class Model(BaseModel): + created_at: datetime + + expected = datetime(2019, 12, 27, 18, 11, 19, 117000, tzinfo=timezone.utc) + + if PYDANTIC_V1: + expected_json = '{"created_at": "2019-12-27T18:11:19.117000+00:00"}' + else: + expected_json = '{"created_at":"2019-12-27T18:11:19.117000Z"}' + + model = Model.construct(created_at="2019-12-27T18:11:19.117Z") + assert model.created_at == expected + assert model_json(model) == expected_json + + model = parse_obj(Model, dict(created_at="2019-12-27T18:11:19.117Z")) + assert model.created_at == expected + assert model_json(model) == expected_json + + +def test_does_not_coerce_int() -> None: + class Model(BaseModel): + bar: int + + assert Model.construct(bar=1).bar == 1 + assert Model.construct(bar=10.9).bar == 10.9 + assert Model.construct(bar="19").bar == "19" # type: ignore[comparison-overlap] + assert Model.construct(bar=False).bar is False + + +def test_int_to_float_safe_conversion() -> None: + class Model(BaseModel): + float_field: float + + m = Model.construct(float_field=10) + assert m.float_field == 10.0 + assert isinstance(m.float_field, float) + + m = Model.construct(float_field=10.12) + assert m.float_field == 10.12 + assert isinstance(m.float_field, float) + + # number too big + m = Model.construct(float_field=2**53 + 1) + assert m.float_field == 2**53 + 1 + assert isinstance(m.float_field, int) + + +def test_deprecated_alias() -> None: + class Model(BaseModel): + resource_id: str = Field(alias="model_id") + + @property + def model_id(self) -> str: + return self.resource_id + + m = Model.construct(model_id="id") + assert m.model_id == "id" + assert m.resource_id == "id" + assert m.resource_id is m.model_id + + m = parse_obj(Model, {"model_id": "id"}) + assert m.model_id == "id" + assert m.resource_id == "id" + assert m.resource_id is m.model_id + + +def test_omitted_fields() -> None: + class Model(BaseModel): + resource_id: Optional[str] = None + + m = Model.construct() + assert m.resource_id is None + assert "resource_id" not in m.model_fields_set + + m = Model.construct(resource_id=None) + assert m.resource_id is None + assert "resource_id" in m.model_fields_set + + m = Model.construct(resource_id="foo") + assert m.resource_id == "foo" + assert "resource_id" in m.model_fields_set + + +def test_to_dict() -> None: + class Model(BaseModel): + foo: Optional[str] = Field(alias="FOO", default=None) + + m = Model(FOO="hello") + assert m.to_dict() == {"FOO": "hello"} + assert m.to_dict(use_api_names=False) == {"foo": "hello"} + + m2 = Model() + assert m2.to_dict() == {} + assert m2.to_dict(exclude_unset=False) == {"FOO": None} + assert m2.to_dict(exclude_unset=False, exclude_none=True) == {} + assert m2.to_dict(exclude_unset=False, exclude_defaults=True) == {} + + m3 = Model(FOO=None) + assert m3.to_dict() == {"FOO": None} + assert m3.to_dict(exclude_none=True) == {} + assert m3.to_dict(exclude_defaults=True) == {} + + class Model2(BaseModel): + created_at: datetime + + time_str = "2024-03-21T11:39:01.275859" + m4 = Model2.construct(created_at=time_str) + assert m4.to_dict(mode="python") == {"created_at": datetime.fromisoformat(time_str)} + assert m4.to_dict(mode="json") == {"created_at": time_str} + + if PYDANTIC_V1: + with pytest.raises(ValueError, match="warnings is only supported in Pydantic v2"): + m.to_dict(warnings=False) + + +def test_forwards_compat_model_dump_method() -> None: + class Model(BaseModel): + foo: Optional[str] = Field(alias="FOO", default=None) + + m = Model(FOO="hello") + assert m.model_dump() == {"foo": "hello"} + assert m.model_dump(include={"bar"}) == {} + assert m.model_dump(exclude={"foo"}) == {} + assert m.model_dump(by_alias=True) == {"FOO": "hello"} + + m2 = Model() + assert m2.model_dump() == {"foo": None} + assert m2.model_dump(exclude_unset=True) == {} + assert m2.model_dump(exclude_none=True) == {} + assert m2.model_dump(exclude_defaults=True) == {} + + m3 = Model(FOO=None) + assert m3.model_dump() == {"foo": None} + assert m3.model_dump(exclude_none=True) == {} + + if PYDANTIC_V1: + with pytest.raises(ValueError, match="round_trip is only supported in Pydantic v2"): + m.model_dump(round_trip=True) + + with pytest.raises(ValueError, match="warnings is only supported in Pydantic v2"): + m.model_dump(warnings=False) + + +def test_compat_method_no_error_for_warnings() -> None: + class Model(BaseModel): + foo: Optional[str] + + m = Model(foo="hello") + assert isinstance(model_dump(m, warnings=False), dict) + + +def test_to_json() -> None: + class Model(BaseModel): + foo: Optional[str] = Field(alias="FOO", default=None) + + m = Model(FOO="hello") + assert json.loads(m.to_json()) == {"FOO": "hello"} + assert json.loads(m.to_json(use_api_names=False)) == {"foo": "hello"} + + if PYDANTIC_V1: + assert m.to_json(indent=None) == '{"FOO": "hello"}' + else: + assert m.to_json(indent=None) == '{"FOO":"hello"}' + + m2 = Model() + assert json.loads(m2.to_json()) == {} + assert json.loads(m2.to_json(exclude_unset=False)) == {"FOO": None} + assert json.loads(m2.to_json(exclude_unset=False, exclude_none=True)) == {} + assert json.loads(m2.to_json(exclude_unset=False, exclude_defaults=True)) == {} + + m3 = Model(FOO=None) + assert json.loads(m3.to_json()) == {"FOO": None} + assert json.loads(m3.to_json(exclude_none=True)) == {} + + if PYDANTIC_V1: + with pytest.raises(ValueError, match="warnings is only supported in Pydantic v2"): + m.to_json(warnings=False) + + +def test_forwards_compat_model_dump_json_method() -> None: + class Model(BaseModel): + foo: Optional[str] = Field(alias="FOO", default=None) + + m = Model(FOO="hello") + assert json.loads(m.model_dump_json()) == {"foo": "hello"} + assert json.loads(m.model_dump_json(include={"bar"})) == {} + assert json.loads(m.model_dump_json(include={"foo"})) == {"foo": "hello"} + assert json.loads(m.model_dump_json(by_alias=True)) == {"FOO": "hello"} + + assert m.model_dump_json(indent=2) == '{\n "foo": "hello"\n}' + + m2 = Model() + assert json.loads(m2.model_dump_json()) == {"foo": None} + assert json.loads(m2.model_dump_json(exclude_unset=True)) == {} + assert json.loads(m2.model_dump_json(exclude_none=True)) == {} + assert json.loads(m2.model_dump_json(exclude_defaults=True)) == {} + + m3 = Model(FOO=None) + assert json.loads(m3.model_dump_json()) == {"foo": None} + assert json.loads(m3.model_dump_json(exclude_none=True)) == {} + + if PYDANTIC_V1: + with pytest.raises(ValueError, match="round_trip is only supported in Pydantic v2"): + m.model_dump_json(round_trip=True) + + with pytest.raises(ValueError, match="warnings is only supported in Pydantic v2"): + m.model_dump_json(warnings=False) + + +def test_type_compat() -> None: + # our model type can be assigned to Pydantic's model type + + def takes_pydantic(model: pydantic.BaseModel) -> None: # noqa: ARG001 + ... + + class OurModel(BaseModel): + foo: Optional[str] = None + + takes_pydantic(OurModel()) + + +def test_annotated_types() -> None: + class Model(BaseModel): + value: str + + m = construct_type( + value={"value": "foo"}, + type_=cast(Any, Annotated[Model, "random metadata"]), + ) + assert isinstance(m, Model) + assert m.value == "foo" + + +def test_discriminated_unions_invalid_data() -> None: + class A(BaseModel): + type: Literal["a"] + + data: str + + class B(BaseModel): + type: Literal["b"] + + data: int + + m = construct_type( + value={"type": "b", "data": "foo"}, + type_=cast(Any, Annotated[Union[A, B], PropertyInfo(discriminator="type")]), + ) + assert isinstance(m, B) + assert m.type == "b" + assert m.data == "foo" # type: ignore[comparison-overlap] + + m = construct_type( + value={"type": "a", "data": 100}, + type_=cast(Any, Annotated[Union[A, B], PropertyInfo(discriminator="type")]), + ) + assert isinstance(m, A) + assert m.type == "a" + if PYDANTIC_V1: + # pydantic v1 automatically converts inputs to strings + # if the expected type is a str + assert m.data == "100" + else: + assert m.data == 100 # type: ignore[comparison-overlap] + + +def test_discriminated_unions_unknown_variant() -> None: + class A(BaseModel): + type: Literal["a"] + + data: str + + class B(BaseModel): + type: Literal["b"] + + data: int + + m = construct_type( + value={"type": "c", "data": None, "new_thing": "bar"}, + type_=cast(Any, Annotated[Union[A, B], PropertyInfo(discriminator="type")]), + ) + + # just chooses the first variant + assert isinstance(m, A) + assert m.type == "c" # type: ignore[comparison-overlap] + assert m.data == None # type: ignore[unreachable] + assert m.new_thing == "bar" + + +def test_discriminated_unions_invalid_data_nested_unions() -> None: + class A(BaseModel): + type: Literal["a"] + + data: str + + class B(BaseModel): + type: Literal["b"] + + data: int + + class C(BaseModel): + type: Literal["c"] + + data: bool + + m = construct_type( + value={"type": "b", "data": "foo"}, + type_=cast(Any, Annotated[Union[Union[A, B], C], PropertyInfo(discriminator="type")]), + ) + assert isinstance(m, B) + assert m.type == "b" + assert m.data == "foo" # type: ignore[comparison-overlap] + + m = construct_type( + value={"type": "c", "data": "foo"}, + type_=cast(Any, Annotated[Union[Union[A, B], C], PropertyInfo(discriminator="type")]), + ) + assert isinstance(m, C) + assert m.type == "c" + assert m.data == "foo" # type: ignore[comparison-overlap] + + +def test_discriminated_unions_with_aliases_invalid_data() -> None: + class A(BaseModel): + foo_type: Literal["a"] = Field(alias="type") + + data: str + + class B(BaseModel): + foo_type: Literal["b"] = Field(alias="type") + + data: int + + m = construct_type( + value={"type": "b", "data": "foo"}, + type_=cast(Any, Annotated[Union[A, B], PropertyInfo(discriminator="foo_type")]), + ) + assert isinstance(m, B) + assert m.foo_type == "b" + assert m.data == "foo" # type: ignore[comparison-overlap] + + m = construct_type( + value={"type": "a", "data": 100}, + type_=cast(Any, Annotated[Union[A, B], PropertyInfo(discriminator="foo_type")]), + ) + assert isinstance(m, A) + assert m.foo_type == "a" + if PYDANTIC_V1: + # pydantic v1 automatically converts inputs to strings + # if the expected type is a str + assert m.data == "100" + else: + assert m.data == 100 # type: ignore[comparison-overlap] + + +def test_discriminated_unions_overlapping_discriminators_invalid_data() -> None: + class A(BaseModel): + type: Literal["a"] + + data: bool + + class B(BaseModel): + type: Literal["a"] + + data: int + + m = construct_type( + value={"type": "a", "data": "foo"}, + type_=cast(Any, Annotated[Union[A, B], PropertyInfo(discriminator="type")]), + ) + assert isinstance(m, B) + assert m.type == "a" + assert m.data == "foo" # type: ignore[comparison-overlap] + + +def test_discriminated_unions_invalid_data_uses_cache() -> None: + class A(BaseModel): + type: Literal["a"] + + data: str + + class B(BaseModel): + type: Literal["b"] + + data: int + + UnionType = cast(Any, Union[A, B]) + + assert not DISCRIMINATOR_CACHE.get(UnionType) + + m = construct_type( + value={"type": "b", "data": "foo"}, type_=cast(Any, Annotated[UnionType, PropertyInfo(discriminator="type")]) + ) + assert isinstance(m, B) + assert m.type == "b" + assert m.data == "foo" # type: ignore[comparison-overlap] + + discriminator = DISCRIMINATOR_CACHE.get(UnionType) + assert discriminator is not None + + m = construct_type( + value={"type": "b", "data": "foo"}, type_=cast(Any, Annotated[UnionType, PropertyInfo(discriminator="type")]) + ) + assert isinstance(m, B) + assert m.type == "b" + assert m.data == "foo" # type: ignore[comparison-overlap] + + # if the discriminator details object stays the same between invocations then + # we hit the cache + assert DISCRIMINATOR_CACHE.get(UnionType) is discriminator + + +@pytest.mark.skipif(PYDANTIC_V1, reason="TypeAliasType is not supported in Pydantic v1") +def test_type_alias_type() -> None: + Alias = TypeAliasType("Alias", str) # pyright: ignore + + class Model(BaseModel): + alias: Alias + union: Union[int, Alias] + + m = construct_type(value={"alias": "foo", "union": "bar"}, type_=Model) + assert isinstance(m, Model) + assert isinstance(m.alias, str) + assert m.alias == "foo" + assert isinstance(m.union, str) + assert m.union == "bar" + + +@pytest.mark.skipif(PYDANTIC_V1, reason="TypeAliasType is not supported in Pydantic v1") +def test_field_named_cls() -> None: + class Model(BaseModel): + cls: str + + m = construct_type(value={"cls": "foo"}, type_=Model) + assert isinstance(m, Model) + assert isinstance(m.cls, str) + + +def test_discriminated_union_case() -> None: + class A(BaseModel): + type: Literal["a"] + + data: bool + + class B(BaseModel): + type: Literal["b"] + + data: List[Union[A, object]] + + class ModelA(BaseModel): + type: Literal["modelA"] + + data: int + + class ModelB(BaseModel): + type: Literal["modelB"] + + required: str + + data: Union[A, B] + + # when constructing ModelA | ModelB, value data doesn't match ModelB exactly - missing `required` + m = construct_type( + value={"type": "modelB", "data": {"type": "a", "data": True}}, + type_=cast(Any, Annotated[Union[ModelA, ModelB], PropertyInfo(discriminator="type")]), + ) + + assert isinstance(m, ModelB) + + +def test_nested_discriminated_union() -> None: + class InnerType1(BaseModel): + type: Literal["type_1"] + + class InnerModel(BaseModel): + inner_value: str + + class InnerType2(BaseModel): + type: Literal["type_2"] + some_inner_model: InnerModel + + class Type1(BaseModel): + base_type: Literal["base_type_1"] + value: Annotated[ + Union[ + InnerType1, + InnerType2, + ], + PropertyInfo(discriminator="type"), + ] + + class Type2(BaseModel): + base_type: Literal["base_type_2"] + + T = Annotated[ + Union[ + Type1, + Type2, + ], + PropertyInfo(discriminator="base_type"), + ] + + model = construct_type( + type_=T, + value={ + "base_type": "base_type_1", + "value": { + "type": "type_2", + }, + }, + ) + assert isinstance(model, Type1) + assert isinstance(model.value, InnerType2) + + +@pytest.mark.skipif(PYDANTIC_V1, reason="this is only supported in pydantic v2 for now") +def test_extra_properties() -> None: + class Item(BaseModel): + prop: int + + class Model(BaseModel): + __pydantic_extra__: Dict[str, Item] = Field(init=False) # pyright: ignore[reportIncompatibleVariableOverride] + + other: str + + if TYPE_CHECKING: + + def __getattr__(self, attr: str) -> Item: ... + + model = construct_type( + type_=Model, + value={ + "a": {"prop": 1}, + "other": "foo", + }, + ) + assert isinstance(model, Model) + assert model.a.prop == 1 + assert isinstance(model.a, Item) + assert model.other == "foo" diff --git a/tests/test_qs.py b/tests/test_qs.py new file mode 100644 index 0000000..39ce40a --- /dev/null +++ b/tests/test_qs.py @@ -0,0 +1,78 @@ +from typing import Any, cast +from functools import partial +from urllib.parse import unquote + +import pytest + +from unlayer._qs import Querystring, stringify + + +def test_empty() -> None: + assert stringify({}) == "" + assert stringify({"a": {}}) == "" + assert stringify({"a": {"b": {"c": {}}}}) == "" + + +def test_basic() -> None: + assert stringify({"a": 1}) == "a=1" + assert stringify({"a": "b"}) == "a=b" + assert stringify({"a": True}) == "a=true" + assert stringify({"a": False}) == "a=false" + assert stringify({"a": 1.23456}) == "a=1.23456" + assert stringify({"a": None}) == "" + + +@pytest.mark.parametrize("method", ["class", "function"]) +def test_nested_dotted(method: str) -> None: + if method == "class": + serialise = Querystring(nested_format="dots").stringify + else: + serialise = partial(stringify, nested_format="dots") + + assert unquote(serialise({"a": {"b": "c"}})) == "a.b=c" + assert unquote(serialise({"a": {"b": "c", "d": "e", "f": "g"}})) == "a.b=c&a.d=e&a.f=g" + assert unquote(serialise({"a": {"b": {"c": {"d": "e"}}}})) == "a.b.c.d=e" + assert unquote(serialise({"a": {"b": True}})) == "a.b=true" + + +def test_nested_brackets() -> None: + assert unquote(stringify({"a": {"b": "c"}})) == "a[b]=c" + assert unquote(stringify({"a": {"b": "c", "d": "e", "f": "g"}})) == "a[b]=c&a[d]=e&a[f]=g" + assert unquote(stringify({"a": {"b": {"c": {"d": "e"}}}})) == "a[b][c][d]=e" + assert unquote(stringify({"a": {"b": True}})) == "a[b]=true" + + +@pytest.mark.parametrize("method", ["class", "function"]) +def test_array_comma(method: str) -> None: + if method == "class": + serialise = Querystring(array_format="comma").stringify + else: + serialise = partial(stringify, array_format="comma") + + assert unquote(serialise({"in": ["foo", "bar"]})) == "in=foo,bar" + assert unquote(serialise({"a": {"b": [True, False]}})) == "a[b]=true,false" + assert unquote(serialise({"a": {"b": [True, False, None, True]}})) == "a[b]=true,false,true" + + +def test_array_repeat() -> None: + assert unquote(stringify({"in": ["foo", "bar"]})) == "in=foo&in=bar" + assert unquote(stringify({"a": {"b": [True, False]}})) == "a[b]=true&a[b]=false" + assert unquote(stringify({"a": {"b": [True, False, None, True]}})) == "a[b]=true&a[b]=false&a[b]=true" + assert unquote(stringify({"in": ["foo", {"b": {"c": ["d", "e"]}}]})) == "in=foo&in[b][c]=d&in[b][c]=e" + + +@pytest.mark.parametrize("method", ["class", "function"]) +def test_array_brackets(method: str) -> None: + if method == "class": + serialise = Querystring(array_format="brackets").stringify + else: + serialise = partial(stringify, array_format="brackets") + + assert unquote(serialise({"in": ["foo", "bar"]})) == "in[]=foo&in[]=bar" + assert unquote(serialise({"a": {"b": [True, False]}})) == "a[b][]=true&a[b][]=false" + assert unquote(serialise({"a": {"b": [True, False, None, True]}})) == "a[b][]=true&a[b][]=false&a[b][]=true" + + +def test_unknown_array_format() -> None: + with pytest.raises(NotImplementedError, match="Unknown array_format value: foo, choose from comma, repeat"): + stringify({"a": ["foo", "bar"]}, array_format=cast(Any, "foo")) diff --git a/tests/test_required_args.py b/tests/test_required_args.py new file mode 100644 index 0000000..d4af672 --- /dev/null +++ b/tests/test_required_args.py @@ -0,0 +1,111 @@ +from __future__ import annotations + +import pytest + +from unlayer._utils import required_args + + +def test_too_many_positional_params() -> None: + @required_args(["a"]) + def foo(a: str | None = None) -> str | None: + return a + + with pytest.raises(TypeError, match=r"foo\(\) takes 1 argument\(s\) but 2 were given"): + foo("a", "b") # type: ignore + + +def test_positional_param() -> None: + @required_args(["a"]) + def foo(a: str | None = None) -> str | None: + return a + + assert foo("a") == "a" + assert foo(None) is None + assert foo(a="b") == "b" + + with pytest.raises(TypeError, match="Missing required argument: 'a'"): + foo() + + +def test_keyword_only_param() -> None: + @required_args(["a"]) + def foo(*, a: str | None = None) -> str | None: + return a + + assert foo(a="a") == "a" + assert foo(a=None) is None + assert foo(a="b") == "b" + + with pytest.raises(TypeError, match="Missing required argument: 'a'"): + foo() + + +def test_multiple_params() -> None: + @required_args(["a", "b", "c"]) + def foo(a: str = "", *, b: str = "", c: str = "") -> str | None: + return f"{a} {b} {c}" + + assert foo(a="a", b="b", c="c") == "a b c" + + error_message = r"Missing required arguments.*" + + with pytest.raises(TypeError, match=error_message): + foo() + + with pytest.raises(TypeError, match=error_message): + foo(a="a") + + with pytest.raises(TypeError, match=error_message): + foo(b="b") + + with pytest.raises(TypeError, match=error_message): + foo(c="c") + + with pytest.raises(TypeError, match=r"Missing required argument: 'a'"): + foo(b="a", c="c") + + with pytest.raises(TypeError, match=r"Missing required argument: 'b'"): + foo("a", c="c") + + +def test_multiple_variants() -> None: + @required_args(["a"], ["b"]) + def foo(*, a: str | None = None, b: str | None = None) -> str | None: + return a if a is not None else b + + assert foo(a="foo") == "foo" + assert foo(b="bar") == "bar" + assert foo(a=None) is None + assert foo(b=None) is None + + # TODO: this error message could probably be improved + with pytest.raises( + TypeError, + match=r"Missing required arguments; Expected either \('a'\) or \('b'\) arguments to be given", + ): + foo() + + +def test_multiple_params_multiple_variants() -> None: + @required_args(["a", "b"], ["c"]) + def foo(*, a: str | None = None, b: str | None = None, c: str | None = None) -> str | None: + if a is not None: + return a + if b is not None: + return b + return c + + error_message = r"Missing required arguments; Expected either \('a' and 'b'\) or \('c'\) arguments to be given" + + with pytest.raises(TypeError, match=error_message): + foo(a="foo") + + with pytest.raises(TypeError, match=error_message): + foo(b="bar") + + with pytest.raises(TypeError, match=error_message): + foo() + + assert foo(a=None, b="bar") == "bar" + assert foo(c=None) is None + assert foo(c="foo") == "foo" diff --git a/tests/test_response.py b/tests/test_response.py new file mode 100644 index 0000000..59831e1 --- /dev/null +++ b/tests/test_response.py @@ -0,0 +1,277 @@ +import json +from typing import Any, List, Union, cast +from typing_extensions import Annotated + +import httpx +import pytest +import pydantic + +from unlayer import Unlayer, BaseModel, AsyncUnlayer +from unlayer._response import ( + APIResponse, + BaseAPIResponse, + AsyncAPIResponse, + BinaryAPIResponse, + AsyncBinaryAPIResponse, + extract_response_type, +) +from unlayer._streaming import Stream +from unlayer._base_client import FinalRequestOptions + + +class ConcreteBaseAPIResponse(APIResponse[bytes]): ... + + +class ConcreteAPIResponse(APIResponse[List[str]]): ... + + +class ConcreteAsyncAPIResponse(APIResponse[httpx.Response]): ... + + +def test_extract_response_type_direct_classes() -> None: + assert extract_response_type(BaseAPIResponse[str]) == str + assert extract_response_type(APIResponse[str]) == str + assert extract_response_type(AsyncAPIResponse[str]) == str + + +def test_extract_response_type_direct_class_missing_type_arg() -> None: + with pytest.raises( + RuntimeError, + match="Expected type to have a type argument at index 0 but it did not", + ): + extract_response_type(AsyncAPIResponse) + + +def test_extract_response_type_concrete_subclasses() -> None: + assert extract_response_type(ConcreteBaseAPIResponse) == bytes + assert extract_response_type(ConcreteAPIResponse) == List[str] + assert extract_response_type(ConcreteAsyncAPIResponse) == httpx.Response + + +def test_extract_response_type_binary_response() -> None: + assert extract_response_type(BinaryAPIResponse) == bytes + assert extract_response_type(AsyncBinaryAPIResponse) == bytes + + +class PydanticModel(pydantic.BaseModel): ... + + +def test_response_parse_mismatched_basemodel(client: Unlayer) -> None: + response = APIResponse( + raw=httpx.Response(200, content=b"foo"), + client=client, + stream=False, + stream_cls=None, + cast_to=str, + options=FinalRequestOptions.construct(method="get", url="/foo"), + ) + + with pytest.raises( + TypeError, + match="Pydantic models must subclass our base model type, e.g. `from unlayer import BaseModel`", + ): + response.parse(to=PydanticModel) + + +@pytest.mark.asyncio +async def test_async_response_parse_mismatched_basemodel(async_client: AsyncUnlayer) -> None: + response = AsyncAPIResponse( + raw=httpx.Response(200, content=b"foo"), + client=async_client, + stream=False, + stream_cls=None, + cast_to=str, + options=FinalRequestOptions.construct(method="get", url="/foo"), + ) + + with pytest.raises( + TypeError, + match="Pydantic models must subclass our base model type, e.g. `from unlayer import BaseModel`", + ): + await response.parse(to=PydanticModel) + + +def test_response_parse_custom_stream(client: Unlayer) -> None: + response = APIResponse( + raw=httpx.Response(200, content=b"foo"), + client=client, + stream=True, + stream_cls=None, + cast_to=str, + options=FinalRequestOptions.construct(method="get", url="/foo"), + ) + + stream = response.parse(to=Stream[int]) + assert stream._cast_to == int + + +@pytest.mark.asyncio +async def test_async_response_parse_custom_stream(async_client: AsyncUnlayer) -> None: + response = AsyncAPIResponse( + raw=httpx.Response(200, content=b"foo"), + client=async_client, + stream=True, + stream_cls=None, + cast_to=str, + options=FinalRequestOptions.construct(method="get", url="/foo"), + ) + + stream = await response.parse(to=Stream[int]) + assert stream._cast_to == int + + +class CustomModel(BaseModel): + foo: str + bar: int + + +def test_response_parse_custom_model(client: Unlayer) -> None: + response = APIResponse( + raw=httpx.Response(200, content=json.dumps({"foo": "hello!", "bar": 2})), + client=client, + stream=False, + stream_cls=None, + cast_to=str, + options=FinalRequestOptions.construct(method="get", url="/foo"), + ) + + obj = response.parse(to=CustomModel) + assert obj.foo == "hello!" + assert obj.bar == 2 + + +@pytest.mark.asyncio +async def test_async_response_parse_custom_model(async_client: AsyncUnlayer) -> None: + response = AsyncAPIResponse( + raw=httpx.Response(200, content=json.dumps({"foo": "hello!", "bar": 2})), + client=async_client, + stream=False, + stream_cls=None, + cast_to=str, + options=FinalRequestOptions.construct(method="get", url="/foo"), + ) + + obj = await response.parse(to=CustomModel) + assert obj.foo == "hello!" + assert obj.bar == 2 + + +def test_response_parse_annotated_type(client: Unlayer) -> None: + response = APIResponse( + raw=httpx.Response(200, content=json.dumps({"foo": "hello!", "bar": 2})), + client=client, + stream=False, + stream_cls=None, + cast_to=str, + options=FinalRequestOptions.construct(method="get", url="/foo"), + ) + + obj = response.parse( + to=cast("type[CustomModel]", Annotated[CustomModel, "random metadata"]), + ) + assert obj.foo == "hello!" + assert obj.bar == 2 + + +async def test_async_response_parse_annotated_type(async_client: AsyncUnlayer) -> None: + response = AsyncAPIResponse( + raw=httpx.Response(200, content=json.dumps({"foo": "hello!", "bar": 2})), + client=async_client, + stream=False, + stream_cls=None, + cast_to=str, + options=FinalRequestOptions.construct(method="get", url="/foo"), + ) + + obj = await response.parse( + to=cast("type[CustomModel]", Annotated[CustomModel, "random metadata"]), + ) + assert obj.foo == "hello!" + assert obj.bar == 2 + + +@pytest.mark.parametrize( + "content, expected", + [ + ("false", False), + ("true", True), + ("False", False), + ("True", True), + ("TrUe", True), + ("FalSe", False), + ], +) +def test_response_parse_bool(client: Unlayer, content: str, expected: bool) -> None: + response = APIResponse( + raw=httpx.Response(200, content=content), + client=client, + stream=False, + stream_cls=None, + cast_to=str, + options=FinalRequestOptions.construct(method="get", url="/foo"), + ) + + result = response.parse(to=bool) + assert result is expected + + +@pytest.mark.parametrize( + "content, expected", + [ + ("false", False), + ("true", True), + ("False", False), + ("True", True), + ("TrUe", True), + ("FalSe", False), + ], +) +async def test_async_response_parse_bool(client: AsyncUnlayer, content: str, expected: bool) -> None: + response = AsyncAPIResponse( + raw=httpx.Response(200, content=content), + client=client, + stream=False, + stream_cls=None, + cast_to=str, + options=FinalRequestOptions.construct(method="get", url="/foo"), + ) + + result = await response.parse(to=bool) + assert result is expected + + +class OtherModel(BaseModel): + a: str + + +@pytest.mark.parametrize("client", [False], indirect=True) # loose validation +def test_response_parse_expect_model_union_non_json_content(client: Unlayer) -> None: + response = APIResponse( + raw=httpx.Response(200, content=b"foo", headers={"Content-Type": "application/text"}), + client=client, + stream=False, + stream_cls=None, + cast_to=str, + options=FinalRequestOptions.construct(method="get", url="/foo"), + ) + + obj = response.parse(to=cast(Any, Union[CustomModel, OtherModel])) + assert isinstance(obj, str) + assert obj == "foo" + + +@pytest.mark.asyncio +@pytest.mark.parametrize("async_client", [False], indirect=True) # loose validation +async def test_async_response_parse_expect_model_union_non_json_content(async_client: AsyncUnlayer) -> None: + response = AsyncAPIResponse( + raw=httpx.Response(200, content=b"foo", headers={"Content-Type": "application/text"}), + client=async_client, + stream=False, + stream_cls=None, + cast_to=str, + options=FinalRequestOptions.construct(method="get", url="/foo"), + ) + + obj = await response.parse(to=cast(Any, Union[CustomModel, OtherModel])) + assert isinstance(obj, str) + assert obj == "foo" diff --git a/tests/test_streaming.py b/tests/test_streaming.py new file mode 100644 index 0000000..187984d --- /dev/null +++ b/tests/test_streaming.py @@ -0,0 +1,248 @@ +from __future__ import annotations + +from typing import Iterator, AsyncIterator + +import httpx +import pytest + +from unlayer import Unlayer, AsyncUnlayer +from unlayer._streaming import Stream, AsyncStream, ServerSentEvent + + +@pytest.mark.asyncio +@pytest.mark.parametrize("sync", [True, False], ids=["sync", "async"]) +async def test_basic(sync: bool, client: Unlayer, async_client: AsyncUnlayer) -> None: + def body() -> Iterator[bytes]: + yield b"event: completion\n" + yield b'data: {"foo":true}\n' + yield b"\n" + + iterator = make_event_iterator(content=body(), sync=sync, client=client, async_client=async_client) + + sse = await iter_next(iterator) + assert sse.event == "completion" + assert sse.json() == {"foo": True} + + await assert_empty_iter(iterator) + + +@pytest.mark.asyncio +@pytest.mark.parametrize("sync", [True, False], ids=["sync", "async"]) +async def test_data_missing_event(sync: bool, client: Unlayer, async_client: AsyncUnlayer) -> None: + def body() -> Iterator[bytes]: + yield b'data: {"foo":true}\n' + yield b"\n" + + iterator = make_event_iterator(content=body(), sync=sync, client=client, async_client=async_client) + + sse = await iter_next(iterator) + assert sse.event is None + assert sse.json() == {"foo": True} + + await assert_empty_iter(iterator) + + +@pytest.mark.asyncio +@pytest.mark.parametrize("sync", [True, False], ids=["sync", "async"]) +async def test_event_missing_data(sync: bool, client: Unlayer, async_client: AsyncUnlayer) -> None: + def body() -> Iterator[bytes]: + yield b"event: ping\n" + yield b"\n" + + iterator = make_event_iterator(content=body(), sync=sync, client=client, async_client=async_client) + + sse = await iter_next(iterator) + assert sse.event == "ping" + assert sse.data == "" + + await assert_empty_iter(iterator) + + +@pytest.mark.asyncio +@pytest.mark.parametrize("sync", [True, False], ids=["sync", "async"]) +async def test_multiple_events(sync: bool, client: Unlayer, async_client: AsyncUnlayer) -> None: + def body() -> Iterator[bytes]: + yield b"event: ping\n" + yield b"\n" + yield b"event: completion\n" + yield b"\n" + + iterator = make_event_iterator(content=body(), sync=sync, client=client, async_client=async_client) + + sse = await iter_next(iterator) + assert sse.event == "ping" + assert sse.data == "" + + sse = await iter_next(iterator) + assert sse.event == "completion" + assert sse.data == "" + + await assert_empty_iter(iterator) + + +@pytest.mark.asyncio +@pytest.mark.parametrize("sync", [True, False], ids=["sync", "async"]) +async def test_multiple_events_with_data(sync: bool, client: Unlayer, async_client: AsyncUnlayer) -> None: + def body() -> Iterator[bytes]: + yield b"event: ping\n" + yield b'data: {"foo":true}\n' + yield b"\n" + yield b"event: completion\n" + yield b'data: {"bar":false}\n' + yield b"\n" + + iterator = make_event_iterator(content=body(), sync=sync, client=client, async_client=async_client) + + sse = await iter_next(iterator) + assert sse.event == "ping" + assert sse.json() == {"foo": True} + + sse = await iter_next(iterator) + assert sse.event == "completion" + assert sse.json() == {"bar": False} + + await assert_empty_iter(iterator) + + +@pytest.mark.asyncio +@pytest.mark.parametrize("sync", [True, False], ids=["sync", "async"]) +async def test_multiple_data_lines_with_empty_line(sync: bool, client: Unlayer, async_client: AsyncUnlayer) -> None: + def body() -> Iterator[bytes]: + yield b"event: ping\n" + yield b"data: {\n" + yield b'data: "foo":\n' + yield b"data: \n" + yield b"data:\n" + yield b"data: true}\n" + yield b"\n\n" + + iterator = make_event_iterator(content=body(), sync=sync, client=client, async_client=async_client) + + sse = await iter_next(iterator) + assert sse.event == "ping" + assert sse.json() == {"foo": True} + assert sse.data == '{\n"foo":\n\n\ntrue}' + + await assert_empty_iter(iterator) + + +@pytest.mark.asyncio +@pytest.mark.parametrize("sync", [True, False], ids=["sync", "async"]) +async def test_data_json_escaped_double_new_line(sync: bool, client: Unlayer, async_client: AsyncUnlayer) -> None: + def body() -> Iterator[bytes]: + yield b"event: ping\n" + yield b'data: {"foo": "my long\\n\\ncontent"}' + yield b"\n\n" + + iterator = make_event_iterator(content=body(), sync=sync, client=client, async_client=async_client) + + sse = await iter_next(iterator) + assert sse.event == "ping" + assert sse.json() == {"foo": "my long\n\ncontent"} + + await assert_empty_iter(iterator) + + +@pytest.mark.asyncio +@pytest.mark.parametrize("sync", [True, False], ids=["sync", "async"]) +async def test_multiple_data_lines(sync: bool, client: Unlayer, async_client: AsyncUnlayer) -> None: + def body() -> Iterator[bytes]: + yield b"event: ping\n" + yield b"data: {\n" + yield b'data: "foo":\n' + yield b"data: true}\n" + yield b"\n\n" + + iterator = make_event_iterator(content=body(), sync=sync, client=client, async_client=async_client) + + sse = await iter_next(iterator) + assert sse.event == "ping" + assert sse.json() == {"foo": True} + + await assert_empty_iter(iterator) + + +@pytest.mark.parametrize("sync", [True, False], ids=["sync", "async"]) +async def test_special_new_line_character( + sync: bool, + client: Unlayer, + async_client: AsyncUnlayer, +) -> None: + def body() -> Iterator[bytes]: + yield b'data: {"content":" culpa"}\n' + yield b"\n" + yield b'data: {"content":" \xe2\x80\xa8"}\n' + yield b"\n" + yield b'data: {"content":"foo"}\n' + yield b"\n" + + iterator = make_event_iterator(content=body(), sync=sync, client=client, async_client=async_client) + + sse = await iter_next(iterator) + assert sse.event is None + assert sse.json() == {"content": " culpa"} + + sse = await iter_next(iterator) + assert sse.event is None + assert sse.json() == {"content": " 
"} + + sse = await iter_next(iterator) + assert sse.event is None + assert sse.json() == {"content": "foo"} + + await assert_empty_iter(iterator) + + +@pytest.mark.parametrize("sync", [True, False], ids=["sync", "async"]) +async def test_multi_byte_character_multiple_chunks( + sync: bool, + client: Unlayer, + async_client: AsyncUnlayer, +) -> None: + def body() -> Iterator[bytes]: + yield b'data: {"content":"' + # bytes taken from the string 'известни' and arbitrarily split + # so that some multi-byte characters span multiple chunks + yield b"\xd0" + yield b"\xb8\xd0\xb7\xd0" + yield b"\xb2\xd0\xb5\xd1\x81\xd1\x82\xd0\xbd\xd0\xb8" + yield b'"}\n' + yield b"\n" + + iterator = make_event_iterator(content=body(), sync=sync, client=client, async_client=async_client) + + sse = await iter_next(iterator) + assert sse.event is None + assert sse.json() == {"content": "известни"} + + +async def to_aiter(iter: Iterator[bytes]) -> AsyncIterator[bytes]: + for chunk in iter: + yield chunk + + +async def iter_next(iter: Iterator[ServerSentEvent] | AsyncIterator[ServerSentEvent]) -> ServerSentEvent: + if isinstance(iter, AsyncIterator): + return await iter.__anext__() + + return next(iter) + + +async def assert_empty_iter(iter: Iterator[ServerSentEvent] | AsyncIterator[ServerSentEvent]) -> None: + with pytest.raises((StopAsyncIteration, RuntimeError)): + await iter_next(iter) + + +def make_event_iterator( + content: Iterator[bytes], + *, + sync: bool, + client: Unlayer, + async_client: AsyncUnlayer, +) -> Iterator[ServerSentEvent] | AsyncIterator[ServerSentEvent]: + if sync: + return Stream(cast_to=object, client=client, response=httpx.Response(200, content=content))._iter_events() + + return AsyncStream( + cast_to=object, client=async_client, response=httpx.Response(200, content=to_aiter(content)) + )._iter_events() diff --git a/tests/test_transform.py b/tests/test_transform.py new file mode 100644 index 0000000..f33db03 --- /dev/null +++ b/tests/test_transform.py @@ -0,0 +1,460 @@ +from __future__ import annotations + +import io +import pathlib +from typing import Any, Dict, List, Union, TypeVar, Iterable, Optional, cast +from datetime import date, datetime +from typing_extensions import Required, Annotated, TypedDict + +import pytest + +from unlayer._types import Base64FileInput, omit, not_given +from unlayer._utils import ( + PropertyInfo, + transform as _transform, + parse_datetime, + async_transform as _async_transform, +) +from unlayer._compat import PYDANTIC_V1 +from unlayer._models import BaseModel + +_T = TypeVar("_T") + +SAMPLE_FILE_PATH = pathlib.Path(__file__).parent.joinpath("sample_file.txt") + + +async def transform( + data: _T, + expected_type: object, + use_async: bool, +) -> _T: + if use_async: + return await _async_transform(data, expected_type=expected_type) + + return _transform(data, expected_type=expected_type) + + +parametrize = pytest.mark.parametrize("use_async", [False, True], ids=["sync", "async"]) + + +class Foo1(TypedDict): + foo_bar: Annotated[str, PropertyInfo(alias="fooBar")] + + +@parametrize +@pytest.mark.asyncio +async def test_top_level_alias(use_async: bool) -> None: + assert await transform({"foo_bar": "hello"}, expected_type=Foo1, use_async=use_async) == {"fooBar": "hello"} + + +class Foo2(TypedDict): + bar: Bar2 + + +class Bar2(TypedDict): + this_thing: Annotated[int, PropertyInfo(alias="this__thing")] + baz: Annotated[Baz2, PropertyInfo(alias="Baz")] + + +class Baz2(TypedDict): + my_baz: Annotated[str, PropertyInfo(alias="myBaz")] + + +@parametrize +@pytest.mark.asyncio +async def test_recursive_typeddict(use_async: bool) -> None: + assert await transform({"bar": {"this_thing": 1}}, Foo2, use_async) == {"bar": {"this__thing": 1}} + assert await transform({"bar": {"baz": {"my_baz": "foo"}}}, Foo2, use_async) == {"bar": {"Baz": {"myBaz": "foo"}}} + + +class Foo3(TypedDict): + things: List[Bar3] + + +class Bar3(TypedDict): + my_field: Annotated[str, PropertyInfo(alias="myField")] + + +@parametrize +@pytest.mark.asyncio +async def test_list_of_typeddict(use_async: bool) -> None: + result = await transform({"things": [{"my_field": "foo"}, {"my_field": "foo2"}]}, Foo3, use_async) + assert result == {"things": [{"myField": "foo"}, {"myField": "foo2"}]} + + +class Foo4(TypedDict): + foo: Union[Bar4, Baz4] + + +class Bar4(TypedDict): + foo_bar: Annotated[str, PropertyInfo(alias="fooBar")] + + +class Baz4(TypedDict): + foo_baz: Annotated[str, PropertyInfo(alias="fooBaz")] + + +@parametrize +@pytest.mark.asyncio +async def test_union_of_typeddict(use_async: bool) -> None: + assert await transform({"foo": {"foo_bar": "bar"}}, Foo4, use_async) == {"foo": {"fooBar": "bar"}} + assert await transform({"foo": {"foo_baz": "baz"}}, Foo4, use_async) == {"foo": {"fooBaz": "baz"}} + assert await transform({"foo": {"foo_baz": "baz", "foo_bar": "bar"}}, Foo4, use_async) == { + "foo": {"fooBaz": "baz", "fooBar": "bar"} + } + + +class Foo5(TypedDict): + foo: Annotated[Union[Bar4, List[Baz4]], PropertyInfo(alias="FOO")] + + +class Bar5(TypedDict): + foo_bar: Annotated[str, PropertyInfo(alias="fooBar")] + + +class Baz5(TypedDict): + foo_baz: Annotated[str, PropertyInfo(alias="fooBaz")] + + +@parametrize +@pytest.mark.asyncio +async def test_union_of_list(use_async: bool) -> None: + assert await transform({"foo": {"foo_bar": "bar"}}, Foo5, use_async) == {"FOO": {"fooBar": "bar"}} + assert await transform( + { + "foo": [ + {"foo_baz": "baz"}, + {"foo_baz": "baz"}, + ] + }, + Foo5, + use_async, + ) == {"FOO": [{"fooBaz": "baz"}, {"fooBaz": "baz"}]} + + +class Foo6(TypedDict): + bar: Annotated[str, PropertyInfo(alias="Bar")] + + +@parametrize +@pytest.mark.asyncio +async def test_includes_unknown_keys(use_async: bool) -> None: + assert await transform({"bar": "bar", "baz_": {"FOO": 1}}, Foo6, use_async) == { + "Bar": "bar", + "baz_": {"FOO": 1}, + } + + +class Foo7(TypedDict): + bar: Annotated[List[Bar7], PropertyInfo(alias="bAr")] + foo: Bar7 + + +class Bar7(TypedDict): + foo: str + + +@parametrize +@pytest.mark.asyncio +async def test_ignores_invalid_input(use_async: bool) -> None: + assert await transform({"bar": ""}, Foo7, use_async) == {"bAr": ""} + assert await transform({"foo": ""}, Foo7, use_async) == {"foo": ""} + + +class DatetimeDict(TypedDict, total=False): + foo: Annotated[datetime, PropertyInfo(format="iso8601")] + + bar: Annotated[Optional[datetime], PropertyInfo(format="iso8601")] + + required: Required[Annotated[Optional[datetime], PropertyInfo(format="iso8601")]] + + list_: Required[Annotated[Optional[List[datetime]], PropertyInfo(format="iso8601")]] + + union: Annotated[Union[int, datetime], PropertyInfo(format="iso8601")] + + +class DateDict(TypedDict, total=False): + foo: Annotated[date, PropertyInfo(format="iso8601")] + + +class DatetimeModel(BaseModel): + foo: datetime + + +class DateModel(BaseModel): + foo: Optional[date] + + +@parametrize +@pytest.mark.asyncio +async def test_iso8601_format(use_async: bool) -> None: + dt = datetime.fromisoformat("2023-02-23T14:16:36.337692+00:00") + tz = "+00:00" if PYDANTIC_V1 else "Z" + assert await transform({"foo": dt}, DatetimeDict, use_async) == {"foo": "2023-02-23T14:16:36.337692+00:00"} # type: ignore[comparison-overlap] + assert await transform(DatetimeModel(foo=dt), Any, use_async) == {"foo": "2023-02-23T14:16:36.337692" + tz} # type: ignore[comparison-overlap] + + dt = dt.replace(tzinfo=None) + assert await transform({"foo": dt}, DatetimeDict, use_async) == {"foo": "2023-02-23T14:16:36.337692"} # type: ignore[comparison-overlap] + assert await transform(DatetimeModel(foo=dt), Any, use_async) == {"foo": "2023-02-23T14:16:36.337692"} # type: ignore[comparison-overlap] + + assert await transform({"foo": None}, DateDict, use_async) == {"foo": None} # type: ignore[comparison-overlap] + assert await transform(DateModel(foo=None), Any, use_async) == {"foo": None} # type: ignore + assert await transform({"foo": date.fromisoformat("2023-02-23")}, DateDict, use_async) == {"foo": "2023-02-23"} # type: ignore[comparison-overlap] + assert await transform(DateModel(foo=date.fromisoformat("2023-02-23")), DateDict, use_async) == { + "foo": "2023-02-23" + } # type: ignore[comparison-overlap] + + +@parametrize +@pytest.mark.asyncio +async def test_optional_iso8601_format(use_async: bool) -> None: + dt = datetime.fromisoformat("2023-02-23T14:16:36.337692+00:00") + assert await transform({"bar": dt}, DatetimeDict, use_async) == {"bar": "2023-02-23T14:16:36.337692+00:00"} # type: ignore[comparison-overlap] + + assert await transform({"bar": None}, DatetimeDict, use_async) == {"bar": None} + + +@parametrize +@pytest.mark.asyncio +async def test_required_iso8601_format(use_async: bool) -> None: + dt = datetime.fromisoformat("2023-02-23T14:16:36.337692+00:00") + assert await transform({"required": dt}, DatetimeDict, use_async) == { + "required": "2023-02-23T14:16:36.337692+00:00" + } # type: ignore[comparison-overlap] + + assert await transform({"required": None}, DatetimeDict, use_async) == {"required": None} + + +@parametrize +@pytest.mark.asyncio +async def test_union_datetime(use_async: bool) -> None: + dt = datetime.fromisoformat("2023-02-23T14:16:36.337692+00:00") + assert await transform({"union": dt}, DatetimeDict, use_async) == { # type: ignore[comparison-overlap] + "union": "2023-02-23T14:16:36.337692+00:00" + } + + assert await transform({"union": "foo"}, DatetimeDict, use_async) == {"union": "foo"} + + +@parametrize +@pytest.mark.asyncio +async def test_nested_list_iso6801_format(use_async: bool) -> None: + dt1 = datetime.fromisoformat("2023-02-23T14:16:36.337692+00:00") + dt2 = parse_datetime("2022-01-15T06:34:23Z") + assert await transform({"list_": [dt1, dt2]}, DatetimeDict, use_async) == { # type: ignore[comparison-overlap] + "list_": ["2023-02-23T14:16:36.337692+00:00", "2022-01-15T06:34:23+00:00"] + } + + +@parametrize +@pytest.mark.asyncio +async def test_datetime_custom_format(use_async: bool) -> None: + dt = parse_datetime("2022-01-15T06:34:23Z") + + result = await transform(dt, Annotated[datetime, PropertyInfo(format="custom", format_template="%H")], use_async) + assert result == "06" # type: ignore[comparison-overlap] + + +class DateDictWithRequiredAlias(TypedDict, total=False): + required_prop: Required[Annotated[date, PropertyInfo(format="iso8601", alias="prop")]] + + +@parametrize +@pytest.mark.asyncio +async def test_datetime_with_alias(use_async: bool) -> None: + assert await transform({"required_prop": None}, DateDictWithRequiredAlias, use_async) == {"prop": None} # type: ignore[comparison-overlap] + assert await transform( + {"required_prop": date.fromisoformat("2023-02-23")}, DateDictWithRequiredAlias, use_async + ) == {"prop": "2023-02-23"} # type: ignore[comparison-overlap] + + +class MyModel(BaseModel): + foo: str + + +@parametrize +@pytest.mark.asyncio +async def test_pydantic_model_to_dictionary(use_async: bool) -> None: + assert cast(Any, await transform(MyModel(foo="hi!"), Any, use_async)) == {"foo": "hi!"} + assert cast(Any, await transform(MyModel.construct(foo="hi!"), Any, use_async)) == {"foo": "hi!"} + + +@parametrize +@pytest.mark.asyncio +async def test_pydantic_empty_model(use_async: bool) -> None: + assert cast(Any, await transform(MyModel.construct(), Any, use_async)) == {} + + +@parametrize +@pytest.mark.asyncio +async def test_pydantic_unknown_field(use_async: bool) -> None: + assert cast(Any, await transform(MyModel.construct(my_untyped_field=True), Any, use_async)) == { + "my_untyped_field": True + } + + +@parametrize +@pytest.mark.asyncio +async def test_pydantic_mismatched_types(use_async: bool) -> None: + model = MyModel.construct(foo=True) + if PYDANTIC_V1: + params = await transform(model, Any, use_async) + else: + with pytest.warns(UserWarning): + params = await transform(model, Any, use_async) + assert cast(Any, params) == {"foo": True} + + +@parametrize +@pytest.mark.asyncio +async def test_pydantic_mismatched_object_type(use_async: bool) -> None: + model = MyModel.construct(foo=MyModel.construct(hello="world")) + if PYDANTIC_V1: + params = await transform(model, Any, use_async) + else: + with pytest.warns(UserWarning): + params = await transform(model, Any, use_async) + assert cast(Any, params) == {"foo": {"hello": "world"}} + + +class ModelNestedObjects(BaseModel): + nested: MyModel + + +@parametrize +@pytest.mark.asyncio +async def test_pydantic_nested_objects(use_async: bool) -> None: + model = ModelNestedObjects.construct(nested={"foo": "stainless"}) + assert isinstance(model.nested, MyModel) + assert cast(Any, await transform(model, Any, use_async)) == {"nested": {"foo": "stainless"}} + + +class ModelWithDefaultField(BaseModel): + foo: str + with_none_default: Union[str, None] = None + with_str_default: str = "foo" + + +@parametrize +@pytest.mark.asyncio +async def test_pydantic_default_field(use_async: bool) -> None: + # should be excluded when defaults are used + model = ModelWithDefaultField.construct() + assert model.with_none_default is None + assert model.with_str_default == "foo" + assert cast(Any, await transform(model, Any, use_async)) == {} + + # should be included when the default value is explicitly given + model = ModelWithDefaultField.construct(with_none_default=None, with_str_default="foo") + assert model.with_none_default is None + assert model.with_str_default == "foo" + assert cast(Any, await transform(model, Any, use_async)) == {"with_none_default": None, "with_str_default": "foo"} + + # should be included when a non-default value is explicitly given + model = ModelWithDefaultField.construct(with_none_default="bar", with_str_default="baz") + assert model.with_none_default == "bar" + assert model.with_str_default == "baz" + assert cast(Any, await transform(model, Any, use_async)) == {"with_none_default": "bar", "with_str_default": "baz"} + + +class TypedDictIterableUnion(TypedDict): + foo: Annotated[Union[Bar8, Iterable[Baz8]], PropertyInfo(alias="FOO")] + + +class Bar8(TypedDict): + foo_bar: Annotated[str, PropertyInfo(alias="fooBar")] + + +class Baz8(TypedDict): + foo_baz: Annotated[str, PropertyInfo(alias="fooBaz")] + + +@parametrize +@pytest.mark.asyncio +async def test_iterable_of_dictionaries(use_async: bool) -> None: + assert await transform({"foo": [{"foo_baz": "bar"}]}, TypedDictIterableUnion, use_async) == { + "FOO": [{"fooBaz": "bar"}] + } + assert cast(Any, await transform({"foo": ({"foo_baz": "bar"},)}, TypedDictIterableUnion, use_async)) == { + "FOO": [{"fooBaz": "bar"}] + } + + def my_iter() -> Iterable[Baz8]: + yield {"foo_baz": "hello"} + yield {"foo_baz": "world"} + + assert await transform({"foo": my_iter()}, TypedDictIterableUnion, use_async) == { + "FOO": [{"fooBaz": "hello"}, {"fooBaz": "world"}] + } + + +@parametrize +@pytest.mark.asyncio +async def test_dictionary_items(use_async: bool) -> None: + class DictItems(TypedDict): + foo_baz: Annotated[str, PropertyInfo(alias="fooBaz")] + + assert await transform({"foo": {"foo_baz": "bar"}}, Dict[str, DictItems], use_async) == {"foo": {"fooBaz": "bar"}} + + +class TypedDictIterableUnionStr(TypedDict): + foo: Annotated[Union[str, Iterable[Baz8]], PropertyInfo(alias="FOO")] + + +@parametrize +@pytest.mark.asyncio +async def test_iterable_union_str(use_async: bool) -> None: + assert await transform({"foo": "bar"}, TypedDictIterableUnionStr, use_async) == {"FOO": "bar"} + assert cast(Any, await transform(iter([{"foo_baz": "bar"}]), Union[str, Iterable[Baz8]], use_async)) == [ + {"fooBaz": "bar"} + ] + + +class TypedDictBase64Input(TypedDict): + foo: Annotated[Union[str, Base64FileInput], PropertyInfo(format="base64")] + + +@parametrize +@pytest.mark.asyncio +async def test_base64_file_input(use_async: bool) -> None: + # strings are left as-is + assert await transform({"foo": "bar"}, TypedDictBase64Input, use_async) == {"foo": "bar"} + + # pathlib.Path is automatically converted to base64 + assert await transform({"foo": SAMPLE_FILE_PATH}, TypedDictBase64Input, use_async) == { + "foo": "SGVsbG8sIHdvcmxkIQo=" + } # type: ignore[comparison-overlap] + + # io instances are automatically converted to base64 + assert await transform({"foo": io.StringIO("Hello, world!")}, TypedDictBase64Input, use_async) == { + "foo": "SGVsbG8sIHdvcmxkIQ==" + } # type: ignore[comparison-overlap] + assert await transform({"foo": io.BytesIO(b"Hello, world!")}, TypedDictBase64Input, use_async) == { + "foo": "SGVsbG8sIHdvcmxkIQ==" + } # type: ignore[comparison-overlap] + + +@parametrize +@pytest.mark.asyncio +async def test_transform_skipping(use_async: bool) -> None: + # lists of ints are left as-is + data = [1, 2, 3] + assert await transform(data, List[int], use_async) is data + + # iterables of ints are converted to a list + data = iter([1, 2, 3]) + assert await transform(data, Iterable[int], use_async) == [1, 2, 3] + + +@parametrize +@pytest.mark.asyncio +async def test_strips_notgiven(use_async: bool) -> None: + assert await transform({"foo_bar": "bar"}, Foo1, use_async) == {"fooBar": "bar"} + assert await transform({"foo_bar": not_given}, Foo1, use_async) == {} + + +@parametrize +@pytest.mark.asyncio +async def test_strips_omit(use_async: bool) -> None: + assert await transform({"foo_bar": "bar"}, Foo1, use_async) == {"fooBar": "bar"} + assert await transform({"foo_bar": omit}, Foo1, use_async) == {} diff --git a/tests/test_utils/test_datetime_parse.py b/tests/test_utils/test_datetime_parse.py new file mode 100644 index 0000000..d3e9d42 --- /dev/null +++ b/tests/test_utils/test_datetime_parse.py @@ -0,0 +1,110 @@ +""" +Copied from https://github.com/pydantic/pydantic/blob/v1.10.22/tests/test_datetime_parse.py +with modifications so it works without pydantic v1 imports. +""" + +from typing import Type, Union +from datetime import date, datetime, timezone, timedelta + +import pytest + +from unlayer._utils import parse_date, parse_datetime + + +def create_tz(minutes: int) -> timezone: + return timezone(timedelta(minutes=minutes)) + + +@pytest.mark.parametrize( + "value,result", + [ + # Valid inputs + ("1494012444.883309", date(2017, 5, 5)), + (b"1494012444.883309", date(2017, 5, 5)), + (1_494_012_444.883_309, date(2017, 5, 5)), + ("1494012444", date(2017, 5, 5)), + (1_494_012_444, date(2017, 5, 5)), + (0, date(1970, 1, 1)), + ("2012-04-23", date(2012, 4, 23)), + (b"2012-04-23", date(2012, 4, 23)), + ("2012-4-9", date(2012, 4, 9)), + (date(2012, 4, 9), date(2012, 4, 9)), + (datetime(2012, 4, 9, 12, 15), date(2012, 4, 9)), + # Invalid inputs + ("x20120423", ValueError), + ("2012-04-56", ValueError), + (19_999_999_999, date(2603, 10, 11)), # just before watershed + (20_000_000_001, date(1970, 8, 20)), # just after watershed + (1_549_316_052, date(2019, 2, 4)), # nowish in s + (1_549_316_052_104, date(2019, 2, 4)), # nowish in ms + (1_549_316_052_104_324, date(2019, 2, 4)), # nowish in μs + (1_549_316_052_104_324_096, date(2019, 2, 4)), # nowish in ns + ("infinity", date(9999, 12, 31)), + ("inf", date(9999, 12, 31)), + (float("inf"), date(9999, 12, 31)), + ("infinity ", date(9999, 12, 31)), + (int("1" + "0" * 100), date(9999, 12, 31)), + (1e1000, date(9999, 12, 31)), + ("-infinity", date(1, 1, 1)), + ("-inf", date(1, 1, 1)), + ("nan", ValueError), + ], +) +def test_date_parsing(value: Union[str, bytes, int, float], result: Union[date, Type[Exception]]) -> None: + if type(result) == type and issubclass(result, Exception): # pyright: ignore[reportUnnecessaryIsInstance] + with pytest.raises(result): + parse_date(value) + else: + assert parse_date(value) == result + + +@pytest.mark.parametrize( + "value,result", + [ + # Valid inputs + # values in seconds + ("1494012444.883309", datetime(2017, 5, 5, 19, 27, 24, 883_309, tzinfo=timezone.utc)), + (1_494_012_444.883_309, datetime(2017, 5, 5, 19, 27, 24, 883_309, tzinfo=timezone.utc)), + ("1494012444", datetime(2017, 5, 5, 19, 27, 24, tzinfo=timezone.utc)), + (b"1494012444", datetime(2017, 5, 5, 19, 27, 24, tzinfo=timezone.utc)), + (1_494_012_444, datetime(2017, 5, 5, 19, 27, 24, tzinfo=timezone.utc)), + # values in ms + ("1494012444000.883309", datetime(2017, 5, 5, 19, 27, 24, 883, tzinfo=timezone.utc)), + ("-1494012444000.883309", datetime(1922, 8, 29, 4, 32, 35, 999117, tzinfo=timezone.utc)), + (1_494_012_444_000, datetime(2017, 5, 5, 19, 27, 24, tzinfo=timezone.utc)), + ("2012-04-23T09:15:00", datetime(2012, 4, 23, 9, 15)), + ("2012-4-9 4:8:16", datetime(2012, 4, 9, 4, 8, 16)), + ("2012-04-23T09:15:00Z", datetime(2012, 4, 23, 9, 15, 0, 0, timezone.utc)), + ("2012-4-9 4:8:16-0320", datetime(2012, 4, 9, 4, 8, 16, 0, create_tz(-200))), + ("2012-04-23T10:20:30.400+02:30", datetime(2012, 4, 23, 10, 20, 30, 400_000, create_tz(150))), + ("2012-04-23T10:20:30.400+02", datetime(2012, 4, 23, 10, 20, 30, 400_000, create_tz(120))), + ("2012-04-23T10:20:30.400-02", datetime(2012, 4, 23, 10, 20, 30, 400_000, create_tz(-120))), + (b"2012-04-23T10:20:30.400-02", datetime(2012, 4, 23, 10, 20, 30, 400_000, create_tz(-120))), + (datetime(2017, 5, 5), datetime(2017, 5, 5)), + (0, datetime(1970, 1, 1, 0, 0, 0, tzinfo=timezone.utc)), + # Invalid inputs + ("x20120423091500", ValueError), + ("2012-04-56T09:15:90", ValueError), + ("2012-04-23T11:05:00-25:00", ValueError), + (19_999_999_999, datetime(2603, 10, 11, 11, 33, 19, tzinfo=timezone.utc)), # just before watershed + (20_000_000_001, datetime(1970, 8, 20, 11, 33, 20, 1000, tzinfo=timezone.utc)), # just after watershed + (1_549_316_052, datetime(2019, 2, 4, 21, 34, 12, 0, tzinfo=timezone.utc)), # nowish in s + (1_549_316_052_104, datetime(2019, 2, 4, 21, 34, 12, 104_000, tzinfo=timezone.utc)), # nowish in ms + (1_549_316_052_104_324, datetime(2019, 2, 4, 21, 34, 12, 104_324, tzinfo=timezone.utc)), # nowish in μs + (1_549_316_052_104_324_096, datetime(2019, 2, 4, 21, 34, 12, 104_324, tzinfo=timezone.utc)), # nowish in ns + ("infinity", datetime(9999, 12, 31, 23, 59, 59, 999999)), + ("inf", datetime(9999, 12, 31, 23, 59, 59, 999999)), + ("inf ", datetime(9999, 12, 31, 23, 59, 59, 999999)), + (1e50, datetime(9999, 12, 31, 23, 59, 59, 999999)), + (float("inf"), datetime(9999, 12, 31, 23, 59, 59, 999999)), + ("-infinity", datetime(1, 1, 1, 0, 0)), + ("-inf", datetime(1, 1, 1, 0, 0)), + ("nan", ValueError), + ], +) +def test_datetime_parsing(value: Union[str, bytes, int, float], result: Union[datetime, Type[Exception]]) -> None: + if type(result) == type and issubclass(result, Exception): # pyright: ignore[reportUnnecessaryIsInstance] + with pytest.raises(result): + parse_datetime(value) + else: + assert parse_datetime(value) == result diff --git a/tests/test_utils/test_proxy.py b/tests/test_utils/test_proxy.py new file mode 100644 index 0000000..97516db --- /dev/null +++ b/tests/test_utils/test_proxy.py @@ -0,0 +1,34 @@ +import operator +from typing import Any +from typing_extensions import override + +from unlayer._utils import LazyProxy + + +class RecursiveLazyProxy(LazyProxy[Any]): + @override + def __load__(self) -> Any: + return self + + def __call__(self, *_args: Any, **_kwds: Any) -> Any: + raise RuntimeError("This should never be called!") + + +def test_recursive_proxy() -> None: + proxy = RecursiveLazyProxy() + assert repr(proxy) == "RecursiveLazyProxy" + assert str(proxy) == "RecursiveLazyProxy" + assert dir(proxy) == [] + assert type(proxy).__name__ == "RecursiveLazyProxy" + assert type(operator.attrgetter("name.foo.bar.baz")(proxy)).__name__ == "RecursiveLazyProxy" + + +def test_isinstance_does_not_error() -> None: + class AlwaysErrorProxy(LazyProxy[Any]): + @override + def __load__(self) -> Any: + raise RuntimeError("Mocking missing dependency") + + proxy = AlwaysErrorProxy() + assert not isinstance(proxy, dict) + assert isinstance(proxy, LazyProxy) diff --git a/tests/test_utils/test_typing.py b/tests/test_utils/test_typing.py new file mode 100644 index 0000000..d9bb9ef --- /dev/null +++ b/tests/test_utils/test_typing.py @@ -0,0 +1,73 @@ +from __future__ import annotations + +from typing import Generic, TypeVar, cast + +from unlayer._utils import extract_type_var_from_base + +_T = TypeVar("_T") +_T2 = TypeVar("_T2") +_T3 = TypeVar("_T3") + + +class BaseGeneric(Generic[_T]): ... + + +class SubclassGeneric(BaseGeneric[_T]): ... + + +class BaseGenericMultipleTypeArgs(Generic[_T, _T2, _T3]): ... + + +class SubclassGenericMultipleTypeArgs(BaseGenericMultipleTypeArgs[_T, _T2, _T3]): ... + + +class SubclassDifferentOrderGenericMultipleTypeArgs(BaseGenericMultipleTypeArgs[_T2, _T, _T3]): ... + + +def test_extract_type_var() -> None: + assert ( + extract_type_var_from_base( + BaseGeneric[int], + index=0, + generic_bases=cast("tuple[type, ...]", (BaseGeneric,)), + ) + == int + ) + + +def test_extract_type_var_generic_subclass() -> None: + assert ( + extract_type_var_from_base( + SubclassGeneric[int], + index=0, + generic_bases=cast("tuple[type, ...]", (BaseGeneric,)), + ) + == int + ) + + +def test_extract_type_var_multiple() -> None: + typ = BaseGenericMultipleTypeArgs[int, str, None] + + generic_bases = cast("tuple[type, ...]", (BaseGenericMultipleTypeArgs,)) + assert extract_type_var_from_base(typ, index=0, generic_bases=generic_bases) == int + assert extract_type_var_from_base(typ, index=1, generic_bases=generic_bases) == str + assert extract_type_var_from_base(typ, index=2, generic_bases=generic_bases) == type(None) + + +def test_extract_type_var_generic_subclass_multiple() -> None: + typ = SubclassGenericMultipleTypeArgs[int, str, None] + + generic_bases = cast("tuple[type, ...]", (BaseGenericMultipleTypeArgs,)) + assert extract_type_var_from_base(typ, index=0, generic_bases=generic_bases) == int + assert extract_type_var_from_base(typ, index=1, generic_bases=generic_bases) == str + assert extract_type_var_from_base(typ, index=2, generic_bases=generic_bases) == type(None) + + +def test_extract_type_var_generic_subclass_different_ordering_multiple() -> None: + typ = SubclassDifferentOrderGenericMultipleTypeArgs[int, str, None] + + generic_bases = cast("tuple[type, ...]", (BaseGenericMultipleTypeArgs,)) + assert extract_type_var_from_base(typ, index=0, generic_bases=generic_bases) == int + assert extract_type_var_from_base(typ, index=1, generic_bases=generic_bases) == str + assert extract_type_var_from_base(typ, index=2, generic_bases=generic_bases) == type(None) diff --git a/tests/utils.py b/tests/utils.py new file mode 100644 index 0000000..3218723 --- /dev/null +++ b/tests/utils.py @@ -0,0 +1,167 @@ +from __future__ import annotations + +import os +import inspect +import traceback +import contextlib +from typing import Any, TypeVar, Iterator, Sequence, cast +from datetime import date, datetime +from typing_extensions import Literal, get_args, get_origin, assert_type + +from unlayer._types import Omit, NoneType +from unlayer._utils import ( + is_dict, + is_list, + is_list_type, + is_union_type, + extract_type_arg, + is_sequence_type, + is_annotated_type, + is_type_alias_type, +) +from unlayer._compat import PYDANTIC_V1, field_outer_type, get_model_fields +from unlayer._models import BaseModel + +BaseModelT = TypeVar("BaseModelT", bound=BaseModel) + + +def assert_matches_model(model: type[BaseModelT], value: BaseModelT, *, path: list[str]) -> bool: + for name, field in get_model_fields(model).items(): + field_value = getattr(value, name) + if PYDANTIC_V1: + # in v1 nullability was structured differently + # https://docs.pydantic.dev/2.0/migration/#required-optional-and-nullable-fields + allow_none = getattr(field, "allow_none", False) + else: + allow_none = False + + assert_matches_type( + field_outer_type(field), + field_value, + path=[*path, name], + allow_none=allow_none, + ) + + return True + + +# Note: the `path` argument is only used to improve error messages when `--showlocals` is used +def assert_matches_type( + type_: Any, + value: object, + *, + path: list[str], + allow_none: bool = False, +) -> None: + if is_type_alias_type(type_): + type_ = type_.__value__ + + # unwrap `Annotated[T, ...]` -> `T` + if is_annotated_type(type_): + type_ = extract_type_arg(type_, 0) + + if allow_none and value is None: + return + + if type_ is None or type_ is NoneType: + assert value is None + return + + origin = get_origin(type_) or type_ + + if is_list_type(type_): + return _assert_list_type(type_, value) + + if is_sequence_type(type_): + assert isinstance(value, Sequence) + inner_type = get_args(type_)[0] + for entry in value: # type: ignore + assert_type(inner_type, entry) # type: ignore + return + + if origin == str: + assert isinstance(value, str) + elif origin == int: + assert isinstance(value, int) + elif origin == bool: + assert isinstance(value, bool) + elif origin == float: + assert isinstance(value, float) + elif origin == bytes: + assert isinstance(value, bytes) + elif origin == datetime: + assert isinstance(value, datetime) + elif origin == date: + assert isinstance(value, date) + elif origin == object: + # nothing to do here, the expected type is unknown + pass + elif origin == Literal: + assert value in get_args(type_) + elif origin == dict: + assert is_dict(value) + + args = get_args(type_) + key_type = args[0] + items_type = args[1] + + for key, item in value.items(): + assert_matches_type(key_type, key, path=[*path, ""]) + assert_matches_type(items_type, item, path=[*path, ""]) + elif is_union_type(type_): + variants = get_args(type_) + + try: + none_index = variants.index(type(None)) + except ValueError: + pass + else: + # special case Optional[T] for better error messages + if len(variants) == 2: + if value is None: + # valid + return + + return assert_matches_type(type_=variants[not none_index], value=value, path=path) + + for i, variant in enumerate(variants): + try: + assert_matches_type(variant, value, path=[*path, f"variant {i}"]) + return + except AssertionError: + traceback.print_exc() + continue + + raise AssertionError("Did not match any variants") + elif issubclass(origin, BaseModel): + assert isinstance(value, type_) + assert assert_matches_model(type_, cast(Any, value), path=path) + elif inspect.isclass(origin) and origin.__name__ == "HttpxBinaryResponseContent": + assert value.__class__.__name__ == "HttpxBinaryResponseContent" + else: + assert None, f"Unhandled field type: {type_}" + + +def _assert_list_type(type_: type[object], value: object) -> None: + assert is_list(value) + + inner_type = get_args(type_)[0] + for entry in value: + assert_type(inner_type, entry) # type: ignore + + +@contextlib.contextmanager +def update_env(**new_env: str | Omit) -> Iterator[None]: + old = os.environ.copy() + + try: + for name, value in new_env.items(): + if isinstance(value, Omit): + os.environ.pop(name, None) + else: + os.environ[name] = value + + yield None + finally: + os.environ.clear() + os.environ.update(old) From 50948dcf0105c466bab83fdf3243d82535b20bf5 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Wed, 10 Dec 2025 10:25:51 +0000 Subject: [PATCH 02/62] feat(api): api update --- .stats.yml | 6 +-- api.md | 80 +++++++++++++++---------------- src/unlayer/_client.py | 32 ++++++------- src/unlayer/resources/__init__.py | 24 +++++----- 4 files changed, 71 insertions(+), 71 deletions(-) diff --git a/.stats.yml b/.stats.yml index 4b2f990..2a30352 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 24 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/unlayer%2Funlayer-36e6b164f4fc145ef8b0068290d8ffa479dbfadb8fe11263d6ccd49ea0dd4a22.yml -openapi_spec_hash: b4022090026a2d0b27e2c2c19e6581b2 -config_hash: 256da0d3adcdb5fa292234ddd8247f58 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/unlayer%2Funlayer-e7d9e7f911c6b5d98e662c31862450cb0519a86c051c80d6eaa47cdebfe86fb4.yml +openapi_spec_hash: e2a7835f5a7b7c5a6b6bd7c3a4786af3 +config_hash: a4f34863030203b80da5a78b0d4ea416 diff --git a/api.md b/api.md index 13c618d..fcf4ede 100644 --- a/api.md +++ b/api.md @@ -1,3 +1,43 @@ +# EmailsV1 + +Types: + +```python +from unlayer.types import ( + EmailsV1EmailsRetrieveResponse, + EmailsV1RenderCreateResponse, + EmailsV1SendCreateResponse, + EmailsV1SendTemplateTemplateResponse, +) +``` + +Methods: + +- client.emails_v1.emails_retrieve(id) -> EmailsV1EmailsRetrieveResponse +- client.emails_v1.render_create(\*\*params) -> EmailsV1RenderCreateResponse +- client.emails_v1.send_create(\*\*params) -> EmailsV1SendCreateResponse +- client.emails_v1.send_template_template(\*\*params) -> EmailsV1SendTemplateTemplateResponse + +# Emails + +Types: + +```python +from unlayer.types import ( + EmailEmailsRetrieveResponse, + EmailRenderCreateResponse, + EmailSendCreateResponse, + EmailSendTemplateTemplateResponse, +) +``` + +Methods: + +- client.emails.emails_retrieve(id) -> EmailEmailsRetrieveResponse +- client.emails.render_create(\*\*params) -> EmailRenderCreateResponse +- client.emails.send_create(\*\*params) -> EmailSendCreateResponse +- client.emails.send_template_template(\*\*params) -> EmailSendTemplateTemplateResponse + # ProjectV1 Types: @@ -80,46 +120,6 @@ Methods: - client.project.templates_retrieve(id) -> ProjectTemplatesRetrieveResponse - client.project.templates_update(id, \*\*params) -> ProjectTemplatesUpdateResponse -# EmailsV1 - -Types: - -```python -from unlayer.types import ( - EmailsV1EmailsRetrieveResponse, - EmailsV1RenderCreateResponse, - EmailsV1SendCreateResponse, - EmailsV1SendTemplateTemplateResponse, -) -``` - -Methods: - -- client.emails_v1.emails_retrieve(id) -> EmailsV1EmailsRetrieveResponse -- client.emails_v1.render_create(\*\*params) -> EmailsV1RenderCreateResponse -- client.emails_v1.send_create(\*\*params) -> EmailsV1SendCreateResponse -- client.emails_v1.send_template_template(\*\*params) -> EmailsV1SendTemplateTemplateResponse - -# Emails - -Types: - -```python -from unlayer.types import ( - EmailEmailsRetrieveResponse, - EmailRenderCreateResponse, - EmailSendCreateResponse, - EmailSendTemplateTemplateResponse, -) -``` - -Methods: - -- client.emails.emails_retrieve(id) -> EmailEmailsRetrieveResponse -- client.emails.render_create(\*\*params) -> EmailRenderCreateResponse -- client.emails.send_create(\*\*params) -> EmailSendCreateResponse -- client.emails.send_template_template(\*\*params) -> EmailSendTemplateTemplateResponse - # DocumentsV1 Types: diff --git a/src/unlayer/_client.py b/src/unlayer/_client.py index 94b3089..1528e7a 100644 --- a/src/unlayer/_client.py +++ b/src/unlayer/_client.py @@ -34,10 +34,10 @@ class Unlayer(SyncAPIClient): - project_v1: project_v1.ProjectV1Resource - project: project.ProjectResource emails_v1: emails_v1.EmailsV1Resource emails: emails.EmailsResource + project_v1: project_v1.ProjectV1Resource + project: project.ProjectResource documents_v1: documents_v1.DocumentsV1Resource documents: documents.DocumentsResource pages_v1: pages_v1.PagesV1Resource @@ -99,10 +99,10 @@ def __init__( _strict_response_validation=_strict_response_validation, ) - self.project_v1 = project_v1.ProjectV1Resource(self) - self.project = project.ProjectResource(self) self.emails_v1 = emails_v1.EmailsV1Resource(self) self.emails = emails.EmailsResource(self) + self.project_v1 = project_v1.ProjectV1Resource(self) + self.project = project.ProjectResource(self) self.documents_v1 = documents_v1.DocumentsV1Resource(self) self.documents = documents.DocumentsResource(self) self.pages_v1 = pages_v1.PagesV1Resource(self) @@ -216,10 +216,10 @@ def _make_status_error( class AsyncUnlayer(AsyncAPIClient): - project_v1: project_v1.AsyncProjectV1Resource - project: project.AsyncProjectResource emails_v1: emails_v1.AsyncEmailsV1Resource emails: emails.AsyncEmailsResource + project_v1: project_v1.AsyncProjectV1Resource + project: project.AsyncProjectResource documents_v1: documents_v1.AsyncDocumentsV1Resource documents: documents.AsyncDocumentsResource pages_v1: pages_v1.AsyncPagesV1Resource @@ -281,10 +281,10 @@ def __init__( _strict_response_validation=_strict_response_validation, ) - self.project_v1 = project_v1.AsyncProjectV1Resource(self) - self.project = project.AsyncProjectResource(self) self.emails_v1 = emails_v1.AsyncEmailsV1Resource(self) self.emails = emails.AsyncEmailsResource(self) + self.project_v1 = project_v1.AsyncProjectV1Resource(self) + self.project = project.AsyncProjectResource(self) self.documents_v1 = documents_v1.AsyncDocumentsV1Resource(self) self.documents = documents.AsyncDocumentsResource(self) self.pages_v1 = pages_v1.AsyncPagesV1Resource(self) @@ -399,10 +399,10 @@ def _make_status_error( class UnlayerWithRawResponse: def __init__(self, client: Unlayer) -> None: - self.project_v1 = project_v1.ProjectV1ResourceWithRawResponse(client.project_v1) - self.project = project.ProjectResourceWithRawResponse(client.project) self.emails_v1 = emails_v1.EmailsV1ResourceWithRawResponse(client.emails_v1) self.emails = emails.EmailsResourceWithRawResponse(client.emails) + self.project_v1 = project_v1.ProjectV1ResourceWithRawResponse(client.project_v1) + self.project = project.ProjectResourceWithRawResponse(client.project) self.documents_v1 = documents_v1.DocumentsV1ResourceWithRawResponse(client.documents_v1) self.documents = documents.DocumentsResourceWithRawResponse(client.documents) self.pages_v1 = pages_v1.PagesV1ResourceWithRawResponse(client.pages_v1) @@ -411,10 +411,10 @@ def __init__(self, client: Unlayer) -> None: class AsyncUnlayerWithRawResponse: def __init__(self, client: AsyncUnlayer) -> None: - self.project_v1 = project_v1.AsyncProjectV1ResourceWithRawResponse(client.project_v1) - self.project = project.AsyncProjectResourceWithRawResponse(client.project) self.emails_v1 = emails_v1.AsyncEmailsV1ResourceWithRawResponse(client.emails_v1) self.emails = emails.AsyncEmailsResourceWithRawResponse(client.emails) + self.project_v1 = project_v1.AsyncProjectV1ResourceWithRawResponse(client.project_v1) + self.project = project.AsyncProjectResourceWithRawResponse(client.project) self.documents_v1 = documents_v1.AsyncDocumentsV1ResourceWithRawResponse(client.documents_v1) self.documents = documents.AsyncDocumentsResourceWithRawResponse(client.documents) self.pages_v1 = pages_v1.AsyncPagesV1ResourceWithRawResponse(client.pages_v1) @@ -423,10 +423,10 @@ def __init__(self, client: AsyncUnlayer) -> None: class UnlayerWithStreamedResponse: def __init__(self, client: Unlayer) -> None: - self.project_v1 = project_v1.ProjectV1ResourceWithStreamingResponse(client.project_v1) - self.project = project.ProjectResourceWithStreamingResponse(client.project) self.emails_v1 = emails_v1.EmailsV1ResourceWithStreamingResponse(client.emails_v1) self.emails = emails.EmailsResourceWithStreamingResponse(client.emails) + self.project_v1 = project_v1.ProjectV1ResourceWithStreamingResponse(client.project_v1) + self.project = project.ProjectResourceWithStreamingResponse(client.project) self.documents_v1 = documents_v1.DocumentsV1ResourceWithStreamingResponse(client.documents_v1) self.documents = documents.DocumentsResourceWithStreamingResponse(client.documents) self.pages_v1 = pages_v1.PagesV1ResourceWithStreamingResponse(client.pages_v1) @@ -435,10 +435,10 @@ def __init__(self, client: Unlayer) -> None: class AsyncUnlayerWithStreamedResponse: def __init__(self, client: AsyncUnlayer) -> None: - self.project_v1 = project_v1.AsyncProjectV1ResourceWithStreamingResponse(client.project_v1) - self.project = project.AsyncProjectResourceWithStreamingResponse(client.project) self.emails_v1 = emails_v1.AsyncEmailsV1ResourceWithStreamingResponse(client.emails_v1) self.emails = emails.AsyncEmailsResourceWithStreamingResponse(client.emails) + self.project_v1 = project_v1.AsyncProjectV1ResourceWithStreamingResponse(client.project_v1) + self.project = project.AsyncProjectResourceWithStreamingResponse(client.project) self.documents_v1 = documents_v1.AsyncDocumentsV1ResourceWithStreamingResponse(client.documents_v1) self.documents = documents.AsyncDocumentsResourceWithStreamingResponse(client.documents) self.pages_v1 = pages_v1.AsyncPagesV1ResourceWithStreamingResponse(client.pages_v1) diff --git a/src/unlayer/resources/__init__.py b/src/unlayer/resources/__init__.py index 8ddd0c6..a898572 100644 --- a/src/unlayer/resources/__init__.py +++ b/src/unlayer/resources/__init__.py @@ -66,18 +66,6 @@ ) __all__ = [ - "ProjectV1Resource", - "AsyncProjectV1Resource", - "ProjectV1ResourceWithRawResponse", - "AsyncProjectV1ResourceWithRawResponse", - "ProjectV1ResourceWithStreamingResponse", - "AsyncProjectV1ResourceWithStreamingResponse", - "ProjectResource", - "AsyncProjectResource", - "ProjectResourceWithRawResponse", - "AsyncProjectResourceWithRawResponse", - "ProjectResourceWithStreamingResponse", - "AsyncProjectResourceWithStreamingResponse", "EmailsV1Resource", "AsyncEmailsV1Resource", "EmailsV1ResourceWithRawResponse", @@ -90,6 +78,18 @@ "AsyncEmailsResourceWithRawResponse", "EmailsResourceWithStreamingResponse", "AsyncEmailsResourceWithStreamingResponse", + "ProjectV1Resource", + "AsyncProjectV1Resource", + "ProjectV1ResourceWithRawResponse", + "AsyncProjectV1ResourceWithRawResponse", + "ProjectV1ResourceWithStreamingResponse", + "AsyncProjectV1ResourceWithStreamingResponse", + "ProjectResource", + "AsyncProjectResource", + "ProjectResourceWithRawResponse", + "AsyncProjectResourceWithRawResponse", + "ProjectResourceWithStreamingResponse", + "AsyncProjectResourceWithStreamingResponse", "DocumentsV1Resource", "AsyncDocumentsV1Resource", "DocumentsV1ResourceWithRawResponse", From 8fb139f82f12ec4c735c1bc6c8135be4cb1e129b Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Wed, 10 Dec 2025 18:55:49 +0000 Subject: [PATCH 03/62] feat(api): api update --- .stats.yml | 6 +++--- tests/api_resources/test_emails.py | 4 ++-- tests/api_resources/test_emails_v1.py | 4 ++-- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/.stats.yml b/.stats.yml index 2a30352..f155fdc 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 24 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/unlayer%2Funlayer-e7d9e7f911c6b5d98e662c31862450cb0519a86c051c80d6eaa47cdebfe86fb4.yml -openapi_spec_hash: e2a7835f5a7b7c5a6b6bd7c3a4786af3 -config_hash: a4f34863030203b80da5a78b0d4ea416 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/unlayer%2Funlayer-7b157ae796e7fc651848e8626a9adb4b00614a203c5140530edb0c52f5c82f79.yml +openapi_spec_hash: 00ccd8fc07a13e525a6fc1af6445230e +config_hash: 60fe170373c35bb6b1455ffa12ac8dc2 diff --git a/tests/api_resources/test_emails.py b/tests/api_resources/test_emails.py index 4fc6a86..f1747a0 100644 --- a/tests/api_resources/test_emails.py +++ b/tests/api_resources/test_emails.py @@ -132,7 +132,7 @@ def test_method_send_create_with_all_params(self, client: Unlayer) -> None: to="test@example.com", html="html", merge_tags={"foo": "string"}, - subject="Test Email", + subject="Test", ) assert_matches_type(EmailSendCreateResponse, email, path=["response"]) @@ -328,7 +328,7 @@ async def test_method_send_create_with_all_params(self, async_client: AsyncUnlay to="test@example.com", html="html", merge_tags={"foo": "string"}, - subject="Test Email", + subject="Test", ) assert_matches_type(EmailSendCreateResponse, email, path=["response"]) diff --git a/tests/api_resources/test_emails_v1.py b/tests/api_resources/test_emails_v1.py index d728ca3..8a5f5fe 100644 --- a/tests/api_resources/test_emails_v1.py +++ b/tests/api_resources/test_emails_v1.py @@ -132,7 +132,7 @@ def test_method_send_create_with_all_params(self, client: Unlayer) -> None: to="test@example.com", html="html", merge_tags={"foo": "string"}, - subject="Test Email", + subject="Test", ) assert_matches_type(EmailsV1SendCreateResponse, emails_v1, path=["response"]) @@ -328,7 +328,7 @@ async def test_method_send_create_with_all_params(self, async_client: AsyncUnlay to="test@example.com", html="html", merge_tags={"foo": "string"}, - subject="Test Email", + subject="Test", ) assert_matches_type(EmailsV1SendCreateResponse, emails_v1, path=["response"]) From 05cf54b079080a1a0766ddf74799480c27d96b37 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Wed, 10 Dec 2025 19:06:21 +0000 Subject: [PATCH 04/62] feat(api): api update --- .stats.yml | 6 +- README.md | 20 +- api.md | 206 +++--- src/unlayer/_client.py | 53 +- src/unlayer/resources/__init__.py | 68 +- src/unlayer/resources/documents/__init__.py | 33 + .../resources/{ => documents}/documents.py | 52 +- .../{documents_v1.py => documents/v1.py} | 122 ++-- src/unlayer/resources/emails/__init__.py | 33 + src/unlayer/resources/{ => emails}/emails.py | 54 +- .../resources/{emails_v1.py => emails/v1.py} | 148 ++--- src/unlayer/resources/pages/__init__.py | 33 + src/unlayer/resources/{ => pages}/pages.py | 48 +- .../resources/{pages_v1.py => pages/v1.py} | 82 +-- src/unlayer/resources/project/__init__.py | 33 + .../resources/{ => project}/project.py | 72 +- .../{project_v1.py => project/v1.py} | 366 +++++------ src/unlayer/types/__init__.py | 47 -- src/unlayer/types/documents/__init__.py | 11 + .../v1_documents_retrieve_response.py} | 6 +- .../v1_generate_create_params.py} | 6 +- .../v1_generate_create_response.py} | 6 +- .../v1_generate_template_template_params.py} | 6 +- ...v1_generate_template_template_response.py} | 6 +- src/unlayer/types/emails/__init__.py | 11 + .../v1_emails_retrieve_response.py} | 6 +- .../v1_render_create_params.py} | 6 +- .../v1_render_create_response.py} | 6 +- .../v1_send_create_params.py} | 6 +- .../v1_send_create_response.py} | 6 +- .../v1_send_template_template_params.py} | 6 +- .../v1_send_template_template_response.py} | 6 +- src/unlayer/types/pages/__init__.py | 6 + .../v1_render_create_params.py} | 6 +- .../v1_render_create_response.py} | 6 +- src/unlayer/types/project/__init__.py | 23 + .../v1_api_keys_create_params.py} | 6 +- .../v1_api_keys_create_response.py} | 6 +- .../v1_api_keys_list_response.py} | 6 +- .../v1_api_keys_retrieve_response.py} | 6 +- .../v1_api_keys_update_params.py} | 6 +- .../v1_api_keys_update_response.py} | 6 +- .../v1_current_list_response.py} | 6 +- .../v1_domains_create_params.py} | 4 +- .../v1_domains_create_response.py} | 6 +- .../v1_domains_list_response.py} | 6 +- .../v1_domains_retrieve_response.py} | 6 +- .../v1_domains_update_params.py} | 4 +- .../v1_domains_update_response.py} | 6 +- .../v1_templates_create_params.py} | 4 +- .../v1_templates_create_response.py} | 6 +- .../v1_templates_list_response.py} | 6 +- .../v1_templates_retrieve_response.py} | 6 +- .../v1_templates_update_params.py} | 4 +- .../v1_templates_update_response.py} | 6 +- tests/api_resources/documents/__init__.py | 1 + .../test_v1.py} | 128 ++-- tests/api_resources/emails/__init__.py | 1 + .../{test_emails_v1.py => emails/test_v1.py} | 170 ++--- tests/api_resources/pages/__init__.py | 1 + .../{test_pages_v1.py => pages/test_v1.py} | 46 +- tests/api_resources/project/__init__.py | 1 + .../test_v1.py} | 620 +++++++++--------- tests/test_client.py | 22 +- 64 files changed, 1440 insertions(+), 1267 deletions(-) create mode 100644 src/unlayer/resources/documents/__init__.py rename src/unlayer/resources/{ => documents}/documents.py (89%) rename src/unlayer/resources/{documents_v1.py => documents/v1.py} (76%) create mode 100644 src/unlayer/resources/emails/__init__.py rename src/unlayer/resources/{ => emails}/emails.py (91%) rename src/unlayer/resources/{emails_v1.py => emails/v1.py} (79%) create mode 100644 src/unlayer/resources/pages/__init__.py rename src/unlayer/resources/{ => pages}/pages.py (81%) rename src/unlayer/resources/{pages_v1.py => pages/v1.py} (69%) create mode 100644 src/unlayer/resources/project/__init__.py rename src/unlayer/resources/{ => project}/project.py (95%) rename src/unlayer/resources/{project_v1.py => project/v1.py} (82%) create mode 100644 src/unlayer/types/documents/__init__.py rename src/unlayer/types/{documents_v1_documents_retrieve_response.py => documents/v1_documents_retrieve_response.py} (88%) rename src/unlayer/types/{documents_v1_generate_create_params.py => documents/v1_generate_create_params.py} (80%) rename src/unlayer/types/{documents_v1_generate_create_response.py => documents/v1_generate_create_response.py} (81%) rename src/unlayer/types/{documents_v1_generate_template_template_params.py => documents/v1_generate_template_template_params.py} (76%) rename src/unlayer/types/{documents_v1_generate_template_template_response.py => documents/v1_generate_template_template_response.py} (78%) create mode 100644 src/unlayer/types/emails/__init__.py rename src/unlayer/types/{emails_v1_emails_retrieve_response.py => emails/v1_emails_retrieve_response.py} (85%) rename src/unlayer/types/{pages_v1_render_create_params.py => emails/v1_render_create_params.py} (76%) rename src/unlayer/types/{pages_v1_render_create_response.py => emails/v1_render_create_response.py} (60%) rename src/unlayer/types/{emails_v1_send_create_params.py => emails/v1_send_create_params.py} (81%) rename src/unlayer/types/{emails_v1_send_create_response.py => emails/v1_send_create_response.py} (76%) rename src/unlayer/types/{emails_v1_send_template_template_params.py => emails/v1_send_template_template_params.py} (80%) rename src/unlayer/types/{emails_v1_send_template_template_response.py => emails/v1_send_template_template_response.py} (73%) create mode 100644 src/unlayer/types/pages/__init__.py rename src/unlayer/types/{emails_v1_render_create_params.py => pages/v1_render_create_params.py} (75%) rename src/unlayer/types/{emails_v1_render_create_response.py => pages/v1_render_create_response.py} (59%) create mode 100644 src/unlayer/types/project/__init__.py rename src/unlayer/types/{project_v1_api_keys_create_params.py => project/v1_api_keys_create_params.py} (68%) rename src/unlayer/types/{project_v1_api_keys_create_response.py => project/v1_api_keys_create_response.py} (78%) rename src/unlayer/types/{project_v1_api_keys_list_response.py => project/v1_api_keys_list_response.py} (81%) rename src/unlayer/types/{project_v1_api_keys_update_response.py => project/v1_api_keys_retrieve_response.py} (81%) rename src/unlayer/types/{project_v1_api_keys_update_params.py => project/v1_api_keys_update_params.py} (71%) rename src/unlayer/types/{project_v1_api_keys_retrieve_response.py => project/v1_api_keys_update_response.py} (80%) rename src/unlayer/types/{project_v1_current_list_response.py => project/v1_current_list_response.py} (79%) rename src/unlayer/types/{project_v1_domains_create_params.py => project/v1_domains_create_params.py} (69%) rename src/unlayer/types/{project_v1_domains_update_response.py => project/v1_domains_create_response.py} (77%) rename src/unlayer/types/{project_v1_domains_list_response.py => project/v1_domains_list_response.py} (80%) rename src/unlayer/types/{project_v1_domains_create_response.py => project/v1_domains_retrieve_response.py} (77%) rename src/unlayer/types/{project_v1_domains_update_params.py => project/v1_domains_update_params.py} (67%) rename src/unlayer/types/{project_v1_domains_retrieve_response.py => project/v1_domains_update_response.py} (77%) rename src/unlayer/types/{project_v1_templates_create_params.py => project/v1_templates_create_params.py} (74%) rename src/unlayer/types/{project_v1_templates_create_response.py => project/v1_templates_create_response.py} (79%) rename src/unlayer/types/{project_v1_templates_list_response.py => project/v1_templates_list_response.py} (80%) rename src/unlayer/types/{project_v1_templates_update_response.py => project/v1_templates_retrieve_response.py} (79%) rename src/unlayer/types/{project_v1_templates_update_params.py => project/v1_templates_update_params.py} (74%) rename src/unlayer/types/{project_v1_templates_retrieve_response.py => project/v1_templates_update_response.py} (79%) create mode 100644 tests/api_resources/documents/__init__.py rename tests/api_resources/{test_documents_v1.py => documents/test_v1.py} (62%) create mode 100644 tests/api_resources/emails/__init__.py rename tests/api_resources/{test_emails_v1.py => emails/test_v1.py} (64%) create mode 100644 tests/api_resources/pages/__init__.py rename tests/api_resources/{test_pages_v1.py => pages/test_v1.py} (68%) create mode 100644 tests/api_resources/project/__init__.py rename tests/api_resources/{test_project_v1.py => project/test_v1.py} (60%) diff --git a/.stats.yml b/.stats.yml index f155fdc..b7a5fc0 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 24 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/unlayer%2Funlayer-7b157ae796e7fc651848e8626a9adb4b00614a203c5140530edb0c52f5c82f79.yml -openapi_spec_hash: 00ccd8fc07a13e525a6fc1af6445230e -config_hash: 60fe170373c35bb6b1455ffa12ac8dc2 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/unlayer%2Funlayer-d746e93c3c920dca97596edec38c9ec25feef644db419156ae1b538bb54b6d72.yml +openapi_spec_hash: 437dce81b84c463ef9cc84dafa2ca92a +config_hash: 355cc7936ae5249f192357f93dab04c8 diff --git a/README.md b/README.md index 8c44d3b..74e8e82 100644 --- a/README.md +++ b/README.md @@ -35,7 +35,7 @@ client = Unlayer( api_key=os.environ.get("UNLAYER_API_KEY"), # This is the default and can be omitted ) -response = client.project_v1.current_list() +response = client.project.current_list() print(response.data) ``` @@ -59,7 +59,7 @@ client = AsyncUnlayer( async def main() -> None: - response = await client.project_v1.current_list() + response = await client.project.current_list() print(response.data) @@ -93,7 +93,7 @@ async def main() -> None: api_key=os.environ.get("UNLAYER_API_KEY"), # This is the default and can be omitted http_client=DefaultAioHttpClient(), ) as client: - response = await client.project_v1.current_list() + response = await client.project.current_list() print(response.data) @@ -125,7 +125,7 @@ from unlayer import Unlayer client = Unlayer() try: - client.project_v1.current_list() + client.project.current_list() except unlayer.APIConnectionError as e: print("The server could not be reached") print(e.__cause__) # an underlying Exception, likely raised within httpx. @@ -168,7 +168,7 @@ client = Unlayer( ) # Or, configure per-request: -client.with_options(max_retries=5).project_v1.current_list() +client.with_options(max_retries=5).project.current_list() ``` ### Timeouts @@ -191,7 +191,7 @@ client = Unlayer( ) # Override per-request: -client.with_options(timeout=5.0).project_v1.current_list() +client.with_options(timeout=5.0).project.current_list() ``` On timeout, an `APITimeoutError` is thrown. @@ -232,11 +232,11 @@ The "raw" Response object can be accessed by prefixing `.with_raw_response.` to from unlayer import Unlayer client = Unlayer() -response = client.project_v1.with_raw_response.current_list() +response = client.project.with_raw_response.current_list() print(response.headers.get('X-My-Header')) -project_v1 = response.parse() # get the object that `project_v1.current_list()` would have returned -print(project_v1.data) +project = response.parse() # get the object that `project.current_list()` would have returned +print(project.data) ``` These methods return an [`APIResponse`](https://github.com/stainless-sdks/unlayer-python/tree/main/src/unlayer/_response.py) object. @@ -250,7 +250,7 @@ The above interface eagerly reads the full response body when you make the reque To stream the response body, use `.with_streaming_response` instead, which requires a context manager and only reads the response body once you call `.read()`, `.text()`, `.json()`, `.iter_bytes()`, `.iter_text()`, `.iter_lines()` or `.parse()`. In the async client, these are async methods. ```python -with client.project_v1.with_streaming_response.current_list() as response: +with client.project.with_streaming_response.current_list() as response: print(response.headers.get("X-My-Header")) for line in response.iter_lines(): diff --git a/api.md b/api.md index fcf4ede..98b788a 100644 --- a/api.md +++ b/api.md @@ -1,181 +1,181 @@ -# EmailsV1 +# Project Types: ```python from unlayer.types import ( - EmailsV1EmailsRetrieveResponse, - EmailsV1RenderCreateResponse, - EmailsV1SendCreateResponse, - EmailsV1SendTemplateTemplateResponse, + ProjectAPIKeysCreateResponse, + ProjectAPIKeysListResponse, + ProjectAPIKeysRetrieveResponse, + ProjectAPIKeysUpdateResponse, + ProjectCurrentListResponse, + ProjectDomainsCreateResponse, + ProjectDomainsListResponse, + ProjectDomainsRetrieveResponse, + ProjectDomainsUpdateResponse, + ProjectTemplatesCreateResponse, + ProjectTemplatesListResponse, + ProjectTemplatesRetrieveResponse, + ProjectTemplatesUpdateResponse, ) ``` Methods: -- client.emails_v1.emails_retrieve(id) -> EmailsV1EmailsRetrieveResponse -- client.emails_v1.render_create(\*\*params) -> EmailsV1RenderCreateResponse -- client.emails_v1.send_create(\*\*params) -> EmailsV1SendCreateResponse -- client.emails_v1.send_template_template(\*\*params) -> EmailsV1SendTemplateTemplateResponse - -# Emails +- client.project.api_keys_create(\*\*params) -> ProjectAPIKeysCreateResponse +- client.project.api_keys_delete(id) -> None +- client.project.api_keys_list() -> ProjectAPIKeysListResponse +- client.project.api_keys_retrieve(id) -> ProjectAPIKeysRetrieveResponse +- client.project.api_keys_update(id, \*\*params) -> ProjectAPIKeysUpdateResponse +- client.project.current_list() -> ProjectCurrentListResponse +- client.project.domains_create(\*\*params) -> ProjectDomainsCreateResponse +- client.project.domains_delete(id) -> None +- client.project.domains_list() -> ProjectDomainsListResponse +- client.project.domains_retrieve(id) -> ProjectDomainsRetrieveResponse +- client.project.domains_update(id, \*\*params) -> ProjectDomainsUpdateResponse +- client.project.templates_create(\*\*params) -> ProjectTemplatesCreateResponse +- client.project.templates_delete(id) -> None +- client.project.templates_list() -> ProjectTemplatesListResponse +- client.project.templates_retrieve(id) -> ProjectTemplatesRetrieveResponse +- client.project.templates_update(id, \*\*params) -> ProjectTemplatesUpdateResponse + +## V1 Types: ```python -from unlayer.types import ( - EmailEmailsRetrieveResponse, - EmailRenderCreateResponse, - EmailSendCreateResponse, - EmailSendTemplateTemplateResponse, +from unlayer.types.project import ( + V1APIKeysCreateResponse, + V1APIKeysListResponse, + V1APIKeysRetrieveResponse, + V1APIKeysUpdateResponse, + V1CurrentListResponse, + V1DomainsCreateResponse, + V1DomainsListResponse, + V1DomainsRetrieveResponse, + V1DomainsUpdateResponse, + V1TemplatesCreateResponse, + V1TemplatesListResponse, + V1TemplatesRetrieveResponse, + V1TemplatesUpdateResponse, ) ``` Methods: -- client.emails.emails_retrieve(id) -> EmailEmailsRetrieveResponse -- client.emails.render_create(\*\*params) -> EmailRenderCreateResponse -- client.emails.send_create(\*\*params) -> EmailSendCreateResponse -- client.emails.send_template_template(\*\*params) -> EmailSendTemplateTemplateResponse +- client.project.v1.api_keys_create(\*\*params) -> V1APIKeysCreateResponse +- client.project.v1.api_keys_delete(id) -> None +- client.project.v1.api_keys_list() -> V1APIKeysListResponse +- client.project.v1.api_keys_retrieve(id) -> V1APIKeysRetrieveResponse +- client.project.v1.api_keys_update(id, \*\*params) -> V1APIKeysUpdateResponse +- client.project.v1.current_list() -> V1CurrentListResponse +- client.project.v1.domains_create(\*\*params) -> V1DomainsCreateResponse +- client.project.v1.domains_delete(id) -> None +- client.project.v1.domains_list() -> V1DomainsListResponse +- client.project.v1.domains_retrieve(id) -> V1DomainsRetrieveResponse +- client.project.v1.domains_update(id, \*\*params) -> V1DomainsUpdateResponse +- client.project.v1.templates_create(\*\*params) -> V1TemplatesCreateResponse +- client.project.v1.templates_delete(id) -> None +- client.project.v1.templates_list() -> V1TemplatesListResponse +- client.project.v1.templates_retrieve(id) -> V1TemplatesRetrieveResponse +- client.project.v1.templates_update(id, \*\*params) -> V1TemplatesUpdateResponse -# ProjectV1 +# Emails Types: ```python from unlayer.types import ( - ProjectV1APIKeysCreateResponse, - ProjectV1APIKeysListResponse, - ProjectV1APIKeysRetrieveResponse, - ProjectV1APIKeysUpdateResponse, - ProjectV1CurrentListResponse, - ProjectV1DomainsCreateResponse, - ProjectV1DomainsListResponse, - ProjectV1DomainsRetrieveResponse, - ProjectV1DomainsUpdateResponse, - ProjectV1TemplatesCreateResponse, - ProjectV1TemplatesListResponse, - ProjectV1TemplatesRetrieveResponse, - ProjectV1TemplatesUpdateResponse, + EmailEmailsRetrieveResponse, + EmailRenderCreateResponse, + EmailSendCreateResponse, + EmailSendTemplateTemplateResponse, ) ``` Methods: -- client.project_v1.api_keys_create(\*\*params) -> ProjectV1APIKeysCreateResponse -- client.project_v1.api_keys_delete(id) -> None -- client.project_v1.api_keys_list() -> ProjectV1APIKeysListResponse -- client.project_v1.api_keys_retrieve(id) -> ProjectV1APIKeysRetrieveResponse -- client.project_v1.api_keys_update(id, \*\*params) -> ProjectV1APIKeysUpdateResponse -- client.project_v1.current_list() -> ProjectV1CurrentListResponse -- client.project_v1.domains_create(\*\*params) -> ProjectV1DomainsCreateResponse -- client.project_v1.domains_delete(id) -> None -- client.project_v1.domains_list() -> ProjectV1DomainsListResponse -- client.project_v1.domains_retrieve(id) -> ProjectV1DomainsRetrieveResponse -- client.project_v1.domains_update(id, \*\*params) -> ProjectV1DomainsUpdateResponse -- client.project_v1.templates_create(\*\*params) -> ProjectV1TemplatesCreateResponse -- client.project_v1.templates_delete(id) -> None -- client.project_v1.templates_list() -> ProjectV1TemplatesListResponse -- client.project_v1.templates_retrieve(id) -> ProjectV1TemplatesRetrieveResponse -- client.project_v1.templates_update(id, \*\*params) -> ProjectV1TemplatesUpdateResponse +- client.emails.emails_retrieve(id) -> EmailEmailsRetrieveResponse +- client.emails.render_create(\*\*params) -> EmailRenderCreateResponse +- client.emails.send_create(\*\*params) -> EmailSendCreateResponse +- client.emails.send_template_template(\*\*params) -> EmailSendTemplateTemplateResponse -# Project +## V1 Types: ```python -from unlayer.types import ( - ProjectAPIKeysCreateResponse, - ProjectAPIKeysListResponse, - ProjectAPIKeysRetrieveResponse, - ProjectAPIKeysUpdateResponse, - ProjectCurrentListResponse, - ProjectDomainsCreateResponse, - ProjectDomainsListResponse, - ProjectDomainsRetrieveResponse, - ProjectDomainsUpdateResponse, - ProjectTemplatesCreateResponse, - ProjectTemplatesListResponse, - ProjectTemplatesRetrieveResponse, - ProjectTemplatesUpdateResponse, +from unlayer.types.emails import ( + V1EmailsRetrieveResponse, + V1RenderCreateResponse, + V1SendCreateResponse, + V1SendTemplateTemplateResponse, ) ``` Methods: -- client.project.api_keys_create(\*\*params) -> ProjectAPIKeysCreateResponse -- client.project.api_keys_delete(id) -> None -- client.project.api_keys_list() -> ProjectAPIKeysListResponse -- client.project.api_keys_retrieve(id) -> ProjectAPIKeysRetrieveResponse -- client.project.api_keys_update(id, \*\*params) -> ProjectAPIKeysUpdateResponse -- client.project.current_list() -> ProjectCurrentListResponse -- client.project.domains_create(\*\*params) -> ProjectDomainsCreateResponse -- client.project.domains_delete(id) -> None -- client.project.domains_list() -> ProjectDomainsListResponse -- client.project.domains_retrieve(id) -> ProjectDomainsRetrieveResponse -- client.project.domains_update(id, \*\*params) -> ProjectDomainsUpdateResponse -- client.project.templates_create(\*\*params) -> ProjectTemplatesCreateResponse -- client.project.templates_delete(id) -> None -- client.project.templates_list() -> ProjectTemplatesListResponse -- client.project.templates_retrieve(id) -> ProjectTemplatesRetrieveResponse -- client.project.templates_update(id, \*\*params) -> ProjectTemplatesUpdateResponse - -# DocumentsV1 +- client.emails.v1.emails_retrieve(id) -> V1EmailsRetrieveResponse +- client.emails.v1.render_create(\*\*params) -> V1RenderCreateResponse +- client.emails.v1.send_create(\*\*params) -> V1SendCreateResponse +- client.emails.v1.send_template_template(\*\*params) -> V1SendTemplateTemplateResponse + +# Documents Types: ```python from unlayer.types import ( - DocumentsV1DocumentsRetrieveResponse, - DocumentsV1GenerateCreateResponse, - DocumentsV1GenerateTemplateTemplateResponse, + DocumentDocumentsRetrieveResponse, + DocumentGenerateCreateResponse, + DocumentGenerateTemplateTemplateResponse, ) ``` Methods: -- client.documents_v1.documents_retrieve(id) -> DocumentsV1DocumentsRetrieveResponse -- client.documents_v1.generate_create(\*\*params) -> DocumentsV1GenerateCreateResponse -- client.documents_v1.generate_template_template(\*\*params) -> DocumentsV1GenerateTemplateTemplateResponse +- client.documents.documents_retrieve(id) -> DocumentDocumentsRetrieveResponse +- client.documents.generate_create(\*\*params) -> DocumentGenerateCreateResponse +- client.documents.generate_template_template(\*\*params) -> DocumentGenerateTemplateTemplateResponse -# Documents +## V1 Types: ```python -from unlayer.types import ( - DocumentDocumentsRetrieveResponse, - DocumentGenerateCreateResponse, - DocumentGenerateTemplateTemplateResponse, +from unlayer.types.documents import ( + V1DocumentsRetrieveResponse, + V1GenerateCreateResponse, + V1GenerateTemplateTemplateResponse, ) ``` Methods: -- client.documents.documents_retrieve(id) -> DocumentDocumentsRetrieveResponse -- client.documents.generate_create(\*\*params) -> DocumentGenerateCreateResponse -- client.documents.generate_template_template(\*\*params) -> DocumentGenerateTemplateTemplateResponse +- client.documents.v1.documents_retrieve(id) -> V1DocumentsRetrieveResponse +- client.documents.v1.generate_create(\*\*params) -> V1GenerateCreateResponse +- client.documents.v1.generate_template_template(\*\*params) -> V1GenerateTemplateTemplateResponse -# PagesV1 +# Pages Types: ```python -from unlayer.types import PagesV1RenderCreateResponse +from unlayer.types import PageRenderCreateResponse ``` Methods: -- client.pages_v1.render_create(\*\*params) -> PagesV1RenderCreateResponse +- client.pages.render_create(\*\*params) -> PageRenderCreateResponse -# Pages +## V1 Types: ```python -from unlayer.types import PageRenderCreateResponse +from unlayer.types.pages import V1RenderCreateResponse ``` Methods: -- client.pages.render_create(\*\*params) -> PageRenderCreateResponse +- client.pages.v1.render_create(\*\*params) -> V1RenderCreateResponse diff --git a/src/unlayer/_client.py b/src/unlayer/_client.py index 1528e7a..28cb1e1 100644 --- a/src/unlayer/_client.py +++ b/src/unlayer/_client.py @@ -21,7 +21,6 @@ ) from ._utils import is_given, get_async_library from ._version import __version__ -from .resources import pages, emails, project, pages_v1, documents, emails_v1, project_v1, documents_v1 from ._streaming import Stream as Stream, AsyncStream as AsyncStream from ._exceptions import UnlayerError, APIStatusError from ._base_client import ( @@ -29,18 +28,18 @@ SyncAPIClient, AsyncAPIClient, ) +from .resources.pages import pages +from .resources.emails import emails +from .resources.project import project +from .resources.documents import documents __all__ = ["Timeout", "Transport", "ProxiesTypes", "RequestOptions", "Unlayer", "AsyncUnlayer", "Client", "AsyncClient"] class Unlayer(SyncAPIClient): - emails_v1: emails_v1.EmailsV1Resource - emails: emails.EmailsResource - project_v1: project_v1.ProjectV1Resource project: project.ProjectResource - documents_v1: documents_v1.DocumentsV1Resource + emails: emails.EmailsResource documents: documents.DocumentsResource - pages_v1: pages_v1.PagesV1Resource pages: pages.PagesResource with_raw_response: UnlayerWithRawResponse with_streaming_response: UnlayerWithStreamedResponse @@ -99,13 +98,9 @@ def __init__( _strict_response_validation=_strict_response_validation, ) - self.emails_v1 = emails_v1.EmailsV1Resource(self) - self.emails = emails.EmailsResource(self) - self.project_v1 = project_v1.ProjectV1Resource(self) self.project = project.ProjectResource(self) - self.documents_v1 = documents_v1.DocumentsV1Resource(self) + self.emails = emails.EmailsResource(self) self.documents = documents.DocumentsResource(self) - self.pages_v1 = pages_v1.PagesV1Resource(self) self.pages = pages.PagesResource(self) self.with_raw_response = UnlayerWithRawResponse(self) self.with_streaming_response = UnlayerWithStreamedResponse(self) @@ -216,13 +211,9 @@ def _make_status_error( class AsyncUnlayer(AsyncAPIClient): - emails_v1: emails_v1.AsyncEmailsV1Resource - emails: emails.AsyncEmailsResource - project_v1: project_v1.AsyncProjectV1Resource project: project.AsyncProjectResource - documents_v1: documents_v1.AsyncDocumentsV1Resource + emails: emails.AsyncEmailsResource documents: documents.AsyncDocumentsResource - pages_v1: pages_v1.AsyncPagesV1Resource pages: pages.AsyncPagesResource with_raw_response: AsyncUnlayerWithRawResponse with_streaming_response: AsyncUnlayerWithStreamedResponse @@ -281,13 +272,9 @@ def __init__( _strict_response_validation=_strict_response_validation, ) - self.emails_v1 = emails_v1.AsyncEmailsV1Resource(self) - self.emails = emails.AsyncEmailsResource(self) - self.project_v1 = project_v1.AsyncProjectV1Resource(self) self.project = project.AsyncProjectResource(self) - self.documents_v1 = documents_v1.AsyncDocumentsV1Resource(self) + self.emails = emails.AsyncEmailsResource(self) self.documents = documents.AsyncDocumentsResource(self) - self.pages_v1 = pages_v1.AsyncPagesV1Resource(self) self.pages = pages.AsyncPagesResource(self) self.with_raw_response = AsyncUnlayerWithRawResponse(self) self.with_streaming_response = AsyncUnlayerWithStreamedResponse(self) @@ -399,49 +386,33 @@ def _make_status_error( class UnlayerWithRawResponse: def __init__(self, client: Unlayer) -> None: - self.emails_v1 = emails_v1.EmailsV1ResourceWithRawResponse(client.emails_v1) - self.emails = emails.EmailsResourceWithRawResponse(client.emails) - self.project_v1 = project_v1.ProjectV1ResourceWithRawResponse(client.project_v1) self.project = project.ProjectResourceWithRawResponse(client.project) - self.documents_v1 = documents_v1.DocumentsV1ResourceWithRawResponse(client.documents_v1) + self.emails = emails.EmailsResourceWithRawResponse(client.emails) self.documents = documents.DocumentsResourceWithRawResponse(client.documents) - self.pages_v1 = pages_v1.PagesV1ResourceWithRawResponse(client.pages_v1) self.pages = pages.PagesResourceWithRawResponse(client.pages) class AsyncUnlayerWithRawResponse: def __init__(self, client: AsyncUnlayer) -> None: - self.emails_v1 = emails_v1.AsyncEmailsV1ResourceWithRawResponse(client.emails_v1) - self.emails = emails.AsyncEmailsResourceWithRawResponse(client.emails) - self.project_v1 = project_v1.AsyncProjectV1ResourceWithRawResponse(client.project_v1) self.project = project.AsyncProjectResourceWithRawResponse(client.project) - self.documents_v1 = documents_v1.AsyncDocumentsV1ResourceWithRawResponse(client.documents_v1) + self.emails = emails.AsyncEmailsResourceWithRawResponse(client.emails) self.documents = documents.AsyncDocumentsResourceWithRawResponse(client.documents) - self.pages_v1 = pages_v1.AsyncPagesV1ResourceWithRawResponse(client.pages_v1) self.pages = pages.AsyncPagesResourceWithRawResponse(client.pages) class UnlayerWithStreamedResponse: def __init__(self, client: Unlayer) -> None: - self.emails_v1 = emails_v1.EmailsV1ResourceWithStreamingResponse(client.emails_v1) - self.emails = emails.EmailsResourceWithStreamingResponse(client.emails) - self.project_v1 = project_v1.ProjectV1ResourceWithStreamingResponse(client.project_v1) self.project = project.ProjectResourceWithStreamingResponse(client.project) - self.documents_v1 = documents_v1.DocumentsV1ResourceWithStreamingResponse(client.documents_v1) + self.emails = emails.EmailsResourceWithStreamingResponse(client.emails) self.documents = documents.DocumentsResourceWithStreamingResponse(client.documents) - self.pages_v1 = pages_v1.PagesV1ResourceWithStreamingResponse(client.pages_v1) self.pages = pages.PagesResourceWithStreamingResponse(client.pages) class AsyncUnlayerWithStreamedResponse: def __init__(self, client: AsyncUnlayer) -> None: - self.emails_v1 = emails_v1.AsyncEmailsV1ResourceWithStreamingResponse(client.emails_v1) - self.emails = emails.AsyncEmailsResourceWithStreamingResponse(client.emails) - self.project_v1 = project_v1.AsyncProjectV1ResourceWithStreamingResponse(client.project_v1) self.project = project.AsyncProjectResourceWithStreamingResponse(client.project) - self.documents_v1 = documents_v1.AsyncDocumentsV1ResourceWithStreamingResponse(client.documents_v1) + self.emails = emails.AsyncEmailsResourceWithStreamingResponse(client.emails) self.documents = documents.AsyncDocumentsResourceWithStreamingResponse(client.documents) - self.pages_v1 = pages_v1.AsyncPagesV1ResourceWithStreamingResponse(client.pages_v1) self.pages = pages.AsyncPagesResourceWithStreamingResponse(client.pages) diff --git a/src/unlayer/resources/__init__.py b/src/unlayer/resources/__init__.py index a898572..b1a13d2 100644 --- a/src/unlayer/resources/__init__.py +++ b/src/unlayer/resources/__init__.py @@ -24,14 +24,6 @@ ProjectResourceWithStreamingResponse, AsyncProjectResourceWithStreamingResponse, ) -from .pages_v1 import ( - PagesV1Resource, - AsyncPagesV1Resource, - PagesV1ResourceWithRawResponse, - AsyncPagesV1ResourceWithRawResponse, - PagesV1ResourceWithStreamingResponse, - AsyncPagesV1ResourceWithStreamingResponse, -) from .documents import ( DocumentsResource, AsyncDocumentsResource, @@ -40,74 +32,26 @@ DocumentsResourceWithStreamingResponse, AsyncDocumentsResourceWithStreamingResponse, ) -from .emails_v1 import ( - EmailsV1Resource, - AsyncEmailsV1Resource, - EmailsV1ResourceWithRawResponse, - AsyncEmailsV1ResourceWithRawResponse, - EmailsV1ResourceWithStreamingResponse, - AsyncEmailsV1ResourceWithStreamingResponse, -) -from .project_v1 import ( - ProjectV1Resource, - AsyncProjectV1Resource, - ProjectV1ResourceWithRawResponse, - AsyncProjectV1ResourceWithRawResponse, - ProjectV1ResourceWithStreamingResponse, - AsyncProjectV1ResourceWithStreamingResponse, -) -from .documents_v1 import ( - DocumentsV1Resource, - AsyncDocumentsV1Resource, - DocumentsV1ResourceWithRawResponse, - AsyncDocumentsV1ResourceWithRawResponse, - DocumentsV1ResourceWithStreamingResponse, - AsyncDocumentsV1ResourceWithStreamingResponse, -) __all__ = [ - "EmailsV1Resource", - "AsyncEmailsV1Resource", - "EmailsV1ResourceWithRawResponse", - "AsyncEmailsV1ResourceWithRawResponse", - "EmailsV1ResourceWithStreamingResponse", - "AsyncEmailsV1ResourceWithStreamingResponse", - "EmailsResource", - "AsyncEmailsResource", - "EmailsResourceWithRawResponse", - "AsyncEmailsResourceWithRawResponse", - "EmailsResourceWithStreamingResponse", - "AsyncEmailsResourceWithStreamingResponse", - "ProjectV1Resource", - "AsyncProjectV1Resource", - "ProjectV1ResourceWithRawResponse", - "AsyncProjectV1ResourceWithRawResponse", - "ProjectV1ResourceWithStreamingResponse", - "AsyncProjectV1ResourceWithStreamingResponse", "ProjectResource", "AsyncProjectResource", "ProjectResourceWithRawResponse", "AsyncProjectResourceWithRawResponse", "ProjectResourceWithStreamingResponse", "AsyncProjectResourceWithStreamingResponse", - "DocumentsV1Resource", - "AsyncDocumentsV1Resource", - "DocumentsV1ResourceWithRawResponse", - "AsyncDocumentsV1ResourceWithRawResponse", - "DocumentsV1ResourceWithStreamingResponse", - "AsyncDocumentsV1ResourceWithStreamingResponse", + "EmailsResource", + "AsyncEmailsResource", + "EmailsResourceWithRawResponse", + "AsyncEmailsResourceWithRawResponse", + "EmailsResourceWithStreamingResponse", + "AsyncEmailsResourceWithStreamingResponse", "DocumentsResource", "AsyncDocumentsResource", "DocumentsResourceWithRawResponse", "AsyncDocumentsResourceWithRawResponse", "DocumentsResourceWithStreamingResponse", "AsyncDocumentsResourceWithStreamingResponse", - "PagesV1Resource", - "AsyncPagesV1Resource", - "PagesV1ResourceWithRawResponse", - "AsyncPagesV1ResourceWithRawResponse", - "PagesV1ResourceWithStreamingResponse", - "AsyncPagesV1ResourceWithStreamingResponse", "PagesResource", "AsyncPagesResource", "PagesResourceWithRawResponse", diff --git a/src/unlayer/resources/documents/__init__.py b/src/unlayer/resources/documents/__init__.py new file mode 100644 index 0000000..51e4843 --- /dev/null +++ b/src/unlayer/resources/documents/__init__.py @@ -0,0 +1,33 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from .v1 import ( + V1Resource, + AsyncV1Resource, + V1ResourceWithRawResponse, + AsyncV1ResourceWithRawResponse, + V1ResourceWithStreamingResponse, + AsyncV1ResourceWithStreamingResponse, +) +from .documents import ( + DocumentsResource, + AsyncDocumentsResource, + DocumentsResourceWithRawResponse, + AsyncDocumentsResourceWithRawResponse, + DocumentsResourceWithStreamingResponse, + AsyncDocumentsResourceWithStreamingResponse, +) + +__all__ = [ + "V1Resource", + "AsyncV1Resource", + "V1ResourceWithRawResponse", + "AsyncV1ResourceWithRawResponse", + "V1ResourceWithStreamingResponse", + "AsyncV1ResourceWithStreamingResponse", + "DocumentsResource", + "AsyncDocumentsResource", + "DocumentsResourceWithRawResponse", + "AsyncDocumentsResourceWithRawResponse", + "DocumentsResourceWithStreamingResponse", + "AsyncDocumentsResourceWithStreamingResponse", +] diff --git a/src/unlayer/resources/documents.py b/src/unlayer/resources/documents/documents.py similarity index 89% rename from src/unlayer/resources/documents.py rename to src/unlayer/resources/documents/documents.py index 803d2f9..0c93199 100644 --- a/src/unlayer/resources/documents.py +++ b/src/unlayer/resources/documents/documents.py @@ -6,26 +6,38 @@ import httpx -from ..types import document_generate_create_params, document_generate_template_template_params -from .._types import Body, Omit, Query, Headers, NotGiven, omit, not_given -from .._utils import maybe_transform, async_maybe_transform -from .._compat import cached_property -from .._resource import SyncAPIResource, AsyncAPIResource -from .._response import ( +from .v1 import ( + V1Resource, + AsyncV1Resource, + V1ResourceWithRawResponse, + AsyncV1ResourceWithRawResponse, + V1ResourceWithStreamingResponse, + AsyncV1ResourceWithStreamingResponse, +) +from ...types import document_generate_create_params, document_generate_template_template_params +from ..._types import Body, Omit, Query, Headers, NotGiven, omit, not_given +from ..._utils import maybe_transform, async_maybe_transform +from ..._compat import cached_property +from ..._resource import SyncAPIResource, AsyncAPIResource +from ..._response import ( to_raw_response_wrapper, to_streamed_response_wrapper, async_to_raw_response_wrapper, async_to_streamed_response_wrapper, ) -from .._base_client import make_request_options -from ..types.document_generate_create_response import DocumentGenerateCreateResponse -from ..types.document_documents_retrieve_response import DocumentDocumentsRetrieveResponse -from ..types.document_generate_template_template_response import DocumentGenerateTemplateTemplateResponse +from ..._base_client import make_request_options +from ...types.document_generate_create_response import DocumentGenerateCreateResponse +from ...types.document_documents_retrieve_response import DocumentDocumentsRetrieveResponse +from ...types.document_generate_template_template_response import DocumentGenerateTemplateTemplateResponse __all__ = ["DocumentsResource", "AsyncDocumentsResource"] class DocumentsResource(SyncAPIResource): + @cached_property + def v1(self) -> V1Resource: + return V1Resource(self._client) + @cached_property def with_raw_response(self) -> DocumentsResourceWithRawResponse: """ @@ -182,6 +194,10 @@ def generate_template_template( class AsyncDocumentsResource(AsyncAPIResource): + @cached_property + def v1(self) -> AsyncV1Resource: + return AsyncV1Resource(self._client) + @cached_property def with_raw_response(self) -> AsyncDocumentsResourceWithRawResponse: """ @@ -351,6 +367,10 @@ def __init__(self, documents: DocumentsResource) -> None: documents.generate_template_template, ) + @cached_property + def v1(self) -> V1ResourceWithRawResponse: + return V1ResourceWithRawResponse(self._documents.v1) + class AsyncDocumentsResourceWithRawResponse: def __init__(self, documents: AsyncDocumentsResource) -> None: @@ -366,6 +386,10 @@ def __init__(self, documents: AsyncDocumentsResource) -> None: documents.generate_template_template, ) + @cached_property + def v1(self) -> AsyncV1ResourceWithRawResponse: + return AsyncV1ResourceWithRawResponse(self._documents.v1) + class DocumentsResourceWithStreamingResponse: def __init__(self, documents: DocumentsResource) -> None: @@ -381,6 +405,10 @@ def __init__(self, documents: DocumentsResource) -> None: documents.generate_template_template, ) + @cached_property + def v1(self) -> V1ResourceWithStreamingResponse: + return V1ResourceWithStreamingResponse(self._documents.v1) + class AsyncDocumentsResourceWithStreamingResponse: def __init__(self, documents: AsyncDocumentsResource) -> None: @@ -395,3 +423,7 @@ def __init__(self, documents: AsyncDocumentsResource) -> None: self.generate_template_template = async_to_streamed_response_wrapper( documents.generate_template_template, ) + + @cached_property + def v1(self) -> AsyncV1ResourceWithStreamingResponse: + return AsyncV1ResourceWithStreamingResponse(self._documents.v1) diff --git a/src/unlayer/resources/documents_v1.py b/src/unlayer/resources/documents/v1.py similarity index 76% rename from src/unlayer/resources/documents_v1.py rename to src/unlayer/resources/documents/v1.py index 1a2d68e..cf6f243 100644 --- a/src/unlayer/resources/documents_v1.py +++ b/src/unlayer/resources/documents/v1.py @@ -6,44 +6,44 @@ import httpx -from ..types import documents_v1_generate_create_params, documents_v1_generate_template_template_params -from .._types import Body, Omit, Query, Headers, NotGiven, omit, not_given -from .._utils import maybe_transform, async_maybe_transform -from .._compat import cached_property -from .._resource import SyncAPIResource, AsyncAPIResource -from .._response import ( +from ..._types import Body, Omit, Query, Headers, NotGiven, omit, not_given +from ..._utils import maybe_transform, async_maybe_transform +from ..._compat import cached_property +from ..._resource import SyncAPIResource, AsyncAPIResource +from ..._response import ( to_raw_response_wrapper, to_streamed_response_wrapper, async_to_raw_response_wrapper, async_to_streamed_response_wrapper, ) -from .._base_client import make_request_options -from ..types.documents_v1_generate_create_response import DocumentsV1GenerateCreateResponse -from ..types.documents_v1_documents_retrieve_response import DocumentsV1DocumentsRetrieveResponse -from ..types.documents_v1_generate_template_template_response import DocumentsV1GenerateTemplateTemplateResponse +from ..._base_client import make_request_options +from ...types.documents import v1_generate_create_params, v1_generate_template_template_params +from ...types.documents.v1_generate_create_response import V1GenerateCreateResponse +from ...types.documents.v1_documents_retrieve_response import V1DocumentsRetrieveResponse +from ...types.documents.v1_generate_template_template_response import V1GenerateTemplateTemplateResponse -__all__ = ["DocumentsV1Resource", "AsyncDocumentsV1Resource"] +__all__ = ["V1Resource", "AsyncV1Resource"] -class DocumentsV1Resource(SyncAPIResource): +class V1Resource(SyncAPIResource): @cached_property - def with_raw_response(self) -> DocumentsV1ResourceWithRawResponse: + def with_raw_response(self) -> V1ResourceWithRawResponse: """ This property can be used as a prefix for any HTTP method call to return the raw response object instead of the parsed content. For more information, see https://www.github.com/stainless-sdks/unlayer-python#accessing-raw-response-data-eg-headers """ - return DocumentsV1ResourceWithRawResponse(self) + return V1ResourceWithRawResponse(self) @cached_property - def with_streaming_response(self) -> DocumentsV1ResourceWithStreamingResponse: + def with_streaming_response(self) -> V1ResourceWithStreamingResponse: """ An alternative to `.with_raw_response` that doesn't eagerly read the response body. For more information, see https://www.github.com/stainless-sdks/unlayer-python#with_streaming_response """ - return DocumentsV1ResourceWithStreamingResponse(self) + return V1ResourceWithStreamingResponse(self) def documents_retrieve( self, @@ -55,7 +55,7 @@ def documents_retrieve( extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> DocumentsV1DocumentsRetrieveResponse: + ) -> V1DocumentsRetrieveResponse: """ Retrieve details of a previously generated document. @@ -75,7 +75,7 @@ def documents_retrieve( options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), - cast_to=DocumentsV1DocumentsRetrieveResponse, + cast_to=V1DocumentsRetrieveResponse, ) def generate_create( @@ -92,7 +92,7 @@ def generate_create( extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> DocumentsV1GenerateCreateResponse: + ) -> V1GenerateCreateResponse: """ Generate PDF document from JSON design, HTML content, or URL. @@ -125,12 +125,12 @@ def generate_create( "merge_tags": merge_tags, "url": url, }, - documents_v1_generate_create_params.DocumentsV1GenerateCreateParams, + v1_generate_create_params.V1GenerateCreateParams, ), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), - cast_to=DocumentsV1GenerateCreateResponse, + cast_to=V1GenerateCreateResponse, ) def generate_template_template( @@ -145,7 +145,7 @@ def generate_template_template( extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> DocumentsV1GenerateTemplateTemplateResponse: + ) -> V1GenerateTemplateTemplateResponse: """ Generate PDF document from an existing template with merge tags. @@ -172,34 +172,34 @@ def generate_template_template( "filename": filename, "merge_tags": merge_tags, }, - documents_v1_generate_template_template_params.DocumentsV1GenerateTemplateTemplateParams, + v1_generate_template_template_params.V1GenerateTemplateTemplateParams, ), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), - cast_to=DocumentsV1GenerateTemplateTemplateResponse, + cast_to=V1GenerateTemplateTemplateResponse, ) -class AsyncDocumentsV1Resource(AsyncAPIResource): +class AsyncV1Resource(AsyncAPIResource): @cached_property - def with_raw_response(self) -> AsyncDocumentsV1ResourceWithRawResponse: + def with_raw_response(self) -> AsyncV1ResourceWithRawResponse: """ This property can be used as a prefix for any HTTP method call to return the raw response object instead of the parsed content. For more information, see https://www.github.com/stainless-sdks/unlayer-python#accessing-raw-response-data-eg-headers """ - return AsyncDocumentsV1ResourceWithRawResponse(self) + return AsyncV1ResourceWithRawResponse(self) @cached_property - def with_streaming_response(self) -> AsyncDocumentsV1ResourceWithStreamingResponse: + def with_streaming_response(self) -> AsyncV1ResourceWithStreamingResponse: """ An alternative to `.with_raw_response` that doesn't eagerly read the response body. For more information, see https://www.github.com/stainless-sdks/unlayer-python#with_streaming_response """ - return AsyncDocumentsV1ResourceWithStreamingResponse(self) + return AsyncV1ResourceWithStreamingResponse(self) async def documents_retrieve( self, @@ -211,7 +211,7 @@ async def documents_retrieve( extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> DocumentsV1DocumentsRetrieveResponse: + ) -> V1DocumentsRetrieveResponse: """ Retrieve details of a previously generated document. @@ -231,7 +231,7 @@ async def documents_retrieve( options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), - cast_to=DocumentsV1DocumentsRetrieveResponse, + cast_to=V1DocumentsRetrieveResponse, ) async def generate_create( @@ -248,7 +248,7 @@ async def generate_create( extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> DocumentsV1GenerateCreateResponse: + ) -> V1GenerateCreateResponse: """ Generate PDF document from JSON design, HTML content, or URL. @@ -281,12 +281,12 @@ async def generate_create( "merge_tags": merge_tags, "url": url, }, - documents_v1_generate_create_params.DocumentsV1GenerateCreateParams, + v1_generate_create_params.V1GenerateCreateParams, ), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), - cast_to=DocumentsV1GenerateCreateResponse, + cast_to=V1GenerateCreateResponse, ) async def generate_template_template( @@ -301,7 +301,7 @@ async def generate_template_template( extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> DocumentsV1GenerateTemplateTemplateResponse: + ) -> V1GenerateTemplateTemplateResponse: """ Generate PDF document from an existing template with merge tags. @@ -328,70 +328,70 @@ async def generate_template_template( "filename": filename, "merge_tags": merge_tags, }, - documents_v1_generate_template_template_params.DocumentsV1GenerateTemplateTemplateParams, + v1_generate_template_template_params.V1GenerateTemplateTemplateParams, ), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), - cast_to=DocumentsV1GenerateTemplateTemplateResponse, + cast_to=V1GenerateTemplateTemplateResponse, ) -class DocumentsV1ResourceWithRawResponse: - def __init__(self, documents_v1: DocumentsV1Resource) -> None: - self._documents_v1 = documents_v1 +class V1ResourceWithRawResponse: + def __init__(self, v1: V1Resource) -> None: + self._v1 = v1 self.documents_retrieve = to_raw_response_wrapper( - documents_v1.documents_retrieve, + v1.documents_retrieve, ) self.generate_create = to_raw_response_wrapper( - documents_v1.generate_create, + v1.generate_create, ) self.generate_template_template = to_raw_response_wrapper( - documents_v1.generate_template_template, + v1.generate_template_template, ) -class AsyncDocumentsV1ResourceWithRawResponse: - def __init__(self, documents_v1: AsyncDocumentsV1Resource) -> None: - self._documents_v1 = documents_v1 +class AsyncV1ResourceWithRawResponse: + def __init__(self, v1: AsyncV1Resource) -> None: + self._v1 = v1 self.documents_retrieve = async_to_raw_response_wrapper( - documents_v1.documents_retrieve, + v1.documents_retrieve, ) self.generate_create = async_to_raw_response_wrapper( - documents_v1.generate_create, + v1.generate_create, ) self.generate_template_template = async_to_raw_response_wrapper( - documents_v1.generate_template_template, + v1.generate_template_template, ) -class DocumentsV1ResourceWithStreamingResponse: - def __init__(self, documents_v1: DocumentsV1Resource) -> None: - self._documents_v1 = documents_v1 +class V1ResourceWithStreamingResponse: + def __init__(self, v1: V1Resource) -> None: + self._v1 = v1 self.documents_retrieve = to_streamed_response_wrapper( - documents_v1.documents_retrieve, + v1.documents_retrieve, ) self.generate_create = to_streamed_response_wrapper( - documents_v1.generate_create, + v1.generate_create, ) self.generate_template_template = to_streamed_response_wrapper( - documents_v1.generate_template_template, + v1.generate_template_template, ) -class AsyncDocumentsV1ResourceWithStreamingResponse: - def __init__(self, documents_v1: AsyncDocumentsV1Resource) -> None: - self._documents_v1 = documents_v1 +class AsyncV1ResourceWithStreamingResponse: + def __init__(self, v1: AsyncV1Resource) -> None: + self._v1 = v1 self.documents_retrieve = async_to_streamed_response_wrapper( - documents_v1.documents_retrieve, + v1.documents_retrieve, ) self.generate_create = async_to_streamed_response_wrapper( - documents_v1.generate_create, + v1.generate_create, ) self.generate_template_template = async_to_streamed_response_wrapper( - documents_v1.generate_template_template, + v1.generate_template_template, ) diff --git a/src/unlayer/resources/emails/__init__.py b/src/unlayer/resources/emails/__init__.py new file mode 100644 index 0000000..752f051 --- /dev/null +++ b/src/unlayer/resources/emails/__init__.py @@ -0,0 +1,33 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from .v1 import ( + V1Resource, + AsyncV1Resource, + V1ResourceWithRawResponse, + AsyncV1ResourceWithRawResponse, + V1ResourceWithStreamingResponse, + AsyncV1ResourceWithStreamingResponse, +) +from .emails import ( + EmailsResource, + AsyncEmailsResource, + EmailsResourceWithRawResponse, + AsyncEmailsResourceWithRawResponse, + EmailsResourceWithStreamingResponse, + AsyncEmailsResourceWithStreamingResponse, +) + +__all__ = [ + "V1Resource", + "AsyncV1Resource", + "V1ResourceWithRawResponse", + "AsyncV1ResourceWithRawResponse", + "V1ResourceWithStreamingResponse", + "AsyncV1ResourceWithStreamingResponse", + "EmailsResource", + "AsyncEmailsResource", + "EmailsResourceWithRawResponse", + "AsyncEmailsResourceWithRawResponse", + "EmailsResourceWithStreamingResponse", + "AsyncEmailsResourceWithStreamingResponse", +] diff --git a/src/unlayer/resources/emails.py b/src/unlayer/resources/emails/emails.py similarity index 91% rename from src/unlayer/resources/emails.py rename to src/unlayer/resources/emails/emails.py index 1f2ca5c..1585e4e 100644 --- a/src/unlayer/resources/emails.py +++ b/src/unlayer/resources/emails/emails.py @@ -6,27 +6,39 @@ import httpx -from ..types import email_send_create_params, email_render_create_params, email_send_template_template_params -from .._types import Body, Omit, Query, Headers, NotGiven, omit, not_given -from .._utils import maybe_transform, async_maybe_transform -from .._compat import cached_property -from .._resource import SyncAPIResource, AsyncAPIResource -from .._response import ( +from .v1 import ( + V1Resource, + AsyncV1Resource, + V1ResourceWithRawResponse, + AsyncV1ResourceWithRawResponse, + V1ResourceWithStreamingResponse, + AsyncV1ResourceWithStreamingResponse, +) +from ...types import email_send_create_params, email_render_create_params, email_send_template_template_params +from ..._types import Body, Omit, Query, Headers, NotGiven, omit, not_given +from ..._utils import maybe_transform, async_maybe_transform +from ..._compat import cached_property +from ..._resource import SyncAPIResource, AsyncAPIResource +from ..._response import ( to_raw_response_wrapper, to_streamed_response_wrapper, async_to_raw_response_wrapper, async_to_streamed_response_wrapper, ) -from .._base_client import make_request_options -from ..types.email_send_create_response import EmailSendCreateResponse -from ..types.email_render_create_response import EmailRenderCreateResponse -from ..types.email_emails_retrieve_response import EmailEmailsRetrieveResponse -from ..types.email_send_template_template_response import EmailSendTemplateTemplateResponse +from ..._base_client import make_request_options +from ...types.email_send_create_response import EmailSendCreateResponse +from ...types.email_render_create_response import EmailRenderCreateResponse +from ...types.email_emails_retrieve_response import EmailEmailsRetrieveResponse +from ...types.email_send_template_template_response import EmailSendTemplateTemplateResponse __all__ = ["EmailsResource", "AsyncEmailsResource"] class EmailsResource(SyncAPIResource): + @cached_property + def v1(self) -> V1Resource: + return V1Resource(self._client) + @cached_property def with_raw_response(self) -> EmailsResourceWithRawResponse: """ @@ -230,6 +242,10 @@ def send_template_template( class AsyncEmailsResource(AsyncAPIResource): + @cached_property + def v1(self) -> AsyncV1Resource: + return AsyncV1Resource(self._client) + @cached_property def with_raw_response(self) -> AsyncEmailsResourceWithRawResponse: """ @@ -449,6 +465,10 @@ def __init__(self, emails: EmailsResource) -> None: emails.send_template_template, ) + @cached_property + def v1(self) -> V1ResourceWithRawResponse: + return V1ResourceWithRawResponse(self._emails.v1) + class AsyncEmailsResourceWithRawResponse: def __init__(self, emails: AsyncEmailsResource) -> None: @@ -467,6 +487,10 @@ def __init__(self, emails: AsyncEmailsResource) -> None: emails.send_template_template, ) + @cached_property + def v1(self) -> AsyncV1ResourceWithRawResponse: + return AsyncV1ResourceWithRawResponse(self._emails.v1) + class EmailsResourceWithStreamingResponse: def __init__(self, emails: EmailsResource) -> None: @@ -485,6 +509,10 @@ def __init__(self, emails: EmailsResource) -> None: emails.send_template_template, ) + @cached_property + def v1(self) -> V1ResourceWithStreamingResponse: + return V1ResourceWithStreamingResponse(self._emails.v1) + class AsyncEmailsResourceWithStreamingResponse: def __init__(self, emails: AsyncEmailsResource) -> None: @@ -502,3 +530,7 @@ def __init__(self, emails: AsyncEmailsResource) -> None: self.send_template_template = async_to_streamed_response_wrapper( emails.send_template_template, ) + + @cached_property + def v1(self) -> AsyncV1ResourceWithStreamingResponse: + return AsyncV1ResourceWithStreamingResponse(self._emails.v1) diff --git a/src/unlayer/resources/emails_v1.py b/src/unlayer/resources/emails/v1.py similarity index 79% rename from src/unlayer/resources/emails_v1.py rename to src/unlayer/resources/emails/v1.py index 7744a72..90b3b3c 100644 --- a/src/unlayer/resources/emails_v1.py +++ b/src/unlayer/resources/emails/v1.py @@ -6,49 +6,45 @@ import httpx -from ..types import ( - emails_v1_send_create_params, - emails_v1_render_create_params, - emails_v1_send_template_template_params, -) -from .._types import Body, Omit, Query, Headers, NotGiven, omit, not_given -from .._utils import maybe_transform, async_maybe_transform -from .._compat import cached_property -from .._resource import SyncAPIResource, AsyncAPIResource -from .._response import ( +from ..._types import Body, Omit, Query, Headers, NotGiven, omit, not_given +from ..._utils import maybe_transform, async_maybe_transform +from ..._compat import cached_property +from ..._resource import SyncAPIResource, AsyncAPIResource +from ..._response import ( to_raw_response_wrapper, to_streamed_response_wrapper, async_to_raw_response_wrapper, async_to_streamed_response_wrapper, ) -from .._base_client import make_request_options -from ..types.emails_v1_send_create_response import EmailsV1SendCreateResponse -from ..types.emails_v1_render_create_response import EmailsV1RenderCreateResponse -from ..types.emails_v1_emails_retrieve_response import EmailsV1EmailsRetrieveResponse -from ..types.emails_v1_send_template_template_response import EmailsV1SendTemplateTemplateResponse +from ..._base_client import make_request_options +from ...types.emails import v1_send_create_params, v1_render_create_params, v1_send_template_template_params +from ...types.emails.v1_send_create_response import V1SendCreateResponse +from ...types.emails.v1_render_create_response import V1RenderCreateResponse +from ...types.emails.v1_emails_retrieve_response import V1EmailsRetrieveResponse +from ...types.emails.v1_send_template_template_response import V1SendTemplateTemplateResponse -__all__ = ["EmailsV1Resource", "AsyncEmailsV1Resource"] +__all__ = ["V1Resource", "AsyncV1Resource"] -class EmailsV1Resource(SyncAPIResource): +class V1Resource(SyncAPIResource): @cached_property - def with_raw_response(self) -> EmailsV1ResourceWithRawResponse: + def with_raw_response(self) -> V1ResourceWithRawResponse: """ This property can be used as a prefix for any HTTP method call to return the raw response object instead of the parsed content. For more information, see https://www.github.com/stainless-sdks/unlayer-python#accessing-raw-response-data-eg-headers """ - return EmailsV1ResourceWithRawResponse(self) + return V1ResourceWithRawResponse(self) @cached_property - def with_streaming_response(self) -> EmailsV1ResourceWithStreamingResponse: + def with_streaming_response(self) -> V1ResourceWithStreamingResponse: """ An alternative to `.with_raw_response` that doesn't eagerly read the response body. For more information, see https://www.github.com/stainless-sdks/unlayer-python#with_streaming_response """ - return EmailsV1ResourceWithStreamingResponse(self) + return V1ResourceWithStreamingResponse(self) def emails_retrieve( self, @@ -60,7 +56,7 @@ def emails_retrieve( extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> EmailsV1EmailsRetrieveResponse: + ) -> V1EmailsRetrieveResponse: """ Retrieve details of a previously sent email. @@ -80,7 +76,7 @@ def emails_retrieve( options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), - cast_to=EmailsV1EmailsRetrieveResponse, + cast_to=V1EmailsRetrieveResponse, ) def render_create( @@ -94,7 +90,7 @@ def render_create( extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> EmailsV1RenderCreateResponse: + ) -> V1RenderCreateResponse: """ Convert design JSON to HTML with optional merge tags. @@ -118,12 +114,12 @@ def render_create( "design": design, "merge_tags": merge_tags, }, - emails_v1_render_create_params.EmailsV1RenderCreateParams, + v1_render_create_params.V1RenderCreateParams, ), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), - cast_to=EmailsV1RenderCreateResponse, + cast_to=V1RenderCreateResponse, ) def send_create( @@ -140,7 +136,7 @@ def send_create( extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> EmailsV1SendCreateResponse: + ) -> V1SendCreateResponse: """ Send email with design JSON or HTML content. @@ -173,12 +169,12 @@ def send_create( "merge_tags": merge_tags, "subject": subject, }, - emails_v1_send_create_params.EmailsV1SendCreateParams, + v1_send_create_params.V1SendCreateParams, ), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), - cast_to=EmailsV1SendCreateResponse, + cast_to=V1SendCreateResponse, ) def send_template_template( @@ -194,7 +190,7 @@ def send_template_template( extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> EmailsV1SendTemplateTemplateResponse: + ) -> V1SendTemplateTemplateResponse: """ Send email using an existing template with merge tags. @@ -224,34 +220,34 @@ def send_template_template( "merge_tags": merge_tags, "subject": subject, }, - emails_v1_send_template_template_params.EmailsV1SendTemplateTemplateParams, + v1_send_template_template_params.V1SendTemplateTemplateParams, ), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), - cast_to=EmailsV1SendTemplateTemplateResponse, + cast_to=V1SendTemplateTemplateResponse, ) -class AsyncEmailsV1Resource(AsyncAPIResource): +class AsyncV1Resource(AsyncAPIResource): @cached_property - def with_raw_response(self) -> AsyncEmailsV1ResourceWithRawResponse: + def with_raw_response(self) -> AsyncV1ResourceWithRawResponse: """ This property can be used as a prefix for any HTTP method call to return the raw response object instead of the parsed content. For more information, see https://www.github.com/stainless-sdks/unlayer-python#accessing-raw-response-data-eg-headers """ - return AsyncEmailsV1ResourceWithRawResponse(self) + return AsyncV1ResourceWithRawResponse(self) @cached_property - def with_streaming_response(self) -> AsyncEmailsV1ResourceWithStreamingResponse: + def with_streaming_response(self) -> AsyncV1ResourceWithStreamingResponse: """ An alternative to `.with_raw_response` that doesn't eagerly read the response body. For more information, see https://www.github.com/stainless-sdks/unlayer-python#with_streaming_response """ - return AsyncEmailsV1ResourceWithStreamingResponse(self) + return AsyncV1ResourceWithStreamingResponse(self) async def emails_retrieve( self, @@ -263,7 +259,7 @@ async def emails_retrieve( extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> EmailsV1EmailsRetrieveResponse: + ) -> V1EmailsRetrieveResponse: """ Retrieve details of a previously sent email. @@ -283,7 +279,7 @@ async def emails_retrieve( options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), - cast_to=EmailsV1EmailsRetrieveResponse, + cast_to=V1EmailsRetrieveResponse, ) async def render_create( @@ -297,7 +293,7 @@ async def render_create( extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> EmailsV1RenderCreateResponse: + ) -> V1RenderCreateResponse: """ Convert design JSON to HTML with optional merge tags. @@ -321,12 +317,12 @@ async def render_create( "design": design, "merge_tags": merge_tags, }, - emails_v1_render_create_params.EmailsV1RenderCreateParams, + v1_render_create_params.V1RenderCreateParams, ), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), - cast_to=EmailsV1RenderCreateResponse, + cast_to=V1RenderCreateResponse, ) async def send_create( @@ -343,7 +339,7 @@ async def send_create( extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> EmailsV1SendCreateResponse: + ) -> V1SendCreateResponse: """ Send email with design JSON or HTML content. @@ -376,12 +372,12 @@ async def send_create( "merge_tags": merge_tags, "subject": subject, }, - emails_v1_send_create_params.EmailsV1SendCreateParams, + v1_send_create_params.V1SendCreateParams, ), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), - cast_to=EmailsV1SendCreateResponse, + cast_to=V1SendCreateResponse, ) async def send_template_template( @@ -397,7 +393,7 @@ async def send_template_template( extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> EmailsV1SendTemplateTemplateResponse: + ) -> V1SendTemplateTemplateResponse: """ Send email using an existing template with merge tags. @@ -427,82 +423,82 @@ async def send_template_template( "merge_tags": merge_tags, "subject": subject, }, - emails_v1_send_template_template_params.EmailsV1SendTemplateTemplateParams, + v1_send_template_template_params.V1SendTemplateTemplateParams, ), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), - cast_to=EmailsV1SendTemplateTemplateResponse, + cast_to=V1SendTemplateTemplateResponse, ) -class EmailsV1ResourceWithRawResponse: - def __init__(self, emails_v1: EmailsV1Resource) -> None: - self._emails_v1 = emails_v1 +class V1ResourceWithRawResponse: + def __init__(self, v1: V1Resource) -> None: + self._v1 = v1 self.emails_retrieve = to_raw_response_wrapper( - emails_v1.emails_retrieve, + v1.emails_retrieve, ) self.render_create = to_raw_response_wrapper( - emails_v1.render_create, + v1.render_create, ) self.send_create = to_raw_response_wrapper( - emails_v1.send_create, + v1.send_create, ) self.send_template_template = to_raw_response_wrapper( - emails_v1.send_template_template, + v1.send_template_template, ) -class AsyncEmailsV1ResourceWithRawResponse: - def __init__(self, emails_v1: AsyncEmailsV1Resource) -> None: - self._emails_v1 = emails_v1 +class AsyncV1ResourceWithRawResponse: + def __init__(self, v1: AsyncV1Resource) -> None: + self._v1 = v1 self.emails_retrieve = async_to_raw_response_wrapper( - emails_v1.emails_retrieve, + v1.emails_retrieve, ) self.render_create = async_to_raw_response_wrapper( - emails_v1.render_create, + v1.render_create, ) self.send_create = async_to_raw_response_wrapper( - emails_v1.send_create, + v1.send_create, ) self.send_template_template = async_to_raw_response_wrapper( - emails_v1.send_template_template, + v1.send_template_template, ) -class EmailsV1ResourceWithStreamingResponse: - def __init__(self, emails_v1: EmailsV1Resource) -> None: - self._emails_v1 = emails_v1 +class V1ResourceWithStreamingResponse: + def __init__(self, v1: V1Resource) -> None: + self._v1 = v1 self.emails_retrieve = to_streamed_response_wrapper( - emails_v1.emails_retrieve, + v1.emails_retrieve, ) self.render_create = to_streamed_response_wrapper( - emails_v1.render_create, + v1.render_create, ) self.send_create = to_streamed_response_wrapper( - emails_v1.send_create, + v1.send_create, ) self.send_template_template = to_streamed_response_wrapper( - emails_v1.send_template_template, + v1.send_template_template, ) -class AsyncEmailsV1ResourceWithStreamingResponse: - def __init__(self, emails_v1: AsyncEmailsV1Resource) -> None: - self._emails_v1 = emails_v1 +class AsyncV1ResourceWithStreamingResponse: + def __init__(self, v1: AsyncV1Resource) -> None: + self._v1 = v1 self.emails_retrieve = async_to_streamed_response_wrapper( - emails_v1.emails_retrieve, + v1.emails_retrieve, ) self.render_create = async_to_streamed_response_wrapper( - emails_v1.render_create, + v1.render_create, ) self.send_create = async_to_streamed_response_wrapper( - emails_v1.send_create, + v1.send_create, ) self.send_template_template = async_to_streamed_response_wrapper( - emails_v1.send_template_template, + v1.send_template_template, ) diff --git a/src/unlayer/resources/pages/__init__.py b/src/unlayer/resources/pages/__init__.py new file mode 100644 index 0000000..bc25669 --- /dev/null +++ b/src/unlayer/resources/pages/__init__.py @@ -0,0 +1,33 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from .v1 import ( + V1Resource, + AsyncV1Resource, + V1ResourceWithRawResponse, + AsyncV1ResourceWithRawResponse, + V1ResourceWithStreamingResponse, + AsyncV1ResourceWithStreamingResponse, +) +from .pages import ( + PagesResource, + AsyncPagesResource, + PagesResourceWithRawResponse, + AsyncPagesResourceWithRawResponse, + PagesResourceWithStreamingResponse, + AsyncPagesResourceWithStreamingResponse, +) + +__all__ = [ + "V1Resource", + "AsyncV1Resource", + "V1ResourceWithRawResponse", + "AsyncV1ResourceWithRawResponse", + "V1ResourceWithStreamingResponse", + "AsyncV1ResourceWithStreamingResponse", + "PagesResource", + "AsyncPagesResource", + "PagesResourceWithRawResponse", + "AsyncPagesResourceWithRawResponse", + "PagesResourceWithStreamingResponse", + "AsyncPagesResourceWithStreamingResponse", +] diff --git a/src/unlayer/resources/pages.py b/src/unlayer/resources/pages/pages.py similarity index 81% rename from src/unlayer/resources/pages.py rename to src/unlayer/resources/pages/pages.py index 158cd79..aaa7b0e 100644 --- a/src/unlayer/resources/pages.py +++ b/src/unlayer/resources/pages/pages.py @@ -6,24 +6,36 @@ import httpx -from ..types import page_render_create_params -from .._types import Body, Omit, Query, Headers, NotGiven, omit, not_given -from .._utils import maybe_transform, async_maybe_transform -from .._compat import cached_property -from .._resource import SyncAPIResource, AsyncAPIResource -from .._response import ( +from .v1 import ( + V1Resource, + AsyncV1Resource, + V1ResourceWithRawResponse, + AsyncV1ResourceWithRawResponse, + V1ResourceWithStreamingResponse, + AsyncV1ResourceWithStreamingResponse, +) +from ...types import page_render_create_params +from ..._types import Body, Omit, Query, Headers, NotGiven, omit, not_given +from ..._utils import maybe_transform, async_maybe_transform +from ..._compat import cached_property +from ..._resource import SyncAPIResource, AsyncAPIResource +from ..._response import ( to_raw_response_wrapper, to_streamed_response_wrapper, async_to_raw_response_wrapper, async_to_streamed_response_wrapper, ) -from .._base_client import make_request_options -from ..types.page_render_create_response import PageRenderCreateResponse +from ..._base_client import make_request_options +from ...types.page_render_create_response import PageRenderCreateResponse __all__ = ["PagesResource", "AsyncPagesResource"] class PagesResource(SyncAPIResource): + @cached_property + def v1(self) -> V1Resource: + return V1Resource(self._client) + @cached_property def with_raw_response(self) -> PagesResourceWithRawResponse: """ @@ -88,6 +100,10 @@ def render_create( class AsyncPagesResource(AsyncAPIResource): + @cached_property + def v1(self) -> AsyncV1Resource: + return AsyncV1Resource(self._client) + @cached_property def with_raw_response(self) -> AsyncPagesResourceWithRawResponse: """ @@ -159,6 +175,10 @@ def __init__(self, pages: PagesResource) -> None: pages.render_create, ) + @cached_property + def v1(self) -> V1ResourceWithRawResponse: + return V1ResourceWithRawResponse(self._pages.v1) + class AsyncPagesResourceWithRawResponse: def __init__(self, pages: AsyncPagesResource) -> None: @@ -168,6 +188,10 @@ def __init__(self, pages: AsyncPagesResource) -> None: pages.render_create, ) + @cached_property + def v1(self) -> AsyncV1ResourceWithRawResponse: + return AsyncV1ResourceWithRawResponse(self._pages.v1) + class PagesResourceWithStreamingResponse: def __init__(self, pages: PagesResource) -> None: @@ -177,6 +201,10 @@ def __init__(self, pages: PagesResource) -> None: pages.render_create, ) + @cached_property + def v1(self) -> V1ResourceWithStreamingResponse: + return V1ResourceWithStreamingResponse(self._pages.v1) + class AsyncPagesResourceWithStreamingResponse: def __init__(self, pages: AsyncPagesResource) -> None: @@ -185,3 +213,7 @@ def __init__(self, pages: AsyncPagesResource) -> None: self.render_create = async_to_streamed_response_wrapper( pages.render_create, ) + + @cached_property + def v1(self) -> AsyncV1ResourceWithStreamingResponse: + return AsyncV1ResourceWithStreamingResponse(self._pages.v1) diff --git a/src/unlayer/resources/pages_v1.py b/src/unlayer/resources/pages/v1.py similarity index 69% rename from src/unlayer/resources/pages_v1.py rename to src/unlayer/resources/pages/v1.py index 17c64ed..b232272 100644 --- a/src/unlayer/resources/pages_v1.py +++ b/src/unlayer/resources/pages/v1.py @@ -6,42 +6,42 @@ import httpx -from ..types import pages_v1_render_create_params -from .._types import Body, Omit, Query, Headers, NotGiven, omit, not_given -from .._utils import maybe_transform, async_maybe_transform -from .._compat import cached_property -from .._resource import SyncAPIResource, AsyncAPIResource -from .._response import ( +from ..._types import Body, Omit, Query, Headers, NotGiven, omit, not_given +from ..._utils import maybe_transform, async_maybe_transform +from ..._compat import cached_property +from ..._resource import SyncAPIResource, AsyncAPIResource +from ..._response import ( to_raw_response_wrapper, to_streamed_response_wrapper, async_to_raw_response_wrapper, async_to_streamed_response_wrapper, ) -from .._base_client import make_request_options -from ..types.pages_v1_render_create_response import PagesV1RenderCreateResponse +from ...types.pages import v1_render_create_params +from ..._base_client import make_request_options +from ...types.pages.v1_render_create_response import V1RenderCreateResponse -__all__ = ["PagesV1Resource", "AsyncPagesV1Resource"] +__all__ = ["V1Resource", "AsyncV1Resource"] -class PagesV1Resource(SyncAPIResource): +class V1Resource(SyncAPIResource): @cached_property - def with_raw_response(self) -> PagesV1ResourceWithRawResponse: + def with_raw_response(self) -> V1ResourceWithRawResponse: """ This property can be used as a prefix for any HTTP method call to return the raw response object instead of the parsed content. For more information, see https://www.github.com/stainless-sdks/unlayer-python#accessing-raw-response-data-eg-headers """ - return PagesV1ResourceWithRawResponse(self) + return V1ResourceWithRawResponse(self) @cached_property - def with_streaming_response(self) -> PagesV1ResourceWithStreamingResponse: + def with_streaming_response(self) -> V1ResourceWithStreamingResponse: """ An alternative to `.with_raw_response` that doesn't eagerly read the response body. For more information, see https://www.github.com/stainless-sdks/unlayer-python#with_streaming_response """ - return PagesV1ResourceWithStreamingResponse(self) + return V1ResourceWithStreamingResponse(self) def render_create( self, @@ -54,7 +54,7 @@ def render_create( extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> PagesV1RenderCreateResponse: + ) -> V1RenderCreateResponse: """ Convert page design JSON to HTML with optional merge tags. @@ -78,34 +78,34 @@ def render_create( "design": design, "merge_tags": merge_tags, }, - pages_v1_render_create_params.PagesV1RenderCreateParams, + v1_render_create_params.V1RenderCreateParams, ), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), - cast_to=PagesV1RenderCreateResponse, + cast_to=V1RenderCreateResponse, ) -class AsyncPagesV1Resource(AsyncAPIResource): +class AsyncV1Resource(AsyncAPIResource): @cached_property - def with_raw_response(self) -> AsyncPagesV1ResourceWithRawResponse: + def with_raw_response(self) -> AsyncV1ResourceWithRawResponse: """ This property can be used as a prefix for any HTTP method call to return the raw response object instead of the parsed content. For more information, see https://www.github.com/stainless-sdks/unlayer-python#accessing-raw-response-data-eg-headers """ - return AsyncPagesV1ResourceWithRawResponse(self) + return AsyncV1ResourceWithRawResponse(self) @cached_property - def with_streaming_response(self) -> AsyncPagesV1ResourceWithStreamingResponse: + def with_streaming_response(self) -> AsyncV1ResourceWithStreamingResponse: """ An alternative to `.with_raw_response` that doesn't eagerly read the response body. For more information, see https://www.github.com/stainless-sdks/unlayer-python#with_streaming_response """ - return AsyncPagesV1ResourceWithStreamingResponse(self) + return AsyncV1ResourceWithStreamingResponse(self) async def render_create( self, @@ -118,7 +118,7 @@ async def render_create( extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> PagesV1RenderCreateResponse: + ) -> V1RenderCreateResponse: """ Convert page design JSON to HTML with optional merge tags. @@ -142,46 +142,46 @@ async def render_create( "design": design, "merge_tags": merge_tags, }, - pages_v1_render_create_params.PagesV1RenderCreateParams, + v1_render_create_params.V1RenderCreateParams, ), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), - cast_to=PagesV1RenderCreateResponse, + cast_to=V1RenderCreateResponse, ) -class PagesV1ResourceWithRawResponse: - def __init__(self, pages_v1: PagesV1Resource) -> None: - self._pages_v1 = pages_v1 +class V1ResourceWithRawResponse: + def __init__(self, v1: V1Resource) -> None: + self._v1 = v1 self.render_create = to_raw_response_wrapper( - pages_v1.render_create, + v1.render_create, ) -class AsyncPagesV1ResourceWithRawResponse: - def __init__(self, pages_v1: AsyncPagesV1Resource) -> None: - self._pages_v1 = pages_v1 +class AsyncV1ResourceWithRawResponse: + def __init__(self, v1: AsyncV1Resource) -> None: + self._v1 = v1 self.render_create = async_to_raw_response_wrapper( - pages_v1.render_create, + v1.render_create, ) -class PagesV1ResourceWithStreamingResponse: - def __init__(self, pages_v1: PagesV1Resource) -> None: - self._pages_v1 = pages_v1 +class V1ResourceWithStreamingResponse: + def __init__(self, v1: V1Resource) -> None: + self._v1 = v1 self.render_create = to_streamed_response_wrapper( - pages_v1.render_create, + v1.render_create, ) -class AsyncPagesV1ResourceWithStreamingResponse: - def __init__(self, pages_v1: AsyncPagesV1Resource) -> None: - self._pages_v1 = pages_v1 +class AsyncV1ResourceWithStreamingResponse: + def __init__(self, v1: AsyncV1Resource) -> None: + self._v1 = v1 self.render_create = async_to_streamed_response_wrapper( - pages_v1.render_create, + v1.render_create, ) diff --git a/src/unlayer/resources/project/__init__.py b/src/unlayer/resources/project/__init__.py new file mode 100644 index 0000000..a65f9f8 --- /dev/null +++ b/src/unlayer/resources/project/__init__.py @@ -0,0 +1,33 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from .v1 import ( + V1Resource, + AsyncV1Resource, + V1ResourceWithRawResponse, + AsyncV1ResourceWithRawResponse, + V1ResourceWithStreamingResponse, + AsyncV1ResourceWithStreamingResponse, +) +from .project import ( + ProjectResource, + AsyncProjectResource, + ProjectResourceWithRawResponse, + AsyncProjectResourceWithRawResponse, + ProjectResourceWithStreamingResponse, + AsyncProjectResourceWithStreamingResponse, +) + +__all__ = [ + "V1Resource", + "AsyncV1Resource", + "V1ResourceWithRawResponse", + "AsyncV1ResourceWithRawResponse", + "V1ResourceWithStreamingResponse", + "AsyncV1ResourceWithStreamingResponse", + "ProjectResource", + "AsyncProjectResource", + "ProjectResourceWithRawResponse", + "AsyncProjectResourceWithRawResponse", + "ProjectResourceWithStreamingResponse", + "AsyncProjectResourceWithStreamingResponse", +] diff --git a/src/unlayer/resources/project.py b/src/unlayer/resources/project/project.py similarity index 95% rename from src/unlayer/resources/project.py rename to src/unlayer/resources/project/project.py index bea717d..e7f096a 100644 --- a/src/unlayer/resources/project.py +++ b/src/unlayer/resources/project/project.py @@ -4,7 +4,15 @@ import httpx -from ..types import ( +from .v1 import ( + V1Resource, + AsyncV1Resource, + V1ResourceWithRawResponse, + AsyncV1ResourceWithRawResponse, + V1ResourceWithStreamingResponse, + AsyncV1ResourceWithStreamingResponse, +) +from ...types import ( project_domains_create_params, project_domains_update_params, project_api_keys_create_params, @@ -12,35 +20,39 @@ project_templates_create_params, project_templates_update_params, ) -from .._types import Body, Omit, Query, Headers, NoneType, NotGiven, SequenceNotStr, omit, not_given -from .._utils import maybe_transform, async_maybe_transform -from .._compat import cached_property -from .._resource import SyncAPIResource, AsyncAPIResource -from .._response import ( +from ..._types import Body, Omit, Query, Headers, NoneType, NotGiven, SequenceNotStr, omit, not_given +from ..._utils import maybe_transform, async_maybe_transform +from ..._compat import cached_property +from ..._resource import SyncAPIResource, AsyncAPIResource +from ..._response import ( to_raw_response_wrapper, to_streamed_response_wrapper, async_to_raw_response_wrapper, async_to_streamed_response_wrapper, ) -from .._base_client import make_request_options -from ..types.project_current_list_response import ProjectCurrentListResponse -from ..types.project_domains_list_response import ProjectDomainsListResponse -from ..types.project_api_keys_list_response import ProjectAPIKeysListResponse -from ..types.project_domains_create_response import ProjectDomainsCreateResponse -from ..types.project_domains_update_response import ProjectDomainsUpdateResponse -from ..types.project_templates_list_response import ProjectTemplatesListResponse -from ..types.project_api_keys_create_response import ProjectAPIKeysCreateResponse -from ..types.project_api_keys_update_response import ProjectAPIKeysUpdateResponse -from ..types.project_domains_retrieve_response import ProjectDomainsRetrieveResponse -from ..types.project_templates_create_response import ProjectTemplatesCreateResponse -from ..types.project_templates_update_response import ProjectTemplatesUpdateResponse -from ..types.project_api_keys_retrieve_response import ProjectAPIKeysRetrieveResponse -from ..types.project_templates_retrieve_response import ProjectTemplatesRetrieveResponse +from ..._base_client import make_request_options +from ...types.project_current_list_response import ProjectCurrentListResponse +from ...types.project_domains_list_response import ProjectDomainsListResponse +from ...types.project_api_keys_list_response import ProjectAPIKeysListResponse +from ...types.project_domains_create_response import ProjectDomainsCreateResponse +from ...types.project_domains_update_response import ProjectDomainsUpdateResponse +from ...types.project_templates_list_response import ProjectTemplatesListResponse +from ...types.project_api_keys_create_response import ProjectAPIKeysCreateResponse +from ...types.project_api_keys_update_response import ProjectAPIKeysUpdateResponse +from ...types.project_domains_retrieve_response import ProjectDomainsRetrieveResponse +from ...types.project_templates_create_response import ProjectTemplatesCreateResponse +from ...types.project_templates_update_response import ProjectTemplatesUpdateResponse +from ...types.project_api_keys_retrieve_response import ProjectAPIKeysRetrieveResponse +from ...types.project_templates_retrieve_response import ProjectTemplatesRetrieveResponse __all__ = ["ProjectResource", "AsyncProjectResource"] class ProjectResource(SyncAPIResource): + @cached_property + def v1(self) -> V1Resource: + return V1Resource(self._client) + @cached_property def with_raw_response(self) -> ProjectResourceWithRawResponse: """ @@ -600,6 +612,10 @@ def templates_update( class AsyncProjectResource(AsyncAPIResource): + @cached_property + def v1(self) -> AsyncV1Resource: + return AsyncV1Resource(self._client) + @cached_property def with_raw_response(self) -> AsyncProjectResourceWithRawResponse: """ @@ -1215,6 +1231,10 @@ def __init__(self, project: ProjectResource) -> None: project.templates_update, ) + @cached_property + def v1(self) -> V1ResourceWithRawResponse: + return V1ResourceWithRawResponse(self._project.v1) + class AsyncProjectResourceWithRawResponse: def __init__(self, project: AsyncProjectResource) -> None: @@ -1269,6 +1289,10 @@ def __init__(self, project: AsyncProjectResource) -> None: project.templates_update, ) + @cached_property + def v1(self) -> AsyncV1ResourceWithRawResponse: + return AsyncV1ResourceWithRawResponse(self._project.v1) + class ProjectResourceWithStreamingResponse: def __init__(self, project: ProjectResource) -> None: @@ -1323,6 +1347,10 @@ def __init__(self, project: ProjectResource) -> None: project.templates_update, ) + @cached_property + def v1(self) -> V1ResourceWithStreamingResponse: + return V1ResourceWithStreamingResponse(self._project.v1) + class AsyncProjectResourceWithStreamingResponse: def __init__(self, project: AsyncProjectResource) -> None: @@ -1376,3 +1404,7 @@ def __init__(self, project: AsyncProjectResource) -> None: self.templates_update = async_to_streamed_response_wrapper( project.templates_update, ) + + @cached_property + def v1(self) -> AsyncV1ResourceWithStreamingResponse: + return AsyncV1ResourceWithStreamingResponse(self._project.v1) diff --git a/src/unlayer/resources/project_v1.py b/src/unlayer/resources/project/v1.py similarity index 82% rename from src/unlayer/resources/project_v1.py rename to src/unlayer/resources/project/v1.py index 5752a5c..d98b566 100644 --- a/src/unlayer/resources/project_v1.py +++ b/src/unlayer/resources/project/v1.py @@ -4,61 +4,61 @@ import httpx -from ..types import ( - project_v1_domains_create_params, - project_v1_domains_update_params, - project_v1_api_keys_create_params, - project_v1_api_keys_update_params, - project_v1_templates_create_params, - project_v1_templates_update_params, -) -from .._types import Body, Omit, Query, Headers, NoneType, NotGiven, SequenceNotStr, omit, not_given -from .._utils import maybe_transform, async_maybe_transform -from .._compat import cached_property -from .._resource import SyncAPIResource, AsyncAPIResource -from .._response import ( +from ..._types import Body, Omit, Query, Headers, NoneType, NotGiven, SequenceNotStr, omit, not_given +from ..._utils import maybe_transform, async_maybe_transform +from ..._compat import cached_property +from ..._resource import SyncAPIResource, AsyncAPIResource +from ..._response import ( to_raw_response_wrapper, to_streamed_response_wrapper, async_to_raw_response_wrapper, async_to_streamed_response_wrapper, ) -from .._base_client import make_request_options -from ..types.project_v1_current_list_response import ProjectV1CurrentListResponse -from ..types.project_v1_domains_list_response import ProjectV1DomainsListResponse -from ..types.project_v1_api_keys_list_response import ProjectV1APIKeysListResponse -from ..types.project_v1_domains_create_response import ProjectV1DomainsCreateResponse -from ..types.project_v1_domains_update_response import ProjectV1DomainsUpdateResponse -from ..types.project_v1_templates_list_response import ProjectV1TemplatesListResponse -from ..types.project_v1_api_keys_create_response import ProjectV1APIKeysCreateResponse -from ..types.project_v1_api_keys_update_response import ProjectV1APIKeysUpdateResponse -from ..types.project_v1_domains_retrieve_response import ProjectV1DomainsRetrieveResponse -from ..types.project_v1_templates_create_response import ProjectV1TemplatesCreateResponse -from ..types.project_v1_templates_update_response import ProjectV1TemplatesUpdateResponse -from ..types.project_v1_api_keys_retrieve_response import ProjectV1APIKeysRetrieveResponse -from ..types.project_v1_templates_retrieve_response import ProjectV1TemplatesRetrieveResponse - -__all__ = ["ProjectV1Resource", "AsyncProjectV1Resource"] - - -class ProjectV1Resource(SyncAPIResource): +from ..._base_client import make_request_options +from ...types.project import ( + v1_domains_create_params, + v1_domains_update_params, + v1_api_keys_create_params, + v1_api_keys_update_params, + v1_templates_create_params, + v1_templates_update_params, +) +from ...types.project.v1_current_list_response import V1CurrentListResponse +from ...types.project.v1_domains_list_response import V1DomainsListResponse +from ...types.project.v1_api_keys_list_response import V1APIKeysListResponse +from ...types.project.v1_domains_create_response import V1DomainsCreateResponse +from ...types.project.v1_domains_update_response import V1DomainsUpdateResponse +from ...types.project.v1_templates_list_response import V1TemplatesListResponse +from ...types.project.v1_api_keys_create_response import V1APIKeysCreateResponse +from ...types.project.v1_api_keys_update_response import V1APIKeysUpdateResponse +from ...types.project.v1_domains_retrieve_response import V1DomainsRetrieveResponse +from ...types.project.v1_templates_create_response import V1TemplatesCreateResponse +from ...types.project.v1_templates_update_response import V1TemplatesUpdateResponse +from ...types.project.v1_api_keys_retrieve_response import V1APIKeysRetrieveResponse +from ...types.project.v1_templates_retrieve_response import V1TemplatesRetrieveResponse + +__all__ = ["V1Resource", "AsyncV1Resource"] + + +class V1Resource(SyncAPIResource): @cached_property - def with_raw_response(self) -> ProjectV1ResourceWithRawResponse: + def with_raw_response(self) -> V1ResourceWithRawResponse: """ This property can be used as a prefix for any HTTP method call to return the raw response object instead of the parsed content. For more information, see https://www.github.com/stainless-sdks/unlayer-python#accessing-raw-response-data-eg-headers """ - return ProjectV1ResourceWithRawResponse(self) + return V1ResourceWithRawResponse(self) @cached_property - def with_streaming_response(self) -> ProjectV1ResourceWithStreamingResponse: + def with_streaming_response(self) -> V1ResourceWithStreamingResponse: """ An alternative to `.with_raw_response` that doesn't eagerly read the response body. For more information, see https://www.github.com/stainless-sdks/unlayer-python#with_streaming_response """ - return ProjectV1ResourceWithStreamingResponse(self) + return V1ResourceWithStreamingResponse(self) def api_keys_create( self, @@ -71,7 +71,7 @@ def api_keys_create( extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> ProjectV1APIKeysCreateResponse: + ) -> V1APIKeysCreateResponse: """ Create a new API key for the project. @@ -95,12 +95,12 @@ def api_keys_create( "name": name, "domains": domains, }, - project_v1_api_keys_create_params.ProjectV1APIKeysCreateParams, + v1_api_keys_create_params.V1APIKeysCreateParams, ), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), - cast_to=ProjectV1APIKeysCreateResponse, + cast_to=V1APIKeysCreateResponse, ) def api_keys_delete( @@ -146,14 +146,14 @@ def api_keys_list( extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> ProjectV1APIKeysListResponse: + ) -> V1APIKeysListResponse: """List all API keys for the project.""" return self._get( "/project/v1/api-keys", options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), - cast_to=ProjectV1APIKeysListResponse, + cast_to=V1APIKeysListResponse, ) def api_keys_retrieve( @@ -166,7 +166,7 @@ def api_keys_retrieve( extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> ProjectV1APIKeysRetrieveResponse: + ) -> V1APIKeysRetrieveResponse: """ Get API key details by ID. @@ -186,7 +186,7 @@ def api_keys_retrieve( options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), - cast_to=ProjectV1APIKeysRetrieveResponse, + cast_to=V1APIKeysRetrieveResponse, ) def api_keys_update( @@ -202,7 +202,7 @@ def api_keys_update( extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> ProjectV1APIKeysUpdateResponse: + ) -> V1APIKeysUpdateResponse: """ Update API key settings. @@ -231,12 +231,12 @@ def api_keys_update( "domains": domains, "name": name, }, - project_v1_api_keys_update_params.ProjectV1APIKeysUpdateParams, + v1_api_keys_update_params.V1APIKeysUpdateParams, ), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), - cast_to=ProjectV1APIKeysUpdateResponse, + cast_to=V1APIKeysUpdateResponse, ) def current_list( @@ -248,14 +248,14 @@ def current_list( extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> ProjectV1CurrentListResponse: + ) -> V1CurrentListResponse: """Get project details for the authenticated project.""" return self._get( "/project/v1/current", options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), - cast_to=ProjectV1CurrentListResponse, + cast_to=V1CurrentListResponse, ) def domains_create( @@ -268,7 +268,7 @@ def domains_create( extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> ProjectV1DomainsCreateResponse: + ) -> V1DomainsCreateResponse: """ Add a new domain to the project. @@ -285,11 +285,11 @@ def domains_create( """ return self._post( "/project/v1/domains", - body=maybe_transform({"domain": domain}, project_v1_domains_create_params.ProjectV1DomainsCreateParams), + body=maybe_transform({"domain": domain}, v1_domains_create_params.V1DomainsCreateParams), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), - cast_to=ProjectV1DomainsCreateResponse, + cast_to=V1DomainsCreateResponse, ) def domains_delete( @@ -335,14 +335,14 @@ def domains_list( extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> ProjectV1DomainsListResponse: + ) -> V1DomainsListResponse: """List all domains for the project.""" return self._get( "/project/v1/domains", options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), - cast_to=ProjectV1DomainsListResponse, + cast_to=V1DomainsListResponse, ) def domains_retrieve( @@ -355,7 +355,7 @@ def domains_retrieve( extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> ProjectV1DomainsRetrieveResponse: + ) -> V1DomainsRetrieveResponse: """ Get domain details by ID. @@ -375,7 +375,7 @@ def domains_retrieve( options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), - cast_to=ProjectV1DomainsRetrieveResponse, + cast_to=V1DomainsRetrieveResponse, ) def domains_update( @@ -389,7 +389,7 @@ def domains_update( extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> ProjectV1DomainsUpdateResponse: + ) -> V1DomainsUpdateResponse: """ Update domain settings. @@ -408,11 +408,11 @@ def domains_update( raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return self._put( f"/project/v1/domains/{id}", - body=maybe_transform({"domain": domain}, project_v1_domains_update_params.ProjectV1DomainsUpdateParams), + body=maybe_transform({"domain": domain}, v1_domains_update_params.V1DomainsUpdateParams), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), - cast_to=ProjectV1DomainsUpdateResponse, + cast_to=V1DomainsUpdateResponse, ) def templates_create( @@ -427,7 +427,7 @@ def templates_create( extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> ProjectV1TemplatesCreateResponse: + ) -> V1TemplatesCreateResponse: """ Create a new project template. @@ -454,12 +454,12 @@ def templates_create( "body": body, "subject": subject, }, - project_v1_templates_create_params.ProjectV1TemplatesCreateParams, + v1_templates_create_params.V1TemplatesCreateParams, ), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), - cast_to=ProjectV1TemplatesCreateResponse, + cast_to=V1TemplatesCreateResponse, ) def templates_delete( @@ -505,14 +505,14 @@ def templates_list( extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> ProjectV1TemplatesListResponse: + ) -> V1TemplatesListResponse: """Get all project templates.""" return self._get( "/project/v1/templates", options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), - cast_to=ProjectV1TemplatesListResponse, + cast_to=V1TemplatesListResponse, ) def templates_retrieve( @@ -525,7 +525,7 @@ def templates_retrieve( extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> ProjectV1TemplatesRetrieveResponse: + ) -> V1TemplatesRetrieveResponse: """ Get project template by ID. @@ -545,7 +545,7 @@ def templates_retrieve( options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), - cast_to=ProjectV1TemplatesRetrieveResponse, + cast_to=V1TemplatesRetrieveResponse, ) def templates_update( @@ -561,7 +561,7 @@ def templates_update( extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> ProjectV1TemplatesUpdateResponse: + ) -> V1TemplatesUpdateResponse: """ Update project template. @@ -590,34 +590,34 @@ def templates_update( "name": name, "subject": subject, }, - project_v1_templates_update_params.ProjectV1TemplatesUpdateParams, + v1_templates_update_params.V1TemplatesUpdateParams, ), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), - cast_to=ProjectV1TemplatesUpdateResponse, + cast_to=V1TemplatesUpdateResponse, ) -class AsyncProjectV1Resource(AsyncAPIResource): +class AsyncV1Resource(AsyncAPIResource): @cached_property - def with_raw_response(self) -> AsyncProjectV1ResourceWithRawResponse: + def with_raw_response(self) -> AsyncV1ResourceWithRawResponse: """ This property can be used as a prefix for any HTTP method call to return the raw response object instead of the parsed content. For more information, see https://www.github.com/stainless-sdks/unlayer-python#accessing-raw-response-data-eg-headers """ - return AsyncProjectV1ResourceWithRawResponse(self) + return AsyncV1ResourceWithRawResponse(self) @cached_property - def with_streaming_response(self) -> AsyncProjectV1ResourceWithStreamingResponse: + def with_streaming_response(self) -> AsyncV1ResourceWithStreamingResponse: """ An alternative to `.with_raw_response` that doesn't eagerly read the response body. For more information, see https://www.github.com/stainless-sdks/unlayer-python#with_streaming_response """ - return AsyncProjectV1ResourceWithStreamingResponse(self) + return AsyncV1ResourceWithStreamingResponse(self) async def api_keys_create( self, @@ -630,7 +630,7 @@ async def api_keys_create( extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> ProjectV1APIKeysCreateResponse: + ) -> V1APIKeysCreateResponse: """ Create a new API key for the project. @@ -654,12 +654,12 @@ async def api_keys_create( "name": name, "domains": domains, }, - project_v1_api_keys_create_params.ProjectV1APIKeysCreateParams, + v1_api_keys_create_params.V1APIKeysCreateParams, ), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), - cast_to=ProjectV1APIKeysCreateResponse, + cast_to=V1APIKeysCreateResponse, ) async def api_keys_delete( @@ -705,14 +705,14 @@ async def api_keys_list( extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> ProjectV1APIKeysListResponse: + ) -> V1APIKeysListResponse: """List all API keys for the project.""" return await self._get( "/project/v1/api-keys", options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), - cast_to=ProjectV1APIKeysListResponse, + cast_to=V1APIKeysListResponse, ) async def api_keys_retrieve( @@ -725,7 +725,7 @@ async def api_keys_retrieve( extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> ProjectV1APIKeysRetrieveResponse: + ) -> V1APIKeysRetrieveResponse: """ Get API key details by ID. @@ -745,7 +745,7 @@ async def api_keys_retrieve( options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), - cast_to=ProjectV1APIKeysRetrieveResponse, + cast_to=V1APIKeysRetrieveResponse, ) async def api_keys_update( @@ -761,7 +761,7 @@ async def api_keys_update( extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> ProjectV1APIKeysUpdateResponse: + ) -> V1APIKeysUpdateResponse: """ Update API key settings. @@ -790,12 +790,12 @@ async def api_keys_update( "domains": domains, "name": name, }, - project_v1_api_keys_update_params.ProjectV1APIKeysUpdateParams, + v1_api_keys_update_params.V1APIKeysUpdateParams, ), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), - cast_to=ProjectV1APIKeysUpdateResponse, + cast_to=V1APIKeysUpdateResponse, ) async def current_list( @@ -807,14 +807,14 @@ async def current_list( extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> ProjectV1CurrentListResponse: + ) -> V1CurrentListResponse: """Get project details for the authenticated project.""" return await self._get( "/project/v1/current", options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), - cast_to=ProjectV1CurrentListResponse, + cast_to=V1CurrentListResponse, ) async def domains_create( @@ -827,7 +827,7 @@ async def domains_create( extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> ProjectV1DomainsCreateResponse: + ) -> V1DomainsCreateResponse: """ Add a new domain to the project. @@ -844,13 +844,11 @@ async def domains_create( """ return await self._post( "/project/v1/domains", - body=await async_maybe_transform( - {"domain": domain}, project_v1_domains_create_params.ProjectV1DomainsCreateParams - ), + body=await async_maybe_transform({"domain": domain}, v1_domains_create_params.V1DomainsCreateParams), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), - cast_to=ProjectV1DomainsCreateResponse, + cast_to=V1DomainsCreateResponse, ) async def domains_delete( @@ -896,14 +894,14 @@ async def domains_list( extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> ProjectV1DomainsListResponse: + ) -> V1DomainsListResponse: """List all domains for the project.""" return await self._get( "/project/v1/domains", options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), - cast_to=ProjectV1DomainsListResponse, + cast_to=V1DomainsListResponse, ) async def domains_retrieve( @@ -916,7 +914,7 @@ async def domains_retrieve( extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> ProjectV1DomainsRetrieveResponse: + ) -> V1DomainsRetrieveResponse: """ Get domain details by ID. @@ -936,7 +934,7 @@ async def domains_retrieve( options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), - cast_to=ProjectV1DomainsRetrieveResponse, + cast_to=V1DomainsRetrieveResponse, ) async def domains_update( @@ -950,7 +948,7 @@ async def domains_update( extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> ProjectV1DomainsUpdateResponse: + ) -> V1DomainsUpdateResponse: """ Update domain settings. @@ -969,13 +967,11 @@ async def domains_update( raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return await self._put( f"/project/v1/domains/{id}", - body=await async_maybe_transform( - {"domain": domain}, project_v1_domains_update_params.ProjectV1DomainsUpdateParams - ), + body=await async_maybe_transform({"domain": domain}, v1_domains_update_params.V1DomainsUpdateParams), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), - cast_to=ProjectV1DomainsUpdateResponse, + cast_to=V1DomainsUpdateResponse, ) async def templates_create( @@ -990,7 +986,7 @@ async def templates_create( extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> ProjectV1TemplatesCreateResponse: + ) -> V1TemplatesCreateResponse: """ Create a new project template. @@ -1017,12 +1013,12 @@ async def templates_create( "body": body, "subject": subject, }, - project_v1_templates_create_params.ProjectV1TemplatesCreateParams, + v1_templates_create_params.V1TemplatesCreateParams, ), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), - cast_to=ProjectV1TemplatesCreateResponse, + cast_to=V1TemplatesCreateResponse, ) async def templates_delete( @@ -1068,14 +1064,14 @@ async def templates_list( extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> ProjectV1TemplatesListResponse: + ) -> V1TemplatesListResponse: """Get all project templates.""" return await self._get( "/project/v1/templates", options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), - cast_to=ProjectV1TemplatesListResponse, + cast_to=V1TemplatesListResponse, ) async def templates_retrieve( @@ -1088,7 +1084,7 @@ async def templates_retrieve( extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> ProjectV1TemplatesRetrieveResponse: + ) -> V1TemplatesRetrieveResponse: """ Get project template by ID. @@ -1108,7 +1104,7 @@ async def templates_retrieve( options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), - cast_to=ProjectV1TemplatesRetrieveResponse, + cast_to=V1TemplatesRetrieveResponse, ) async def templates_update( @@ -1124,7 +1120,7 @@ async def templates_update( extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> ProjectV1TemplatesUpdateResponse: + ) -> V1TemplatesUpdateResponse: """ Update project template. @@ -1153,226 +1149,226 @@ async def templates_update( "name": name, "subject": subject, }, - project_v1_templates_update_params.ProjectV1TemplatesUpdateParams, + v1_templates_update_params.V1TemplatesUpdateParams, ), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), - cast_to=ProjectV1TemplatesUpdateResponse, + cast_to=V1TemplatesUpdateResponse, ) -class ProjectV1ResourceWithRawResponse: - def __init__(self, project_v1: ProjectV1Resource) -> None: - self._project_v1 = project_v1 +class V1ResourceWithRawResponse: + def __init__(self, v1: V1Resource) -> None: + self._v1 = v1 self.api_keys_create = to_raw_response_wrapper( - project_v1.api_keys_create, + v1.api_keys_create, ) self.api_keys_delete = to_raw_response_wrapper( - project_v1.api_keys_delete, + v1.api_keys_delete, ) self.api_keys_list = to_raw_response_wrapper( - project_v1.api_keys_list, + v1.api_keys_list, ) self.api_keys_retrieve = to_raw_response_wrapper( - project_v1.api_keys_retrieve, + v1.api_keys_retrieve, ) self.api_keys_update = to_raw_response_wrapper( - project_v1.api_keys_update, + v1.api_keys_update, ) self.current_list = to_raw_response_wrapper( - project_v1.current_list, + v1.current_list, ) self.domains_create = to_raw_response_wrapper( - project_v1.domains_create, + v1.domains_create, ) self.domains_delete = to_raw_response_wrapper( - project_v1.domains_delete, + v1.domains_delete, ) self.domains_list = to_raw_response_wrapper( - project_v1.domains_list, + v1.domains_list, ) self.domains_retrieve = to_raw_response_wrapper( - project_v1.domains_retrieve, + v1.domains_retrieve, ) self.domains_update = to_raw_response_wrapper( - project_v1.domains_update, + v1.domains_update, ) self.templates_create = to_raw_response_wrapper( - project_v1.templates_create, + v1.templates_create, ) self.templates_delete = to_raw_response_wrapper( - project_v1.templates_delete, + v1.templates_delete, ) self.templates_list = to_raw_response_wrapper( - project_v1.templates_list, + v1.templates_list, ) self.templates_retrieve = to_raw_response_wrapper( - project_v1.templates_retrieve, + v1.templates_retrieve, ) self.templates_update = to_raw_response_wrapper( - project_v1.templates_update, + v1.templates_update, ) -class AsyncProjectV1ResourceWithRawResponse: - def __init__(self, project_v1: AsyncProjectV1Resource) -> None: - self._project_v1 = project_v1 +class AsyncV1ResourceWithRawResponse: + def __init__(self, v1: AsyncV1Resource) -> None: + self._v1 = v1 self.api_keys_create = async_to_raw_response_wrapper( - project_v1.api_keys_create, + v1.api_keys_create, ) self.api_keys_delete = async_to_raw_response_wrapper( - project_v1.api_keys_delete, + v1.api_keys_delete, ) self.api_keys_list = async_to_raw_response_wrapper( - project_v1.api_keys_list, + v1.api_keys_list, ) self.api_keys_retrieve = async_to_raw_response_wrapper( - project_v1.api_keys_retrieve, + v1.api_keys_retrieve, ) self.api_keys_update = async_to_raw_response_wrapper( - project_v1.api_keys_update, + v1.api_keys_update, ) self.current_list = async_to_raw_response_wrapper( - project_v1.current_list, + v1.current_list, ) self.domains_create = async_to_raw_response_wrapper( - project_v1.domains_create, + v1.domains_create, ) self.domains_delete = async_to_raw_response_wrapper( - project_v1.domains_delete, + v1.domains_delete, ) self.domains_list = async_to_raw_response_wrapper( - project_v1.domains_list, + v1.domains_list, ) self.domains_retrieve = async_to_raw_response_wrapper( - project_v1.domains_retrieve, + v1.domains_retrieve, ) self.domains_update = async_to_raw_response_wrapper( - project_v1.domains_update, + v1.domains_update, ) self.templates_create = async_to_raw_response_wrapper( - project_v1.templates_create, + v1.templates_create, ) self.templates_delete = async_to_raw_response_wrapper( - project_v1.templates_delete, + v1.templates_delete, ) self.templates_list = async_to_raw_response_wrapper( - project_v1.templates_list, + v1.templates_list, ) self.templates_retrieve = async_to_raw_response_wrapper( - project_v1.templates_retrieve, + v1.templates_retrieve, ) self.templates_update = async_to_raw_response_wrapper( - project_v1.templates_update, + v1.templates_update, ) -class ProjectV1ResourceWithStreamingResponse: - def __init__(self, project_v1: ProjectV1Resource) -> None: - self._project_v1 = project_v1 +class V1ResourceWithStreamingResponse: + def __init__(self, v1: V1Resource) -> None: + self._v1 = v1 self.api_keys_create = to_streamed_response_wrapper( - project_v1.api_keys_create, + v1.api_keys_create, ) self.api_keys_delete = to_streamed_response_wrapper( - project_v1.api_keys_delete, + v1.api_keys_delete, ) self.api_keys_list = to_streamed_response_wrapper( - project_v1.api_keys_list, + v1.api_keys_list, ) self.api_keys_retrieve = to_streamed_response_wrapper( - project_v1.api_keys_retrieve, + v1.api_keys_retrieve, ) self.api_keys_update = to_streamed_response_wrapper( - project_v1.api_keys_update, + v1.api_keys_update, ) self.current_list = to_streamed_response_wrapper( - project_v1.current_list, + v1.current_list, ) self.domains_create = to_streamed_response_wrapper( - project_v1.domains_create, + v1.domains_create, ) self.domains_delete = to_streamed_response_wrapper( - project_v1.domains_delete, + v1.domains_delete, ) self.domains_list = to_streamed_response_wrapper( - project_v1.domains_list, + v1.domains_list, ) self.domains_retrieve = to_streamed_response_wrapper( - project_v1.domains_retrieve, + v1.domains_retrieve, ) self.domains_update = to_streamed_response_wrapper( - project_v1.domains_update, + v1.domains_update, ) self.templates_create = to_streamed_response_wrapper( - project_v1.templates_create, + v1.templates_create, ) self.templates_delete = to_streamed_response_wrapper( - project_v1.templates_delete, + v1.templates_delete, ) self.templates_list = to_streamed_response_wrapper( - project_v1.templates_list, + v1.templates_list, ) self.templates_retrieve = to_streamed_response_wrapper( - project_v1.templates_retrieve, + v1.templates_retrieve, ) self.templates_update = to_streamed_response_wrapper( - project_v1.templates_update, + v1.templates_update, ) -class AsyncProjectV1ResourceWithStreamingResponse: - def __init__(self, project_v1: AsyncProjectV1Resource) -> None: - self._project_v1 = project_v1 +class AsyncV1ResourceWithStreamingResponse: + def __init__(self, v1: AsyncV1Resource) -> None: + self._v1 = v1 self.api_keys_create = async_to_streamed_response_wrapper( - project_v1.api_keys_create, + v1.api_keys_create, ) self.api_keys_delete = async_to_streamed_response_wrapper( - project_v1.api_keys_delete, + v1.api_keys_delete, ) self.api_keys_list = async_to_streamed_response_wrapper( - project_v1.api_keys_list, + v1.api_keys_list, ) self.api_keys_retrieve = async_to_streamed_response_wrapper( - project_v1.api_keys_retrieve, + v1.api_keys_retrieve, ) self.api_keys_update = async_to_streamed_response_wrapper( - project_v1.api_keys_update, + v1.api_keys_update, ) self.current_list = async_to_streamed_response_wrapper( - project_v1.current_list, + v1.current_list, ) self.domains_create = async_to_streamed_response_wrapper( - project_v1.domains_create, + v1.domains_create, ) self.domains_delete = async_to_streamed_response_wrapper( - project_v1.domains_delete, + v1.domains_delete, ) self.domains_list = async_to_streamed_response_wrapper( - project_v1.domains_list, + v1.domains_list, ) self.domains_retrieve = async_to_streamed_response_wrapper( - project_v1.domains_retrieve, + v1.domains_retrieve, ) self.domains_update = async_to_streamed_response_wrapper( - project_v1.domains_update, + v1.domains_update, ) self.templates_create = async_to_streamed_response_wrapper( - project_v1.templates_create, + v1.templates_create, ) self.templates_delete = async_to_streamed_response_wrapper( - project_v1.templates_delete, + v1.templates_delete, ) self.templates_list = async_to_streamed_response_wrapper( - project_v1.templates_list, + v1.templates_list, ) self.templates_retrieve = async_to_streamed_response_wrapper( - project_v1.templates_retrieve, + v1.templates_retrieve, ) self.templates_update = async_to_streamed_response_wrapper( - project_v1.templates_update, + v1.templates_update, ) diff --git a/src/unlayer/types/__init__.py b/src/unlayer/types/__init__.py index 9e6dfd4..290242a 100644 --- a/src/unlayer/types/__init__.py +++ b/src/unlayer/types/__init__.py @@ -8,83 +8,36 @@ from .email_send_create_response import EmailSendCreateResponse as EmailSendCreateResponse from .page_render_create_response import PageRenderCreateResponse as PageRenderCreateResponse from .email_render_create_response import EmailRenderCreateResponse as EmailRenderCreateResponse -from .emails_v1_send_create_params import EmailsV1SendCreateParams as EmailsV1SendCreateParams -from .pages_v1_render_create_params import PagesV1RenderCreateParams as PagesV1RenderCreateParams from .project_current_list_response import ProjectCurrentListResponse as ProjectCurrentListResponse from .project_domains_create_params import ProjectDomainsCreateParams as ProjectDomainsCreateParams from .project_domains_list_response import ProjectDomainsListResponse as ProjectDomainsListResponse from .project_domains_update_params import ProjectDomainsUpdateParams as ProjectDomainsUpdateParams from .email_emails_retrieve_response import EmailEmailsRetrieveResponse as EmailEmailsRetrieveResponse -from .emails_v1_render_create_params import EmailsV1RenderCreateParams as EmailsV1RenderCreateParams -from .emails_v1_send_create_response import EmailsV1SendCreateResponse as EmailsV1SendCreateResponse from .project_api_keys_create_params import ProjectAPIKeysCreateParams as ProjectAPIKeysCreateParams from .project_api_keys_list_response import ProjectAPIKeysListResponse as ProjectAPIKeysListResponse from .project_api_keys_update_params import ProjectAPIKeysUpdateParams as ProjectAPIKeysUpdateParams from .document_generate_create_params import DocumentGenerateCreateParams as DocumentGenerateCreateParams -from .pages_v1_render_create_response import PagesV1RenderCreateResponse as PagesV1RenderCreateResponse from .project_domains_create_response import ProjectDomainsCreateResponse as ProjectDomainsCreateResponse from .project_domains_update_response import ProjectDomainsUpdateResponse as ProjectDomainsUpdateResponse from .project_templates_create_params import ProjectTemplatesCreateParams as ProjectTemplatesCreateParams from .project_templates_list_response import ProjectTemplatesListResponse as ProjectTemplatesListResponse from .project_templates_update_params import ProjectTemplatesUpdateParams as ProjectTemplatesUpdateParams -from .emails_v1_render_create_response import EmailsV1RenderCreateResponse as EmailsV1RenderCreateResponse from .project_api_keys_create_response import ProjectAPIKeysCreateResponse as ProjectAPIKeysCreateResponse from .project_api_keys_update_response import ProjectAPIKeysUpdateResponse as ProjectAPIKeysUpdateResponse -from .project_v1_current_list_response import ProjectV1CurrentListResponse as ProjectV1CurrentListResponse -from .project_v1_domains_create_params import ProjectV1DomainsCreateParams as ProjectV1DomainsCreateParams -from .project_v1_domains_list_response import ProjectV1DomainsListResponse as ProjectV1DomainsListResponse -from .project_v1_domains_update_params import ProjectV1DomainsUpdateParams as ProjectV1DomainsUpdateParams from .document_generate_create_response import DocumentGenerateCreateResponse as DocumentGenerateCreateResponse from .project_domains_retrieve_response import ProjectDomainsRetrieveResponse as ProjectDomainsRetrieveResponse from .project_templates_create_response import ProjectTemplatesCreateResponse as ProjectTemplatesCreateResponse from .project_templates_update_response import ProjectTemplatesUpdateResponse as ProjectTemplatesUpdateResponse -from .project_v1_api_keys_create_params import ProjectV1APIKeysCreateParams as ProjectV1APIKeysCreateParams -from .project_v1_api_keys_list_response import ProjectV1APIKeysListResponse as ProjectV1APIKeysListResponse -from .project_v1_api_keys_update_params import ProjectV1APIKeysUpdateParams as ProjectV1APIKeysUpdateParams -from .emails_v1_emails_retrieve_response import EmailsV1EmailsRetrieveResponse as EmailsV1EmailsRetrieveResponse from .project_api_keys_retrieve_response import ProjectAPIKeysRetrieveResponse as ProjectAPIKeysRetrieveResponse -from .project_v1_domains_create_response import ProjectV1DomainsCreateResponse as ProjectV1DomainsCreateResponse -from .project_v1_domains_update_response import ProjectV1DomainsUpdateResponse as ProjectV1DomainsUpdateResponse -from .project_v1_templates_create_params import ProjectV1TemplatesCreateParams as ProjectV1TemplatesCreateParams -from .project_v1_templates_list_response import ProjectV1TemplatesListResponse as ProjectV1TemplatesListResponse -from .project_v1_templates_update_params import ProjectV1TemplatesUpdateParams as ProjectV1TemplatesUpdateParams -from .documents_v1_generate_create_params import DocumentsV1GenerateCreateParams as DocumentsV1GenerateCreateParams from .email_send_template_template_params import EmailSendTemplateTemplateParams as EmailSendTemplateTemplateParams from .project_templates_retrieve_response import ProjectTemplatesRetrieveResponse as ProjectTemplatesRetrieveResponse -from .project_v1_api_keys_create_response import ProjectV1APIKeysCreateResponse as ProjectV1APIKeysCreateResponse -from .project_v1_api_keys_update_response import ProjectV1APIKeysUpdateResponse as ProjectV1APIKeysUpdateResponse from .document_documents_retrieve_response import DocumentDocumentsRetrieveResponse as DocumentDocumentsRetrieveResponse -from .project_v1_domains_retrieve_response import ProjectV1DomainsRetrieveResponse as ProjectV1DomainsRetrieveResponse -from .project_v1_templates_create_response import ProjectV1TemplatesCreateResponse as ProjectV1TemplatesCreateResponse -from .project_v1_templates_update_response import ProjectV1TemplatesUpdateResponse as ProjectV1TemplatesUpdateResponse -from .documents_v1_generate_create_response import ( - DocumentsV1GenerateCreateResponse as DocumentsV1GenerateCreateResponse, -) from .email_send_template_template_response import ( EmailSendTemplateTemplateResponse as EmailSendTemplateTemplateResponse, ) -from .project_v1_api_keys_retrieve_response import ProjectV1APIKeysRetrieveResponse as ProjectV1APIKeysRetrieveResponse -from .project_v1_templates_retrieve_response import ( - ProjectV1TemplatesRetrieveResponse as ProjectV1TemplatesRetrieveResponse, -) -from .emails_v1_send_template_template_params import ( - EmailsV1SendTemplateTemplateParams as EmailsV1SendTemplateTemplateParams, -) -from .documents_v1_documents_retrieve_response import ( - DocumentsV1DocumentsRetrieveResponse as DocumentsV1DocumentsRetrieveResponse, -) -from .emails_v1_send_template_template_response import ( - EmailsV1SendTemplateTemplateResponse as EmailsV1SendTemplateTemplateResponse, -) from .document_generate_template_template_params import ( DocumentGenerateTemplateTemplateParams as DocumentGenerateTemplateTemplateParams, ) from .document_generate_template_template_response import ( DocumentGenerateTemplateTemplateResponse as DocumentGenerateTemplateTemplateResponse, ) -from .documents_v1_generate_template_template_params import ( - DocumentsV1GenerateTemplateTemplateParams as DocumentsV1GenerateTemplateTemplateParams, -) -from .documents_v1_generate_template_template_response import ( - DocumentsV1GenerateTemplateTemplateResponse as DocumentsV1GenerateTemplateTemplateResponse, -) diff --git a/src/unlayer/types/documents/__init__.py b/src/unlayer/types/documents/__init__.py new file mode 100644 index 0000000..987e2ee --- /dev/null +++ b/src/unlayer/types/documents/__init__.py @@ -0,0 +1,11 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from .v1_generate_create_params import V1GenerateCreateParams as V1GenerateCreateParams +from .v1_generate_create_response import V1GenerateCreateResponse as V1GenerateCreateResponse +from .v1_documents_retrieve_response import V1DocumentsRetrieveResponse as V1DocumentsRetrieveResponse +from .v1_generate_template_template_params import V1GenerateTemplateTemplateParams as V1GenerateTemplateTemplateParams +from .v1_generate_template_template_response import ( + V1GenerateTemplateTemplateResponse as V1GenerateTemplateTemplateResponse, +) diff --git a/src/unlayer/types/documents_v1_documents_retrieve_response.py b/src/unlayer/types/documents/v1_documents_retrieve_response.py similarity index 88% rename from src/unlayer/types/documents_v1_documents_retrieve_response.py rename to src/unlayer/types/documents/v1_documents_retrieve_response.py index 88ba0d4..119146c 100644 --- a/src/unlayer/types/documents_v1_documents_retrieve_response.py +++ b/src/unlayer/types/documents/v1_documents_retrieve_response.py @@ -6,12 +6,12 @@ from pydantic import Field as FieldInfo -from .._models import BaseModel +from ..._models import BaseModel -__all__ = ["DocumentsV1DocumentsRetrieveResponse"] +__all__ = ["V1DocumentsRetrieveResponse"] -class DocumentsV1DocumentsRetrieveResponse(BaseModel): +class V1DocumentsRetrieveResponse(BaseModel): id: Optional[str] = None """Document ID""" diff --git a/src/unlayer/types/documents_v1_generate_create_params.py b/src/unlayer/types/documents/v1_generate_create_params.py similarity index 80% rename from src/unlayer/types/documents_v1_generate_create_params.py rename to src/unlayer/types/documents/v1_generate_create_params.py index fb63fdc..3ae71b7 100644 --- a/src/unlayer/types/documents_v1_generate_create_params.py +++ b/src/unlayer/types/documents/v1_generate_create_params.py @@ -5,12 +5,12 @@ from typing import Dict from typing_extensions import Required, Annotated, TypedDict -from .._utils import PropertyInfo +from ..._utils import PropertyInfo -__all__ = ["DocumentsV1GenerateCreateParams"] +__all__ = ["V1GenerateCreateParams"] -class DocumentsV1GenerateCreateParams(TypedDict, total=False): +class V1GenerateCreateParams(TypedDict, total=False): design: Required[Dict[str, object]] """Proprietary design format JSON""" diff --git a/src/unlayer/types/documents_v1_generate_create_response.py b/src/unlayer/types/documents/v1_generate_create_response.py similarity index 81% rename from src/unlayer/types/documents_v1_generate_create_response.py rename to src/unlayer/types/documents/v1_generate_create_response.py index 0c162cc..754837d 100644 --- a/src/unlayer/types/documents_v1_generate_create_response.py +++ b/src/unlayer/types/documents/v1_generate_create_response.py @@ -5,12 +5,12 @@ from pydantic import Field as FieldInfo -from .._models import BaseModel +from ..._models import BaseModel -__all__ = ["DocumentsV1GenerateCreateResponse"] +__all__ = ["V1GenerateCreateResponse"] -class DocumentsV1GenerateCreateResponse(BaseModel): +class V1GenerateCreateResponse(BaseModel): document_id: Optional[str] = FieldInfo(alias="documentId", default=None) """Unique document identifier""" diff --git a/src/unlayer/types/documents_v1_generate_template_template_params.py b/src/unlayer/types/documents/v1_generate_template_template_params.py similarity index 76% rename from src/unlayer/types/documents_v1_generate_template_template_params.py rename to src/unlayer/types/documents/v1_generate_template_template_params.py index 8fdc116..ca9e978 100644 --- a/src/unlayer/types/documents_v1_generate_template_template_params.py +++ b/src/unlayer/types/documents/v1_generate_template_template_params.py @@ -5,12 +5,12 @@ from typing import Dict from typing_extensions import Required, Annotated, TypedDict -from .._utils import PropertyInfo +from ..._utils import PropertyInfo -__all__ = ["DocumentsV1GenerateTemplateTemplateParams"] +__all__ = ["V1GenerateTemplateTemplateParams"] -class DocumentsV1GenerateTemplateTemplateParams(TypedDict, total=False): +class V1GenerateTemplateTemplateParams(TypedDict, total=False): template_id: Required[Annotated[str, PropertyInfo(alias="templateId")]] """ID of the template to use for generation""" diff --git a/src/unlayer/types/documents_v1_generate_template_template_response.py b/src/unlayer/types/documents/v1_generate_template_template_response.py similarity index 78% rename from src/unlayer/types/documents_v1_generate_template_template_response.py rename to src/unlayer/types/documents/v1_generate_template_template_response.py index e73822c..013c170 100644 --- a/src/unlayer/types/documents_v1_generate_template_template_response.py +++ b/src/unlayer/types/documents/v1_generate_template_template_response.py @@ -5,12 +5,12 @@ from pydantic import Field as FieldInfo -from .._models import BaseModel +from ..._models import BaseModel -__all__ = ["DocumentsV1GenerateTemplateTemplateResponse"] +__all__ = ["V1GenerateTemplateTemplateResponse"] -class DocumentsV1GenerateTemplateTemplateResponse(BaseModel): +class V1GenerateTemplateTemplateResponse(BaseModel): document_id: Optional[str] = FieldInfo(alias="documentId", default=None) """Unique document identifier""" diff --git a/src/unlayer/types/emails/__init__.py b/src/unlayer/types/emails/__init__.py new file mode 100644 index 0000000..813185f --- /dev/null +++ b/src/unlayer/types/emails/__init__.py @@ -0,0 +1,11 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from .v1_send_create_params import V1SendCreateParams as V1SendCreateParams +from .v1_render_create_params import V1RenderCreateParams as V1RenderCreateParams +from .v1_send_create_response import V1SendCreateResponse as V1SendCreateResponse +from .v1_render_create_response import V1RenderCreateResponse as V1RenderCreateResponse +from .v1_emails_retrieve_response import V1EmailsRetrieveResponse as V1EmailsRetrieveResponse +from .v1_send_template_template_params import V1SendTemplateTemplateParams as V1SendTemplateTemplateParams +from .v1_send_template_template_response import V1SendTemplateTemplateResponse as V1SendTemplateTemplateResponse diff --git a/src/unlayer/types/emails_v1_emails_retrieve_response.py b/src/unlayer/types/emails/v1_emails_retrieve_response.py similarity index 85% rename from src/unlayer/types/emails_v1_emails_retrieve_response.py rename to src/unlayer/types/emails/v1_emails_retrieve_response.py index 3bba362..7d30a1f 100644 --- a/src/unlayer/types/emails_v1_emails_retrieve_response.py +++ b/src/unlayer/types/emails/v1_emails_retrieve_response.py @@ -6,12 +6,12 @@ from pydantic import Field as FieldInfo -from .._models import BaseModel +from ..._models import BaseModel -__all__ = ["EmailsV1EmailsRetrieveResponse"] +__all__ = ["V1EmailsRetrieveResponse"] -class EmailsV1EmailsRetrieveResponse(BaseModel): +class V1EmailsRetrieveResponse(BaseModel): id: Optional[str] = None """Email message ID""" diff --git a/src/unlayer/types/pages_v1_render_create_params.py b/src/unlayer/types/emails/v1_render_create_params.py similarity index 76% rename from src/unlayer/types/pages_v1_render_create_params.py rename to src/unlayer/types/emails/v1_render_create_params.py index 605f9f7..9fcd33c 100644 --- a/src/unlayer/types/pages_v1_render_create_params.py +++ b/src/unlayer/types/emails/v1_render_create_params.py @@ -5,12 +5,12 @@ from typing import Dict from typing_extensions import Required, Annotated, TypedDict -from .._utils import PropertyInfo +from ..._utils import PropertyInfo -__all__ = ["PagesV1RenderCreateParams"] +__all__ = ["V1RenderCreateParams"] -class PagesV1RenderCreateParams(TypedDict, total=False): +class V1RenderCreateParams(TypedDict, total=False): design: Required[Dict[str, object]] """Proprietary design format JSON""" diff --git a/src/unlayer/types/pages_v1_render_create_response.py b/src/unlayer/types/emails/v1_render_create_response.py similarity index 60% rename from src/unlayer/types/pages_v1_render_create_response.py rename to src/unlayer/types/emails/v1_render_create_response.py index 3c8e001..fadb69c 100644 --- a/src/unlayer/types/pages_v1_render_create_response.py +++ b/src/unlayer/types/emails/v1_render_create_response.py @@ -2,11 +2,11 @@ from typing import Optional -from .._models import BaseModel +from ..._models import BaseModel -__all__ = ["PagesV1RenderCreateResponse"] +__all__ = ["V1RenderCreateResponse"] -class PagesV1RenderCreateResponse(BaseModel): +class V1RenderCreateResponse(BaseModel): html: Optional[str] = None """Rendered HTML content""" diff --git a/src/unlayer/types/emails_v1_send_create_params.py b/src/unlayer/types/emails/v1_send_create_params.py similarity index 81% rename from src/unlayer/types/emails_v1_send_create_params.py rename to src/unlayer/types/emails/v1_send_create_params.py index e9a82ba..089845f 100644 --- a/src/unlayer/types/emails_v1_send_create_params.py +++ b/src/unlayer/types/emails/v1_send_create_params.py @@ -5,12 +5,12 @@ from typing import Dict from typing_extensions import Required, Annotated, TypedDict -from .._utils import PropertyInfo +from ..._utils import PropertyInfo -__all__ = ["EmailsV1SendCreateParams"] +__all__ = ["V1SendCreateParams"] -class EmailsV1SendCreateParams(TypedDict, total=False): +class V1SendCreateParams(TypedDict, total=False): design: Required[Dict[str, object]] """Proprietary design format JSON""" diff --git a/src/unlayer/types/emails_v1_send_create_response.py b/src/unlayer/types/emails/v1_send_create_response.py similarity index 76% rename from src/unlayer/types/emails_v1_send_create_response.py rename to src/unlayer/types/emails/v1_send_create_response.py index 1ff2c5d..4503ab0 100644 --- a/src/unlayer/types/emails_v1_send_create_response.py +++ b/src/unlayer/types/emails/v1_send_create_response.py @@ -5,12 +5,12 @@ from pydantic import Field as FieldInfo -from .._models import BaseModel +from ..._models import BaseModel -__all__ = ["EmailsV1SendCreateResponse"] +__all__ = ["V1SendCreateResponse"] -class EmailsV1SendCreateResponse(BaseModel): +class V1SendCreateResponse(BaseModel): message_id: Optional[str] = FieldInfo(alias="messageId", default=None) """Unique message identifier""" diff --git a/src/unlayer/types/emails_v1_send_template_template_params.py b/src/unlayer/types/emails/v1_send_template_template_params.py similarity index 80% rename from src/unlayer/types/emails_v1_send_template_template_params.py rename to src/unlayer/types/emails/v1_send_template_template_params.py index d6afa22..d4aca78 100644 --- a/src/unlayer/types/emails_v1_send_template_template_params.py +++ b/src/unlayer/types/emails/v1_send_template_template_params.py @@ -5,12 +5,12 @@ from typing import Dict from typing_extensions import Required, Annotated, TypedDict -from .._utils import PropertyInfo +from ..._utils import PropertyInfo -__all__ = ["EmailsV1SendTemplateTemplateParams"] +__all__ = ["V1SendTemplateTemplateParams"] -class EmailsV1SendTemplateTemplateParams(TypedDict, total=False): +class V1SendTemplateTemplateParams(TypedDict, total=False): template_id: Required[Annotated[str, PropertyInfo(alias="templateId")]] """ID of the template to use""" diff --git a/src/unlayer/types/emails_v1_send_template_template_response.py b/src/unlayer/types/emails/v1_send_template_template_response.py similarity index 73% rename from src/unlayer/types/emails_v1_send_template_template_response.py rename to src/unlayer/types/emails/v1_send_template_template_response.py index 66ef594..f876fe6 100644 --- a/src/unlayer/types/emails_v1_send_template_template_response.py +++ b/src/unlayer/types/emails/v1_send_template_template_response.py @@ -5,12 +5,12 @@ from pydantic import Field as FieldInfo -from .._models import BaseModel +from ..._models import BaseModel -__all__ = ["EmailsV1SendTemplateTemplateResponse"] +__all__ = ["V1SendTemplateTemplateResponse"] -class EmailsV1SendTemplateTemplateResponse(BaseModel): +class V1SendTemplateTemplateResponse(BaseModel): message_id: Optional[str] = FieldInfo(alias="messageId", default=None) """Unique message identifier""" diff --git a/src/unlayer/types/pages/__init__.py b/src/unlayer/types/pages/__init__.py new file mode 100644 index 0000000..c366123 --- /dev/null +++ b/src/unlayer/types/pages/__init__.py @@ -0,0 +1,6 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from .v1_render_create_params import V1RenderCreateParams as V1RenderCreateParams +from .v1_render_create_response import V1RenderCreateResponse as V1RenderCreateResponse diff --git a/src/unlayer/types/emails_v1_render_create_params.py b/src/unlayer/types/pages/v1_render_create_params.py similarity index 75% rename from src/unlayer/types/emails_v1_render_create_params.py rename to src/unlayer/types/pages/v1_render_create_params.py index 5f6f577..9fcd33c 100644 --- a/src/unlayer/types/emails_v1_render_create_params.py +++ b/src/unlayer/types/pages/v1_render_create_params.py @@ -5,12 +5,12 @@ from typing import Dict from typing_extensions import Required, Annotated, TypedDict -from .._utils import PropertyInfo +from ..._utils import PropertyInfo -__all__ = ["EmailsV1RenderCreateParams"] +__all__ = ["V1RenderCreateParams"] -class EmailsV1RenderCreateParams(TypedDict, total=False): +class V1RenderCreateParams(TypedDict, total=False): design: Required[Dict[str, object]] """Proprietary design format JSON""" diff --git a/src/unlayer/types/emails_v1_render_create_response.py b/src/unlayer/types/pages/v1_render_create_response.py similarity index 59% rename from src/unlayer/types/emails_v1_render_create_response.py rename to src/unlayer/types/pages/v1_render_create_response.py index 35cdf03..fadb69c 100644 --- a/src/unlayer/types/emails_v1_render_create_response.py +++ b/src/unlayer/types/pages/v1_render_create_response.py @@ -2,11 +2,11 @@ from typing import Optional -from .._models import BaseModel +from ..._models import BaseModel -__all__ = ["EmailsV1RenderCreateResponse"] +__all__ = ["V1RenderCreateResponse"] -class EmailsV1RenderCreateResponse(BaseModel): +class V1RenderCreateResponse(BaseModel): html: Optional[str] = None """Rendered HTML content""" diff --git a/src/unlayer/types/project/__init__.py b/src/unlayer/types/project/__init__.py new file mode 100644 index 0000000..8de899a --- /dev/null +++ b/src/unlayer/types/project/__init__.py @@ -0,0 +1,23 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from .v1_current_list_response import V1CurrentListResponse as V1CurrentListResponse +from .v1_domains_create_params import V1DomainsCreateParams as V1DomainsCreateParams +from .v1_domains_list_response import V1DomainsListResponse as V1DomainsListResponse +from .v1_domains_update_params import V1DomainsUpdateParams as V1DomainsUpdateParams +from .v1_api_keys_create_params import V1APIKeysCreateParams as V1APIKeysCreateParams +from .v1_api_keys_list_response import V1APIKeysListResponse as V1APIKeysListResponse +from .v1_api_keys_update_params import V1APIKeysUpdateParams as V1APIKeysUpdateParams +from .v1_domains_create_response import V1DomainsCreateResponse as V1DomainsCreateResponse +from .v1_domains_update_response import V1DomainsUpdateResponse as V1DomainsUpdateResponse +from .v1_templates_create_params import V1TemplatesCreateParams as V1TemplatesCreateParams +from .v1_templates_list_response import V1TemplatesListResponse as V1TemplatesListResponse +from .v1_templates_update_params import V1TemplatesUpdateParams as V1TemplatesUpdateParams +from .v1_api_keys_create_response import V1APIKeysCreateResponse as V1APIKeysCreateResponse +from .v1_api_keys_update_response import V1APIKeysUpdateResponse as V1APIKeysUpdateResponse +from .v1_domains_retrieve_response import V1DomainsRetrieveResponse as V1DomainsRetrieveResponse +from .v1_templates_create_response import V1TemplatesCreateResponse as V1TemplatesCreateResponse +from .v1_templates_update_response import V1TemplatesUpdateResponse as V1TemplatesUpdateResponse +from .v1_api_keys_retrieve_response import V1APIKeysRetrieveResponse as V1APIKeysRetrieveResponse +from .v1_templates_retrieve_response import V1TemplatesRetrieveResponse as V1TemplatesRetrieveResponse diff --git a/src/unlayer/types/project_v1_api_keys_create_params.py b/src/unlayer/types/project/v1_api_keys_create_params.py similarity index 68% rename from src/unlayer/types/project_v1_api_keys_create_params.py rename to src/unlayer/types/project/v1_api_keys_create_params.py index f1111f3..b5cc46c 100644 --- a/src/unlayer/types/project_v1_api_keys_create_params.py +++ b/src/unlayer/types/project/v1_api_keys_create_params.py @@ -4,12 +4,12 @@ from typing_extensions import Required, TypedDict -from .._types import SequenceNotStr +from ..._types import SequenceNotStr -__all__ = ["ProjectV1APIKeysCreateParams"] +__all__ = ["V1APIKeysCreateParams"] -class ProjectV1APIKeysCreateParams(TypedDict, total=False): +class V1APIKeysCreateParams(TypedDict, total=False): name: Required[str] """Name for the API key""" diff --git a/src/unlayer/types/project_v1_api_keys_create_response.py b/src/unlayer/types/project/v1_api_keys_create_response.py similarity index 78% rename from src/unlayer/types/project_v1_api_keys_create_response.py rename to src/unlayer/types/project/v1_api_keys_create_response.py index 3983423..ac3a6d2 100644 --- a/src/unlayer/types/project_v1_api_keys_create_response.py +++ b/src/unlayer/types/project/v1_api_keys_create_response.py @@ -5,9 +5,9 @@ from pydantic import Field as FieldInfo -from .._models import BaseModel +from ..._models import BaseModel -__all__ = ["ProjectV1APIKeysCreateResponse", "Data"] +__all__ = ["V1APIKeysCreateResponse", "Data"] class Data(BaseModel): @@ -24,5 +24,5 @@ class Data(BaseModel): name: Optional[str] = None -class ProjectV1APIKeysCreateResponse(BaseModel): +class V1APIKeysCreateResponse(BaseModel): data: Optional[Data] = None diff --git a/src/unlayer/types/project_v1_api_keys_list_response.py b/src/unlayer/types/project/v1_api_keys_list_response.py similarity index 81% rename from src/unlayer/types/project_v1_api_keys_list_response.py rename to src/unlayer/types/project/v1_api_keys_list_response.py index 8dcaa2f..e7a49c0 100644 --- a/src/unlayer/types/project_v1_api_keys_list_response.py +++ b/src/unlayer/types/project/v1_api_keys_list_response.py @@ -5,9 +5,9 @@ from pydantic import Field as FieldInfo -from .._models import BaseModel +from ..._models import BaseModel -__all__ = ["ProjectV1APIKeysListResponse", "Data"] +__all__ = ["V1APIKeysListResponse", "Data"] class Data(BaseModel): @@ -26,5 +26,5 @@ class Data(BaseModel): name: Optional[str] = None -class ProjectV1APIKeysListResponse(BaseModel): +class V1APIKeysListResponse(BaseModel): data: Optional[List[Data]] = None diff --git a/src/unlayer/types/project_v1_api_keys_update_response.py b/src/unlayer/types/project/v1_api_keys_retrieve_response.py similarity index 81% rename from src/unlayer/types/project_v1_api_keys_update_response.py rename to src/unlayer/types/project/v1_api_keys_retrieve_response.py index 75f61e3..c52caf2 100644 --- a/src/unlayer/types/project_v1_api_keys_update_response.py +++ b/src/unlayer/types/project/v1_api_keys_retrieve_response.py @@ -5,9 +5,9 @@ from pydantic import Field as FieldInfo -from .._models import BaseModel +from ..._models import BaseModel -__all__ = ["ProjectV1APIKeysUpdateResponse", "Data"] +__all__ = ["V1APIKeysRetrieveResponse", "Data"] class Data(BaseModel): @@ -26,5 +26,5 @@ class Data(BaseModel): name: Optional[str] = None -class ProjectV1APIKeysUpdateResponse(BaseModel): +class V1APIKeysRetrieveResponse(BaseModel): data: Optional[Data] = None diff --git a/src/unlayer/types/project_v1_api_keys_update_params.py b/src/unlayer/types/project/v1_api_keys_update_params.py similarity index 71% rename from src/unlayer/types/project_v1_api_keys_update_params.py rename to src/unlayer/types/project/v1_api_keys_update_params.py index 16cf1af..965902f 100644 --- a/src/unlayer/types/project_v1_api_keys_update_params.py +++ b/src/unlayer/types/project/v1_api_keys_update_params.py @@ -4,12 +4,12 @@ from typing_extensions import TypedDict -from .._types import SequenceNotStr +from ..._types import SequenceNotStr -__all__ = ["ProjectV1APIKeysUpdateParams"] +__all__ = ["V1APIKeysUpdateParams"] -class ProjectV1APIKeysUpdateParams(TypedDict, total=False): +class V1APIKeysUpdateParams(TypedDict, total=False): active: bool """Whether the API key is active""" diff --git a/src/unlayer/types/project_v1_api_keys_retrieve_response.py b/src/unlayer/types/project/v1_api_keys_update_response.py similarity index 80% rename from src/unlayer/types/project_v1_api_keys_retrieve_response.py rename to src/unlayer/types/project/v1_api_keys_update_response.py index b27fbf6..16c675e 100644 --- a/src/unlayer/types/project_v1_api_keys_retrieve_response.py +++ b/src/unlayer/types/project/v1_api_keys_update_response.py @@ -5,9 +5,9 @@ from pydantic import Field as FieldInfo -from .._models import BaseModel +from ..._models import BaseModel -__all__ = ["ProjectV1APIKeysRetrieveResponse", "Data"] +__all__ = ["V1APIKeysUpdateResponse", "Data"] class Data(BaseModel): @@ -26,5 +26,5 @@ class Data(BaseModel): name: Optional[str] = None -class ProjectV1APIKeysRetrieveResponse(BaseModel): +class V1APIKeysUpdateResponse(BaseModel): data: Optional[Data] = None diff --git a/src/unlayer/types/project_v1_current_list_response.py b/src/unlayer/types/project/v1_current_list_response.py similarity index 79% rename from src/unlayer/types/project_v1_current_list_response.py rename to src/unlayer/types/project/v1_current_list_response.py index bec0ada..26a480e 100644 --- a/src/unlayer/types/project_v1_current_list_response.py +++ b/src/unlayer/types/project/v1_current_list_response.py @@ -5,9 +5,9 @@ from pydantic import Field as FieldInfo -from .._models import BaseModel +from ..._models import BaseModel -__all__ = ["ProjectV1CurrentListResponse", "Data", "DataWorkspace"] +__all__ = ["V1CurrentListResponse", "Data", "DataWorkspace"] class DataWorkspace(BaseModel): @@ -28,5 +28,5 @@ class Data(BaseModel): workspace: Optional[DataWorkspace] = None -class ProjectV1CurrentListResponse(BaseModel): +class V1CurrentListResponse(BaseModel): data: Optional[Data] = None diff --git a/src/unlayer/types/project_v1_domains_create_params.py b/src/unlayer/types/project/v1_domains_create_params.py similarity index 69% rename from src/unlayer/types/project_v1_domains_create_params.py rename to src/unlayer/types/project/v1_domains_create_params.py index 8ba07c6..15f083b 100644 --- a/src/unlayer/types/project_v1_domains_create_params.py +++ b/src/unlayer/types/project/v1_domains_create_params.py @@ -4,9 +4,9 @@ from typing_extensions import Required, TypedDict -__all__ = ["ProjectV1DomainsCreateParams"] +__all__ = ["V1DomainsCreateParams"] -class ProjectV1DomainsCreateParams(TypedDict, total=False): +class V1DomainsCreateParams(TypedDict, total=False): domain: Required[str] """Domain name to add""" diff --git a/src/unlayer/types/project_v1_domains_update_response.py b/src/unlayer/types/project/v1_domains_create_response.py similarity index 77% rename from src/unlayer/types/project_v1_domains_update_response.py rename to src/unlayer/types/project/v1_domains_create_response.py index 23cbe30..2f25e01 100644 --- a/src/unlayer/types/project_v1_domains_update_response.py +++ b/src/unlayer/types/project/v1_domains_create_response.py @@ -5,9 +5,9 @@ from pydantic import Field as FieldInfo -from .._models import BaseModel +from ..._models import BaseModel -__all__ = ["ProjectV1DomainsUpdateResponse", "Data"] +__all__ = ["V1DomainsCreateResponse", "Data"] class Data(BaseModel): @@ -22,5 +22,5 @@ class Data(BaseModel): verified: Optional[bool] = None -class ProjectV1DomainsUpdateResponse(BaseModel): +class V1DomainsCreateResponse(BaseModel): data: Optional[Data] = None diff --git a/src/unlayer/types/project_v1_domains_list_response.py b/src/unlayer/types/project/v1_domains_list_response.py similarity index 80% rename from src/unlayer/types/project_v1_domains_list_response.py rename to src/unlayer/types/project/v1_domains_list_response.py index 134a6ab..5834ecd 100644 --- a/src/unlayer/types/project_v1_domains_list_response.py +++ b/src/unlayer/types/project/v1_domains_list_response.py @@ -6,9 +6,9 @@ from pydantic import Field as FieldInfo -from .._models import BaseModel +from ..._models import BaseModel -__all__ = ["ProjectV1DomainsListResponse", "Data"] +__all__ = ["V1DomainsListResponse", "Data"] class Data(BaseModel): @@ -23,5 +23,5 @@ class Data(BaseModel): verified: Optional[bool] = None -class ProjectV1DomainsListResponse(BaseModel): +class V1DomainsListResponse(BaseModel): data: Optional[List[Data]] = None diff --git a/src/unlayer/types/project_v1_domains_create_response.py b/src/unlayer/types/project/v1_domains_retrieve_response.py similarity index 77% rename from src/unlayer/types/project_v1_domains_create_response.py rename to src/unlayer/types/project/v1_domains_retrieve_response.py index 31325f3..cabe1f9 100644 --- a/src/unlayer/types/project_v1_domains_create_response.py +++ b/src/unlayer/types/project/v1_domains_retrieve_response.py @@ -5,9 +5,9 @@ from pydantic import Field as FieldInfo -from .._models import BaseModel +from ..._models import BaseModel -__all__ = ["ProjectV1DomainsCreateResponse", "Data"] +__all__ = ["V1DomainsRetrieveResponse", "Data"] class Data(BaseModel): @@ -22,5 +22,5 @@ class Data(BaseModel): verified: Optional[bool] = None -class ProjectV1DomainsCreateResponse(BaseModel): +class V1DomainsRetrieveResponse(BaseModel): data: Optional[Data] = None diff --git a/src/unlayer/types/project_v1_domains_update_params.py b/src/unlayer/types/project/v1_domains_update_params.py similarity index 67% rename from src/unlayer/types/project_v1_domains_update_params.py rename to src/unlayer/types/project/v1_domains_update_params.py index 851bef7..36267c6 100644 --- a/src/unlayer/types/project_v1_domains_update_params.py +++ b/src/unlayer/types/project/v1_domains_update_params.py @@ -4,9 +4,9 @@ from typing_extensions import TypedDict -__all__ = ["ProjectV1DomainsUpdateParams"] +__all__ = ["V1DomainsUpdateParams"] -class ProjectV1DomainsUpdateParams(TypedDict, total=False): +class V1DomainsUpdateParams(TypedDict, total=False): domain: str """Updated domain name""" diff --git a/src/unlayer/types/project_v1_domains_retrieve_response.py b/src/unlayer/types/project/v1_domains_update_response.py similarity index 77% rename from src/unlayer/types/project_v1_domains_retrieve_response.py rename to src/unlayer/types/project/v1_domains_update_response.py index cd3fbf2..4389356 100644 --- a/src/unlayer/types/project_v1_domains_retrieve_response.py +++ b/src/unlayer/types/project/v1_domains_update_response.py @@ -5,9 +5,9 @@ from pydantic import Field as FieldInfo -from .._models import BaseModel +from ..._models import BaseModel -__all__ = ["ProjectV1DomainsRetrieveResponse", "Data"] +__all__ = ["V1DomainsUpdateResponse", "Data"] class Data(BaseModel): @@ -22,5 +22,5 @@ class Data(BaseModel): verified: Optional[bool] = None -class ProjectV1DomainsRetrieveResponse(BaseModel): +class V1DomainsUpdateResponse(BaseModel): data: Optional[Data] = None diff --git a/src/unlayer/types/project_v1_templates_create_params.py b/src/unlayer/types/project/v1_templates_create_params.py similarity index 74% rename from src/unlayer/types/project_v1_templates_create_params.py rename to src/unlayer/types/project/v1_templates_create_params.py index eeeb406..0251cb6 100644 --- a/src/unlayer/types/project_v1_templates_create_params.py +++ b/src/unlayer/types/project/v1_templates_create_params.py @@ -4,10 +4,10 @@ from typing_extensions import Required, TypedDict -__all__ = ["ProjectV1TemplatesCreateParams"] +__all__ = ["V1TemplatesCreateParams"] -class ProjectV1TemplatesCreateParams(TypedDict, total=False): +class V1TemplatesCreateParams(TypedDict, total=False): name: Required[str] """Template name""" diff --git a/src/unlayer/types/project_v1_templates_create_response.py b/src/unlayer/types/project/v1_templates_create_response.py similarity index 79% rename from src/unlayer/types/project_v1_templates_create_response.py rename to src/unlayer/types/project/v1_templates_create_response.py index 787e4a2..59a6315 100644 --- a/src/unlayer/types/project_v1_templates_create_response.py +++ b/src/unlayer/types/project/v1_templates_create_response.py @@ -5,9 +5,9 @@ from pydantic import Field as FieldInfo -from .._models import BaseModel +from ..._models import BaseModel -__all__ = ["ProjectV1TemplatesCreateResponse", "Data"] +__all__ = ["V1TemplatesCreateResponse", "Data"] class Data(BaseModel): @@ -24,5 +24,5 @@ class Data(BaseModel): updated_at: Optional[datetime] = FieldInfo(alias="updatedAt", default=None) -class ProjectV1TemplatesCreateResponse(BaseModel): +class V1TemplatesCreateResponse(BaseModel): data: Optional[Data] = None diff --git a/src/unlayer/types/project_v1_templates_list_response.py b/src/unlayer/types/project/v1_templates_list_response.py similarity index 80% rename from src/unlayer/types/project_v1_templates_list_response.py rename to src/unlayer/types/project/v1_templates_list_response.py index da39fa4..494335e 100644 --- a/src/unlayer/types/project_v1_templates_list_response.py +++ b/src/unlayer/types/project/v1_templates_list_response.py @@ -5,9 +5,9 @@ from pydantic import Field as FieldInfo -from .._models import BaseModel +from ..._models import BaseModel -__all__ = ["ProjectV1TemplatesListResponse", "Data"] +__all__ = ["V1TemplatesListResponse", "Data"] class Data(BaseModel): @@ -24,5 +24,5 @@ class Data(BaseModel): updated_at: Optional[datetime] = FieldInfo(alias="updatedAt", default=None) -class ProjectV1TemplatesListResponse(BaseModel): +class V1TemplatesListResponse(BaseModel): data: Optional[List[Data]] = None diff --git a/src/unlayer/types/project_v1_templates_update_response.py b/src/unlayer/types/project/v1_templates_retrieve_response.py similarity index 79% rename from src/unlayer/types/project_v1_templates_update_response.py rename to src/unlayer/types/project/v1_templates_retrieve_response.py index 5445311..2a113b8 100644 --- a/src/unlayer/types/project_v1_templates_update_response.py +++ b/src/unlayer/types/project/v1_templates_retrieve_response.py @@ -5,9 +5,9 @@ from pydantic import Field as FieldInfo -from .._models import BaseModel +from ..._models import BaseModel -__all__ = ["ProjectV1TemplatesUpdateResponse", "Data"] +__all__ = ["V1TemplatesRetrieveResponse", "Data"] class Data(BaseModel): @@ -24,5 +24,5 @@ class Data(BaseModel): updated_at: Optional[datetime] = FieldInfo(alias="updatedAt", default=None) -class ProjectV1TemplatesUpdateResponse(BaseModel): +class V1TemplatesRetrieveResponse(BaseModel): data: Optional[Data] = None diff --git a/src/unlayer/types/project_v1_templates_update_params.py b/src/unlayer/types/project/v1_templates_update_params.py similarity index 74% rename from src/unlayer/types/project_v1_templates_update_params.py rename to src/unlayer/types/project/v1_templates_update_params.py index f0caf44..4c3848c 100644 --- a/src/unlayer/types/project_v1_templates_update_params.py +++ b/src/unlayer/types/project/v1_templates_update_params.py @@ -4,10 +4,10 @@ from typing_extensions import TypedDict -__all__ = ["ProjectV1TemplatesUpdateParams"] +__all__ = ["V1TemplatesUpdateParams"] -class ProjectV1TemplatesUpdateParams(TypedDict, total=False): +class V1TemplatesUpdateParams(TypedDict, total=False): body: str """Updated email body content""" diff --git a/src/unlayer/types/project_v1_templates_retrieve_response.py b/src/unlayer/types/project/v1_templates_update_response.py similarity index 79% rename from src/unlayer/types/project_v1_templates_retrieve_response.py rename to src/unlayer/types/project/v1_templates_update_response.py index 15c6171..fa82212 100644 --- a/src/unlayer/types/project_v1_templates_retrieve_response.py +++ b/src/unlayer/types/project/v1_templates_update_response.py @@ -5,9 +5,9 @@ from pydantic import Field as FieldInfo -from .._models import BaseModel +from ..._models import BaseModel -__all__ = ["ProjectV1TemplatesRetrieveResponse", "Data"] +__all__ = ["V1TemplatesUpdateResponse", "Data"] class Data(BaseModel): @@ -24,5 +24,5 @@ class Data(BaseModel): updated_at: Optional[datetime] = FieldInfo(alias="updatedAt", default=None) -class ProjectV1TemplatesRetrieveResponse(BaseModel): +class V1TemplatesUpdateResponse(BaseModel): data: Optional[Data] = None diff --git a/tests/api_resources/documents/__init__.py b/tests/api_resources/documents/__init__.py new file mode 100644 index 0000000..fd8019a --- /dev/null +++ b/tests/api_resources/documents/__init__.py @@ -0,0 +1 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. diff --git a/tests/api_resources/test_documents_v1.py b/tests/api_resources/documents/test_v1.py similarity index 62% rename from tests/api_resources/test_documents_v1.py rename to tests/api_resources/documents/test_v1.py index 6c94054..a3e4ba0 100644 --- a/tests/api_resources/test_documents_v1.py +++ b/tests/api_resources/documents/test_v1.py @@ -9,69 +9,69 @@ from unlayer import Unlayer, AsyncUnlayer from tests.utils import assert_matches_type -from unlayer.types import ( - DocumentsV1GenerateCreateResponse, - DocumentsV1DocumentsRetrieveResponse, - DocumentsV1GenerateTemplateTemplateResponse, +from unlayer.types.documents import ( + V1GenerateCreateResponse, + V1DocumentsRetrieveResponse, + V1GenerateTemplateTemplateResponse, ) base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") -class TestDocumentsV1: +class TestV1: parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) @parametrize def test_method_documents_retrieve(self, client: Unlayer) -> None: - documents_v1 = client.documents_v1.documents_retrieve( + v1 = client.documents.v1.documents_retrieve( "id", ) - assert_matches_type(DocumentsV1DocumentsRetrieveResponse, documents_v1, path=["response"]) + assert_matches_type(V1DocumentsRetrieveResponse, v1, path=["response"]) @parametrize def test_raw_response_documents_retrieve(self, client: Unlayer) -> None: - response = client.documents_v1.with_raw_response.documents_retrieve( + response = client.documents.v1.with_raw_response.documents_retrieve( "id", ) assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" - documents_v1 = response.parse() - assert_matches_type(DocumentsV1DocumentsRetrieveResponse, documents_v1, path=["response"]) + v1 = response.parse() + assert_matches_type(V1DocumentsRetrieveResponse, v1, path=["response"]) @parametrize def test_streaming_response_documents_retrieve(self, client: Unlayer) -> None: - with client.documents_v1.with_streaming_response.documents_retrieve( + with client.documents.v1.with_streaming_response.documents_retrieve( "id", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" - documents_v1 = response.parse() - assert_matches_type(DocumentsV1DocumentsRetrieveResponse, documents_v1, path=["response"]) + v1 = response.parse() + assert_matches_type(V1DocumentsRetrieveResponse, v1, path=["response"]) assert cast(Any, response.is_closed) is True @parametrize def test_path_params_documents_retrieve(self, client: Unlayer) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): - client.documents_v1.with_raw_response.documents_retrieve( + client.documents.v1.with_raw_response.documents_retrieve( "", ) @parametrize def test_method_generate_create(self, client: Unlayer) -> None: - documents_v1 = client.documents_v1.generate_create( + v1 = client.documents.v1.generate_create( design={ "counters": "bar", "body": "bar", }, ) - assert_matches_type(DocumentsV1GenerateCreateResponse, documents_v1, path=["response"]) + assert_matches_type(V1GenerateCreateResponse, v1, path=["response"]) @parametrize def test_method_generate_create_with_all_params(self, client: Unlayer) -> None: - documents_v1 = client.documents_v1.generate_create( + v1 = client.documents.v1.generate_create( design={ "counters": "bar", "body": "bar", @@ -81,11 +81,11 @@ def test_method_generate_create_with_all_params(self, client: Unlayer) -> None: merge_tags={"foo": "string"}, url="https://example.com", ) - assert_matches_type(DocumentsV1GenerateCreateResponse, documents_v1, path=["response"]) + assert_matches_type(V1GenerateCreateResponse, v1, path=["response"]) @parametrize def test_raw_response_generate_create(self, client: Unlayer) -> None: - response = client.documents_v1.with_raw_response.generate_create( + response = client.documents.v1.with_raw_response.generate_create( design={ "counters": "bar", "body": "bar", @@ -94,12 +94,12 @@ def test_raw_response_generate_create(self, client: Unlayer) -> None: assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" - documents_v1 = response.parse() - assert_matches_type(DocumentsV1GenerateCreateResponse, documents_v1, path=["response"]) + v1 = response.parse() + assert_matches_type(V1GenerateCreateResponse, v1, path=["response"]) @parametrize def test_streaming_response_generate_create(self, client: Unlayer) -> None: - with client.documents_v1.with_streaming_response.generate_create( + with client.documents.v1.with_streaming_response.generate_create( design={ "counters": "bar", "body": "bar", @@ -108,108 +108,108 @@ def test_streaming_response_generate_create(self, client: Unlayer) -> None: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" - documents_v1 = response.parse() - assert_matches_type(DocumentsV1GenerateCreateResponse, documents_v1, path=["response"]) + v1 = response.parse() + assert_matches_type(V1GenerateCreateResponse, v1, path=["response"]) assert cast(Any, response.is_closed) is True @parametrize def test_method_generate_template_template(self, client: Unlayer) -> None: - documents_v1 = client.documents_v1.generate_template_template( + v1 = client.documents.v1.generate_template_template( template_id="templateId", ) - assert_matches_type(DocumentsV1GenerateTemplateTemplateResponse, documents_v1, path=["response"]) + assert_matches_type(V1GenerateTemplateTemplateResponse, v1, path=["response"]) @parametrize def test_method_generate_template_template_with_all_params(self, client: Unlayer) -> None: - documents_v1 = client.documents_v1.generate_template_template( + v1 = client.documents.v1.generate_template_template( template_id="templateId", filename="filename", merge_tags={"foo": "string"}, ) - assert_matches_type(DocumentsV1GenerateTemplateTemplateResponse, documents_v1, path=["response"]) + assert_matches_type(V1GenerateTemplateTemplateResponse, v1, path=["response"]) @parametrize def test_raw_response_generate_template_template(self, client: Unlayer) -> None: - response = client.documents_v1.with_raw_response.generate_template_template( + response = client.documents.v1.with_raw_response.generate_template_template( template_id="templateId", ) assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" - documents_v1 = response.parse() - assert_matches_type(DocumentsV1GenerateTemplateTemplateResponse, documents_v1, path=["response"]) + v1 = response.parse() + assert_matches_type(V1GenerateTemplateTemplateResponse, v1, path=["response"]) @parametrize def test_streaming_response_generate_template_template(self, client: Unlayer) -> None: - with client.documents_v1.with_streaming_response.generate_template_template( + with client.documents.v1.with_streaming_response.generate_template_template( template_id="templateId", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" - documents_v1 = response.parse() - assert_matches_type(DocumentsV1GenerateTemplateTemplateResponse, documents_v1, path=["response"]) + v1 = response.parse() + assert_matches_type(V1GenerateTemplateTemplateResponse, v1, path=["response"]) assert cast(Any, response.is_closed) is True -class TestAsyncDocumentsV1: +class TestAsyncV1: parametrize = pytest.mark.parametrize( "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] ) @parametrize async def test_method_documents_retrieve(self, async_client: AsyncUnlayer) -> None: - documents_v1 = await async_client.documents_v1.documents_retrieve( + v1 = await async_client.documents.v1.documents_retrieve( "id", ) - assert_matches_type(DocumentsV1DocumentsRetrieveResponse, documents_v1, path=["response"]) + assert_matches_type(V1DocumentsRetrieveResponse, v1, path=["response"]) @parametrize async def test_raw_response_documents_retrieve(self, async_client: AsyncUnlayer) -> None: - response = await async_client.documents_v1.with_raw_response.documents_retrieve( + response = await async_client.documents.v1.with_raw_response.documents_retrieve( "id", ) assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" - documents_v1 = await response.parse() - assert_matches_type(DocumentsV1DocumentsRetrieveResponse, documents_v1, path=["response"]) + v1 = await response.parse() + assert_matches_type(V1DocumentsRetrieveResponse, v1, path=["response"]) @parametrize async def test_streaming_response_documents_retrieve(self, async_client: AsyncUnlayer) -> None: - async with async_client.documents_v1.with_streaming_response.documents_retrieve( + async with async_client.documents.v1.with_streaming_response.documents_retrieve( "id", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" - documents_v1 = await response.parse() - assert_matches_type(DocumentsV1DocumentsRetrieveResponse, documents_v1, path=["response"]) + v1 = await response.parse() + assert_matches_type(V1DocumentsRetrieveResponse, v1, path=["response"]) assert cast(Any, response.is_closed) is True @parametrize async def test_path_params_documents_retrieve(self, async_client: AsyncUnlayer) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): - await async_client.documents_v1.with_raw_response.documents_retrieve( + await async_client.documents.v1.with_raw_response.documents_retrieve( "", ) @parametrize async def test_method_generate_create(self, async_client: AsyncUnlayer) -> None: - documents_v1 = await async_client.documents_v1.generate_create( + v1 = await async_client.documents.v1.generate_create( design={ "counters": "bar", "body": "bar", }, ) - assert_matches_type(DocumentsV1GenerateCreateResponse, documents_v1, path=["response"]) + assert_matches_type(V1GenerateCreateResponse, v1, path=["response"]) @parametrize async def test_method_generate_create_with_all_params(self, async_client: AsyncUnlayer) -> None: - documents_v1 = await async_client.documents_v1.generate_create( + v1 = await async_client.documents.v1.generate_create( design={ "counters": "bar", "body": "bar", @@ -219,11 +219,11 @@ async def test_method_generate_create_with_all_params(self, async_client: AsyncU merge_tags={"foo": "string"}, url="https://example.com", ) - assert_matches_type(DocumentsV1GenerateCreateResponse, documents_v1, path=["response"]) + assert_matches_type(V1GenerateCreateResponse, v1, path=["response"]) @parametrize async def test_raw_response_generate_create(self, async_client: AsyncUnlayer) -> None: - response = await async_client.documents_v1.with_raw_response.generate_create( + response = await async_client.documents.v1.with_raw_response.generate_create( design={ "counters": "bar", "body": "bar", @@ -232,12 +232,12 @@ async def test_raw_response_generate_create(self, async_client: AsyncUnlayer) -> assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" - documents_v1 = await response.parse() - assert_matches_type(DocumentsV1GenerateCreateResponse, documents_v1, path=["response"]) + v1 = await response.parse() + assert_matches_type(V1GenerateCreateResponse, v1, path=["response"]) @parametrize async def test_streaming_response_generate_create(self, async_client: AsyncUnlayer) -> None: - async with async_client.documents_v1.with_streaming_response.generate_create( + async with async_client.documents.v1.with_streaming_response.generate_create( design={ "counters": "bar", "body": "bar", @@ -246,47 +246,47 @@ async def test_streaming_response_generate_create(self, async_client: AsyncUnlay assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" - documents_v1 = await response.parse() - assert_matches_type(DocumentsV1GenerateCreateResponse, documents_v1, path=["response"]) + v1 = await response.parse() + assert_matches_type(V1GenerateCreateResponse, v1, path=["response"]) assert cast(Any, response.is_closed) is True @parametrize async def test_method_generate_template_template(self, async_client: AsyncUnlayer) -> None: - documents_v1 = await async_client.documents_v1.generate_template_template( + v1 = await async_client.documents.v1.generate_template_template( template_id="templateId", ) - assert_matches_type(DocumentsV1GenerateTemplateTemplateResponse, documents_v1, path=["response"]) + assert_matches_type(V1GenerateTemplateTemplateResponse, v1, path=["response"]) @parametrize async def test_method_generate_template_template_with_all_params(self, async_client: AsyncUnlayer) -> None: - documents_v1 = await async_client.documents_v1.generate_template_template( + v1 = await async_client.documents.v1.generate_template_template( template_id="templateId", filename="filename", merge_tags={"foo": "string"}, ) - assert_matches_type(DocumentsV1GenerateTemplateTemplateResponse, documents_v1, path=["response"]) + assert_matches_type(V1GenerateTemplateTemplateResponse, v1, path=["response"]) @parametrize async def test_raw_response_generate_template_template(self, async_client: AsyncUnlayer) -> None: - response = await async_client.documents_v1.with_raw_response.generate_template_template( + response = await async_client.documents.v1.with_raw_response.generate_template_template( template_id="templateId", ) assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" - documents_v1 = await response.parse() - assert_matches_type(DocumentsV1GenerateTemplateTemplateResponse, documents_v1, path=["response"]) + v1 = await response.parse() + assert_matches_type(V1GenerateTemplateTemplateResponse, v1, path=["response"]) @parametrize async def test_streaming_response_generate_template_template(self, async_client: AsyncUnlayer) -> None: - async with async_client.documents_v1.with_streaming_response.generate_template_template( + async with async_client.documents.v1.with_streaming_response.generate_template_template( template_id="templateId", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" - documents_v1 = await response.parse() - assert_matches_type(DocumentsV1GenerateTemplateTemplateResponse, documents_v1, path=["response"]) + v1 = await response.parse() + assert_matches_type(V1GenerateTemplateTemplateResponse, v1, path=["response"]) assert cast(Any, response.is_closed) is True diff --git a/tests/api_resources/emails/__init__.py b/tests/api_resources/emails/__init__.py new file mode 100644 index 0000000..fd8019a --- /dev/null +++ b/tests/api_resources/emails/__init__.py @@ -0,0 +1 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. diff --git a/tests/api_resources/test_emails_v1.py b/tests/api_resources/emails/test_v1.py similarity index 64% rename from tests/api_resources/test_emails_v1.py rename to tests/api_resources/emails/test_v1.py index 8a5f5fe..70d02ee 100644 --- a/tests/api_resources/test_emails_v1.py +++ b/tests/api_resources/emails/test_v1.py @@ -9,81 +9,81 @@ from unlayer import Unlayer, AsyncUnlayer from tests.utils import assert_matches_type -from unlayer.types import ( - EmailsV1SendCreateResponse, - EmailsV1RenderCreateResponse, - EmailsV1EmailsRetrieveResponse, - EmailsV1SendTemplateTemplateResponse, +from unlayer.types.emails import ( + V1SendCreateResponse, + V1RenderCreateResponse, + V1EmailsRetrieveResponse, + V1SendTemplateTemplateResponse, ) base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") -class TestEmailsV1: +class TestV1: parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) @parametrize def test_method_emails_retrieve(self, client: Unlayer) -> None: - emails_v1 = client.emails_v1.emails_retrieve( + v1 = client.emails.v1.emails_retrieve( "id", ) - assert_matches_type(EmailsV1EmailsRetrieveResponse, emails_v1, path=["response"]) + assert_matches_type(V1EmailsRetrieveResponse, v1, path=["response"]) @parametrize def test_raw_response_emails_retrieve(self, client: Unlayer) -> None: - response = client.emails_v1.with_raw_response.emails_retrieve( + response = client.emails.v1.with_raw_response.emails_retrieve( "id", ) assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" - emails_v1 = response.parse() - assert_matches_type(EmailsV1EmailsRetrieveResponse, emails_v1, path=["response"]) + v1 = response.parse() + assert_matches_type(V1EmailsRetrieveResponse, v1, path=["response"]) @parametrize def test_streaming_response_emails_retrieve(self, client: Unlayer) -> None: - with client.emails_v1.with_streaming_response.emails_retrieve( + with client.emails.v1.with_streaming_response.emails_retrieve( "id", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" - emails_v1 = response.parse() - assert_matches_type(EmailsV1EmailsRetrieveResponse, emails_v1, path=["response"]) + v1 = response.parse() + assert_matches_type(V1EmailsRetrieveResponse, v1, path=["response"]) assert cast(Any, response.is_closed) is True @parametrize def test_path_params_emails_retrieve(self, client: Unlayer) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): - client.emails_v1.with_raw_response.emails_retrieve( + client.emails.v1.with_raw_response.emails_retrieve( "", ) @parametrize def test_method_render_create(self, client: Unlayer) -> None: - emails_v1 = client.emails_v1.render_create( + v1 = client.emails.v1.render_create( design={ "counters": "bar", "body": "bar", }, ) - assert_matches_type(EmailsV1RenderCreateResponse, emails_v1, path=["response"]) + assert_matches_type(V1RenderCreateResponse, v1, path=["response"]) @parametrize def test_method_render_create_with_all_params(self, client: Unlayer) -> None: - emails_v1 = client.emails_v1.render_create( + v1 = client.emails.v1.render_create( design={ "counters": "bar", "body": "bar", }, merge_tags={"foo": "string"}, ) - assert_matches_type(EmailsV1RenderCreateResponse, emails_v1, path=["response"]) + assert_matches_type(V1RenderCreateResponse, v1, path=["response"]) @parametrize def test_raw_response_render_create(self, client: Unlayer) -> None: - response = client.emails_v1.with_raw_response.render_create( + response = client.emails.v1.with_raw_response.render_create( design={ "counters": "bar", "body": "bar", @@ -92,12 +92,12 @@ def test_raw_response_render_create(self, client: Unlayer) -> None: assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" - emails_v1 = response.parse() - assert_matches_type(EmailsV1RenderCreateResponse, emails_v1, path=["response"]) + v1 = response.parse() + assert_matches_type(V1RenderCreateResponse, v1, path=["response"]) @parametrize def test_streaming_response_render_create(self, client: Unlayer) -> None: - with client.emails_v1.with_streaming_response.render_create( + with client.emails.v1.with_streaming_response.render_create( design={ "counters": "bar", "body": "bar", @@ -106,25 +106,25 @@ def test_streaming_response_render_create(self, client: Unlayer) -> None: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" - emails_v1 = response.parse() - assert_matches_type(EmailsV1RenderCreateResponse, emails_v1, path=["response"]) + v1 = response.parse() + assert_matches_type(V1RenderCreateResponse, v1, path=["response"]) assert cast(Any, response.is_closed) is True @parametrize def test_method_send_create(self, client: Unlayer) -> None: - emails_v1 = client.emails_v1.send_create( + v1 = client.emails.v1.send_create( design={ "counters": "bar", "body": "bar", }, to="test@example.com", ) - assert_matches_type(EmailsV1SendCreateResponse, emails_v1, path=["response"]) + assert_matches_type(V1SendCreateResponse, v1, path=["response"]) @parametrize def test_method_send_create_with_all_params(self, client: Unlayer) -> None: - emails_v1 = client.emails_v1.send_create( + v1 = client.emails.v1.send_create( design={ "counters": "bar", "body": "bar", @@ -134,11 +134,11 @@ def test_method_send_create_with_all_params(self, client: Unlayer) -> None: merge_tags={"foo": "string"}, subject="Test", ) - assert_matches_type(EmailsV1SendCreateResponse, emails_v1, path=["response"]) + assert_matches_type(V1SendCreateResponse, v1, path=["response"]) @parametrize def test_raw_response_send_create(self, client: Unlayer) -> None: - response = client.emails_v1.with_raw_response.send_create( + response = client.emails.v1.with_raw_response.send_create( design={ "counters": "bar", "body": "bar", @@ -148,12 +148,12 @@ def test_raw_response_send_create(self, client: Unlayer) -> None: assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" - emails_v1 = response.parse() - assert_matches_type(EmailsV1SendCreateResponse, emails_v1, path=["response"]) + v1 = response.parse() + assert_matches_type(V1SendCreateResponse, v1, path=["response"]) @parametrize def test_streaming_response_send_create(self, client: Unlayer) -> None: - with client.emails_v1.with_streaming_response.send_create( + with client.emails.v1.with_streaming_response.send_create( design={ "counters": "bar", "body": "bar", @@ -163,123 +163,123 @@ def test_streaming_response_send_create(self, client: Unlayer) -> None: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" - emails_v1 = response.parse() - assert_matches_type(EmailsV1SendCreateResponse, emails_v1, path=["response"]) + v1 = response.parse() + assert_matches_type(V1SendCreateResponse, v1, path=["response"]) assert cast(Any, response.is_closed) is True @parametrize def test_method_send_template_template(self, client: Unlayer) -> None: - emails_v1 = client.emails_v1.send_template_template( + v1 = client.emails.v1.send_template_template( template_id="templateId", to="dev@stainless.com", ) - assert_matches_type(EmailsV1SendTemplateTemplateResponse, emails_v1, path=["response"]) + assert_matches_type(V1SendTemplateTemplateResponse, v1, path=["response"]) @parametrize def test_method_send_template_template_with_all_params(self, client: Unlayer) -> None: - emails_v1 = client.emails_v1.send_template_template( + v1 = client.emails.v1.send_template_template( template_id="templateId", to="dev@stainless.com", merge_tags={"foo": "string"}, subject="subject", ) - assert_matches_type(EmailsV1SendTemplateTemplateResponse, emails_v1, path=["response"]) + assert_matches_type(V1SendTemplateTemplateResponse, v1, path=["response"]) @parametrize def test_raw_response_send_template_template(self, client: Unlayer) -> None: - response = client.emails_v1.with_raw_response.send_template_template( + response = client.emails.v1.with_raw_response.send_template_template( template_id="templateId", to="dev@stainless.com", ) assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" - emails_v1 = response.parse() - assert_matches_type(EmailsV1SendTemplateTemplateResponse, emails_v1, path=["response"]) + v1 = response.parse() + assert_matches_type(V1SendTemplateTemplateResponse, v1, path=["response"]) @parametrize def test_streaming_response_send_template_template(self, client: Unlayer) -> None: - with client.emails_v1.with_streaming_response.send_template_template( + with client.emails.v1.with_streaming_response.send_template_template( template_id="templateId", to="dev@stainless.com", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" - emails_v1 = response.parse() - assert_matches_type(EmailsV1SendTemplateTemplateResponse, emails_v1, path=["response"]) + v1 = response.parse() + assert_matches_type(V1SendTemplateTemplateResponse, v1, path=["response"]) assert cast(Any, response.is_closed) is True -class TestAsyncEmailsV1: +class TestAsyncV1: parametrize = pytest.mark.parametrize( "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] ) @parametrize async def test_method_emails_retrieve(self, async_client: AsyncUnlayer) -> None: - emails_v1 = await async_client.emails_v1.emails_retrieve( + v1 = await async_client.emails.v1.emails_retrieve( "id", ) - assert_matches_type(EmailsV1EmailsRetrieveResponse, emails_v1, path=["response"]) + assert_matches_type(V1EmailsRetrieveResponse, v1, path=["response"]) @parametrize async def test_raw_response_emails_retrieve(self, async_client: AsyncUnlayer) -> None: - response = await async_client.emails_v1.with_raw_response.emails_retrieve( + response = await async_client.emails.v1.with_raw_response.emails_retrieve( "id", ) assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" - emails_v1 = await response.parse() - assert_matches_type(EmailsV1EmailsRetrieveResponse, emails_v1, path=["response"]) + v1 = await response.parse() + assert_matches_type(V1EmailsRetrieveResponse, v1, path=["response"]) @parametrize async def test_streaming_response_emails_retrieve(self, async_client: AsyncUnlayer) -> None: - async with async_client.emails_v1.with_streaming_response.emails_retrieve( + async with async_client.emails.v1.with_streaming_response.emails_retrieve( "id", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" - emails_v1 = await response.parse() - assert_matches_type(EmailsV1EmailsRetrieveResponse, emails_v1, path=["response"]) + v1 = await response.parse() + assert_matches_type(V1EmailsRetrieveResponse, v1, path=["response"]) assert cast(Any, response.is_closed) is True @parametrize async def test_path_params_emails_retrieve(self, async_client: AsyncUnlayer) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): - await async_client.emails_v1.with_raw_response.emails_retrieve( + await async_client.emails.v1.with_raw_response.emails_retrieve( "", ) @parametrize async def test_method_render_create(self, async_client: AsyncUnlayer) -> None: - emails_v1 = await async_client.emails_v1.render_create( + v1 = await async_client.emails.v1.render_create( design={ "counters": "bar", "body": "bar", }, ) - assert_matches_type(EmailsV1RenderCreateResponse, emails_v1, path=["response"]) + assert_matches_type(V1RenderCreateResponse, v1, path=["response"]) @parametrize async def test_method_render_create_with_all_params(self, async_client: AsyncUnlayer) -> None: - emails_v1 = await async_client.emails_v1.render_create( + v1 = await async_client.emails.v1.render_create( design={ "counters": "bar", "body": "bar", }, merge_tags={"foo": "string"}, ) - assert_matches_type(EmailsV1RenderCreateResponse, emails_v1, path=["response"]) + assert_matches_type(V1RenderCreateResponse, v1, path=["response"]) @parametrize async def test_raw_response_render_create(self, async_client: AsyncUnlayer) -> None: - response = await async_client.emails_v1.with_raw_response.render_create( + response = await async_client.emails.v1.with_raw_response.render_create( design={ "counters": "bar", "body": "bar", @@ -288,12 +288,12 @@ async def test_raw_response_render_create(self, async_client: AsyncUnlayer) -> N assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" - emails_v1 = await response.parse() - assert_matches_type(EmailsV1RenderCreateResponse, emails_v1, path=["response"]) + v1 = await response.parse() + assert_matches_type(V1RenderCreateResponse, v1, path=["response"]) @parametrize async def test_streaming_response_render_create(self, async_client: AsyncUnlayer) -> None: - async with async_client.emails_v1.with_streaming_response.render_create( + async with async_client.emails.v1.with_streaming_response.render_create( design={ "counters": "bar", "body": "bar", @@ -302,25 +302,25 @@ async def test_streaming_response_render_create(self, async_client: AsyncUnlayer assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" - emails_v1 = await response.parse() - assert_matches_type(EmailsV1RenderCreateResponse, emails_v1, path=["response"]) + v1 = await response.parse() + assert_matches_type(V1RenderCreateResponse, v1, path=["response"]) assert cast(Any, response.is_closed) is True @parametrize async def test_method_send_create(self, async_client: AsyncUnlayer) -> None: - emails_v1 = await async_client.emails_v1.send_create( + v1 = await async_client.emails.v1.send_create( design={ "counters": "bar", "body": "bar", }, to="test@example.com", ) - assert_matches_type(EmailsV1SendCreateResponse, emails_v1, path=["response"]) + assert_matches_type(V1SendCreateResponse, v1, path=["response"]) @parametrize async def test_method_send_create_with_all_params(self, async_client: AsyncUnlayer) -> None: - emails_v1 = await async_client.emails_v1.send_create( + v1 = await async_client.emails.v1.send_create( design={ "counters": "bar", "body": "bar", @@ -330,11 +330,11 @@ async def test_method_send_create_with_all_params(self, async_client: AsyncUnlay merge_tags={"foo": "string"}, subject="Test", ) - assert_matches_type(EmailsV1SendCreateResponse, emails_v1, path=["response"]) + assert_matches_type(V1SendCreateResponse, v1, path=["response"]) @parametrize async def test_raw_response_send_create(self, async_client: AsyncUnlayer) -> None: - response = await async_client.emails_v1.with_raw_response.send_create( + response = await async_client.emails.v1.with_raw_response.send_create( design={ "counters": "bar", "body": "bar", @@ -344,12 +344,12 @@ async def test_raw_response_send_create(self, async_client: AsyncUnlayer) -> Non assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" - emails_v1 = await response.parse() - assert_matches_type(EmailsV1SendCreateResponse, emails_v1, path=["response"]) + v1 = await response.parse() + assert_matches_type(V1SendCreateResponse, v1, path=["response"]) @parametrize async def test_streaming_response_send_create(self, async_client: AsyncUnlayer) -> None: - async with async_client.emails_v1.with_streaming_response.send_create( + async with async_client.emails.v1.with_streaming_response.send_create( design={ "counters": "bar", "body": "bar", @@ -359,51 +359,51 @@ async def test_streaming_response_send_create(self, async_client: AsyncUnlayer) assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" - emails_v1 = await response.parse() - assert_matches_type(EmailsV1SendCreateResponse, emails_v1, path=["response"]) + v1 = await response.parse() + assert_matches_type(V1SendCreateResponse, v1, path=["response"]) assert cast(Any, response.is_closed) is True @parametrize async def test_method_send_template_template(self, async_client: AsyncUnlayer) -> None: - emails_v1 = await async_client.emails_v1.send_template_template( + v1 = await async_client.emails.v1.send_template_template( template_id="templateId", to="dev@stainless.com", ) - assert_matches_type(EmailsV1SendTemplateTemplateResponse, emails_v1, path=["response"]) + assert_matches_type(V1SendTemplateTemplateResponse, v1, path=["response"]) @parametrize async def test_method_send_template_template_with_all_params(self, async_client: AsyncUnlayer) -> None: - emails_v1 = await async_client.emails_v1.send_template_template( + v1 = await async_client.emails.v1.send_template_template( template_id="templateId", to="dev@stainless.com", merge_tags={"foo": "string"}, subject="subject", ) - assert_matches_type(EmailsV1SendTemplateTemplateResponse, emails_v1, path=["response"]) + assert_matches_type(V1SendTemplateTemplateResponse, v1, path=["response"]) @parametrize async def test_raw_response_send_template_template(self, async_client: AsyncUnlayer) -> None: - response = await async_client.emails_v1.with_raw_response.send_template_template( + response = await async_client.emails.v1.with_raw_response.send_template_template( template_id="templateId", to="dev@stainless.com", ) assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" - emails_v1 = await response.parse() - assert_matches_type(EmailsV1SendTemplateTemplateResponse, emails_v1, path=["response"]) + v1 = await response.parse() + assert_matches_type(V1SendTemplateTemplateResponse, v1, path=["response"]) @parametrize async def test_streaming_response_send_template_template(self, async_client: AsyncUnlayer) -> None: - async with async_client.emails_v1.with_streaming_response.send_template_template( + async with async_client.emails.v1.with_streaming_response.send_template_template( template_id="templateId", to="dev@stainless.com", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" - emails_v1 = await response.parse() - assert_matches_type(EmailsV1SendTemplateTemplateResponse, emails_v1, path=["response"]) + v1 = await response.parse() + assert_matches_type(V1SendTemplateTemplateResponse, v1, path=["response"]) assert cast(Any, response.is_closed) is True diff --git a/tests/api_resources/pages/__init__.py b/tests/api_resources/pages/__init__.py new file mode 100644 index 0000000..fd8019a --- /dev/null +++ b/tests/api_resources/pages/__init__.py @@ -0,0 +1 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. diff --git a/tests/api_resources/test_pages_v1.py b/tests/api_resources/pages/test_v1.py similarity index 68% rename from tests/api_resources/test_pages_v1.py rename to tests/api_resources/pages/test_v1.py index d55cff5..fd27b6c 100644 --- a/tests/api_resources/test_pages_v1.py +++ b/tests/api_resources/pages/test_v1.py @@ -9,38 +9,38 @@ from unlayer import Unlayer, AsyncUnlayer from tests.utils import assert_matches_type -from unlayer.types import PagesV1RenderCreateResponse +from unlayer.types.pages import V1RenderCreateResponse base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") -class TestPagesV1: +class TestV1: parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) @parametrize def test_method_render_create(self, client: Unlayer) -> None: - pages_v1 = client.pages_v1.render_create( + v1 = client.pages.v1.render_create( design={ "counters": "bar", "body": "bar", }, ) - assert_matches_type(PagesV1RenderCreateResponse, pages_v1, path=["response"]) + assert_matches_type(V1RenderCreateResponse, v1, path=["response"]) @parametrize def test_method_render_create_with_all_params(self, client: Unlayer) -> None: - pages_v1 = client.pages_v1.render_create( + v1 = client.pages.v1.render_create( design={ "counters": "bar", "body": "bar", }, merge_tags={"foo": "string"}, ) - assert_matches_type(PagesV1RenderCreateResponse, pages_v1, path=["response"]) + assert_matches_type(V1RenderCreateResponse, v1, path=["response"]) @parametrize def test_raw_response_render_create(self, client: Unlayer) -> None: - response = client.pages_v1.with_raw_response.render_create( + response = client.pages.v1.with_raw_response.render_create( design={ "counters": "bar", "body": "bar", @@ -49,12 +49,12 @@ def test_raw_response_render_create(self, client: Unlayer) -> None: assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" - pages_v1 = response.parse() - assert_matches_type(PagesV1RenderCreateResponse, pages_v1, path=["response"]) + v1 = response.parse() + assert_matches_type(V1RenderCreateResponse, v1, path=["response"]) @parametrize def test_streaming_response_render_create(self, client: Unlayer) -> None: - with client.pages_v1.with_streaming_response.render_create( + with client.pages.v1.with_streaming_response.render_create( design={ "counters": "bar", "body": "bar", @@ -63,41 +63,41 @@ def test_streaming_response_render_create(self, client: Unlayer) -> None: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" - pages_v1 = response.parse() - assert_matches_type(PagesV1RenderCreateResponse, pages_v1, path=["response"]) + v1 = response.parse() + assert_matches_type(V1RenderCreateResponse, v1, path=["response"]) assert cast(Any, response.is_closed) is True -class TestAsyncPagesV1: +class TestAsyncV1: parametrize = pytest.mark.parametrize( "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] ) @parametrize async def test_method_render_create(self, async_client: AsyncUnlayer) -> None: - pages_v1 = await async_client.pages_v1.render_create( + v1 = await async_client.pages.v1.render_create( design={ "counters": "bar", "body": "bar", }, ) - assert_matches_type(PagesV1RenderCreateResponse, pages_v1, path=["response"]) + assert_matches_type(V1RenderCreateResponse, v1, path=["response"]) @parametrize async def test_method_render_create_with_all_params(self, async_client: AsyncUnlayer) -> None: - pages_v1 = await async_client.pages_v1.render_create( + v1 = await async_client.pages.v1.render_create( design={ "counters": "bar", "body": "bar", }, merge_tags={"foo": "string"}, ) - assert_matches_type(PagesV1RenderCreateResponse, pages_v1, path=["response"]) + assert_matches_type(V1RenderCreateResponse, v1, path=["response"]) @parametrize async def test_raw_response_render_create(self, async_client: AsyncUnlayer) -> None: - response = await async_client.pages_v1.with_raw_response.render_create( + response = await async_client.pages.v1.with_raw_response.render_create( design={ "counters": "bar", "body": "bar", @@ -106,12 +106,12 @@ async def test_raw_response_render_create(self, async_client: AsyncUnlayer) -> N assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" - pages_v1 = await response.parse() - assert_matches_type(PagesV1RenderCreateResponse, pages_v1, path=["response"]) + v1 = await response.parse() + assert_matches_type(V1RenderCreateResponse, v1, path=["response"]) @parametrize async def test_streaming_response_render_create(self, async_client: AsyncUnlayer) -> None: - async with async_client.pages_v1.with_streaming_response.render_create( + async with async_client.pages.v1.with_streaming_response.render_create( design={ "counters": "bar", "body": "bar", @@ -120,7 +120,7 @@ async def test_streaming_response_render_create(self, async_client: AsyncUnlayer assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" - pages_v1 = await response.parse() - assert_matches_type(PagesV1RenderCreateResponse, pages_v1, path=["response"]) + v1 = await response.parse() + assert_matches_type(V1RenderCreateResponse, v1, path=["response"]) assert cast(Any, response.is_closed) is True diff --git a/tests/api_resources/project/__init__.py b/tests/api_resources/project/__init__.py new file mode 100644 index 0000000..fd8019a --- /dev/null +++ b/tests/api_resources/project/__init__.py @@ -0,0 +1 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. diff --git a/tests/api_resources/test_project_v1.py b/tests/api_resources/project/test_v1.py similarity index 60% rename from tests/api_resources/test_project_v1.py rename to tests/api_resources/project/test_v1.py index 16dcd69..0cc89d2 100644 --- a/tests/api_resources/test_project_v1.py +++ b/tests/api_resources/project/test_v1.py @@ -9,1190 +9,1190 @@ from unlayer import Unlayer, AsyncUnlayer from tests.utils import assert_matches_type -from unlayer.types import ( - ProjectV1APIKeysListResponse, - ProjectV1CurrentListResponse, - ProjectV1DomainsListResponse, - ProjectV1APIKeysCreateResponse, - ProjectV1APIKeysUpdateResponse, - ProjectV1DomainsCreateResponse, - ProjectV1DomainsUpdateResponse, - ProjectV1TemplatesListResponse, - ProjectV1APIKeysRetrieveResponse, - ProjectV1DomainsRetrieveResponse, - ProjectV1TemplatesCreateResponse, - ProjectV1TemplatesUpdateResponse, - ProjectV1TemplatesRetrieveResponse, +from unlayer.types.project import ( + V1APIKeysListResponse, + V1CurrentListResponse, + V1DomainsListResponse, + V1APIKeysCreateResponse, + V1APIKeysUpdateResponse, + V1DomainsCreateResponse, + V1DomainsUpdateResponse, + V1TemplatesListResponse, + V1APIKeysRetrieveResponse, + V1DomainsRetrieveResponse, + V1TemplatesCreateResponse, + V1TemplatesUpdateResponse, + V1TemplatesRetrieveResponse, ) base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") -class TestProjectV1: +class TestV1: parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) @parametrize def test_method_api_keys_create(self, client: Unlayer) -> None: - project_v1 = client.project_v1.api_keys_create( + v1 = client.project.v1.api_keys_create( name="name", ) - assert_matches_type(ProjectV1APIKeysCreateResponse, project_v1, path=["response"]) + assert_matches_type(V1APIKeysCreateResponse, v1, path=["response"]) @parametrize def test_method_api_keys_create_with_all_params(self, client: Unlayer) -> None: - project_v1 = client.project_v1.api_keys_create( + v1 = client.project.v1.api_keys_create( name="name", domains=["string"], ) - assert_matches_type(ProjectV1APIKeysCreateResponse, project_v1, path=["response"]) + assert_matches_type(V1APIKeysCreateResponse, v1, path=["response"]) @parametrize def test_raw_response_api_keys_create(self, client: Unlayer) -> None: - response = client.project_v1.with_raw_response.api_keys_create( + response = client.project.v1.with_raw_response.api_keys_create( name="name", ) assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" - project_v1 = response.parse() - assert_matches_type(ProjectV1APIKeysCreateResponse, project_v1, path=["response"]) + v1 = response.parse() + assert_matches_type(V1APIKeysCreateResponse, v1, path=["response"]) @parametrize def test_streaming_response_api_keys_create(self, client: Unlayer) -> None: - with client.project_v1.with_streaming_response.api_keys_create( + with client.project.v1.with_streaming_response.api_keys_create( name="name", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" - project_v1 = response.parse() - assert_matches_type(ProjectV1APIKeysCreateResponse, project_v1, path=["response"]) + v1 = response.parse() + assert_matches_type(V1APIKeysCreateResponse, v1, path=["response"]) assert cast(Any, response.is_closed) is True @parametrize def test_method_api_keys_delete(self, client: Unlayer) -> None: - project_v1 = client.project_v1.api_keys_delete( + v1 = client.project.v1.api_keys_delete( "id", ) - assert project_v1 is None + assert v1 is None @parametrize def test_raw_response_api_keys_delete(self, client: Unlayer) -> None: - response = client.project_v1.with_raw_response.api_keys_delete( + response = client.project.v1.with_raw_response.api_keys_delete( "id", ) assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" - project_v1 = response.parse() - assert project_v1 is None + v1 = response.parse() + assert v1 is None @parametrize def test_streaming_response_api_keys_delete(self, client: Unlayer) -> None: - with client.project_v1.with_streaming_response.api_keys_delete( + with client.project.v1.with_streaming_response.api_keys_delete( "id", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" - project_v1 = response.parse() - assert project_v1 is None + v1 = response.parse() + assert v1 is None assert cast(Any, response.is_closed) is True @parametrize def test_path_params_api_keys_delete(self, client: Unlayer) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): - client.project_v1.with_raw_response.api_keys_delete( + client.project.v1.with_raw_response.api_keys_delete( "", ) @parametrize def test_method_api_keys_list(self, client: Unlayer) -> None: - project_v1 = client.project_v1.api_keys_list() - assert_matches_type(ProjectV1APIKeysListResponse, project_v1, path=["response"]) + v1 = client.project.v1.api_keys_list() + assert_matches_type(V1APIKeysListResponse, v1, path=["response"]) @parametrize def test_raw_response_api_keys_list(self, client: Unlayer) -> None: - response = client.project_v1.with_raw_response.api_keys_list() + response = client.project.v1.with_raw_response.api_keys_list() assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" - project_v1 = response.parse() - assert_matches_type(ProjectV1APIKeysListResponse, project_v1, path=["response"]) + v1 = response.parse() + assert_matches_type(V1APIKeysListResponse, v1, path=["response"]) @parametrize def test_streaming_response_api_keys_list(self, client: Unlayer) -> None: - with client.project_v1.with_streaming_response.api_keys_list() as response: + with client.project.v1.with_streaming_response.api_keys_list() as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" - project_v1 = response.parse() - assert_matches_type(ProjectV1APIKeysListResponse, project_v1, path=["response"]) + v1 = response.parse() + assert_matches_type(V1APIKeysListResponse, v1, path=["response"]) assert cast(Any, response.is_closed) is True @parametrize def test_method_api_keys_retrieve(self, client: Unlayer) -> None: - project_v1 = client.project_v1.api_keys_retrieve( + v1 = client.project.v1.api_keys_retrieve( "id", ) - assert_matches_type(ProjectV1APIKeysRetrieveResponse, project_v1, path=["response"]) + assert_matches_type(V1APIKeysRetrieveResponse, v1, path=["response"]) @parametrize def test_raw_response_api_keys_retrieve(self, client: Unlayer) -> None: - response = client.project_v1.with_raw_response.api_keys_retrieve( + response = client.project.v1.with_raw_response.api_keys_retrieve( "id", ) assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" - project_v1 = response.parse() - assert_matches_type(ProjectV1APIKeysRetrieveResponse, project_v1, path=["response"]) + v1 = response.parse() + assert_matches_type(V1APIKeysRetrieveResponse, v1, path=["response"]) @parametrize def test_streaming_response_api_keys_retrieve(self, client: Unlayer) -> None: - with client.project_v1.with_streaming_response.api_keys_retrieve( + with client.project.v1.with_streaming_response.api_keys_retrieve( "id", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" - project_v1 = response.parse() - assert_matches_type(ProjectV1APIKeysRetrieveResponse, project_v1, path=["response"]) + v1 = response.parse() + assert_matches_type(V1APIKeysRetrieveResponse, v1, path=["response"]) assert cast(Any, response.is_closed) is True @parametrize def test_path_params_api_keys_retrieve(self, client: Unlayer) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): - client.project_v1.with_raw_response.api_keys_retrieve( + client.project.v1.with_raw_response.api_keys_retrieve( "", ) @parametrize def test_method_api_keys_update(self, client: Unlayer) -> None: - project_v1 = client.project_v1.api_keys_update( + v1 = client.project.v1.api_keys_update( id="id", ) - assert_matches_type(ProjectV1APIKeysUpdateResponse, project_v1, path=["response"]) + assert_matches_type(V1APIKeysUpdateResponse, v1, path=["response"]) @parametrize def test_method_api_keys_update_with_all_params(self, client: Unlayer) -> None: - project_v1 = client.project_v1.api_keys_update( + v1 = client.project.v1.api_keys_update( id="id", active=True, domains=["string"], name="name", ) - assert_matches_type(ProjectV1APIKeysUpdateResponse, project_v1, path=["response"]) + assert_matches_type(V1APIKeysUpdateResponse, v1, path=["response"]) @parametrize def test_raw_response_api_keys_update(self, client: Unlayer) -> None: - response = client.project_v1.with_raw_response.api_keys_update( + response = client.project.v1.with_raw_response.api_keys_update( id="id", ) assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" - project_v1 = response.parse() - assert_matches_type(ProjectV1APIKeysUpdateResponse, project_v1, path=["response"]) + v1 = response.parse() + assert_matches_type(V1APIKeysUpdateResponse, v1, path=["response"]) @parametrize def test_streaming_response_api_keys_update(self, client: Unlayer) -> None: - with client.project_v1.with_streaming_response.api_keys_update( + with client.project.v1.with_streaming_response.api_keys_update( id="id", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" - project_v1 = response.parse() - assert_matches_type(ProjectV1APIKeysUpdateResponse, project_v1, path=["response"]) + v1 = response.parse() + assert_matches_type(V1APIKeysUpdateResponse, v1, path=["response"]) assert cast(Any, response.is_closed) is True @parametrize def test_path_params_api_keys_update(self, client: Unlayer) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): - client.project_v1.with_raw_response.api_keys_update( + client.project.v1.with_raw_response.api_keys_update( id="", ) @parametrize def test_method_current_list(self, client: Unlayer) -> None: - project_v1 = client.project_v1.current_list() - assert_matches_type(ProjectV1CurrentListResponse, project_v1, path=["response"]) + v1 = client.project.v1.current_list() + assert_matches_type(V1CurrentListResponse, v1, path=["response"]) @parametrize def test_raw_response_current_list(self, client: Unlayer) -> None: - response = client.project_v1.with_raw_response.current_list() + response = client.project.v1.with_raw_response.current_list() assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" - project_v1 = response.parse() - assert_matches_type(ProjectV1CurrentListResponse, project_v1, path=["response"]) + v1 = response.parse() + assert_matches_type(V1CurrentListResponse, v1, path=["response"]) @parametrize def test_streaming_response_current_list(self, client: Unlayer) -> None: - with client.project_v1.with_streaming_response.current_list() as response: + with client.project.v1.with_streaming_response.current_list() as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" - project_v1 = response.parse() - assert_matches_type(ProjectV1CurrentListResponse, project_v1, path=["response"]) + v1 = response.parse() + assert_matches_type(V1CurrentListResponse, v1, path=["response"]) assert cast(Any, response.is_closed) is True @parametrize def test_method_domains_create(self, client: Unlayer) -> None: - project_v1 = client.project_v1.domains_create( + v1 = client.project.v1.domains_create( domain="domain", ) - assert_matches_type(ProjectV1DomainsCreateResponse, project_v1, path=["response"]) + assert_matches_type(V1DomainsCreateResponse, v1, path=["response"]) @parametrize def test_raw_response_domains_create(self, client: Unlayer) -> None: - response = client.project_v1.with_raw_response.domains_create( + response = client.project.v1.with_raw_response.domains_create( domain="domain", ) assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" - project_v1 = response.parse() - assert_matches_type(ProjectV1DomainsCreateResponse, project_v1, path=["response"]) + v1 = response.parse() + assert_matches_type(V1DomainsCreateResponse, v1, path=["response"]) @parametrize def test_streaming_response_domains_create(self, client: Unlayer) -> None: - with client.project_v1.with_streaming_response.domains_create( + with client.project.v1.with_streaming_response.domains_create( domain="domain", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" - project_v1 = response.parse() - assert_matches_type(ProjectV1DomainsCreateResponse, project_v1, path=["response"]) + v1 = response.parse() + assert_matches_type(V1DomainsCreateResponse, v1, path=["response"]) assert cast(Any, response.is_closed) is True @parametrize def test_method_domains_delete(self, client: Unlayer) -> None: - project_v1 = client.project_v1.domains_delete( + v1 = client.project.v1.domains_delete( "id", ) - assert project_v1 is None + assert v1 is None @parametrize def test_raw_response_domains_delete(self, client: Unlayer) -> None: - response = client.project_v1.with_raw_response.domains_delete( + response = client.project.v1.with_raw_response.domains_delete( "id", ) assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" - project_v1 = response.parse() - assert project_v1 is None + v1 = response.parse() + assert v1 is None @parametrize def test_streaming_response_domains_delete(self, client: Unlayer) -> None: - with client.project_v1.with_streaming_response.domains_delete( + with client.project.v1.with_streaming_response.domains_delete( "id", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" - project_v1 = response.parse() - assert project_v1 is None + v1 = response.parse() + assert v1 is None assert cast(Any, response.is_closed) is True @parametrize def test_path_params_domains_delete(self, client: Unlayer) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): - client.project_v1.with_raw_response.domains_delete( + client.project.v1.with_raw_response.domains_delete( "", ) @parametrize def test_method_domains_list(self, client: Unlayer) -> None: - project_v1 = client.project_v1.domains_list() - assert_matches_type(ProjectV1DomainsListResponse, project_v1, path=["response"]) + v1 = client.project.v1.domains_list() + assert_matches_type(V1DomainsListResponse, v1, path=["response"]) @parametrize def test_raw_response_domains_list(self, client: Unlayer) -> None: - response = client.project_v1.with_raw_response.domains_list() + response = client.project.v1.with_raw_response.domains_list() assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" - project_v1 = response.parse() - assert_matches_type(ProjectV1DomainsListResponse, project_v1, path=["response"]) + v1 = response.parse() + assert_matches_type(V1DomainsListResponse, v1, path=["response"]) @parametrize def test_streaming_response_domains_list(self, client: Unlayer) -> None: - with client.project_v1.with_streaming_response.domains_list() as response: + with client.project.v1.with_streaming_response.domains_list() as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" - project_v1 = response.parse() - assert_matches_type(ProjectV1DomainsListResponse, project_v1, path=["response"]) + v1 = response.parse() + assert_matches_type(V1DomainsListResponse, v1, path=["response"]) assert cast(Any, response.is_closed) is True @parametrize def test_method_domains_retrieve(self, client: Unlayer) -> None: - project_v1 = client.project_v1.domains_retrieve( + v1 = client.project.v1.domains_retrieve( "id", ) - assert_matches_type(ProjectV1DomainsRetrieveResponse, project_v1, path=["response"]) + assert_matches_type(V1DomainsRetrieveResponse, v1, path=["response"]) @parametrize def test_raw_response_domains_retrieve(self, client: Unlayer) -> None: - response = client.project_v1.with_raw_response.domains_retrieve( + response = client.project.v1.with_raw_response.domains_retrieve( "id", ) assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" - project_v1 = response.parse() - assert_matches_type(ProjectV1DomainsRetrieveResponse, project_v1, path=["response"]) + v1 = response.parse() + assert_matches_type(V1DomainsRetrieveResponse, v1, path=["response"]) @parametrize def test_streaming_response_domains_retrieve(self, client: Unlayer) -> None: - with client.project_v1.with_streaming_response.domains_retrieve( + with client.project.v1.with_streaming_response.domains_retrieve( "id", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" - project_v1 = response.parse() - assert_matches_type(ProjectV1DomainsRetrieveResponse, project_v1, path=["response"]) + v1 = response.parse() + assert_matches_type(V1DomainsRetrieveResponse, v1, path=["response"]) assert cast(Any, response.is_closed) is True @parametrize def test_path_params_domains_retrieve(self, client: Unlayer) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): - client.project_v1.with_raw_response.domains_retrieve( + client.project.v1.with_raw_response.domains_retrieve( "", ) @parametrize def test_method_domains_update(self, client: Unlayer) -> None: - project_v1 = client.project_v1.domains_update( + v1 = client.project.v1.domains_update( id="id", ) - assert_matches_type(ProjectV1DomainsUpdateResponse, project_v1, path=["response"]) + assert_matches_type(V1DomainsUpdateResponse, v1, path=["response"]) @parametrize def test_method_domains_update_with_all_params(self, client: Unlayer) -> None: - project_v1 = client.project_v1.domains_update( + v1 = client.project.v1.domains_update( id="id", domain="domain", ) - assert_matches_type(ProjectV1DomainsUpdateResponse, project_v1, path=["response"]) + assert_matches_type(V1DomainsUpdateResponse, v1, path=["response"]) @parametrize def test_raw_response_domains_update(self, client: Unlayer) -> None: - response = client.project_v1.with_raw_response.domains_update( + response = client.project.v1.with_raw_response.domains_update( id="id", ) assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" - project_v1 = response.parse() - assert_matches_type(ProjectV1DomainsUpdateResponse, project_v1, path=["response"]) + v1 = response.parse() + assert_matches_type(V1DomainsUpdateResponse, v1, path=["response"]) @parametrize def test_streaming_response_domains_update(self, client: Unlayer) -> None: - with client.project_v1.with_streaming_response.domains_update( + with client.project.v1.with_streaming_response.domains_update( id="id", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" - project_v1 = response.parse() - assert_matches_type(ProjectV1DomainsUpdateResponse, project_v1, path=["response"]) + v1 = response.parse() + assert_matches_type(V1DomainsUpdateResponse, v1, path=["response"]) assert cast(Any, response.is_closed) is True @parametrize def test_path_params_domains_update(self, client: Unlayer) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): - client.project_v1.with_raw_response.domains_update( + client.project.v1.with_raw_response.domains_update( id="", ) @parametrize def test_method_templates_create(self, client: Unlayer) -> None: - project_v1 = client.project_v1.templates_create( + v1 = client.project.v1.templates_create( name="name", ) - assert_matches_type(ProjectV1TemplatesCreateResponse, project_v1, path=["response"]) + assert_matches_type(V1TemplatesCreateResponse, v1, path=["response"]) @parametrize def test_method_templates_create_with_all_params(self, client: Unlayer) -> None: - project_v1 = client.project_v1.templates_create( + v1 = client.project.v1.templates_create( name="name", body="body", subject="subject", ) - assert_matches_type(ProjectV1TemplatesCreateResponse, project_v1, path=["response"]) + assert_matches_type(V1TemplatesCreateResponse, v1, path=["response"]) @parametrize def test_raw_response_templates_create(self, client: Unlayer) -> None: - response = client.project_v1.with_raw_response.templates_create( + response = client.project.v1.with_raw_response.templates_create( name="name", ) assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" - project_v1 = response.parse() - assert_matches_type(ProjectV1TemplatesCreateResponse, project_v1, path=["response"]) + v1 = response.parse() + assert_matches_type(V1TemplatesCreateResponse, v1, path=["response"]) @parametrize def test_streaming_response_templates_create(self, client: Unlayer) -> None: - with client.project_v1.with_streaming_response.templates_create( + with client.project.v1.with_streaming_response.templates_create( name="name", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" - project_v1 = response.parse() - assert_matches_type(ProjectV1TemplatesCreateResponse, project_v1, path=["response"]) + v1 = response.parse() + assert_matches_type(V1TemplatesCreateResponse, v1, path=["response"]) assert cast(Any, response.is_closed) is True @parametrize def test_method_templates_delete(self, client: Unlayer) -> None: - project_v1 = client.project_v1.templates_delete( + v1 = client.project.v1.templates_delete( "id", ) - assert project_v1 is None + assert v1 is None @parametrize def test_raw_response_templates_delete(self, client: Unlayer) -> None: - response = client.project_v1.with_raw_response.templates_delete( + response = client.project.v1.with_raw_response.templates_delete( "id", ) assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" - project_v1 = response.parse() - assert project_v1 is None + v1 = response.parse() + assert v1 is None @parametrize def test_streaming_response_templates_delete(self, client: Unlayer) -> None: - with client.project_v1.with_streaming_response.templates_delete( + with client.project.v1.with_streaming_response.templates_delete( "id", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" - project_v1 = response.parse() - assert project_v1 is None + v1 = response.parse() + assert v1 is None assert cast(Any, response.is_closed) is True @parametrize def test_path_params_templates_delete(self, client: Unlayer) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): - client.project_v1.with_raw_response.templates_delete( + client.project.v1.with_raw_response.templates_delete( "", ) @parametrize def test_method_templates_list(self, client: Unlayer) -> None: - project_v1 = client.project_v1.templates_list() - assert_matches_type(ProjectV1TemplatesListResponse, project_v1, path=["response"]) + v1 = client.project.v1.templates_list() + assert_matches_type(V1TemplatesListResponse, v1, path=["response"]) @parametrize def test_raw_response_templates_list(self, client: Unlayer) -> None: - response = client.project_v1.with_raw_response.templates_list() + response = client.project.v1.with_raw_response.templates_list() assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" - project_v1 = response.parse() - assert_matches_type(ProjectV1TemplatesListResponse, project_v1, path=["response"]) + v1 = response.parse() + assert_matches_type(V1TemplatesListResponse, v1, path=["response"]) @parametrize def test_streaming_response_templates_list(self, client: Unlayer) -> None: - with client.project_v1.with_streaming_response.templates_list() as response: + with client.project.v1.with_streaming_response.templates_list() as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" - project_v1 = response.parse() - assert_matches_type(ProjectV1TemplatesListResponse, project_v1, path=["response"]) + v1 = response.parse() + assert_matches_type(V1TemplatesListResponse, v1, path=["response"]) assert cast(Any, response.is_closed) is True @parametrize def test_method_templates_retrieve(self, client: Unlayer) -> None: - project_v1 = client.project_v1.templates_retrieve( + v1 = client.project.v1.templates_retrieve( "id", ) - assert_matches_type(ProjectV1TemplatesRetrieveResponse, project_v1, path=["response"]) + assert_matches_type(V1TemplatesRetrieveResponse, v1, path=["response"]) @parametrize def test_raw_response_templates_retrieve(self, client: Unlayer) -> None: - response = client.project_v1.with_raw_response.templates_retrieve( + response = client.project.v1.with_raw_response.templates_retrieve( "id", ) assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" - project_v1 = response.parse() - assert_matches_type(ProjectV1TemplatesRetrieveResponse, project_v1, path=["response"]) + v1 = response.parse() + assert_matches_type(V1TemplatesRetrieveResponse, v1, path=["response"]) @parametrize def test_streaming_response_templates_retrieve(self, client: Unlayer) -> None: - with client.project_v1.with_streaming_response.templates_retrieve( + with client.project.v1.with_streaming_response.templates_retrieve( "id", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" - project_v1 = response.parse() - assert_matches_type(ProjectV1TemplatesRetrieveResponse, project_v1, path=["response"]) + v1 = response.parse() + assert_matches_type(V1TemplatesRetrieveResponse, v1, path=["response"]) assert cast(Any, response.is_closed) is True @parametrize def test_path_params_templates_retrieve(self, client: Unlayer) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): - client.project_v1.with_raw_response.templates_retrieve( + client.project.v1.with_raw_response.templates_retrieve( "", ) @parametrize def test_method_templates_update(self, client: Unlayer) -> None: - project_v1 = client.project_v1.templates_update( + v1 = client.project.v1.templates_update( id="id", ) - assert_matches_type(ProjectV1TemplatesUpdateResponse, project_v1, path=["response"]) + assert_matches_type(V1TemplatesUpdateResponse, v1, path=["response"]) @parametrize def test_method_templates_update_with_all_params(self, client: Unlayer) -> None: - project_v1 = client.project_v1.templates_update( + v1 = client.project.v1.templates_update( id="id", body="body", name="name", subject="subject", ) - assert_matches_type(ProjectV1TemplatesUpdateResponse, project_v1, path=["response"]) + assert_matches_type(V1TemplatesUpdateResponse, v1, path=["response"]) @parametrize def test_raw_response_templates_update(self, client: Unlayer) -> None: - response = client.project_v1.with_raw_response.templates_update( + response = client.project.v1.with_raw_response.templates_update( id="id", ) assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" - project_v1 = response.parse() - assert_matches_type(ProjectV1TemplatesUpdateResponse, project_v1, path=["response"]) + v1 = response.parse() + assert_matches_type(V1TemplatesUpdateResponse, v1, path=["response"]) @parametrize def test_streaming_response_templates_update(self, client: Unlayer) -> None: - with client.project_v1.with_streaming_response.templates_update( + with client.project.v1.with_streaming_response.templates_update( id="id", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" - project_v1 = response.parse() - assert_matches_type(ProjectV1TemplatesUpdateResponse, project_v1, path=["response"]) + v1 = response.parse() + assert_matches_type(V1TemplatesUpdateResponse, v1, path=["response"]) assert cast(Any, response.is_closed) is True @parametrize def test_path_params_templates_update(self, client: Unlayer) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): - client.project_v1.with_raw_response.templates_update( + client.project.v1.with_raw_response.templates_update( id="", ) -class TestAsyncProjectV1: +class TestAsyncV1: parametrize = pytest.mark.parametrize( "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] ) @parametrize async def test_method_api_keys_create(self, async_client: AsyncUnlayer) -> None: - project_v1 = await async_client.project_v1.api_keys_create( + v1 = await async_client.project.v1.api_keys_create( name="name", ) - assert_matches_type(ProjectV1APIKeysCreateResponse, project_v1, path=["response"]) + assert_matches_type(V1APIKeysCreateResponse, v1, path=["response"]) @parametrize async def test_method_api_keys_create_with_all_params(self, async_client: AsyncUnlayer) -> None: - project_v1 = await async_client.project_v1.api_keys_create( + v1 = await async_client.project.v1.api_keys_create( name="name", domains=["string"], ) - assert_matches_type(ProjectV1APIKeysCreateResponse, project_v1, path=["response"]) + assert_matches_type(V1APIKeysCreateResponse, v1, path=["response"]) @parametrize async def test_raw_response_api_keys_create(self, async_client: AsyncUnlayer) -> None: - response = await async_client.project_v1.with_raw_response.api_keys_create( + response = await async_client.project.v1.with_raw_response.api_keys_create( name="name", ) assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" - project_v1 = await response.parse() - assert_matches_type(ProjectV1APIKeysCreateResponse, project_v1, path=["response"]) + v1 = await response.parse() + assert_matches_type(V1APIKeysCreateResponse, v1, path=["response"]) @parametrize async def test_streaming_response_api_keys_create(self, async_client: AsyncUnlayer) -> None: - async with async_client.project_v1.with_streaming_response.api_keys_create( + async with async_client.project.v1.with_streaming_response.api_keys_create( name="name", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" - project_v1 = await response.parse() - assert_matches_type(ProjectV1APIKeysCreateResponse, project_v1, path=["response"]) + v1 = await response.parse() + assert_matches_type(V1APIKeysCreateResponse, v1, path=["response"]) assert cast(Any, response.is_closed) is True @parametrize async def test_method_api_keys_delete(self, async_client: AsyncUnlayer) -> None: - project_v1 = await async_client.project_v1.api_keys_delete( + v1 = await async_client.project.v1.api_keys_delete( "id", ) - assert project_v1 is None + assert v1 is None @parametrize async def test_raw_response_api_keys_delete(self, async_client: AsyncUnlayer) -> None: - response = await async_client.project_v1.with_raw_response.api_keys_delete( + response = await async_client.project.v1.with_raw_response.api_keys_delete( "id", ) assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" - project_v1 = await response.parse() - assert project_v1 is None + v1 = await response.parse() + assert v1 is None @parametrize async def test_streaming_response_api_keys_delete(self, async_client: AsyncUnlayer) -> None: - async with async_client.project_v1.with_streaming_response.api_keys_delete( + async with async_client.project.v1.with_streaming_response.api_keys_delete( "id", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" - project_v1 = await response.parse() - assert project_v1 is None + v1 = await response.parse() + assert v1 is None assert cast(Any, response.is_closed) is True @parametrize async def test_path_params_api_keys_delete(self, async_client: AsyncUnlayer) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): - await async_client.project_v1.with_raw_response.api_keys_delete( + await async_client.project.v1.with_raw_response.api_keys_delete( "", ) @parametrize async def test_method_api_keys_list(self, async_client: AsyncUnlayer) -> None: - project_v1 = await async_client.project_v1.api_keys_list() - assert_matches_type(ProjectV1APIKeysListResponse, project_v1, path=["response"]) + v1 = await async_client.project.v1.api_keys_list() + assert_matches_type(V1APIKeysListResponse, v1, path=["response"]) @parametrize async def test_raw_response_api_keys_list(self, async_client: AsyncUnlayer) -> None: - response = await async_client.project_v1.with_raw_response.api_keys_list() + response = await async_client.project.v1.with_raw_response.api_keys_list() assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" - project_v1 = await response.parse() - assert_matches_type(ProjectV1APIKeysListResponse, project_v1, path=["response"]) + v1 = await response.parse() + assert_matches_type(V1APIKeysListResponse, v1, path=["response"]) @parametrize async def test_streaming_response_api_keys_list(self, async_client: AsyncUnlayer) -> None: - async with async_client.project_v1.with_streaming_response.api_keys_list() as response: + async with async_client.project.v1.with_streaming_response.api_keys_list() as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" - project_v1 = await response.parse() - assert_matches_type(ProjectV1APIKeysListResponse, project_v1, path=["response"]) + v1 = await response.parse() + assert_matches_type(V1APIKeysListResponse, v1, path=["response"]) assert cast(Any, response.is_closed) is True @parametrize async def test_method_api_keys_retrieve(self, async_client: AsyncUnlayer) -> None: - project_v1 = await async_client.project_v1.api_keys_retrieve( + v1 = await async_client.project.v1.api_keys_retrieve( "id", ) - assert_matches_type(ProjectV1APIKeysRetrieveResponse, project_v1, path=["response"]) + assert_matches_type(V1APIKeysRetrieveResponse, v1, path=["response"]) @parametrize async def test_raw_response_api_keys_retrieve(self, async_client: AsyncUnlayer) -> None: - response = await async_client.project_v1.with_raw_response.api_keys_retrieve( + response = await async_client.project.v1.with_raw_response.api_keys_retrieve( "id", ) assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" - project_v1 = await response.parse() - assert_matches_type(ProjectV1APIKeysRetrieveResponse, project_v1, path=["response"]) + v1 = await response.parse() + assert_matches_type(V1APIKeysRetrieveResponse, v1, path=["response"]) @parametrize async def test_streaming_response_api_keys_retrieve(self, async_client: AsyncUnlayer) -> None: - async with async_client.project_v1.with_streaming_response.api_keys_retrieve( + async with async_client.project.v1.with_streaming_response.api_keys_retrieve( "id", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" - project_v1 = await response.parse() - assert_matches_type(ProjectV1APIKeysRetrieveResponse, project_v1, path=["response"]) + v1 = await response.parse() + assert_matches_type(V1APIKeysRetrieveResponse, v1, path=["response"]) assert cast(Any, response.is_closed) is True @parametrize async def test_path_params_api_keys_retrieve(self, async_client: AsyncUnlayer) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): - await async_client.project_v1.with_raw_response.api_keys_retrieve( + await async_client.project.v1.with_raw_response.api_keys_retrieve( "", ) @parametrize async def test_method_api_keys_update(self, async_client: AsyncUnlayer) -> None: - project_v1 = await async_client.project_v1.api_keys_update( + v1 = await async_client.project.v1.api_keys_update( id="id", ) - assert_matches_type(ProjectV1APIKeysUpdateResponse, project_v1, path=["response"]) + assert_matches_type(V1APIKeysUpdateResponse, v1, path=["response"]) @parametrize async def test_method_api_keys_update_with_all_params(self, async_client: AsyncUnlayer) -> None: - project_v1 = await async_client.project_v1.api_keys_update( + v1 = await async_client.project.v1.api_keys_update( id="id", active=True, domains=["string"], name="name", ) - assert_matches_type(ProjectV1APIKeysUpdateResponse, project_v1, path=["response"]) + assert_matches_type(V1APIKeysUpdateResponse, v1, path=["response"]) @parametrize async def test_raw_response_api_keys_update(self, async_client: AsyncUnlayer) -> None: - response = await async_client.project_v1.with_raw_response.api_keys_update( + response = await async_client.project.v1.with_raw_response.api_keys_update( id="id", ) assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" - project_v1 = await response.parse() - assert_matches_type(ProjectV1APIKeysUpdateResponse, project_v1, path=["response"]) + v1 = await response.parse() + assert_matches_type(V1APIKeysUpdateResponse, v1, path=["response"]) @parametrize async def test_streaming_response_api_keys_update(self, async_client: AsyncUnlayer) -> None: - async with async_client.project_v1.with_streaming_response.api_keys_update( + async with async_client.project.v1.with_streaming_response.api_keys_update( id="id", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" - project_v1 = await response.parse() - assert_matches_type(ProjectV1APIKeysUpdateResponse, project_v1, path=["response"]) + v1 = await response.parse() + assert_matches_type(V1APIKeysUpdateResponse, v1, path=["response"]) assert cast(Any, response.is_closed) is True @parametrize async def test_path_params_api_keys_update(self, async_client: AsyncUnlayer) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): - await async_client.project_v1.with_raw_response.api_keys_update( + await async_client.project.v1.with_raw_response.api_keys_update( id="", ) @parametrize async def test_method_current_list(self, async_client: AsyncUnlayer) -> None: - project_v1 = await async_client.project_v1.current_list() - assert_matches_type(ProjectV1CurrentListResponse, project_v1, path=["response"]) + v1 = await async_client.project.v1.current_list() + assert_matches_type(V1CurrentListResponse, v1, path=["response"]) @parametrize async def test_raw_response_current_list(self, async_client: AsyncUnlayer) -> None: - response = await async_client.project_v1.with_raw_response.current_list() + response = await async_client.project.v1.with_raw_response.current_list() assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" - project_v1 = await response.parse() - assert_matches_type(ProjectV1CurrentListResponse, project_v1, path=["response"]) + v1 = await response.parse() + assert_matches_type(V1CurrentListResponse, v1, path=["response"]) @parametrize async def test_streaming_response_current_list(self, async_client: AsyncUnlayer) -> None: - async with async_client.project_v1.with_streaming_response.current_list() as response: + async with async_client.project.v1.with_streaming_response.current_list() as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" - project_v1 = await response.parse() - assert_matches_type(ProjectV1CurrentListResponse, project_v1, path=["response"]) + v1 = await response.parse() + assert_matches_type(V1CurrentListResponse, v1, path=["response"]) assert cast(Any, response.is_closed) is True @parametrize async def test_method_domains_create(self, async_client: AsyncUnlayer) -> None: - project_v1 = await async_client.project_v1.domains_create( + v1 = await async_client.project.v1.domains_create( domain="domain", ) - assert_matches_type(ProjectV1DomainsCreateResponse, project_v1, path=["response"]) + assert_matches_type(V1DomainsCreateResponse, v1, path=["response"]) @parametrize async def test_raw_response_domains_create(self, async_client: AsyncUnlayer) -> None: - response = await async_client.project_v1.with_raw_response.domains_create( + response = await async_client.project.v1.with_raw_response.domains_create( domain="domain", ) assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" - project_v1 = await response.parse() - assert_matches_type(ProjectV1DomainsCreateResponse, project_v1, path=["response"]) + v1 = await response.parse() + assert_matches_type(V1DomainsCreateResponse, v1, path=["response"]) @parametrize async def test_streaming_response_domains_create(self, async_client: AsyncUnlayer) -> None: - async with async_client.project_v1.with_streaming_response.domains_create( + async with async_client.project.v1.with_streaming_response.domains_create( domain="domain", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" - project_v1 = await response.parse() - assert_matches_type(ProjectV1DomainsCreateResponse, project_v1, path=["response"]) + v1 = await response.parse() + assert_matches_type(V1DomainsCreateResponse, v1, path=["response"]) assert cast(Any, response.is_closed) is True @parametrize async def test_method_domains_delete(self, async_client: AsyncUnlayer) -> None: - project_v1 = await async_client.project_v1.domains_delete( + v1 = await async_client.project.v1.domains_delete( "id", ) - assert project_v1 is None + assert v1 is None @parametrize async def test_raw_response_domains_delete(self, async_client: AsyncUnlayer) -> None: - response = await async_client.project_v1.with_raw_response.domains_delete( + response = await async_client.project.v1.with_raw_response.domains_delete( "id", ) assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" - project_v1 = await response.parse() - assert project_v1 is None + v1 = await response.parse() + assert v1 is None @parametrize async def test_streaming_response_domains_delete(self, async_client: AsyncUnlayer) -> None: - async with async_client.project_v1.with_streaming_response.domains_delete( + async with async_client.project.v1.with_streaming_response.domains_delete( "id", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" - project_v1 = await response.parse() - assert project_v1 is None + v1 = await response.parse() + assert v1 is None assert cast(Any, response.is_closed) is True @parametrize async def test_path_params_domains_delete(self, async_client: AsyncUnlayer) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): - await async_client.project_v1.with_raw_response.domains_delete( + await async_client.project.v1.with_raw_response.domains_delete( "", ) @parametrize async def test_method_domains_list(self, async_client: AsyncUnlayer) -> None: - project_v1 = await async_client.project_v1.domains_list() - assert_matches_type(ProjectV1DomainsListResponse, project_v1, path=["response"]) + v1 = await async_client.project.v1.domains_list() + assert_matches_type(V1DomainsListResponse, v1, path=["response"]) @parametrize async def test_raw_response_domains_list(self, async_client: AsyncUnlayer) -> None: - response = await async_client.project_v1.with_raw_response.domains_list() + response = await async_client.project.v1.with_raw_response.domains_list() assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" - project_v1 = await response.parse() - assert_matches_type(ProjectV1DomainsListResponse, project_v1, path=["response"]) + v1 = await response.parse() + assert_matches_type(V1DomainsListResponse, v1, path=["response"]) @parametrize async def test_streaming_response_domains_list(self, async_client: AsyncUnlayer) -> None: - async with async_client.project_v1.with_streaming_response.domains_list() as response: + async with async_client.project.v1.with_streaming_response.domains_list() as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" - project_v1 = await response.parse() - assert_matches_type(ProjectV1DomainsListResponse, project_v1, path=["response"]) + v1 = await response.parse() + assert_matches_type(V1DomainsListResponse, v1, path=["response"]) assert cast(Any, response.is_closed) is True @parametrize async def test_method_domains_retrieve(self, async_client: AsyncUnlayer) -> None: - project_v1 = await async_client.project_v1.domains_retrieve( + v1 = await async_client.project.v1.domains_retrieve( "id", ) - assert_matches_type(ProjectV1DomainsRetrieveResponse, project_v1, path=["response"]) + assert_matches_type(V1DomainsRetrieveResponse, v1, path=["response"]) @parametrize async def test_raw_response_domains_retrieve(self, async_client: AsyncUnlayer) -> None: - response = await async_client.project_v1.with_raw_response.domains_retrieve( + response = await async_client.project.v1.with_raw_response.domains_retrieve( "id", ) assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" - project_v1 = await response.parse() - assert_matches_type(ProjectV1DomainsRetrieveResponse, project_v1, path=["response"]) + v1 = await response.parse() + assert_matches_type(V1DomainsRetrieveResponse, v1, path=["response"]) @parametrize async def test_streaming_response_domains_retrieve(self, async_client: AsyncUnlayer) -> None: - async with async_client.project_v1.with_streaming_response.domains_retrieve( + async with async_client.project.v1.with_streaming_response.domains_retrieve( "id", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" - project_v1 = await response.parse() - assert_matches_type(ProjectV1DomainsRetrieveResponse, project_v1, path=["response"]) + v1 = await response.parse() + assert_matches_type(V1DomainsRetrieveResponse, v1, path=["response"]) assert cast(Any, response.is_closed) is True @parametrize async def test_path_params_domains_retrieve(self, async_client: AsyncUnlayer) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): - await async_client.project_v1.with_raw_response.domains_retrieve( + await async_client.project.v1.with_raw_response.domains_retrieve( "", ) @parametrize async def test_method_domains_update(self, async_client: AsyncUnlayer) -> None: - project_v1 = await async_client.project_v1.domains_update( + v1 = await async_client.project.v1.domains_update( id="id", ) - assert_matches_type(ProjectV1DomainsUpdateResponse, project_v1, path=["response"]) + assert_matches_type(V1DomainsUpdateResponse, v1, path=["response"]) @parametrize async def test_method_domains_update_with_all_params(self, async_client: AsyncUnlayer) -> None: - project_v1 = await async_client.project_v1.domains_update( + v1 = await async_client.project.v1.domains_update( id="id", domain="domain", ) - assert_matches_type(ProjectV1DomainsUpdateResponse, project_v1, path=["response"]) + assert_matches_type(V1DomainsUpdateResponse, v1, path=["response"]) @parametrize async def test_raw_response_domains_update(self, async_client: AsyncUnlayer) -> None: - response = await async_client.project_v1.with_raw_response.domains_update( + response = await async_client.project.v1.with_raw_response.domains_update( id="id", ) assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" - project_v1 = await response.parse() - assert_matches_type(ProjectV1DomainsUpdateResponse, project_v1, path=["response"]) + v1 = await response.parse() + assert_matches_type(V1DomainsUpdateResponse, v1, path=["response"]) @parametrize async def test_streaming_response_domains_update(self, async_client: AsyncUnlayer) -> None: - async with async_client.project_v1.with_streaming_response.domains_update( + async with async_client.project.v1.with_streaming_response.domains_update( id="id", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" - project_v1 = await response.parse() - assert_matches_type(ProjectV1DomainsUpdateResponse, project_v1, path=["response"]) + v1 = await response.parse() + assert_matches_type(V1DomainsUpdateResponse, v1, path=["response"]) assert cast(Any, response.is_closed) is True @parametrize async def test_path_params_domains_update(self, async_client: AsyncUnlayer) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): - await async_client.project_v1.with_raw_response.domains_update( + await async_client.project.v1.with_raw_response.domains_update( id="", ) @parametrize async def test_method_templates_create(self, async_client: AsyncUnlayer) -> None: - project_v1 = await async_client.project_v1.templates_create( + v1 = await async_client.project.v1.templates_create( name="name", ) - assert_matches_type(ProjectV1TemplatesCreateResponse, project_v1, path=["response"]) + assert_matches_type(V1TemplatesCreateResponse, v1, path=["response"]) @parametrize async def test_method_templates_create_with_all_params(self, async_client: AsyncUnlayer) -> None: - project_v1 = await async_client.project_v1.templates_create( + v1 = await async_client.project.v1.templates_create( name="name", body="body", subject="subject", ) - assert_matches_type(ProjectV1TemplatesCreateResponse, project_v1, path=["response"]) + assert_matches_type(V1TemplatesCreateResponse, v1, path=["response"]) @parametrize async def test_raw_response_templates_create(self, async_client: AsyncUnlayer) -> None: - response = await async_client.project_v1.with_raw_response.templates_create( + response = await async_client.project.v1.with_raw_response.templates_create( name="name", ) assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" - project_v1 = await response.parse() - assert_matches_type(ProjectV1TemplatesCreateResponse, project_v1, path=["response"]) + v1 = await response.parse() + assert_matches_type(V1TemplatesCreateResponse, v1, path=["response"]) @parametrize async def test_streaming_response_templates_create(self, async_client: AsyncUnlayer) -> None: - async with async_client.project_v1.with_streaming_response.templates_create( + async with async_client.project.v1.with_streaming_response.templates_create( name="name", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" - project_v1 = await response.parse() - assert_matches_type(ProjectV1TemplatesCreateResponse, project_v1, path=["response"]) + v1 = await response.parse() + assert_matches_type(V1TemplatesCreateResponse, v1, path=["response"]) assert cast(Any, response.is_closed) is True @parametrize async def test_method_templates_delete(self, async_client: AsyncUnlayer) -> None: - project_v1 = await async_client.project_v1.templates_delete( + v1 = await async_client.project.v1.templates_delete( "id", ) - assert project_v1 is None + assert v1 is None @parametrize async def test_raw_response_templates_delete(self, async_client: AsyncUnlayer) -> None: - response = await async_client.project_v1.with_raw_response.templates_delete( + response = await async_client.project.v1.with_raw_response.templates_delete( "id", ) assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" - project_v1 = await response.parse() - assert project_v1 is None + v1 = await response.parse() + assert v1 is None @parametrize async def test_streaming_response_templates_delete(self, async_client: AsyncUnlayer) -> None: - async with async_client.project_v1.with_streaming_response.templates_delete( + async with async_client.project.v1.with_streaming_response.templates_delete( "id", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" - project_v1 = await response.parse() - assert project_v1 is None + v1 = await response.parse() + assert v1 is None assert cast(Any, response.is_closed) is True @parametrize async def test_path_params_templates_delete(self, async_client: AsyncUnlayer) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): - await async_client.project_v1.with_raw_response.templates_delete( + await async_client.project.v1.with_raw_response.templates_delete( "", ) @parametrize async def test_method_templates_list(self, async_client: AsyncUnlayer) -> None: - project_v1 = await async_client.project_v1.templates_list() - assert_matches_type(ProjectV1TemplatesListResponse, project_v1, path=["response"]) + v1 = await async_client.project.v1.templates_list() + assert_matches_type(V1TemplatesListResponse, v1, path=["response"]) @parametrize async def test_raw_response_templates_list(self, async_client: AsyncUnlayer) -> None: - response = await async_client.project_v1.with_raw_response.templates_list() + response = await async_client.project.v1.with_raw_response.templates_list() assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" - project_v1 = await response.parse() - assert_matches_type(ProjectV1TemplatesListResponse, project_v1, path=["response"]) + v1 = await response.parse() + assert_matches_type(V1TemplatesListResponse, v1, path=["response"]) @parametrize async def test_streaming_response_templates_list(self, async_client: AsyncUnlayer) -> None: - async with async_client.project_v1.with_streaming_response.templates_list() as response: + async with async_client.project.v1.with_streaming_response.templates_list() as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" - project_v1 = await response.parse() - assert_matches_type(ProjectV1TemplatesListResponse, project_v1, path=["response"]) + v1 = await response.parse() + assert_matches_type(V1TemplatesListResponse, v1, path=["response"]) assert cast(Any, response.is_closed) is True @parametrize async def test_method_templates_retrieve(self, async_client: AsyncUnlayer) -> None: - project_v1 = await async_client.project_v1.templates_retrieve( + v1 = await async_client.project.v1.templates_retrieve( "id", ) - assert_matches_type(ProjectV1TemplatesRetrieveResponse, project_v1, path=["response"]) + assert_matches_type(V1TemplatesRetrieveResponse, v1, path=["response"]) @parametrize async def test_raw_response_templates_retrieve(self, async_client: AsyncUnlayer) -> None: - response = await async_client.project_v1.with_raw_response.templates_retrieve( + response = await async_client.project.v1.with_raw_response.templates_retrieve( "id", ) assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" - project_v1 = await response.parse() - assert_matches_type(ProjectV1TemplatesRetrieveResponse, project_v1, path=["response"]) + v1 = await response.parse() + assert_matches_type(V1TemplatesRetrieveResponse, v1, path=["response"]) @parametrize async def test_streaming_response_templates_retrieve(self, async_client: AsyncUnlayer) -> None: - async with async_client.project_v1.with_streaming_response.templates_retrieve( + async with async_client.project.v1.with_streaming_response.templates_retrieve( "id", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" - project_v1 = await response.parse() - assert_matches_type(ProjectV1TemplatesRetrieveResponse, project_v1, path=["response"]) + v1 = await response.parse() + assert_matches_type(V1TemplatesRetrieveResponse, v1, path=["response"]) assert cast(Any, response.is_closed) is True @parametrize async def test_path_params_templates_retrieve(self, async_client: AsyncUnlayer) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): - await async_client.project_v1.with_raw_response.templates_retrieve( + await async_client.project.v1.with_raw_response.templates_retrieve( "", ) @parametrize async def test_method_templates_update(self, async_client: AsyncUnlayer) -> None: - project_v1 = await async_client.project_v1.templates_update( + v1 = await async_client.project.v1.templates_update( id="id", ) - assert_matches_type(ProjectV1TemplatesUpdateResponse, project_v1, path=["response"]) + assert_matches_type(V1TemplatesUpdateResponse, v1, path=["response"]) @parametrize async def test_method_templates_update_with_all_params(self, async_client: AsyncUnlayer) -> None: - project_v1 = await async_client.project_v1.templates_update( + v1 = await async_client.project.v1.templates_update( id="id", body="body", name="name", subject="subject", ) - assert_matches_type(ProjectV1TemplatesUpdateResponse, project_v1, path=["response"]) + assert_matches_type(V1TemplatesUpdateResponse, v1, path=["response"]) @parametrize async def test_raw_response_templates_update(self, async_client: AsyncUnlayer) -> None: - response = await async_client.project_v1.with_raw_response.templates_update( + response = await async_client.project.v1.with_raw_response.templates_update( id="id", ) assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" - project_v1 = await response.parse() - assert_matches_type(ProjectV1TemplatesUpdateResponse, project_v1, path=["response"]) + v1 = await response.parse() + assert_matches_type(V1TemplatesUpdateResponse, v1, path=["response"]) @parametrize async def test_streaming_response_templates_update(self, async_client: AsyncUnlayer) -> None: - async with async_client.project_v1.with_streaming_response.templates_update( + async with async_client.project.v1.with_streaming_response.templates_update( id="id", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" - project_v1 = await response.parse() - assert_matches_type(ProjectV1TemplatesUpdateResponse, project_v1, path=["response"]) + v1 = await response.parse() + assert_matches_type(V1TemplatesUpdateResponse, v1, path=["response"]) assert cast(Any, response.is_closed) is True @parametrize async def test_path_params_templates_update(self, async_client: AsyncUnlayer) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): - await async_client.project_v1.with_raw_response.templates_update( + await async_client.project.v1.with_raw_response.templates_update( id="", ) diff --git a/tests/test_client.py b/tests/test_client.py index f8e2095..55d91cc 100644 --- a/tests/test_client.py +++ b/tests/test_client.py @@ -735,7 +735,7 @@ def test_retrying_timeout_errors_doesnt_leak(self, respx_mock: MockRouter, clien respx_mock.get("/project/v1/current").mock(side_effect=httpx.TimeoutException("Test timeout error")) with pytest.raises(APITimeoutError): - client.project_v1.with_streaming_response.current_list().__enter__() + client.project.with_streaming_response.current_list().__enter__() assert _get_open_connections(client) == 0 @@ -745,7 +745,7 @@ def test_retrying_status_errors_doesnt_leak(self, respx_mock: MockRouter, client respx_mock.get("/project/v1/current").mock(return_value=httpx.Response(500)) with pytest.raises(APIStatusError): - client.project_v1.with_streaming_response.current_list().__enter__() + client.project.with_streaming_response.current_list().__enter__() assert _get_open_connections(client) == 0 @pytest.mark.parametrize("failures_before_success", [0, 2, 4]) @@ -774,7 +774,7 @@ def retry_handler(_request: httpx.Request) -> httpx.Response: respx_mock.get("/project/v1/current").mock(side_effect=retry_handler) - response = client.project_v1.with_raw_response.current_list() + response = client.project.with_raw_response.current_list() assert response.retries_taken == failures_before_success assert int(response.http_request.headers.get("x-stainless-retry-count")) == failures_before_success @@ -798,7 +798,7 @@ def retry_handler(_request: httpx.Request) -> httpx.Response: respx_mock.get("/project/v1/current").mock(side_effect=retry_handler) - response = client.project_v1.with_raw_response.current_list(extra_headers={"x-stainless-retry-count": Omit()}) + response = client.project.with_raw_response.current_list(extra_headers={"x-stainless-retry-count": Omit()}) assert len(response.http_request.headers.get_list("x-stainless-retry-count")) == 0 @@ -821,7 +821,7 @@ def retry_handler(_request: httpx.Request) -> httpx.Response: respx_mock.get("/project/v1/current").mock(side_effect=retry_handler) - response = client.project_v1.with_raw_response.current_list(extra_headers={"x-stainless-retry-count": "42"}) + response = client.project.with_raw_response.current_list(extra_headers={"x-stainless-retry-count": "42"}) assert response.http_request.headers.get("x-stainless-retry-count") == "42" @@ -1569,7 +1569,7 @@ async def test_retrying_timeout_errors_doesnt_leak( respx_mock.get("/project/v1/current").mock(side_effect=httpx.TimeoutException("Test timeout error")) with pytest.raises(APITimeoutError): - await async_client.project_v1.with_streaming_response.current_list().__aenter__() + await async_client.project.with_streaming_response.current_list().__aenter__() assert _get_open_connections(async_client) == 0 @@ -1579,7 +1579,7 @@ async def test_retrying_status_errors_doesnt_leak(self, respx_mock: MockRouter, respx_mock.get("/project/v1/current").mock(return_value=httpx.Response(500)) with pytest.raises(APIStatusError): - await async_client.project_v1.with_streaming_response.current_list().__aenter__() + await async_client.project.with_streaming_response.current_list().__aenter__() assert _get_open_connections(async_client) == 0 @pytest.mark.parametrize("failures_before_success", [0, 2, 4]) @@ -1608,7 +1608,7 @@ def retry_handler(_request: httpx.Request) -> httpx.Response: respx_mock.get("/project/v1/current").mock(side_effect=retry_handler) - response = await client.project_v1.with_raw_response.current_list() + response = await client.project.with_raw_response.current_list() assert response.retries_taken == failures_before_success assert int(response.http_request.headers.get("x-stainless-retry-count")) == failures_before_success @@ -1632,7 +1632,7 @@ def retry_handler(_request: httpx.Request) -> httpx.Response: respx_mock.get("/project/v1/current").mock(side_effect=retry_handler) - response = await client.project_v1.with_raw_response.current_list( + response = await client.project.with_raw_response.current_list( extra_headers={"x-stainless-retry-count": Omit()} ) @@ -1657,9 +1657,7 @@ def retry_handler(_request: httpx.Request) -> httpx.Response: respx_mock.get("/project/v1/current").mock(side_effect=retry_handler) - response = await client.project_v1.with_raw_response.current_list( - extra_headers={"x-stainless-retry-count": "42"} - ) + response = await client.project.with_raw_response.current_list(extra_headers={"x-stainless-retry-count": "42"}) assert response.http_request.headers.get("x-stainless-retry-count") == "42" From 24d383672623d7c1d762163c064773ab8587acaf Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Wed, 10 Dec 2025 19:13:22 +0000 Subject: [PATCH 05/62] feat(api): api update --- .stats.yml | 2 +- api.md | 8 ++-- src/unlayer/resources/emails/emails.py | 30 ++++++------ src/unlayer/resources/emails/v1.py | 30 ++++++------ src/unlayer/types/__init__.py | 2 +- ...response.py => email_retrieve_response.py} | 4 +- src/unlayer/types/emails/__init__.py | 2 +- ...ve_response.py => v1_retrieve_response.py} | 4 +- tests/api_resources/emails/test_v1.py | 46 +++++++++---------- tests/api_resources/test_emails.py | 46 +++++++++---------- 10 files changed, 87 insertions(+), 87 deletions(-) rename src/unlayer/types/{email_emails_retrieve_response.py => email_retrieve_response.py} (89%) rename src/unlayer/types/emails/{v1_emails_retrieve_response.py => v1_retrieve_response.py} (90%) diff --git a/.stats.yml b/.stats.yml index b7a5fc0..27cd530 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 24 openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/unlayer%2Funlayer-d746e93c3c920dca97596edec38c9ec25feef644db419156ae1b538bb54b6d72.yml openapi_spec_hash: 437dce81b84c463ef9cc84dafa2ca92a -config_hash: 355cc7936ae5249f192357f93dab04c8 +config_hash: 89910371aa6d40f1ed85ee933924cf2e diff --git a/api.md b/api.md index 98b788a..d8b8e74 100644 --- a/api.md +++ b/api.md @@ -86,7 +86,7 @@ Types: ```python from unlayer.types import ( - EmailEmailsRetrieveResponse, + EmailRetrieveResponse, EmailRenderCreateResponse, EmailSendCreateResponse, EmailSendTemplateTemplateResponse, @@ -95,7 +95,7 @@ from unlayer.types import ( Methods: -- client.emails.emails_retrieve(id) -> EmailEmailsRetrieveResponse +- client.emails.retrieve(id) -> EmailRetrieveResponse - client.emails.render_create(\*\*params) -> EmailRenderCreateResponse - client.emails.send_create(\*\*params) -> EmailSendCreateResponse - client.emails.send_template_template(\*\*params) -> EmailSendTemplateTemplateResponse @@ -106,7 +106,7 @@ Types: ```python from unlayer.types.emails import ( - V1EmailsRetrieveResponse, + V1RetrieveResponse, V1RenderCreateResponse, V1SendCreateResponse, V1SendTemplateTemplateResponse, @@ -115,7 +115,7 @@ from unlayer.types.emails import ( Methods: -- client.emails.v1.emails_retrieve(id) -> V1EmailsRetrieveResponse +- client.emails.v1.retrieve(id) -> V1RetrieveResponse - client.emails.v1.render_create(\*\*params) -> V1RenderCreateResponse - client.emails.v1.send_create(\*\*params) -> V1SendCreateResponse - client.emails.v1.send_template_template(\*\*params) -> V1SendTemplateTemplateResponse diff --git a/src/unlayer/resources/emails/emails.py b/src/unlayer/resources/emails/emails.py index 1585e4e..f8ffd59 100644 --- a/src/unlayer/resources/emails/emails.py +++ b/src/unlayer/resources/emails/emails.py @@ -26,9 +26,9 @@ async_to_streamed_response_wrapper, ) from ..._base_client import make_request_options +from ...types.email_retrieve_response import EmailRetrieveResponse from ...types.email_send_create_response import EmailSendCreateResponse from ...types.email_render_create_response import EmailRenderCreateResponse -from ...types.email_emails_retrieve_response import EmailEmailsRetrieveResponse from ...types.email_send_template_template_response import EmailSendTemplateTemplateResponse __all__ = ["EmailsResource", "AsyncEmailsResource"] @@ -58,7 +58,7 @@ def with_streaming_response(self) -> EmailsResourceWithStreamingResponse: """ return EmailsResourceWithStreamingResponse(self) - def emails_retrieve( + def retrieve( self, id: str, *, @@ -68,7 +68,7 @@ def emails_retrieve( extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> EmailEmailsRetrieveResponse: + ) -> EmailRetrieveResponse: """ Retrieve details of a previously sent email. @@ -88,7 +88,7 @@ def emails_retrieve( options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), - cast_to=EmailEmailsRetrieveResponse, + cast_to=EmailRetrieveResponse, ) def render_create( @@ -265,7 +265,7 @@ def with_streaming_response(self) -> AsyncEmailsResourceWithStreamingResponse: """ return AsyncEmailsResourceWithStreamingResponse(self) - async def emails_retrieve( + async def retrieve( self, id: str, *, @@ -275,7 +275,7 @@ async def emails_retrieve( extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> EmailEmailsRetrieveResponse: + ) -> EmailRetrieveResponse: """ Retrieve details of a previously sent email. @@ -295,7 +295,7 @@ async def emails_retrieve( options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), - cast_to=EmailEmailsRetrieveResponse, + cast_to=EmailRetrieveResponse, ) async def render_create( @@ -452,8 +452,8 @@ class EmailsResourceWithRawResponse: def __init__(self, emails: EmailsResource) -> None: self._emails = emails - self.emails_retrieve = to_raw_response_wrapper( - emails.emails_retrieve, + self.retrieve = to_raw_response_wrapper( + emails.retrieve, ) self.render_create = to_raw_response_wrapper( emails.render_create, @@ -474,8 +474,8 @@ class AsyncEmailsResourceWithRawResponse: def __init__(self, emails: AsyncEmailsResource) -> None: self._emails = emails - self.emails_retrieve = async_to_raw_response_wrapper( - emails.emails_retrieve, + self.retrieve = async_to_raw_response_wrapper( + emails.retrieve, ) self.render_create = async_to_raw_response_wrapper( emails.render_create, @@ -496,8 +496,8 @@ class EmailsResourceWithStreamingResponse: def __init__(self, emails: EmailsResource) -> None: self._emails = emails - self.emails_retrieve = to_streamed_response_wrapper( - emails.emails_retrieve, + self.retrieve = to_streamed_response_wrapper( + emails.retrieve, ) self.render_create = to_streamed_response_wrapper( emails.render_create, @@ -518,8 +518,8 @@ class AsyncEmailsResourceWithStreamingResponse: def __init__(self, emails: AsyncEmailsResource) -> None: self._emails = emails - self.emails_retrieve = async_to_streamed_response_wrapper( - emails.emails_retrieve, + self.retrieve = async_to_streamed_response_wrapper( + emails.retrieve, ) self.render_create = async_to_streamed_response_wrapper( emails.render_create, diff --git a/src/unlayer/resources/emails/v1.py b/src/unlayer/resources/emails/v1.py index 90b3b3c..fdc1e00 100644 --- a/src/unlayer/resources/emails/v1.py +++ b/src/unlayer/resources/emails/v1.py @@ -18,9 +18,9 @@ ) from ..._base_client import make_request_options from ...types.emails import v1_send_create_params, v1_render_create_params, v1_send_template_template_params +from ...types.emails.v1_retrieve_response import V1RetrieveResponse from ...types.emails.v1_send_create_response import V1SendCreateResponse from ...types.emails.v1_render_create_response import V1RenderCreateResponse -from ...types.emails.v1_emails_retrieve_response import V1EmailsRetrieveResponse from ...types.emails.v1_send_template_template_response import V1SendTemplateTemplateResponse __all__ = ["V1Resource", "AsyncV1Resource"] @@ -46,7 +46,7 @@ def with_streaming_response(self) -> V1ResourceWithStreamingResponse: """ return V1ResourceWithStreamingResponse(self) - def emails_retrieve( + def retrieve( self, id: str, *, @@ -56,7 +56,7 @@ def emails_retrieve( extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> V1EmailsRetrieveResponse: + ) -> V1RetrieveResponse: """ Retrieve details of a previously sent email. @@ -76,7 +76,7 @@ def emails_retrieve( options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), - cast_to=V1EmailsRetrieveResponse, + cast_to=V1RetrieveResponse, ) def render_create( @@ -249,7 +249,7 @@ def with_streaming_response(self) -> AsyncV1ResourceWithStreamingResponse: """ return AsyncV1ResourceWithStreamingResponse(self) - async def emails_retrieve( + async def retrieve( self, id: str, *, @@ -259,7 +259,7 @@ async def emails_retrieve( extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> V1EmailsRetrieveResponse: + ) -> V1RetrieveResponse: """ Retrieve details of a previously sent email. @@ -279,7 +279,7 @@ async def emails_retrieve( options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), - cast_to=V1EmailsRetrieveResponse, + cast_to=V1RetrieveResponse, ) async def render_create( @@ -436,8 +436,8 @@ class V1ResourceWithRawResponse: def __init__(self, v1: V1Resource) -> None: self._v1 = v1 - self.emails_retrieve = to_raw_response_wrapper( - v1.emails_retrieve, + self.retrieve = to_raw_response_wrapper( + v1.retrieve, ) self.render_create = to_raw_response_wrapper( v1.render_create, @@ -454,8 +454,8 @@ class AsyncV1ResourceWithRawResponse: def __init__(self, v1: AsyncV1Resource) -> None: self._v1 = v1 - self.emails_retrieve = async_to_raw_response_wrapper( - v1.emails_retrieve, + self.retrieve = async_to_raw_response_wrapper( + v1.retrieve, ) self.render_create = async_to_raw_response_wrapper( v1.render_create, @@ -472,8 +472,8 @@ class V1ResourceWithStreamingResponse: def __init__(self, v1: V1Resource) -> None: self._v1 = v1 - self.emails_retrieve = to_streamed_response_wrapper( - v1.emails_retrieve, + self.retrieve = to_streamed_response_wrapper( + v1.retrieve, ) self.render_create = to_streamed_response_wrapper( v1.render_create, @@ -490,8 +490,8 @@ class AsyncV1ResourceWithStreamingResponse: def __init__(self, v1: AsyncV1Resource) -> None: self._v1 = v1 - self.emails_retrieve = async_to_streamed_response_wrapper( - v1.emails_retrieve, + self.retrieve = async_to_streamed_response_wrapper( + v1.retrieve, ) self.render_create = async_to_streamed_response_wrapper( v1.render_create, diff --git a/src/unlayer/types/__init__.py b/src/unlayer/types/__init__.py index 290242a..3c047df 100644 --- a/src/unlayer/types/__init__.py +++ b/src/unlayer/types/__init__.py @@ -2,6 +2,7 @@ from __future__ import annotations +from .email_retrieve_response import EmailRetrieveResponse as EmailRetrieveResponse from .email_send_create_params import EmailSendCreateParams as EmailSendCreateParams from .page_render_create_params import PageRenderCreateParams as PageRenderCreateParams from .email_render_create_params import EmailRenderCreateParams as EmailRenderCreateParams @@ -12,7 +13,6 @@ from .project_domains_create_params import ProjectDomainsCreateParams as ProjectDomainsCreateParams from .project_domains_list_response import ProjectDomainsListResponse as ProjectDomainsListResponse from .project_domains_update_params import ProjectDomainsUpdateParams as ProjectDomainsUpdateParams -from .email_emails_retrieve_response import EmailEmailsRetrieveResponse as EmailEmailsRetrieveResponse from .project_api_keys_create_params import ProjectAPIKeysCreateParams as ProjectAPIKeysCreateParams from .project_api_keys_list_response import ProjectAPIKeysListResponse as ProjectAPIKeysListResponse from .project_api_keys_update_params import ProjectAPIKeysUpdateParams as ProjectAPIKeysUpdateParams diff --git a/src/unlayer/types/email_emails_retrieve_response.py b/src/unlayer/types/email_retrieve_response.py similarity index 89% rename from src/unlayer/types/email_emails_retrieve_response.py rename to src/unlayer/types/email_retrieve_response.py index 1fed895..55c62c4 100644 --- a/src/unlayer/types/email_emails_retrieve_response.py +++ b/src/unlayer/types/email_retrieve_response.py @@ -8,10 +8,10 @@ from .._models import BaseModel -__all__ = ["EmailEmailsRetrieveResponse"] +__all__ = ["EmailRetrieveResponse"] -class EmailEmailsRetrieveResponse(BaseModel): +class EmailRetrieveResponse(BaseModel): id: Optional[str] = None """Email message ID""" diff --git a/src/unlayer/types/emails/__init__.py b/src/unlayer/types/emails/__init__.py index 813185f..893ea05 100644 --- a/src/unlayer/types/emails/__init__.py +++ b/src/unlayer/types/emails/__init__.py @@ -2,10 +2,10 @@ from __future__ import annotations +from .v1_retrieve_response import V1RetrieveResponse as V1RetrieveResponse from .v1_send_create_params import V1SendCreateParams as V1SendCreateParams from .v1_render_create_params import V1RenderCreateParams as V1RenderCreateParams from .v1_send_create_response import V1SendCreateResponse as V1SendCreateResponse from .v1_render_create_response import V1RenderCreateResponse as V1RenderCreateResponse -from .v1_emails_retrieve_response import V1EmailsRetrieveResponse as V1EmailsRetrieveResponse from .v1_send_template_template_params import V1SendTemplateTemplateParams as V1SendTemplateTemplateParams from .v1_send_template_template_response import V1SendTemplateTemplateResponse as V1SendTemplateTemplateResponse diff --git a/src/unlayer/types/emails/v1_emails_retrieve_response.py b/src/unlayer/types/emails/v1_retrieve_response.py similarity index 90% rename from src/unlayer/types/emails/v1_emails_retrieve_response.py rename to src/unlayer/types/emails/v1_retrieve_response.py index 7d30a1f..5262e60 100644 --- a/src/unlayer/types/emails/v1_emails_retrieve_response.py +++ b/src/unlayer/types/emails/v1_retrieve_response.py @@ -8,10 +8,10 @@ from ..._models import BaseModel -__all__ = ["V1EmailsRetrieveResponse"] +__all__ = ["V1RetrieveResponse"] -class V1EmailsRetrieveResponse(BaseModel): +class V1RetrieveResponse(BaseModel): id: Optional[str] = None """Email message ID""" diff --git a/tests/api_resources/emails/test_v1.py b/tests/api_resources/emails/test_v1.py index 70d02ee..c1da30c 100644 --- a/tests/api_resources/emails/test_v1.py +++ b/tests/api_resources/emails/test_v1.py @@ -10,9 +10,9 @@ from unlayer import Unlayer, AsyncUnlayer from tests.utils import assert_matches_type from unlayer.types.emails import ( + V1RetrieveResponse, V1SendCreateResponse, V1RenderCreateResponse, - V1EmailsRetrieveResponse, V1SendTemplateTemplateResponse, ) @@ -23,40 +23,40 @@ class TestV1: parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) @parametrize - def test_method_emails_retrieve(self, client: Unlayer) -> None: - v1 = client.emails.v1.emails_retrieve( + def test_method_retrieve(self, client: Unlayer) -> None: + v1 = client.emails.v1.retrieve( "id", ) - assert_matches_type(V1EmailsRetrieveResponse, v1, path=["response"]) + assert_matches_type(V1RetrieveResponse, v1, path=["response"]) @parametrize - def test_raw_response_emails_retrieve(self, client: Unlayer) -> None: - response = client.emails.v1.with_raw_response.emails_retrieve( + def test_raw_response_retrieve(self, client: Unlayer) -> None: + response = client.emails.v1.with_raw_response.retrieve( "id", ) assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" v1 = response.parse() - assert_matches_type(V1EmailsRetrieveResponse, v1, path=["response"]) + assert_matches_type(V1RetrieveResponse, v1, path=["response"]) @parametrize - def test_streaming_response_emails_retrieve(self, client: Unlayer) -> None: - with client.emails.v1.with_streaming_response.emails_retrieve( + def test_streaming_response_retrieve(self, client: Unlayer) -> None: + with client.emails.v1.with_streaming_response.retrieve( "id", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" v1 = response.parse() - assert_matches_type(V1EmailsRetrieveResponse, v1, path=["response"]) + assert_matches_type(V1RetrieveResponse, v1, path=["response"]) assert cast(Any, response.is_closed) is True @parametrize - def test_path_params_emails_retrieve(self, client: Unlayer) -> None: + def test_path_params_retrieve(self, client: Unlayer) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): - client.emails.v1.with_raw_response.emails_retrieve( + client.emails.v1.with_raw_response.retrieve( "", ) @@ -219,40 +219,40 @@ class TestAsyncV1: ) @parametrize - async def test_method_emails_retrieve(self, async_client: AsyncUnlayer) -> None: - v1 = await async_client.emails.v1.emails_retrieve( + async def test_method_retrieve(self, async_client: AsyncUnlayer) -> None: + v1 = await async_client.emails.v1.retrieve( "id", ) - assert_matches_type(V1EmailsRetrieveResponse, v1, path=["response"]) + assert_matches_type(V1RetrieveResponse, v1, path=["response"]) @parametrize - async def test_raw_response_emails_retrieve(self, async_client: AsyncUnlayer) -> None: - response = await async_client.emails.v1.with_raw_response.emails_retrieve( + async def test_raw_response_retrieve(self, async_client: AsyncUnlayer) -> None: + response = await async_client.emails.v1.with_raw_response.retrieve( "id", ) assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" v1 = await response.parse() - assert_matches_type(V1EmailsRetrieveResponse, v1, path=["response"]) + assert_matches_type(V1RetrieveResponse, v1, path=["response"]) @parametrize - async def test_streaming_response_emails_retrieve(self, async_client: AsyncUnlayer) -> None: - async with async_client.emails.v1.with_streaming_response.emails_retrieve( + async def test_streaming_response_retrieve(self, async_client: AsyncUnlayer) -> None: + async with async_client.emails.v1.with_streaming_response.retrieve( "id", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" v1 = await response.parse() - assert_matches_type(V1EmailsRetrieveResponse, v1, path=["response"]) + assert_matches_type(V1RetrieveResponse, v1, path=["response"]) assert cast(Any, response.is_closed) is True @parametrize - async def test_path_params_emails_retrieve(self, async_client: AsyncUnlayer) -> None: + async def test_path_params_retrieve(self, async_client: AsyncUnlayer) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): - await async_client.emails.v1.with_raw_response.emails_retrieve( + await async_client.emails.v1.with_raw_response.retrieve( "", ) diff --git a/tests/api_resources/test_emails.py b/tests/api_resources/test_emails.py index f1747a0..ee12932 100644 --- a/tests/api_resources/test_emails.py +++ b/tests/api_resources/test_emails.py @@ -10,9 +10,9 @@ from unlayer import Unlayer, AsyncUnlayer from tests.utils import assert_matches_type from unlayer.types import ( + EmailRetrieveResponse, EmailSendCreateResponse, EmailRenderCreateResponse, - EmailEmailsRetrieveResponse, EmailSendTemplateTemplateResponse, ) @@ -23,40 +23,40 @@ class TestEmails: parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) @parametrize - def test_method_emails_retrieve(self, client: Unlayer) -> None: - email = client.emails.emails_retrieve( + def test_method_retrieve(self, client: Unlayer) -> None: + email = client.emails.retrieve( "id", ) - assert_matches_type(EmailEmailsRetrieveResponse, email, path=["response"]) + assert_matches_type(EmailRetrieveResponse, email, path=["response"]) @parametrize - def test_raw_response_emails_retrieve(self, client: Unlayer) -> None: - response = client.emails.with_raw_response.emails_retrieve( + def test_raw_response_retrieve(self, client: Unlayer) -> None: + response = client.emails.with_raw_response.retrieve( "id", ) assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" email = response.parse() - assert_matches_type(EmailEmailsRetrieveResponse, email, path=["response"]) + assert_matches_type(EmailRetrieveResponse, email, path=["response"]) @parametrize - def test_streaming_response_emails_retrieve(self, client: Unlayer) -> None: - with client.emails.with_streaming_response.emails_retrieve( + def test_streaming_response_retrieve(self, client: Unlayer) -> None: + with client.emails.with_streaming_response.retrieve( "id", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" email = response.parse() - assert_matches_type(EmailEmailsRetrieveResponse, email, path=["response"]) + assert_matches_type(EmailRetrieveResponse, email, path=["response"]) assert cast(Any, response.is_closed) is True @parametrize - def test_path_params_emails_retrieve(self, client: Unlayer) -> None: + def test_path_params_retrieve(self, client: Unlayer) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): - client.emails.with_raw_response.emails_retrieve( + client.emails.with_raw_response.retrieve( "", ) @@ -219,40 +219,40 @@ class TestAsyncEmails: ) @parametrize - async def test_method_emails_retrieve(self, async_client: AsyncUnlayer) -> None: - email = await async_client.emails.emails_retrieve( + async def test_method_retrieve(self, async_client: AsyncUnlayer) -> None: + email = await async_client.emails.retrieve( "id", ) - assert_matches_type(EmailEmailsRetrieveResponse, email, path=["response"]) + assert_matches_type(EmailRetrieveResponse, email, path=["response"]) @parametrize - async def test_raw_response_emails_retrieve(self, async_client: AsyncUnlayer) -> None: - response = await async_client.emails.with_raw_response.emails_retrieve( + async def test_raw_response_retrieve(self, async_client: AsyncUnlayer) -> None: + response = await async_client.emails.with_raw_response.retrieve( "id", ) assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" email = await response.parse() - assert_matches_type(EmailEmailsRetrieveResponse, email, path=["response"]) + assert_matches_type(EmailRetrieveResponse, email, path=["response"]) @parametrize - async def test_streaming_response_emails_retrieve(self, async_client: AsyncUnlayer) -> None: - async with async_client.emails.with_streaming_response.emails_retrieve( + async def test_streaming_response_retrieve(self, async_client: AsyncUnlayer) -> None: + async with async_client.emails.with_streaming_response.retrieve( "id", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" email = await response.parse() - assert_matches_type(EmailEmailsRetrieveResponse, email, path=["response"]) + assert_matches_type(EmailRetrieveResponse, email, path=["response"]) assert cast(Any, response.is_closed) is True @parametrize - async def test_path_params_emails_retrieve(self, async_client: AsyncUnlayer) -> None: + async def test_path_params_retrieve(self, async_client: AsyncUnlayer) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): - await async_client.emails.with_raw_response.emails_retrieve( + await async_client.emails.with_raw_response.retrieve( "", ) From 3cf26263f6f6aa99e5d1cc04b95507a75f82bf05 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Thu, 11 Dec 2025 08:08:03 +0000 Subject: [PATCH 06/62] feat(api): api update --- .stats.yml | 6 +- api.md | 153 +- src/unlayer/_client.py | 21 +- src/unlayer/resources/__init__.py | 12 +- .../resources/{documents => }/documents.py | 52 +- src/unlayer/resources/documents/__init__.py | 33 - src/unlayer/resources/documents/v1.py | 397 ----- src/unlayer/resources/{emails => }/emails.py | 54 +- src/unlayer/resources/emails/__init__.py | 33 - src/unlayer/resources/emails/v1.py | 504 ------ src/unlayer/resources/{pages => }/pages.py | 48 +- src/unlayer/resources/pages/__init__.py | 33 - src/unlayer/resources/pages/v1.py | 187 --- .../resources/{project => }/project.py | 72 +- src/unlayer/resources/project/__init__.py | 33 - src/unlayer/resources/project/v1.py | 1374 ----------------- src/unlayer/types/documents/__init__.py | 11 - .../v1_documents_retrieve_response.py | 37 - .../documents/v1_generate_create_params.py | 27 - .../documents/v1_generate_create_response.py | 23 - .../v1_generate_template_template_params.py | 21 - .../v1_generate_template_template_response.py | 23 - src/unlayer/types/emails/__init__.py | 11 - .../types/emails/v1_render_create_params.py | 18 - .../types/emails/v1_render_create_response.py | 12 - .../types/emails/v1_retrieve_response.py | 31 - .../types/emails/v1_send_create_params.py | 27 - .../types/emails/v1_send_create_response.py | 17 - .../v1_send_template_template_params.py | 24 - .../v1_send_template_template_response.py | 17 - src/unlayer/types/pages/__init__.py | 6 - .../types/pages/v1_render_create_params.py | 18 - .../types/pages/v1_render_create_response.py | 12 - src/unlayer/types/project/__init__.py | 23 - .../project/v1_api_keys_create_params.py | 17 - .../project/v1_api_keys_create_response.py | 28 - .../project/v1_api_keys_list_response.py | 30 - .../project/v1_api_keys_retrieve_response.py | 30 - .../project/v1_api_keys_update_params.py | 20 - .../project/v1_api_keys_update_response.py | 30 - .../types/project/v1_current_list_response.py | 32 - .../types/project/v1_domains_create_params.py | 12 - .../project/v1_domains_create_response.py | 26 - .../types/project/v1_domains_list_response.py | 27 - .../project/v1_domains_retrieve_response.py | 26 - .../types/project/v1_domains_update_params.py | 12 - .../project/v1_domains_update_response.py | 26 - .../project/v1_templates_create_params.py | 18 - .../project/v1_templates_create_response.py | 28 - .../project/v1_templates_list_response.py | 28 - .../project/v1_templates_retrieve_response.py | 28 - .../project/v1_templates_update_params.py | 18 - .../project/v1_templates_update_response.py | 28 - tests/api_resources/documents/__init__.py | 1 - tests/api_resources/documents/test_v1.py | 292 ---- tests/api_resources/emails/__init__.py | 1 - tests/api_resources/emails/test_v1.py | 409 ----- tests/api_resources/pages/__init__.py | 1 - tests/api_resources/pages/test_v1.py | 126 -- tests/api_resources/project/__init__.py | 1 - tests/api_resources/project/test_v1.py | 1198 -------------- 61 files changed, 98 insertions(+), 5765 deletions(-) rename src/unlayer/resources/{documents => }/documents.py (89%) delete mode 100644 src/unlayer/resources/documents/__init__.py delete mode 100644 src/unlayer/resources/documents/v1.py rename src/unlayer/resources/{emails => }/emails.py (91%) delete mode 100644 src/unlayer/resources/emails/__init__.py delete mode 100644 src/unlayer/resources/emails/v1.py rename src/unlayer/resources/{pages => }/pages.py (81%) delete mode 100644 src/unlayer/resources/pages/__init__.py delete mode 100644 src/unlayer/resources/pages/v1.py rename src/unlayer/resources/{project => }/project.py (95%) delete mode 100644 src/unlayer/resources/project/__init__.py delete mode 100644 src/unlayer/resources/project/v1.py delete mode 100644 src/unlayer/types/documents/__init__.py delete mode 100644 src/unlayer/types/documents/v1_documents_retrieve_response.py delete mode 100644 src/unlayer/types/documents/v1_generate_create_params.py delete mode 100644 src/unlayer/types/documents/v1_generate_create_response.py delete mode 100644 src/unlayer/types/documents/v1_generate_template_template_params.py delete mode 100644 src/unlayer/types/documents/v1_generate_template_template_response.py delete mode 100644 src/unlayer/types/emails/__init__.py delete mode 100644 src/unlayer/types/emails/v1_render_create_params.py delete mode 100644 src/unlayer/types/emails/v1_render_create_response.py delete mode 100644 src/unlayer/types/emails/v1_retrieve_response.py delete mode 100644 src/unlayer/types/emails/v1_send_create_params.py delete mode 100644 src/unlayer/types/emails/v1_send_create_response.py delete mode 100644 src/unlayer/types/emails/v1_send_template_template_params.py delete mode 100644 src/unlayer/types/emails/v1_send_template_template_response.py delete mode 100644 src/unlayer/types/pages/__init__.py delete mode 100644 src/unlayer/types/pages/v1_render_create_params.py delete mode 100644 src/unlayer/types/pages/v1_render_create_response.py delete mode 100644 src/unlayer/types/project/__init__.py delete mode 100644 src/unlayer/types/project/v1_api_keys_create_params.py delete mode 100644 src/unlayer/types/project/v1_api_keys_create_response.py delete mode 100644 src/unlayer/types/project/v1_api_keys_list_response.py delete mode 100644 src/unlayer/types/project/v1_api_keys_retrieve_response.py delete mode 100644 src/unlayer/types/project/v1_api_keys_update_params.py delete mode 100644 src/unlayer/types/project/v1_api_keys_update_response.py delete mode 100644 src/unlayer/types/project/v1_current_list_response.py delete mode 100644 src/unlayer/types/project/v1_domains_create_params.py delete mode 100644 src/unlayer/types/project/v1_domains_create_response.py delete mode 100644 src/unlayer/types/project/v1_domains_list_response.py delete mode 100644 src/unlayer/types/project/v1_domains_retrieve_response.py delete mode 100644 src/unlayer/types/project/v1_domains_update_params.py delete mode 100644 src/unlayer/types/project/v1_domains_update_response.py delete mode 100644 src/unlayer/types/project/v1_templates_create_params.py delete mode 100644 src/unlayer/types/project/v1_templates_create_response.py delete mode 100644 src/unlayer/types/project/v1_templates_list_response.py delete mode 100644 src/unlayer/types/project/v1_templates_retrieve_response.py delete mode 100644 src/unlayer/types/project/v1_templates_update_params.py delete mode 100644 src/unlayer/types/project/v1_templates_update_response.py delete mode 100644 tests/api_resources/documents/__init__.py delete mode 100644 tests/api_resources/documents/test_v1.py delete mode 100644 tests/api_resources/emails/__init__.py delete mode 100644 tests/api_resources/emails/test_v1.py delete mode 100644 tests/api_resources/pages/__init__.py delete mode 100644 tests/api_resources/pages/test_v1.py delete mode 100644 tests/api_resources/project/__init__.py delete mode 100644 tests/api_resources/project/test_v1.py diff --git a/.stats.yml b/.stats.yml index 27cd530..3b76cc0 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 24 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/unlayer%2Funlayer-d746e93c3c920dca97596edec38c9ec25feef644db419156ae1b538bb54b6d72.yml -openapi_spec_hash: 437dce81b84c463ef9cc84dafa2ca92a -config_hash: 89910371aa6d40f1ed85ee933924cf2e +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/unlayer%2Funlayer-9942f4da50a072ed2feb0c1aa835272455da6a6e9a22b23d51f75c6002c9da64.yml +openapi_spec_hash: 19ba50f4ff2d2bb8b2a16a6083d1e1e9 +config_hash: 6d277064312d0ee93ce6df0eee354fa6 diff --git a/api.md b/api.md index d8b8e74..b30df0d 100644 --- a/api.md +++ b/api.md @@ -22,103 +22,22 @@ from unlayer.types import ( Methods: -- client.project.api_keys_create(\*\*params) -> ProjectAPIKeysCreateResponse -- client.project.api_keys_delete(id) -> None -- client.project.api_keys_list() -> ProjectAPIKeysListResponse -- client.project.api_keys_retrieve(id) -> ProjectAPIKeysRetrieveResponse -- client.project.api_keys_update(id, \*\*params) -> ProjectAPIKeysUpdateResponse -- client.project.current_list() -> ProjectCurrentListResponse -- client.project.domains_create(\*\*params) -> ProjectDomainsCreateResponse -- client.project.domains_delete(id) -> None -- client.project.domains_list() -> ProjectDomainsListResponse -- client.project.domains_retrieve(id) -> ProjectDomainsRetrieveResponse -- client.project.domains_update(id, \*\*params) -> ProjectDomainsUpdateResponse -- client.project.templates_create(\*\*params) -> ProjectTemplatesCreateResponse -- client.project.templates_delete(id) -> None -- client.project.templates_list() -> ProjectTemplatesListResponse -- client.project.templates_retrieve(id) -> ProjectTemplatesRetrieveResponse -- client.project.templates_update(id, \*\*params) -> ProjectTemplatesUpdateResponse - -## V1 - -Types: - -```python -from unlayer.types.project import ( - V1APIKeysCreateResponse, - V1APIKeysListResponse, - V1APIKeysRetrieveResponse, - V1APIKeysUpdateResponse, - V1CurrentListResponse, - V1DomainsCreateResponse, - V1DomainsListResponse, - V1DomainsRetrieveResponse, - V1DomainsUpdateResponse, - V1TemplatesCreateResponse, - V1TemplatesListResponse, - V1TemplatesRetrieveResponse, - V1TemplatesUpdateResponse, -) -``` - -Methods: - -- client.project.v1.api_keys_create(\*\*params) -> V1APIKeysCreateResponse -- client.project.v1.api_keys_delete(id) -> None -- client.project.v1.api_keys_list() -> V1APIKeysListResponse -- client.project.v1.api_keys_retrieve(id) -> V1APIKeysRetrieveResponse -- client.project.v1.api_keys_update(id, \*\*params) -> V1APIKeysUpdateResponse -- client.project.v1.current_list() -> V1CurrentListResponse -- client.project.v1.domains_create(\*\*params) -> V1DomainsCreateResponse -- client.project.v1.domains_delete(id) -> None -- client.project.v1.domains_list() -> V1DomainsListResponse -- client.project.v1.domains_retrieve(id) -> V1DomainsRetrieveResponse -- client.project.v1.domains_update(id, \*\*params) -> V1DomainsUpdateResponse -- client.project.v1.templates_create(\*\*params) -> V1TemplatesCreateResponse -- client.project.v1.templates_delete(id) -> None -- client.project.v1.templates_list() -> V1TemplatesListResponse -- client.project.v1.templates_retrieve(id) -> V1TemplatesRetrieveResponse -- client.project.v1.templates_update(id, \*\*params) -> V1TemplatesUpdateResponse - -# Emails - -Types: - -```python -from unlayer.types import ( - EmailRetrieveResponse, - EmailRenderCreateResponse, - EmailSendCreateResponse, - EmailSendTemplateTemplateResponse, -) -``` - -Methods: - -- client.emails.retrieve(id) -> EmailRetrieveResponse -- client.emails.render_create(\*\*params) -> EmailRenderCreateResponse -- client.emails.send_create(\*\*params) -> EmailSendCreateResponse -- client.emails.send_template_template(\*\*params) -> EmailSendTemplateTemplateResponse - -## V1 - -Types: - -```python -from unlayer.types.emails import ( - V1RetrieveResponse, - V1RenderCreateResponse, - V1SendCreateResponse, - V1SendTemplateTemplateResponse, -) -``` - -Methods: - -- client.emails.v1.retrieve(id) -> V1RetrieveResponse -- client.emails.v1.render_create(\*\*params) -> V1RenderCreateResponse -- client.emails.v1.send_create(\*\*params) -> V1SendCreateResponse -- client.emails.v1.send_template_template(\*\*params) -> V1SendTemplateTemplateResponse +- client.project.api_keys_create(\*\*params) -> ProjectAPIKeysCreateResponse +- client.project.api_keys_delete(id) -> None +- client.project.api_keys_list() -> ProjectAPIKeysListResponse +- client.project.api_keys_retrieve(id) -> ProjectAPIKeysRetrieveResponse +- client.project.api_keys_update(id, \*\*params) -> ProjectAPIKeysUpdateResponse +- client.project.current_list() -> ProjectCurrentListResponse +- client.project.domains_create(\*\*params) -> ProjectDomainsCreateResponse +- client.project.domains_delete(id) -> None +- client.project.domains_list() -> ProjectDomainsListResponse +- client.project.domains_retrieve(id) -> ProjectDomainsRetrieveResponse +- client.project.domains_update(id, \*\*params) -> ProjectDomainsUpdateResponse +- client.project.templates_create(\*\*params) -> ProjectTemplatesCreateResponse +- client.project.templates_delete(id) -> None +- client.project.templates_list() -> ProjectTemplatesListResponse +- client.project.templates_retrieve(id) -> ProjectTemplatesRetrieveResponse +- client.project.templates_update(id, \*\*params) -> ProjectTemplatesUpdateResponse # Documents @@ -134,27 +53,9 @@ from unlayer.types import ( Methods: -- client.documents.documents_retrieve(id) -> DocumentDocumentsRetrieveResponse -- client.documents.generate_create(\*\*params) -> DocumentGenerateCreateResponse -- client.documents.generate_template_template(\*\*params) -> DocumentGenerateTemplateTemplateResponse - -## V1 - -Types: - -```python -from unlayer.types.documents import ( - V1DocumentsRetrieveResponse, - V1GenerateCreateResponse, - V1GenerateTemplateTemplateResponse, -) -``` - -Methods: - -- client.documents.v1.documents_retrieve(id) -> V1DocumentsRetrieveResponse -- client.documents.v1.generate_create(\*\*params) -> V1GenerateCreateResponse -- client.documents.v1.generate_template_template(\*\*params) -> V1GenerateTemplateTemplateResponse +- client.documents.documents_retrieve(id) -> DocumentDocumentsRetrieveResponse +- client.documents.generate_create(\*\*params) -> DocumentGenerateCreateResponse +- client.documents.generate_template_template(\*\*params) -> DocumentGenerateTemplateTemplateResponse # Pages @@ -166,16 +67,24 @@ from unlayer.types import PageRenderCreateResponse Methods: -- client.pages.render_create(\*\*params) -> PageRenderCreateResponse +- client.pages.render_create(\*\*params) -> PageRenderCreateResponse -## V1 +# Emails Types: ```python -from unlayer.types.pages import V1RenderCreateResponse +from unlayer.types import ( + EmailRetrieveResponse, + EmailRenderCreateResponse, + EmailSendCreateResponse, + EmailSendTemplateTemplateResponse, +) ``` Methods: -- client.pages.v1.render_create(\*\*params) -> V1RenderCreateResponse +- client.emails.retrieve(id) -> EmailRetrieveResponse +- client.emails.render_create(\*\*params) -> EmailRenderCreateResponse +- client.emails.send_create(\*\*params) -> EmailSendCreateResponse +- client.emails.send_template_template(\*\*params) -> EmailSendTemplateTemplateResponse diff --git a/src/unlayer/_client.py b/src/unlayer/_client.py index 28cb1e1..6da7474 100644 --- a/src/unlayer/_client.py +++ b/src/unlayer/_client.py @@ -21,6 +21,7 @@ ) from ._utils import is_given, get_async_library from ._version import __version__ +from .resources import pages, emails, project, documents from ._streaming import Stream as Stream, AsyncStream as AsyncStream from ._exceptions import UnlayerError, APIStatusError from ._base_client import ( @@ -28,19 +29,15 @@ SyncAPIClient, AsyncAPIClient, ) -from .resources.pages import pages -from .resources.emails import emails -from .resources.project import project -from .resources.documents import documents __all__ = ["Timeout", "Transport", "ProxiesTypes", "RequestOptions", "Unlayer", "AsyncUnlayer", "Client", "AsyncClient"] class Unlayer(SyncAPIClient): project: project.ProjectResource - emails: emails.EmailsResource documents: documents.DocumentsResource pages: pages.PagesResource + emails: emails.EmailsResource with_raw_response: UnlayerWithRawResponse with_streaming_response: UnlayerWithStreamedResponse @@ -99,9 +96,9 @@ def __init__( ) self.project = project.ProjectResource(self) - self.emails = emails.EmailsResource(self) self.documents = documents.DocumentsResource(self) self.pages = pages.PagesResource(self) + self.emails = emails.EmailsResource(self) self.with_raw_response = UnlayerWithRawResponse(self) self.with_streaming_response = UnlayerWithStreamedResponse(self) @@ -212,9 +209,9 @@ def _make_status_error( class AsyncUnlayer(AsyncAPIClient): project: project.AsyncProjectResource - emails: emails.AsyncEmailsResource documents: documents.AsyncDocumentsResource pages: pages.AsyncPagesResource + emails: emails.AsyncEmailsResource with_raw_response: AsyncUnlayerWithRawResponse with_streaming_response: AsyncUnlayerWithStreamedResponse @@ -273,9 +270,9 @@ def __init__( ) self.project = project.AsyncProjectResource(self) - self.emails = emails.AsyncEmailsResource(self) self.documents = documents.AsyncDocumentsResource(self) self.pages = pages.AsyncPagesResource(self) + self.emails = emails.AsyncEmailsResource(self) self.with_raw_response = AsyncUnlayerWithRawResponse(self) self.with_streaming_response = AsyncUnlayerWithStreamedResponse(self) @@ -387,33 +384,33 @@ def _make_status_error( class UnlayerWithRawResponse: def __init__(self, client: Unlayer) -> None: self.project = project.ProjectResourceWithRawResponse(client.project) - self.emails = emails.EmailsResourceWithRawResponse(client.emails) self.documents = documents.DocumentsResourceWithRawResponse(client.documents) self.pages = pages.PagesResourceWithRawResponse(client.pages) + self.emails = emails.EmailsResourceWithRawResponse(client.emails) class AsyncUnlayerWithRawResponse: def __init__(self, client: AsyncUnlayer) -> None: self.project = project.AsyncProjectResourceWithRawResponse(client.project) - self.emails = emails.AsyncEmailsResourceWithRawResponse(client.emails) self.documents = documents.AsyncDocumentsResourceWithRawResponse(client.documents) self.pages = pages.AsyncPagesResourceWithRawResponse(client.pages) + self.emails = emails.AsyncEmailsResourceWithRawResponse(client.emails) class UnlayerWithStreamedResponse: def __init__(self, client: Unlayer) -> None: self.project = project.ProjectResourceWithStreamingResponse(client.project) - self.emails = emails.EmailsResourceWithStreamingResponse(client.emails) self.documents = documents.DocumentsResourceWithStreamingResponse(client.documents) self.pages = pages.PagesResourceWithStreamingResponse(client.pages) + self.emails = emails.EmailsResourceWithStreamingResponse(client.emails) class AsyncUnlayerWithStreamedResponse: def __init__(self, client: AsyncUnlayer) -> None: self.project = project.AsyncProjectResourceWithStreamingResponse(client.project) - self.emails = emails.AsyncEmailsResourceWithStreamingResponse(client.emails) self.documents = documents.AsyncDocumentsResourceWithStreamingResponse(client.documents) self.pages = pages.AsyncPagesResourceWithStreamingResponse(client.pages) + self.emails = emails.AsyncEmailsResourceWithStreamingResponse(client.emails) Client = Unlayer diff --git a/src/unlayer/resources/__init__.py b/src/unlayer/resources/__init__.py index b1a13d2..616e08d 100644 --- a/src/unlayer/resources/__init__.py +++ b/src/unlayer/resources/__init__.py @@ -40,12 +40,6 @@ "AsyncProjectResourceWithRawResponse", "ProjectResourceWithStreamingResponse", "AsyncProjectResourceWithStreamingResponse", - "EmailsResource", - "AsyncEmailsResource", - "EmailsResourceWithRawResponse", - "AsyncEmailsResourceWithRawResponse", - "EmailsResourceWithStreamingResponse", - "AsyncEmailsResourceWithStreamingResponse", "DocumentsResource", "AsyncDocumentsResource", "DocumentsResourceWithRawResponse", @@ -58,4 +52,10 @@ "AsyncPagesResourceWithRawResponse", "PagesResourceWithStreamingResponse", "AsyncPagesResourceWithStreamingResponse", + "EmailsResource", + "AsyncEmailsResource", + "EmailsResourceWithRawResponse", + "AsyncEmailsResourceWithRawResponse", + "EmailsResourceWithStreamingResponse", + "AsyncEmailsResourceWithStreamingResponse", ] diff --git a/src/unlayer/resources/documents/documents.py b/src/unlayer/resources/documents.py similarity index 89% rename from src/unlayer/resources/documents/documents.py rename to src/unlayer/resources/documents.py index 0c93199..803d2f9 100644 --- a/src/unlayer/resources/documents/documents.py +++ b/src/unlayer/resources/documents.py @@ -6,38 +6,26 @@ import httpx -from .v1 import ( - V1Resource, - AsyncV1Resource, - V1ResourceWithRawResponse, - AsyncV1ResourceWithRawResponse, - V1ResourceWithStreamingResponse, - AsyncV1ResourceWithStreamingResponse, -) -from ...types import document_generate_create_params, document_generate_template_template_params -from ..._types import Body, Omit, Query, Headers, NotGiven, omit, not_given -from ..._utils import maybe_transform, async_maybe_transform -from ..._compat import cached_property -from ..._resource import SyncAPIResource, AsyncAPIResource -from ..._response import ( +from ..types import document_generate_create_params, document_generate_template_template_params +from .._types import Body, Omit, Query, Headers, NotGiven, omit, not_given +from .._utils import maybe_transform, async_maybe_transform +from .._compat import cached_property +from .._resource import SyncAPIResource, AsyncAPIResource +from .._response import ( to_raw_response_wrapper, to_streamed_response_wrapper, async_to_raw_response_wrapper, async_to_streamed_response_wrapper, ) -from ..._base_client import make_request_options -from ...types.document_generate_create_response import DocumentGenerateCreateResponse -from ...types.document_documents_retrieve_response import DocumentDocumentsRetrieveResponse -from ...types.document_generate_template_template_response import DocumentGenerateTemplateTemplateResponse +from .._base_client import make_request_options +from ..types.document_generate_create_response import DocumentGenerateCreateResponse +from ..types.document_documents_retrieve_response import DocumentDocumentsRetrieveResponse +from ..types.document_generate_template_template_response import DocumentGenerateTemplateTemplateResponse __all__ = ["DocumentsResource", "AsyncDocumentsResource"] class DocumentsResource(SyncAPIResource): - @cached_property - def v1(self) -> V1Resource: - return V1Resource(self._client) - @cached_property def with_raw_response(self) -> DocumentsResourceWithRawResponse: """ @@ -194,10 +182,6 @@ def generate_template_template( class AsyncDocumentsResource(AsyncAPIResource): - @cached_property - def v1(self) -> AsyncV1Resource: - return AsyncV1Resource(self._client) - @cached_property def with_raw_response(self) -> AsyncDocumentsResourceWithRawResponse: """ @@ -367,10 +351,6 @@ def __init__(self, documents: DocumentsResource) -> None: documents.generate_template_template, ) - @cached_property - def v1(self) -> V1ResourceWithRawResponse: - return V1ResourceWithRawResponse(self._documents.v1) - class AsyncDocumentsResourceWithRawResponse: def __init__(self, documents: AsyncDocumentsResource) -> None: @@ -386,10 +366,6 @@ def __init__(self, documents: AsyncDocumentsResource) -> None: documents.generate_template_template, ) - @cached_property - def v1(self) -> AsyncV1ResourceWithRawResponse: - return AsyncV1ResourceWithRawResponse(self._documents.v1) - class DocumentsResourceWithStreamingResponse: def __init__(self, documents: DocumentsResource) -> None: @@ -405,10 +381,6 @@ def __init__(self, documents: DocumentsResource) -> None: documents.generate_template_template, ) - @cached_property - def v1(self) -> V1ResourceWithStreamingResponse: - return V1ResourceWithStreamingResponse(self._documents.v1) - class AsyncDocumentsResourceWithStreamingResponse: def __init__(self, documents: AsyncDocumentsResource) -> None: @@ -423,7 +395,3 @@ def __init__(self, documents: AsyncDocumentsResource) -> None: self.generate_template_template = async_to_streamed_response_wrapper( documents.generate_template_template, ) - - @cached_property - def v1(self) -> AsyncV1ResourceWithStreamingResponse: - return AsyncV1ResourceWithStreamingResponse(self._documents.v1) diff --git a/src/unlayer/resources/documents/__init__.py b/src/unlayer/resources/documents/__init__.py deleted file mode 100644 index 51e4843..0000000 --- a/src/unlayer/resources/documents/__init__.py +++ /dev/null @@ -1,33 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from .v1 import ( - V1Resource, - AsyncV1Resource, - V1ResourceWithRawResponse, - AsyncV1ResourceWithRawResponse, - V1ResourceWithStreamingResponse, - AsyncV1ResourceWithStreamingResponse, -) -from .documents import ( - DocumentsResource, - AsyncDocumentsResource, - DocumentsResourceWithRawResponse, - AsyncDocumentsResourceWithRawResponse, - DocumentsResourceWithStreamingResponse, - AsyncDocumentsResourceWithStreamingResponse, -) - -__all__ = [ - "V1Resource", - "AsyncV1Resource", - "V1ResourceWithRawResponse", - "AsyncV1ResourceWithRawResponse", - "V1ResourceWithStreamingResponse", - "AsyncV1ResourceWithStreamingResponse", - "DocumentsResource", - "AsyncDocumentsResource", - "DocumentsResourceWithRawResponse", - "AsyncDocumentsResourceWithRawResponse", - "DocumentsResourceWithStreamingResponse", - "AsyncDocumentsResourceWithStreamingResponse", -] diff --git a/src/unlayer/resources/documents/v1.py b/src/unlayer/resources/documents/v1.py deleted file mode 100644 index cf6f243..0000000 --- a/src/unlayer/resources/documents/v1.py +++ /dev/null @@ -1,397 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing import Dict - -import httpx - -from ..._types import Body, Omit, Query, Headers, NotGiven, omit, not_given -from ..._utils import maybe_transform, async_maybe_transform -from ..._compat import cached_property -from ..._resource import SyncAPIResource, AsyncAPIResource -from ..._response import ( - to_raw_response_wrapper, - to_streamed_response_wrapper, - async_to_raw_response_wrapper, - async_to_streamed_response_wrapper, -) -from ..._base_client import make_request_options -from ...types.documents import v1_generate_create_params, v1_generate_template_template_params -from ...types.documents.v1_generate_create_response import V1GenerateCreateResponse -from ...types.documents.v1_documents_retrieve_response import V1DocumentsRetrieveResponse -from ...types.documents.v1_generate_template_template_response import V1GenerateTemplateTemplateResponse - -__all__ = ["V1Resource", "AsyncV1Resource"] - - -class V1Resource(SyncAPIResource): - @cached_property - def with_raw_response(self) -> V1ResourceWithRawResponse: - """ - This property can be used as a prefix for any HTTP method call to return - the raw response object instead of the parsed content. - - For more information, see https://www.github.com/stainless-sdks/unlayer-python#accessing-raw-response-data-eg-headers - """ - return V1ResourceWithRawResponse(self) - - @cached_property - def with_streaming_response(self) -> V1ResourceWithStreamingResponse: - """ - An alternative to `.with_raw_response` that doesn't eagerly read the response body. - - For more information, see https://www.github.com/stainless-sdks/unlayer-python#with_streaming_response - """ - return V1ResourceWithStreamingResponse(self) - - def documents_retrieve( - self, - id: str, - *, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> V1DocumentsRetrieveResponse: - """ - Retrieve details of a previously generated document. - - Args: - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - if not id: - raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") - return self._get( - f"/documents/v1/documents/{id}", - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=V1DocumentsRetrieveResponse, - ) - - def generate_create( - self, - *, - design: Dict[str, object], - filename: str | Omit = omit, - html: str | Omit = omit, - merge_tags: Dict[str, str] | Omit = omit, - url: str | Omit = omit, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> V1GenerateCreateResponse: - """ - Generate PDF document from JSON design, HTML content, or URL. - - Args: - design: Proprietary design format JSON - - filename: Optional filename for the generated PDF - - html: HTML content to convert to PDF - - merge_tags: Optional merge tags for personalization - - url: URL to convert to PDF - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - return self._post( - "/documents/v1/generate", - body=maybe_transform( - { - "design": design, - "filename": filename, - "html": html, - "merge_tags": merge_tags, - "url": url, - }, - v1_generate_create_params.V1GenerateCreateParams, - ), - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=V1GenerateCreateResponse, - ) - - def generate_template_template( - self, - *, - template_id: str, - filename: str | Omit = omit, - merge_tags: Dict[str, str] | Omit = omit, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> V1GenerateTemplateTemplateResponse: - """ - Generate PDF document from an existing template with merge tags. - - Args: - template_id: ID of the template to use for generation - - filename: Optional filename for the generated PDF - - merge_tags: Optional merge tags for personalization - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - return self._post( - "/documents/v1/generate/template", - body=maybe_transform( - { - "template_id": template_id, - "filename": filename, - "merge_tags": merge_tags, - }, - v1_generate_template_template_params.V1GenerateTemplateTemplateParams, - ), - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=V1GenerateTemplateTemplateResponse, - ) - - -class AsyncV1Resource(AsyncAPIResource): - @cached_property - def with_raw_response(self) -> AsyncV1ResourceWithRawResponse: - """ - This property can be used as a prefix for any HTTP method call to return - the raw response object instead of the parsed content. - - For more information, see https://www.github.com/stainless-sdks/unlayer-python#accessing-raw-response-data-eg-headers - """ - return AsyncV1ResourceWithRawResponse(self) - - @cached_property - def with_streaming_response(self) -> AsyncV1ResourceWithStreamingResponse: - """ - An alternative to `.with_raw_response` that doesn't eagerly read the response body. - - For more information, see https://www.github.com/stainless-sdks/unlayer-python#with_streaming_response - """ - return AsyncV1ResourceWithStreamingResponse(self) - - async def documents_retrieve( - self, - id: str, - *, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> V1DocumentsRetrieveResponse: - """ - Retrieve details of a previously generated document. - - Args: - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - if not id: - raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") - return await self._get( - f"/documents/v1/documents/{id}", - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=V1DocumentsRetrieveResponse, - ) - - async def generate_create( - self, - *, - design: Dict[str, object], - filename: str | Omit = omit, - html: str | Omit = omit, - merge_tags: Dict[str, str] | Omit = omit, - url: str | Omit = omit, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> V1GenerateCreateResponse: - """ - Generate PDF document from JSON design, HTML content, or URL. - - Args: - design: Proprietary design format JSON - - filename: Optional filename for the generated PDF - - html: HTML content to convert to PDF - - merge_tags: Optional merge tags for personalization - - url: URL to convert to PDF - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - return await self._post( - "/documents/v1/generate", - body=await async_maybe_transform( - { - "design": design, - "filename": filename, - "html": html, - "merge_tags": merge_tags, - "url": url, - }, - v1_generate_create_params.V1GenerateCreateParams, - ), - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=V1GenerateCreateResponse, - ) - - async def generate_template_template( - self, - *, - template_id: str, - filename: str | Omit = omit, - merge_tags: Dict[str, str] | Omit = omit, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> V1GenerateTemplateTemplateResponse: - """ - Generate PDF document from an existing template with merge tags. - - Args: - template_id: ID of the template to use for generation - - filename: Optional filename for the generated PDF - - merge_tags: Optional merge tags for personalization - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - return await self._post( - "/documents/v1/generate/template", - body=await async_maybe_transform( - { - "template_id": template_id, - "filename": filename, - "merge_tags": merge_tags, - }, - v1_generate_template_template_params.V1GenerateTemplateTemplateParams, - ), - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=V1GenerateTemplateTemplateResponse, - ) - - -class V1ResourceWithRawResponse: - def __init__(self, v1: V1Resource) -> None: - self._v1 = v1 - - self.documents_retrieve = to_raw_response_wrapper( - v1.documents_retrieve, - ) - self.generate_create = to_raw_response_wrapper( - v1.generate_create, - ) - self.generate_template_template = to_raw_response_wrapper( - v1.generate_template_template, - ) - - -class AsyncV1ResourceWithRawResponse: - def __init__(self, v1: AsyncV1Resource) -> None: - self._v1 = v1 - - self.documents_retrieve = async_to_raw_response_wrapper( - v1.documents_retrieve, - ) - self.generate_create = async_to_raw_response_wrapper( - v1.generate_create, - ) - self.generate_template_template = async_to_raw_response_wrapper( - v1.generate_template_template, - ) - - -class V1ResourceWithStreamingResponse: - def __init__(self, v1: V1Resource) -> None: - self._v1 = v1 - - self.documents_retrieve = to_streamed_response_wrapper( - v1.documents_retrieve, - ) - self.generate_create = to_streamed_response_wrapper( - v1.generate_create, - ) - self.generate_template_template = to_streamed_response_wrapper( - v1.generate_template_template, - ) - - -class AsyncV1ResourceWithStreamingResponse: - def __init__(self, v1: AsyncV1Resource) -> None: - self._v1 = v1 - - self.documents_retrieve = async_to_streamed_response_wrapper( - v1.documents_retrieve, - ) - self.generate_create = async_to_streamed_response_wrapper( - v1.generate_create, - ) - self.generate_template_template = async_to_streamed_response_wrapper( - v1.generate_template_template, - ) diff --git a/src/unlayer/resources/emails/emails.py b/src/unlayer/resources/emails.py similarity index 91% rename from src/unlayer/resources/emails/emails.py rename to src/unlayer/resources/emails.py index f8ffd59..372b424 100644 --- a/src/unlayer/resources/emails/emails.py +++ b/src/unlayer/resources/emails.py @@ -6,39 +6,27 @@ import httpx -from .v1 import ( - V1Resource, - AsyncV1Resource, - V1ResourceWithRawResponse, - AsyncV1ResourceWithRawResponse, - V1ResourceWithStreamingResponse, - AsyncV1ResourceWithStreamingResponse, -) -from ...types import email_send_create_params, email_render_create_params, email_send_template_template_params -from ..._types import Body, Omit, Query, Headers, NotGiven, omit, not_given -from ..._utils import maybe_transform, async_maybe_transform -from ..._compat import cached_property -from ..._resource import SyncAPIResource, AsyncAPIResource -from ..._response import ( +from ..types import email_send_create_params, email_render_create_params, email_send_template_template_params +from .._types import Body, Omit, Query, Headers, NotGiven, omit, not_given +from .._utils import maybe_transform, async_maybe_transform +from .._compat import cached_property +from .._resource import SyncAPIResource, AsyncAPIResource +from .._response import ( to_raw_response_wrapper, to_streamed_response_wrapper, async_to_raw_response_wrapper, async_to_streamed_response_wrapper, ) -from ..._base_client import make_request_options -from ...types.email_retrieve_response import EmailRetrieveResponse -from ...types.email_send_create_response import EmailSendCreateResponse -from ...types.email_render_create_response import EmailRenderCreateResponse -from ...types.email_send_template_template_response import EmailSendTemplateTemplateResponse +from .._base_client import make_request_options +from ..types.email_retrieve_response import EmailRetrieveResponse +from ..types.email_send_create_response import EmailSendCreateResponse +from ..types.email_render_create_response import EmailRenderCreateResponse +from ..types.email_send_template_template_response import EmailSendTemplateTemplateResponse __all__ = ["EmailsResource", "AsyncEmailsResource"] class EmailsResource(SyncAPIResource): - @cached_property - def v1(self) -> V1Resource: - return V1Resource(self._client) - @cached_property def with_raw_response(self) -> EmailsResourceWithRawResponse: """ @@ -242,10 +230,6 @@ def send_template_template( class AsyncEmailsResource(AsyncAPIResource): - @cached_property - def v1(self) -> AsyncV1Resource: - return AsyncV1Resource(self._client) - @cached_property def with_raw_response(self) -> AsyncEmailsResourceWithRawResponse: """ @@ -465,10 +449,6 @@ def __init__(self, emails: EmailsResource) -> None: emails.send_template_template, ) - @cached_property - def v1(self) -> V1ResourceWithRawResponse: - return V1ResourceWithRawResponse(self._emails.v1) - class AsyncEmailsResourceWithRawResponse: def __init__(self, emails: AsyncEmailsResource) -> None: @@ -487,10 +467,6 @@ def __init__(self, emails: AsyncEmailsResource) -> None: emails.send_template_template, ) - @cached_property - def v1(self) -> AsyncV1ResourceWithRawResponse: - return AsyncV1ResourceWithRawResponse(self._emails.v1) - class EmailsResourceWithStreamingResponse: def __init__(self, emails: EmailsResource) -> None: @@ -509,10 +485,6 @@ def __init__(self, emails: EmailsResource) -> None: emails.send_template_template, ) - @cached_property - def v1(self) -> V1ResourceWithStreamingResponse: - return V1ResourceWithStreamingResponse(self._emails.v1) - class AsyncEmailsResourceWithStreamingResponse: def __init__(self, emails: AsyncEmailsResource) -> None: @@ -530,7 +502,3 @@ def __init__(self, emails: AsyncEmailsResource) -> None: self.send_template_template = async_to_streamed_response_wrapper( emails.send_template_template, ) - - @cached_property - def v1(self) -> AsyncV1ResourceWithStreamingResponse: - return AsyncV1ResourceWithStreamingResponse(self._emails.v1) diff --git a/src/unlayer/resources/emails/__init__.py b/src/unlayer/resources/emails/__init__.py deleted file mode 100644 index 752f051..0000000 --- a/src/unlayer/resources/emails/__init__.py +++ /dev/null @@ -1,33 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from .v1 import ( - V1Resource, - AsyncV1Resource, - V1ResourceWithRawResponse, - AsyncV1ResourceWithRawResponse, - V1ResourceWithStreamingResponse, - AsyncV1ResourceWithStreamingResponse, -) -from .emails import ( - EmailsResource, - AsyncEmailsResource, - EmailsResourceWithRawResponse, - AsyncEmailsResourceWithRawResponse, - EmailsResourceWithStreamingResponse, - AsyncEmailsResourceWithStreamingResponse, -) - -__all__ = [ - "V1Resource", - "AsyncV1Resource", - "V1ResourceWithRawResponse", - "AsyncV1ResourceWithRawResponse", - "V1ResourceWithStreamingResponse", - "AsyncV1ResourceWithStreamingResponse", - "EmailsResource", - "AsyncEmailsResource", - "EmailsResourceWithRawResponse", - "AsyncEmailsResourceWithRawResponse", - "EmailsResourceWithStreamingResponse", - "AsyncEmailsResourceWithStreamingResponse", -] diff --git a/src/unlayer/resources/emails/v1.py b/src/unlayer/resources/emails/v1.py deleted file mode 100644 index fdc1e00..0000000 --- a/src/unlayer/resources/emails/v1.py +++ /dev/null @@ -1,504 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing import Dict - -import httpx - -from ..._types import Body, Omit, Query, Headers, NotGiven, omit, not_given -from ..._utils import maybe_transform, async_maybe_transform -from ..._compat import cached_property -from ..._resource import SyncAPIResource, AsyncAPIResource -from ..._response import ( - to_raw_response_wrapper, - to_streamed_response_wrapper, - async_to_raw_response_wrapper, - async_to_streamed_response_wrapper, -) -from ..._base_client import make_request_options -from ...types.emails import v1_send_create_params, v1_render_create_params, v1_send_template_template_params -from ...types.emails.v1_retrieve_response import V1RetrieveResponse -from ...types.emails.v1_send_create_response import V1SendCreateResponse -from ...types.emails.v1_render_create_response import V1RenderCreateResponse -from ...types.emails.v1_send_template_template_response import V1SendTemplateTemplateResponse - -__all__ = ["V1Resource", "AsyncV1Resource"] - - -class V1Resource(SyncAPIResource): - @cached_property - def with_raw_response(self) -> V1ResourceWithRawResponse: - """ - This property can be used as a prefix for any HTTP method call to return - the raw response object instead of the parsed content. - - For more information, see https://www.github.com/stainless-sdks/unlayer-python#accessing-raw-response-data-eg-headers - """ - return V1ResourceWithRawResponse(self) - - @cached_property - def with_streaming_response(self) -> V1ResourceWithStreamingResponse: - """ - An alternative to `.with_raw_response` that doesn't eagerly read the response body. - - For more information, see https://www.github.com/stainless-sdks/unlayer-python#with_streaming_response - """ - return V1ResourceWithStreamingResponse(self) - - def retrieve( - self, - id: str, - *, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> V1RetrieveResponse: - """ - Retrieve details of a previously sent email. - - Args: - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - if not id: - raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") - return self._get( - f"/emails/v1/emails/{id}", - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=V1RetrieveResponse, - ) - - def render_create( - self, - *, - design: Dict[str, object], - merge_tags: Dict[str, str] | Omit = omit, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> V1RenderCreateResponse: - """ - Convert design JSON to HTML with optional merge tags. - - Args: - design: Proprietary design format JSON - - merge_tags: Optional merge tags for personalization - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - return self._post( - "/emails/v1/render", - body=maybe_transform( - { - "design": design, - "merge_tags": merge_tags, - }, - v1_render_create_params.V1RenderCreateParams, - ), - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=V1RenderCreateResponse, - ) - - def send_create( - self, - *, - design: Dict[str, object], - to: str, - html: str | Omit = omit, - merge_tags: Dict[str, str] | Omit = omit, - subject: str | Omit = omit, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> V1SendCreateResponse: - """ - Send email with design JSON or HTML content. - - Args: - design: Proprietary design format JSON - - to: Recipient email address - - html: HTML content to send - - merge_tags: Optional merge tags for personalization - - subject: Email subject line - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - return self._post( - "/emails/v1/send", - body=maybe_transform( - { - "design": design, - "to": to, - "html": html, - "merge_tags": merge_tags, - "subject": subject, - }, - v1_send_create_params.V1SendCreateParams, - ), - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=V1SendCreateResponse, - ) - - def send_template_template( - self, - *, - template_id: str, - to: str, - merge_tags: Dict[str, str] | Omit = omit, - subject: str | Omit = omit, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> V1SendTemplateTemplateResponse: - """ - Send email using an existing template with merge tags. - - Args: - template_id: ID of the template to use - - to: Recipient email address - - merge_tags: Optional merge tags for personalization - - subject: Email subject line (optional, uses template default if not provided) - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - return self._post( - "/emails/v1/send/template", - body=maybe_transform( - { - "template_id": template_id, - "to": to, - "merge_tags": merge_tags, - "subject": subject, - }, - v1_send_template_template_params.V1SendTemplateTemplateParams, - ), - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=V1SendTemplateTemplateResponse, - ) - - -class AsyncV1Resource(AsyncAPIResource): - @cached_property - def with_raw_response(self) -> AsyncV1ResourceWithRawResponse: - """ - This property can be used as a prefix for any HTTP method call to return - the raw response object instead of the parsed content. - - For more information, see https://www.github.com/stainless-sdks/unlayer-python#accessing-raw-response-data-eg-headers - """ - return AsyncV1ResourceWithRawResponse(self) - - @cached_property - def with_streaming_response(self) -> AsyncV1ResourceWithStreamingResponse: - """ - An alternative to `.with_raw_response` that doesn't eagerly read the response body. - - For more information, see https://www.github.com/stainless-sdks/unlayer-python#with_streaming_response - """ - return AsyncV1ResourceWithStreamingResponse(self) - - async def retrieve( - self, - id: str, - *, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> V1RetrieveResponse: - """ - Retrieve details of a previously sent email. - - Args: - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - if not id: - raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") - return await self._get( - f"/emails/v1/emails/{id}", - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=V1RetrieveResponse, - ) - - async def render_create( - self, - *, - design: Dict[str, object], - merge_tags: Dict[str, str] | Omit = omit, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> V1RenderCreateResponse: - """ - Convert design JSON to HTML with optional merge tags. - - Args: - design: Proprietary design format JSON - - merge_tags: Optional merge tags for personalization - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - return await self._post( - "/emails/v1/render", - body=await async_maybe_transform( - { - "design": design, - "merge_tags": merge_tags, - }, - v1_render_create_params.V1RenderCreateParams, - ), - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=V1RenderCreateResponse, - ) - - async def send_create( - self, - *, - design: Dict[str, object], - to: str, - html: str | Omit = omit, - merge_tags: Dict[str, str] | Omit = omit, - subject: str | Omit = omit, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> V1SendCreateResponse: - """ - Send email with design JSON or HTML content. - - Args: - design: Proprietary design format JSON - - to: Recipient email address - - html: HTML content to send - - merge_tags: Optional merge tags for personalization - - subject: Email subject line - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - return await self._post( - "/emails/v1/send", - body=await async_maybe_transform( - { - "design": design, - "to": to, - "html": html, - "merge_tags": merge_tags, - "subject": subject, - }, - v1_send_create_params.V1SendCreateParams, - ), - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=V1SendCreateResponse, - ) - - async def send_template_template( - self, - *, - template_id: str, - to: str, - merge_tags: Dict[str, str] | Omit = omit, - subject: str | Omit = omit, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> V1SendTemplateTemplateResponse: - """ - Send email using an existing template with merge tags. - - Args: - template_id: ID of the template to use - - to: Recipient email address - - merge_tags: Optional merge tags for personalization - - subject: Email subject line (optional, uses template default if not provided) - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - return await self._post( - "/emails/v1/send/template", - body=await async_maybe_transform( - { - "template_id": template_id, - "to": to, - "merge_tags": merge_tags, - "subject": subject, - }, - v1_send_template_template_params.V1SendTemplateTemplateParams, - ), - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=V1SendTemplateTemplateResponse, - ) - - -class V1ResourceWithRawResponse: - def __init__(self, v1: V1Resource) -> None: - self._v1 = v1 - - self.retrieve = to_raw_response_wrapper( - v1.retrieve, - ) - self.render_create = to_raw_response_wrapper( - v1.render_create, - ) - self.send_create = to_raw_response_wrapper( - v1.send_create, - ) - self.send_template_template = to_raw_response_wrapper( - v1.send_template_template, - ) - - -class AsyncV1ResourceWithRawResponse: - def __init__(self, v1: AsyncV1Resource) -> None: - self._v1 = v1 - - self.retrieve = async_to_raw_response_wrapper( - v1.retrieve, - ) - self.render_create = async_to_raw_response_wrapper( - v1.render_create, - ) - self.send_create = async_to_raw_response_wrapper( - v1.send_create, - ) - self.send_template_template = async_to_raw_response_wrapper( - v1.send_template_template, - ) - - -class V1ResourceWithStreamingResponse: - def __init__(self, v1: V1Resource) -> None: - self._v1 = v1 - - self.retrieve = to_streamed_response_wrapper( - v1.retrieve, - ) - self.render_create = to_streamed_response_wrapper( - v1.render_create, - ) - self.send_create = to_streamed_response_wrapper( - v1.send_create, - ) - self.send_template_template = to_streamed_response_wrapper( - v1.send_template_template, - ) - - -class AsyncV1ResourceWithStreamingResponse: - def __init__(self, v1: AsyncV1Resource) -> None: - self._v1 = v1 - - self.retrieve = async_to_streamed_response_wrapper( - v1.retrieve, - ) - self.render_create = async_to_streamed_response_wrapper( - v1.render_create, - ) - self.send_create = async_to_streamed_response_wrapper( - v1.send_create, - ) - self.send_template_template = async_to_streamed_response_wrapper( - v1.send_template_template, - ) diff --git a/src/unlayer/resources/pages/pages.py b/src/unlayer/resources/pages.py similarity index 81% rename from src/unlayer/resources/pages/pages.py rename to src/unlayer/resources/pages.py index aaa7b0e..158cd79 100644 --- a/src/unlayer/resources/pages/pages.py +++ b/src/unlayer/resources/pages.py @@ -6,36 +6,24 @@ import httpx -from .v1 import ( - V1Resource, - AsyncV1Resource, - V1ResourceWithRawResponse, - AsyncV1ResourceWithRawResponse, - V1ResourceWithStreamingResponse, - AsyncV1ResourceWithStreamingResponse, -) -from ...types import page_render_create_params -from ..._types import Body, Omit, Query, Headers, NotGiven, omit, not_given -from ..._utils import maybe_transform, async_maybe_transform -from ..._compat import cached_property -from ..._resource import SyncAPIResource, AsyncAPIResource -from ..._response import ( +from ..types import page_render_create_params +from .._types import Body, Omit, Query, Headers, NotGiven, omit, not_given +from .._utils import maybe_transform, async_maybe_transform +from .._compat import cached_property +from .._resource import SyncAPIResource, AsyncAPIResource +from .._response import ( to_raw_response_wrapper, to_streamed_response_wrapper, async_to_raw_response_wrapper, async_to_streamed_response_wrapper, ) -from ..._base_client import make_request_options -from ...types.page_render_create_response import PageRenderCreateResponse +from .._base_client import make_request_options +from ..types.page_render_create_response import PageRenderCreateResponse __all__ = ["PagesResource", "AsyncPagesResource"] class PagesResource(SyncAPIResource): - @cached_property - def v1(self) -> V1Resource: - return V1Resource(self._client) - @cached_property def with_raw_response(self) -> PagesResourceWithRawResponse: """ @@ -100,10 +88,6 @@ def render_create( class AsyncPagesResource(AsyncAPIResource): - @cached_property - def v1(self) -> AsyncV1Resource: - return AsyncV1Resource(self._client) - @cached_property def with_raw_response(self) -> AsyncPagesResourceWithRawResponse: """ @@ -175,10 +159,6 @@ def __init__(self, pages: PagesResource) -> None: pages.render_create, ) - @cached_property - def v1(self) -> V1ResourceWithRawResponse: - return V1ResourceWithRawResponse(self._pages.v1) - class AsyncPagesResourceWithRawResponse: def __init__(self, pages: AsyncPagesResource) -> None: @@ -188,10 +168,6 @@ def __init__(self, pages: AsyncPagesResource) -> None: pages.render_create, ) - @cached_property - def v1(self) -> AsyncV1ResourceWithRawResponse: - return AsyncV1ResourceWithRawResponse(self._pages.v1) - class PagesResourceWithStreamingResponse: def __init__(self, pages: PagesResource) -> None: @@ -201,10 +177,6 @@ def __init__(self, pages: PagesResource) -> None: pages.render_create, ) - @cached_property - def v1(self) -> V1ResourceWithStreamingResponse: - return V1ResourceWithStreamingResponse(self._pages.v1) - class AsyncPagesResourceWithStreamingResponse: def __init__(self, pages: AsyncPagesResource) -> None: @@ -213,7 +185,3 @@ def __init__(self, pages: AsyncPagesResource) -> None: self.render_create = async_to_streamed_response_wrapper( pages.render_create, ) - - @cached_property - def v1(self) -> AsyncV1ResourceWithStreamingResponse: - return AsyncV1ResourceWithStreamingResponse(self._pages.v1) diff --git a/src/unlayer/resources/pages/__init__.py b/src/unlayer/resources/pages/__init__.py deleted file mode 100644 index bc25669..0000000 --- a/src/unlayer/resources/pages/__init__.py +++ /dev/null @@ -1,33 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from .v1 import ( - V1Resource, - AsyncV1Resource, - V1ResourceWithRawResponse, - AsyncV1ResourceWithRawResponse, - V1ResourceWithStreamingResponse, - AsyncV1ResourceWithStreamingResponse, -) -from .pages import ( - PagesResource, - AsyncPagesResource, - PagesResourceWithRawResponse, - AsyncPagesResourceWithRawResponse, - PagesResourceWithStreamingResponse, - AsyncPagesResourceWithStreamingResponse, -) - -__all__ = [ - "V1Resource", - "AsyncV1Resource", - "V1ResourceWithRawResponse", - "AsyncV1ResourceWithRawResponse", - "V1ResourceWithStreamingResponse", - "AsyncV1ResourceWithStreamingResponse", - "PagesResource", - "AsyncPagesResource", - "PagesResourceWithRawResponse", - "AsyncPagesResourceWithRawResponse", - "PagesResourceWithStreamingResponse", - "AsyncPagesResourceWithStreamingResponse", -] diff --git a/src/unlayer/resources/pages/v1.py b/src/unlayer/resources/pages/v1.py deleted file mode 100644 index b232272..0000000 --- a/src/unlayer/resources/pages/v1.py +++ /dev/null @@ -1,187 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing import Dict - -import httpx - -from ..._types import Body, Omit, Query, Headers, NotGiven, omit, not_given -from ..._utils import maybe_transform, async_maybe_transform -from ..._compat import cached_property -from ..._resource import SyncAPIResource, AsyncAPIResource -from ..._response import ( - to_raw_response_wrapper, - to_streamed_response_wrapper, - async_to_raw_response_wrapper, - async_to_streamed_response_wrapper, -) -from ...types.pages import v1_render_create_params -from ..._base_client import make_request_options -from ...types.pages.v1_render_create_response import V1RenderCreateResponse - -__all__ = ["V1Resource", "AsyncV1Resource"] - - -class V1Resource(SyncAPIResource): - @cached_property - def with_raw_response(self) -> V1ResourceWithRawResponse: - """ - This property can be used as a prefix for any HTTP method call to return - the raw response object instead of the parsed content. - - For more information, see https://www.github.com/stainless-sdks/unlayer-python#accessing-raw-response-data-eg-headers - """ - return V1ResourceWithRawResponse(self) - - @cached_property - def with_streaming_response(self) -> V1ResourceWithStreamingResponse: - """ - An alternative to `.with_raw_response` that doesn't eagerly read the response body. - - For more information, see https://www.github.com/stainless-sdks/unlayer-python#with_streaming_response - """ - return V1ResourceWithStreamingResponse(self) - - def render_create( - self, - *, - design: Dict[str, object], - merge_tags: Dict[str, str] | Omit = omit, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> V1RenderCreateResponse: - """ - Convert page design JSON to HTML with optional merge tags. - - Args: - design: Proprietary design format JSON - - merge_tags: Optional merge tags for personalization - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - return self._post( - "/pages/v1/render", - body=maybe_transform( - { - "design": design, - "merge_tags": merge_tags, - }, - v1_render_create_params.V1RenderCreateParams, - ), - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=V1RenderCreateResponse, - ) - - -class AsyncV1Resource(AsyncAPIResource): - @cached_property - def with_raw_response(self) -> AsyncV1ResourceWithRawResponse: - """ - This property can be used as a prefix for any HTTP method call to return - the raw response object instead of the parsed content. - - For more information, see https://www.github.com/stainless-sdks/unlayer-python#accessing-raw-response-data-eg-headers - """ - return AsyncV1ResourceWithRawResponse(self) - - @cached_property - def with_streaming_response(self) -> AsyncV1ResourceWithStreamingResponse: - """ - An alternative to `.with_raw_response` that doesn't eagerly read the response body. - - For more information, see https://www.github.com/stainless-sdks/unlayer-python#with_streaming_response - """ - return AsyncV1ResourceWithStreamingResponse(self) - - async def render_create( - self, - *, - design: Dict[str, object], - merge_tags: Dict[str, str] | Omit = omit, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> V1RenderCreateResponse: - """ - Convert page design JSON to HTML with optional merge tags. - - Args: - design: Proprietary design format JSON - - merge_tags: Optional merge tags for personalization - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - return await self._post( - "/pages/v1/render", - body=await async_maybe_transform( - { - "design": design, - "merge_tags": merge_tags, - }, - v1_render_create_params.V1RenderCreateParams, - ), - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=V1RenderCreateResponse, - ) - - -class V1ResourceWithRawResponse: - def __init__(self, v1: V1Resource) -> None: - self._v1 = v1 - - self.render_create = to_raw_response_wrapper( - v1.render_create, - ) - - -class AsyncV1ResourceWithRawResponse: - def __init__(self, v1: AsyncV1Resource) -> None: - self._v1 = v1 - - self.render_create = async_to_raw_response_wrapper( - v1.render_create, - ) - - -class V1ResourceWithStreamingResponse: - def __init__(self, v1: V1Resource) -> None: - self._v1 = v1 - - self.render_create = to_streamed_response_wrapper( - v1.render_create, - ) - - -class AsyncV1ResourceWithStreamingResponse: - def __init__(self, v1: AsyncV1Resource) -> None: - self._v1 = v1 - - self.render_create = async_to_streamed_response_wrapper( - v1.render_create, - ) diff --git a/src/unlayer/resources/project/project.py b/src/unlayer/resources/project.py similarity index 95% rename from src/unlayer/resources/project/project.py rename to src/unlayer/resources/project.py index e7f096a..bea717d 100644 --- a/src/unlayer/resources/project/project.py +++ b/src/unlayer/resources/project.py @@ -4,15 +4,7 @@ import httpx -from .v1 import ( - V1Resource, - AsyncV1Resource, - V1ResourceWithRawResponse, - AsyncV1ResourceWithRawResponse, - V1ResourceWithStreamingResponse, - AsyncV1ResourceWithStreamingResponse, -) -from ...types import ( +from ..types import ( project_domains_create_params, project_domains_update_params, project_api_keys_create_params, @@ -20,39 +12,35 @@ project_templates_create_params, project_templates_update_params, ) -from ..._types import Body, Omit, Query, Headers, NoneType, NotGiven, SequenceNotStr, omit, not_given -from ..._utils import maybe_transform, async_maybe_transform -from ..._compat import cached_property -from ..._resource import SyncAPIResource, AsyncAPIResource -from ..._response import ( +from .._types import Body, Omit, Query, Headers, NoneType, NotGiven, SequenceNotStr, omit, not_given +from .._utils import maybe_transform, async_maybe_transform +from .._compat import cached_property +from .._resource import SyncAPIResource, AsyncAPIResource +from .._response import ( to_raw_response_wrapper, to_streamed_response_wrapper, async_to_raw_response_wrapper, async_to_streamed_response_wrapper, ) -from ..._base_client import make_request_options -from ...types.project_current_list_response import ProjectCurrentListResponse -from ...types.project_domains_list_response import ProjectDomainsListResponse -from ...types.project_api_keys_list_response import ProjectAPIKeysListResponse -from ...types.project_domains_create_response import ProjectDomainsCreateResponse -from ...types.project_domains_update_response import ProjectDomainsUpdateResponse -from ...types.project_templates_list_response import ProjectTemplatesListResponse -from ...types.project_api_keys_create_response import ProjectAPIKeysCreateResponse -from ...types.project_api_keys_update_response import ProjectAPIKeysUpdateResponse -from ...types.project_domains_retrieve_response import ProjectDomainsRetrieveResponse -from ...types.project_templates_create_response import ProjectTemplatesCreateResponse -from ...types.project_templates_update_response import ProjectTemplatesUpdateResponse -from ...types.project_api_keys_retrieve_response import ProjectAPIKeysRetrieveResponse -from ...types.project_templates_retrieve_response import ProjectTemplatesRetrieveResponse +from .._base_client import make_request_options +from ..types.project_current_list_response import ProjectCurrentListResponse +from ..types.project_domains_list_response import ProjectDomainsListResponse +from ..types.project_api_keys_list_response import ProjectAPIKeysListResponse +from ..types.project_domains_create_response import ProjectDomainsCreateResponse +from ..types.project_domains_update_response import ProjectDomainsUpdateResponse +from ..types.project_templates_list_response import ProjectTemplatesListResponse +from ..types.project_api_keys_create_response import ProjectAPIKeysCreateResponse +from ..types.project_api_keys_update_response import ProjectAPIKeysUpdateResponse +from ..types.project_domains_retrieve_response import ProjectDomainsRetrieveResponse +from ..types.project_templates_create_response import ProjectTemplatesCreateResponse +from ..types.project_templates_update_response import ProjectTemplatesUpdateResponse +from ..types.project_api_keys_retrieve_response import ProjectAPIKeysRetrieveResponse +from ..types.project_templates_retrieve_response import ProjectTemplatesRetrieveResponse __all__ = ["ProjectResource", "AsyncProjectResource"] class ProjectResource(SyncAPIResource): - @cached_property - def v1(self) -> V1Resource: - return V1Resource(self._client) - @cached_property def with_raw_response(self) -> ProjectResourceWithRawResponse: """ @@ -612,10 +600,6 @@ def templates_update( class AsyncProjectResource(AsyncAPIResource): - @cached_property - def v1(self) -> AsyncV1Resource: - return AsyncV1Resource(self._client) - @cached_property def with_raw_response(self) -> AsyncProjectResourceWithRawResponse: """ @@ -1231,10 +1215,6 @@ def __init__(self, project: ProjectResource) -> None: project.templates_update, ) - @cached_property - def v1(self) -> V1ResourceWithRawResponse: - return V1ResourceWithRawResponse(self._project.v1) - class AsyncProjectResourceWithRawResponse: def __init__(self, project: AsyncProjectResource) -> None: @@ -1289,10 +1269,6 @@ def __init__(self, project: AsyncProjectResource) -> None: project.templates_update, ) - @cached_property - def v1(self) -> AsyncV1ResourceWithRawResponse: - return AsyncV1ResourceWithRawResponse(self._project.v1) - class ProjectResourceWithStreamingResponse: def __init__(self, project: ProjectResource) -> None: @@ -1347,10 +1323,6 @@ def __init__(self, project: ProjectResource) -> None: project.templates_update, ) - @cached_property - def v1(self) -> V1ResourceWithStreamingResponse: - return V1ResourceWithStreamingResponse(self._project.v1) - class AsyncProjectResourceWithStreamingResponse: def __init__(self, project: AsyncProjectResource) -> None: @@ -1404,7 +1376,3 @@ def __init__(self, project: AsyncProjectResource) -> None: self.templates_update = async_to_streamed_response_wrapper( project.templates_update, ) - - @cached_property - def v1(self) -> AsyncV1ResourceWithStreamingResponse: - return AsyncV1ResourceWithStreamingResponse(self._project.v1) diff --git a/src/unlayer/resources/project/__init__.py b/src/unlayer/resources/project/__init__.py deleted file mode 100644 index a65f9f8..0000000 --- a/src/unlayer/resources/project/__init__.py +++ /dev/null @@ -1,33 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from .v1 import ( - V1Resource, - AsyncV1Resource, - V1ResourceWithRawResponse, - AsyncV1ResourceWithRawResponse, - V1ResourceWithStreamingResponse, - AsyncV1ResourceWithStreamingResponse, -) -from .project import ( - ProjectResource, - AsyncProjectResource, - ProjectResourceWithRawResponse, - AsyncProjectResourceWithRawResponse, - ProjectResourceWithStreamingResponse, - AsyncProjectResourceWithStreamingResponse, -) - -__all__ = [ - "V1Resource", - "AsyncV1Resource", - "V1ResourceWithRawResponse", - "AsyncV1ResourceWithRawResponse", - "V1ResourceWithStreamingResponse", - "AsyncV1ResourceWithStreamingResponse", - "ProjectResource", - "AsyncProjectResource", - "ProjectResourceWithRawResponse", - "AsyncProjectResourceWithRawResponse", - "ProjectResourceWithStreamingResponse", - "AsyncProjectResourceWithStreamingResponse", -] diff --git a/src/unlayer/resources/project/v1.py b/src/unlayer/resources/project/v1.py deleted file mode 100644 index d98b566..0000000 --- a/src/unlayer/resources/project/v1.py +++ /dev/null @@ -1,1374 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -import httpx - -from ..._types import Body, Omit, Query, Headers, NoneType, NotGiven, SequenceNotStr, omit, not_given -from ..._utils import maybe_transform, async_maybe_transform -from ..._compat import cached_property -from ..._resource import SyncAPIResource, AsyncAPIResource -from ..._response import ( - to_raw_response_wrapper, - to_streamed_response_wrapper, - async_to_raw_response_wrapper, - async_to_streamed_response_wrapper, -) -from ..._base_client import make_request_options -from ...types.project import ( - v1_domains_create_params, - v1_domains_update_params, - v1_api_keys_create_params, - v1_api_keys_update_params, - v1_templates_create_params, - v1_templates_update_params, -) -from ...types.project.v1_current_list_response import V1CurrentListResponse -from ...types.project.v1_domains_list_response import V1DomainsListResponse -from ...types.project.v1_api_keys_list_response import V1APIKeysListResponse -from ...types.project.v1_domains_create_response import V1DomainsCreateResponse -from ...types.project.v1_domains_update_response import V1DomainsUpdateResponse -from ...types.project.v1_templates_list_response import V1TemplatesListResponse -from ...types.project.v1_api_keys_create_response import V1APIKeysCreateResponse -from ...types.project.v1_api_keys_update_response import V1APIKeysUpdateResponse -from ...types.project.v1_domains_retrieve_response import V1DomainsRetrieveResponse -from ...types.project.v1_templates_create_response import V1TemplatesCreateResponse -from ...types.project.v1_templates_update_response import V1TemplatesUpdateResponse -from ...types.project.v1_api_keys_retrieve_response import V1APIKeysRetrieveResponse -from ...types.project.v1_templates_retrieve_response import V1TemplatesRetrieveResponse - -__all__ = ["V1Resource", "AsyncV1Resource"] - - -class V1Resource(SyncAPIResource): - @cached_property - def with_raw_response(self) -> V1ResourceWithRawResponse: - """ - This property can be used as a prefix for any HTTP method call to return - the raw response object instead of the parsed content. - - For more information, see https://www.github.com/stainless-sdks/unlayer-python#accessing-raw-response-data-eg-headers - """ - return V1ResourceWithRawResponse(self) - - @cached_property - def with_streaming_response(self) -> V1ResourceWithStreamingResponse: - """ - An alternative to `.with_raw_response` that doesn't eagerly read the response body. - - For more information, see https://www.github.com/stainless-sdks/unlayer-python#with_streaming_response - """ - return V1ResourceWithStreamingResponse(self) - - def api_keys_create( - self, - *, - name: str, - domains: SequenceNotStr[str] | Omit = omit, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> V1APIKeysCreateResponse: - """ - Create a new API key for the project. - - Args: - name: Name for the API key - - domains: Allowed domains for this API key - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - return self._post( - "/project/v1/api-keys", - body=maybe_transform( - { - "name": name, - "domains": domains, - }, - v1_api_keys_create_params.V1APIKeysCreateParams, - ), - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=V1APIKeysCreateResponse, - ) - - def api_keys_delete( - self, - id: str, - *, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> None: - """ - Revoke API key. - - Args: - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - if not id: - raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") - extra_headers = {"Accept": "*/*", **(extra_headers or {})} - return self._delete( - f"/project/v1/api-keys/{id}", - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=NoneType, - ) - - def api_keys_list( - self, - *, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> V1APIKeysListResponse: - """List all API keys for the project.""" - return self._get( - "/project/v1/api-keys", - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=V1APIKeysListResponse, - ) - - def api_keys_retrieve( - self, - id: str, - *, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> V1APIKeysRetrieveResponse: - """ - Get API key details by ID. - - Args: - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - if not id: - raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") - return self._get( - f"/project/v1/api-keys/{id}", - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=V1APIKeysRetrieveResponse, - ) - - def api_keys_update( - self, - id: str, - *, - active: bool | Omit = omit, - domains: SequenceNotStr[str] | Omit = omit, - name: str | Omit = omit, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> V1APIKeysUpdateResponse: - """ - Update API key settings. - - Args: - active: Whether the API key is active - - domains: Updated allowed domains - - name: Updated name for the API key - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - if not id: - raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") - return self._put( - f"/project/v1/api-keys/{id}", - body=maybe_transform( - { - "active": active, - "domains": domains, - "name": name, - }, - v1_api_keys_update_params.V1APIKeysUpdateParams, - ), - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=V1APIKeysUpdateResponse, - ) - - def current_list( - self, - *, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> V1CurrentListResponse: - """Get project details for the authenticated project.""" - return self._get( - "/project/v1/current", - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=V1CurrentListResponse, - ) - - def domains_create( - self, - *, - domain: str, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> V1DomainsCreateResponse: - """ - Add a new domain to the project. - - Args: - domain: Domain name to add - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - return self._post( - "/project/v1/domains", - body=maybe_transform({"domain": domain}, v1_domains_create_params.V1DomainsCreateParams), - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=V1DomainsCreateResponse, - ) - - def domains_delete( - self, - id: str, - *, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> None: - """ - Remove domain from project. - - Args: - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - if not id: - raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") - extra_headers = {"Accept": "*/*", **(extra_headers or {})} - return self._delete( - f"/project/v1/domains/{id}", - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=NoneType, - ) - - def domains_list( - self, - *, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> V1DomainsListResponse: - """List all domains for the project.""" - return self._get( - "/project/v1/domains", - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=V1DomainsListResponse, - ) - - def domains_retrieve( - self, - id: str, - *, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> V1DomainsRetrieveResponse: - """ - Get domain details by ID. - - Args: - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - if not id: - raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") - return self._get( - f"/project/v1/domains/{id}", - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=V1DomainsRetrieveResponse, - ) - - def domains_update( - self, - id: str, - *, - domain: str | Omit = omit, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> V1DomainsUpdateResponse: - """ - Update domain settings. - - Args: - domain: Updated domain name - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - if not id: - raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") - return self._put( - f"/project/v1/domains/{id}", - body=maybe_transform({"domain": domain}, v1_domains_update_params.V1DomainsUpdateParams), - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=V1DomainsUpdateResponse, - ) - - def templates_create( - self, - *, - name: str, - body: str | Omit = omit, - subject: str | Omit = omit, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> V1TemplatesCreateResponse: - """ - Create a new project template. - - Args: - name: Template name - - body: Email body content - - subject: Email subject line - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - return self._post( - "/project/v1/templates", - body=maybe_transform( - { - "name": name, - "body": body, - "subject": subject, - }, - v1_templates_create_params.V1TemplatesCreateParams, - ), - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=V1TemplatesCreateResponse, - ) - - def templates_delete( - self, - id: str, - *, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> None: - """ - Delete project template. - - Args: - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - if not id: - raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") - extra_headers = {"Accept": "*/*", **(extra_headers or {})} - return self._delete( - f"/project/v1/templates/{id}", - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=NoneType, - ) - - def templates_list( - self, - *, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> V1TemplatesListResponse: - """Get all project templates.""" - return self._get( - "/project/v1/templates", - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=V1TemplatesListResponse, - ) - - def templates_retrieve( - self, - id: str, - *, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> V1TemplatesRetrieveResponse: - """ - Get project template by ID. - - Args: - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - if not id: - raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") - return self._get( - f"/project/v1/templates/{id}", - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=V1TemplatesRetrieveResponse, - ) - - def templates_update( - self, - id: str, - *, - body: str | Omit = omit, - name: str | Omit = omit, - subject: str | Omit = omit, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> V1TemplatesUpdateResponse: - """ - Update project template. - - Args: - body: Updated email body content - - name: Updated template name - - subject: Updated email subject line - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - if not id: - raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") - return self._put( - f"/project/v1/templates/{id}", - body=maybe_transform( - { - "body": body, - "name": name, - "subject": subject, - }, - v1_templates_update_params.V1TemplatesUpdateParams, - ), - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=V1TemplatesUpdateResponse, - ) - - -class AsyncV1Resource(AsyncAPIResource): - @cached_property - def with_raw_response(self) -> AsyncV1ResourceWithRawResponse: - """ - This property can be used as a prefix for any HTTP method call to return - the raw response object instead of the parsed content. - - For more information, see https://www.github.com/stainless-sdks/unlayer-python#accessing-raw-response-data-eg-headers - """ - return AsyncV1ResourceWithRawResponse(self) - - @cached_property - def with_streaming_response(self) -> AsyncV1ResourceWithStreamingResponse: - """ - An alternative to `.with_raw_response` that doesn't eagerly read the response body. - - For more information, see https://www.github.com/stainless-sdks/unlayer-python#with_streaming_response - """ - return AsyncV1ResourceWithStreamingResponse(self) - - async def api_keys_create( - self, - *, - name: str, - domains: SequenceNotStr[str] | Omit = omit, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> V1APIKeysCreateResponse: - """ - Create a new API key for the project. - - Args: - name: Name for the API key - - domains: Allowed domains for this API key - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - return await self._post( - "/project/v1/api-keys", - body=await async_maybe_transform( - { - "name": name, - "domains": domains, - }, - v1_api_keys_create_params.V1APIKeysCreateParams, - ), - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=V1APIKeysCreateResponse, - ) - - async def api_keys_delete( - self, - id: str, - *, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> None: - """ - Revoke API key. - - Args: - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - if not id: - raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") - extra_headers = {"Accept": "*/*", **(extra_headers or {})} - return await self._delete( - f"/project/v1/api-keys/{id}", - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=NoneType, - ) - - async def api_keys_list( - self, - *, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> V1APIKeysListResponse: - """List all API keys for the project.""" - return await self._get( - "/project/v1/api-keys", - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=V1APIKeysListResponse, - ) - - async def api_keys_retrieve( - self, - id: str, - *, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> V1APIKeysRetrieveResponse: - """ - Get API key details by ID. - - Args: - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - if not id: - raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") - return await self._get( - f"/project/v1/api-keys/{id}", - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=V1APIKeysRetrieveResponse, - ) - - async def api_keys_update( - self, - id: str, - *, - active: bool | Omit = omit, - domains: SequenceNotStr[str] | Omit = omit, - name: str | Omit = omit, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> V1APIKeysUpdateResponse: - """ - Update API key settings. - - Args: - active: Whether the API key is active - - domains: Updated allowed domains - - name: Updated name for the API key - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - if not id: - raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") - return await self._put( - f"/project/v1/api-keys/{id}", - body=await async_maybe_transform( - { - "active": active, - "domains": domains, - "name": name, - }, - v1_api_keys_update_params.V1APIKeysUpdateParams, - ), - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=V1APIKeysUpdateResponse, - ) - - async def current_list( - self, - *, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> V1CurrentListResponse: - """Get project details for the authenticated project.""" - return await self._get( - "/project/v1/current", - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=V1CurrentListResponse, - ) - - async def domains_create( - self, - *, - domain: str, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> V1DomainsCreateResponse: - """ - Add a new domain to the project. - - Args: - domain: Domain name to add - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - return await self._post( - "/project/v1/domains", - body=await async_maybe_transform({"domain": domain}, v1_domains_create_params.V1DomainsCreateParams), - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=V1DomainsCreateResponse, - ) - - async def domains_delete( - self, - id: str, - *, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> None: - """ - Remove domain from project. - - Args: - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - if not id: - raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") - extra_headers = {"Accept": "*/*", **(extra_headers or {})} - return await self._delete( - f"/project/v1/domains/{id}", - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=NoneType, - ) - - async def domains_list( - self, - *, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> V1DomainsListResponse: - """List all domains for the project.""" - return await self._get( - "/project/v1/domains", - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=V1DomainsListResponse, - ) - - async def domains_retrieve( - self, - id: str, - *, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> V1DomainsRetrieveResponse: - """ - Get domain details by ID. - - Args: - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - if not id: - raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") - return await self._get( - f"/project/v1/domains/{id}", - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=V1DomainsRetrieveResponse, - ) - - async def domains_update( - self, - id: str, - *, - domain: str | Omit = omit, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> V1DomainsUpdateResponse: - """ - Update domain settings. - - Args: - domain: Updated domain name - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - if not id: - raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") - return await self._put( - f"/project/v1/domains/{id}", - body=await async_maybe_transform({"domain": domain}, v1_domains_update_params.V1DomainsUpdateParams), - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=V1DomainsUpdateResponse, - ) - - async def templates_create( - self, - *, - name: str, - body: str | Omit = omit, - subject: str | Omit = omit, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> V1TemplatesCreateResponse: - """ - Create a new project template. - - Args: - name: Template name - - body: Email body content - - subject: Email subject line - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - return await self._post( - "/project/v1/templates", - body=await async_maybe_transform( - { - "name": name, - "body": body, - "subject": subject, - }, - v1_templates_create_params.V1TemplatesCreateParams, - ), - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=V1TemplatesCreateResponse, - ) - - async def templates_delete( - self, - id: str, - *, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> None: - """ - Delete project template. - - Args: - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - if not id: - raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") - extra_headers = {"Accept": "*/*", **(extra_headers or {})} - return await self._delete( - f"/project/v1/templates/{id}", - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=NoneType, - ) - - async def templates_list( - self, - *, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> V1TemplatesListResponse: - """Get all project templates.""" - return await self._get( - "/project/v1/templates", - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=V1TemplatesListResponse, - ) - - async def templates_retrieve( - self, - id: str, - *, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> V1TemplatesRetrieveResponse: - """ - Get project template by ID. - - Args: - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - if not id: - raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") - return await self._get( - f"/project/v1/templates/{id}", - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=V1TemplatesRetrieveResponse, - ) - - async def templates_update( - self, - id: str, - *, - body: str | Omit = omit, - name: str | Omit = omit, - subject: str | Omit = omit, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> V1TemplatesUpdateResponse: - """ - Update project template. - - Args: - body: Updated email body content - - name: Updated template name - - subject: Updated email subject line - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - if not id: - raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") - return await self._put( - f"/project/v1/templates/{id}", - body=await async_maybe_transform( - { - "body": body, - "name": name, - "subject": subject, - }, - v1_templates_update_params.V1TemplatesUpdateParams, - ), - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=V1TemplatesUpdateResponse, - ) - - -class V1ResourceWithRawResponse: - def __init__(self, v1: V1Resource) -> None: - self._v1 = v1 - - self.api_keys_create = to_raw_response_wrapper( - v1.api_keys_create, - ) - self.api_keys_delete = to_raw_response_wrapper( - v1.api_keys_delete, - ) - self.api_keys_list = to_raw_response_wrapper( - v1.api_keys_list, - ) - self.api_keys_retrieve = to_raw_response_wrapper( - v1.api_keys_retrieve, - ) - self.api_keys_update = to_raw_response_wrapper( - v1.api_keys_update, - ) - self.current_list = to_raw_response_wrapper( - v1.current_list, - ) - self.domains_create = to_raw_response_wrapper( - v1.domains_create, - ) - self.domains_delete = to_raw_response_wrapper( - v1.domains_delete, - ) - self.domains_list = to_raw_response_wrapper( - v1.domains_list, - ) - self.domains_retrieve = to_raw_response_wrapper( - v1.domains_retrieve, - ) - self.domains_update = to_raw_response_wrapper( - v1.domains_update, - ) - self.templates_create = to_raw_response_wrapper( - v1.templates_create, - ) - self.templates_delete = to_raw_response_wrapper( - v1.templates_delete, - ) - self.templates_list = to_raw_response_wrapper( - v1.templates_list, - ) - self.templates_retrieve = to_raw_response_wrapper( - v1.templates_retrieve, - ) - self.templates_update = to_raw_response_wrapper( - v1.templates_update, - ) - - -class AsyncV1ResourceWithRawResponse: - def __init__(self, v1: AsyncV1Resource) -> None: - self._v1 = v1 - - self.api_keys_create = async_to_raw_response_wrapper( - v1.api_keys_create, - ) - self.api_keys_delete = async_to_raw_response_wrapper( - v1.api_keys_delete, - ) - self.api_keys_list = async_to_raw_response_wrapper( - v1.api_keys_list, - ) - self.api_keys_retrieve = async_to_raw_response_wrapper( - v1.api_keys_retrieve, - ) - self.api_keys_update = async_to_raw_response_wrapper( - v1.api_keys_update, - ) - self.current_list = async_to_raw_response_wrapper( - v1.current_list, - ) - self.domains_create = async_to_raw_response_wrapper( - v1.domains_create, - ) - self.domains_delete = async_to_raw_response_wrapper( - v1.domains_delete, - ) - self.domains_list = async_to_raw_response_wrapper( - v1.domains_list, - ) - self.domains_retrieve = async_to_raw_response_wrapper( - v1.domains_retrieve, - ) - self.domains_update = async_to_raw_response_wrapper( - v1.domains_update, - ) - self.templates_create = async_to_raw_response_wrapper( - v1.templates_create, - ) - self.templates_delete = async_to_raw_response_wrapper( - v1.templates_delete, - ) - self.templates_list = async_to_raw_response_wrapper( - v1.templates_list, - ) - self.templates_retrieve = async_to_raw_response_wrapper( - v1.templates_retrieve, - ) - self.templates_update = async_to_raw_response_wrapper( - v1.templates_update, - ) - - -class V1ResourceWithStreamingResponse: - def __init__(self, v1: V1Resource) -> None: - self._v1 = v1 - - self.api_keys_create = to_streamed_response_wrapper( - v1.api_keys_create, - ) - self.api_keys_delete = to_streamed_response_wrapper( - v1.api_keys_delete, - ) - self.api_keys_list = to_streamed_response_wrapper( - v1.api_keys_list, - ) - self.api_keys_retrieve = to_streamed_response_wrapper( - v1.api_keys_retrieve, - ) - self.api_keys_update = to_streamed_response_wrapper( - v1.api_keys_update, - ) - self.current_list = to_streamed_response_wrapper( - v1.current_list, - ) - self.domains_create = to_streamed_response_wrapper( - v1.domains_create, - ) - self.domains_delete = to_streamed_response_wrapper( - v1.domains_delete, - ) - self.domains_list = to_streamed_response_wrapper( - v1.domains_list, - ) - self.domains_retrieve = to_streamed_response_wrapper( - v1.domains_retrieve, - ) - self.domains_update = to_streamed_response_wrapper( - v1.domains_update, - ) - self.templates_create = to_streamed_response_wrapper( - v1.templates_create, - ) - self.templates_delete = to_streamed_response_wrapper( - v1.templates_delete, - ) - self.templates_list = to_streamed_response_wrapper( - v1.templates_list, - ) - self.templates_retrieve = to_streamed_response_wrapper( - v1.templates_retrieve, - ) - self.templates_update = to_streamed_response_wrapper( - v1.templates_update, - ) - - -class AsyncV1ResourceWithStreamingResponse: - def __init__(self, v1: AsyncV1Resource) -> None: - self._v1 = v1 - - self.api_keys_create = async_to_streamed_response_wrapper( - v1.api_keys_create, - ) - self.api_keys_delete = async_to_streamed_response_wrapper( - v1.api_keys_delete, - ) - self.api_keys_list = async_to_streamed_response_wrapper( - v1.api_keys_list, - ) - self.api_keys_retrieve = async_to_streamed_response_wrapper( - v1.api_keys_retrieve, - ) - self.api_keys_update = async_to_streamed_response_wrapper( - v1.api_keys_update, - ) - self.current_list = async_to_streamed_response_wrapper( - v1.current_list, - ) - self.domains_create = async_to_streamed_response_wrapper( - v1.domains_create, - ) - self.domains_delete = async_to_streamed_response_wrapper( - v1.domains_delete, - ) - self.domains_list = async_to_streamed_response_wrapper( - v1.domains_list, - ) - self.domains_retrieve = async_to_streamed_response_wrapper( - v1.domains_retrieve, - ) - self.domains_update = async_to_streamed_response_wrapper( - v1.domains_update, - ) - self.templates_create = async_to_streamed_response_wrapper( - v1.templates_create, - ) - self.templates_delete = async_to_streamed_response_wrapper( - v1.templates_delete, - ) - self.templates_list = async_to_streamed_response_wrapper( - v1.templates_list, - ) - self.templates_retrieve = async_to_streamed_response_wrapper( - v1.templates_retrieve, - ) - self.templates_update = async_to_streamed_response_wrapper( - v1.templates_update, - ) diff --git a/src/unlayer/types/documents/__init__.py b/src/unlayer/types/documents/__init__.py deleted file mode 100644 index 987e2ee..0000000 --- a/src/unlayer/types/documents/__init__.py +++ /dev/null @@ -1,11 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from .v1_generate_create_params import V1GenerateCreateParams as V1GenerateCreateParams -from .v1_generate_create_response import V1GenerateCreateResponse as V1GenerateCreateResponse -from .v1_documents_retrieve_response import V1DocumentsRetrieveResponse as V1DocumentsRetrieveResponse -from .v1_generate_template_template_params import V1GenerateTemplateTemplateParams as V1GenerateTemplateTemplateParams -from .v1_generate_template_template_response import ( - V1GenerateTemplateTemplateResponse as V1GenerateTemplateTemplateResponse, -) diff --git a/src/unlayer/types/documents/v1_documents_retrieve_response.py b/src/unlayer/types/documents/v1_documents_retrieve_response.py deleted file mode 100644 index 119146c..0000000 --- a/src/unlayer/types/documents/v1_documents_retrieve_response.py +++ /dev/null @@ -1,37 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing import Optional -from datetime import datetime -from typing_extensions import Literal - -from pydantic import Field as FieldInfo - -from ..._models import BaseModel - -__all__ = ["V1DocumentsRetrieveResponse"] - - -class V1DocumentsRetrieveResponse(BaseModel): - id: Optional[str] = None - """Document ID""" - - completed_at: Optional[datetime] = FieldInfo(alias="completedAt", default=None) - """When the document generation was completed""" - - created_at: Optional[datetime] = FieldInfo(alias="createdAt", default=None) - """When the document was created""" - - filename: Optional[str] = None - """Generated filename""" - - file_size: Optional[float] = FieldInfo(alias="fileSize", default=None) - """File size in bytes""" - - page_count: Optional[float] = FieldInfo(alias="pageCount", default=None) - """Number of pages in the PDF""" - - pdf_url: Optional[str] = FieldInfo(alias="pdfUrl", default=None) - """URL to download the PDF""" - - status: Optional[Literal["generating", "completed", "failed"]] = None - """Current document status""" diff --git a/src/unlayer/types/documents/v1_generate_create_params.py b/src/unlayer/types/documents/v1_generate_create_params.py deleted file mode 100644 index 3ae71b7..0000000 --- a/src/unlayer/types/documents/v1_generate_create_params.py +++ /dev/null @@ -1,27 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing import Dict -from typing_extensions import Required, Annotated, TypedDict - -from ..._utils import PropertyInfo - -__all__ = ["V1GenerateCreateParams"] - - -class V1GenerateCreateParams(TypedDict, total=False): - design: Required[Dict[str, object]] - """Proprietary design format JSON""" - - filename: str - """Optional filename for the generated PDF""" - - html: str - """HTML content to convert to PDF""" - - merge_tags: Annotated[Dict[str, str], PropertyInfo(alias="mergeTags")] - """Optional merge tags for personalization""" - - url: str - """URL to convert to PDF""" diff --git a/src/unlayer/types/documents/v1_generate_create_response.py b/src/unlayer/types/documents/v1_generate_create_response.py deleted file mode 100644 index 754837d..0000000 --- a/src/unlayer/types/documents/v1_generate_create_response.py +++ /dev/null @@ -1,23 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing import Optional -from typing_extensions import Literal - -from pydantic import Field as FieldInfo - -from ..._models import BaseModel - -__all__ = ["V1GenerateCreateResponse"] - - -class V1GenerateCreateResponse(BaseModel): - document_id: Optional[str] = FieldInfo(alias="documentId", default=None) - """Unique document identifier""" - - filename: Optional[str] = None - """Generated filename""" - - pdf_url: Optional[str] = FieldInfo(alias="pdfUrl", default=None) - """URL to download the generated PDF""" - - status: Optional[Literal["generating", "completed", "failed"]] = None diff --git a/src/unlayer/types/documents/v1_generate_template_template_params.py b/src/unlayer/types/documents/v1_generate_template_template_params.py deleted file mode 100644 index ca9e978..0000000 --- a/src/unlayer/types/documents/v1_generate_template_template_params.py +++ /dev/null @@ -1,21 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing import Dict -from typing_extensions import Required, Annotated, TypedDict - -from ..._utils import PropertyInfo - -__all__ = ["V1GenerateTemplateTemplateParams"] - - -class V1GenerateTemplateTemplateParams(TypedDict, total=False): - template_id: Required[Annotated[str, PropertyInfo(alias="templateId")]] - """ID of the template to use for generation""" - - filename: str - """Optional filename for the generated PDF""" - - merge_tags: Annotated[Dict[str, str], PropertyInfo(alias="mergeTags")] - """Optional merge tags for personalization""" diff --git a/src/unlayer/types/documents/v1_generate_template_template_response.py b/src/unlayer/types/documents/v1_generate_template_template_response.py deleted file mode 100644 index 013c170..0000000 --- a/src/unlayer/types/documents/v1_generate_template_template_response.py +++ /dev/null @@ -1,23 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing import Optional -from typing_extensions import Literal - -from pydantic import Field as FieldInfo - -from ..._models import BaseModel - -__all__ = ["V1GenerateTemplateTemplateResponse"] - - -class V1GenerateTemplateTemplateResponse(BaseModel): - document_id: Optional[str] = FieldInfo(alias="documentId", default=None) - """Unique document identifier""" - - filename: Optional[str] = None - """Generated filename""" - - pdf_url: Optional[str] = FieldInfo(alias="pdfUrl", default=None) - """URL to download the generated PDF""" - - status: Optional[Literal["generating", "completed", "failed"]] = None diff --git a/src/unlayer/types/emails/__init__.py b/src/unlayer/types/emails/__init__.py deleted file mode 100644 index 893ea05..0000000 --- a/src/unlayer/types/emails/__init__.py +++ /dev/null @@ -1,11 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from .v1_retrieve_response import V1RetrieveResponse as V1RetrieveResponse -from .v1_send_create_params import V1SendCreateParams as V1SendCreateParams -from .v1_render_create_params import V1RenderCreateParams as V1RenderCreateParams -from .v1_send_create_response import V1SendCreateResponse as V1SendCreateResponse -from .v1_render_create_response import V1RenderCreateResponse as V1RenderCreateResponse -from .v1_send_template_template_params import V1SendTemplateTemplateParams as V1SendTemplateTemplateParams -from .v1_send_template_template_response import V1SendTemplateTemplateResponse as V1SendTemplateTemplateResponse diff --git a/src/unlayer/types/emails/v1_render_create_params.py b/src/unlayer/types/emails/v1_render_create_params.py deleted file mode 100644 index 9fcd33c..0000000 --- a/src/unlayer/types/emails/v1_render_create_params.py +++ /dev/null @@ -1,18 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing import Dict -from typing_extensions import Required, Annotated, TypedDict - -from ..._utils import PropertyInfo - -__all__ = ["V1RenderCreateParams"] - - -class V1RenderCreateParams(TypedDict, total=False): - design: Required[Dict[str, object]] - """Proprietary design format JSON""" - - merge_tags: Annotated[Dict[str, str], PropertyInfo(alias="mergeTags")] - """Optional merge tags for personalization""" diff --git a/src/unlayer/types/emails/v1_render_create_response.py b/src/unlayer/types/emails/v1_render_create_response.py deleted file mode 100644 index fadb69c..0000000 --- a/src/unlayer/types/emails/v1_render_create_response.py +++ /dev/null @@ -1,12 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing import Optional - -from ..._models import BaseModel - -__all__ = ["V1RenderCreateResponse"] - - -class V1RenderCreateResponse(BaseModel): - html: Optional[str] = None - """Rendered HTML content""" diff --git a/src/unlayer/types/emails/v1_retrieve_response.py b/src/unlayer/types/emails/v1_retrieve_response.py deleted file mode 100644 index 5262e60..0000000 --- a/src/unlayer/types/emails/v1_retrieve_response.py +++ /dev/null @@ -1,31 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing import Optional -from datetime import datetime -from typing_extensions import Literal - -from pydantic import Field as FieldInfo - -from ..._models import BaseModel - -__all__ = ["V1RetrieveResponse"] - - -class V1RetrieveResponse(BaseModel): - id: Optional[str] = None - """Email message ID""" - - html: Optional[str] = None - """HTML content of the email (optional)""" - - sent_at: Optional[datetime] = FieldInfo(alias="sentAt", default=None) - """When the email was sent""" - - status: Optional[Literal["sent", "delivered", "opened", "clicked", "bounced", "failed"]] = None - """Current email status""" - - subject: Optional[str] = None - """Email subject line""" - - to: Optional[str] = None - """Recipient email address""" diff --git a/src/unlayer/types/emails/v1_send_create_params.py b/src/unlayer/types/emails/v1_send_create_params.py deleted file mode 100644 index 089845f..0000000 --- a/src/unlayer/types/emails/v1_send_create_params.py +++ /dev/null @@ -1,27 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing import Dict -from typing_extensions import Required, Annotated, TypedDict - -from ..._utils import PropertyInfo - -__all__ = ["V1SendCreateParams"] - - -class V1SendCreateParams(TypedDict, total=False): - design: Required[Dict[str, object]] - """Proprietary design format JSON""" - - to: Required[str] - """Recipient email address""" - - html: str - """HTML content to send""" - - merge_tags: Annotated[Dict[str, str], PropertyInfo(alias="mergeTags")] - """Optional merge tags for personalization""" - - subject: str - """Email subject line""" diff --git a/src/unlayer/types/emails/v1_send_create_response.py b/src/unlayer/types/emails/v1_send_create_response.py deleted file mode 100644 index 4503ab0..0000000 --- a/src/unlayer/types/emails/v1_send_create_response.py +++ /dev/null @@ -1,17 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing import Optional -from typing_extensions import Literal - -from pydantic import Field as FieldInfo - -from ..._models import BaseModel - -__all__ = ["V1SendCreateResponse"] - - -class V1SendCreateResponse(BaseModel): - message_id: Optional[str] = FieldInfo(alias="messageId", default=None) - """Unique message identifier""" - - status: Optional[Literal["sent", "queued", "failed"]] = None diff --git a/src/unlayer/types/emails/v1_send_template_template_params.py b/src/unlayer/types/emails/v1_send_template_template_params.py deleted file mode 100644 index d4aca78..0000000 --- a/src/unlayer/types/emails/v1_send_template_template_params.py +++ /dev/null @@ -1,24 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing import Dict -from typing_extensions import Required, Annotated, TypedDict - -from ..._utils import PropertyInfo - -__all__ = ["V1SendTemplateTemplateParams"] - - -class V1SendTemplateTemplateParams(TypedDict, total=False): - template_id: Required[Annotated[str, PropertyInfo(alias="templateId")]] - """ID of the template to use""" - - to: Required[str] - """Recipient email address""" - - merge_tags: Annotated[Dict[str, str], PropertyInfo(alias="mergeTags")] - """Optional merge tags for personalization""" - - subject: str - """Email subject line (optional, uses template default if not provided)""" diff --git a/src/unlayer/types/emails/v1_send_template_template_response.py b/src/unlayer/types/emails/v1_send_template_template_response.py deleted file mode 100644 index f876fe6..0000000 --- a/src/unlayer/types/emails/v1_send_template_template_response.py +++ /dev/null @@ -1,17 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing import Optional -from typing_extensions import Literal - -from pydantic import Field as FieldInfo - -from ..._models import BaseModel - -__all__ = ["V1SendTemplateTemplateResponse"] - - -class V1SendTemplateTemplateResponse(BaseModel): - message_id: Optional[str] = FieldInfo(alias="messageId", default=None) - """Unique message identifier""" - - status: Optional[Literal["sent", "queued", "failed"]] = None diff --git a/src/unlayer/types/pages/__init__.py b/src/unlayer/types/pages/__init__.py deleted file mode 100644 index c366123..0000000 --- a/src/unlayer/types/pages/__init__.py +++ /dev/null @@ -1,6 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from .v1_render_create_params import V1RenderCreateParams as V1RenderCreateParams -from .v1_render_create_response import V1RenderCreateResponse as V1RenderCreateResponse diff --git a/src/unlayer/types/pages/v1_render_create_params.py b/src/unlayer/types/pages/v1_render_create_params.py deleted file mode 100644 index 9fcd33c..0000000 --- a/src/unlayer/types/pages/v1_render_create_params.py +++ /dev/null @@ -1,18 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing import Dict -from typing_extensions import Required, Annotated, TypedDict - -from ..._utils import PropertyInfo - -__all__ = ["V1RenderCreateParams"] - - -class V1RenderCreateParams(TypedDict, total=False): - design: Required[Dict[str, object]] - """Proprietary design format JSON""" - - merge_tags: Annotated[Dict[str, str], PropertyInfo(alias="mergeTags")] - """Optional merge tags for personalization""" diff --git a/src/unlayer/types/pages/v1_render_create_response.py b/src/unlayer/types/pages/v1_render_create_response.py deleted file mode 100644 index fadb69c..0000000 --- a/src/unlayer/types/pages/v1_render_create_response.py +++ /dev/null @@ -1,12 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing import Optional - -from ..._models import BaseModel - -__all__ = ["V1RenderCreateResponse"] - - -class V1RenderCreateResponse(BaseModel): - html: Optional[str] = None - """Rendered HTML content""" diff --git a/src/unlayer/types/project/__init__.py b/src/unlayer/types/project/__init__.py deleted file mode 100644 index 8de899a..0000000 --- a/src/unlayer/types/project/__init__.py +++ /dev/null @@ -1,23 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from .v1_current_list_response import V1CurrentListResponse as V1CurrentListResponse -from .v1_domains_create_params import V1DomainsCreateParams as V1DomainsCreateParams -from .v1_domains_list_response import V1DomainsListResponse as V1DomainsListResponse -from .v1_domains_update_params import V1DomainsUpdateParams as V1DomainsUpdateParams -from .v1_api_keys_create_params import V1APIKeysCreateParams as V1APIKeysCreateParams -from .v1_api_keys_list_response import V1APIKeysListResponse as V1APIKeysListResponse -from .v1_api_keys_update_params import V1APIKeysUpdateParams as V1APIKeysUpdateParams -from .v1_domains_create_response import V1DomainsCreateResponse as V1DomainsCreateResponse -from .v1_domains_update_response import V1DomainsUpdateResponse as V1DomainsUpdateResponse -from .v1_templates_create_params import V1TemplatesCreateParams as V1TemplatesCreateParams -from .v1_templates_list_response import V1TemplatesListResponse as V1TemplatesListResponse -from .v1_templates_update_params import V1TemplatesUpdateParams as V1TemplatesUpdateParams -from .v1_api_keys_create_response import V1APIKeysCreateResponse as V1APIKeysCreateResponse -from .v1_api_keys_update_response import V1APIKeysUpdateResponse as V1APIKeysUpdateResponse -from .v1_domains_retrieve_response import V1DomainsRetrieveResponse as V1DomainsRetrieveResponse -from .v1_templates_create_response import V1TemplatesCreateResponse as V1TemplatesCreateResponse -from .v1_templates_update_response import V1TemplatesUpdateResponse as V1TemplatesUpdateResponse -from .v1_api_keys_retrieve_response import V1APIKeysRetrieveResponse as V1APIKeysRetrieveResponse -from .v1_templates_retrieve_response import V1TemplatesRetrieveResponse as V1TemplatesRetrieveResponse diff --git a/src/unlayer/types/project/v1_api_keys_create_params.py b/src/unlayer/types/project/v1_api_keys_create_params.py deleted file mode 100644 index b5cc46c..0000000 --- a/src/unlayer/types/project/v1_api_keys_create_params.py +++ /dev/null @@ -1,17 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing_extensions import Required, TypedDict - -from ..._types import SequenceNotStr - -__all__ = ["V1APIKeysCreateParams"] - - -class V1APIKeysCreateParams(TypedDict, total=False): - name: Required[str] - """Name for the API key""" - - domains: SequenceNotStr[str] - """Allowed domains for this API key""" diff --git a/src/unlayer/types/project/v1_api_keys_create_response.py b/src/unlayer/types/project/v1_api_keys_create_response.py deleted file mode 100644 index ac3a6d2..0000000 --- a/src/unlayer/types/project/v1_api_keys_create_response.py +++ /dev/null @@ -1,28 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing import List, Optional -from datetime import datetime - -from pydantic import Field as FieldInfo - -from ..._models import BaseModel - -__all__ = ["V1APIKeysCreateResponse", "Data"] - - -class Data(BaseModel): - id: Optional[str] = None - - active: Optional[bool] = None - - created_at: Optional[datetime] = FieldInfo(alias="createdAt", default=None) - - domains: Optional[List[str]] = None - - key: Optional[str] = None - - name: Optional[str] = None - - -class V1APIKeysCreateResponse(BaseModel): - data: Optional[Data] = None diff --git a/src/unlayer/types/project/v1_api_keys_list_response.py b/src/unlayer/types/project/v1_api_keys_list_response.py deleted file mode 100644 index e7a49c0..0000000 --- a/src/unlayer/types/project/v1_api_keys_list_response.py +++ /dev/null @@ -1,30 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing import List, Optional -from datetime import datetime - -from pydantic import Field as FieldInfo - -from ..._models import BaseModel - -__all__ = ["V1APIKeysListResponse", "Data"] - - -class Data(BaseModel): - id: Optional[str] = None - - active: Optional[bool] = None - - created_at: Optional[datetime] = FieldInfo(alias="createdAt", default=None) - - domains: Optional[List[str]] = None - - key: Optional[str] = None - - last_used: Optional[datetime] = FieldInfo(alias="lastUsed", default=None) - - name: Optional[str] = None - - -class V1APIKeysListResponse(BaseModel): - data: Optional[List[Data]] = None diff --git a/src/unlayer/types/project/v1_api_keys_retrieve_response.py b/src/unlayer/types/project/v1_api_keys_retrieve_response.py deleted file mode 100644 index c52caf2..0000000 --- a/src/unlayer/types/project/v1_api_keys_retrieve_response.py +++ /dev/null @@ -1,30 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing import List, Optional -from datetime import datetime - -from pydantic import Field as FieldInfo - -from ..._models import BaseModel - -__all__ = ["V1APIKeysRetrieveResponse", "Data"] - - -class Data(BaseModel): - id: Optional[str] = None - - active: Optional[bool] = None - - created_at: Optional[datetime] = FieldInfo(alias="createdAt", default=None) - - domains: Optional[List[str]] = None - - key: Optional[str] = None - - last_used: Optional[datetime] = FieldInfo(alias="lastUsed", default=None) - - name: Optional[str] = None - - -class V1APIKeysRetrieveResponse(BaseModel): - data: Optional[Data] = None diff --git a/src/unlayer/types/project/v1_api_keys_update_params.py b/src/unlayer/types/project/v1_api_keys_update_params.py deleted file mode 100644 index 965902f..0000000 --- a/src/unlayer/types/project/v1_api_keys_update_params.py +++ /dev/null @@ -1,20 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing_extensions import TypedDict - -from ..._types import SequenceNotStr - -__all__ = ["V1APIKeysUpdateParams"] - - -class V1APIKeysUpdateParams(TypedDict, total=False): - active: bool - """Whether the API key is active""" - - domains: SequenceNotStr[str] - """Updated allowed domains""" - - name: str - """Updated name for the API key""" diff --git a/src/unlayer/types/project/v1_api_keys_update_response.py b/src/unlayer/types/project/v1_api_keys_update_response.py deleted file mode 100644 index 16c675e..0000000 --- a/src/unlayer/types/project/v1_api_keys_update_response.py +++ /dev/null @@ -1,30 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing import List, Optional -from datetime import datetime - -from pydantic import Field as FieldInfo - -from ..._models import BaseModel - -__all__ = ["V1APIKeysUpdateResponse", "Data"] - - -class Data(BaseModel): - id: Optional[str] = None - - active: Optional[bool] = None - - created_at: Optional[datetime] = FieldInfo(alias="createdAt", default=None) - - domains: Optional[List[str]] = None - - key: Optional[str] = None - - last_used: Optional[datetime] = FieldInfo(alias="lastUsed", default=None) - - name: Optional[str] = None - - -class V1APIKeysUpdateResponse(BaseModel): - data: Optional[Data] = None diff --git a/src/unlayer/types/project/v1_current_list_response.py b/src/unlayer/types/project/v1_current_list_response.py deleted file mode 100644 index 26a480e..0000000 --- a/src/unlayer/types/project/v1_current_list_response.py +++ /dev/null @@ -1,32 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing import Optional -from datetime import datetime - -from pydantic import Field as FieldInfo - -from ..._models import BaseModel - -__all__ = ["V1CurrentListResponse", "Data", "DataWorkspace"] - - -class DataWorkspace(BaseModel): - id: Optional[float] = None - - name: Optional[str] = None - - -class Data(BaseModel): - id: Optional[float] = None - - created_at: Optional[datetime] = FieldInfo(alias="createdAt", default=None) - - name: Optional[str] = None - - status: Optional[str] = None - - workspace: Optional[DataWorkspace] = None - - -class V1CurrentListResponse(BaseModel): - data: Optional[Data] = None diff --git a/src/unlayer/types/project/v1_domains_create_params.py b/src/unlayer/types/project/v1_domains_create_params.py deleted file mode 100644 index 15f083b..0000000 --- a/src/unlayer/types/project/v1_domains_create_params.py +++ /dev/null @@ -1,12 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing_extensions import Required, TypedDict - -__all__ = ["V1DomainsCreateParams"] - - -class V1DomainsCreateParams(TypedDict, total=False): - domain: Required[str] - """Domain name to add""" diff --git a/src/unlayer/types/project/v1_domains_create_response.py b/src/unlayer/types/project/v1_domains_create_response.py deleted file mode 100644 index 2f25e01..0000000 --- a/src/unlayer/types/project/v1_domains_create_response.py +++ /dev/null @@ -1,26 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing import Optional -from datetime import datetime - -from pydantic import Field as FieldInfo - -from ..._models import BaseModel - -__all__ = ["V1DomainsCreateResponse", "Data"] - - -class Data(BaseModel): - id: Optional[str] = None - - created_at: Optional[datetime] = FieldInfo(alias="createdAt", default=None) - - domain: Optional[str] = None - - status: Optional[str] = None - - verified: Optional[bool] = None - - -class V1DomainsCreateResponse(BaseModel): - data: Optional[Data] = None diff --git a/src/unlayer/types/project/v1_domains_list_response.py b/src/unlayer/types/project/v1_domains_list_response.py deleted file mode 100644 index 5834ecd..0000000 --- a/src/unlayer/types/project/v1_domains_list_response.py +++ /dev/null @@ -1,27 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing import List, Optional -from datetime import datetime -from typing_extensions import Literal - -from pydantic import Field as FieldInfo - -from ..._models import BaseModel - -__all__ = ["V1DomainsListResponse", "Data"] - - -class Data(BaseModel): - id: Optional[str] = None - - created_at: Optional[datetime] = FieldInfo(alias="createdAt", default=None) - - domain: Optional[str] = None - - status: Optional[Literal["active", "pending", "failed"]] = None - - verified: Optional[bool] = None - - -class V1DomainsListResponse(BaseModel): - data: Optional[List[Data]] = None diff --git a/src/unlayer/types/project/v1_domains_retrieve_response.py b/src/unlayer/types/project/v1_domains_retrieve_response.py deleted file mode 100644 index cabe1f9..0000000 --- a/src/unlayer/types/project/v1_domains_retrieve_response.py +++ /dev/null @@ -1,26 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing import Optional -from datetime import datetime - -from pydantic import Field as FieldInfo - -from ..._models import BaseModel - -__all__ = ["V1DomainsRetrieveResponse", "Data"] - - -class Data(BaseModel): - id: Optional[str] = None - - created_at: Optional[datetime] = FieldInfo(alias="createdAt", default=None) - - domain: Optional[str] = None - - status: Optional[str] = None - - verified: Optional[bool] = None - - -class V1DomainsRetrieveResponse(BaseModel): - data: Optional[Data] = None diff --git a/src/unlayer/types/project/v1_domains_update_params.py b/src/unlayer/types/project/v1_domains_update_params.py deleted file mode 100644 index 36267c6..0000000 --- a/src/unlayer/types/project/v1_domains_update_params.py +++ /dev/null @@ -1,12 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing_extensions import TypedDict - -__all__ = ["V1DomainsUpdateParams"] - - -class V1DomainsUpdateParams(TypedDict, total=False): - domain: str - """Updated domain name""" diff --git a/src/unlayer/types/project/v1_domains_update_response.py b/src/unlayer/types/project/v1_domains_update_response.py deleted file mode 100644 index 4389356..0000000 --- a/src/unlayer/types/project/v1_domains_update_response.py +++ /dev/null @@ -1,26 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing import Optional -from datetime import datetime - -from pydantic import Field as FieldInfo - -from ..._models import BaseModel - -__all__ = ["V1DomainsUpdateResponse", "Data"] - - -class Data(BaseModel): - id: Optional[str] = None - - created_at: Optional[datetime] = FieldInfo(alias="createdAt", default=None) - - domain: Optional[str] = None - - status: Optional[str] = None - - verified: Optional[bool] = None - - -class V1DomainsUpdateResponse(BaseModel): - data: Optional[Data] = None diff --git a/src/unlayer/types/project/v1_templates_create_params.py b/src/unlayer/types/project/v1_templates_create_params.py deleted file mode 100644 index 0251cb6..0000000 --- a/src/unlayer/types/project/v1_templates_create_params.py +++ /dev/null @@ -1,18 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing_extensions import Required, TypedDict - -__all__ = ["V1TemplatesCreateParams"] - - -class V1TemplatesCreateParams(TypedDict, total=False): - name: Required[str] - """Template name""" - - body: str - """Email body content""" - - subject: str - """Email subject line""" diff --git a/src/unlayer/types/project/v1_templates_create_response.py b/src/unlayer/types/project/v1_templates_create_response.py deleted file mode 100644 index 59a6315..0000000 --- a/src/unlayer/types/project/v1_templates_create_response.py +++ /dev/null @@ -1,28 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing import Optional -from datetime import datetime - -from pydantic import Field as FieldInfo - -from ..._models import BaseModel - -__all__ = ["V1TemplatesCreateResponse", "Data"] - - -class Data(BaseModel): - id: Optional[str] = None - - body: Optional[str] = None - - created_at: Optional[datetime] = FieldInfo(alias="createdAt", default=None) - - name: Optional[str] = None - - subject: Optional[str] = None - - updated_at: Optional[datetime] = FieldInfo(alias="updatedAt", default=None) - - -class V1TemplatesCreateResponse(BaseModel): - data: Optional[Data] = None diff --git a/src/unlayer/types/project/v1_templates_list_response.py b/src/unlayer/types/project/v1_templates_list_response.py deleted file mode 100644 index 494335e..0000000 --- a/src/unlayer/types/project/v1_templates_list_response.py +++ /dev/null @@ -1,28 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing import List, Optional -from datetime import datetime - -from pydantic import Field as FieldInfo - -from ..._models import BaseModel - -__all__ = ["V1TemplatesListResponse", "Data"] - - -class Data(BaseModel): - id: Optional[str] = None - - body: Optional[str] = None - - created_at: Optional[datetime] = FieldInfo(alias="createdAt", default=None) - - name: Optional[str] = None - - subject: Optional[str] = None - - updated_at: Optional[datetime] = FieldInfo(alias="updatedAt", default=None) - - -class V1TemplatesListResponse(BaseModel): - data: Optional[List[Data]] = None diff --git a/src/unlayer/types/project/v1_templates_retrieve_response.py b/src/unlayer/types/project/v1_templates_retrieve_response.py deleted file mode 100644 index 2a113b8..0000000 --- a/src/unlayer/types/project/v1_templates_retrieve_response.py +++ /dev/null @@ -1,28 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing import Optional -from datetime import datetime - -from pydantic import Field as FieldInfo - -from ..._models import BaseModel - -__all__ = ["V1TemplatesRetrieveResponse", "Data"] - - -class Data(BaseModel): - id: Optional[str] = None - - body: Optional[str] = None - - created_at: Optional[datetime] = FieldInfo(alias="createdAt", default=None) - - name: Optional[str] = None - - subject: Optional[str] = None - - updated_at: Optional[datetime] = FieldInfo(alias="updatedAt", default=None) - - -class V1TemplatesRetrieveResponse(BaseModel): - data: Optional[Data] = None diff --git a/src/unlayer/types/project/v1_templates_update_params.py b/src/unlayer/types/project/v1_templates_update_params.py deleted file mode 100644 index 4c3848c..0000000 --- a/src/unlayer/types/project/v1_templates_update_params.py +++ /dev/null @@ -1,18 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing_extensions import TypedDict - -__all__ = ["V1TemplatesUpdateParams"] - - -class V1TemplatesUpdateParams(TypedDict, total=False): - body: str - """Updated email body content""" - - name: str - """Updated template name""" - - subject: str - """Updated email subject line""" diff --git a/src/unlayer/types/project/v1_templates_update_response.py b/src/unlayer/types/project/v1_templates_update_response.py deleted file mode 100644 index fa82212..0000000 --- a/src/unlayer/types/project/v1_templates_update_response.py +++ /dev/null @@ -1,28 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing import Optional -from datetime import datetime - -from pydantic import Field as FieldInfo - -from ..._models import BaseModel - -__all__ = ["V1TemplatesUpdateResponse", "Data"] - - -class Data(BaseModel): - id: Optional[str] = None - - body: Optional[str] = None - - created_at: Optional[datetime] = FieldInfo(alias="createdAt", default=None) - - name: Optional[str] = None - - subject: Optional[str] = None - - updated_at: Optional[datetime] = FieldInfo(alias="updatedAt", default=None) - - -class V1TemplatesUpdateResponse(BaseModel): - data: Optional[Data] = None diff --git a/tests/api_resources/documents/__init__.py b/tests/api_resources/documents/__init__.py deleted file mode 100644 index fd8019a..0000000 --- a/tests/api_resources/documents/__init__.py +++ /dev/null @@ -1 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. diff --git a/tests/api_resources/documents/test_v1.py b/tests/api_resources/documents/test_v1.py deleted file mode 100644 index a3e4ba0..0000000 --- a/tests/api_resources/documents/test_v1.py +++ /dev/null @@ -1,292 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -import os -from typing import Any, cast - -import pytest - -from unlayer import Unlayer, AsyncUnlayer -from tests.utils import assert_matches_type -from unlayer.types.documents import ( - V1GenerateCreateResponse, - V1DocumentsRetrieveResponse, - V1GenerateTemplateTemplateResponse, -) - -base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") - - -class TestV1: - parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) - - @parametrize - def test_method_documents_retrieve(self, client: Unlayer) -> None: - v1 = client.documents.v1.documents_retrieve( - "id", - ) - assert_matches_type(V1DocumentsRetrieveResponse, v1, path=["response"]) - - @parametrize - def test_raw_response_documents_retrieve(self, client: Unlayer) -> None: - response = client.documents.v1.with_raw_response.documents_retrieve( - "id", - ) - - assert response.is_closed is True - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - v1 = response.parse() - assert_matches_type(V1DocumentsRetrieveResponse, v1, path=["response"]) - - @parametrize - def test_streaming_response_documents_retrieve(self, client: Unlayer) -> None: - with client.documents.v1.with_streaming_response.documents_retrieve( - "id", - ) as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - - v1 = response.parse() - assert_matches_type(V1DocumentsRetrieveResponse, v1, path=["response"]) - - assert cast(Any, response.is_closed) is True - - @parametrize - def test_path_params_documents_retrieve(self, client: Unlayer) -> None: - with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): - client.documents.v1.with_raw_response.documents_retrieve( - "", - ) - - @parametrize - def test_method_generate_create(self, client: Unlayer) -> None: - v1 = client.documents.v1.generate_create( - design={ - "counters": "bar", - "body": "bar", - }, - ) - assert_matches_type(V1GenerateCreateResponse, v1, path=["response"]) - - @parametrize - def test_method_generate_create_with_all_params(self, client: Unlayer) -> None: - v1 = client.documents.v1.generate_create( - design={ - "counters": "bar", - "body": "bar", - }, - filename="filename", - html="html", - merge_tags={"foo": "string"}, - url="https://example.com", - ) - assert_matches_type(V1GenerateCreateResponse, v1, path=["response"]) - - @parametrize - def test_raw_response_generate_create(self, client: Unlayer) -> None: - response = client.documents.v1.with_raw_response.generate_create( - design={ - "counters": "bar", - "body": "bar", - }, - ) - - assert response.is_closed is True - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - v1 = response.parse() - assert_matches_type(V1GenerateCreateResponse, v1, path=["response"]) - - @parametrize - def test_streaming_response_generate_create(self, client: Unlayer) -> None: - with client.documents.v1.with_streaming_response.generate_create( - design={ - "counters": "bar", - "body": "bar", - }, - ) as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - - v1 = response.parse() - assert_matches_type(V1GenerateCreateResponse, v1, path=["response"]) - - assert cast(Any, response.is_closed) is True - - @parametrize - def test_method_generate_template_template(self, client: Unlayer) -> None: - v1 = client.documents.v1.generate_template_template( - template_id="templateId", - ) - assert_matches_type(V1GenerateTemplateTemplateResponse, v1, path=["response"]) - - @parametrize - def test_method_generate_template_template_with_all_params(self, client: Unlayer) -> None: - v1 = client.documents.v1.generate_template_template( - template_id="templateId", - filename="filename", - merge_tags={"foo": "string"}, - ) - assert_matches_type(V1GenerateTemplateTemplateResponse, v1, path=["response"]) - - @parametrize - def test_raw_response_generate_template_template(self, client: Unlayer) -> None: - response = client.documents.v1.with_raw_response.generate_template_template( - template_id="templateId", - ) - - assert response.is_closed is True - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - v1 = response.parse() - assert_matches_type(V1GenerateTemplateTemplateResponse, v1, path=["response"]) - - @parametrize - def test_streaming_response_generate_template_template(self, client: Unlayer) -> None: - with client.documents.v1.with_streaming_response.generate_template_template( - template_id="templateId", - ) as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - - v1 = response.parse() - assert_matches_type(V1GenerateTemplateTemplateResponse, v1, path=["response"]) - - assert cast(Any, response.is_closed) is True - - -class TestAsyncV1: - parametrize = pytest.mark.parametrize( - "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] - ) - - @parametrize - async def test_method_documents_retrieve(self, async_client: AsyncUnlayer) -> None: - v1 = await async_client.documents.v1.documents_retrieve( - "id", - ) - assert_matches_type(V1DocumentsRetrieveResponse, v1, path=["response"]) - - @parametrize - async def test_raw_response_documents_retrieve(self, async_client: AsyncUnlayer) -> None: - response = await async_client.documents.v1.with_raw_response.documents_retrieve( - "id", - ) - - assert response.is_closed is True - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - v1 = await response.parse() - assert_matches_type(V1DocumentsRetrieveResponse, v1, path=["response"]) - - @parametrize - async def test_streaming_response_documents_retrieve(self, async_client: AsyncUnlayer) -> None: - async with async_client.documents.v1.with_streaming_response.documents_retrieve( - "id", - ) as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - - v1 = await response.parse() - assert_matches_type(V1DocumentsRetrieveResponse, v1, path=["response"]) - - assert cast(Any, response.is_closed) is True - - @parametrize - async def test_path_params_documents_retrieve(self, async_client: AsyncUnlayer) -> None: - with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): - await async_client.documents.v1.with_raw_response.documents_retrieve( - "", - ) - - @parametrize - async def test_method_generate_create(self, async_client: AsyncUnlayer) -> None: - v1 = await async_client.documents.v1.generate_create( - design={ - "counters": "bar", - "body": "bar", - }, - ) - assert_matches_type(V1GenerateCreateResponse, v1, path=["response"]) - - @parametrize - async def test_method_generate_create_with_all_params(self, async_client: AsyncUnlayer) -> None: - v1 = await async_client.documents.v1.generate_create( - design={ - "counters": "bar", - "body": "bar", - }, - filename="filename", - html="html", - merge_tags={"foo": "string"}, - url="https://example.com", - ) - assert_matches_type(V1GenerateCreateResponse, v1, path=["response"]) - - @parametrize - async def test_raw_response_generate_create(self, async_client: AsyncUnlayer) -> None: - response = await async_client.documents.v1.with_raw_response.generate_create( - design={ - "counters": "bar", - "body": "bar", - }, - ) - - assert response.is_closed is True - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - v1 = await response.parse() - assert_matches_type(V1GenerateCreateResponse, v1, path=["response"]) - - @parametrize - async def test_streaming_response_generate_create(self, async_client: AsyncUnlayer) -> None: - async with async_client.documents.v1.with_streaming_response.generate_create( - design={ - "counters": "bar", - "body": "bar", - }, - ) as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - - v1 = await response.parse() - assert_matches_type(V1GenerateCreateResponse, v1, path=["response"]) - - assert cast(Any, response.is_closed) is True - - @parametrize - async def test_method_generate_template_template(self, async_client: AsyncUnlayer) -> None: - v1 = await async_client.documents.v1.generate_template_template( - template_id="templateId", - ) - assert_matches_type(V1GenerateTemplateTemplateResponse, v1, path=["response"]) - - @parametrize - async def test_method_generate_template_template_with_all_params(self, async_client: AsyncUnlayer) -> None: - v1 = await async_client.documents.v1.generate_template_template( - template_id="templateId", - filename="filename", - merge_tags={"foo": "string"}, - ) - assert_matches_type(V1GenerateTemplateTemplateResponse, v1, path=["response"]) - - @parametrize - async def test_raw_response_generate_template_template(self, async_client: AsyncUnlayer) -> None: - response = await async_client.documents.v1.with_raw_response.generate_template_template( - template_id="templateId", - ) - - assert response.is_closed is True - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - v1 = await response.parse() - assert_matches_type(V1GenerateTemplateTemplateResponse, v1, path=["response"]) - - @parametrize - async def test_streaming_response_generate_template_template(self, async_client: AsyncUnlayer) -> None: - async with async_client.documents.v1.with_streaming_response.generate_template_template( - template_id="templateId", - ) as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - - v1 = await response.parse() - assert_matches_type(V1GenerateTemplateTemplateResponse, v1, path=["response"]) - - assert cast(Any, response.is_closed) is True diff --git a/tests/api_resources/emails/__init__.py b/tests/api_resources/emails/__init__.py deleted file mode 100644 index fd8019a..0000000 --- a/tests/api_resources/emails/__init__.py +++ /dev/null @@ -1 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. diff --git a/tests/api_resources/emails/test_v1.py b/tests/api_resources/emails/test_v1.py deleted file mode 100644 index c1da30c..0000000 --- a/tests/api_resources/emails/test_v1.py +++ /dev/null @@ -1,409 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -import os -from typing import Any, cast - -import pytest - -from unlayer import Unlayer, AsyncUnlayer -from tests.utils import assert_matches_type -from unlayer.types.emails import ( - V1RetrieveResponse, - V1SendCreateResponse, - V1RenderCreateResponse, - V1SendTemplateTemplateResponse, -) - -base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") - - -class TestV1: - parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) - - @parametrize - def test_method_retrieve(self, client: Unlayer) -> None: - v1 = client.emails.v1.retrieve( - "id", - ) - assert_matches_type(V1RetrieveResponse, v1, path=["response"]) - - @parametrize - def test_raw_response_retrieve(self, client: Unlayer) -> None: - response = client.emails.v1.with_raw_response.retrieve( - "id", - ) - - assert response.is_closed is True - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - v1 = response.parse() - assert_matches_type(V1RetrieveResponse, v1, path=["response"]) - - @parametrize - def test_streaming_response_retrieve(self, client: Unlayer) -> None: - with client.emails.v1.with_streaming_response.retrieve( - "id", - ) as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - - v1 = response.parse() - assert_matches_type(V1RetrieveResponse, v1, path=["response"]) - - assert cast(Any, response.is_closed) is True - - @parametrize - def test_path_params_retrieve(self, client: Unlayer) -> None: - with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): - client.emails.v1.with_raw_response.retrieve( - "", - ) - - @parametrize - def test_method_render_create(self, client: Unlayer) -> None: - v1 = client.emails.v1.render_create( - design={ - "counters": "bar", - "body": "bar", - }, - ) - assert_matches_type(V1RenderCreateResponse, v1, path=["response"]) - - @parametrize - def test_method_render_create_with_all_params(self, client: Unlayer) -> None: - v1 = client.emails.v1.render_create( - design={ - "counters": "bar", - "body": "bar", - }, - merge_tags={"foo": "string"}, - ) - assert_matches_type(V1RenderCreateResponse, v1, path=["response"]) - - @parametrize - def test_raw_response_render_create(self, client: Unlayer) -> None: - response = client.emails.v1.with_raw_response.render_create( - design={ - "counters": "bar", - "body": "bar", - }, - ) - - assert response.is_closed is True - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - v1 = response.parse() - assert_matches_type(V1RenderCreateResponse, v1, path=["response"]) - - @parametrize - def test_streaming_response_render_create(self, client: Unlayer) -> None: - with client.emails.v1.with_streaming_response.render_create( - design={ - "counters": "bar", - "body": "bar", - }, - ) as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - - v1 = response.parse() - assert_matches_type(V1RenderCreateResponse, v1, path=["response"]) - - assert cast(Any, response.is_closed) is True - - @parametrize - def test_method_send_create(self, client: Unlayer) -> None: - v1 = client.emails.v1.send_create( - design={ - "counters": "bar", - "body": "bar", - }, - to="test@example.com", - ) - assert_matches_type(V1SendCreateResponse, v1, path=["response"]) - - @parametrize - def test_method_send_create_with_all_params(self, client: Unlayer) -> None: - v1 = client.emails.v1.send_create( - design={ - "counters": "bar", - "body": "bar", - }, - to="test@example.com", - html="html", - merge_tags={"foo": "string"}, - subject="Test", - ) - assert_matches_type(V1SendCreateResponse, v1, path=["response"]) - - @parametrize - def test_raw_response_send_create(self, client: Unlayer) -> None: - response = client.emails.v1.with_raw_response.send_create( - design={ - "counters": "bar", - "body": "bar", - }, - to="test@example.com", - ) - - assert response.is_closed is True - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - v1 = response.parse() - assert_matches_type(V1SendCreateResponse, v1, path=["response"]) - - @parametrize - def test_streaming_response_send_create(self, client: Unlayer) -> None: - with client.emails.v1.with_streaming_response.send_create( - design={ - "counters": "bar", - "body": "bar", - }, - to="test@example.com", - ) as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - - v1 = response.parse() - assert_matches_type(V1SendCreateResponse, v1, path=["response"]) - - assert cast(Any, response.is_closed) is True - - @parametrize - def test_method_send_template_template(self, client: Unlayer) -> None: - v1 = client.emails.v1.send_template_template( - template_id="templateId", - to="dev@stainless.com", - ) - assert_matches_type(V1SendTemplateTemplateResponse, v1, path=["response"]) - - @parametrize - def test_method_send_template_template_with_all_params(self, client: Unlayer) -> None: - v1 = client.emails.v1.send_template_template( - template_id="templateId", - to="dev@stainless.com", - merge_tags={"foo": "string"}, - subject="subject", - ) - assert_matches_type(V1SendTemplateTemplateResponse, v1, path=["response"]) - - @parametrize - def test_raw_response_send_template_template(self, client: Unlayer) -> None: - response = client.emails.v1.with_raw_response.send_template_template( - template_id="templateId", - to="dev@stainless.com", - ) - - assert response.is_closed is True - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - v1 = response.parse() - assert_matches_type(V1SendTemplateTemplateResponse, v1, path=["response"]) - - @parametrize - def test_streaming_response_send_template_template(self, client: Unlayer) -> None: - with client.emails.v1.with_streaming_response.send_template_template( - template_id="templateId", - to="dev@stainless.com", - ) as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - - v1 = response.parse() - assert_matches_type(V1SendTemplateTemplateResponse, v1, path=["response"]) - - assert cast(Any, response.is_closed) is True - - -class TestAsyncV1: - parametrize = pytest.mark.parametrize( - "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] - ) - - @parametrize - async def test_method_retrieve(self, async_client: AsyncUnlayer) -> None: - v1 = await async_client.emails.v1.retrieve( - "id", - ) - assert_matches_type(V1RetrieveResponse, v1, path=["response"]) - - @parametrize - async def test_raw_response_retrieve(self, async_client: AsyncUnlayer) -> None: - response = await async_client.emails.v1.with_raw_response.retrieve( - "id", - ) - - assert response.is_closed is True - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - v1 = await response.parse() - assert_matches_type(V1RetrieveResponse, v1, path=["response"]) - - @parametrize - async def test_streaming_response_retrieve(self, async_client: AsyncUnlayer) -> None: - async with async_client.emails.v1.with_streaming_response.retrieve( - "id", - ) as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - - v1 = await response.parse() - assert_matches_type(V1RetrieveResponse, v1, path=["response"]) - - assert cast(Any, response.is_closed) is True - - @parametrize - async def test_path_params_retrieve(self, async_client: AsyncUnlayer) -> None: - with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): - await async_client.emails.v1.with_raw_response.retrieve( - "", - ) - - @parametrize - async def test_method_render_create(self, async_client: AsyncUnlayer) -> None: - v1 = await async_client.emails.v1.render_create( - design={ - "counters": "bar", - "body": "bar", - }, - ) - assert_matches_type(V1RenderCreateResponse, v1, path=["response"]) - - @parametrize - async def test_method_render_create_with_all_params(self, async_client: AsyncUnlayer) -> None: - v1 = await async_client.emails.v1.render_create( - design={ - "counters": "bar", - "body": "bar", - }, - merge_tags={"foo": "string"}, - ) - assert_matches_type(V1RenderCreateResponse, v1, path=["response"]) - - @parametrize - async def test_raw_response_render_create(self, async_client: AsyncUnlayer) -> None: - response = await async_client.emails.v1.with_raw_response.render_create( - design={ - "counters": "bar", - "body": "bar", - }, - ) - - assert response.is_closed is True - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - v1 = await response.parse() - assert_matches_type(V1RenderCreateResponse, v1, path=["response"]) - - @parametrize - async def test_streaming_response_render_create(self, async_client: AsyncUnlayer) -> None: - async with async_client.emails.v1.with_streaming_response.render_create( - design={ - "counters": "bar", - "body": "bar", - }, - ) as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - - v1 = await response.parse() - assert_matches_type(V1RenderCreateResponse, v1, path=["response"]) - - assert cast(Any, response.is_closed) is True - - @parametrize - async def test_method_send_create(self, async_client: AsyncUnlayer) -> None: - v1 = await async_client.emails.v1.send_create( - design={ - "counters": "bar", - "body": "bar", - }, - to="test@example.com", - ) - assert_matches_type(V1SendCreateResponse, v1, path=["response"]) - - @parametrize - async def test_method_send_create_with_all_params(self, async_client: AsyncUnlayer) -> None: - v1 = await async_client.emails.v1.send_create( - design={ - "counters": "bar", - "body": "bar", - }, - to="test@example.com", - html="html", - merge_tags={"foo": "string"}, - subject="Test", - ) - assert_matches_type(V1SendCreateResponse, v1, path=["response"]) - - @parametrize - async def test_raw_response_send_create(self, async_client: AsyncUnlayer) -> None: - response = await async_client.emails.v1.with_raw_response.send_create( - design={ - "counters": "bar", - "body": "bar", - }, - to="test@example.com", - ) - - assert response.is_closed is True - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - v1 = await response.parse() - assert_matches_type(V1SendCreateResponse, v1, path=["response"]) - - @parametrize - async def test_streaming_response_send_create(self, async_client: AsyncUnlayer) -> None: - async with async_client.emails.v1.with_streaming_response.send_create( - design={ - "counters": "bar", - "body": "bar", - }, - to="test@example.com", - ) as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - - v1 = await response.parse() - assert_matches_type(V1SendCreateResponse, v1, path=["response"]) - - assert cast(Any, response.is_closed) is True - - @parametrize - async def test_method_send_template_template(self, async_client: AsyncUnlayer) -> None: - v1 = await async_client.emails.v1.send_template_template( - template_id="templateId", - to="dev@stainless.com", - ) - assert_matches_type(V1SendTemplateTemplateResponse, v1, path=["response"]) - - @parametrize - async def test_method_send_template_template_with_all_params(self, async_client: AsyncUnlayer) -> None: - v1 = await async_client.emails.v1.send_template_template( - template_id="templateId", - to="dev@stainless.com", - merge_tags={"foo": "string"}, - subject="subject", - ) - assert_matches_type(V1SendTemplateTemplateResponse, v1, path=["response"]) - - @parametrize - async def test_raw_response_send_template_template(self, async_client: AsyncUnlayer) -> None: - response = await async_client.emails.v1.with_raw_response.send_template_template( - template_id="templateId", - to="dev@stainless.com", - ) - - assert response.is_closed is True - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - v1 = await response.parse() - assert_matches_type(V1SendTemplateTemplateResponse, v1, path=["response"]) - - @parametrize - async def test_streaming_response_send_template_template(self, async_client: AsyncUnlayer) -> None: - async with async_client.emails.v1.with_streaming_response.send_template_template( - template_id="templateId", - to="dev@stainless.com", - ) as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - - v1 = await response.parse() - assert_matches_type(V1SendTemplateTemplateResponse, v1, path=["response"]) - - assert cast(Any, response.is_closed) is True diff --git a/tests/api_resources/pages/__init__.py b/tests/api_resources/pages/__init__.py deleted file mode 100644 index fd8019a..0000000 --- a/tests/api_resources/pages/__init__.py +++ /dev/null @@ -1 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. diff --git a/tests/api_resources/pages/test_v1.py b/tests/api_resources/pages/test_v1.py deleted file mode 100644 index fd27b6c..0000000 --- a/tests/api_resources/pages/test_v1.py +++ /dev/null @@ -1,126 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -import os -from typing import Any, cast - -import pytest - -from unlayer import Unlayer, AsyncUnlayer -from tests.utils import assert_matches_type -from unlayer.types.pages import V1RenderCreateResponse - -base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") - - -class TestV1: - parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) - - @parametrize - def test_method_render_create(self, client: Unlayer) -> None: - v1 = client.pages.v1.render_create( - design={ - "counters": "bar", - "body": "bar", - }, - ) - assert_matches_type(V1RenderCreateResponse, v1, path=["response"]) - - @parametrize - def test_method_render_create_with_all_params(self, client: Unlayer) -> None: - v1 = client.pages.v1.render_create( - design={ - "counters": "bar", - "body": "bar", - }, - merge_tags={"foo": "string"}, - ) - assert_matches_type(V1RenderCreateResponse, v1, path=["response"]) - - @parametrize - def test_raw_response_render_create(self, client: Unlayer) -> None: - response = client.pages.v1.with_raw_response.render_create( - design={ - "counters": "bar", - "body": "bar", - }, - ) - - assert response.is_closed is True - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - v1 = response.parse() - assert_matches_type(V1RenderCreateResponse, v1, path=["response"]) - - @parametrize - def test_streaming_response_render_create(self, client: Unlayer) -> None: - with client.pages.v1.with_streaming_response.render_create( - design={ - "counters": "bar", - "body": "bar", - }, - ) as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - - v1 = response.parse() - assert_matches_type(V1RenderCreateResponse, v1, path=["response"]) - - assert cast(Any, response.is_closed) is True - - -class TestAsyncV1: - parametrize = pytest.mark.parametrize( - "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] - ) - - @parametrize - async def test_method_render_create(self, async_client: AsyncUnlayer) -> None: - v1 = await async_client.pages.v1.render_create( - design={ - "counters": "bar", - "body": "bar", - }, - ) - assert_matches_type(V1RenderCreateResponse, v1, path=["response"]) - - @parametrize - async def test_method_render_create_with_all_params(self, async_client: AsyncUnlayer) -> None: - v1 = await async_client.pages.v1.render_create( - design={ - "counters": "bar", - "body": "bar", - }, - merge_tags={"foo": "string"}, - ) - assert_matches_type(V1RenderCreateResponse, v1, path=["response"]) - - @parametrize - async def test_raw_response_render_create(self, async_client: AsyncUnlayer) -> None: - response = await async_client.pages.v1.with_raw_response.render_create( - design={ - "counters": "bar", - "body": "bar", - }, - ) - - assert response.is_closed is True - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - v1 = await response.parse() - assert_matches_type(V1RenderCreateResponse, v1, path=["response"]) - - @parametrize - async def test_streaming_response_render_create(self, async_client: AsyncUnlayer) -> None: - async with async_client.pages.v1.with_streaming_response.render_create( - design={ - "counters": "bar", - "body": "bar", - }, - ) as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - - v1 = await response.parse() - assert_matches_type(V1RenderCreateResponse, v1, path=["response"]) - - assert cast(Any, response.is_closed) is True diff --git a/tests/api_resources/project/__init__.py b/tests/api_resources/project/__init__.py deleted file mode 100644 index fd8019a..0000000 --- a/tests/api_resources/project/__init__.py +++ /dev/null @@ -1 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. diff --git a/tests/api_resources/project/test_v1.py b/tests/api_resources/project/test_v1.py deleted file mode 100644 index 0cc89d2..0000000 --- a/tests/api_resources/project/test_v1.py +++ /dev/null @@ -1,1198 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -import os -from typing import Any, cast - -import pytest - -from unlayer import Unlayer, AsyncUnlayer -from tests.utils import assert_matches_type -from unlayer.types.project import ( - V1APIKeysListResponse, - V1CurrentListResponse, - V1DomainsListResponse, - V1APIKeysCreateResponse, - V1APIKeysUpdateResponse, - V1DomainsCreateResponse, - V1DomainsUpdateResponse, - V1TemplatesListResponse, - V1APIKeysRetrieveResponse, - V1DomainsRetrieveResponse, - V1TemplatesCreateResponse, - V1TemplatesUpdateResponse, - V1TemplatesRetrieveResponse, -) - -base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") - - -class TestV1: - parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) - - @parametrize - def test_method_api_keys_create(self, client: Unlayer) -> None: - v1 = client.project.v1.api_keys_create( - name="name", - ) - assert_matches_type(V1APIKeysCreateResponse, v1, path=["response"]) - - @parametrize - def test_method_api_keys_create_with_all_params(self, client: Unlayer) -> None: - v1 = client.project.v1.api_keys_create( - name="name", - domains=["string"], - ) - assert_matches_type(V1APIKeysCreateResponse, v1, path=["response"]) - - @parametrize - def test_raw_response_api_keys_create(self, client: Unlayer) -> None: - response = client.project.v1.with_raw_response.api_keys_create( - name="name", - ) - - assert response.is_closed is True - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - v1 = response.parse() - assert_matches_type(V1APIKeysCreateResponse, v1, path=["response"]) - - @parametrize - def test_streaming_response_api_keys_create(self, client: Unlayer) -> None: - with client.project.v1.with_streaming_response.api_keys_create( - name="name", - ) as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - - v1 = response.parse() - assert_matches_type(V1APIKeysCreateResponse, v1, path=["response"]) - - assert cast(Any, response.is_closed) is True - - @parametrize - def test_method_api_keys_delete(self, client: Unlayer) -> None: - v1 = client.project.v1.api_keys_delete( - "id", - ) - assert v1 is None - - @parametrize - def test_raw_response_api_keys_delete(self, client: Unlayer) -> None: - response = client.project.v1.with_raw_response.api_keys_delete( - "id", - ) - - assert response.is_closed is True - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - v1 = response.parse() - assert v1 is None - - @parametrize - def test_streaming_response_api_keys_delete(self, client: Unlayer) -> None: - with client.project.v1.with_streaming_response.api_keys_delete( - "id", - ) as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - - v1 = response.parse() - assert v1 is None - - assert cast(Any, response.is_closed) is True - - @parametrize - def test_path_params_api_keys_delete(self, client: Unlayer) -> None: - with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): - client.project.v1.with_raw_response.api_keys_delete( - "", - ) - - @parametrize - def test_method_api_keys_list(self, client: Unlayer) -> None: - v1 = client.project.v1.api_keys_list() - assert_matches_type(V1APIKeysListResponse, v1, path=["response"]) - - @parametrize - def test_raw_response_api_keys_list(self, client: Unlayer) -> None: - response = client.project.v1.with_raw_response.api_keys_list() - - assert response.is_closed is True - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - v1 = response.parse() - assert_matches_type(V1APIKeysListResponse, v1, path=["response"]) - - @parametrize - def test_streaming_response_api_keys_list(self, client: Unlayer) -> None: - with client.project.v1.with_streaming_response.api_keys_list() as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - - v1 = response.parse() - assert_matches_type(V1APIKeysListResponse, v1, path=["response"]) - - assert cast(Any, response.is_closed) is True - - @parametrize - def test_method_api_keys_retrieve(self, client: Unlayer) -> None: - v1 = client.project.v1.api_keys_retrieve( - "id", - ) - assert_matches_type(V1APIKeysRetrieveResponse, v1, path=["response"]) - - @parametrize - def test_raw_response_api_keys_retrieve(self, client: Unlayer) -> None: - response = client.project.v1.with_raw_response.api_keys_retrieve( - "id", - ) - - assert response.is_closed is True - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - v1 = response.parse() - assert_matches_type(V1APIKeysRetrieveResponse, v1, path=["response"]) - - @parametrize - def test_streaming_response_api_keys_retrieve(self, client: Unlayer) -> None: - with client.project.v1.with_streaming_response.api_keys_retrieve( - "id", - ) as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - - v1 = response.parse() - assert_matches_type(V1APIKeysRetrieveResponse, v1, path=["response"]) - - assert cast(Any, response.is_closed) is True - - @parametrize - def test_path_params_api_keys_retrieve(self, client: Unlayer) -> None: - with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): - client.project.v1.with_raw_response.api_keys_retrieve( - "", - ) - - @parametrize - def test_method_api_keys_update(self, client: Unlayer) -> None: - v1 = client.project.v1.api_keys_update( - id="id", - ) - assert_matches_type(V1APIKeysUpdateResponse, v1, path=["response"]) - - @parametrize - def test_method_api_keys_update_with_all_params(self, client: Unlayer) -> None: - v1 = client.project.v1.api_keys_update( - id="id", - active=True, - domains=["string"], - name="name", - ) - assert_matches_type(V1APIKeysUpdateResponse, v1, path=["response"]) - - @parametrize - def test_raw_response_api_keys_update(self, client: Unlayer) -> None: - response = client.project.v1.with_raw_response.api_keys_update( - id="id", - ) - - assert response.is_closed is True - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - v1 = response.parse() - assert_matches_type(V1APIKeysUpdateResponse, v1, path=["response"]) - - @parametrize - def test_streaming_response_api_keys_update(self, client: Unlayer) -> None: - with client.project.v1.with_streaming_response.api_keys_update( - id="id", - ) as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - - v1 = response.parse() - assert_matches_type(V1APIKeysUpdateResponse, v1, path=["response"]) - - assert cast(Any, response.is_closed) is True - - @parametrize - def test_path_params_api_keys_update(self, client: Unlayer) -> None: - with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): - client.project.v1.with_raw_response.api_keys_update( - id="", - ) - - @parametrize - def test_method_current_list(self, client: Unlayer) -> None: - v1 = client.project.v1.current_list() - assert_matches_type(V1CurrentListResponse, v1, path=["response"]) - - @parametrize - def test_raw_response_current_list(self, client: Unlayer) -> None: - response = client.project.v1.with_raw_response.current_list() - - assert response.is_closed is True - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - v1 = response.parse() - assert_matches_type(V1CurrentListResponse, v1, path=["response"]) - - @parametrize - def test_streaming_response_current_list(self, client: Unlayer) -> None: - with client.project.v1.with_streaming_response.current_list() as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - - v1 = response.parse() - assert_matches_type(V1CurrentListResponse, v1, path=["response"]) - - assert cast(Any, response.is_closed) is True - - @parametrize - def test_method_domains_create(self, client: Unlayer) -> None: - v1 = client.project.v1.domains_create( - domain="domain", - ) - assert_matches_type(V1DomainsCreateResponse, v1, path=["response"]) - - @parametrize - def test_raw_response_domains_create(self, client: Unlayer) -> None: - response = client.project.v1.with_raw_response.domains_create( - domain="domain", - ) - - assert response.is_closed is True - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - v1 = response.parse() - assert_matches_type(V1DomainsCreateResponse, v1, path=["response"]) - - @parametrize - def test_streaming_response_domains_create(self, client: Unlayer) -> None: - with client.project.v1.with_streaming_response.domains_create( - domain="domain", - ) as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - - v1 = response.parse() - assert_matches_type(V1DomainsCreateResponse, v1, path=["response"]) - - assert cast(Any, response.is_closed) is True - - @parametrize - def test_method_domains_delete(self, client: Unlayer) -> None: - v1 = client.project.v1.domains_delete( - "id", - ) - assert v1 is None - - @parametrize - def test_raw_response_domains_delete(self, client: Unlayer) -> None: - response = client.project.v1.with_raw_response.domains_delete( - "id", - ) - - assert response.is_closed is True - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - v1 = response.parse() - assert v1 is None - - @parametrize - def test_streaming_response_domains_delete(self, client: Unlayer) -> None: - with client.project.v1.with_streaming_response.domains_delete( - "id", - ) as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - - v1 = response.parse() - assert v1 is None - - assert cast(Any, response.is_closed) is True - - @parametrize - def test_path_params_domains_delete(self, client: Unlayer) -> None: - with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): - client.project.v1.with_raw_response.domains_delete( - "", - ) - - @parametrize - def test_method_domains_list(self, client: Unlayer) -> None: - v1 = client.project.v1.domains_list() - assert_matches_type(V1DomainsListResponse, v1, path=["response"]) - - @parametrize - def test_raw_response_domains_list(self, client: Unlayer) -> None: - response = client.project.v1.with_raw_response.domains_list() - - assert response.is_closed is True - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - v1 = response.parse() - assert_matches_type(V1DomainsListResponse, v1, path=["response"]) - - @parametrize - def test_streaming_response_domains_list(self, client: Unlayer) -> None: - with client.project.v1.with_streaming_response.domains_list() as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - - v1 = response.parse() - assert_matches_type(V1DomainsListResponse, v1, path=["response"]) - - assert cast(Any, response.is_closed) is True - - @parametrize - def test_method_domains_retrieve(self, client: Unlayer) -> None: - v1 = client.project.v1.domains_retrieve( - "id", - ) - assert_matches_type(V1DomainsRetrieveResponse, v1, path=["response"]) - - @parametrize - def test_raw_response_domains_retrieve(self, client: Unlayer) -> None: - response = client.project.v1.with_raw_response.domains_retrieve( - "id", - ) - - assert response.is_closed is True - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - v1 = response.parse() - assert_matches_type(V1DomainsRetrieveResponse, v1, path=["response"]) - - @parametrize - def test_streaming_response_domains_retrieve(self, client: Unlayer) -> None: - with client.project.v1.with_streaming_response.domains_retrieve( - "id", - ) as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - - v1 = response.parse() - assert_matches_type(V1DomainsRetrieveResponse, v1, path=["response"]) - - assert cast(Any, response.is_closed) is True - - @parametrize - def test_path_params_domains_retrieve(self, client: Unlayer) -> None: - with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): - client.project.v1.with_raw_response.domains_retrieve( - "", - ) - - @parametrize - def test_method_domains_update(self, client: Unlayer) -> None: - v1 = client.project.v1.domains_update( - id="id", - ) - assert_matches_type(V1DomainsUpdateResponse, v1, path=["response"]) - - @parametrize - def test_method_domains_update_with_all_params(self, client: Unlayer) -> None: - v1 = client.project.v1.domains_update( - id="id", - domain="domain", - ) - assert_matches_type(V1DomainsUpdateResponse, v1, path=["response"]) - - @parametrize - def test_raw_response_domains_update(self, client: Unlayer) -> None: - response = client.project.v1.with_raw_response.domains_update( - id="id", - ) - - assert response.is_closed is True - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - v1 = response.parse() - assert_matches_type(V1DomainsUpdateResponse, v1, path=["response"]) - - @parametrize - def test_streaming_response_domains_update(self, client: Unlayer) -> None: - with client.project.v1.with_streaming_response.domains_update( - id="id", - ) as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - - v1 = response.parse() - assert_matches_type(V1DomainsUpdateResponse, v1, path=["response"]) - - assert cast(Any, response.is_closed) is True - - @parametrize - def test_path_params_domains_update(self, client: Unlayer) -> None: - with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): - client.project.v1.with_raw_response.domains_update( - id="", - ) - - @parametrize - def test_method_templates_create(self, client: Unlayer) -> None: - v1 = client.project.v1.templates_create( - name="name", - ) - assert_matches_type(V1TemplatesCreateResponse, v1, path=["response"]) - - @parametrize - def test_method_templates_create_with_all_params(self, client: Unlayer) -> None: - v1 = client.project.v1.templates_create( - name="name", - body="body", - subject="subject", - ) - assert_matches_type(V1TemplatesCreateResponse, v1, path=["response"]) - - @parametrize - def test_raw_response_templates_create(self, client: Unlayer) -> None: - response = client.project.v1.with_raw_response.templates_create( - name="name", - ) - - assert response.is_closed is True - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - v1 = response.parse() - assert_matches_type(V1TemplatesCreateResponse, v1, path=["response"]) - - @parametrize - def test_streaming_response_templates_create(self, client: Unlayer) -> None: - with client.project.v1.with_streaming_response.templates_create( - name="name", - ) as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - - v1 = response.parse() - assert_matches_type(V1TemplatesCreateResponse, v1, path=["response"]) - - assert cast(Any, response.is_closed) is True - - @parametrize - def test_method_templates_delete(self, client: Unlayer) -> None: - v1 = client.project.v1.templates_delete( - "id", - ) - assert v1 is None - - @parametrize - def test_raw_response_templates_delete(self, client: Unlayer) -> None: - response = client.project.v1.with_raw_response.templates_delete( - "id", - ) - - assert response.is_closed is True - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - v1 = response.parse() - assert v1 is None - - @parametrize - def test_streaming_response_templates_delete(self, client: Unlayer) -> None: - with client.project.v1.with_streaming_response.templates_delete( - "id", - ) as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - - v1 = response.parse() - assert v1 is None - - assert cast(Any, response.is_closed) is True - - @parametrize - def test_path_params_templates_delete(self, client: Unlayer) -> None: - with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): - client.project.v1.with_raw_response.templates_delete( - "", - ) - - @parametrize - def test_method_templates_list(self, client: Unlayer) -> None: - v1 = client.project.v1.templates_list() - assert_matches_type(V1TemplatesListResponse, v1, path=["response"]) - - @parametrize - def test_raw_response_templates_list(self, client: Unlayer) -> None: - response = client.project.v1.with_raw_response.templates_list() - - assert response.is_closed is True - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - v1 = response.parse() - assert_matches_type(V1TemplatesListResponse, v1, path=["response"]) - - @parametrize - def test_streaming_response_templates_list(self, client: Unlayer) -> None: - with client.project.v1.with_streaming_response.templates_list() as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - - v1 = response.parse() - assert_matches_type(V1TemplatesListResponse, v1, path=["response"]) - - assert cast(Any, response.is_closed) is True - - @parametrize - def test_method_templates_retrieve(self, client: Unlayer) -> None: - v1 = client.project.v1.templates_retrieve( - "id", - ) - assert_matches_type(V1TemplatesRetrieveResponse, v1, path=["response"]) - - @parametrize - def test_raw_response_templates_retrieve(self, client: Unlayer) -> None: - response = client.project.v1.with_raw_response.templates_retrieve( - "id", - ) - - assert response.is_closed is True - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - v1 = response.parse() - assert_matches_type(V1TemplatesRetrieveResponse, v1, path=["response"]) - - @parametrize - def test_streaming_response_templates_retrieve(self, client: Unlayer) -> None: - with client.project.v1.with_streaming_response.templates_retrieve( - "id", - ) as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - - v1 = response.parse() - assert_matches_type(V1TemplatesRetrieveResponse, v1, path=["response"]) - - assert cast(Any, response.is_closed) is True - - @parametrize - def test_path_params_templates_retrieve(self, client: Unlayer) -> None: - with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): - client.project.v1.with_raw_response.templates_retrieve( - "", - ) - - @parametrize - def test_method_templates_update(self, client: Unlayer) -> None: - v1 = client.project.v1.templates_update( - id="id", - ) - assert_matches_type(V1TemplatesUpdateResponse, v1, path=["response"]) - - @parametrize - def test_method_templates_update_with_all_params(self, client: Unlayer) -> None: - v1 = client.project.v1.templates_update( - id="id", - body="body", - name="name", - subject="subject", - ) - assert_matches_type(V1TemplatesUpdateResponse, v1, path=["response"]) - - @parametrize - def test_raw_response_templates_update(self, client: Unlayer) -> None: - response = client.project.v1.with_raw_response.templates_update( - id="id", - ) - - assert response.is_closed is True - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - v1 = response.parse() - assert_matches_type(V1TemplatesUpdateResponse, v1, path=["response"]) - - @parametrize - def test_streaming_response_templates_update(self, client: Unlayer) -> None: - with client.project.v1.with_streaming_response.templates_update( - id="id", - ) as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - - v1 = response.parse() - assert_matches_type(V1TemplatesUpdateResponse, v1, path=["response"]) - - assert cast(Any, response.is_closed) is True - - @parametrize - def test_path_params_templates_update(self, client: Unlayer) -> None: - with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): - client.project.v1.with_raw_response.templates_update( - id="", - ) - - -class TestAsyncV1: - parametrize = pytest.mark.parametrize( - "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] - ) - - @parametrize - async def test_method_api_keys_create(self, async_client: AsyncUnlayer) -> None: - v1 = await async_client.project.v1.api_keys_create( - name="name", - ) - assert_matches_type(V1APIKeysCreateResponse, v1, path=["response"]) - - @parametrize - async def test_method_api_keys_create_with_all_params(self, async_client: AsyncUnlayer) -> None: - v1 = await async_client.project.v1.api_keys_create( - name="name", - domains=["string"], - ) - assert_matches_type(V1APIKeysCreateResponse, v1, path=["response"]) - - @parametrize - async def test_raw_response_api_keys_create(self, async_client: AsyncUnlayer) -> None: - response = await async_client.project.v1.with_raw_response.api_keys_create( - name="name", - ) - - assert response.is_closed is True - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - v1 = await response.parse() - assert_matches_type(V1APIKeysCreateResponse, v1, path=["response"]) - - @parametrize - async def test_streaming_response_api_keys_create(self, async_client: AsyncUnlayer) -> None: - async with async_client.project.v1.with_streaming_response.api_keys_create( - name="name", - ) as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - - v1 = await response.parse() - assert_matches_type(V1APIKeysCreateResponse, v1, path=["response"]) - - assert cast(Any, response.is_closed) is True - - @parametrize - async def test_method_api_keys_delete(self, async_client: AsyncUnlayer) -> None: - v1 = await async_client.project.v1.api_keys_delete( - "id", - ) - assert v1 is None - - @parametrize - async def test_raw_response_api_keys_delete(self, async_client: AsyncUnlayer) -> None: - response = await async_client.project.v1.with_raw_response.api_keys_delete( - "id", - ) - - assert response.is_closed is True - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - v1 = await response.parse() - assert v1 is None - - @parametrize - async def test_streaming_response_api_keys_delete(self, async_client: AsyncUnlayer) -> None: - async with async_client.project.v1.with_streaming_response.api_keys_delete( - "id", - ) as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - - v1 = await response.parse() - assert v1 is None - - assert cast(Any, response.is_closed) is True - - @parametrize - async def test_path_params_api_keys_delete(self, async_client: AsyncUnlayer) -> None: - with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): - await async_client.project.v1.with_raw_response.api_keys_delete( - "", - ) - - @parametrize - async def test_method_api_keys_list(self, async_client: AsyncUnlayer) -> None: - v1 = await async_client.project.v1.api_keys_list() - assert_matches_type(V1APIKeysListResponse, v1, path=["response"]) - - @parametrize - async def test_raw_response_api_keys_list(self, async_client: AsyncUnlayer) -> None: - response = await async_client.project.v1.with_raw_response.api_keys_list() - - assert response.is_closed is True - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - v1 = await response.parse() - assert_matches_type(V1APIKeysListResponse, v1, path=["response"]) - - @parametrize - async def test_streaming_response_api_keys_list(self, async_client: AsyncUnlayer) -> None: - async with async_client.project.v1.with_streaming_response.api_keys_list() as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - - v1 = await response.parse() - assert_matches_type(V1APIKeysListResponse, v1, path=["response"]) - - assert cast(Any, response.is_closed) is True - - @parametrize - async def test_method_api_keys_retrieve(self, async_client: AsyncUnlayer) -> None: - v1 = await async_client.project.v1.api_keys_retrieve( - "id", - ) - assert_matches_type(V1APIKeysRetrieveResponse, v1, path=["response"]) - - @parametrize - async def test_raw_response_api_keys_retrieve(self, async_client: AsyncUnlayer) -> None: - response = await async_client.project.v1.with_raw_response.api_keys_retrieve( - "id", - ) - - assert response.is_closed is True - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - v1 = await response.parse() - assert_matches_type(V1APIKeysRetrieveResponse, v1, path=["response"]) - - @parametrize - async def test_streaming_response_api_keys_retrieve(self, async_client: AsyncUnlayer) -> None: - async with async_client.project.v1.with_streaming_response.api_keys_retrieve( - "id", - ) as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - - v1 = await response.parse() - assert_matches_type(V1APIKeysRetrieveResponse, v1, path=["response"]) - - assert cast(Any, response.is_closed) is True - - @parametrize - async def test_path_params_api_keys_retrieve(self, async_client: AsyncUnlayer) -> None: - with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): - await async_client.project.v1.with_raw_response.api_keys_retrieve( - "", - ) - - @parametrize - async def test_method_api_keys_update(self, async_client: AsyncUnlayer) -> None: - v1 = await async_client.project.v1.api_keys_update( - id="id", - ) - assert_matches_type(V1APIKeysUpdateResponse, v1, path=["response"]) - - @parametrize - async def test_method_api_keys_update_with_all_params(self, async_client: AsyncUnlayer) -> None: - v1 = await async_client.project.v1.api_keys_update( - id="id", - active=True, - domains=["string"], - name="name", - ) - assert_matches_type(V1APIKeysUpdateResponse, v1, path=["response"]) - - @parametrize - async def test_raw_response_api_keys_update(self, async_client: AsyncUnlayer) -> None: - response = await async_client.project.v1.with_raw_response.api_keys_update( - id="id", - ) - - assert response.is_closed is True - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - v1 = await response.parse() - assert_matches_type(V1APIKeysUpdateResponse, v1, path=["response"]) - - @parametrize - async def test_streaming_response_api_keys_update(self, async_client: AsyncUnlayer) -> None: - async with async_client.project.v1.with_streaming_response.api_keys_update( - id="id", - ) as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - - v1 = await response.parse() - assert_matches_type(V1APIKeysUpdateResponse, v1, path=["response"]) - - assert cast(Any, response.is_closed) is True - - @parametrize - async def test_path_params_api_keys_update(self, async_client: AsyncUnlayer) -> None: - with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): - await async_client.project.v1.with_raw_response.api_keys_update( - id="", - ) - - @parametrize - async def test_method_current_list(self, async_client: AsyncUnlayer) -> None: - v1 = await async_client.project.v1.current_list() - assert_matches_type(V1CurrentListResponse, v1, path=["response"]) - - @parametrize - async def test_raw_response_current_list(self, async_client: AsyncUnlayer) -> None: - response = await async_client.project.v1.with_raw_response.current_list() - - assert response.is_closed is True - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - v1 = await response.parse() - assert_matches_type(V1CurrentListResponse, v1, path=["response"]) - - @parametrize - async def test_streaming_response_current_list(self, async_client: AsyncUnlayer) -> None: - async with async_client.project.v1.with_streaming_response.current_list() as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - - v1 = await response.parse() - assert_matches_type(V1CurrentListResponse, v1, path=["response"]) - - assert cast(Any, response.is_closed) is True - - @parametrize - async def test_method_domains_create(self, async_client: AsyncUnlayer) -> None: - v1 = await async_client.project.v1.domains_create( - domain="domain", - ) - assert_matches_type(V1DomainsCreateResponse, v1, path=["response"]) - - @parametrize - async def test_raw_response_domains_create(self, async_client: AsyncUnlayer) -> None: - response = await async_client.project.v1.with_raw_response.domains_create( - domain="domain", - ) - - assert response.is_closed is True - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - v1 = await response.parse() - assert_matches_type(V1DomainsCreateResponse, v1, path=["response"]) - - @parametrize - async def test_streaming_response_domains_create(self, async_client: AsyncUnlayer) -> None: - async with async_client.project.v1.with_streaming_response.domains_create( - domain="domain", - ) as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - - v1 = await response.parse() - assert_matches_type(V1DomainsCreateResponse, v1, path=["response"]) - - assert cast(Any, response.is_closed) is True - - @parametrize - async def test_method_domains_delete(self, async_client: AsyncUnlayer) -> None: - v1 = await async_client.project.v1.domains_delete( - "id", - ) - assert v1 is None - - @parametrize - async def test_raw_response_domains_delete(self, async_client: AsyncUnlayer) -> None: - response = await async_client.project.v1.with_raw_response.domains_delete( - "id", - ) - - assert response.is_closed is True - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - v1 = await response.parse() - assert v1 is None - - @parametrize - async def test_streaming_response_domains_delete(self, async_client: AsyncUnlayer) -> None: - async with async_client.project.v1.with_streaming_response.domains_delete( - "id", - ) as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - - v1 = await response.parse() - assert v1 is None - - assert cast(Any, response.is_closed) is True - - @parametrize - async def test_path_params_domains_delete(self, async_client: AsyncUnlayer) -> None: - with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): - await async_client.project.v1.with_raw_response.domains_delete( - "", - ) - - @parametrize - async def test_method_domains_list(self, async_client: AsyncUnlayer) -> None: - v1 = await async_client.project.v1.domains_list() - assert_matches_type(V1DomainsListResponse, v1, path=["response"]) - - @parametrize - async def test_raw_response_domains_list(self, async_client: AsyncUnlayer) -> None: - response = await async_client.project.v1.with_raw_response.domains_list() - - assert response.is_closed is True - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - v1 = await response.parse() - assert_matches_type(V1DomainsListResponse, v1, path=["response"]) - - @parametrize - async def test_streaming_response_domains_list(self, async_client: AsyncUnlayer) -> None: - async with async_client.project.v1.with_streaming_response.domains_list() as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - - v1 = await response.parse() - assert_matches_type(V1DomainsListResponse, v1, path=["response"]) - - assert cast(Any, response.is_closed) is True - - @parametrize - async def test_method_domains_retrieve(self, async_client: AsyncUnlayer) -> None: - v1 = await async_client.project.v1.domains_retrieve( - "id", - ) - assert_matches_type(V1DomainsRetrieveResponse, v1, path=["response"]) - - @parametrize - async def test_raw_response_domains_retrieve(self, async_client: AsyncUnlayer) -> None: - response = await async_client.project.v1.with_raw_response.domains_retrieve( - "id", - ) - - assert response.is_closed is True - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - v1 = await response.parse() - assert_matches_type(V1DomainsRetrieveResponse, v1, path=["response"]) - - @parametrize - async def test_streaming_response_domains_retrieve(self, async_client: AsyncUnlayer) -> None: - async with async_client.project.v1.with_streaming_response.domains_retrieve( - "id", - ) as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - - v1 = await response.parse() - assert_matches_type(V1DomainsRetrieveResponse, v1, path=["response"]) - - assert cast(Any, response.is_closed) is True - - @parametrize - async def test_path_params_domains_retrieve(self, async_client: AsyncUnlayer) -> None: - with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): - await async_client.project.v1.with_raw_response.domains_retrieve( - "", - ) - - @parametrize - async def test_method_domains_update(self, async_client: AsyncUnlayer) -> None: - v1 = await async_client.project.v1.domains_update( - id="id", - ) - assert_matches_type(V1DomainsUpdateResponse, v1, path=["response"]) - - @parametrize - async def test_method_domains_update_with_all_params(self, async_client: AsyncUnlayer) -> None: - v1 = await async_client.project.v1.domains_update( - id="id", - domain="domain", - ) - assert_matches_type(V1DomainsUpdateResponse, v1, path=["response"]) - - @parametrize - async def test_raw_response_domains_update(self, async_client: AsyncUnlayer) -> None: - response = await async_client.project.v1.with_raw_response.domains_update( - id="id", - ) - - assert response.is_closed is True - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - v1 = await response.parse() - assert_matches_type(V1DomainsUpdateResponse, v1, path=["response"]) - - @parametrize - async def test_streaming_response_domains_update(self, async_client: AsyncUnlayer) -> None: - async with async_client.project.v1.with_streaming_response.domains_update( - id="id", - ) as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - - v1 = await response.parse() - assert_matches_type(V1DomainsUpdateResponse, v1, path=["response"]) - - assert cast(Any, response.is_closed) is True - - @parametrize - async def test_path_params_domains_update(self, async_client: AsyncUnlayer) -> None: - with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): - await async_client.project.v1.with_raw_response.domains_update( - id="", - ) - - @parametrize - async def test_method_templates_create(self, async_client: AsyncUnlayer) -> None: - v1 = await async_client.project.v1.templates_create( - name="name", - ) - assert_matches_type(V1TemplatesCreateResponse, v1, path=["response"]) - - @parametrize - async def test_method_templates_create_with_all_params(self, async_client: AsyncUnlayer) -> None: - v1 = await async_client.project.v1.templates_create( - name="name", - body="body", - subject="subject", - ) - assert_matches_type(V1TemplatesCreateResponse, v1, path=["response"]) - - @parametrize - async def test_raw_response_templates_create(self, async_client: AsyncUnlayer) -> None: - response = await async_client.project.v1.with_raw_response.templates_create( - name="name", - ) - - assert response.is_closed is True - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - v1 = await response.parse() - assert_matches_type(V1TemplatesCreateResponse, v1, path=["response"]) - - @parametrize - async def test_streaming_response_templates_create(self, async_client: AsyncUnlayer) -> None: - async with async_client.project.v1.with_streaming_response.templates_create( - name="name", - ) as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - - v1 = await response.parse() - assert_matches_type(V1TemplatesCreateResponse, v1, path=["response"]) - - assert cast(Any, response.is_closed) is True - - @parametrize - async def test_method_templates_delete(self, async_client: AsyncUnlayer) -> None: - v1 = await async_client.project.v1.templates_delete( - "id", - ) - assert v1 is None - - @parametrize - async def test_raw_response_templates_delete(self, async_client: AsyncUnlayer) -> None: - response = await async_client.project.v1.with_raw_response.templates_delete( - "id", - ) - - assert response.is_closed is True - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - v1 = await response.parse() - assert v1 is None - - @parametrize - async def test_streaming_response_templates_delete(self, async_client: AsyncUnlayer) -> None: - async with async_client.project.v1.with_streaming_response.templates_delete( - "id", - ) as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - - v1 = await response.parse() - assert v1 is None - - assert cast(Any, response.is_closed) is True - - @parametrize - async def test_path_params_templates_delete(self, async_client: AsyncUnlayer) -> None: - with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): - await async_client.project.v1.with_raw_response.templates_delete( - "", - ) - - @parametrize - async def test_method_templates_list(self, async_client: AsyncUnlayer) -> None: - v1 = await async_client.project.v1.templates_list() - assert_matches_type(V1TemplatesListResponse, v1, path=["response"]) - - @parametrize - async def test_raw_response_templates_list(self, async_client: AsyncUnlayer) -> None: - response = await async_client.project.v1.with_raw_response.templates_list() - - assert response.is_closed is True - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - v1 = await response.parse() - assert_matches_type(V1TemplatesListResponse, v1, path=["response"]) - - @parametrize - async def test_streaming_response_templates_list(self, async_client: AsyncUnlayer) -> None: - async with async_client.project.v1.with_streaming_response.templates_list() as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - - v1 = await response.parse() - assert_matches_type(V1TemplatesListResponse, v1, path=["response"]) - - assert cast(Any, response.is_closed) is True - - @parametrize - async def test_method_templates_retrieve(self, async_client: AsyncUnlayer) -> None: - v1 = await async_client.project.v1.templates_retrieve( - "id", - ) - assert_matches_type(V1TemplatesRetrieveResponse, v1, path=["response"]) - - @parametrize - async def test_raw_response_templates_retrieve(self, async_client: AsyncUnlayer) -> None: - response = await async_client.project.v1.with_raw_response.templates_retrieve( - "id", - ) - - assert response.is_closed is True - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - v1 = await response.parse() - assert_matches_type(V1TemplatesRetrieveResponse, v1, path=["response"]) - - @parametrize - async def test_streaming_response_templates_retrieve(self, async_client: AsyncUnlayer) -> None: - async with async_client.project.v1.with_streaming_response.templates_retrieve( - "id", - ) as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - - v1 = await response.parse() - assert_matches_type(V1TemplatesRetrieveResponse, v1, path=["response"]) - - assert cast(Any, response.is_closed) is True - - @parametrize - async def test_path_params_templates_retrieve(self, async_client: AsyncUnlayer) -> None: - with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): - await async_client.project.v1.with_raw_response.templates_retrieve( - "", - ) - - @parametrize - async def test_method_templates_update(self, async_client: AsyncUnlayer) -> None: - v1 = await async_client.project.v1.templates_update( - id="id", - ) - assert_matches_type(V1TemplatesUpdateResponse, v1, path=["response"]) - - @parametrize - async def test_method_templates_update_with_all_params(self, async_client: AsyncUnlayer) -> None: - v1 = await async_client.project.v1.templates_update( - id="id", - body="body", - name="name", - subject="subject", - ) - assert_matches_type(V1TemplatesUpdateResponse, v1, path=["response"]) - - @parametrize - async def test_raw_response_templates_update(self, async_client: AsyncUnlayer) -> None: - response = await async_client.project.v1.with_raw_response.templates_update( - id="id", - ) - - assert response.is_closed is True - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - v1 = await response.parse() - assert_matches_type(V1TemplatesUpdateResponse, v1, path=["response"]) - - @parametrize - async def test_streaming_response_templates_update(self, async_client: AsyncUnlayer) -> None: - async with async_client.project.v1.with_streaming_response.templates_update( - id="id", - ) as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - - v1 = await response.parse() - assert_matches_type(V1TemplatesUpdateResponse, v1, path=["response"]) - - assert cast(Any, response.is_closed) is True - - @parametrize - async def test_path_params_templates_update(self, async_client: AsyncUnlayer) -> None: - with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): - await async_client.project.v1.with_raw_response.templates_update( - id="", - ) From f067a5bd954fb31c492111c4502f522c93349df3 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Mon, 15 Dec 2025 09:59:55 +0000 Subject: [PATCH 07/62] feat(api): api update --- .stats.yml | 6 +- README.md | 4 + api.md | 32 ++++---- src/unlayer/__init__.py | 14 +++- src/unlayer/_client.py | 126 +++++++++++++++++++++++------- src/unlayer/resources/__init__.py | 24 +++--- tests/test_client.py | 22 ++++++ 7 files changed, 167 insertions(+), 61 deletions(-) diff --git a/.stats.yml b/.stats.yml index 3b76cc0..59b1ef8 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 24 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/unlayer%2Funlayer-9942f4da50a072ed2feb0c1aa835272455da6a6e9a22b23d51f75c6002c9da64.yml -openapi_spec_hash: 19ba50f4ff2d2bb8b2a16a6083d1e1e9 -config_hash: 6d277064312d0ee93ce6df0eee354fa6 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/unlayer%2Funlayer-737d922f62f9846af680567f50b999b1cec12674d24ae99e262ab1bcc0cbe311.yml +openapi_spec_hash: e1c7ddabf7b87df222fd4e268193d1dd +config_hash: 69225430c17187a9fea0a90e757affff diff --git a/README.md b/README.md index 74e8e82..0fae0ad 100644 --- a/README.md +++ b/README.md @@ -33,6 +33,8 @@ from unlayer import Unlayer client = Unlayer( api_key=os.environ.get("UNLAYER_API_KEY"), # This is the default and can be omitted + # or 'production' | 'dev'; defaults to "production". + environment="qa", ) response = client.project.current_list() @@ -55,6 +57,8 @@ from unlayer import AsyncUnlayer client = AsyncUnlayer( api_key=os.environ.get("UNLAYER_API_KEY"), # This is the default and can be omitted + # or 'production' | 'dev'; defaults to "production". + environment="qa", ) diff --git a/api.md b/api.md index b30df0d..8d63dc8 100644 --- a/api.md +++ b/api.md @@ -39,23 +39,25 @@ Methods: - client.project.templates_retrieve(id) -> ProjectTemplatesRetrieveResponse - client.project.templates_update(id, \*\*params) -> ProjectTemplatesUpdateResponse -# Documents +# Emails Types: ```python from unlayer.types import ( - DocumentDocumentsRetrieveResponse, - DocumentGenerateCreateResponse, - DocumentGenerateTemplateTemplateResponse, + EmailRetrieveResponse, + EmailRenderCreateResponse, + EmailSendCreateResponse, + EmailSendTemplateTemplateResponse, ) ``` Methods: -- client.documents.documents_retrieve(id) -> DocumentDocumentsRetrieveResponse -- client.documents.generate_create(\*\*params) -> DocumentGenerateCreateResponse -- client.documents.generate_template_template(\*\*params) -> DocumentGenerateTemplateTemplateResponse +- client.emails.retrieve(id) -> EmailRetrieveResponse +- client.emails.render_create(\*\*params) -> EmailRenderCreateResponse +- client.emails.send_create(\*\*params) -> EmailSendCreateResponse +- client.emails.send_template_template(\*\*params) -> EmailSendTemplateTemplateResponse # Pages @@ -69,22 +71,20 @@ Methods: - client.pages.render_create(\*\*params) -> PageRenderCreateResponse -# Emails +# Documents Types: ```python from unlayer.types import ( - EmailRetrieveResponse, - EmailRenderCreateResponse, - EmailSendCreateResponse, - EmailSendTemplateTemplateResponse, + DocumentDocumentsRetrieveResponse, + DocumentGenerateCreateResponse, + DocumentGenerateTemplateTemplateResponse, ) ``` Methods: -- client.emails.retrieve(id) -> EmailRetrieveResponse -- client.emails.render_create(\*\*params) -> EmailRenderCreateResponse -- client.emails.send_create(\*\*params) -> EmailSendCreateResponse -- client.emails.send_template_template(\*\*params) -> EmailSendTemplateTemplateResponse +- client.documents.documents_retrieve(id) -> DocumentDocumentsRetrieveResponse +- client.documents.generate_create(\*\*params) -> DocumentGenerateCreateResponse +- client.documents.generate_template_template(\*\*params) -> DocumentGenerateTemplateTemplateResponse diff --git a/src/unlayer/__init__.py b/src/unlayer/__init__.py index 37d9b74..196b04d 100644 --- a/src/unlayer/__init__.py +++ b/src/unlayer/__init__.py @@ -5,7 +5,18 @@ from . import types from ._types import NOT_GIVEN, Omit, NoneType, NotGiven, Transport, ProxiesTypes, omit, not_given from ._utils import file_from_path -from ._client import Client, Stream, Timeout, Unlayer, Transport, AsyncClient, AsyncStream, AsyncUnlayer, RequestOptions +from ._client import ( + ENVIRONMENTS, + Client, + Stream, + Timeout, + Unlayer, + Transport, + AsyncClient, + AsyncStream, + AsyncUnlayer, + RequestOptions, +) from ._models import BaseModel from ._version import __title__, __version__ from ._response import APIResponse as APIResponse, AsyncAPIResponse as AsyncAPIResponse @@ -63,6 +74,7 @@ "AsyncStream", "Unlayer", "AsyncUnlayer", + "ENVIRONMENTS", "file_from_path", "BaseModel", "DEFAULT_TIMEOUT", diff --git a/src/unlayer/_client.py b/src/unlayer/_client.py index 6da7474..45063d7 100644 --- a/src/unlayer/_client.py +++ b/src/unlayer/_client.py @@ -3,8 +3,8 @@ from __future__ import annotations import os -from typing import Any, Mapping -from typing_extensions import Self, override +from typing import Any, Dict, Mapping, cast +from typing_extensions import Self, Literal, override import httpx @@ -30,25 +30,44 @@ AsyncAPIClient, ) -__all__ = ["Timeout", "Transport", "ProxiesTypes", "RequestOptions", "Unlayer", "AsyncUnlayer", "Client", "AsyncClient"] +__all__ = [ + "ENVIRONMENTS", + "Timeout", + "Transport", + "ProxiesTypes", + "RequestOptions", + "Unlayer", + "AsyncUnlayer", + "Client", + "AsyncClient", +] + +ENVIRONMENTS: Dict[str, str] = { + "production": "https://api.unlayer.com", + "qa": "https://api.qa.unlayer.com", + "dev": "https://api.dev.unlayer.com", +} class Unlayer(SyncAPIClient): project: project.ProjectResource - documents: documents.DocumentsResource - pages: pages.PagesResource emails: emails.EmailsResource + pages: pages.PagesResource + documents: documents.DocumentsResource with_raw_response: UnlayerWithRawResponse with_streaming_response: UnlayerWithStreamedResponse # client options api_key: str + _environment: Literal["production", "qa", "dev"] | NotGiven + def __init__( self, *, api_key: str | None = None, - base_url: str | httpx.URL | None = None, + environment: Literal["production", "qa", "dev"] | NotGiven = not_given, + base_url: str | httpx.URL | None | NotGiven = not_given, timeout: float | Timeout | None | NotGiven = not_given, max_retries: int = DEFAULT_MAX_RETRIES, default_headers: Mapping[str, str] | None = None, @@ -79,10 +98,31 @@ def __init__( ) self.api_key = api_key - if base_url is None: - base_url = os.environ.get("UNLAYER_BASE_URL") - if base_url is None: - base_url = f"https://api.unlayer.com" + self._environment = environment + + base_url_env = os.environ.get("UNLAYER_BASE_URL") + if is_given(base_url) and base_url is not None: + # cast required because mypy doesn't understand the type narrowing + base_url = cast("str | httpx.URL", base_url) # pyright: ignore[reportUnnecessaryCast] + elif is_given(environment): + if base_url_env and base_url is not None: + raise ValueError( + "Ambiguous URL; The `UNLAYER_BASE_URL` env var and the `environment` argument are given. If you want to use the environment, you must pass base_url=None", + ) + + try: + base_url = ENVIRONMENTS[environment] + except KeyError as exc: + raise ValueError(f"Unknown environment: {environment}") from exc + elif base_url_env is not None: + base_url = base_url_env + else: + self._environment = environment = "production" + + try: + base_url = ENVIRONMENTS[environment] + except KeyError as exc: + raise ValueError(f"Unknown environment: {environment}") from exc super().__init__( version=__version__, @@ -96,9 +136,9 @@ def __init__( ) self.project = project.ProjectResource(self) - self.documents = documents.DocumentsResource(self) - self.pages = pages.PagesResource(self) self.emails = emails.EmailsResource(self) + self.pages = pages.PagesResource(self) + self.documents = documents.DocumentsResource(self) self.with_raw_response = UnlayerWithRawResponse(self) self.with_streaming_response = UnlayerWithStreamedResponse(self) @@ -126,6 +166,7 @@ def copy( self, *, api_key: str | None = None, + environment: Literal["production", "qa", "dev"] | None = None, base_url: str | httpx.URL | None = None, timeout: float | Timeout | None | NotGiven = not_given, http_client: httpx.Client | None = None, @@ -161,6 +202,7 @@ def copy( return self.__class__( api_key=api_key or self.api_key, base_url=base_url or self.base_url, + environment=environment or self._environment, timeout=self.timeout if isinstance(timeout, NotGiven) else timeout, http_client=http_client, max_retries=max_retries if is_given(max_retries) else self.max_retries, @@ -209,20 +251,23 @@ def _make_status_error( class AsyncUnlayer(AsyncAPIClient): project: project.AsyncProjectResource - documents: documents.AsyncDocumentsResource - pages: pages.AsyncPagesResource emails: emails.AsyncEmailsResource + pages: pages.AsyncPagesResource + documents: documents.AsyncDocumentsResource with_raw_response: AsyncUnlayerWithRawResponse with_streaming_response: AsyncUnlayerWithStreamedResponse # client options api_key: str + _environment: Literal["production", "qa", "dev"] | NotGiven + def __init__( self, *, api_key: str | None = None, - base_url: str | httpx.URL | None = None, + environment: Literal["production", "qa", "dev"] | NotGiven = not_given, + base_url: str | httpx.URL | None | NotGiven = not_given, timeout: float | Timeout | None | NotGiven = not_given, max_retries: int = DEFAULT_MAX_RETRIES, default_headers: Mapping[str, str] | None = None, @@ -253,10 +298,31 @@ def __init__( ) self.api_key = api_key - if base_url is None: - base_url = os.environ.get("UNLAYER_BASE_URL") - if base_url is None: - base_url = f"https://api.unlayer.com" + self._environment = environment + + base_url_env = os.environ.get("UNLAYER_BASE_URL") + if is_given(base_url) and base_url is not None: + # cast required because mypy doesn't understand the type narrowing + base_url = cast("str | httpx.URL", base_url) # pyright: ignore[reportUnnecessaryCast] + elif is_given(environment): + if base_url_env and base_url is not None: + raise ValueError( + "Ambiguous URL; The `UNLAYER_BASE_URL` env var and the `environment` argument are given. If you want to use the environment, you must pass base_url=None", + ) + + try: + base_url = ENVIRONMENTS[environment] + except KeyError as exc: + raise ValueError(f"Unknown environment: {environment}") from exc + elif base_url_env is not None: + base_url = base_url_env + else: + self._environment = environment = "production" + + try: + base_url = ENVIRONMENTS[environment] + except KeyError as exc: + raise ValueError(f"Unknown environment: {environment}") from exc super().__init__( version=__version__, @@ -270,9 +336,9 @@ def __init__( ) self.project = project.AsyncProjectResource(self) - self.documents = documents.AsyncDocumentsResource(self) - self.pages = pages.AsyncPagesResource(self) self.emails = emails.AsyncEmailsResource(self) + self.pages = pages.AsyncPagesResource(self) + self.documents = documents.AsyncDocumentsResource(self) self.with_raw_response = AsyncUnlayerWithRawResponse(self) self.with_streaming_response = AsyncUnlayerWithStreamedResponse(self) @@ -300,6 +366,7 @@ def copy( self, *, api_key: str | None = None, + environment: Literal["production", "qa", "dev"] | None = None, base_url: str | httpx.URL | None = None, timeout: float | Timeout | None | NotGiven = not_given, http_client: httpx.AsyncClient | None = None, @@ -335,6 +402,7 @@ def copy( return self.__class__( api_key=api_key or self.api_key, base_url=base_url or self.base_url, + environment=environment or self._environment, timeout=self.timeout if isinstance(timeout, NotGiven) else timeout, http_client=http_client, max_retries=max_retries if is_given(max_retries) else self.max_retries, @@ -384,33 +452,33 @@ def _make_status_error( class UnlayerWithRawResponse: def __init__(self, client: Unlayer) -> None: self.project = project.ProjectResourceWithRawResponse(client.project) - self.documents = documents.DocumentsResourceWithRawResponse(client.documents) - self.pages = pages.PagesResourceWithRawResponse(client.pages) self.emails = emails.EmailsResourceWithRawResponse(client.emails) + self.pages = pages.PagesResourceWithRawResponse(client.pages) + self.documents = documents.DocumentsResourceWithRawResponse(client.documents) class AsyncUnlayerWithRawResponse: def __init__(self, client: AsyncUnlayer) -> None: self.project = project.AsyncProjectResourceWithRawResponse(client.project) - self.documents = documents.AsyncDocumentsResourceWithRawResponse(client.documents) - self.pages = pages.AsyncPagesResourceWithRawResponse(client.pages) self.emails = emails.AsyncEmailsResourceWithRawResponse(client.emails) + self.pages = pages.AsyncPagesResourceWithRawResponse(client.pages) + self.documents = documents.AsyncDocumentsResourceWithRawResponse(client.documents) class UnlayerWithStreamedResponse: def __init__(self, client: Unlayer) -> None: self.project = project.ProjectResourceWithStreamingResponse(client.project) - self.documents = documents.DocumentsResourceWithStreamingResponse(client.documents) - self.pages = pages.PagesResourceWithStreamingResponse(client.pages) self.emails = emails.EmailsResourceWithStreamingResponse(client.emails) + self.pages = pages.PagesResourceWithStreamingResponse(client.pages) + self.documents = documents.DocumentsResourceWithStreamingResponse(client.documents) class AsyncUnlayerWithStreamedResponse: def __init__(self, client: AsyncUnlayer) -> None: self.project = project.AsyncProjectResourceWithStreamingResponse(client.project) - self.documents = documents.AsyncDocumentsResourceWithStreamingResponse(client.documents) - self.pages = pages.AsyncPagesResourceWithStreamingResponse(client.pages) self.emails = emails.AsyncEmailsResourceWithStreamingResponse(client.emails) + self.pages = pages.AsyncPagesResourceWithStreamingResponse(client.pages) + self.documents = documents.AsyncDocumentsResourceWithStreamingResponse(client.documents) Client = Unlayer diff --git a/src/unlayer/resources/__init__.py b/src/unlayer/resources/__init__.py index 616e08d..157be92 100644 --- a/src/unlayer/resources/__init__.py +++ b/src/unlayer/resources/__init__.py @@ -40,22 +40,22 @@ "AsyncProjectResourceWithRawResponse", "ProjectResourceWithStreamingResponse", "AsyncProjectResourceWithStreamingResponse", - "DocumentsResource", - "AsyncDocumentsResource", - "DocumentsResourceWithRawResponse", - "AsyncDocumentsResourceWithRawResponse", - "DocumentsResourceWithStreamingResponse", - "AsyncDocumentsResourceWithStreamingResponse", - "PagesResource", - "AsyncPagesResource", - "PagesResourceWithRawResponse", - "AsyncPagesResourceWithRawResponse", - "PagesResourceWithStreamingResponse", - "AsyncPagesResourceWithStreamingResponse", "EmailsResource", "AsyncEmailsResource", "EmailsResourceWithRawResponse", "AsyncEmailsResourceWithRawResponse", "EmailsResourceWithStreamingResponse", "AsyncEmailsResourceWithStreamingResponse", + "PagesResource", + "AsyncPagesResource", + "PagesResourceWithRawResponse", + "AsyncPagesResourceWithRawResponse", + "PagesResourceWithStreamingResponse", + "AsyncPagesResourceWithStreamingResponse", + "DocumentsResource", + "AsyncDocumentsResource", + "DocumentsResourceWithRawResponse", + "AsyncDocumentsResourceWithRawResponse", + "DocumentsResourceWithStreamingResponse", + "AsyncDocumentsResourceWithStreamingResponse", ] diff --git a/tests/test_client.py b/tests/test_client.py index 55d91cc..d120c95 100644 --- a/tests/test_client.py +++ b/tests/test_client.py @@ -572,6 +572,16 @@ def test_base_url_env(self) -> None: client = Unlayer(api_key=api_key, _strict_response_validation=True) assert client.base_url == "http://localhost:5000/from/env/" + # explicit environment arg requires explicitness + with update_env(UNLAYER_BASE_URL="http://localhost:5000/from/env"): + with pytest.raises(ValueError, match=r"you must pass base_url=None"): + Unlayer(api_key=api_key, _strict_response_validation=True, environment="production") + + client = Unlayer(base_url=None, api_key=api_key, _strict_response_validation=True, environment="production") + assert str(client.base_url).startswith("https://api.unlayer.com") + + client.close() + @pytest.mark.parametrize( "client", [ @@ -1395,6 +1405,18 @@ async def test_base_url_env(self) -> None: client = AsyncUnlayer(api_key=api_key, _strict_response_validation=True) assert client.base_url == "http://localhost:5000/from/env/" + # explicit environment arg requires explicitness + with update_env(UNLAYER_BASE_URL="http://localhost:5000/from/env"): + with pytest.raises(ValueError, match=r"you must pass base_url=None"): + AsyncUnlayer(api_key=api_key, _strict_response_validation=True, environment="production") + + client = AsyncUnlayer( + base_url=None, api_key=api_key, _strict_response_validation=True, environment="production" + ) + assert str(client.base_url).startswith("https://api.unlayer.com") + + await client.close() + @pytest.mark.parametrize( "client", [ From 2a8cd520469618b78c744254ddc6f4b68cd6f92f Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 16 Dec 2025 03:23:45 +0000 Subject: [PATCH 08/62] chore(internal): add missing files argument to base client --- src/unlayer/_base_client.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/unlayer/_base_client.py b/src/unlayer/_base_client.py index f3883b2..ee69aa3 100644 --- a/src/unlayer/_base_client.py +++ b/src/unlayer/_base_client.py @@ -1247,9 +1247,12 @@ def patch( *, cast_to: Type[ResponseT], body: Body | None = None, + files: RequestFiles | None = None, options: RequestOptions = {}, ) -> ResponseT: - opts = FinalRequestOptions.construct(method="patch", url=path, json_data=body, **options) + opts = FinalRequestOptions.construct( + method="patch", url=path, json_data=body, files=to_httpx_files(files), **options + ) return self.request(cast_to, opts) def put( @@ -1767,9 +1770,12 @@ async def patch( *, cast_to: Type[ResponseT], body: Body | None = None, + files: RequestFiles | None = None, options: RequestOptions = {}, ) -> ResponseT: - opts = FinalRequestOptions.construct(method="patch", url=path, json_data=body, **options) + opts = FinalRequestOptions.construct( + method="patch", url=path, json_data=body, files=to_httpx_files(files), **options + ) return await self.request(cast_to, opts) async def put( From 88a47a10200dfd85c73c4224a268da3680e88829 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Wed, 17 Dec 2025 03:46:14 +0000 Subject: [PATCH 09/62] chore: speedup initial import --- src/unlayer/_client.py | 223 +++++++++++++++++++++++++++++++++-------- 1 file changed, 179 insertions(+), 44 deletions(-) diff --git a/src/unlayer/_client.py b/src/unlayer/_client.py index 45063d7..8a4da8b 100644 --- a/src/unlayer/_client.py +++ b/src/unlayer/_client.py @@ -3,7 +3,7 @@ from __future__ import annotations import os -from typing import Any, Dict, Mapping, cast +from typing import TYPE_CHECKING, Any, Dict, Mapping, cast from typing_extensions import Self, Literal, override import httpx @@ -20,8 +20,8 @@ not_given, ) from ._utils import is_given, get_async_library +from ._compat import cached_property from ._version import __version__ -from .resources import pages, emails, project, documents from ._streaming import Stream as Stream, AsyncStream as AsyncStream from ._exceptions import UnlayerError, APIStatusError from ._base_client import ( @@ -30,6 +30,13 @@ AsyncAPIClient, ) +if TYPE_CHECKING: + from .resources import pages, emails, project, documents + from .resources.pages import PagesResource, AsyncPagesResource + from .resources.emails import EmailsResource, AsyncEmailsResource + from .resources.project import ProjectResource, AsyncProjectResource + from .resources.documents import DocumentsResource, AsyncDocumentsResource + __all__ = [ "ENVIRONMENTS", "Timeout", @@ -50,13 +57,6 @@ class Unlayer(SyncAPIClient): - project: project.ProjectResource - emails: emails.EmailsResource - pages: pages.PagesResource - documents: documents.DocumentsResource - with_raw_response: UnlayerWithRawResponse - with_streaming_response: UnlayerWithStreamedResponse - # client options api_key: str @@ -135,12 +135,37 @@ def __init__( _strict_response_validation=_strict_response_validation, ) - self.project = project.ProjectResource(self) - self.emails = emails.EmailsResource(self) - self.pages = pages.PagesResource(self) - self.documents = documents.DocumentsResource(self) - self.with_raw_response = UnlayerWithRawResponse(self) - self.with_streaming_response = UnlayerWithStreamedResponse(self) + @cached_property + def project(self) -> ProjectResource: + from .resources.project import ProjectResource + + return ProjectResource(self) + + @cached_property + def emails(self) -> EmailsResource: + from .resources.emails import EmailsResource + + return EmailsResource(self) + + @cached_property + def pages(self) -> PagesResource: + from .resources.pages import PagesResource + + return PagesResource(self) + + @cached_property + def documents(self) -> DocumentsResource: + from .resources.documents import DocumentsResource + + return DocumentsResource(self) + + @cached_property + def with_raw_response(self) -> UnlayerWithRawResponse: + return UnlayerWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> UnlayerWithStreamedResponse: + return UnlayerWithStreamedResponse(self) @property @override @@ -250,13 +275,6 @@ def _make_status_error( class AsyncUnlayer(AsyncAPIClient): - project: project.AsyncProjectResource - emails: emails.AsyncEmailsResource - pages: pages.AsyncPagesResource - documents: documents.AsyncDocumentsResource - with_raw_response: AsyncUnlayerWithRawResponse - with_streaming_response: AsyncUnlayerWithStreamedResponse - # client options api_key: str @@ -335,12 +353,37 @@ def __init__( _strict_response_validation=_strict_response_validation, ) - self.project = project.AsyncProjectResource(self) - self.emails = emails.AsyncEmailsResource(self) - self.pages = pages.AsyncPagesResource(self) - self.documents = documents.AsyncDocumentsResource(self) - self.with_raw_response = AsyncUnlayerWithRawResponse(self) - self.with_streaming_response = AsyncUnlayerWithStreamedResponse(self) + @cached_property + def project(self) -> AsyncProjectResource: + from .resources.project import AsyncProjectResource + + return AsyncProjectResource(self) + + @cached_property + def emails(self) -> AsyncEmailsResource: + from .resources.emails import AsyncEmailsResource + + return AsyncEmailsResource(self) + + @cached_property + def pages(self) -> AsyncPagesResource: + from .resources.pages import AsyncPagesResource + + return AsyncPagesResource(self) + + @cached_property + def documents(self) -> AsyncDocumentsResource: + from .resources.documents import AsyncDocumentsResource + + return AsyncDocumentsResource(self) + + @cached_property + def with_raw_response(self) -> AsyncUnlayerWithRawResponse: + return AsyncUnlayerWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncUnlayerWithStreamedResponse: + return AsyncUnlayerWithStreamedResponse(self) @property @override @@ -450,35 +493,127 @@ def _make_status_error( class UnlayerWithRawResponse: + _client: Unlayer + def __init__(self, client: Unlayer) -> None: - self.project = project.ProjectResourceWithRawResponse(client.project) - self.emails = emails.EmailsResourceWithRawResponse(client.emails) - self.pages = pages.PagesResourceWithRawResponse(client.pages) - self.documents = documents.DocumentsResourceWithRawResponse(client.documents) + self._client = client + + @cached_property + def project(self) -> project.ProjectResourceWithRawResponse: + from .resources.project import ProjectResourceWithRawResponse + + return ProjectResourceWithRawResponse(self._client.project) + + @cached_property + def emails(self) -> emails.EmailsResourceWithRawResponse: + from .resources.emails import EmailsResourceWithRawResponse + + return EmailsResourceWithRawResponse(self._client.emails) + + @cached_property + def pages(self) -> pages.PagesResourceWithRawResponse: + from .resources.pages import PagesResourceWithRawResponse + + return PagesResourceWithRawResponse(self._client.pages) + + @cached_property + def documents(self) -> documents.DocumentsResourceWithRawResponse: + from .resources.documents import DocumentsResourceWithRawResponse + + return DocumentsResourceWithRawResponse(self._client.documents) class AsyncUnlayerWithRawResponse: + _client: AsyncUnlayer + def __init__(self, client: AsyncUnlayer) -> None: - self.project = project.AsyncProjectResourceWithRawResponse(client.project) - self.emails = emails.AsyncEmailsResourceWithRawResponse(client.emails) - self.pages = pages.AsyncPagesResourceWithRawResponse(client.pages) - self.documents = documents.AsyncDocumentsResourceWithRawResponse(client.documents) + self._client = client + + @cached_property + def project(self) -> project.AsyncProjectResourceWithRawResponse: + from .resources.project import AsyncProjectResourceWithRawResponse + + return AsyncProjectResourceWithRawResponse(self._client.project) + + @cached_property + def emails(self) -> emails.AsyncEmailsResourceWithRawResponse: + from .resources.emails import AsyncEmailsResourceWithRawResponse + + return AsyncEmailsResourceWithRawResponse(self._client.emails) + + @cached_property + def pages(self) -> pages.AsyncPagesResourceWithRawResponse: + from .resources.pages import AsyncPagesResourceWithRawResponse + + return AsyncPagesResourceWithRawResponse(self._client.pages) + + @cached_property + def documents(self) -> documents.AsyncDocumentsResourceWithRawResponse: + from .resources.documents import AsyncDocumentsResourceWithRawResponse + + return AsyncDocumentsResourceWithRawResponse(self._client.documents) class UnlayerWithStreamedResponse: + _client: Unlayer + def __init__(self, client: Unlayer) -> None: - self.project = project.ProjectResourceWithStreamingResponse(client.project) - self.emails = emails.EmailsResourceWithStreamingResponse(client.emails) - self.pages = pages.PagesResourceWithStreamingResponse(client.pages) - self.documents = documents.DocumentsResourceWithStreamingResponse(client.documents) + self._client = client + + @cached_property + def project(self) -> project.ProjectResourceWithStreamingResponse: + from .resources.project import ProjectResourceWithStreamingResponse + + return ProjectResourceWithStreamingResponse(self._client.project) + + @cached_property + def emails(self) -> emails.EmailsResourceWithStreamingResponse: + from .resources.emails import EmailsResourceWithStreamingResponse + + return EmailsResourceWithStreamingResponse(self._client.emails) + + @cached_property + def pages(self) -> pages.PagesResourceWithStreamingResponse: + from .resources.pages import PagesResourceWithStreamingResponse + + return PagesResourceWithStreamingResponse(self._client.pages) + + @cached_property + def documents(self) -> documents.DocumentsResourceWithStreamingResponse: + from .resources.documents import DocumentsResourceWithStreamingResponse + + return DocumentsResourceWithStreamingResponse(self._client.documents) class AsyncUnlayerWithStreamedResponse: + _client: AsyncUnlayer + def __init__(self, client: AsyncUnlayer) -> None: - self.project = project.AsyncProjectResourceWithStreamingResponse(client.project) - self.emails = emails.AsyncEmailsResourceWithStreamingResponse(client.emails) - self.pages = pages.AsyncPagesResourceWithStreamingResponse(client.pages) - self.documents = documents.AsyncDocumentsResourceWithStreamingResponse(client.documents) + self._client = client + + @cached_property + def project(self) -> project.AsyncProjectResourceWithStreamingResponse: + from .resources.project import AsyncProjectResourceWithStreamingResponse + + return AsyncProjectResourceWithStreamingResponse(self._client.project) + + @cached_property + def emails(self) -> emails.AsyncEmailsResourceWithStreamingResponse: + from .resources.emails import AsyncEmailsResourceWithStreamingResponse + + return AsyncEmailsResourceWithStreamingResponse(self._client.emails) + + @cached_property + def pages(self) -> pages.AsyncPagesResourceWithStreamingResponse: + from .resources.pages import AsyncPagesResourceWithStreamingResponse + + return AsyncPagesResourceWithStreamingResponse(self._client.pages) + + @cached_property + def documents(self) -> documents.AsyncDocumentsResourceWithStreamingResponse: + from .resources.documents import AsyncDocumentsResourceWithStreamingResponse + + return AsyncDocumentsResourceWithStreamingResponse(self._client.documents) Client = Unlayer From 1ef1fd18647320ffef9655a3df8f73564b91d171 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Thu, 18 Dec 2025 03:56:59 +0000 Subject: [PATCH 10/62] fix: use async_to_httpx_files in patch method --- src/unlayer/_base_client.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/unlayer/_base_client.py b/src/unlayer/_base_client.py index ee69aa3..bbc2e91 100644 --- a/src/unlayer/_base_client.py +++ b/src/unlayer/_base_client.py @@ -1774,7 +1774,7 @@ async def patch( options: RequestOptions = {}, ) -> ResponseT: opts = FinalRequestOptions.construct( - method="patch", url=path, json_data=body, files=to_httpx_files(files), **options + method="patch", url=path, json_data=body, files=await async_to_httpx_files(files), **options ) return await self.request(cast_to, opts) From 5a81d38e892e90ca14095cfae23571796f182861 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Fri, 19 Dec 2025 03:47:12 +0000 Subject: [PATCH 11/62] chore(internal): add `--fix` argument to lint script --- scripts/lint | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/scripts/lint b/scripts/lint index 1b9382a..913377a 100755 --- a/scripts/lint +++ b/scripts/lint @@ -4,8 +4,13 @@ set -e cd "$(dirname "$0")/.." -echo "==> Running lints" -rye run lint +if [ "$1" = "--fix" ]; then + echo "==> Running lints with --fix" + rye run fix:ruff +else + echo "==> Running lints" + rye run lint +fi echo "==> Making sure it imports" rye run python -c 'import unlayer' From b2aa17a2cee39b561b609e4894d74caf670aab04 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Fri, 19 Dec 2025 09:14:20 +0000 Subject: [PATCH 12/62] feat(api): api update --- .stats.yml | 6 +- api.md | 54 +++++++-------- src/unlayer/_client.py | 108 +++++++++++++++--------------- src/unlayer/resources/__init__.py | 24 +++---- 4 files changed, 96 insertions(+), 96 deletions(-) diff --git a/.stats.yml b/.stats.yml index 59b1ef8..2e5df92 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 24 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/unlayer%2Funlayer-737d922f62f9846af680567f50b999b1cec12674d24ae99e262ab1bcc0cbe311.yml -openapi_spec_hash: e1c7ddabf7b87df222fd4e268193d1dd -config_hash: 69225430c17187a9fea0a90e757affff +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/unlayer%2Funlayer-7b157ae796e7fc651848e8626a9adb4b00614a203c5140530edb0c52f5c82f79.yml +openapi_spec_hash: 00ccd8fc07a13e525a6fc1af6445230e +config_hash: 4700894a8daff98f13b4480d5d3e2bed diff --git a/api.md b/api.md index 8d63dc8..31c3417 100644 --- a/api.md +++ b/api.md @@ -1,3 +1,23 @@ +# Emails + +Types: + +```python +from unlayer.types import ( + EmailRetrieveResponse, + EmailRenderCreateResponse, + EmailSendCreateResponse, + EmailSendTemplateTemplateResponse, +) +``` + +Methods: + +- client.emails.retrieve(id) -> EmailRetrieveResponse +- client.emails.render_create(\*\*params) -> EmailRenderCreateResponse +- client.emails.send_create(\*\*params) -> EmailSendCreateResponse +- client.emails.send_template_template(\*\*params) -> EmailSendTemplateTemplateResponse + # Project Types: @@ -39,25 +59,23 @@ Methods: - client.project.templates_retrieve(id) -> ProjectTemplatesRetrieveResponse - client.project.templates_update(id, \*\*params) -> ProjectTemplatesUpdateResponse -# Emails +# Documents Types: ```python from unlayer.types import ( - EmailRetrieveResponse, - EmailRenderCreateResponse, - EmailSendCreateResponse, - EmailSendTemplateTemplateResponse, + DocumentDocumentsRetrieveResponse, + DocumentGenerateCreateResponse, + DocumentGenerateTemplateTemplateResponse, ) ``` Methods: -- client.emails.retrieve(id) -> EmailRetrieveResponse -- client.emails.render_create(\*\*params) -> EmailRenderCreateResponse -- client.emails.send_create(\*\*params) -> EmailSendCreateResponse -- client.emails.send_template_template(\*\*params) -> EmailSendTemplateTemplateResponse +- client.documents.documents_retrieve(id) -> DocumentDocumentsRetrieveResponse +- client.documents.generate_create(\*\*params) -> DocumentGenerateCreateResponse +- client.documents.generate_template_template(\*\*params) -> DocumentGenerateTemplateTemplateResponse # Pages @@ -70,21 +88,3 @@ from unlayer.types import PageRenderCreateResponse Methods: - client.pages.render_create(\*\*params) -> PageRenderCreateResponse - -# Documents - -Types: - -```python -from unlayer.types import ( - DocumentDocumentsRetrieveResponse, - DocumentGenerateCreateResponse, - DocumentGenerateTemplateTemplateResponse, -) -``` - -Methods: - -- client.documents.documents_retrieve(id) -> DocumentDocumentsRetrieveResponse -- client.documents.generate_create(\*\*params) -> DocumentGenerateCreateResponse -- client.documents.generate_template_template(\*\*params) -> DocumentGenerateTemplateTemplateResponse diff --git a/src/unlayer/_client.py b/src/unlayer/_client.py index 8a4da8b..d61aee7 100644 --- a/src/unlayer/_client.py +++ b/src/unlayer/_client.py @@ -135,12 +135,6 @@ def __init__( _strict_response_validation=_strict_response_validation, ) - @cached_property - def project(self) -> ProjectResource: - from .resources.project import ProjectResource - - return ProjectResource(self) - @cached_property def emails(self) -> EmailsResource: from .resources.emails import EmailsResource @@ -148,10 +142,10 @@ def emails(self) -> EmailsResource: return EmailsResource(self) @cached_property - def pages(self) -> PagesResource: - from .resources.pages import PagesResource + def project(self) -> ProjectResource: + from .resources.project import ProjectResource - return PagesResource(self) + return ProjectResource(self) @cached_property def documents(self) -> DocumentsResource: @@ -159,6 +153,12 @@ def documents(self) -> DocumentsResource: return DocumentsResource(self) + @cached_property + def pages(self) -> PagesResource: + from .resources.pages import PagesResource + + return PagesResource(self) + @cached_property def with_raw_response(self) -> UnlayerWithRawResponse: return UnlayerWithRawResponse(self) @@ -353,12 +353,6 @@ def __init__( _strict_response_validation=_strict_response_validation, ) - @cached_property - def project(self) -> AsyncProjectResource: - from .resources.project import AsyncProjectResource - - return AsyncProjectResource(self) - @cached_property def emails(self) -> AsyncEmailsResource: from .resources.emails import AsyncEmailsResource @@ -366,10 +360,10 @@ def emails(self) -> AsyncEmailsResource: return AsyncEmailsResource(self) @cached_property - def pages(self) -> AsyncPagesResource: - from .resources.pages import AsyncPagesResource + def project(self) -> AsyncProjectResource: + from .resources.project import AsyncProjectResource - return AsyncPagesResource(self) + return AsyncProjectResource(self) @cached_property def documents(self) -> AsyncDocumentsResource: @@ -377,6 +371,12 @@ def documents(self) -> AsyncDocumentsResource: return AsyncDocumentsResource(self) + @cached_property + def pages(self) -> AsyncPagesResource: + from .resources.pages import AsyncPagesResource + + return AsyncPagesResource(self) + @cached_property def with_raw_response(self) -> AsyncUnlayerWithRawResponse: return AsyncUnlayerWithRawResponse(self) @@ -498,12 +498,6 @@ class UnlayerWithRawResponse: def __init__(self, client: Unlayer) -> None: self._client = client - @cached_property - def project(self) -> project.ProjectResourceWithRawResponse: - from .resources.project import ProjectResourceWithRawResponse - - return ProjectResourceWithRawResponse(self._client.project) - @cached_property def emails(self) -> emails.EmailsResourceWithRawResponse: from .resources.emails import EmailsResourceWithRawResponse @@ -511,10 +505,10 @@ def emails(self) -> emails.EmailsResourceWithRawResponse: return EmailsResourceWithRawResponse(self._client.emails) @cached_property - def pages(self) -> pages.PagesResourceWithRawResponse: - from .resources.pages import PagesResourceWithRawResponse + def project(self) -> project.ProjectResourceWithRawResponse: + from .resources.project import ProjectResourceWithRawResponse - return PagesResourceWithRawResponse(self._client.pages) + return ProjectResourceWithRawResponse(self._client.project) @cached_property def documents(self) -> documents.DocumentsResourceWithRawResponse: @@ -522,6 +516,12 @@ def documents(self) -> documents.DocumentsResourceWithRawResponse: return DocumentsResourceWithRawResponse(self._client.documents) + @cached_property + def pages(self) -> pages.PagesResourceWithRawResponse: + from .resources.pages import PagesResourceWithRawResponse + + return PagesResourceWithRawResponse(self._client.pages) + class AsyncUnlayerWithRawResponse: _client: AsyncUnlayer @@ -529,12 +529,6 @@ class AsyncUnlayerWithRawResponse: def __init__(self, client: AsyncUnlayer) -> None: self._client = client - @cached_property - def project(self) -> project.AsyncProjectResourceWithRawResponse: - from .resources.project import AsyncProjectResourceWithRawResponse - - return AsyncProjectResourceWithRawResponse(self._client.project) - @cached_property def emails(self) -> emails.AsyncEmailsResourceWithRawResponse: from .resources.emails import AsyncEmailsResourceWithRawResponse @@ -542,10 +536,10 @@ def emails(self) -> emails.AsyncEmailsResourceWithRawResponse: return AsyncEmailsResourceWithRawResponse(self._client.emails) @cached_property - def pages(self) -> pages.AsyncPagesResourceWithRawResponse: - from .resources.pages import AsyncPagesResourceWithRawResponse + def project(self) -> project.AsyncProjectResourceWithRawResponse: + from .resources.project import AsyncProjectResourceWithRawResponse - return AsyncPagesResourceWithRawResponse(self._client.pages) + return AsyncProjectResourceWithRawResponse(self._client.project) @cached_property def documents(self) -> documents.AsyncDocumentsResourceWithRawResponse: @@ -553,6 +547,12 @@ def documents(self) -> documents.AsyncDocumentsResourceWithRawResponse: return AsyncDocumentsResourceWithRawResponse(self._client.documents) + @cached_property + def pages(self) -> pages.AsyncPagesResourceWithRawResponse: + from .resources.pages import AsyncPagesResourceWithRawResponse + + return AsyncPagesResourceWithRawResponse(self._client.pages) + class UnlayerWithStreamedResponse: _client: Unlayer @@ -560,12 +560,6 @@ class UnlayerWithStreamedResponse: def __init__(self, client: Unlayer) -> None: self._client = client - @cached_property - def project(self) -> project.ProjectResourceWithStreamingResponse: - from .resources.project import ProjectResourceWithStreamingResponse - - return ProjectResourceWithStreamingResponse(self._client.project) - @cached_property def emails(self) -> emails.EmailsResourceWithStreamingResponse: from .resources.emails import EmailsResourceWithStreamingResponse @@ -573,10 +567,10 @@ def emails(self) -> emails.EmailsResourceWithStreamingResponse: return EmailsResourceWithStreamingResponse(self._client.emails) @cached_property - def pages(self) -> pages.PagesResourceWithStreamingResponse: - from .resources.pages import PagesResourceWithStreamingResponse + def project(self) -> project.ProjectResourceWithStreamingResponse: + from .resources.project import ProjectResourceWithStreamingResponse - return PagesResourceWithStreamingResponse(self._client.pages) + return ProjectResourceWithStreamingResponse(self._client.project) @cached_property def documents(self) -> documents.DocumentsResourceWithStreamingResponse: @@ -584,6 +578,12 @@ def documents(self) -> documents.DocumentsResourceWithStreamingResponse: return DocumentsResourceWithStreamingResponse(self._client.documents) + @cached_property + def pages(self) -> pages.PagesResourceWithStreamingResponse: + from .resources.pages import PagesResourceWithStreamingResponse + + return PagesResourceWithStreamingResponse(self._client.pages) + class AsyncUnlayerWithStreamedResponse: _client: AsyncUnlayer @@ -591,12 +591,6 @@ class AsyncUnlayerWithStreamedResponse: def __init__(self, client: AsyncUnlayer) -> None: self._client = client - @cached_property - def project(self) -> project.AsyncProjectResourceWithStreamingResponse: - from .resources.project import AsyncProjectResourceWithStreamingResponse - - return AsyncProjectResourceWithStreamingResponse(self._client.project) - @cached_property def emails(self) -> emails.AsyncEmailsResourceWithStreamingResponse: from .resources.emails import AsyncEmailsResourceWithStreamingResponse @@ -604,10 +598,10 @@ def emails(self) -> emails.AsyncEmailsResourceWithStreamingResponse: return AsyncEmailsResourceWithStreamingResponse(self._client.emails) @cached_property - def pages(self) -> pages.AsyncPagesResourceWithStreamingResponse: - from .resources.pages import AsyncPagesResourceWithStreamingResponse + def project(self) -> project.AsyncProjectResourceWithStreamingResponse: + from .resources.project import AsyncProjectResourceWithStreamingResponse - return AsyncPagesResourceWithStreamingResponse(self._client.pages) + return AsyncProjectResourceWithStreamingResponse(self._client.project) @cached_property def documents(self) -> documents.AsyncDocumentsResourceWithStreamingResponse: @@ -615,6 +609,12 @@ def documents(self) -> documents.AsyncDocumentsResourceWithStreamingResponse: return AsyncDocumentsResourceWithStreamingResponse(self._client.documents) + @cached_property + def pages(self) -> pages.AsyncPagesResourceWithStreamingResponse: + from .resources.pages import AsyncPagesResourceWithStreamingResponse + + return AsyncPagesResourceWithStreamingResponse(self._client.pages) + Client = Unlayer diff --git a/src/unlayer/resources/__init__.py b/src/unlayer/resources/__init__.py index 157be92..86c9e06 100644 --- a/src/unlayer/resources/__init__.py +++ b/src/unlayer/resources/__init__.py @@ -34,28 +34,28 @@ ) __all__ = [ - "ProjectResource", - "AsyncProjectResource", - "ProjectResourceWithRawResponse", - "AsyncProjectResourceWithRawResponse", - "ProjectResourceWithStreamingResponse", - "AsyncProjectResourceWithStreamingResponse", "EmailsResource", "AsyncEmailsResource", "EmailsResourceWithRawResponse", "AsyncEmailsResourceWithRawResponse", "EmailsResourceWithStreamingResponse", "AsyncEmailsResourceWithStreamingResponse", - "PagesResource", - "AsyncPagesResource", - "PagesResourceWithRawResponse", - "AsyncPagesResourceWithRawResponse", - "PagesResourceWithStreamingResponse", - "AsyncPagesResourceWithStreamingResponse", + "ProjectResource", + "AsyncProjectResource", + "ProjectResourceWithRawResponse", + "AsyncProjectResourceWithRawResponse", + "ProjectResourceWithStreamingResponse", + "AsyncProjectResourceWithStreamingResponse", "DocumentsResource", "AsyncDocumentsResource", "DocumentsResourceWithRawResponse", "AsyncDocumentsResourceWithRawResponse", "DocumentsResourceWithStreamingResponse", "AsyncDocumentsResourceWithStreamingResponse", + "PagesResource", + "AsyncPagesResource", + "PagesResourceWithRawResponse", + "AsyncPagesResourceWithRawResponse", + "PagesResourceWithStreamingResponse", + "AsyncPagesResourceWithStreamingResponse", ] From 62619dd0da29e0eb37ea2c4eea80478b0f80fe23 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Mon, 29 Dec 2025 15:20:12 +0000 Subject: [PATCH 13/62] feat(api): api update --- .stats.yml | 6 +- api.md | 60 ++++++------- src/unlayer/_client.py | 144 +++++++++++++++--------------- src/unlayer/resources/__init__.py | 24 ++--- 4 files changed, 117 insertions(+), 117 deletions(-) diff --git a/.stats.yml b/.stats.yml index 2e5df92..8c69178 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 24 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/unlayer%2Funlayer-7b157ae796e7fc651848e8626a9adb4b00614a203c5140530edb0c52f5c82f79.yml -openapi_spec_hash: 00ccd8fc07a13e525a6fc1af6445230e -config_hash: 4700894a8daff98f13b4480d5d3e2bed +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/unlayer%2Funlayer-8e523084a4299d49f71a9dd36232b00a4f3f4c5a94d2b95f118d54a49184595f.yml +openapi_spec_hash: 16e58acb9377ae565449741fce8857ea +config_hash: 6e7ac0b0bf24c892c17a4773cfdfbadc diff --git a/api.md b/api.md index 31c3417..318ce88 100644 --- a/api.md +++ b/api.md @@ -1,3 +1,33 @@ +# Pages + +Types: + +```python +from unlayer.types import PageRenderCreateResponse +``` + +Methods: + +- client.pages.render_create(\*\*params) -> PageRenderCreateResponse + +# Documents + +Types: + +```python +from unlayer.types import ( + DocumentDocumentsRetrieveResponse, + DocumentGenerateCreateResponse, + DocumentGenerateTemplateTemplateResponse, +) +``` + +Methods: + +- client.documents.documents_retrieve(id) -> DocumentDocumentsRetrieveResponse +- client.documents.generate_create(\*\*params) -> DocumentGenerateCreateResponse +- client.documents.generate_template_template(\*\*params) -> DocumentGenerateTemplateTemplateResponse + # Emails Types: @@ -58,33 +88,3 @@ Methods: - client.project.templates_list() -> ProjectTemplatesListResponse - client.project.templates_retrieve(id) -> ProjectTemplatesRetrieveResponse - client.project.templates_update(id, \*\*params) -> ProjectTemplatesUpdateResponse - -# Documents - -Types: - -```python -from unlayer.types import ( - DocumentDocumentsRetrieveResponse, - DocumentGenerateCreateResponse, - DocumentGenerateTemplateTemplateResponse, -) -``` - -Methods: - -- client.documents.documents_retrieve(id) -> DocumentDocumentsRetrieveResponse -- client.documents.generate_create(\*\*params) -> DocumentGenerateCreateResponse -- client.documents.generate_template_template(\*\*params) -> DocumentGenerateTemplateTemplateResponse - -# Pages - -Types: - -```python -from unlayer.types import PageRenderCreateResponse -``` - -Methods: - -- client.pages.render_create(\*\*params) -> PageRenderCreateResponse diff --git a/src/unlayer/_client.py b/src/unlayer/_client.py index d61aee7..80baa7f 100644 --- a/src/unlayer/_client.py +++ b/src/unlayer/_client.py @@ -136,16 +136,10 @@ def __init__( ) @cached_property - def emails(self) -> EmailsResource: - from .resources.emails import EmailsResource - - return EmailsResource(self) - - @cached_property - def project(self) -> ProjectResource: - from .resources.project import ProjectResource + def pages(self) -> PagesResource: + from .resources.pages import PagesResource - return ProjectResource(self) + return PagesResource(self) @cached_property def documents(self) -> DocumentsResource: @@ -154,10 +148,16 @@ def documents(self) -> DocumentsResource: return DocumentsResource(self) @cached_property - def pages(self) -> PagesResource: - from .resources.pages import PagesResource + def emails(self) -> EmailsResource: + from .resources.emails import EmailsResource - return PagesResource(self) + return EmailsResource(self) + + @cached_property + def project(self) -> ProjectResource: + from .resources.project import ProjectResource + + return ProjectResource(self) @cached_property def with_raw_response(self) -> UnlayerWithRawResponse: @@ -354,16 +354,10 @@ def __init__( ) @cached_property - def emails(self) -> AsyncEmailsResource: - from .resources.emails import AsyncEmailsResource - - return AsyncEmailsResource(self) - - @cached_property - def project(self) -> AsyncProjectResource: - from .resources.project import AsyncProjectResource + def pages(self) -> AsyncPagesResource: + from .resources.pages import AsyncPagesResource - return AsyncProjectResource(self) + return AsyncPagesResource(self) @cached_property def documents(self) -> AsyncDocumentsResource: @@ -372,10 +366,16 @@ def documents(self) -> AsyncDocumentsResource: return AsyncDocumentsResource(self) @cached_property - def pages(self) -> AsyncPagesResource: - from .resources.pages import AsyncPagesResource + def emails(self) -> AsyncEmailsResource: + from .resources.emails import AsyncEmailsResource - return AsyncPagesResource(self) + return AsyncEmailsResource(self) + + @cached_property + def project(self) -> AsyncProjectResource: + from .resources.project import AsyncProjectResource + + return AsyncProjectResource(self) @cached_property def with_raw_response(self) -> AsyncUnlayerWithRawResponse: @@ -499,16 +499,10 @@ def __init__(self, client: Unlayer) -> None: self._client = client @cached_property - def emails(self) -> emails.EmailsResourceWithRawResponse: - from .resources.emails import EmailsResourceWithRawResponse - - return EmailsResourceWithRawResponse(self._client.emails) - - @cached_property - def project(self) -> project.ProjectResourceWithRawResponse: - from .resources.project import ProjectResourceWithRawResponse + def pages(self) -> pages.PagesResourceWithRawResponse: + from .resources.pages import PagesResourceWithRawResponse - return ProjectResourceWithRawResponse(self._client.project) + return PagesResourceWithRawResponse(self._client.pages) @cached_property def documents(self) -> documents.DocumentsResourceWithRawResponse: @@ -517,10 +511,16 @@ def documents(self) -> documents.DocumentsResourceWithRawResponse: return DocumentsResourceWithRawResponse(self._client.documents) @cached_property - def pages(self) -> pages.PagesResourceWithRawResponse: - from .resources.pages import PagesResourceWithRawResponse + def emails(self) -> emails.EmailsResourceWithRawResponse: + from .resources.emails import EmailsResourceWithRawResponse - return PagesResourceWithRawResponse(self._client.pages) + return EmailsResourceWithRawResponse(self._client.emails) + + @cached_property + def project(self) -> project.ProjectResourceWithRawResponse: + from .resources.project import ProjectResourceWithRawResponse + + return ProjectResourceWithRawResponse(self._client.project) class AsyncUnlayerWithRawResponse: @@ -530,16 +530,10 @@ def __init__(self, client: AsyncUnlayer) -> None: self._client = client @cached_property - def emails(self) -> emails.AsyncEmailsResourceWithRawResponse: - from .resources.emails import AsyncEmailsResourceWithRawResponse - - return AsyncEmailsResourceWithRawResponse(self._client.emails) - - @cached_property - def project(self) -> project.AsyncProjectResourceWithRawResponse: - from .resources.project import AsyncProjectResourceWithRawResponse + def pages(self) -> pages.AsyncPagesResourceWithRawResponse: + from .resources.pages import AsyncPagesResourceWithRawResponse - return AsyncProjectResourceWithRawResponse(self._client.project) + return AsyncPagesResourceWithRawResponse(self._client.pages) @cached_property def documents(self) -> documents.AsyncDocumentsResourceWithRawResponse: @@ -548,10 +542,16 @@ def documents(self) -> documents.AsyncDocumentsResourceWithRawResponse: return AsyncDocumentsResourceWithRawResponse(self._client.documents) @cached_property - def pages(self) -> pages.AsyncPagesResourceWithRawResponse: - from .resources.pages import AsyncPagesResourceWithRawResponse + def emails(self) -> emails.AsyncEmailsResourceWithRawResponse: + from .resources.emails import AsyncEmailsResourceWithRawResponse - return AsyncPagesResourceWithRawResponse(self._client.pages) + return AsyncEmailsResourceWithRawResponse(self._client.emails) + + @cached_property + def project(self) -> project.AsyncProjectResourceWithRawResponse: + from .resources.project import AsyncProjectResourceWithRawResponse + + return AsyncProjectResourceWithRawResponse(self._client.project) class UnlayerWithStreamedResponse: @@ -561,16 +561,10 @@ def __init__(self, client: Unlayer) -> None: self._client = client @cached_property - def emails(self) -> emails.EmailsResourceWithStreamingResponse: - from .resources.emails import EmailsResourceWithStreamingResponse - - return EmailsResourceWithStreamingResponse(self._client.emails) - - @cached_property - def project(self) -> project.ProjectResourceWithStreamingResponse: - from .resources.project import ProjectResourceWithStreamingResponse + def pages(self) -> pages.PagesResourceWithStreamingResponse: + from .resources.pages import PagesResourceWithStreamingResponse - return ProjectResourceWithStreamingResponse(self._client.project) + return PagesResourceWithStreamingResponse(self._client.pages) @cached_property def documents(self) -> documents.DocumentsResourceWithStreamingResponse: @@ -579,10 +573,16 @@ def documents(self) -> documents.DocumentsResourceWithStreamingResponse: return DocumentsResourceWithStreamingResponse(self._client.documents) @cached_property - def pages(self) -> pages.PagesResourceWithStreamingResponse: - from .resources.pages import PagesResourceWithStreamingResponse + def emails(self) -> emails.EmailsResourceWithStreamingResponse: + from .resources.emails import EmailsResourceWithStreamingResponse - return PagesResourceWithStreamingResponse(self._client.pages) + return EmailsResourceWithStreamingResponse(self._client.emails) + + @cached_property + def project(self) -> project.ProjectResourceWithStreamingResponse: + from .resources.project import ProjectResourceWithStreamingResponse + + return ProjectResourceWithStreamingResponse(self._client.project) class AsyncUnlayerWithStreamedResponse: @@ -592,16 +592,10 @@ def __init__(self, client: AsyncUnlayer) -> None: self._client = client @cached_property - def emails(self) -> emails.AsyncEmailsResourceWithStreamingResponse: - from .resources.emails import AsyncEmailsResourceWithStreamingResponse - - return AsyncEmailsResourceWithStreamingResponse(self._client.emails) - - @cached_property - def project(self) -> project.AsyncProjectResourceWithStreamingResponse: - from .resources.project import AsyncProjectResourceWithStreamingResponse + def pages(self) -> pages.AsyncPagesResourceWithStreamingResponse: + from .resources.pages import AsyncPagesResourceWithStreamingResponse - return AsyncProjectResourceWithStreamingResponse(self._client.project) + return AsyncPagesResourceWithStreamingResponse(self._client.pages) @cached_property def documents(self) -> documents.AsyncDocumentsResourceWithStreamingResponse: @@ -610,10 +604,16 @@ def documents(self) -> documents.AsyncDocumentsResourceWithStreamingResponse: return AsyncDocumentsResourceWithStreamingResponse(self._client.documents) @cached_property - def pages(self) -> pages.AsyncPagesResourceWithStreamingResponse: - from .resources.pages import AsyncPagesResourceWithStreamingResponse + def emails(self) -> emails.AsyncEmailsResourceWithStreamingResponse: + from .resources.emails import AsyncEmailsResourceWithStreamingResponse - return AsyncPagesResourceWithStreamingResponse(self._client.pages) + return AsyncEmailsResourceWithStreamingResponse(self._client.emails) + + @cached_property + def project(self) -> project.AsyncProjectResourceWithStreamingResponse: + from .resources.project import AsyncProjectResourceWithStreamingResponse + + return AsyncProjectResourceWithStreamingResponse(self._client.project) Client = Unlayer diff --git a/src/unlayer/resources/__init__.py b/src/unlayer/resources/__init__.py index 86c9e06..8eb5b75 100644 --- a/src/unlayer/resources/__init__.py +++ b/src/unlayer/resources/__init__.py @@ -34,6 +34,18 @@ ) __all__ = [ + "PagesResource", + "AsyncPagesResource", + "PagesResourceWithRawResponse", + "AsyncPagesResourceWithRawResponse", + "PagesResourceWithStreamingResponse", + "AsyncPagesResourceWithStreamingResponse", + "DocumentsResource", + "AsyncDocumentsResource", + "DocumentsResourceWithRawResponse", + "AsyncDocumentsResourceWithRawResponse", + "DocumentsResourceWithStreamingResponse", + "AsyncDocumentsResourceWithStreamingResponse", "EmailsResource", "AsyncEmailsResource", "EmailsResourceWithRawResponse", @@ -46,16 +58,4 @@ "AsyncProjectResourceWithRawResponse", "ProjectResourceWithStreamingResponse", "AsyncProjectResourceWithStreamingResponse", - "DocumentsResource", - "AsyncDocumentsResource", - "DocumentsResourceWithRawResponse", - "AsyncDocumentsResourceWithRawResponse", - "DocumentsResourceWithStreamingResponse", - "AsyncDocumentsResourceWithStreamingResponse", - "PagesResource", - "AsyncPagesResource", - "PagesResourceWithRawResponse", - "AsyncPagesResourceWithRawResponse", - "PagesResourceWithStreamingResponse", - "AsyncPagesResourceWithStreamingResponse", ] From 29f2d03ffc87a9a024deafeb7a8e7d64644f4089 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 6 Jan 2026 03:31:46 +0000 Subject: [PATCH 14/62] chore(internal): codegen related update --- LICENSE | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/LICENSE b/LICENSE index 42d0669..82bf563 100644 --- a/LICENSE +++ b/LICENSE @@ -186,7 +186,7 @@ same "printed page" as the copyright notice for easier identification within third-party archives. - Copyright 2025 Unlayer + Copyright 2026 Unlayer Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. From fa2813f35452d8eea1a258a0136b0587a542a685 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 6 Jan 2026 09:00:55 +0000 Subject: [PATCH 15/62] feat(api): api update --- .stats.yml | 6 +- api.md | 60 ++++++------- src/unlayer/_client.py | 144 +++++++++++++++--------------- src/unlayer/resources/__init__.py | 24 ++--- 4 files changed, 117 insertions(+), 117 deletions(-) diff --git a/.stats.yml b/.stats.yml index 8c69178..2e5df92 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 24 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/unlayer%2Funlayer-8e523084a4299d49f71a9dd36232b00a4f3f4c5a94d2b95f118d54a49184595f.yml -openapi_spec_hash: 16e58acb9377ae565449741fce8857ea -config_hash: 6e7ac0b0bf24c892c17a4773cfdfbadc +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/unlayer%2Funlayer-7b157ae796e7fc651848e8626a9adb4b00614a203c5140530edb0c52f5c82f79.yml +openapi_spec_hash: 00ccd8fc07a13e525a6fc1af6445230e +config_hash: 4700894a8daff98f13b4480d5d3e2bed diff --git a/api.md b/api.md index 318ce88..31c3417 100644 --- a/api.md +++ b/api.md @@ -1,33 +1,3 @@ -# Pages - -Types: - -```python -from unlayer.types import PageRenderCreateResponse -``` - -Methods: - -- client.pages.render_create(\*\*params) -> PageRenderCreateResponse - -# Documents - -Types: - -```python -from unlayer.types import ( - DocumentDocumentsRetrieveResponse, - DocumentGenerateCreateResponse, - DocumentGenerateTemplateTemplateResponse, -) -``` - -Methods: - -- client.documents.documents_retrieve(id) -> DocumentDocumentsRetrieveResponse -- client.documents.generate_create(\*\*params) -> DocumentGenerateCreateResponse -- client.documents.generate_template_template(\*\*params) -> DocumentGenerateTemplateTemplateResponse - # Emails Types: @@ -88,3 +58,33 @@ Methods: - client.project.templates_list() -> ProjectTemplatesListResponse - client.project.templates_retrieve(id) -> ProjectTemplatesRetrieveResponse - client.project.templates_update(id, \*\*params) -> ProjectTemplatesUpdateResponse + +# Documents + +Types: + +```python +from unlayer.types import ( + DocumentDocumentsRetrieveResponse, + DocumentGenerateCreateResponse, + DocumentGenerateTemplateTemplateResponse, +) +``` + +Methods: + +- client.documents.documents_retrieve(id) -> DocumentDocumentsRetrieveResponse +- client.documents.generate_create(\*\*params) -> DocumentGenerateCreateResponse +- client.documents.generate_template_template(\*\*params) -> DocumentGenerateTemplateTemplateResponse + +# Pages + +Types: + +```python +from unlayer.types import PageRenderCreateResponse +``` + +Methods: + +- client.pages.render_create(\*\*params) -> PageRenderCreateResponse diff --git a/src/unlayer/_client.py b/src/unlayer/_client.py index 80baa7f..d61aee7 100644 --- a/src/unlayer/_client.py +++ b/src/unlayer/_client.py @@ -135,18 +135,6 @@ def __init__( _strict_response_validation=_strict_response_validation, ) - @cached_property - def pages(self) -> PagesResource: - from .resources.pages import PagesResource - - return PagesResource(self) - - @cached_property - def documents(self) -> DocumentsResource: - from .resources.documents import DocumentsResource - - return DocumentsResource(self) - @cached_property def emails(self) -> EmailsResource: from .resources.emails import EmailsResource @@ -159,6 +147,18 @@ def project(self) -> ProjectResource: return ProjectResource(self) + @cached_property + def documents(self) -> DocumentsResource: + from .resources.documents import DocumentsResource + + return DocumentsResource(self) + + @cached_property + def pages(self) -> PagesResource: + from .resources.pages import PagesResource + + return PagesResource(self) + @cached_property def with_raw_response(self) -> UnlayerWithRawResponse: return UnlayerWithRawResponse(self) @@ -353,18 +353,6 @@ def __init__( _strict_response_validation=_strict_response_validation, ) - @cached_property - def pages(self) -> AsyncPagesResource: - from .resources.pages import AsyncPagesResource - - return AsyncPagesResource(self) - - @cached_property - def documents(self) -> AsyncDocumentsResource: - from .resources.documents import AsyncDocumentsResource - - return AsyncDocumentsResource(self) - @cached_property def emails(self) -> AsyncEmailsResource: from .resources.emails import AsyncEmailsResource @@ -377,6 +365,18 @@ def project(self) -> AsyncProjectResource: return AsyncProjectResource(self) + @cached_property + def documents(self) -> AsyncDocumentsResource: + from .resources.documents import AsyncDocumentsResource + + return AsyncDocumentsResource(self) + + @cached_property + def pages(self) -> AsyncPagesResource: + from .resources.pages import AsyncPagesResource + + return AsyncPagesResource(self) + @cached_property def with_raw_response(self) -> AsyncUnlayerWithRawResponse: return AsyncUnlayerWithRawResponse(self) @@ -498,18 +498,6 @@ class UnlayerWithRawResponse: def __init__(self, client: Unlayer) -> None: self._client = client - @cached_property - def pages(self) -> pages.PagesResourceWithRawResponse: - from .resources.pages import PagesResourceWithRawResponse - - return PagesResourceWithRawResponse(self._client.pages) - - @cached_property - def documents(self) -> documents.DocumentsResourceWithRawResponse: - from .resources.documents import DocumentsResourceWithRawResponse - - return DocumentsResourceWithRawResponse(self._client.documents) - @cached_property def emails(self) -> emails.EmailsResourceWithRawResponse: from .resources.emails import EmailsResourceWithRawResponse @@ -522,24 +510,24 @@ def project(self) -> project.ProjectResourceWithRawResponse: return ProjectResourceWithRawResponse(self._client.project) + @cached_property + def documents(self) -> documents.DocumentsResourceWithRawResponse: + from .resources.documents import DocumentsResourceWithRawResponse -class AsyncUnlayerWithRawResponse: - _client: AsyncUnlayer - - def __init__(self, client: AsyncUnlayer) -> None: - self._client = client + return DocumentsResourceWithRawResponse(self._client.documents) @cached_property - def pages(self) -> pages.AsyncPagesResourceWithRawResponse: - from .resources.pages import AsyncPagesResourceWithRawResponse + def pages(self) -> pages.PagesResourceWithRawResponse: + from .resources.pages import PagesResourceWithRawResponse - return AsyncPagesResourceWithRawResponse(self._client.pages) + return PagesResourceWithRawResponse(self._client.pages) - @cached_property - def documents(self) -> documents.AsyncDocumentsResourceWithRawResponse: - from .resources.documents import AsyncDocumentsResourceWithRawResponse - return AsyncDocumentsResourceWithRawResponse(self._client.documents) +class AsyncUnlayerWithRawResponse: + _client: AsyncUnlayer + + def __init__(self, client: AsyncUnlayer) -> None: + self._client = client @cached_property def emails(self) -> emails.AsyncEmailsResourceWithRawResponse: @@ -553,24 +541,24 @@ def project(self) -> project.AsyncProjectResourceWithRawResponse: return AsyncProjectResourceWithRawResponse(self._client.project) + @cached_property + def documents(self) -> documents.AsyncDocumentsResourceWithRawResponse: + from .resources.documents import AsyncDocumentsResourceWithRawResponse -class UnlayerWithStreamedResponse: - _client: Unlayer - - def __init__(self, client: Unlayer) -> None: - self._client = client + return AsyncDocumentsResourceWithRawResponse(self._client.documents) @cached_property - def pages(self) -> pages.PagesResourceWithStreamingResponse: - from .resources.pages import PagesResourceWithStreamingResponse + def pages(self) -> pages.AsyncPagesResourceWithRawResponse: + from .resources.pages import AsyncPagesResourceWithRawResponse - return PagesResourceWithStreamingResponse(self._client.pages) + return AsyncPagesResourceWithRawResponse(self._client.pages) - @cached_property - def documents(self) -> documents.DocumentsResourceWithStreamingResponse: - from .resources.documents import DocumentsResourceWithStreamingResponse - return DocumentsResourceWithStreamingResponse(self._client.documents) +class UnlayerWithStreamedResponse: + _client: Unlayer + + def __init__(self, client: Unlayer) -> None: + self._client = client @cached_property def emails(self) -> emails.EmailsResourceWithStreamingResponse: @@ -584,24 +572,24 @@ def project(self) -> project.ProjectResourceWithStreamingResponse: return ProjectResourceWithStreamingResponse(self._client.project) + @cached_property + def documents(self) -> documents.DocumentsResourceWithStreamingResponse: + from .resources.documents import DocumentsResourceWithStreamingResponse -class AsyncUnlayerWithStreamedResponse: - _client: AsyncUnlayer - - def __init__(self, client: AsyncUnlayer) -> None: - self._client = client + return DocumentsResourceWithStreamingResponse(self._client.documents) @cached_property - def pages(self) -> pages.AsyncPagesResourceWithStreamingResponse: - from .resources.pages import AsyncPagesResourceWithStreamingResponse + def pages(self) -> pages.PagesResourceWithStreamingResponse: + from .resources.pages import PagesResourceWithStreamingResponse - return AsyncPagesResourceWithStreamingResponse(self._client.pages) + return PagesResourceWithStreamingResponse(self._client.pages) - @cached_property - def documents(self) -> documents.AsyncDocumentsResourceWithStreamingResponse: - from .resources.documents import AsyncDocumentsResourceWithStreamingResponse - return AsyncDocumentsResourceWithStreamingResponse(self._client.documents) +class AsyncUnlayerWithStreamedResponse: + _client: AsyncUnlayer + + def __init__(self, client: AsyncUnlayer) -> None: + self._client = client @cached_property def emails(self) -> emails.AsyncEmailsResourceWithStreamingResponse: @@ -615,6 +603,18 @@ def project(self) -> project.AsyncProjectResourceWithStreamingResponse: return AsyncProjectResourceWithStreamingResponse(self._client.project) + @cached_property + def documents(self) -> documents.AsyncDocumentsResourceWithStreamingResponse: + from .resources.documents import AsyncDocumentsResourceWithStreamingResponse + + return AsyncDocumentsResourceWithStreamingResponse(self._client.documents) + + @cached_property + def pages(self) -> pages.AsyncPagesResourceWithStreamingResponse: + from .resources.pages import AsyncPagesResourceWithStreamingResponse + + return AsyncPagesResourceWithStreamingResponse(self._client.pages) + Client = Unlayer diff --git a/src/unlayer/resources/__init__.py b/src/unlayer/resources/__init__.py index 8eb5b75..86c9e06 100644 --- a/src/unlayer/resources/__init__.py +++ b/src/unlayer/resources/__init__.py @@ -34,18 +34,6 @@ ) __all__ = [ - "PagesResource", - "AsyncPagesResource", - "PagesResourceWithRawResponse", - "AsyncPagesResourceWithRawResponse", - "PagesResourceWithStreamingResponse", - "AsyncPagesResourceWithStreamingResponse", - "DocumentsResource", - "AsyncDocumentsResource", - "DocumentsResourceWithRawResponse", - "AsyncDocumentsResourceWithRawResponse", - "DocumentsResourceWithStreamingResponse", - "AsyncDocumentsResourceWithStreamingResponse", "EmailsResource", "AsyncEmailsResource", "EmailsResourceWithRawResponse", @@ -58,4 +46,16 @@ "AsyncProjectResourceWithRawResponse", "ProjectResourceWithStreamingResponse", "AsyncProjectResourceWithStreamingResponse", + "DocumentsResource", + "AsyncDocumentsResource", + "DocumentsResourceWithRawResponse", + "AsyncDocumentsResourceWithRawResponse", + "DocumentsResourceWithStreamingResponse", + "AsyncDocumentsResourceWithStreamingResponse", + "PagesResource", + "AsyncPagesResource", + "PagesResourceWithRawResponse", + "AsyncPagesResourceWithRawResponse", + "PagesResourceWithStreamingResponse", + "AsyncPagesResourceWithStreamingResponse", ] From b75586f2f544e57e46e2d7744fbf492af1586eb7 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Thu, 8 Jan 2026 02:30:24 +0000 Subject: [PATCH 16/62] feat(api): api update --- .stats.yml | 6 +- api.md | 54 +++++++-------- src/unlayer/_client.py | 108 +++++++++++++++--------------- src/unlayer/resources/__init__.py | 24 +++---- 4 files changed, 96 insertions(+), 96 deletions(-) diff --git a/.stats.yml b/.stats.yml index 2e5df92..59b1ef8 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 24 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/unlayer%2Funlayer-7b157ae796e7fc651848e8626a9adb4b00614a203c5140530edb0c52f5c82f79.yml -openapi_spec_hash: 00ccd8fc07a13e525a6fc1af6445230e -config_hash: 4700894a8daff98f13b4480d5d3e2bed +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/unlayer%2Funlayer-737d922f62f9846af680567f50b999b1cec12674d24ae99e262ab1bcc0cbe311.yml +openapi_spec_hash: e1c7ddabf7b87df222fd4e268193d1dd +config_hash: 69225430c17187a9fea0a90e757affff diff --git a/api.md b/api.md index 31c3417..8d63dc8 100644 --- a/api.md +++ b/api.md @@ -1,23 +1,3 @@ -# Emails - -Types: - -```python -from unlayer.types import ( - EmailRetrieveResponse, - EmailRenderCreateResponse, - EmailSendCreateResponse, - EmailSendTemplateTemplateResponse, -) -``` - -Methods: - -- client.emails.retrieve(id) -> EmailRetrieveResponse -- client.emails.render_create(\*\*params) -> EmailRenderCreateResponse -- client.emails.send_create(\*\*params) -> EmailSendCreateResponse -- client.emails.send_template_template(\*\*params) -> EmailSendTemplateTemplateResponse - # Project Types: @@ -59,23 +39,25 @@ Methods: - client.project.templates_retrieve(id) -> ProjectTemplatesRetrieveResponse - client.project.templates_update(id, \*\*params) -> ProjectTemplatesUpdateResponse -# Documents +# Emails Types: ```python from unlayer.types import ( - DocumentDocumentsRetrieveResponse, - DocumentGenerateCreateResponse, - DocumentGenerateTemplateTemplateResponse, + EmailRetrieveResponse, + EmailRenderCreateResponse, + EmailSendCreateResponse, + EmailSendTemplateTemplateResponse, ) ``` Methods: -- client.documents.documents_retrieve(id) -> DocumentDocumentsRetrieveResponse -- client.documents.generate_create(\*\*params) -> DocumentGenerateCreateResponse -- client.documents.generate_template_template(\*\*params) -> DocumentGenerateTemplateTemplateResponse +- client.emails.retrieve(id) -> EmailRetrieveResponse +- client.emails.render_create(\*\*params) -> EmailRenderCreateResponse +- client.emails.send_create(\*\*params) -> EmailSendCreateResponse +- client.emails.send_template_template(\*\*params) -> EmailSendTemplateTemplateResponse # Pages @@ -88,3 +70,21 @@ from unlayer.types import PageRenderCreateResponse Methods: - client.pages.render_create(\*\*params) -> PageRenderCreateResponse + +# Documents + +Types: + +```python +from unlayer.types import ( + DocumentDocumentsRetrieveResponse, + DocumentGenerateCreateResponse, + DocumentGenerateTemplateTemplateResponse, +) +``` + +Methods: + +- client.documents.documents_retrieve(id) -> DocumentDocumentsRetrieveResponse +- client.documents.generate_create(\*\*params) -> DocumentGenerateCreateResponse +- client.documents.generate_template_template(\*\*params) -> DocumentGenerateTemplateTemplateResponse diff --git a/src/unlayer/_client.py b/src/unlayer/_client.py index d61aee7..8a4da8b 100644 --- a/src/unlayer/_client.py +++ b/src/unlayer/_client.py @@ -135,12 +135,6 @@ def __init__( _strict_response_validation=_strict_response_validation, ) - @cached_property - def emails(self) -> EmailsResource: - from .resources.emails import EmailsResource - - return EmailsResource(self) - @cached_property def project(self) -> ProjectResource: from .resources.project import ProjectResource @@ -148,10 +142,10 @@ def project(self) -> ProjectResource: return ProjectResource(self) @cached_property - def documents(self) -> DocumentsResource: - from .resources.documents import DocumentsResource + def emails(self) -> EmailsResource: + from .resources.emails import EmailsResource - return DocumentsResource(self) + return EmailsResource(self) @cached_property def pages(self) -> PagesResource: @@ -159,6 +153,12 @@ def pages(self) -> PagesResource: return PagesResource(self) + @cached_property + def documents(self) -> DocumentsResource: + from .resources.documents import DocumentsResource + + return DocumentsResource(self) + @cached_property def with_raw_response(self) -> UnlayerWithRawResponse: return UnlayerWithRawResponse(self) @@ -353,12 +353,6 @@ def __init__( _strict_response_validation=_strict_response_validation, ) - @cached_property - def emails(self) -> AsyncEmailsResource: - from .resources.emails import AsyncEmailsResource - - return AsyncEmailsResource(self) - @cached_property def project(self) -> AsyncProjectResource: from .resources.project import AsyncProjectResource @@ -366,10 +360,10 @@ def project(self) -> AsyncProjectResource: return AsyncProjectResource(self) @cached_property - def documents(self) -> AsyncDocumentsResource: - from .resources.documents import AsyncDocumentsResource + def emails(self) -> AsyncEmailsResource: + from .resources.emails import AsyncEmailsResource - return AsyncDocumentsResource(self) + return AsyncEmailsResource(self) @cached_property def pages(self) -> AsyncPagesResource: @@ -377,6 +371,12 @@ def pages(self) -> AsyncPagesResource: return AsyncPagesResource(self) + @cached_property + def documents(self) -> AsyncDocumentsResource: + from .resources.documents import AsyncDocumentsResource + + return AsyncDocumentsResource(self) + @cached_property def with_raw_response(self) -> AsyncUnlayerWithRawResponse: return AsyncUnlayerWithRawResponse(self) @@ -498,12 +498,6 @@ class UnlayerWithRawResponse: def __init__(self, client: Unlayer) -> None: self._client = client - @cached_property - def emails(self) -> emails.EmailsResourceWithRawResponse: - from .resources.emails import EmailsResourceWithRawResponse - - return EmailsResourceWithRawResponse(self._client.emails) - @cached_property def project(self) -> project.ProjectResourceWithRawResponse: from .resources.project import ProjectResourceWithRawResponse @@ -511,10 +505,10 @@ def project(self) -> project.ProjectResourceWithRawResponse: return ProjectResourceWithRawResponse(self._client.project) @cached_property - def documents(self) -> documents.DocumentsResourceWithRawResponse: - from .resources.documents import DocumentsResourceWithRawResponse + def emails(self) -> emails.EmailsResourceWithRawResponse: + from .resources.emails import EmailsResourceWithRawResponse - return DocumentsResourceWithRawResponse(self._client.documents) + return EmailsResourceWithRawResponse(self._client.emails) @cached_property def pages(self) -> pages.PagesResourceWithRawResponse: @@ -522,6 +516,12 @@ def pages(self) -> pages.PagesResourceWithRawResponse: return PagesResourceWithRawResponse(self._client.pages) + @cached_property + def documents(self) -> documents.DocumentsResourceWithRawResponse: + from .resources.documents import DocumentsResourceWithRawResponse + + return DocumentsResourceWithRawResponse(self._client.documents) + class AsyncUnlayerWithRawResponse: _client: AsyncUnlayer @@ -529,12 +529,6 @@ class AsyncUnlayerWithRawResponse: def __init__(self, client: AsyncUnlayer) -> None: self._client = client - @cached_property - def emails(self) -> emails.AsyncEmailsResourceWithRawResponse: - from .resources.emails import AsyncEmailsResourceWithRawResponse - - return AsyncEmailsResourceWithRawResponse(self._client.emails) - @cached_property def project(self) -> project.AsyncProjectResourceWithRawResponse: from .resources.project import AsyncProjectResourceWithRawResponse @@ -542,10 +536,10 @@ def project(self) -> project.AsyncProjectResourceWithRawResponse: return AsyncProjectResourceWithRawResponse(self._client.project) @cached_property - def documents(self) -> documents.AsyncDocumentsResourceWithRawResponse: - from .resources.documents import AsyncDocumentsResourceWithRawResponse + def emails(self) -> emails.AsyncEmailsResourceWithRawResponse: + from .resources.emails import AsyncEmailsResourceWithRawResponse - return AsyncDocumentsResourceWithRawResponse(self._client.documents) + return AsyncEmailsResourceWithRawResponse(self._client.emails) @cached_property def pages(self) -> pages.AsyncPagesResourceWithRawResponse: @@ -553,6 +547,12 @@ def pages(self) -> pages.AsyncPagesResourceWithRawResponse: return AsyncPagesResourceWithRawResponse(self._client.pages) + @cached_property + def documents(self) -> documents.AsyncDocumentsResourceWithRawResponse: + from .resources.documents import AsyncDocumentsResourceWithRawResponse + + return AsyncDocumentsResourceWithRawResponse(self._client.documents) + class UnlayerWithStreamedResponse: _client: Unlayer @@ -560,12 +560,6 @@ class UnlayerWithStreamedResponse: def __init__(self, client: Unlayer) -> None: self._client = client - @cached_property - def emails(self) -> emails.EmailsResourceWithStreamingResponse: - from .resources.emails import EmailsResourceWithStreamingResponse - - return EmailsResourceWithStreamingResponse(self._client.emails) - @cached_property def project(self) -> project.ProjectResourceWithStreamingResponse: from .resources.project import ProjectResourceWithStreamingResponse @@ -573,10 +567,10 @@ def project(self) -> project.ProjectResourceWithStreamingResponse: return ProjectResourceWithStreamingResponse(self._client.project) @cached_property - def documents(self) -> documents.DocumentsResourceWithStreamingResponse: - from .resources.documents import DocumentsResourceWithStreamingResponse + def emails(self) -> emails.EmailsResourceWithStreamingResponse: + from .resources.emails import EmailsResourceWithStreamingResponse - return DocumentsResourceWithStreamingResponse(self._client.documents) + return EmailsResourceWithStreamingResponse(self._client.emails) @cached_property def pages(self) -> pages.PagesResourceWithStreamingResponse: @@ -584,6 +578,12 @@ def pages(self) -> pages.PagesResourceWithStreamingResponse: return PagesResourceWithStreamingResponse(self._client.pages) + @cached_property + def documents(self) -> documents.DocumentsResourceWithStreamingResponse: + from .resources.documents import DocumentsResourceWithStreamingResponse + + return DocumentsResourceWithStreamingResponse(self._client.documents) + class AsyncUnlayerWithStreamedResponse: _client: AsyncUnlayer @@ -591,12 +591,6 @@ class AsyncUnlayerWithStreamedResponse: def __init__(self, client: AsyncUnlayer) -> None: self._client = client - @cached_property - def emails(self) -> emails.AsyncEmailsResourceWithStreamingResponse: - from .resources.emails import AsyncEmailsResourceWithStreamingResponse - - return AsyncEmailsResourceWithStreamingResponse(self._client.emails) - @cached_property def project(self) -> project.AsyncProjectResourceWithStreamingResponse: from .resources.project import AsyncProjectResourceWithStreamingResponse @@ -604,10 +598,10 @@ def project(self) -> project.AsyncProjectResourceWithStreamingResponse: return AsyncProjectResourceWithStreamingResponse(self._client.project) @cached_property - def documents(self) -> documents.AsyncDocumentsResourceWithStreamingResponse: - from .resources.documents import AsyncDocumentsResourceWithStreamingResponse + def emails(self) -> emails.AsyncEmailsResourceWithStreamingResponse: + from .resources.emails import AsyncEmailsResourceWithStreamingResponse - return AsyncDocumentsResourceWithStreamingResponse(self._client.documents) + return AsyncEmailsResourceWithStreamingResponse(self._client.emails) @cached_property def pages(self) -> pages.AsyncPagesResourceWithStreamingResponse: @@ -615,6 +609,12 @@ def pages(self) -> pages.AsyncPagesResourceWithStreamingResponse: return AsyncPagesResourceWithStreamingResponse(self._client.pages) + @cached_property + def documents(self) -> documents.AsyncDocumentsResourceWithStreamingResponse: + from .resources.documents import AsyncDocumentsResourceWithStreamingResponse + + return AsyncDocumentsResourceWithStreamingResponse(self._client.documents) + Client = Unlayer diff --git a/src/unlayer/resources/__init__.py b/src/unlayer/resources/__init__.py index 86c9e06..157be92 100644 --- a/src/unlayer/resources/__init__.py +++ b/src/unlayer/resources/__init__.py @@ -34,28 +34,28 @@ ) __all__ = [ - "EmailsResource", - "AsyncEmailsResource", - "EmailsResourceWithRawResponse", - "AsyncEmailsResourceWithRawResponse", - "EmailsResourceWithStreamingResponse", - "AsyncEmailsResourceWithStreamingResponse", "ProjectResource", "AsyncProjectResource", "ProjectResourceWithRawResponse", "AsyncProjectResourceWithRawResponse", "ProjectResourceWithStreamingResponse", "AsyncProjectResourceWithStreamingResponse", - "DocumentsResource", - "AsyncDocumentsResource", - "DocumentsResourceWithRawResponse", - "AsyncDocumentsResourceWithRawResponse", - "DocumentsResourceWithStreamingResponse", - "AsyncDocumentsResourceWithStreamingResponse", + "EmailsResource", + "AsyncEmailsResource", + "EmailsResourceWithRawResponse", + "AsyncEmailsResourceWithRawResponse", + "EmailsResourceWithStreamingResponse", + "AsyncEmailsResourceWithStreamingResponse", "PagesResource", "AsyncPagesResource", "PagesResourceWithRawResponse", "AsyncPagesResourceWithRawResponse", "PagesResourceWithStreamingResponse", "AsyncPagesResourceWithStreamingResponse", + "DocumentsResource", + "AsyncDocumentsResource", + "DocumentsResourceWithRawResponse", + "AsyncDocumentsResourceWithRawResponse", + "DocumentsResourceWithStreamingResponse", + "AsyncDocumentsResourceWithStreamingResponse", ] From 392bbacc18e0997a771469f09dd1897140cc78e9 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Thu, 8 Jan 2026 22:18:42 +0000 Subject: [PATCH 17/62] feat(api): api update --- .stats.yml | 6 +-- api.md | 64 +++++++++++++-------------- src/unlayer/_client.py | 72 +++++++++++++++---------------- src/unlayer/resources/__init__.py | 24 +++++------ 4 files changed, 83 insertions(+), 83 deletions(-) diff --git a/.stats.yml b/.stats.yml index 59b1ef8..5fdc6f9 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 24 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/unlayer%2Funlayer-737d922f62f9846af680567f50b999b1cec12674d24ae99e262ab1bcc0cbe311.yml -openapi_spec_hash: e1c7ddabf7b87df222fd4e268193d1dd -config_hash: 69225430c17187a9fea0a90e757affff +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/unlayer%2Funlayer-4d5fbaaec8cf4a1c283a9f901d09ebf13663fec824e68284f3aee09a861eb935.yml +openapi_spec_hash: 080ea72a1cecdca3399da83c5a9bfdd9 +config_hash: 22a4c98e4582bc0dddb1b2ae42fbccd0 diff --git a/api.md b/api.md index 8d63dc8..5ffc7b1 100644 --- a/api.md +++ b/api.md @@ -1,3 +1,35 @@ +# Pages + +Types: + +```python +from unlayer.types import PageRenderCreateResponse +``` + +Methods: + +- client.pages.render_create(\*\*params) -> PageRenderCreateResponse + +# Emails + +Types: + +```python +from unlayer.types import ( + EmailRetrieveResponse, + EmailRenderCreateResponse, + EmailSendCreateResponse, + EmailSendTemplateTemplateResponse, +) +``` + +Methods: + +- client.emails.retrieve(id) -> EmailRetrieveResponse +- client.emails.render_create(\*\*params) -> EmailRenderCreateResponse +- client.emails.send_create(\*\*params) -> EmailSendCreateResponse +- client.emails.send_template_template(\*\*params) -> EmailSendTemplateTemplateResponse + # Project Types: @@ -39,38 +71,6 @@ Methods: - client.project.templates_retrieve(id) -> ProjectTemplatesRetrieveResponse - client.project.templates_update(id, \*\*params) -> ProjectTemplatesUpdateResponse -# Emails - -Types: - -```python -from unlayer.types import ( - EmailRetrieveResponse, - EmailRenderCreateResponse, - EmailSendCreateResponse, - EmailSendTemplateTemplateResponse, -) -``` - -Methods: - -- client.emails.retrieve(id) -> EmailRetrieveResponse -- client.emails.render_create(\*\*params) -> EmailRenderCreateResponse -- client.emails.send_create(\*\*params) -> EmailSendCreateResponse -- client.emails.send_template_template(\*\*params) -> EmailSendTemplateTemplateResponse - -# Pages - -Types: - -```python -from unlayer.types import PageRenderCreateResponse -``` - -Methods: - -- client.pages.render_create(\*\*params) -> PageRenderCreateResponse - # Documents Types: diff --git a/src/unlayer/_client.py b/src/unlayer/_client.py index 8a4da8b..eb5b980 100644 --- a/src/unlayer/_client.py +++ b/src/unlayer/_client.py @@ -136,10 +136,10 @@ def __init__( ) @cached_property - def project(self) -> ProjectResource: - from .resources.project import ProjectResource + def pages(self) -> PagesResource: + from .resources.pages import PagesResource - return ProjectResource(self) + return PagesResource(self) @cached_property def emails(self) -> EmailsResource: @@ -148,10 +148,10 @@ def emails(self) -> EmailsResource: return EmailsResource(self) @cached_property - def pages(self) -> PagesResource: - from .resources.pages import PagesResource + def project(self) -> ProjectResource: + from .resources.project import ProjectResource - return PagesResource(self) + return ProjectResource(self) @cached_property def documents(self) -> DocumentsResource: @@ -354,10 +354,10 @@ def __init__( ) @cached_property - def project(self) -> AsyncProjectResource: - from .resources.project import AsyncProjectResource + def pages(self) -> AsyncPagesResource: + from .resources.pages import AsyncPagesResource - return AsyncProjectResource(self) + return AsyncPagesResource(self) @cached_property def emails(self) -> AsyncEmailsResource: @@ -366,10 +366,10 @@ def emails(self) -> AsyncEmailsResource: return AsyncEmailsResource(self) @cached_property - def pages(self) -> AsyncPagesResource: - from .resources.pages import AsyncPagesResource + def project(self) -> AsyncProjectResource: + from .resources.project import AsyncProjectResource - return AsyncPagesResource(self) + return AsyncProjectResource(self) @cached_property def documents(self) -> AsyncDocumentsResource: @@ -499,10 +499,10 @@ def __init__(self, client: Unlayer) -> None: self._client = client @cached_property - def project(self) -> project.ProjectResourceWithRawResponse: - from .resources.project import ProjectResourceWithRawResponse + def pages(self) -> pages.PagesResourceWithRawResponse: + from .resources.pages import PagesResourceWithRawResponse - return ProjectResourceWithRawResponse(self._client.project) + return PagesResourceWithRawResponse(self._client.pages) @cached_property def emails(self) -> emails.EmailsResourceWithRawResponse: @@ -511,10 +511,10 @@ def emails(self) -> emails.EmailsResourceWithRawResponse: return EmailsResourceWithRawResponse(self._client.emails) @cached_property - def pages(self) -> pages.PagesResourceWithRawResponse: - from .resources.pages import PagesResourceWithRawResponse + def project(self) -> project.ProjectResourceWithRawResponse: + from .resources.project import ProjectResourceWithRawResponse - return PagesResourceWithRawResponse(self._client.pages) + return ProjectResourceWithRawResponse(self._client.project) @cached_property def documents(self) -> documents.DocumentsResourceWithRawResponse: @@ -530,10 +530,10 @@ def __init__(self, client: AsyncUnlayer) -> None: self._client = client @cached_property - def project(self) -> project.AsyncProjectResourceWithRawResponse: - from .resources.project import AsyncProjectResourceWithRawResponse + def pages(self) -> pages.AsyncPagesResourceWithRawResponse: + from .resources.pages import AsyncPagesResourceWithRawResponse - return AsyncProjectResourceWithRawResponse(self._client.project) + return AsyncPagesResourceWithRawResponse(self._client.pages) @cached_property def emails(self) -> emails.AsyncEmailsResourceWithRawResponse: @@ -542,10 +542,10 @@ def emails(self) -> emails.AsyncEmailsResourceWithRawResponse: return AsyncEmailsResourceWithRawResponse(self._client.emails) @cached_property - def pages(self) -> pages.AsyncPagesResourceWithRawResponse: - from .resources.pages import AsyncPagesResourceWithRawResponse + def project(self) -> project.AsyncProjectResourceWithRawResponse: + from .resources.project import AsyncProjectResourceWithRawResponse - return AsyncPagesResourceWithRawResponse(self._client.pages) + return AsyncProjectResourceWithRawResponse(self._client.project) @cached_property def documents(self) -> documents.AsyncDocumentsResourceWithRawResponse: @@ -561,10 +561,10 @@ def __init__(self, client: Unlayer) -> None: self._client = client @cached_property - def project(self) -> project.ProjectResourceWithStreamingResponse: - from .resources.project import ProjectResourceWithStreamingResponse + def pages(self) -> pages.PagesResourceWithStreamingResponse: + from .resources.pages import PagesResourceWithStreamingResponse - return ProjectResourceWithStreamingResponse(self._client.project) + return PagesResourceWithStreamingResponse(self._client.pages) @cached_property def emails(self) -> emails.EmailsResourceWithStreamingResponse: @@ -573,10 +573,10 @@ def emails(self) -> emails.EmailsResourceWithStreamingResponse: return EmailsResourceWithStreamingResponse(self._client.emails) @cached_property - def pages(self) -> pages.PagesResourceWithStreamingResponse: - from .resources.pages import PagesResourceWithStreamingResponse + def project(self) -> project.ProjectResourceWithStreamingResponse: + from .resources.project import ProjectResourceWithStreamingResponse - return PagesResourceWithStreamingResponse(self._client.pages) + return ProjectResourceWithStreamingResponse(self._client.project) @cached_property def documents(self) -> documents.DocumentsResourceWithStreamingResponse: @@ -592,10 +592,10 @@ def __init__(self, client: AsyncUnlayer) -> None: self._client = client @cached_property - def project(self) -> project.AsyncProjectResourceWithStreamingResponse: - from .resources.project import AsyncProjectResourceWithStreamingResponse + def pages(self) -> pages.AsyncPagesResourceWithStreamingResponse: + from .resources.pages import AsyncPagesResourceWithStreamingResponse - return AsyncProjectResourceWithStreamingResponse(self._client.project) + return AsyncPagesResourceWithStreamingResponse(self._client.pages) @cached_property def emails(self) -> emails.AsyncEmailsResourceWithStreamingResponse: @@ -604,10 +604,10 @@ def emails(self) -> emails.AsyncEmailsResourceWithStreamingResponse: return AsyncEmailsResourceWithStreamingResponse(self._client.emails) @cached_property - def pages(self) -> pages.AsyncPagesResourceWithStreamingResponse: - from .resources.pages import AsyncPagesResourceWithStreamingResponse + def project(self) -> project.AsyncProjectResourceWithStreamingResponse: + from .resources.project import AsyncProjectResourceWithStreamingResponse - return AsyncPagesResourceWithStreamingResponse(self._client.pages) + return AsyncProjectResourceWithStreamingResponse(self._client.project) @cached_property def documents(self) -> documents.AsyncDocumentsResourceWithStreamingResponse: diff --git a/src/unlayer/resources/__init__.py b/src/unlayer/resources/__init__.py index 157be92..5eda3e5 100644 --- a/src/unlayer/resources/__init__.py +++ b/src/unlayer/resources/__init__.py @@ -34,24 +34,24 @@ ) __all__ = [ - "ProjectResource", - "AsyncProjectResource", - "ProjectResourceWithRawResponse", - "AsyncProjectResourceWithRawResponse", - "ProjectResourceWithStreamingResponse", - "AsyncProjectResourceWithStreamingResponse", - "EmailsResource", - "AsyncEmailsResource", - "EmailsResourceWithRawResponse", - "AsyncEmailsResourceWithRawResponse", - "EmailsResourceWithStreamingResponse", - "AsyncEmailsResourceWithStreamingResponse", "PagesResource", "AsyncPagesResource", "PagesResourceWithRawResponse", "AsyncPagesResourceWithRawResponse", "PagesResourceWithStreamingResponse", "AsyncPagesResourceWithStreamingResponse", + "EmailsResource", + "AsyncEmailsResource", + "EmailsResourceWithRawResponse", + "AsyncEmailsResourceWithRawResponse", + "EmailsResourceWithStreamingResponse", + "AsyncEmailsResourceWithStreamingResponse", + "ProjectResource", + "AsyncProjectResource", + "ProjectResourceWithRawResponse", + "AsyncProjectResourceWithRawResponse", + "ProjectResourceWithStreamingResponse", + "AsyncProjectResourceWithStreamingResponse", "DocumentsResource", "AsyncDocumentsResource", "DocumentsResourceWithRawResponse", From 3c269b0e92b94a3f49df1d81f3aea3da287414fd Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 13 Jan 2026 18:39:01 +0000 Subject: [PATCH 18/62] feat(api): api update --- .stats.yml | 6 +- api.md | 64 +++++++++--------- src/unlayer/_client.py | 108 +++++++++++++++--------------- src/unlayer/resources/__init__.py | 24 +++---- 4 files changed, 101 insertions(+), 101 deletions(-) diff --git a/.stats.yml b/.stats.yml index 5fdc6f9..74b0b57 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 24 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/unlayer%2Funlayer-4d5fbaaec8cf4a1c283a9f901d09ebf13663fec824e68284f3aee09a861eb935.yml -openapi_spec_hash: 080ea72a1cecdca3399da83c5a9bfdd9 -config_hash: 22a4c98e4582bc0dddb1b2ae42fbccd0 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/unlayer%2Funlayer-d746e93c3c920dca97596edec38c9ec25feef644db419156ae1b538bb54b6d72.yml +openapi_spec_hash: 437dce81b84c463ef9cc84dafa2ca92a +config_hash: 7495c5f2aebb250bf705cf2e6f4c1205 diff --git a/api.md b/api.md index 5ffc7b1..9856ee6 100644 --- a/api.md +++ b/api.md @@ -1,35 +1,3 @@ -# Pages - -Types: - -```python -from unlayer.types import PageRenderCreateResponse -``` - -Methods: - -- client.pages.render_create(\*\*params) -> PageRenderCreateResponse - -# Emails - -Types: - -```python -from unlayer.types import ( - EmailRetrieveResponse, - EmailRenderCreateResponse, - EmailSendCreateResponse, - EmailSendTemplateTemplateResponse, -) -``` - -Methods: - -- client.emails.retrieve(id) -> EmailRetrieveResponse -- client.emails.render_create(\*\*params) -> EmailRenderCreateResponse -- client.emails.send_create(\*\*params) -> EmailSendCreateResponse -- client.emails.send_template_template(\*\*params) -> EmailSendTemplateTemplateResponse - # Project Types: @@ -71,6 +39,26 @@ Methods: - client.project.templates_retrieve(id) -> ProjectTemplatesRetrieveResponse - client.project.templates_update(id, \*\*params) -> ProjectTemplatesUpdateResponse +# Emails + +Types: + +```python +from unlayer.types import ( + EmailRetrieveResponse, + EmailRenderCreateResponse, + EmailSendCreateResponse, + EmailSendTemplateTemplateResponse, +) +``` + +Methods: + +- client.emails.retrieve(id) -> EmailRetrieveResponse +- client.emails.render_create(\*\*params) -> EmailRenderCreateResponse +- client.emails.send_create(\*\*params) -> EmailSendCreateResponse +- client.emails.send_template_template(\*\*params) -> EmailSendTemplateTemplateResponse + # Documents Types: @@ -88,3 +76,15 @@ Methods: - client.documents.documents_retrieve(id) -> DocumentDocumentsRetrieveResponse - client.documents.generate_create(\*\*params) -> DocumentGenerateCreateResponse - client.documents.generate_template_template(\*\*params) -> DocumentGenerateTemplateTemplateResponse + +# Pages + +Types: + +```python +from unlayer.types import PageRenderCreateResponse +``` + +Methods: + +- client.pages.render_create(\*\*params) -> PageRenderCreateResponse diff --git a/src/unlayer/_client.py b/src/unlayer/_client.py index eb5b980..4b98994 100644 --- a/src/unlayer/_client.py +++ b/src/unlayer/_client.py @@ -136,10 +136,10 @@ def __init__( ) @cached_property - def pages(self) -> PagesResource: - from .resources.pages import PagesResource + def project(self) -> ProjectResource: + from .resources.project import ProjectResource - return PagesResource(self) + return ProjectResource(self) @cached_property def emails(self) -> EmailsResource: @@ -147,18 +147,18 @@ def emails(self) -> EmailsResource: return EmailsResource(self) - @cached_property - def project(self) -> ProjectResource: - from .resources.project import ProjectResource - - return ProjectResource(self) - @cached_property def documents(self) -> DocumentsResource: from .resources.documents import DocumentsResource return DocumentsResource(self) + @cached_property + def pages(self) -> PagesResource: + from .resources.pages import PagesResource + + return PagesResource(self) + @cached_property def with_raw_response(self) -> UnlayerWithRawResponse: return UnlayerWithRawResponse(self) @@ -354,10 +354,10 @@ def __init__( ) @cached_property - def pages(self) -> AsyncPagesResource: - from .resources.pages import AsyncPagesResource + def project(self) -> AsyncProjectResource: + from .resources.project import AsyncProjectResource - return AsyncPagesResource(self) + return AsyncProjectResource(self) @cached_property def emails(self) -> AsyncEmailsResource: @@ -365,18 +365,18 @@ def emails(self) -> AsyncEmailsResource: return AsyncEmailsResource(self) - @cached_property - def project(self) -> AsyncProjectResource: - from .resources.project import AsyncProjectResource - - return AsyncProjectResource(self) - @cached_property def documents(self) -> AsyncDocumentsResource: from .resources.documents import AsyncDocumentsResource return AsyncDocumentsResource(self) + @cached_property + def pages(self) -> AsyncPagesResource: + from .resources.pages import AsyncPagesResource + + return AsyncPagesResource(self) + @cached_property def with_raw_response(self) -> AsyncUnlayerWithRawResponse: return AsyncUnlayerWithRawResponse(self) @@ -499,10 +499,10 @@ def __init__(self, client: Unlayer) -> None: self._client = client @cached_property - def pages(self) -> pages.PagesResourceWithRawResponse: - from .resources.pages import PagesResourceWithRawResponse + def project(self) -> project.ProjectResourceWithRawResponse: + from .resources.project import ProjectResourceWithRawResponse - return PagesResourceWithRawResponse(self._client.pages) + return ProjectResourceWithRawResponse(self._client.project) @cached_property def emails(self) -> emails.EmailsResourceWithRawResponse: @@ -510,18 +510,18 @@ def emails(self) -> emails.EmailsResourceWithRawResponse: return EmailsResourceWithRawResponse(self._client.emails) - @cached_property - def project(self) -> project.ProjectResourceWithRawResponse: - from .resources.project import ProjectResourceWithRawResponse - - return ProjectResourceWithRawResponse(self._client.project) - @cached_property def documents(self) -> documents.DocumentsResourceWithRawResponse: from .resources.documents import DocumentsResourceWithRawResponse return DocumentsResourceWithRawResponse(self._client.documents) + @cached_property + def pages(self) -> pages.PagesResourceWithRawResponse: + from .resources.pages import PagesResourceWithRawResponse + + return PagesResourceWithRawResponse(self._client.pages) + class AsyncUnlayerWithRawResponse: _client: AsyncUnlayer @@ -530,10 +530,10 @@ def __init__(self, client: AsyncUnlayer) -> None: self._client = client @cached_property - def pages(self) -> pages.AsyncPagesResourceWithRawResponse: - from .resources.pages import AsyncPagesResourceWithRawResponse + def project(self) -> project.AsyncProjectResourceWithRawResponse: + from .resources.project import AsyncProjectResourceWithRawResponse - return AsyncPagesResourceWithRawResponse(self._client.pages) + return AsyncProjectResourceWithRawResponse(self._client.project) @cached_property def emails(self) -> emails.AsyncEmailsResourceWithRawResponse: @@ -541,18 +541,18 @@ def emails(self) -> emails.AsyncEmailsResourceWithRawResponse: return AsyncEmailsResourceWithRawResponse(self._client.emails) - @cached_property - def project(self) -> project.AsyncProjectResourceWithRawResponse: - from .resources.project import AsyncProjectResourceWithRawResponse - - return AsyncProjectResourceWithRawResponse(self._client.project) - @cached_property def documents(self) -> documents.AsyncDocumentsResourceWithRawResponse: from .resources.documents import AsyncDocumentsResourceWithRawResponse return AsyncDocumentsResourceWithRawResponse(self._client.documents) + @cached_property + def pages(self) -> pages.AsyncPagesResourceWithRawResponse: + from .resources.pages import AsyncPagesResourceWithRawResponse + + return AsyncPagesResourceWithRawResponse(self._client.pages) + class UnlayerWithStreamedResponse: _client: Unlayer @@ -561,10 +561,10 @@ def __init__(self, client: Unlayer) -> None: self._client = client @cached_property - def pages(self) -> pages.PagesResourceWithStreamingResponse: - from .resources.pages import PagesResourceWithStreamingResponse + def project(self) -> project.ProjectResourceWithStreamingResponse: + from .resources.project import ProjectResourceWithStreamingResponse - return PagesResourceWithStreamingResponse(self._client.pages) + return ProjectResourceWithStreamingResponse(self._client.project) @cached_property def emails(self) -> emails.EmailsResourceWithStreamingResponse: @@ -572,18 +572,18 @@ def emails(self) -> emails.EmailsResourceWithStreamingResponse: return EmailsResourceWithStreamingResponse(self._client.emails) - @cached_property - def project(self) -> project.ProjectResourceWithStreamingResponse: - from .resources.project import ProjectResourceWithStreamingResponse - - return ProjectResourceWithStreamingResponse(self._client.project) - @cached_property def documents(self) -> documents.DocumentsResourceWithStreamingResponse: from .resources.documents import DocumentsResourceWithStreamingResponse return DocumentsResourceWithStreamingResponse(self._client.documents) + @cached_property + def pages(self) -> pages.PagesResourceWithStreamingResponse: + from .resources.pages import PagesResourceWithStreamingResponse + + return PagesResourceWithStreamingResponse(self._client.pages) + class AsyncUnlayerWithStreamedResponse: _client: AsyncUnlayer @@ -592,10 +592,10 @@ def __init__(self, client: AsyncUnlayer) -> None: self._client = client @cached_property - def pages(self) -> pages.AsyncPagesResourceWithStreamingResponse: - from .resources.pages import AsyncPagesResourceWithStreamingResponse + def project(self) -> project.AsyncProjectResourceWithStreamingResponse: + from .resources.project import AsyncProjectResourceWithStreamingResponse - return AsyncPagesResourceWithStreamingResponse(self._client.pages) + return AsyncProjectResourceWithStreamingResponse(self._client.project) @cached_property def emails(self) -> emails.AsyncEmailsResourceWithStreamingResponse: @@ -603,18 +603,18 @@ def emails(self) -> emails.AsyncEmailsResourceWithStreamingResponse: return AsyncEmailsResourceWithStreamingResponse(self._client.emails) - @cached_property - def project(self) -> project.AsyncProjectResourceWithStreamingResponse: - from .resources.project import AsyncProjectResourceWithStreamingResponse - - return AsyncProjectResourceWithStreamingResponse(self._client.project) - @cached_property def documents(self) -> documents.AsyncDocumentsResourceWithStreamingResponse: from .resources.documents import AsyncDocumentsResourceWithStreamingResponse return AsyncDocumentsResourceWithStreamingResponse(self._client.documents) + @cached_property + def pages(self) -> pages.AsyncPagesResourceWithStreamingResponse: + from .resources.pages import AsyncPagesResourceWithStreamingResponse + + return AsyncPagesResourceWithStreamingResponse(self._client.pages) + Client = Unlayer diff --git a/src/unlayer/resources/__init__.py b/src/unlayer/resources/__init__.py index 5eda3e5..b1a13d2 100644 --- a/src/unlayer/resources/__init__.py +++ b/src/unlayer/resources/__init__.py @@ -34,28 +34,28 @@ ) __all__ = [ - "PagesResource", - "AsyncPagesResource", - "PagesResourceWithRawResponse", - "AsyncPagesResourceWithRawResponse", - "PagesResourceWithStreamingResponse", - "AsyncPagesResourceWithStreamingResponse", - "EmailsResource", - "AsyncEmailsResource", - "EmailsResourceWithRawResponse", - "AsyncEmailsResourceWithRawResponse", - "EmailsResourceWithStreamingResponse", - "AsyncEmailsResourceWithStreamingResponse", "ProjectResource", "AsyncProjectResource", "ProjectResourceWithRawResponse", "AsyncProjectResourceWithRawResponse", "ProjectResourceWithStreamingResponse", "AsyncProjectResourceWithStreamingResponse", + "EmailsResource", + "AsyncEmailsResource", + "EmailsResourceWithRawResponse", + "AsyncEmailsResourceWithRawResponse", + "EmailsResourceWithStreamingResponse", + "AsyncEmailsResourceWithStreamingResponse", "DocumentsResource", "AsyncDocumentsResource", "DocumentsResourceWithRawResponse", "AsyncDocumentsResourceWithRawResponse", "DocumentsResourceWithStreamingResponse", "AsyncDocumentsResourceWithStreamingResponse", + "PagesResource", + "AsyncPagesResource", + "PagesResourceWithRawResponse", + "AsyncPagesResourceWithRawResponse", + "PagesResourceWithStreamingResponse", + "AsyncPagesResourceWithStreamingResponse", ] From b68c23df421990e3ad98ae60d182fc22f25b49ae Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Wed, 14 Jan 2026 04:43:22 +0000 Subject: [PATCH 19/62] feat(client): add support for binary request streaming --- src/unlayer/_base_client.py | 145 +++++++++++++++++++++++++--- src/unlayer/_models.py | 17 +++- src/unlayer/_types.py | 9 ++ tests/test_client.py | 187 +++++++++++++++++++++++++++++++++++- 4 files changed, 344 insertions(+), 14 deletions(-) diff --git a/src/unlayer/_base_client.py b/src/unlayer/_base_client.py index bbc2e91..7f21ba1 100644 --- a/src/unlayer/_base_client.py +++ b/src/unlayer/_base_client.py @@ -9,6 +9,7 @@ import inspect import logging import platform +import warnings import email.utils from types import TracebackType from random import random @@ -51,9 +52,11 @@ ResponseT, AnyMapping, PostParser, + BinaryTypes, RequestFiles, HttpxSendArgs, RequestOptions, + AsyncBinaryTypes, HttpxRequestFiles, ModelBuilderProtocol, not_given, @@ -477,8 +480,19 @@ def _build_request( retries_taken: int = 0, ) -> httpx.Request: if log.isEnabledFor(logging.DEBUG): - log.debug("Request options: %s", model_dump(options, exclude_unset=True)) - + log.debug( + "Request options: %s", + model_dump( + options, + exclude_unset=True, + # Pydantic v1 can't dump every type we support in content, so we exclude it for now. + exclude={ + "content", + } + if PYDANTIC_V1 + else {}, + ), + ) kwargs: dict[str, Any] = {} json_data = options.json_data @@ -532,7 +546,13 @@ def _build_request( is_body_allowed = options.method.lower() != "get" if is_body_allowed: - if isinstance(json_data, bytes): + if options.content is not None and json_data is not None: + raise TypeError("Passing both `content` and `json_data` is not supported") + if options.content is not None and files is not None: + raise TypeError("Passing both `content` and `files` is not supported") + if options.content is not None: + kwargs["content"] = options.content + elif isinstance(json_data, bytes): kwargs["content"] = json_data else: kwargs["json"] = json_data if is_given(json_data) else None @@ -1194,6 +1214,7 @@ def post( *, cast_to: Type[ResponseT], body: Body | None = None, + content: BinaryTypes | None = None, options: RequestOptions = {}, files: RequestFiles | None = None, stream: Literal[False] = False, @@ -1206,6 +1227,7 @@ def post( *, cast_to: Type[ResponseT], body: Body | None = None, + content: BinaryTypes | None = None, options: RequestOptions = {}, files: RequestFiles | None = None, stream: Literal[True], @@ -1219,6 +1241,7 @@ def post( *, cast_to: Type[ResponseT], body: Body | None = None, + content: BinaryTypes | None = None, options: RequestOptions = {}, files: RequestFiles | None = None, stream: bool, @@ -1231,13 +1254,25 @@ def post( *, cast_to: Type[ResponseT], body: Body | None = None, + content: BinaryTypes | None = None, options: RequestOptions = {}, files: RequestFiles | None = None, stream: bool = False, stream_cls: type[_StreamT] | None = None, ) -> ResponseT | _StreamT: + if body is not None and content is not None: + raise TypeError("Passing both `body` and `content` is not supported") + if files is not None and content is not None: + raise TypeError("Passing both `files` and `content` is not supported") + if isinstance(body, bytes): + warnings.warn( + "Passing raw bytes as `body` is deprecated and will be removed in a future version. " + "Please pass raw bytes via the `content` parameter instead.", + DeprecationWarning, + stacklevel=2, + ) opts = FinalRequestOptions.construct( - method="post", url=path, json_data=body, files=to_httpx_files(files), **options + method="post", url=path, json_data=body, content=content, files=to_httpx_files(files), **options ) return cast(ResponseT, self.request(cast_to, opts, stream=stream, stream_cls=stream_cls)) @@ -1247,11 +1282,23 @@ def patch( *, cast_to: Type[ResponseT], body: Body | None = None, + content: BinaryTypes | None = None, files: RequestFiles | None = None, options: RequestOptions = {}, ) -> ResponseT: + if body is not None and content is not None: + raise TypeError("Passing both `body` and `content` is not supported") + if files is not None and content is not None: + raise TypeError("Passing both `files` and `content` is not supported") + if isinstance(body, bytes): + warnings.warn( + "Passing raw bytes as `body` is deprecated and will be removed in a future version. " + "Please pass raw bytes via the `content` parameter instead.", + DeprecationWarning, + stacklevel=2, + ) opts = FinalRequestOptions.construct( - method="patch", url=path, json_data=body, files=to_httpx_files(files), **options + method="patch", url=path, json_data=body, content=content, files=to_httpx_files(files), **options ) return self.request(cast_to, opts) @@ -1261,11 +1308,23 @@ def put( *, cast_to: Type[ResponseT], body: Body | None = None, + content: BinaryTypes | None = None, files: RequestFiles | None = None, options: RequestOptions = {}, ) -> ResponseT: + if body is not None and content is not None: + raise TypeError("Passing both `body` and `content` is not supported") + if files is not None and content is not None: + raise TypeError("Passing both `files` and `content` is not supported") + if isinstance(body, bytes): + warnings.warn( + "Passing raw bytes as `body` is deprecated and will be removed in a future version. " + "Please pass raw bytes via the `content` parameter instead.", + DeprecationWarning, + stacklevel=2, + ) opts = FinalRequestOptions.construct( - method="put", url=path, json_data=body, files=to_httpx_files(files), **options + method="put", url=path, json_data=body, content=content, files=to_httpx_files(files), **options ) return self.request(cast_to, opts) @@ -1275,9 +1334,19 @@ def delete( *, cast_to: Type[ResponseT], body: Body | None = None, + content: BinaryTypes | None = None, options: RequestOptions = {}, ) -> ResponseT: - opts = FinalRequestOptions.construct(method="delete", url=path, json_data=body, **options) + if body is not None and content is not None: + raise TypeError("Passing both `body` and `content` is not supported") + if isinstance(body, bytes): + warnings.warn( + "Passing raw bytes as `body` is deprecated and will be removed in a future version. " + "Please pass raw bytes via the `content` parameter instead.", + DeprecationWarning, + stacklevel=2, + ) + opts = FinalRequestOptions.construct(method="delete", url=path, json_data=body, content=content, **options) return self.request(cast_to, opts) def get_api_list( @@ -1717,6 +1786,7 @@ async def post( *, cast_to: Type[ResponseT], body: Body | None = None, + content: AsyncBinaryTypes | None = None, files: RequestFiles | None = None, options: RequestOptions = {}, stream: Literal[False] = False, @@ -1729,6 +1799,7 @@ async def post( *, cast_to: Type[ResponseT], body: Body | None = None, + content: AsyncBinaryTypes | None = None, files: RequestFiles | None = None, options: RequestOptions = {}, stream: Literal[True], @@ -1742,6 +1813,7 @@ async def post( *, cast_to: Type[ResponseT], body: Body | None = None, + content: AsyncBinaryTypes | None = None, files: RequestFiles | None = None, options: RequestOptions = {}, stream: bool, @@ -1754,13 +1826,25 @@ async def post( *, cast_to: Type[ResponseT], body: Body | None = None, + content: AsyncBinaryTypes | None = None, files: RequestFiles | None = None, options: RequestOptions = {}, stream: bool = False, stream_cls: type[_AsyncStreamT] | None = None, ) -> ResponseT | _AsyncStreamT: + if body is not None and content is not None: + raise TypeError("Passing both `body` and `content` is not supported") + if files is not None and content is not None: + raise TypeError("Passing both `files` and `content` is not supported") + if isinstance(body, bytes): + warnings.warn( + "Passing raw bytes as `body` is deprecated and will be removed in a future version. " + "Please pass raw bytes via the `content` parameter instead.", + DeprecationWarning, + stacklevel=2, + ) opts = FinalRequestOptions.construct( - method="post", url=path, json_data=body, files=await async_to_httpx_files(files), **options + method="post", url=path, json_data=body, content=content, files=await async_to_httpx_files(files), **options ) return await self.request(cast_to, opts, stream=stream, stream_cls=stream_cls) @@ -1770,11 +1854,28 @@ async def patch( *, cast_to: Type[ResponseT], body: Body | None = None, + content: AsyncBinaryTypes | None = None, files: RequestFiles | None = None, options: RequestOptions = {}, ) -> ResponseT: + if body is not None and content is not None: + raise TypeError("Passing both `body` and `content` is not supported") + if files is not None and content is not None: + raise TypeError("Passing both `files` and `content` is not supported") + if isinstance(body, bytes): + warnings.warn( + "Passing raw bytes as `body` is deprecated and will be removed in a future version. " + "Please pass raw bytes via the `content` parameter instead.", + DeprecationWarning, + stacklevel=2, + ) opts = FinalRequestOptions.construct( - method="patch", url=path, json_data=body, files=await async_to_httpx_files(files), **options + method="patch", + url=path, + json_data=body, + content=content, + files=await async_to_httpx_files(files), + **options, ) return await self.request(cast_to, opts) @@ -1784,11 +1885,23 @@ async def put( *, cast_to: Type[ResponseT], body: Body | None = None, + content: AsyncBinaryTypes | None = None, files: RequestFiles | None = None, options: RequestOptions = {}, ) -> ResponseT: + if body is not None and content is not None: + raise TypeError("Passing both `body` and `content` is not supported") + if files is not None and content is not None: + raise TypeError("Passing both `files` and `content` is not supported") + if isinstance(body, bytes): + warnings.warn( + "Passing raw bytes as `body` is deprecated and will be removed in a future version. " + "Please pass raw bytes via the `content` parameter instead.", + DeprecationWarning, + stacklevel=2, + ) opts = FinalRequestOptions.construct( - method="put", url=path, json_data=body, files=await async_to_httpx_files(files), **options + method="put", url=path, json_data=body, content=content, files=await async_to_httpx_files(files), **options ) return await self.request(cast_to, opts) @@ -1798,9 +1911,19 @@ async def delete( *, cast_to: Type[ResponseT], body: Body | None = None, + content: AsyncBinaryTypes | None = None, options: RequestOptions = {}, ) -> ResponseT: - opts = FinalRequestOptions.construct(method="delete", url=path, json_data=body, **options) + if body is not None and content is not None: + raise TypeError("Passing both `body` and `content` is not supported") + if isinstance(body, bytes): + warnings.warn( + "Passing raw bytes as `body` is deprecated and will be removed in a future version. " + "Please pass raw bytes via the `content` parameter instead.", + DeprecationWarning, + stacklevel=2, + ) + opts = FinalRequestOptions.construct(method="delete", url=path, json_data=body, content=content, **options) return await self.request(cast_to, opts) def get_api_list( diff --git a/src/unlayer/_models.py b/src/unlayer/_models.py index ca9500b..29070e0 100644 --- a/src/unlayer/_models.py +++ b/src/unlayer/_models.py @@ -3,7 +3,20 @@ import os import inspect import weakref -from typing import TYPE_CHECKING, Any, Type, Union, Generic, TypeVar, Callable, Optional, cast +from typing import ( + IO, + TYPE_CHECKING, + Any, + Type, + Union, + Generic, + TypeVar, + Callable, + Iterable, + Optional, + AsyncIterable, + cast, +) from datetime import date, datetime from typing_extensions import ( List, @@ -787,6 +800,7 @@ class FinalRequestOptionsInput(TypedDict, total=False): timeout: float | Timeout | None files: HttpxRequestFiles | None idempotency_key: str + content: Union[bytes, bytearray, IO[bytes], Iterable[bytes], AsyncIterable[bytes], None] json_data: Body extra_json: AnyMapping follow_redirects: bool @@ -805,6 +819,7 @@ class FinalRequestOptions(pydantic.BaseModel): post_parser: Union[Callable[[Any], Any], NotGiven] = NotGiven() follow_redirects: Union[bool, None] = None + content: Union[bytes, bytearray, IO[bytes], Iterable[bytes], AsyncIterable[bytes], None] = None # It should be noted that we cannot use `json` here as that would override # a BaseModel method in an incompatible fashion. json_data: Union[Body, None] = None diff --git a/src/unlayer/_types.py b/src/unlayer/_types.py index 067c3d4..a25667d 100644 --- a/src/unlayer/_types.py +++ b/src/unlayer/_types.py @@ -13,9 +13,11 @@ Mapping, TypeVar, Callable, + Iterable, Iterator, Optional, Sequence, + AsyncIterable, ) from typing_extensions import ( Set, @@ -56,6 +58,13 @@ else: Base64FileInput = Union[IO[bytes], PathLike] FileContent = Union[IO[bytes], bytes, PathLike] # PathLike is not subscriptable in Python 3.8. + + +# Used for sending raw binary data / streaming data in request bodies +# e.g. for file uploads without multipart encoding +BinaryTypes = Union[bytes, bytearray, IO[bytes], Iterable[bytes]] +AsyncBinaryTypes = Union[bytes, bytearray, IO[bytes], AsyncIterable[bytes]] + FileTypes = Union[ # file (or bytes) FileContent, diff --git a/tests/test_client.py b/tests/test_client.py index d120c95..e53628e 100644 --- a/tests/test_client.py +++ b/tests/test_client.py @@ -8,10 +8,11 @@ import json import asyncio import inspect +import dataclasses import tracemalloc -from typing import Any, Union, cast +from typing import Any, Union, TypeVar, Callable, Iterable, Iterator, Optional, Coroutine, cast from unittest import mock -from typing_extensions import Literal +from typing_extensions import Literal, AsyncIterator, override import httpx import pytest @@ -36,6 +37,7 @@ from .utils import update_env +T = TypeVar("T") base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") api_key = "My API Key" @@ -50,6 +52,57 @@ def _low_retry_timeout(*_args: Any, **_kwargs: Any) -> float: return 0.1 +def mirror_request_content(request: httpx.Request) -> httpx.Response: + return httpx.Response(200, content=request.content) + + +# note: we can't use the httpx.MockTransport class as it consumes the request +# body itself, which means we can't test that the body is read lazily +class MockTransport(httpx.BaseTransport, httpx.AsyncBaseTransport): + def __init__( + self, + handler: Callable[[httpx.Request], httpx.Response] + | Callable[[httpx.Request], Coroutine[Any, Any, httpx.Response]], + ) -> None: + self.handler = handler + + @override + def handle_request( + self, + request: httpx.Request, + ) -> httpx.Response: + assert not inspect.iscoroutinefunction(self.handler), "handler must not be a coroutine function" + assert inspect.isfunction(self.handler), "handler must be a function" + return self.handler(request) + + @override + async def handle_async_request( + self, + request: httpx.Request, + ) -> httpx.Response: + assert inspect.iscoroutinefunction(self.handler), "handler must be a coroutine function" + return await self.handler(request) + + +@dataclasses.dataclass +class Counter: + value: int = 0 + + +def _make_sync_iterator(iterable: Iterable[T], counter: Optional[Counter] = None) -> Iterator[T]: + for item in iterable: + if counter: + counter.value += 1 + yield item + + +async def _make_async_iterator(iterable: Iterable[T], counter: Optional[Counter] = None) -> AsyncIterator[T]: + for item in iterable: + if counter: + counter.value += 1 + yield item + + def _get_open_connections(client: Unlayer | AsyncUnlayer) -> int: transport = client._client._transport assert isinstance(transport, httpx.HTTPTransport) or isinstance(transport, httpx.AsyncHTTPTransport) @@ -500,6 +553,70 @@ def test_multipart_repeating_array(self, client: Unlayer) -> None: b"", ] + @pytest.mark.respx(base_url=base_url) + def test_binary_content_upload(self, respx_mock: MockRouter, client: Unlayer) -> None: + respx_mock.post("/upload").mock(side_effect=mirror_request_content) + + file_content = b"Hello, this is a test file." + + response = client.post( + "/upload", + content=file_content, + cast_to=httpx.Response, + options={"headers": {"Content-Type": "application/octet-stream"}}, + ) + + assert response.status_code == 200 + assert response.request.headers["Content-Type"] == "application/octet-stream" + assert response.content == file_content + + def test_binary_content_upload_with_iterator(self) -> None: + file_content = b"Hello, this is a test file." + counter = Counter() + iterator = _make_sync_iterator([file_content], counter=counter) + + def mock_handler(request: httpx.Request) -> httpx.Response: + assert counter.value == 0, "the request body should not have been read" + return httpx.Response(200, content=request.read()) + + with Unlayer( + base_url=base_url, + api_key=api_key, + _strict_response_validation=True, + http_client=httpx.Client(transport=MockTransport(handler=mock_handler)), + ) as client: + response = client.post( + "/upload", + content=iterator, + cast_to=httpx.Response, + options={"headers": {"Content-Type": "application/octet-stream"}}, + ) + + assert response.status_code == 200 + assert response.request.headers["Content-Type"] == "application/octet-stream" + assert response.content == file_content + assert counter.value == 1 + + @pytest.mark.respx(base_url=base_url) + def test_binary_content_upload_with_body_is_deprecated(self, respx_mock: MockRouter, client: Unlayer) -> None: + respx_mock.post("/upload").mock(side_effect=mirror_request_content) + + file_content = b"Hello, this is a test file." + + with pytest.deprecated_call( + match="Passing raw bytes as `body` is deprecated and will be removed in a future version. Please pass raw bytes via the `content` parameter instead." + ): + response = client.post( + "/upload", + body=file_content, + cast_to=httpx.Response, + options={"headers": {"Content-Type": "application/octet-stream"}}, + ) + + assert response.status_code == 200 + assert response.request.headers["Content-Type"] == "application/octet-stream" + assert response.content == file_content + @pytest.mark.respx(base_url=base_url) def test_basic_union_response(self, respx_mock: MockRouter, client: Unlayer) -> None: class Model1(BaseModel): @@ -1329,6 +1446,72 @@ def test_multipart_repeating_array(self, async_client: AsyncUnlayer) -> None: b"", ] + @pytest.mark.respx(base_url=base_url) + async def test_binary_content_upload(self, respx_mock: MockRouter, async_client: AsyncUnlayer) -> None: + respx_mock.post("/upload").mock(side_effect=mirror_request_content) + + file_content = b"Hello, this is a test file." + + response = await async_client.post( + "/upload", + content=file_content, + cast_to=httpx.Response, + options={"headers": {"Content-Type": "application/octet-stream"}}, + ) + + assert response.status_code == 200 + assert response.request.headers["Content-Type"] == "application/octet-stream" + assert response.content == file_content + + async def test_binary_content_upload_with_asynciterator(self) -> None: + file_content = b"Hello, this is a test file." + counter = Counter() + iterator = _make_async_iterator([file_content], counter=counter) + + async def mock_handler(request: httpx.Request) -> httpx.Response: + assert counter.value == 0, "the request body should not have been read" + return httpx.Response(200, content=await request.aread()) + + async with AsyncUnlayer( + base_url=base_url, + api_key=api_key, + _strict_response_validation=True, + http_client=httpx.AsyncClient(transport=MockTransport(handler=mock_handler)), + ) as client: + response = await client.post( + "/upload", + content=iterator, + cast_to=httpx.Response, + options={"headers": {"Content-Type": "application/octet-stream"}}, + ) + + assert response.status_code == 200 + assert response.request.headers["Content-Type"] == "application/octet-stream" + assert response.content == file_content + assert counter.value == 1 + + @pytest.mark.respx(base_url=base_url) + async def test_binary_content_upload_with_body_is_deprecated( + self, respx_mock: MockRouter, async_client: AsyncUnlayer + ) -> None: + respx_mock.post("/upload").mock(side_effect=mirror_request_content) + + file_content = b"Hello, this is a test file." + + with pytest.deprecated_call( + match="Passing raw bytes as `body` is deprecated and will be removed in a future version. Please pass raw bytes via the `content` parameter instead." + ): + response = await async_client.post( + "/upload", + body=file_content, + cast_to=httpx.Response, + options={"headers": {"Content-Type": "application/octet-stream"}}, + ) + + assert response.status_code == 200 + assert response.request.headers["Content-Type"] == "application/octet-stream" + assert response.content == file_content + @pytest.mark.respx(base_url=base_url) async def test_basic_union_response(self, respx_mock: MockRouter, async_client: AsyncUnlayer) -> None: class Model1(BaseModel): From 1d1ca4693cf8af1417920907408c87b53f4495f4 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Thu, 15 Jan 2026 15:46:59 +0000 Subject: [PATCH 20/62] feat(api): api update --- .stats.yml | 8 +- README.md | 32 +- api.md | 98 ++-- src/unlayer/_client.py | 108 ++-- src/unlayer/resources/__init__.py | 24 +- src/unlayer/resources/documents.py | 74 ++- src/unlayer/resources/emails.py | 89 ++- src/unlayer/resources/pages.py | 20 +- src/unlayer/resources/project.py | 520 +++++++++++++++++- src/unlayer/types/__init__.py | 10 + .../document_documents_retrieve_params.py | 14 + .../types/document_generate_create_params.py | 3 + ...ument_generate_template_template_params.py | 3 + .../types/email_render_create_params.py | 3 + src/unlayer/types/email_retrieve_params.py | 14 + src/unlayer/types/email_send_create_params.py | 3 + .../email_send_template_template_params.py | 3 + .../types/page_render_create_params.py | 3 + .../types/project_api_keys_create_params.py | 6 +- .../types/project_api_keys_list_params.py | 14 + .../types/project_current_list_params.py | 14 + .../types/project_domains_create_params.py | 7 +- .../types/project_domains_list_params.py | 14 + .../types/project_templates_create_params.py | 7 +- .../types/project_templates_list_params.py | 14 + .../types/project_tokens_delete_response.py | 13 + .../types/project_tokens_list_response.py | 32 ++ .../types/project_workspaces_list_response.py | 17 + .../project_workspaces_retrieve_response.py | 27 + tests/api_resources/test_documents.py | 36 +- tests/api_resources/test_emails.py | 38 +- tests/api_resources/test_pages.py | 2 + tests/api_resources/test_project.py | 374 ++++++++++++- tests/test_client.py | 26 +- 34 files changed, 1454 insertions(+), 216 deletions(-) create mode 100644 src/unlayer/types/document_documents_retrieve_params.py create mode 100644 src/unlayer/types/email_retrieve_params.py create mode 100644 src/unlayer/types/project_api_keys_list_params.py create mode 100644 src/unlayer/types/project_current_list_params.py create mode 100644 src/unlayer/types/project_domains_list_params.py create mode 100644 src/unlayer/types/project_templates_list_params.py create mode 100644 src/unlayer/types/project_tokens_delete_response.py create mode 100644 src/unlayer/types/project_tokens_list_response.py create mode 100644 src/unlayer/types/project_workspaces_list_response.py create mode 100644 src/unlayer/types/project_workspaces_retrieve_response.py diff --git a/.stats.yml b/.stats.yml index 74b0b57..97858dc 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ -configured_endpoints: 24 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/unlayer%2Funlayer-d746e93c3c920dca97596edec38c9ec25feef644db419156ae1b538bb54b6d72.yml -openapi_spec_hash: 437dce81b84c463ef9cc84dafa2ca92a -config_hash: 7495c5f2aebb250bf705cf2e6f4c1205 +configured_endpoints: 28 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/unlayer%2Funlayer-d89823a559be3efaaedf95ef21523f77342db1eb9bd708015fac22f68bcfd8ae.yml +openapi_spec_hash: fda98646b156fdd53a03d433cbacad20 +config_hash: ad8a4186026c9854e45139d7c4a20090 diff --git a/README.md b/README.md index 0fae0ad..b40986b 100644 --- a/README.md +++ b/README.md @@ -37,7 +37,9 @@ client = Unlayer( environment="qa", ) -response = client.project.current_list() +response = client.project.current_list( + project_id="projectId", +) print(response.data) ``` @@ -63,7 +65,9 @@ client = AsyncUnlayer( async def main() -> None: - response = await client.project.current_list() + response = await client.project.current_list( + project_id="projectId", + ) print(response.data) @@ -97,7 +101,9 @@ async def main() -> None: api_key=os.environ.get("UNLAYER_API_KEY"), # This is the default and can be omitted http_client=DefaultAioHttpClient(), ) as client: - response = await client.project.current_list() + response = await client.project.current_list( + project_id="projectId", + ) print(response.data) @@ -129,7 +135,9 @@ from unlayer import Unlayer client = Unlayer() try: - client.project.current_list() + client.project.current_list( + project_id="projectId", + ) except unlayer.APIConnectionError as e: print("The server could not be reached") print(e.__cause__) # an underlying Exception, likely raised within httpx. @@ -172,7 +180,9 @@ client = Unlayer( ) # Or, configure per-request: -client.with_options(max_retries=5).project.current_list() +client.with_options(max_retries=5).project.current_list( + project_id="projectId", +) ``` ### Timeouts @@ -195,7 +205,9 @@ client = Unlayer( ) # Override per-request: -client.with_options(timeout=5.0).project.current_list() +client.with_options(timeout=5.0).project.current_list( + project_id="projectId", +) ``` On timeout, an `APITimeoutError` is thrown. @@ -236,7 +248,9 @@ The "raw" Response object can be accessed by prefixing `.with_raw_response.` to from unlayer import Unlayer client = Unlayer() -response = client.project.with_raw_response.current_list() +response = client.project.with_raw_response.current_list( + project_id="projectId", +) print(response.headers.get('X-My-Header')) project = response.parse() # get the object that `project.current_list()` would have returned @@ -254,7 +268,9 @@ The above interface eagerly reads the full response body when you make the reque To stream the response body, use `.with_streaming_response` instead, which requires a context manager and only reads the response body once you call `.read()`, `.text()`, `.json()`, `.iter_bytes()`, `.iter_text()`, `.iter_lines()` or `.parse()`. In the async client, these are async methods. ```python -with client.project.with_streaming_response.current_list() as response: +with client.project.with_streaming_response.current_list( + project_id="projectId", +) as response: print(response.headers.get("X-My-Header")) for line in response.iter_lines(): diff --git a/api.md b/api.md index 9856ee6..a04e322 100644 --- a/api.md +++ b/api.md @@ -1,63 +1,34 @@ -# Project +# Emails Types: ```python from unlayer.types import ( - ProjectAPIKeysCreateResponse, - ProjectAPIKeysListResponse, - ProjectAPIKeysRetrieveResponse, - ProjectAPIKeysUpdateResponse, - ProjectCurrentListResponse, - ProjectDomainsCreateResponse, - ProjectDomainsListResponse, - ProjectDomainsRetrieveResponse, - ProjectDomainsUpdateResponse, - ProjectTemplatesCreateResponse, - ProjectTemplatesListResponse, - ProjectTemplatesRetrieveResponse, - ProjectTemplatesUpdateResponse, + EmailRetrieveResponse, + EmailRenderCreateResponse, + EmailSendCreateResponse, + EmailSendTemplateTemplateResponse, ) ``` Methods: -- client.project.api_keys_create(\*\*params) -> ProjectAPIKeysCreateResponse -- client.project.api_keys_delete(id) -> None -- client.project.api_keys_list() -> ProjectAPIKeysListResponse -- client.project.api_keys_retrieve(id) -> ProjectAPIKeysRetrieveResponse -- client.project.api_keys_update(id, \*\*params) -> ProjectAPIKeysUpdateResponse -- client.project.current_list() -> ProjectCurrentListResponse -- client.project.domains_create(\*\*params) -> ProjectDomainsCreateResponse -- client.project.domains_delete(id) -> None -- client.project.domains_list() -> ProjectDomainsListResponse -- client.project.domains_retrieve(id) -> ProjectDomainsRetrieveResponse -- client.project.domains_update(id, \*\*params) -> ProjectDomainsUpdateResponse -- client.project.templates_create(\*\*params) -> ProjectTemplatesCreateResponse -- client.project.templates_delete(id) -> None -- client.project.templates_list() -> ProjectTemplatesListResponse -- client.project.templates_retrieve(id) -> ProjectTemplatesRetrieveResponse -- client.project.templates_update(id, \*\*params) -> ProjectTemplatesUpdateResponse +- client.emails.retrieve(id, \*\*params) -> EmailRetrieveResponse +- client.emails.render_create(\*\*params) -> EmailRenderCreateResponse +- client.emails.send_create(\*\*params) -> EmailSendCreateResponse +- client.emails.send_template_template(\*\*params) -> EmailSendTemplateTemplateResponse -# Emails +# Pages Types: ```python -from unlayer.types import ( - EmailRetrieveResponse, - EmailRenderCreateResponse, - EmailSendCreateResponse, - EmailSendTemplateTemplateResponse, -) +from unlayer.types import PageRenderCreateResponse ``` Methods: -- client.emails.retrieve(id) -> EmailRetrieveResponse -- client.emails.render_create(\*\*params) -> EmailRenderCreateResponse -- client.emails.send_create(\*\*params) -> EmailSendCreateResponse -- client.emails.send_template_template(\*\*params) -> EmailSendTemplateTemplateResponse +- client.pages.render_create(\*\*params) -> PageRenderCreateResponse # Documents @@ -73,18 +44,55 @@ from unlayer.types import ( Methods: -- client.documents.documents_retrieve(id) -> DocumentDocumentsRetrieveResponse +- client.documents.documents_retrieve(id, \*\*params) -> DocumentDocumentsRetrieveResponse - client.documents.generate_create(\*\*params) -> DocumentGenerateCreateResponse - client.documents.generate_template_template(\*\*params) -> DocumentGenerateTemplateTemplateResponse -# Pages +# Project Types: ```python -from unlayer.types import PageRenderCreateResponse +from unlayer.types import ( + ProjectAPIKeysCreateResponse, + ProjectAPIKeysListResponse, + ProjectAPIKeysRetrieveResponse, + ProjectAPIKeysUpdateResponse, + ProjectCurrentListResponse, + ProjectDomainsCreateResponse, + ProjectDomainsListResponse, + ProjectDomainsRetrieveResponse, + ProjectDomainsUpdateResponse, + ProjectTemplatesCreateResponse, + ProjectTemplatesListResponse, + ProjectTemplatesRetrieveResponse, + ProjectTemplatesUpdateResponse, + ProjectTokensDeleteResponse, + ProjectTokensListResponse, + ProjectWorkspacesListResponse, + ProjectWorkspacesRetrieveResponse, +) ``` Methods: -- client.pages.render_create(\*\*params) -> PageRenderCreateResponse +- client.project.api_keys_create(\*\*params) -> ProjectAPIKeysCreateResponse +- client.project.api_keys_delete(id) -> None +- client.project.api_keys_list(\*\*params) -> ProjectAPIKeysListResponse +- client.project.api_keys_retrieve(id) -> ProjectAPIKeysRetrieveResponse +- client.project.api_keys_update(id, \*\*params) -> ProjectAPIKeysUpdateResponse +- client.project.current_list(\*\*params) -> ProjectCurrentListResponse +- client.project.domains_create(\*\*params) -> ProjectDomainsCreateResponse +- client.project.domains_delete(id) -> None +- client.project.domains_list(\*\*params) -> ProjectDomainsListResponse +- client.project.domains_retrieve(id) -> ProjectDomainsRetrieveResponse +- client.project.domains_update(id, \*\*params) -> ProjectDomainsUpdateResponse +- client.project.templates_create(\*\*params) -> ProjectTemplatesCreateResponse +- client.project.templates_delete(id) -> None +- client.project.templates_list(\*\*params) -> ProjectTemplatesListResponse +- client.project.templates_retrieve(id) -> ProjectTemplatesRetrieveResponse +- client.project.templates_update(id, \*\*params) -> ProjectTemplatesUpdateResponse +- client.project.tokens_delete(token_id) -> ProjectTokensDeleteResponse +- client.project.tokens_list() -> ProjectTokensListResponse +- client.project.workspaces_list() -> ProjectWorkspacesListResponse +- client.project.workspaces_retrieve(workspace_id) -> ProjectWorkspacesRetrieveResponse diff --git a/src/unlayer/_client.py b/src/unlayer/_client.py index 4b98994..70a1935 100644 --- a/src/unlayer/_client.py +++ b/src/unlayer/_client.py @@ -135,18 +135,18 @@ def __init__( _strict_response_validation=_strict_response_validation, ) - @cached_property - def project(self) -> ProjectResource: - from .resources.project import ProjectResource - - return ProjectResource(self) - @cached_property def emails(self) -> EmailsResource: from .resources.emails import EmailsResource return EmailsResource(self) + @cached_property + def pages(self) -> PagesResource: + from .resources.pages import PagesResource + + return PagesResource(self) + @cached_property def documents(self) -> DocumentsResource: from .resources.documents import DocumentsResource @@ -154,10 +154,10 @@ def documents(self) -> DocumentsResource: return DocumentsResource(self) @cached_property - def pages(self) -> PagesResource: - from .resources.pages import PagesResource + def project(self) -> ProjectResource: + from .resources.project import ProjectResource - return PagesResource(self) + return ProjectResource(self) @cached_property def with_raw_response(self) -> UnlayerWithRawResponse: @@ -353,18 +353,18 @@ def __init__( _strict_response_validation=_strict_response_validation, ) - @cached_property - def project(self) -> AsyncProjectResource: - from .resources.project import AsyncProjectResource - - return AsyncProjectResource(self) - @cached_property def emails(self) -> AsyncEmailsResource: from .resources.emails import AsyncEmailsResource return AsyncEmailsResource(self) + @cached_property + def pages(self) -> AsyncPagesResource: + from .resources.pages import AsyncPagesResource + + return AsyncPagesResource(self) + @cached_property def documents(self) -> AsyncDocumentsResource: from .resources.documents import AsyncDocumentsResource @@ -372,10 +372,10 @@ def documents(self) -> AsyncDocumentsResource: return AsyncDocumentsResource(self) @cached_property - def pages(self) -> AsyncPagesResource: - from .resources.pages import AsyncPagesResource + def project(self) -> AsyncProjectResource: + from .resources.project import AsyncProjectResource - return AsyncPagesResource(self) + return AsyncProjectResource(self) @cached_property def with_raw_response(self) -> AsyncUnlayerWithRawResponse: @@ -498,18 +498,18 @@ class UnlayerWithRawResponse: def __init__(self, client: Unlayer) -> None: self._client = client - @cached_property - def project(self) -> project.ProjectResourceWithRawResponse: - from .resources.project import ProjectResourceWithRawResponse - - return ProjectResourceWithRawResponse(self._client.project) - @cached_property def emails(self) -> emails.EmailsResourceWithRawResponse: from .resources.emails import EmailsResourceWithRawResponse return EmailsResourceWithRawResponse(self._client.emails) + @cached_property + def pages(self) -> pages.PagesResourceWithRawResponse: + from .resources.pages import PagesResourceWithRawResponse + + return PagesResourceWithRawResponse(self._client.pages) + @cached_property def documents(self) -> documents.DocumentsResourceWithRawResponse: from .resources.documents import DocumentsResourceWithRawResponse @@ -517,10 +517,10 @@ def documents(self) -> documents.DocumentsResourceWithRawResponse: return DocumentsResourceWithRawResponse(self._client.documents) @cached_property - def pages(self) -> pages.PagesResourceWithRawResponse: - from .resources.pages import PagesResourceWithRawResponse + def project(self) -> project.ProjectResourceWithRawResponse: + from .resources.project import ProjectResourceWithRawResponse - return PagesResourceWithRawResponse(self._client.pages) + return ProjectResourceWithRawResponse(self._client.project) class AsyncUnlayerWithRawResponse: @@ -529,18 +529,18 @@ class AsyncUnlayerWithRawResponse: def __init__(self, client: AsyncUnlayer) -> None: self._client = client - @cached_property - def project(self) -> project.AsyncProjectResourceWithRawResponse: - from .resources.project import AsyncProjectResourceWithRawResponse - - return AsyncProjectResourceWithRawResponse(self._client.project) - @cached_property def emails(self) -> emails.AsyncEmailsResourceWithRawResponse: from .resources.emails import AsyncEmailsResourceWithRawResponse return AsyncEmailsResourceWithRawResponse(self._client.emails) + @cached_property + def pages(self) -> pages.AsyncPagesResourceWithRawResponse: + from .resources.pages import AsyncPagesResourceWithRawResponse + + return AsyncPagesResourceWithRawResponse(self._client.pages) + @cached_property def documents(self) -> documents.AsyncDocumentsResourceWithRawResponse: from .resources.documents import AsyncDocumentsResourceWithRawResponse @@ -548,10 +548,10 @@ def documents(self) -> documents.AsyncDocumentsResourceWithRawResponse: return AsyncDocumentsResourceWithRawResponse(self._client.documents) @cached_property - def pages(self) -> pages.AsyncPagesResourceWithRawResponse: - from .resources.pages import AsyncPagesResourceWithRawResponse + def project(self) -> project.AsyncProjectResourceWithRawResponse: + from .resources.project import AsyncProjectResourceWithRawResponse - return AsyncPagesResourceWithRawResponse(self._client.pages) + return AsyncProjectResourceWithRawResponse(self._client.project) class UnlayerWithStreamedResponse: @@ -560,18 +560,18 @@ class UnlayerWithStreamedResponse: def __init__(self, client: Unlayer) -> None: self._client = client - @cached_property - def project(self) -> project.ProjectResourceWithStreamingResponse: - from .resources.project import ProjectResourceWithStreamingResponse - - return ProjectResourceWithStreamingResponse(self._client.project) - @cached_property def emails(self) -> emails.EmailsResourceWithStreamingResponse: from .resources.emails import EmailsResourceWithStreamingResponse return EmailsResourceWithStreamingResponse(self._client.emails) + @cached_property + def pages(self) -> pages.PagesResourceWithStreamingResponse: + from .resources.pages import PagesResourceWithStreamingResponse + + return PagesResourceWithStreamingResponse(self._client.pages) + @cached_property def documents(self) -> documents.DocumentsResourceWithStreamingResponse: from .resources.documents import DocumentsResourceWithStreamingResponse @@ -579,10 +579,10 @@ def documents(self) -> documents.DocumentsResourceWithStreamingResponse: return DocumentsResourceWithStreamingResponse(self._client.documents) @cached_property - def pages(self) -> pages.PagesResourceWithStreamingResponse: - from .resources.pages import PagesResourceWithStreamingResponse + def project(self) -> project.ProjectResourceWithStreamingResponse: + from .resources.project import ProjectResourceWithStreamingResponse - return PagesResourceWithStreamingResponse(self._client.pages) + return ProjectResourceWithStreamingResponse(self._client.project) class AsyncUnlayerWithStreamedResponse: @@ -591,18 +591,18 @@ class AsyncUnlayerWithStreamedResponse: def __init__(self, client: AsyncUnlayer) -> None: self._client = client - @cached_property - def project(self) -> project.AsyncProjectResourceWithStreamingResponse: - from .resources.project import AsyncProjectResourceWithStreamingResponse - - return AsyncProjectResourceWithStreamingResponse(self._client.project) - @cached_property def emails(self) -> emails.AsyncEmailsResourceWithStreamingResponse: from .resources.emails import AsyncEmailsResourceWithStreamingResponse return AsyncEmailsResourceWithStreamingResponse(self._client.emails) + @cached_property + def pages(self) -> pages.AsyncPagesResourceWithStreamingResponse: + from .resources.pages import AsyncPagesResourceWithStreamingResponse + + return AsyncPagesResourceWithStreamingResponse(self._client.pages) + @cached_property def documents(self) -> documents.AsyncDocumentsResourceWithStreamingResponse: from .resources.documents import AsyncDocumentsResourceWithStreamingResponse @@ -610,10 +610,10 @@ def documents(self) -> documents.AsyncDocumentsResourceWithStreamingResponse: return AsyncDocumentsResourceWithStreamingResponse(self._client.documents) @cached_property - def pages(self) -> pages.AsyncPagesResourceWithStreamingResponse: - from .resources.pages import AsyncPagesResourceWithStreamingResponse + def project(self) -> project.AsyncProjectResourceWithStreamingResponse: + from .resources.project import AsyncProjectResourceWithStreamingResponse - return AsyncPagesResourceWithStreamingResponse(self._client.pages) + return AsyncProjectResourceWithStreamingResponse(self._client.project) Client = Unlayer diff --git a/src/unlayer/resources/__init__.py b/src/unlayer/resources/__init__.py index b1a13d2..98d5378 100644 --- a/src/unlayer/resources/__init__.py +++ b/src/unlayer/resources/__init__.py @@ -34,28 +34,28 @@ ) __all__ = [ - "ProjectResource", - "AsyncProjectResource", - "ProjectResourceWithRawResponse", - "AsyncProjectResourceWithRawResponse", - "ProjectResourceWithStreamingResponse", - "AsyncProjectResourceWithStreamingResponse", "EmailsResource", "AsyncEmailsResource", "EmailsResourceWithRawResponse", "AsyncEmailsResourceWithRawResponse", "EmailsResourceWithStreamingResponse", "AsyncEmailsResourceWithStreamingResponse", - "DocumentsResource", - "AsyncDocumentsResource", - "DocumentsResourceWithRawResponse", - "AsyncDocumentsResourceWithRawResponse", - "DocumentsResourceWithStreamingResponse", - "AsyncDocumentsResourceWithStreamingResponse", "PagesResource", "AsyncPagesResource", "PagesResourceWithRawResponse", "AsyncPagesResourceWithRawResponse", "PagesResourceWithStreamingResponse", "AsyncPagesResourceWithStreamingResponse", + "DocumentsResource", + "AsyncDocumentsResource", + "DocumentsResourceWithRawResponse", + "AsyncDocumentsResourceWithRawResponse", + "DocumentsResourceWithStreamingResponse", + "AsyncDocumentsResourceWithStreamingResponse", + "ProjectResource", + "AsyncProjectResource", + "ProjectResourceWithRawResponse", + "AsyncProjectResourceWithRawResponse", + "ProjectResourceWithStreamingResponse", + "AsyncProjectResourceWithStreamingResponse", ] diff --git a/src/unlayer/resources/documents.py b/src/unlayer/resources/documents.py index 803d2f9..011d412 100644 --- a/src/unlayer/resources/documents.py +++ b/src/unlayer/resources/documents.py @@ -6,7 +6,11 @@ import httpx -from ..types import document_generate_create_params, document_generate_template_template_params +from ..types import ( + document_generate_create_params, + document_documents_retrieve_params, + document_generate_template_template_params, +) from .._types import Body, Omit, Query, Headers, NotGiven, omit, not_given from .._utils import maybe_transform, async_maybe_transform from .._compat import cached_property @@ -49,6 +53,7 @@ def documents_retrieve( self, id: str, *, + project_id: str | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, @@ -60,6 +65,8 @@ def documents_retrieve( Retrieve details of a previously generated document. Args: + project_id: The project ID (required for PAT auth, not needed for API Key auth) + extra_headers: Send extra headers extra_query: Add additional query parameters to the request @@ -73,7 +80,13 @@ def documents_retrieve( return self._get( f"/documents/v1/documents/{id}", options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + {"project_id": project_id}, document_documents_retrieve_params.DocumentDocumentsRetrieveParams + ), ), cast_to=DocumentDocumentsRetrieveResponse, ) @@ -82,6 +95,7 @@ def generate_create( self, *, design: Dict[str, object], + project_id: str | Omit = omit, filename: str | Omit = omit, html: str | Omit = omit, merge_tags: Dict[str, str] | Omit = omit, @@ -99,6 +113,8 @@ def generate_create( Args: design: Proprietary design format JSON + project_id: The project ID (required for PAT auth, not needed for API Key auth) + filename: Optional filename for the generated PDF html: HTML content to convert to PDF @@ -128,7 +144,13 @@ def generate_create( document_generate_create_params.DocumentGenerateCreateParams, ), options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + {"project_id": project_id}, document_generate_create_params.DocumentGenerateCreateParams + ), ), cast_to=DocumentGenerateCreateResponse, ) @@ -137,6 +159,7 @@ def generate_template_template( self, *, template_id: str, + project_id: str | Omit = omit, filename: str | Omit = omit, merge_tags: Dict[str, str] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. @@ -152,6 +175,8 @@ def generate_template_template( Args: template_id: ID of the template to use for generation + project_id: The project ID (required for PAT auth, not needed for API Key auth) + filename: Optional filename for the generated PDF merge_tags: Optional merge tags for personalization @@ -175,7 +200,14 @@ def generate_template_template( document_generate_template_template_params.DocumentGenerateTemplateTemplateParams, ), options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + {"project_id": project_id}, + document_generate_template_template_params.DocumentGenerateTemplateTemplateParams, + ), ), cast_to=DocumentGenerateTemplateTemplateResponse, ) @@ -205,6 +237,7 @@ async def documents_retrieve( self, id: str, *, + project_id: str | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, @@ -216,6 +249,8 @@ async def documents_retrieve( Retrieve details of a previously generated document. Args: + project_id: The project ID (required for PAT auth, not needed for API Key auth) + extra_headers: Send extra headers extra_query: Add additional query parameters to the request @@ -229,7 +264,13 @@ async def documents_retrieve( return await self._get( f"/documents/v1/documents/{id}", options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=await async_maybe_transform( + {"project_id": project_id}, document_documents_retrieve_params.DocumentDocumentsRetrieveParams + ), ), cast_to=DocumentDocumentsRetrieveResponse, ) @@ -238,6 +279,7 @@ async def generate_create( self, *, design: Dict[str, object], + project_id: str | Omit = omit, filename: str | Omit = omit, html: str | Omit = omit, merge_tags: Dict[str, str] | Omit = omit, @@ -255,6 +297,8 @@ async def generate_create( Args: design: Proprietary design format JSON + project_id: The project ID (required for PAT auth, not needed for API Key auth) + filename: Optional filename for the generated PDF html: HTML content to convert to PDF @@ -284,7 +328,13 @@ async def generate_create( document_generate_create_params.DocumentGenerateCreateParams, ), options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=await async_maybe_transform( + {"project_id": project_id}, document_generate_create_params.DocumentGenerateCreateParams + ), ), cast_to=DocumentGenerateCreateResponse, ) @@ -293,6 +343,7 @@ async def generate_template_template( self, *, template_id: str, + project_id: str | Omit = omit, filename: str | Omit = omit, merge_tags: Dict[str, str] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. @@ -308,6 +359,8 @@ async def generate_template_template( Args: template_id: ID of the template to use for generation + project_id: The project ID (required for PAT auth, not needed for API Key auth) + filename: Optional filename for the generated PDF merge_tags: Optional merge tags for personalization @@ -331,7 +384,14 @@ async def generate_template_template( document_generate_template_template_params.DocumentGenerateTemplateTemplateParams, ), options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=await async_maybe_transform( + {"project_id": project_id}, + document_generate_template_template_params.DocumentGenerateTemplateTemplateParams, + ), ), cast_to=DocumentGenerateTemplateTemplateResponse, ) diff --git a/src/unlayer/resources/emails.py b/src/unlayer/resources/emails.py index 372b424..2341242 100644 --- a/src/unlayer/resources/emails.py +++ b/src/unlayer/resources/emails.py @@ -6,7 +6,12 @@ import httpx -from ..types import email_send_create_params, email_render_create_params, email_send_template_template_params +from ..types import ( + email_retrieve_params, + email_send_create_params, + email_render_create_params, + email_send_template_template_params, +) from .._types import Body, Omit, Query, Headers, NotGiven, omit, not_given from .._utils import maybe_transform, async_maybe_transform from .._compat import cached_property @@ -50,6 +55,7 @@ def retrieve( self, id: str, *, + project_id: str | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, @@ -61,6 +67,8 @@ def retrieve( Retrieve details of a previously sent email. Args: + project_id: The project ID (required for PAT auth, not needed for API Key auth) + extra_headers: Send extra headers extra_query: Add additional query parameters to the request @@ -74,7 +82,11 @@ def retrieve( return self._get( f"/emails/v1/emails/{id}", options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform({"project_id": project_id}, email_retrieve_params.EmailRetrieveParams), ), cast_to=EmailRetrieveResponse, ) @@ -83,6 +95,7 @@ def render_create( self, *, design: Dict[str, object], + project_id: str | Omit = omit, merge_tags: Dict[str, str] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. @@ -97,6 +110,8 @@ def render_create( Args: design: Proprietary design format JSON + project_id: The project ID (required for PAT auth, not needed for API Key auth) + merge_tags: Optional merge tags for personalization extra_headers: Send extra headers @@ -117,7 +132,11 @@ def render_create( email_render_create_params.EmailRenderCreateParams, ), options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform({"project_id": project_id}, email_render_create_params.EmailRenderCreateParams), ), cast_to=EmailRenderCreateResponse, ) @@ -127,6 +146,7 @@ def send_create( *, design: Dict[str, object], to: str, + project_id: str | Omit = omit, html: str | Omit = omit, merge_tags: Dict[str, str] | Omit = omit, subject: str | Omit = omit, @@ -145,6 +165,8 @@ def send_create( to: Recipient email address + project_id: The project ID (required for PAT auth, not needed for API Key auth) + html: HTML content to send merge_tags: Optional merge tags for personalization @@ -172,7 +194,11 @@ def send_create( email_send_create_params.EmailSendCreateParams, ), options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform({"project_id": project_id}, email_send_create_params.EmailSendCreateParams), ), cast_to=EmailSendCreateResponse, ) @@ -182,6 +208,7 @@ def send_template_template( *, template_id: str, to: str, + project_id: str | Omit = omit, merge_tags: Dict[str, str] | Omit = omit, subject: str | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. @@ -199,6 +226,8 @@ def send_template_template( to: Recipient email address + project_id: The project ID (required for PAT auth, not needed for API Key auth) + merge_tags: Optional merge tags for personalization subject: Email subject line (optional, uses template default if not provided) @@ -223,7 +252,13 @@ def send_template_template( email_send_template_template_params.EmailSendTemplateTemplateParams, ), options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + {"project_id": project_id}, email_send_template_template_params.EmailSendTemplateTemplateParams + ), ), cast_to=EmailSendTemplateTemplateResponse, ) @@ -253,6 +288,7 @@ async def retrieve( self, id: str, *, + project_id: str | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, @@ -264,6 +300,8 @@ async def retrieve( Retrieve details of a previously sent email. Args: + project_id: The project ID (required for PAT auth, not needed for API Key auth) + extra_headers: Send extra headers extra_query: Add additional query parameters to the request @@ -277,7 +315,13 @@ async def retrieve( return await self._get( f"/emails/v1/emails/{id}", options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=await async_maybe_transform( + {"project_id": project_id}, email_retrieve_params.EmailRetrieveParams + ), ), cast_to=EmailRetrieveResponse, ) @@ -286,6 +330,7 @@ async def render_create( self, *, design: Dict[str, object], + project_id: str | Omit = omit, merge_tags: Dict[str, str] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. @@ -300,6 +345,8 @@ async def render_create( Args: design: Proprietary design format JSON + project_id: The project ID (required for PAT auth, not needed for API Key auth) + merge_tags: Optional merge tags for personalization extra_headers: Send extra headers @@ -320,7 +367,13 @@ async def render_create( email_render_create_params.EmailRenderCreateParams, ), options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=await async_maybe_transform( + {"project_id": project_id}, email_render_create_params.EmailRenderCreateParams + ), ), cast_to=EmailRenderCreateResponse, ) @@ -330,6 +383,7 @@ async def send_create( *, design: Dict[str, object], to: str, + project_id: str | Omit = omit, html: str | Omit = omit, merge_tags: Dict[str, str] | Omit = omit, subject: str | Omit = omit, @@ -348,6 +402,8 @@ async def send_create( to: Recipient email address + project_id: The project ID (required for PAT auth, not needed for API Key auth) + html: HTML content to send merge_tags: Optional merge tags for personalization @@ -375,7 +431,13 @@ async def send_create( email_send_create_params.EmailSendCreateParams, ), options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=await async_maybe_transform( + {"project_id": project_id}, email_send_create_params.EmailSendCreateParams + ), ), cast_to=EmailSendCreateResponse, ) @@ -385,6 +447,7 @@ async def send_template_template( *, template_id: str, to: str, + project_id: str | Omit = omit, merge_tags: Dict[str, str] | Omit = omit, subject: str | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. @@ -402,6 +465,8 @@ async def send_template_template( to: Recipient email address + project_id: The project ID (required for PAT auth, not needed for API Key auth) + merge_tags: Optional merge tags for personalization subject: Email subject line (optional, uses template default if not provided) @@ -426,7 +491,13 @@ async def send_template_template( email_send_template_template_params.EmailSendTemplateTemplateParams, ), options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=await async_maybe_transform( + {"project_id": project_id}, email_send_template_template_params.EmailSendTemplateTemplateParams + ), ), cast_to=EmailSendTemplateTemplateResponse, ) diff --git a/src/unlayer/resources/pages.py b/src/unlayer/resources/pages.py index 158cd79..1aa21ca 100644 --- a/src/unlayer/resources/pages.py +++ b/src/unlayer/resources/pages.py @@ -47,6 +47,7 @@ def render_create( self, *, design: Dict[str, object], + project_id: str | Omit = omit, merge_tags: Dict[str, str] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. @@ -61,6 +62,8 @@ def render_create( Args: design: Proprietary design format JSON + project_id: The project ID (required for PAT auth, not needed for API Key auth) + merge_tags: Optional merge tags for personalization extra_headers: Send extra headers @@ -81,7 +84,11 @@ def render_create( page_render_create_params.PageRenderCreateParams, ), options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform({"project_id": project_id}, page_render_create_params.PageRenderCreateParams), ), cast_to=PageRenderCreateResponse, ) @@ -111,6 +118,7 @@ async def render_create( self, *, design: Dict[str, object], + project_id: str | Omit = omit, merge_tags: Dict[str, str] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. @@ -125,6 +133,8 @@ async def render_create( Args: design: Proprietary design format JSON + project_id: The project ID (required for PAT auth, not needed for API Key auth) + merge_tags: Optional merge tags for personalization extra_headers: Send extra headers @@ -145,7 +155,13 @@ async def render_create( page_render_create_params.PageRenderCreateParams, ), options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=await async_maybe_transform( + {"project_id": project_id}, page_render_create_params.PageRenderCreateParams + ), ), cast_to=PageRenderCreateResponse, ) diff --git a/src/unlayer/resources/project.py b/src/unlayer/resources/project.py index bea717d..a74e7c9 100644 --- a/src/unlayer/resources/project.py +++ b/src/unlayer/resources/project.py @@ -5,8 +5,12 @@ import httpx from ..types import ( + project_current_list_params, + project_domains_list_params, + project_api_keys_list_params, project_domains_create_params, project_domains_update_params, + project_templates_list_params, project_api_keys_create_params, project_api_keys_update_params, project_templates_create_params, @@ -23,19 +27,23 @@ async_to_streamed_response_wrapper, ) from .._base_client import make_request_options +from ..types.project_tokens_list_response import ProjectTokensListResponse from ..types.project_current_list_response import ProjectCurrentListResponse from ..types.project_domains_list_response import ProjectDomainsListResponse from ..types.project_api_keys_list_response import ProjectAPIKeysListResponse +from ..types.project_tokens_delete_response import ProjectTokensDeleteResponse from ..types.project_domains_create_response import ProjectDomainsCreateResponse from ..types.project_domains_update_response import ProjectDomainsUpdateResponse from ..types.project_templates_list_response import ProjectTemplatesListResponse from ..types.project_api_keys_create_response import ProjectAPIKeysCreateResponse from ..types.project_api_keys_update_response import ProjectAPIKeysUpdateResponse +from ..types.project_workspaces_list_response import ProjectWorkspacesListResponse from ..types.project_domains_retrieve_response import ProjectDomainsRetrieveResponse from ..types.project_templates_create_response import ProjectTemplatesCreateResponse from ..types.project_templates_update_response import ProjectTemplatesUpdateResponse from ..types.project_api_keys_retrieve_response import ProjectAPIKeysRetrieveResponse from ..types.project_templates_retrieve_response import ProjectTemplatesRetrieveResponse +from ..types.project_workspaces_retrieve_response import ProjectWorkspacesRetrieveResponse __all__ = ["ProjectResource", "AsyncProjectResource"] @@ -63,6 +71,7 @@ def with_streaming_response(self) -> ProjectResourceWithStreamingResponse: def api_keys_create( self, *, + project_id: str, name: str, domains: SequenceNotStr[str] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. @@ -76,6 +85,8 @@ def api_keys_create( Create a new API key for the project. Args: + project_id: The project ID to create API key for + name: Name for the API key domains: Allowed domains for this API key @@ -98,7 +109,13 @@ def api_keys_create( project_api_keys_create_params.ProjectAPIKeysCreateParams, ), options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + {"project_id": project_id}, project_api_keys_create_params.ProjectAPIKeysCreateParams + ), ), cast_to=ProjectAPIKeysCreateResponse, ) @@ -140,6 +157,7 @@ def api_keys_delete( def api_keys_list( self, *, + project_id: str, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, @@ -147,11 +165,30 @@ def api_keys_list( extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> ProjectAPIKeysListResponse: - """List all API keys for the project.""" + """ + List all API keys for the project. + + Args: + project_id: The project ID to get API keys for + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ return self._get( "/project/v1/api-keys", options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + {"project_id": project_id}, project_api_keys_list_params.ProjectAPIKeysListParams + ), ), cast_to=ProjectAPIKeysListResponse, ) @@ -242,6 +279,7 @@ def api_keys_update( def current_list( self, *, + project_id: str, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, @@ -249,11 +287,28 @@ def current_list( extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> ProjectCurrentListResponse: - """Get project details for the authenticated project.""" + """ + Get project details for the specified project. + + Args: + project_id: The project ID + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ return self._get( "/project/v1/current", options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform({"project_id": project_id}, project_current_list_params.ProjectCurrentListParams), ), cast_to=ProjectCurrentListResponse, ) @@ -261,6 +316,7 @@ def current_list( def domains_create( self, *, + project_id: str, domain: str, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. @@ -273,6 +329,8 @@ def domains_create( Add a new domain to the project. Args: + project_id: The project ID to add domain to + domain: Domain name to add extra_headers: Send extra headers @@ -287,7 +345,13 @@ def domains_create( "/project/v1/domains", body=maybe_transform({"domain": domain}, project_domains_create_params.ProjectDomainsCreateParams), options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + {"project_id": project_id}, project_domains_create_params.ProjectDomainsCreateParams + ), ), cast_to=ProjectDomainsCreateResponse, ) @@ -329,6 +393,7 @@ def domains_delete( def domains_list( self, *, + project_id: str, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, @@ -336,11 +401,28 @@ def domains_list( extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> ProjectDomainsListResponse: - """List all domains for the project.""" + """ + List all domains for the project. + + Args: + project_id: The project ID to get domains for + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ return self._get( "/project/v1/domains", options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform({"project_id": project_id}, project_domains_list_params.ProjectDomainsListParams), ), cast_to=ProjectDomainsListResponse, ) @@ -418,6 +500,7 @@ def domains_update( def templates_create( self, *, + project_id: str, name: str, body: str | Omit = omit, subject: str | Omit = omit, @@ -432,6 +515,8 @@ def templates_create( Create a new project template. Args: + project_id: The project ID to create template for + name: Template name body: Email body content @@ -457,7 +542,13 @@ def templates_create( project_templates_create_params.ProjectTemplatesCreateParams, ), options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + {"project_id": project_id}, project_templates_create_params.ProjectTemplatesCreateParams + ), ), cast_to=ProjectTemplatesCreateResponse, ) @@ -499,6 +590,7 @@ def templates_delete( def templates_list( self, *, + project_id: str, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, @@ -506,11 +598,30 @@ def templates_list( extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> ProjectTemplatesListResponse: - """Get all project templates.""" + """ + Get all project templates. + + Args: + project_id: The project ID to get templates for + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ return self._get( "/project/v1/templates", options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + {"project_id": project_id}, project_templates_list_params.ProjectTemplatesListParams + ), ), cast_to=ProjectTemplatesListResponse, ) @@ -598,6 +709,111 @@ def templates_update( cast_to=ProjectTemplatesUpdateResponse, ) + def tokens_delete( + self, + token_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> ProjectTokensDeleteResponse: + """Delete a personal access token. + + You can only delete your own tokens. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not token_id: + raise ValueError(f"Expected a non-empty value for `token_id` but received {token_id!r}") + return self._delete( + f"/project/v1/tokens/{token_id}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=ProjectTokensDeleteResponse, + ) + + def tokens_list( + self, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> ProjectTokensListResponse: + """List all personal access tokens for the authenticated user.""" + return self._get( + "/project/v1/tokens", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=ProjectTokensListResponse, + ) + + def workspaces_list( + self, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> ProjectWorkspacesListResponse: + """Get all workspaces accessible by the current token.""" + return self._get( + "/project/v1/workspaces", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=ProjectWorkspacesListResponse, + ) + + def workspaces_retrieve( + self, + workspace_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> ProjectWorkspacesRetrieveResponse: + """ + Get a specific workspace by ID with its projects. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not workspace_id: + raise ValueError(f"Expected a non-empty value for `workspace_id` but received {workspace_id!r}") + return self._get( + f"/project/v1/workspaces/{workspace_id}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=ProjectWorkspacesRetrieveResponse, + ) + class AsyncProjectResource(AsyncAPIResource): @cached_property @@ -622,6 +838,7 @@ def with_streaming_response(self) -> AsyncProjectResourceWithStreamingResponse: async def api_keys_create( self, *, + project_id: str, name: str, domains: SequenceNotStr[str] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. @@ -635,6 +852,8 @@ async def api_keys_create( Create a new API key for the project. Args: + project_id: The project ID to create API key for + name: Name for the API key domains: Allowed domains for this API key @@ -657,7 +876,13 @@ async def api_keys_create( project_api_keys_create_params.ProjectAPIKeysCreateParams, ), options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=await async_maybe_transform( + {"project_id": project_id}, project_api_keys_create_params.ProjectAPIKeysCreateParams + ), ), cast_to=ProjectAPIKeysCreateResponse, ) @@ -699,6 +924,7 @@ async def api_keys_delete( async def api_keys_list( self, *, + project_id: str, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, @@ -706,11 +932,30 @@ async def api_keys_list( extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> ProjectAPIKeysListResponse: - """List all API keys for the project.""" + """ + List all API keys for the project. + + Args: + project_id: The project ID to get API keys for + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ return await self._get( "/project/v1/api-keys", options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=await async_maybe_transform( + {"project_id": project_id}, project_api_keys_list_params.ProjectAPIKeysListParams + ), ), cast_to=ProjectAPIKeysListResponse, ) @@ -801,6 +1046,7 @@ async def api_keys_update( async def current_list( self, *, + project_id: str, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, @@ -808,11 +1054,30 @@ async def current_list( extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> ProjectCurrentListResponse: - """Get project details for the authenticated project.""" + """ + Get project details for the specified project. + + Args: + project_id: The project ID + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ return await self._get( "/project/v1/current", options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=await async_maybe_transform( + {"project_id": project_id}, project_current_list_params.ProjectCurrentListParams + ), ), cast_to=ProjectCurrentListResponse, ) @@ -820,6 +1085,7 @@ async def current_list( async def domains_create( self, *, + project_id: str, domain: str, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. @@ -832,6 +1098,8 @@ async def domains_create( Add a new domain to the project. Args: + project_id: The project ID to add domain to + domain: Domain name to add extra_headers: Send extra headers @@ -848,7 +1116,13 @@ async def domains_create( {"domain": domain}, project_domains_create_params.ProjectDomainsCreateParams ), options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=await async_maybe_transform( + {"project_id": project_id}, project_domains_create_params.ProjectDomainsCreateParams + ), ), cast_to=ProjectDomainsCreateResponse, ) @@ -890,6 +1164,7 @@ async def domains_delete( async def domains_list( self, *, + project_id: str, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, @@ -897,11 +1172,30 @@ async def domains_list( extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> ProjectDomainsListResponse: - """List all domains for the project.""" + """ + List all domains for the project. + + Args: + project_id: The project ID to get domains for + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ return await self._get( "/project/v1/domains", options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=await async_maybe_transform( + {"project_id": project_id}, project_domains_list_params.ProjectDomainsListParams + ), ), cast_to=ProjectDomainsListResponse, ) @@ -981,6 +1275,7 @@ async def domains_update( async def templates_create( self, *, + project_id: str, name: str, body: str | Omit = omit, subject: str | Omit = omit, @@ -995,6 +1290,8 @@ async def templates_create( Create a new project template. Args: + project_id: The project ID to create template for + name: Template name body: Email body content @@ -1020,7 +1317,13 @@ async def templates_create( project_templates_create_params.ProjectTemplatesCreateParams, ), options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=await async_maybe_transform( + {"project_id": project_id}, project_templates_create_params.ProjectTemplatesCreateParams + ), ), cast_to=ProjectTemplatesCreateResponse, ) @@ -1062,6 +1365,7 @@ async def templates_delete( async def templates_list( self, *, + project_id: str, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, @@ -1069,11 +1373,30 @@ async def templates_list( extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> ProjectTemplatesListResponse: - """Get all project templates.""" + """ + Get all project templates. + + Args: + project_id: The project ID to get templates for + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ return await self._get( "/project/v1/templates", options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=await async_maybe_transform( + {"project_id": project_id}, project_templates_list_params.ProjectTemplatesListParams + ), ), cast_to=ProjectTemplatesListResponse, ) @@ -1161,6 +1484,111 @@ async def templates_update( cast_to=ProjectTemplatesUpdateResponse, ) + async def tokens_delete( + self, + token_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> ProjectTokensDeleteResponse: + """Delete a personal access token. + + You can only delete your own tokens. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not token_id: + raise ValueError(f"Expected a non-empty value for `token_id` but received {token_id!r}") + return await self._delete( + f"/project/v1/tokens/{token_id}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=ProjectTokensDeleteResponse, + ) + + async def tokens_list( + self, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> ProjectTokensListResponse: + """List all personal access tokens for the authenticated user.""" + return await self._get( + "/project/v1/tokens", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=ProjectTokensListResponse, + ) + + async def workspaces_list( + self, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> ProjectWorkspacesListResponse: + """Get all workspaces accessible by the current token.""" + return await self._get( + "/project/v1/workspaces", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=ProjectWorkspacesListResponse, + ) + + async def workspaces_retrieve( + self, + workspace_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> ProjectWorkspacesRetrieveResponse: + """ + Get a specific workspace by ID with its projects. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not workspace_id: + raise ValueError(f"Expected a non-empty value for `workspace_id` but received {workspace_id!r}") + return await self._get( + f"/project/v1/workspaces/{workspace_id}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=ProjectWorkspacesRetrieveResponse, + ) + class ProjectResourceWithRawResponse: def __init__(self, project: ProjectResource) -> None: @@ -1214,6 +1642,18 @@ def __init__(self, project: ProjectResource) -> None: self.templates_update = to_raw_response_wrapper( project.templates_update, ) + self.tokens_delete = to_raw_response_wrapper( + project.tokens_delete, + ) + self.tokens_list = to_raw_response_wrapper( + project.tokens_list, + ) + self.workspaces_list = to_raw_response_wrapper( + project.workspaces_list, + ) + self.workspaces_retrieve = to_raw_response_wrapper( + project.workspaces_retrieve, + ) class AsyncProjectResourceWithRawResponse: @@ -1268,6 +1708,18 @@ def __init__(self, project: AsyncProjectResource) -> None: self.templates_update = async_to_raw_response_wrapper( project.templates_update, ) + self.tokens_delete = async_to_raw_response_wrapper( + project.tokens_delete, + ) + self.tokens_list = async_to_raw_response_wrapper( + project.tokens_list, + ) + self.workspaces_list = async_to_raw_response_wrapper( + project.workspaces_list, + ) + self.workspaces_retrieve = async_to_raw_response_wrapper( + project.workspaces_retrieve, + ) class ProjectResourceWithStreamingResponse: @@ -1322,6 +1774,18 @@ def __init__(self, project: ProjectResource) -> None: self.templates_update = to_streamed_response_wrapper( project.templates_update, ) + self.tokens_delete = to_streamed_response_wrapper( + project.tokens_delete, + ) + self.tokens_list = to_streamed_response_wrapper( + project.tokens_list, + ) + self.workspaces_list = to_streamed_response_wrapper( + project.workspaces_list, + ) + self.workspaces_retrieve = to_streamed_response_wrapper( + project.workspaces_retrieve, + ) class AsyncProjectResourceWithStreamingResponse: @@ -1376,3 +1840,15 @@ def __init__(self, project: AsyncProjectResource) -> None: self.templates_update = async_to_streamed_response_wrapper( project.templates_update, ) + self.tokens_delete = async_to_streamed_response_wrapper( + project.tokens_delete, + ) + self.tokens_list = async_to_streamed_response_wrapper( + project.tokens_list, + ) + self.workspaces_list = async_to_streamed_response_wrapper( + project.workspaces_list, + ) + self.workspaces_retrieve = async_to_streamed_response_wrapper( + project.workspaces_retrieve, + ) diff --git a/src/unlayer/types/__init__.py b/src/unlayer/types/__init__.py index 3c047df..ad295c1 100644 --- a/src/unlayer/types/__init__.py +++ b/src/unlayer/types/__init__.py @@ -2,20 +2,27 @@ from __future__ import annotations +from .email_retrieve_params import EmailRetrieveParams as EmailRetrieveParams from .email_retrieve_response import EmailRetrieveResponse as EmailRetrieveResponse from .email_send_create_params import EmailSendCreateParams as EmailSendCreateParams from .page_render_create_params import PageRenderCreateParams as PageRenderCreateParams from .email_render_create_params import EmailRenderCreateParams as EmailRenderCreateParams from .email_send_create_response import EmailSendCreateResponse as EmailSendCreateResponse from .page_render_create_response import PageRenderCreateResponse as PageRenderCreateResponse +from .project_current_list_params import ProjectCurrentListParams as ProjectCurrentListParams +from .project_domains_list_params import ProjectDomainsListParams as ProjectDomainsListParams from .email_render_create_response import EmailRenderCreateResponse as EmailRenderCreateResponse +from .project_api_keys_list_params import ProjectAPIKeysListParams as ProjectAPIKeysListParams +from .project_tokens_list_response import ProjectTokensListResponse as ProjectTokensListResponse from .project_current_list_response import ProjectCurrentListResponse as ProjectCurrentListResponse from .project_domains_create_params import ProjectDomainsCreateParams as ProjectDomainsCreateParams from .project_domains_list_response import ProjectDomainsListResponse as ProjectDomainsListResponse from .project_domains_update_params import ProjectDomainsUpdateParams as ProjectDomainsUpdateParams +from .project_templates_list_params import ProjectTemplatesListParams as ProjectTemplatesListParams from .project_api_keys_create_params import ProjectAPIKeysCreateParams as ProjectAPIKeysCreateParams from .project_api_keys_list_response import ProjectAPIKeysListResponse as ProjectAPIKeysListResponse from .project_api_keys_update_params import ProjectAPIKeysUpdateParams as ProjectAPIKeysUpdateParams +from .project_tokens_delete_response import ProjectTokensDeleteResponse as ProjectTokensDeleteResponse from .document_generate_create_params import DocumentGenerateCreateParams as DocumentGenerateCreateParams from .project_domains_create_response import ProjectDomainsCreateResponse as ProjectDomainsCreateResponse from .project_domains_update_response import ProjectDomainsUpdateResponse as ProjectDomainsUpdateResponse @@ -24,14 +31,17 @@ from .project_templates_update_params import ProjectTemplatesUpdateParams as ProjectTemplatesUpdateParams from .project_api_keys_create_response import ProjectAPIKeysCreateResponse as ProjectAPIKeysCreateResponse from .project_api_keys_update_response import ProjectAPIKeysUpdateResponse as ProjectAPIKeysUpdateResponse +from .project_workspaces_list_response import ProjectWorkspacesListResponse as ProjectWorkspacesListResponse from .document_generate_create_response import DocumentGenerateCreateResponse as DocumentGenerateCreateResponse from .project_domains_retrieve_response import ProjectDomainsRetrieveResponse as ProjectDomainsRetrieveResponse from .project_templates_create_response import ProjectTemplatesCreateResponse as ProjectTemplatesCreateResponse from .project_templates_update_response import ProjectTemplatesUpdateResponse as ProjectTemplatesUpdateResponse +from .document_documents_retrieve_params import DocumentDocumentsRetrieveParams as DocumentDocumentsRetrieveParams from .project_api_keys_retrieve_response import ProjectAPIKeysRetrieveResponse as ProjectAPIKeysRetrieveResponse from .email_send_template_template_params import EmailSendTemplateTemplateParams as EmailSendTemplateTemplateParams from .project_templates_retrieve_response import ProjectTemplatesRetrieveResponse as ProjectTemplatesRetrieveResponse from .document_documents_retrieve_response import DocumentDocumentsRetrieveResponse as DocumentDocumentsRetrieveResponse +from .project_workspaces_retrieve_response import ProjectWorkspacesRetrieveResponse as ProjectWorkspacesRetrieveResponse from .email_send_template_template_response import ( EmailSendTemplateTemplateResponse as EmailSendTemplateTemplateResponse, ) diff --git a/src/unlayer/types/document_documents_retrieve_params.py b/src/unlayer/types/document_documents_retrieve_params.py new file mode 100644 index 0000000..87efae6 --- /dev/null +++ b/src/unlayer/types/document_documents_retrieve_params.py @@ -0,0 +1,14 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Annotated, TypedDict + +from .._utils import PropertyInfo + +__all__ = ["DocumentDocumentsRetrieveParams"] + + +class DocumentDocumentsRetrieveParams(TypedDict, total=False): + project_id: Annotated[str, PropertyInfo(alias="projectId")] + """The project ID (required for PAT auth, not needed for API Key auth)""" diff --git a/src/unlayer/types/document_generate_create_params.py b/src/unlayer/types/document_generate_create_params.py index 012f007..b2b5ead 100644 --- a/src/unlayer/types/document_generate_create_params.py +++ b/src/unlayer/types/document_generate_create_params.py @@ -14,6 +14,9 @@ class DocumentGenerateCreateParams(TypedDict, total=False): design: Required[Dict[str, object]] """Proprietary design format JSON""" + project_id: Annotated[str, PropertyInfo(alias="projectId")] + """The project ID (required for PAT auth, not needed for API Key auth)""" + filename: str """Optional filename for the generated PDF""" diff --git a/src/unlayer/types/document_generate_template_template_params.py b/src/unlayer/types/document_generate_template_template_params.py index 6e2a307..6c73081 100644 --- a/src/unlayer/types/document_generate_template_template_params.py +++ b/src/unlayer/types/document_generate_template_template_params.py @@ -14,6 +14,9 @@ class DocumentGenerateTemplateTemplateParams(TypedDict, total=False): template_id: Required[Annotated[str, PropertyInfo(alias="templateId")]] """ID of the template to use for generation""" + project_id: Annotated[str, PropertyInfo(alias="projectId")] + """The project ID (required for PAT auth, not needed for API Key auth)""" + filename: str """Optional filename for the generated PDF""" diff --git a/src/unlayer/types/email_render_create_params.py b/src/unlayer/types/email_render_create_params.py index 2ece897..07f8137 100644 --- a/src/unlayer/types/email_render_create_params.py +++ b/src/unlayer/types/email_render_create_params.py @@ -14,5 +14,8 @@ class EmailRenderCreateParams(TypedDict, total=False): design: Required[Dict[str, object]] """Proprietary design format JSON""" + project_id: Annotated[str, PropertyInfo(alias="projectId")] + """The project ID (required for PAT auth, not needed for API Key auth)""" + merge_tags: Annotated[Dict[str, str], PropertyInfo(alias="mergeTags")] """Optional merge tags for personalization""" diff --git a/src/unlayer/types/email_retrieve_params.py b/src/unlayer/types/email_retrieve_params.py new file mode 100644 index 0000000..8325207 --- /dev/null +++ b/src/unlayer/types/email_retrieve_params.py @@ -0,0 +1,14 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Annotated, TypedDict + +from .._utils import PropertyInfo + +__all__ = ["EmailRetrieveParams"] + + +class EmailRetrieveParams(TypedDict, total=False): + project_id: Annotated[str, PropertyInfo(alias="projectId")] + """The project ID (required for PAT auth, not needed for API Key auth)""" diff --git a/src/unlayer/types/email_send_create_params.py b/src/unlayer/types/email_send_create_params.py index 073951c..db174a5 100644 --- a/src/unlayer/types/email_send_create_params.py +++ b/src/unlayer/types/email_send_create_params.py @@ -17,6 +17,9 @@ class EmailSendCreateParams(TypedDict, total=False): to: Required[str] """Recipient email address""" + project_id: Annotated[str, PropertyInfo(alias="projectId")] + """The project ID (required for PAT auth, not needed for API Key auth)""" + html: str """HTML content to send""" diff --git a/src/unlayer/types/email_send_template_template_params.py b/src/unlayer/types/email_send_template_template_params.py index 856309e..3f959de 100644 --- a/src/unlayer/types/email_send_template_template_params.py +++ b/src/unlayer/types/email_send_template_template_params.py @@ -17,6 +17,9 @@ class EmailSendTemplateTemplateParams(TypedDict, total=False): to: Required[str] """Recipient email address""" + project_id: Annotated[str, PropertyInfo(alias="projectId")] + """The project ID (required for PAT auth, not needed for API Key auth)""" + merge_tags: Annotated[Dict[str, str], PropertyInfo(alias="mergeTags")] """Optional merge tags for personalization""" diff --git a/src/unlayer/types/page_render_create_params.py b/src/unlayer/types/page_render_create_params.py index 956be96..446924c 100644 --- a/src/unlayer/types/page_render_create_params.py +++ b/src/unlayer/types/page_render_create_params.py @@ -14,5 +14,8 @@ class PageRenderCreateParams(TypedDict, total=False): design: Required[Dict[str, object]] """Proprietary design format JSON""" + project_id: Annotated[str, PropertyInfo(alias="projectId")] + """The project ID (required for PAT auth, not needed for API Key auth)""" + merge_tags: Annotated[Dict[str, str], PropertyInfo(alias="mergeTags")] """Optional merge tags for personalization""" diff --git a/src/unlayer/types/project_api_keys_create_params.py b/src/unlayer/types/project_api_keys_create_params.py index 7cece83..e98c8a8 100644 --- a/src/unlayer/types/project_api_keys_create_params.py +++ b/src/unlayer/types/project_api_keys_create_params.py @@ -2,14 +2,18 @@ from __future__ import annotations -from typing_extensions import Required, TypedDict +from typing_extensions import Required, Annotated, TypedDict from .._types import SequenceNotStr +from .._utils import PropertyInfo __all__ = ["ProjectAPIKeysCreateParams"] class ProjectAPIKeysCreateParams(TypedDict, total=False): + project_id: Required[Annotated[str, PropertyInfo(alias="projectId")]] + """The project ID to create API key for""" + name: Required[str] """Name for the API key""" diff --git a/src/unlayer/types/project_api_keys_list_params.py b/src/unlayer/types/project_api_keys_list_params.py new file mode 100644 index 0000000..223b4fc --- /dev/null +++ b/src/unlayer/types/project_api_keys_list_params.py @@ -0,0 +1,14 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Required, Annotated, TypedDict + +from .._utils import PropertyInfo + +__all__ = ["ProjectAPIKeysListParams"] + + +class ProjectAPIKeysListParams(TypedDict, total=False): + project_id: Required[Annotated[str, PropertyInfo(alias="projectId")]] + """The project ID to get API keys for""" diff --git a/src/unlayer/types/project_current_list_params.py b/src/unlayer/types/project_current_list_params.py new file mode 100644 index 0000000..97ea047 --- /dev/null +++ b/src/unlayer/types/project_current_list_params.py @@ -0,0 +1,14 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Required, Annotated, TypedDict + +from .._utils import PropertyInfo + +__all__ = ["ProjectCurrentListParams"] + + +class ProjectCurrentListParams(TypedDict, total=False): + project_id: Required[Annotated[str, PropertyInfo(alias="projectId")]] + """The project ID""" diff --git a/src/unlayer/types/project_domains_create_params.py b/src/unlayer/types/project_domains_create_params.py index f20e9d3..c1f3f95 100644 --- a/src/unlayer/types/project_domains_create_params.py +++ b/src/unlayer/types/project_domains_create_params.py @@ -2,11 +2,16 @@ from __future__ import annotations -from typing_extensions import Required, TypedDict +from typing_extensions import Required, Annotated, TypedDict + +from .._utils import PropertyInfo __all__ = ["ProjectDomainsCreateParams"] class ProjectDomainsCreateParams(TypedDict, total=False): + project_id: Required[Annotated[str, PropertyInfo(alias="projectId")]] + """The project ID to add domain to""" + domain: Required[str] """Domain name to add""" diff --git a/src/unlayer/types/project_domains_list_params.py b/src/unlayer/types/project_domains_list_params.py new file mode 100644 index 0000000..6c3ba7a --- /dev/null +++ b/src/unlayer/types/project_domains_list_params.py @@ -0,0 +1,14 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Required, Annotated, TypedDict + +from .._utils import PropertyInfo + +__all__ = ["ProjectDomainsListParams"] + + +class ProjectDomainsListParams(TypedDict, total=False): + project_id: Required[Annotated[str, PropertyInfo(alias="projectId")]] + """The project ID to get domains for""" diff --git a/src/unlayer/types/project_templates_create_params.py b/src/unlayer/types/project_templates_create_params.py index 2ae8024..23d879d 100644 --- a/src/unlayer/types/project_templates_create_params.py +++ b/src/unlayer/types/project_templates_create_params.py @@ -2,12 +2,17 @@ from __future__ import annotations -from typing_extensions import Required, TypedDict +from typing_extensions import Required, Annotated, TypedDict + +from .._utils import PropertyInfo __all__ = ["ProjectTemplatesCreateParams"] class ProjectTemplatesCreateParams(TypedDict, total=False): + project_id: Required[Annotated[str, PropertyInfo(alias="projectId")]] + """The project ID to create template for""" + name: Required[str] """Template name""" diff --git a/src/unlayer/types/project_templates_list_params.py b/src/unlayer/types/project_templates_list_params.py new file mode 100644 index 0000000..335c4ed --- /dev/null +++ b/src/unlayer/types/project_templates_list_params.py @@ -0,0 +1,14 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Required, Annotated, TypedDict + +from .._utils import PropertyInfo + +__all__ = ["ProjectTemplatesListParams"] + + +class ProjectTemplatesListParams(TypedDict, total=False): + project_id: Required[Annotated[str, PropertyInfo(alias="projectId")]] + """The project ID to get templates for""" diff --git a/src/unlayer/types/project_tokens_delete_response.py b/src/unlayer/types/project_tokens_delete_response.py new file mode 100644 index 0000000..8067027 --- /dev/null +++ b/src/unlayer/types/project_tokens_delete_response.py @@ -0,0 +1,13 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional + +from .._models import BaseModel + +__all__ = ["ProjectTokensDeleteResponse"] + + +class ProjectTokensDeleteResponse(BaseModel): + message: Optional[str] = None + + success: Optional[bool] = None diff --git a/src/unlayer/types/project_tokens_list_response.py b/src/unlayer/types/project_tokens_list_response.py new file mode 100644 index 0000000..9fe5b8c --- /dev/null +++ b/src/unlayer/types/project_tokens_list_response.py @@ -0,0 +1,32 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List, Optional +from datetime import datetime + +from pydantic import Field as FieldInfo + +from .._models import BaseModel + +__all__ = ["ProjectTokensListResponse", "Data"] + + +class Data(BaseModel): + id: Optional[float] = None + + created_at: Optional[datetime] = FieldInfo(alias="createdAt", default=None) + + expires_at: Optional[datetime] = FieldInfo(alias="expiresAt", default=None) + + last_used_at: Optional[datetime] = FieldInfo(alias="lastUsedAt", default=None) + + name: Optional[str] = None + + scope: Optional[str] = None + + workspace_id: Optional[float] = FieldInfo(alias="workspaceId", default=None) + + workspace_name: Optional[str] = FieldInfo(alias="workspaceName", default=None) + + +class ProjectTokensListResponse(BaseModel): + data: Optional[List[Data]] = None diff --git a/src/unlayer/types/project_workspaces_list_response.py b/src/unlayer/types/project_workspaces_list_response.py new file mode 100644 index 0000000..9df5c1a --- /dev/null +++ b/src/unlayer/types/project_workspaces_list_response.py @@ -0,0 +1,17 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List, Optional + +from .._models import BaseModel + +__all__ = ["ProjectWorkspacesListResponse", "Data"] + + +class Data(BaseModel): + id: Optional[float] = None + + name: Optional[str] = None + + +class ProjectWorkspacesListResponse(BaseModel): + data: Optional[List[Data]] = None diff --git a/src/unlayer/types/project_workspaces_retrieve_response.py b/src/unlayer/types/project_workspaces_retrieve_response.py new file mode 100644 index 0000000..030b0d8 --- /dev/null +++ b/src/unlayer/types/project_workspaces_retrieve_response.py @@ -0,0 +1,27 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List, Optional + +from .._models import BaseModel + +__all__ = ["ProjectWorkspacesRetrieveResponse", "Data", "DataProject"] + + +class DataProject(BaseModel): + id: Optional[float] = None + + name: Optional[str] = None + + status: Optional[str] = None + + +class Data(BaseModel): + id: Optional[float] = None + + name: Optional[str] = None + + projects: Optional[List[DataProject]] = None + + +class ProjectWorkspacesRetrieveResponse(BaseModel): + data: Optional[Data] = None diff --git a/tests/api_resources/test_documents.py b/tests/api_resources/test_documents.py index cc5084c..b732e07 100644 --- a/tests/api_resources/test_documents.py +++ b/tests/api_resources/test_documents.py @@ -24,14 +24,22 @@ class TestDocuments: @parametrize def test_method_documents_retrieve(self, client: Unlayer) -> None: document = client.documents.documents_retrieve( - "id", + id="id", + ) + assert_matches_type(DocumentDocumentsRetrieveResponse, document, path=["response"]) + + @parametrize + def test_method_documents_retrieve_with_all_params(self, client: Unlayer) -> None: + document = client.documents.documents_retrieve( + id="id", + project_id="projectId", ) assert_matches_type(DocumentDocumentsRetrieveResponse, document, path=["response"]) @parametrize def test_raw_response_documents_retrieve(self, client: Unlayer) -> None: response = client.documents.with_raw_response.documents_retrieve( - "id", + id="id", ) assert response.is_closed is True @@ -42,7 +50,7 @@ def test_raw_response_documents_retrieve(self, client: Unlayer) -> None: @parametrize def test_streaming_response_documents_retrieve(self, client: Unlayer) -> None: with client.documents.with_streaming_response.documents_retrieve( - "id", + id="id", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" @@ -56,7 +64,7 @@ def test_streaming_response_documents_retrieve(self, client: Unlayer) -> None: def test_path_params_documents_retrieve(self, client: Unlayer) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): client.documents.with_raw_response.documents_retrieve( - "", + id="", ) @parametrize @@ -76,6 +84,7 @@ def test_method_generate_create_with_all_params(self, client: Unlayer) -> None: "counters": "bar", "body": "bar", }, + project_id="projectId", filename="filename", html="html", merge_tags={"foo": "string"}, @@ -124,6 +133,7 @@ def test_method_generate_template_template(self, client: Unlayer) -> None: def test_method_generate_template_template_with_all_params(self, client: Unlayer) -> None: document = client.documents.generate_template_template( template_id="templateId", + project_id="projectId", filename="filename", merge_tags={"foo": "string"}, ) @@ -162,14 +172,22 @@ class TestAsyncDocuments: @parametrize async def test_method_documents_retrieve(self, async_client: AsyncUnlayer) -> None: document = await async_client.documents.documents_retrieve( - "id", + id="id", + ) + assert_matches_type(DocumentDocumentsRetrieveResponse, document, path=["response"]) + + @parametrize + async def test_method_documents_retrieve_with_all_params(self, async_client: AsyncUnlayer) -> None: + document = await async_client.documents.documents_retrieve( + id="id", + project_id="projectId", ) assert_matches_type(DocumentDocumentsRetrieveResponse, document, path=["response"]) @parametrize async def test_raw_response_documents_retrieve(self, async_client: AsyncUnlayer) -> None: response = await async_client.documents.with_raw_response.documents_retrieve( - "id", + id="id", ) assert response.is_closed is True @@ -180,7 +198,7 @@ async def test_raw_response_documents_retrieve(self, async_client: AsyncUnlayer) @parametrize async def test_streaming_response_documents_retrieve(self, async_client: AsyncUnlayer) -> None: async with async_client.documents.with_streaming_response.documents_retrieve( - "id", + id="id", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" @@ -194,7 +212,7 @@ async def test_streaming_response_documents_retrieve(self, async_client: AsyncUn async def test_path_params_documents_retrieve(self, async_client: AsyncUnlayer) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): await async_client.documents.with_raw_response.documents_retrieve( - "", + id="", ) @parametrize @@ -214,6 +232,7 @@ async def test_method_generate_create_with_all_params(self, async_client: AsyncU "counters": "bar", "body": "bar", }, + project_id="projectId", filename="filename", html="html", merge_tags={"foo": "string"}, @@ -262,6 +281,7 @@ async def test_method_generate_template_template(self, async_client: AsyncUnlaye async def test_method_generate_template_template_with_all_params(self, async_client: AsyncUnlayer) -> None: document = await async_client.documents.generate_template_template( template_id="templateId", + project_id="projectId", filename="filename", merge_tags={"foo": "string"}, ) diff --git a/tests/api_resources/test_emails.py b/tests/api_resources/test_emails.py index ee12932..e005b81 100644 --- a/tests/api_resources/test_emails.py +++ b/tests/api_resources/test_emails.py @@ -25,14 +25,22 @@ class TestEmails: @parametrize def test_method_retrieve(self, client: Unlayer) -> None: email = client.emails.retrieve( - "id", + id="id", + ) + assert_matches_type(EmailRetrieveResponse, email, path=["response"]) + + @parametrize + def test_method_retrieve_with_all_params(self, client: Unlayer) -> None: + email = client.emails.retrieve( + id="id", + project_id="projectId", ) assert_matches_type(EmailRetrieveResponse, email, path=["response"]) @parametrize def test_raw_response_retrieve(self, client: Unlayer) -> None: response = client.emails.with_raw_response.retrieve( - "id", + id="id", ) assert response.is_closed is True @@ -43,7 +51,7 @@ def test_raw_response_retrieve(self, client: Unlayer) -> None: @parametrize def test_streaming_response_retrieve(self, client: Unlayer) -> None: with client.emails.with_streaming_response.retrieve( - "id", + id="id", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" @@ -57,7 +65,7 @@ def test_streaming_response_retrieve(self, client: Unlayer) -> None: def test_path_params_retrieve(self, client: Unlayer) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): client.emails.with_raw_response.retrieve( - "", + id="", ) @parametrize @@ -77,6 +85,7 @@ def test_method_render_create_with_all_params(self, client: Unlayer) -> None: "counters": "bar", "body": "bar", }, + project_id="projectId", merge_tags={"foo": "string"}, ) assert_matches_type(EmailRenderCreateResponse, email, path=["response"]) @@ -130,6 +139,7 @@ def test_method_send_create_with_all_params(self, client: Unlayer) -> None: "body": "bar", }, to="test@example.com", + project_id="projectId", html="html", merge_tags={"foo": "string"}, subject="Test", @@ -181,6 +191,7 @@ def test_method_send_template_template_with_all_params(self, client: Unlayer) -> email = client.emails.send_template_template( template_id="templateId", to="dev@stainless.com", + project_id="projectId", merge_tags={"foo": "string"}, subject="subject", ) @@ -221,14 +232,22 @@ class TestAsyncEmails: @parametrize async def test_method_retrieve(self, async_client: AsyncUnlayer) -> None: email = await async_client.emails.retrieve( - "id", + id="id", + ) + assert_matches_type(EmailRetrieveResponse, email, path=["response"]) + + @parametrize + async def test_method_retrieve_with_all_params(self, async_client: AsyncUnlayer) -> None: + email = await async_client.emails.retrieve( + id="id", + project_id="projectId", ) assert_matches_type(EmailRetrieveResponse, email, path=["response"]) @parametrize async def test_raw_response_retrieve(self, async_client: AsyncUnlayer) -> None: response = await async_client.emails.with_raw_response.retrieve( - "id", + id="id", ) assert response.is_closed is True @@ -239,7 +258,7 @@ async def test_raw_response_retrieve(self, async_client: AsyncUnlayer) -> None: @parametrize async def test_streaming_response_retrieve(self, async_client: AsyncUnlayer) -> None: async with async_client.emails.with_streaming_response.retrieve( - "id", + id="id", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" @@ -253,7 +272,7 @@ async def test_streaming_response_retrieve(self, async_client: AsyncUnlayer) -> async def test_path_params_retrieve(self, async_client: AsyncUnlayer) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): await async_client.emails.with_raw_response.retrieve( - "", + id="", ) @parametrize @@ -273,6 +292,7 @@ async def test_method_render_create_with_all_params(self, async_client: AsyncUnl "counters": "bar", "body": "bar", }, + project_id="projectId", merge_tags={"foo": "string"}, ) assert_matches_type(EmailRenderCreateResponse, email, path=["response"]) @@ -326,6 +346,7 @@ async def test_method_send_create_with_all_params(self, async_client: AsyncUnlay "body": "bar", }, to="test@example.com", + project_id="projectId", html="html", merge_tags={"foo": "string"}, subject="Test", @@ -377,6 +398,7 @@ async def test_method_send_template_template_with_all_params(self, async_client: email = await async_client.emails.send_template_template( template_id="templateId", to="dev@stainless.com", + project_id="projectId", merge_tags={"foo": "string"}, subject="subject", ) diff --git a/tests/api_resources/test_pages.py b/tests/api_resources/test_pages.py index b300fca..a0444c6 100644 --- a/tests/api_resources/test_pages.py +++ b/tests/api_resources/test_pages.py @@ -34,6 +34,7 @@ def test_method_render_create_with_all_params(self, client: Unlayer) -> None: "counters": "bar", "body": "bar", }, + project_id="projectId", merge_tags={"foo": "string"}, ) assert_matches_type(PageRenderCreateResponse, page, path=["response"]) @@ -91,6 +92,7 @@ async def test_method_render_create_with_all_params(self, async_client: AsyncUnl "counters": "bar", "body": "bar", }, + project_id="projectId", merge_tags={"foo": "string"}, ) assert_matches_type(PageRenderCreateResponse, page, path=["response"]) diff --git a/tests/api_resources/test_project.py b/tests/api_resources/test_project.py index 24deaa8..d2c1d77 100644 --- a/tests/api_resources/test_project.py +++ b/tests/api_resources/test_project.py @@ -10,19 +10,23 @@ from unlayer import Unlayer, AsyncUnlayer from tests.utils import assert_matches_type from unlayer.types import ( + ProjectTokensListResponse, ProjectAPIKeysListResponse, ProjectCurrentListResponse, ProjectDomainsListResponse, + ProjectTokensDeleteResponse, ProjectAPIKeysCreateResponse, ProjectAPIKeysUpdateResponse, ProjectDomainsCreateResponse, ProjectDomainsUpdateResponse, ProjectTemplatesListResponse, + ProjectWorkspacesListResponse, ProjectAPIKeysRetrieveResponse, ProjectDomainsRetrieveResponse, ProjectTemplatesCreateResponse, ProjectTemplatesUpdateResponse, ProjectTemplatesRetrieveResponse, + ProjectWorkspacesRetrieveResponse, ) base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") @@ -34,6 +38,7 @@ class TestProject: @parametrize def test_method_api_keys_create(self, client: Unlayer) -> None: project = client.project.api_keys_create( + project_id="projectId", name="name", ) assert_matches_type(ProjectAPIKeysCreateResponse, project, path=["response"]) @@ -41,6 +46,7 @@ def test_method_api_keys_create(self, client: Unlayer) -> None: @parametrize def test_method_api_keys_create_with_all_params(self, client: Unlayer) -> None: project = client.project.api_keys_create( + project_id="projectId", name="name", domains=["string"], ) @@ -49,6 +55,7 @@ def test_method_api_keys_create_with_all_params(self, client: Unlayer) -> None: @parametrize def test_raw_response_api_keys_create(self, client: Unlayer) -> None: response = client.project.with_raw_response.api_keys_create( + project_id="projectId", name="name", ) @@ -60,6 +67,7 @@ def test_raw_response_api_keys_create(self, client: Unlayer) -> None: @parametrize def test_streaming_response_api_keys_create(self, client: Unlayer) -> None: with client.project.with_streaming_response.api_keys_create( + project_id="projectId", name="name", ) as response: assert not response.is_closed @@ -110,12 +118,16 @@ def test_path_params_api_keys_delete(self, client: Unlayer) -> None: @parametrize def test_method_api_keys_list(self, client: Unlayer) -> None: - project = client.project.api_keys_list() + project = client.project.api_keys_list( + project_id="projectId", + ) assert_matches_type(ProjectAPIKeysListResponse, project, path=["response"]) @parametrize def test_raw_response_api_keys_list(self, client: Unlayer) -> None: - response = client.project.with_raw_response.api_keys_list() + response = client.project.with_raw_response.api_keys_list( + project_id="projectId", + ) assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" @@ -124,7 +136,9 @@ def test_raw_response_api_keys_list(self, client: Unlayer) -> None: @parametrize def test_streaming_response_api_keys_list(self, client: Unlayer) -> None: - with client.project.with_streaming_response.api_keys_list() as response: + with client.project.with_streaming_response.api_keys_list( + project_id="projectId", + ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" @@ -221,12 +235,16 @@ def test_path_params_api_keys_update(self, client: Unlayer) -> None: @parametrize def test_method_current_list(self, client: Unlayer) -> None: - project = client.project.current_list() + project = client.project.current_list( + project_id="projectId", + ) assert_matches_type(ProjectCurrentListResponse, project, path=["response"]) @parametrize def test_raw_response_current_list(self, client: Unlayer) -> None: - response = client.project.with_raw_response.current_list() + response = client.project.with_raw_response.current_list( + project_id="projectId", + ) assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" @@ -235,7 +253,9 @@ def test_raw_response_current_list(self, client: Unlayer) -> None: @parametrize def test_streaming_response_current_list(self, client: Unlayer) -> None: - with client.project.with_streaming_response.current_list() as response: + with client.project.with_streaming_response.current_list( + project_id="projectId", + ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" @@ -247,6 +267,7 @@ def test_streaming_response_current_list(self, client: Unlayer) -> None: @parametrize def test_method_domains_create(self, client: Unlayer) -> None: project = client.project.domains_create( + project_id="projectId", domain="domain", ) assert_matches_type(ProjectDomainsCreateResponse, project, path=["response"]) @@ -254,6 +275,7 @@ def test_method_domains_create(self, client: Unlayer) -> None: @parametrize def test_raw_response_domains_create(self, client: Unlayer) -> None: response = client.project.with_raw_response.domains_create( + project_id="projectId", domain="domain", ) @@ -265,6 +287,7 @@ def test_raw_response_domains_create(self, client: Unlayer) -> None: @parametrize def test_streaming_response_domains_create(self, client: Unlayer) -> None: with client.project.with_streaming_response.domains_create( + project_id="projectId", domain="domain", ) as response: assert not response.is_closed @@ -315,12 +338,16 @@ def test_path_params_domains_delete(self, client: Unlayer) -> None: @parametrize def test_method_domains_list(self, client: Unlayer) -> None: - project = client.project.domains_list() + project = client.project.domains_list( + project_id="projectId", + ) assert_matches_type(ProjectDomainsListResponse, project, path=["response"]) @parametrize def test_raw_response_domains_list(self, client: Unlayer) -> None: - response = client.project.with_raw_response.domains_list() + response = client.project.with_raw_response.domains_list( + project_id="projectId", + ) assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" @@ -329,7 +356,9 @@ def test_raw_response_domains_list(self, client: Unlayer) -> None: @parametrize def test_streaming_response_domains_list(self, client: Unlayer) -> None: - with client.project.with_streaming_response.domains_list() as response: + with client.project.with_streaming_response.domains_list( + project_id="projectId", + ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" @@ -425,6 +454,7 @@ def test_path_params_domains_update(self, client: Unlayer) -> None: @parametrize def test_method_templates_create(self, client: Unlayer) -> None: project = client.project.templates_create( + project_id="projectId", name="name", ) assert_matches_type(ProjectTemplatesCreateResponse, project, path=["response"]) @@ -432,6 +462,7 @@ def test_method_templates_create(self, client: Unlayer) -> None: @parametrize def test_method_templates_create_with_all_params(self, client: Unlayer) -> None: project = client.project.templates_create( + project_id="projectId", name="name", body="body", subject="subject", @@ -441,6 +472,7 @@ def test_method_templates_create_with_all_params(self, client: Unlayer) -> None: @parametrize def test_raw_response_templates_create(self, client: Unlayer) -> None: response = client.project.with_raw_response.templates_create( + project_id="projectId", name="name", ) @@ -452,6 +484,7 @@ def test_raw_response_templates_create(self, client: Unlayer) -> None: @parametrize def test_streaming_response_templates_create(self, client: Unlayer) -> None: with client.project.with_streaming_response.templates_create( + project_id="projectId", name="name", ) as response: assert not response.is_closed @@ -502,12 +535,16 @@ def test_path_params_templates_delete(self, client: Unlayer) -> None: @parametrize def test_method_templates_list(self, client: Unlayer) -> None: - project = client.project.templates_list() + project = client.project.templates_list( + project_id="projectId", + ) assert_matches_type(ProjectTemplatesListResponse, project, path=["response"]) @parametrize def test_raw_response_templates_list(self, client: Unlayer) -> None: - response = client.project.with_raw_response.templates_list() + response = client.project.with_raw_response.templates_list( + project_id="projectId", + ) assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" @@ -516,7 +553,9 @@ def test_raw_response_templates_list(self, client: Unlayer) -> None: @parametrize def test_streaming_response_templates_list(self, client: Unlayer) -> None: - with client.project.with_streaming_response.templates_list() as response: + with client.project.with_streaming_response.templates_list( + project_id="projectId", + ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" @@ -611,6 +650,132 @@ def test_path_params_templates_update(self, client: Unlayer) -> None: id="", ) + @parametrize + def test_method_tokens_delete(self, client: Unlayer) -> None: + project = client.project.tokens_delete( + "tokenId", + ) + assert_matches_type(ProjectTokensDeleteResponse, project, path=["response"]) + + @parametrize + def test_raw_response_tokens_delete(self, client: Unlayer) -> None: + response = client.project.with_raw_response.tokens_delete( + "tokenId", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + project = response.parse() + assert_matches_type(ProjectTokensDeleteResponse, project, path=["response"]) + + @parametrize + def test_streaming_response_tokens_delete(self, client: Unlayer) -> None: + with client.project.with_streaming_response.tokens_delete( + "tokenId", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + project = response.parse() + assert_matches_type(ProjectTokensDeleteResponse, project, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_path_params_tokens_delete(self, client: Unlayer) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `token_id` but received ''"): + client.project.with_raw_response.tokens_delete( + "", + ) + + @parametrize + def test_method_tokens_list(self, client: Unlayer) -> None: + project = client.project.tokens_list() + assert_matches_type(ProjectTokensListResponse, project, path=["response"]) + + @parametrize + def test_raw_response_tokens_list(self, client: Unlayer) -> None: + response = client.project.with_raw_response.tokens_list() + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + project = response.parse() + assert_matches_type(ProjectTokensListResponse, project, path=["response"]) + + @parametrize + def test_streaming_response_tokens_list(self, client: Unlayer) -> None: + with client.project.with_streaming_response.tokens_list() as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + project = response.parse() + assert_matches_type(ProjectTokensListResponse, project, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_method_workspaces_list(self, client: Unlayer) -> None: + project = client.project.workspaces_list() + assert_matches_type(ProjectWorkspacesListResponse, project, path=["response"]) + + @parametrize + def test_raw_response_workspaces_list(self, client: Unlayer) -> None: + response = client.project.with_raw_response.workspaces_list() + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + project = response.parse() + assert_matches_type(ProjectWorkspacesListResponse, project, path=["response"]) + + @parametrize + def test_streaming_response_workspaces_list(self, client: Unlayer) -> None: + with client.project.with_streaming_response.workspaces_list() as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + project = response.parse() + assert_matches_type(ProjectWorkspacesListResponse, project, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_method_workspaces_retrieve(self, client: Unlayer) -> None: + project = client.project.workspaces_retrieve( + "workspaceId", + ) + assert_matches_type(ProjectWorkspacesRetrieveResponse, project, path=["response"]) + + @parametrize + def test_raw_response_workspaces_retrieve(self, client: Unlayer) -> None: + response = client.project.with_raw_response.workspaces_retrieve( + "workspaceId", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + project = response.parse() + assert_matches_type(ProjectWorkspacesRetrieveResponse, project, path=["response"]) + + @parametrize + def test_streaming_response_workspaces_retrieve(self, client: Unlayer) -> None: + with client.project.with_streaming_response.workspaces_retrieve( + "workspaceId", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + project = response.parse() + assert_matches_type(ProjectWorkspacesRetrieveResponse, project, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_path_params_workspaces_retrieve(self, client: Unlayer) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `workspace_id` but received ''"): + client.project.with_raw_response.workspaces_retrieve( + "", + ) + class TestAsyncProject: parametrize = pytest.mark.parametrize( @@ -620,6 +785,7 @@ class TestAsyncProject: @parametrize async def test_method_api_keys_create(self, async_client: AsyncUnlayer) -> None: project = await async_client.project.api_keys_create( + project_id="projectId", name="name", ) assert_matches_type(ProjectAPIKeysCreateResponse, project, path=["response"]) @@ -627,6 +793,7 @@ async def test_method_api_keys_create(self, async_client: AsyncUnlayer) -> None: @parametrize async def test_method_api_keys_create_with_all_params(self, async_client: AsyncUnlayer) -> None: project = await async_client.project.api_keys_create( + project_id="projectId", name="name", domains=["string"], ) @@ -635,6 +802,7 @@ async def test_method_api_keys_create_with_all_params(self, async_client: AsyncU @parametrize async def test_raw_response_api_keys_create(self, async_client: AsyncUnlayer) -> None: response = await async_client.project.with_raw_response.api_keys_create( + project_id="projectId", name="name", ) @@ -646,6 +814,7 @@ async def test_raw_response_api_keys_create(self, async_client: AsyncUnlayer) -> @parametrize async def test_streaming_response_api_keys_create(self, async_client: AsyncUnlayer) -> None: async with async_client.project.with_streaming_response.api_keys_create( + project_id="projectId", name="name", ) as response: assert not response.is_closed @@ -696,12 +865,16 @@ async def test_path_params_api_keys_delete(self, async_client: AsyncUnlayer) -> @parametrize async def test_method_api_keys_list(self, async_client: AsyncUnlayer) -> None: - project = await async_client.project.api_keys_list() + project = await async_client.project.api_keys_list( + project_id="projectId", + ) assert_matches_type(ProjectAPIKeysListResponse, project, path=["response"]) @parametrize async def test_raw_response_api_keys_list(self, async_client: AsyncUnlayer) -> None: - response = await async_client.project.with_raw_response.api_keys_list() + response = await async_client.project.with_raw_response.api_keys_list( + project_id="projectId", + ) assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" @@ -710,7 +883,9 @@ async def test_raw_response_api_keys_list(self, async_client: AsyncUnlayer) -> N @parametrize async def test_streaming_response_api_keys_list(self, async_client: AsyncUnlayer) -> None: - async with async_client.project.with_streaming_response.api_keys_list() as response: + async with async_client.project.with_streaming_response.api_keys_list( + project_id="projectId", + ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" @@ -807,12 +982,16 @@ async def test_path_params_api_keys_update(self, async_client: AsyncUnlayer) -> @parametrize async def test_method_current_list(self, async_client: AsyncUnlayer) -> None: - project = await async_client.project.current_list() + project = await async_client.project.current_list( + project_id="projectId", + ) assert_matches_type(ProjectCurrentListResponse, project, path=["response"]) @parametrize async def test_raw_response_current_list(self, async_client: AsyncUnlayer) -> None: - response = await async_client.project.with_raw_response.current_list() + response = await async_client.project.with_raw_response.current_list( + project_id="projectId", + ) assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" @@ -821,7 +1000,9 @@ async def test_raw_response_current_list(self, async_client: AsyncUnlayer) -> No @parametrize async def test_streaming_response_current_list(self, async_client: AsyncUnlayer) -> None: - async with async_client.project.with_streaming_response.current_list() as response: + async with async_client.project.with_streaming_response.current_list( + project_id="projectId", + ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" @@ -833,6 +1014,7 @@ async def test_streaming_response_current_list(self, async_client: AsyncUnlayer) @parametrize async def test_method_domains_create(self, async_client: AsyncUnlayer) -> None: project = await async_client.project.domains_create( + project_id="projectId", domain="domain", ) assert_matches_type(ProjectDomainsCreateResponse, project, path=["response"]) @@ -840,6 +1022,7 @@ async def test_method_domains_create(self, async_client: AsyncUnlayer) -> None: @parametrize async def test_raw_response_domains_create(self, async_client: AsyncUnlayer) -> None: response = await async_client.project.with_raw_response.domains_create( + project_id="projectId", domain="domain", ) @@ -851,6 +1034,7 @@ async def test_raw_response_domains_create(self, async_client: AsyncUnlayer) -> @parametrize async def test_streaming_response_domains_create(self, async_client: AsyncUnlayer) -> None: async with async_client.project.with_streaming_response.domains_create( + project_id="projectId", domain="domain", ) as response: assert not response.is_closed @@ -901,12 +1085,16 @@ async def test_path_params_domains_delete(self, async_client: AsyncUnlayer) -> N @parametrize async def test_method_domains_list(self, async_client: AsyncUnlayer) -> None: - project = await async_client.project.domains_list() + project = await async_client.project.domains_list( + project_id="projectId", + ) assert_matches_type(ProjectDomainsListResponse, project, path=["response"]) @parametrize async def test_raw_response_domains_list(self, async_client: AsyncUnlayer) -> None: - response = await async_client.project.with_raw_response.domains_list() + response = await async_client.project.with_raw_response.domains_list( + project_id="projectId", + ) assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" @@ -915,7 +1103,9 @@ async def test_raw_response_domains_list(self, async_client: AsyncUnlayer) -> No @parametrize async def test_streaming_response_domains_list(self, async_client: AsyncUnlayer) -> None: - async with async_client.project.with_streaming_response.domains_list() as response: + async with async_client.project.with_streaming_response.domains_list( + project_id="projectId", + ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" @@ -1011,6 +1201,7 @@ async def test_path_params_domains_update(self, async_client: AsyncUnlayer) -> N @parametrize async def test_method_templates_create(self, async_client: AsyncUnlayer) -> None: project = await async_client.project.templates_create( + project_id="projectId", name="name", ) assert_matches_type(ProjectTemplatesCreateResponse, project, path=["response"]) @@ -1018,6 +1209,7 @@ async def test_method_templates_create(self, async_client: AsyncUnlayer) -> None @parametrize async def test_method_templates_create_with_all_params(self, async_client: AsyncUnlayer) -> None: project = await async_client.project.templates_create( + project_id="projectId", name="name", body="body", subject="subject", @@ -1027,6 +1219,7 @@ async def test_method_templates_create_with_all_params(self, async_client: Async @parametrize async def test_raw_response_templates_create(self, async_client: AsyncUnlayer) -> None: response = await async_client.project.with_raw_response.templates_create( + project_id="projectId", name="name", ) @@ -1038,6 +1231,7 @@ async def test_raw_response_templates_create(self, async_client: AsyncUnlayer) - @parametrize async def test_streaming_response_templates_create(self, async_client: AsyncUnlayer) -> None: async with async_client.project.with_streaming_response.templates_create( + project_id="projectId", name="name", ) as response: assert not response.is_closed @@ -1088,12 +1282,16 @@ async def test_path_params_templates_delete(self, async_client: AsyncUnlayer) -> @parametrize async def test_method_templates_list(self, async_client: AsyncUnlayer) -> None: - project = await async_client.project.templates_list() + project = await async_client.project.templates_list( + project_id="projectId", + ) assert_matches_type(ProjectTemplatesListResponse, project, path=["response"]) @parametrize async def test_raw_response_templates_list(self, async_client: AsyncUnlayer) -> None: - response = await async_client.project.with_raw_response.templates_list() + response = await async_client.project.with_raw_response.templates_list( + project_id="projectId", + ) assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" @@ -1102,7 +1300,9 @@ async def test_raw_response_templates_list(self, async_client: AsyncUnlayer) -> @parametrize async def test_streaming_response_templates_list(self, async_client: AsyncUnlayer) -> None: - async with async_client.project.with_streaming_response.templates_list() as response: + async with async_client.project.with_streaming_response.templates_list( + project_id="projectId", + ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" @@ -1196,3 +1396,129 @@ async def test_path_params_templates_update(self, async_client: AsyncUnlayer) -> await async_client.project.with_raw_response.templates_update( id="", ) + + @parametrize + async def test_method_tokens_delete(self, async_client: AsyncUnlayer) -> None: + project = await async_client.project.tokens_delete( + "tokenId", + ) + assert_matches_type(ProjectTokensDeleteResponse, project, path=["response"]) + + @parametrize + async def test_raw_response_tokens_delete(self, async_client: AsyncUnlayer) -> None: + response = await async_client.project.with_raw_response.tokens_delete( + "tokenId", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + project = await response.parse() + assert_matches_type(ProjectTokensDeleteResponse, project, path=["response"]) + + @parametrize + async def test_streaming_response_tokens_delete(self, async_client: AsyncUnlayer) -> None: + async with async_client.project.with_streaming_response.tokens_delete( + "tokenId", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + project = await response.parse() + assert_matches_type(ProjectTokensDeleteResponse, project, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_path_params_tokens_delete(self, async_client: AsyncUnlayer) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `token_id` but received ''"): + await async_client.project.with_raw_response.tokens_delete( + "", + ) + + @parametrize + async def test_method_tokens_list(self, async_client: AsyncUnlayer) -> None: + project = await async_client.project.tokens_list() + assert_matches_type(ProjectTokensListResponse, project, path=["response"]) + + @parametrize + async def test_raw_response_tokens_list(self, async_client: AsyncUnlayer) -> None: + response = await async_client.project.with_raw_response.tokens_list() + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + project = await response.parse() + assert_matches_type(ProjectTokensListResponse, project, path=["response"]) + + @parametrize + async def test_streaming_response_tokens_list(self, async_client: AsyncUnlayer) -> None: + async with async_client.project.with_streaming_response.tokens_list() as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + project = await response.parse() + assert_matches_type(ProjectTokensListResponse, project, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_method_workspaces_list(self, async_client: AsyncUnlayer) -> None: + project = await async_client.project.workspaces_list() + assert_matches_type(ProjectWorkspacesListResponse, project, path=["response"]) + + @parametrize + async def test_raw_response_workspaces_list(self, async_client: AsyncUnlayer) -> None: + response = await async_client.project.with_raw_response.workspaces_list() + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + project = await response.parse() + assert_matches_type(ProjectWorkspacesListResponse, project, path=["response"]) + + @parametrize + async def test_streaming_response_workspaces_list(self, async_client: AsyncUnlayer) -> None: + async with async_client.project.with_streaming_response.workspaces_list() as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + project = await response.parse() + assert_matches_type(ProjectWorkspacesListResponse, project, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_method_workspaces_retrieve(self, async_client: AsyncUnlayer) -> None: + project = await async_client.project.workspaces_retrieve( + "workspaceId", + ) + assert_matches_type(ProjectWorkspacesRetrieveResponse, project, path=["response"]) + + @parametrize + async def test_raw_response_workspaces_retrieve(self, async_client: AsyncUnlayer) -> None: + response = await async_client.project.with_raw_response.workspaces_retrieve( + "workspaceId", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + project = await response.parse() + assert_matches_type(ProjectWorkspacesRetrieveResponse, project, path=["response"]) + + @parametrize + async def test_streaming_response_workspaces_retrieve(self, async_client: AsyncUnlayer) -> None: + async with async_client.project.with_streaming_response.workspaces_retrieve( + "workspaceId", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + project = await response.parse() + assert_matches_type(ProjectWorkspacesRetrieveResponse, project, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_path_params_workspaces_retrieve(self, async_client: AsyncUnlayer) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `workspace_id` but received ''"): + await async_client.project.with_raw_response.workspaces_retrieve( + "", + ) diff --git a/tests/test_client.py b/tests/test_client.py index e53628e..3ee68fe 100644 --- a/tests/test_client.py +++ b/tests/test_client.py @@ -862,7 +862,7 @@ def test_retrying_timeout_errors_doesnt_leak(self, respx_mock: MockRouter, clien respx_mock.get("/project/v1/current").mock(side_effect=httpx.TimeoutException("Test timeout error")) with pytest.raises(APITimeoutError): - client.project.with_streaming_response.current_list().__enter__() + client.project.with_streaming_response.current_list(project_id="projectId").__enter__() assert _get_open_connections(client) == 0 @@ -872,7 +872,7 @@ def test_retrying_status_errors_doesnt_leak(self, respx_mock: MockRouter, client respx_mock.get("/project/v1/current").mock(return_value=httpx.Response(500)) with pytest.raises(APIStatusError): - client.project.with_streaming_response.current_list().__enter__() + client.project.with_streaming_response.current_list(project_id="projectId").__enter__() assert _get_open_connections(client) == 0 @pytest.mark.parametrize("failures_before_success", [0, 2, 4]) @@ -901,7 +901,7 @@ def retry_handler(_request: httpx.Request) -> httpx.Response: respx_mock.get("/project/v1/current").mock(side_effect=retry_handler) - response = client.project.with_raw_response.current_list() + response = client.project.with_raw_response.current_list(project_id="projectId") assert response.retries_taken == failures_before_success assert int(response.http_request.headers.get("x-stainless-retry-count")) == failures_before_success @@ -925,7 +925,9 @@ def retry_handler(_request: httpx.Request) -> httpx.Response: respx_mock.get("/project/v1/current").mock(side_effect=retry_handler) - response = client.project.with_raw_response.current_list(extra_headers={"x-stainless-retry-count": Omit()}) + response = client.project.with_raw_response.current_list( + project_id="projectId", extra_headers={"x-stainless-retry-count": Omit()} + ) assert len(response.http_request.headers.get_list("x-stainless-retry-count")) == 0 @@ -948,7 +950,9 @@ def retry_handler(_request: httpx.Request) -> httpx.Response: respx_mock.get("/project/v1/current").mock(side_effect=retry_handler) - response = client.project.with_raw_response.current_list(extra_headers={"x-stainless-retry-count": "42"}) + response = client.project.with_raw_response.current_list( + project_id="projectId", extra_headers={"x-stainless-retry-count": "42"} + ) assert response.http_request.headers.get("x-stainless-retry-count") == "42" @@ -1774,7 +1778,7 @@ async def test_retrying_timeout_errors_doesnt_leak( respx_mock.get("/project/v1/current").mock(side_effect=httpx.TimeoutException("Test timeout error")) with pytest.raises(APITimeoutError): - await async_client.project.with_streaming_response.current_list().__aenter__() + await async_client.project.with_streaming_response.current_list(project_id="projectId").__aenter__() assert _get_open_connections(async_client) == 0 @@ -1784,7 +1788,7 @@ async def test_retrying_status_errors_doesnt_leak(self, respx_mock: MockRouter, respx_mock.get("/project/v1/current").mock(return_value=httpx.Response(500)) with pytest.raises(APIStatusError): - await async_client.project.with_streaming_response.current_list().__aenter__() + await async_client.project.with_streaming_response.current_list(project_id="projectId").__aenter__() assert _get_open_connections(async_client) == 0 @pytest.mark.parametrize("failures_before_success", [0, 2, 4]) @@ -1813,7 +1817,7 @@ def retry_handler(_request: httpx.Request) -> httpx.Response: respx_mock.get("/project/v1/current").mock(side_effect=retry_handler) - response = await client.project.with_raw_response.current_list() + response = await client.project.with_raw_response.current_list(project_id="projectId") assert response.retries_taken == failures_before_success assert int(response.http_request.headers.get("x-stainless-retry-count")) == failures_before_success @@ -1838,7 +1842,7 @@ def retry_handler(_request: httpx.Request) -> httpx.Response: respx_mock.get("/project/v1/current").mock(side_effect=retry_handler) response = await client.project.with_raw_response.current_list( - extra_headers={"x-stainless-retry-count": Omit()} + project_id="projectId", extra_headers={"x-stainless-retry-count": Omit()} ) assert len(response.http_request.headers.get_list("x-stainless-retry-count")) == 0 @@ -1862,7 +1866,9 @@ def retry_handler(_request: httpx.Request) -> httpx.Response: respx_mock.get("/project/v1/current").mock(side_effect=retry_handler) - response = await client.project.with_raw_response.current_list(extra_headers={"x-stainless-retry-count": "42"}) + response = await client.project.with_raw_response.current_list( + project_id="projectId", extra_headers={"x-stainless-retry-count": "42"} + ) assert response.http_request.headers.get("x-stainless-retry-count") == "42" From e8513a6e5f49bd14c4d674df5c731aa0ef389232 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Sat, 17 Jan 2026 04:06:16 +0000 Subject: [PATCH 21/62] chore(internal): update `actions/checkout` version --- .github/workflows/ci.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 66bf079..af33300 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -19,7 +19,7 @@ jobs: runs-on: ${{ github.repository == 'stainless-sdks/unlayer-python' && 'depot-ubuntu-24.04' || 'ubuntu-latest' }} if: github.event_name == 'push' || github.event.pull_request.head.repo.fork steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 - name: Install Rye run: | @@ -44,7 +44,7 @@ jobs: id-token: write runs-on: ${{ github.repository == 'stainless-sdks/unlayer-python' && 'depot-ubuntu-24.04' || 'ubuntu-latest' }} steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 - name: Install Rye run: | @@ -81,7 +81,7 @@ jobs: runs-on: ${{ github.repository == 'stainless-sdks/unlayer-python' && 'depot-ubuntu-24.04' || 'ubuntu-latest' }} if: github.event_name == 'push' || github.event.pull_request.head.repo.fork steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 - name: Install Rye run: | From 443d2d699e2ae64aa9c7e2d18b4180f62431f551 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Mon, 19 Jan 2026 18:08:48 +0000 Subject: [PATCH 22/62] feat(api): api update --- .stats.yml | 8 +- README.md | 113 ++- api.md | 59 +- src/unlayer/_client.py | 172 +++-- src/unlayer/pagination.py | 68 ++ src/unlayer/resources/__init__.py | 26 +- src/unlayer/resources/documents.py | 36 +- src/unlayer/resources/emails.py | 52 +- src/unlayer/resources/export.py | 442 +++++++++++ src/unlayer/resources/pages.py | 12 +- src/unlayer/resources/project.py | 730 ++---------------- src/unlayer/types/__init__.py | 17 +- .../document_documents_retrieve_params.py | 6 +- .../document_documents_retrieve_response.py | 8 +- .../types/document_generate_create_params.py | 8 +- .../document_generate_create_response.py | 8 +- ...ument_generate_template_template_params.py | 6 +- ...ent_generate_template_template_response.py | 8 +- .../types/email_render_create_params.py | 6 +- .../types/email_render_create_response.py | 8 +- src/unlayer/types/email_retrieve_params.py | 6 +- src/unlayer/types/email_retrieve_response.py | 8 +- src/unlayer/types/email_send_create_params.py | 8 +- .../types/email_send_create_response.py | 8 +- .../email_send_template_template_params.py | 6 +- .../email_send_template_template_response.py | 8 +- ...t_params.py => export_html_list_params.py} | 6 +- .../types/export_html_list_response.py | 15 + src/unlayer/types/export_image_list_params.py | 14 + .../types/export_image_list_response.py | 15 + src/unlayer/types/export_pdf_list_params.py | 14 + ...esponse.py => export_pdf_list_response.py} | 10 +- src/unlayer/types/export_zip_list_params.py | 14 + src/unlayer/types/export_zip_list_response.py | 15 + .../types/page_render_create_params.py | 6 +- .../types/page_render_create_response.py | 8 +- .../types/project_api_keys_create_params.py | 21 - .../types/project_api_keys_create_response.py | 28 - .../types/project_api_keys_list_response.py | 30 - .../project_api_keys_retrieve_response.py | 30 - .../types/project_api_keys_update_params.py | 20 - .../types/project_api_keys_update_response.py | 30 - .../types/project_domains_create_params.py | 2 +- .../types/project_domains_list_params.py | 2 +- .../types/project_templates_create_params.py | 11 +- .../project_templates_create_response.py | 10 +- .../types/project_templates_list_params.py | 16 +- .../types/project_templates_list_response.py | 20 +- .../types/project_tokens_list_response.py | 32 - tests/api_resources/test_documents.py | 70 +- tests/api_resources/test_emails.py | 136 ++-- tests/api_resources/test_export.py | 277 +++++++ tests/api_resources/test_pages.py | 46 +- tests/api_resources/test_project.py | 569 +------------- tests/conftest.py | 6 +- tests/test_client.py | 186 +++-- 56 files changed, 1619 insertions(+), 1877 deletions(-) create mode 100644 src/unlayer/pagination.py create mode 100644 src/unlayer/resources/export.py rename src/unlayer/types/{project_api_keys_list_params.py => export_html_list_params.py} (67%) create mode 100644 src/unlayer/types/export_html_list_response.py create mode 100644 src/unlayer/types/export_image_list_params.py create mode 100644 src/unlayer/types/export_image_list_response.py create mode 100644 src/unlayer/types/export_pdf_list_params.py rename src/unlayer/types/{project_tokens_delete_response.py => export_pdf_list_response.py} (57%) create mode 100644 src/unlayer/types/export_zip_list_params.py create mode 100644 src/unlayer/types/export_zip_list_response.py delete mode 100644 src/unlayer/types/project_api_keys_create_params.py delete mode 100644 src/unlayer/types/project_api_keys_create_response.py delete mode 100644 src/unlayer/types/project_api_keys_list_response.py delete mode 100644 src/unlayer/types/project_api_keys_retrieve_response.py delete mode 100644 src/unlayer/types/project_api_keys_update_params.py delete mode 100644 src/unlayer/types/project_api_keys_update_response.py delete mode 100644 src/unlayer/types/project_tokens_list_response.py create mode 100644 tests/api_resources/test_export.py diff --git a/.stats.yml b/.stats.yml index 97858dc..ead84b6 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ -configured_endpoints: 28 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/unlayer%2Funlayer-d89823a559be3efaaedf95ef21523f77342db1eb9bd708015fac22f68bcfd8ae.yml -openapi_spec_hash: fda98646b156fdd53a03d433cbacad20 -config_hash: ad8a4186026c9854e45139d7c4a20090 +configured_endpoints: 25 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/unlayer%2Funlayer-42ce261f6dec6bdbfe9b98a3835cbb95c67440a58023646fe03ab0aa0745d671.yml +openapi_spec_hash: 0f18f2d5ea38b837ccc0156ea80d85cd +config_hash: ae90f2806448a012e25917d01699b3a5 diff --git a/README.md b/README.md index b40986b..afacda3 100644 --- a/README.md +++ b/README.md @@ -32,21 +32,21 @@ import os from unlayer import Unlayer client = Unlayer( - api_key=os.environ.get("UNLAYER_API_KEY"), # This is the default and can be omitted - # or 'production' | 'dev'; defaults to "production". - environment="qa", + access_token=os.environ.get("UNLAYER_ACCESS_TOKEN"), # This is the default and can be omitted + # or 'production' | 'qa' | 'dev'; defaults to "production". + environment="stage", ) response = client.project.current_list( - project_id="projectId", + project_id="your-project-id", ) print(response.data) ``` -While you can provide an `api_key` keyword argument, +While you can provide a `access_token` keyword argument, we recommend using [python-dotenv](https://pypi.org/project/python-dotenv/) -to add `UNLAYER_API_KEY="My API Key"` to your `.env` file -so that your API Key is not stored in source control. +to add `UNLAYER_ACCESS_TOKEN="My Access Token"` to your `.env` file +so that your Access Token is not stored in source control. ## Async usage @@ -58,15 +58,15 @@ import asyncio from unlayer import AsyncUnlayer client = AsyncUnlayer( - api_key=os.environ.get("UNLAYER_API_KEY"), # This is the default and can be omitted - # or 'production' | 'dev'; defaults to "production". - environment="qa", + access_token=os.environ.get("UNLAYER_ACCESS_TOKEN"), # This is the default and can be omitted + # or 'production' | 'qa' | 'dev'; defaults to "production". + environment="stage", ) async def main() -> None: response = await client.project.current_list( - project_id="projectId", + project_id="your-project-id", ) print(response.data) @@ -98,11 +98,13 @@ from unlayer import AsyncUnlayer async def main() -> None: async with AsyncUnlayer( - api_key=os.environ.get("UNLAYER_API_KEY"), # This is the default and can be omitted + access_token=os.environ.get( + "UNLAYER_ACCESS_TOKEN" + ), # This is the default and can be omitted http_client=DefaultAioHttpClient(), ) as client: response = await client.project.current_list( - project_id="projectId", + project_id="your-project-id", ) print(response.data) @@ -119,6 +121,81 @@ Nested request parameters are [TypedDicts](https://docs.python.org/3/library/typ Typed requests and responses provide autocomplete and documentation within your editor. If you would like to see type errors in VS Code to help catch bugs earlier, set `python.analysis.typeCheckingMode` to `basic`. +## Pagination + +List methods in the Unlayer API are paginated. + +This library provides auto-paginating iterators with each list response, so you do not have to request successive pages manually: + +```python +from unlayer import Unlayer + +client = Unlayer() + +all_projects = [] +# Automatically fetches more pages as needed. +for project in client.project.templates_list( + project_id="your-project-id", + limit=10, +): + # Do something with project here + all_projects.append(project) +print(all_projects) +``` + +Or, asynchronously: + +```python +import asyncio +from unlayer import AsyncUnlayer + +client = AsyncUnlayer() + + +async def main() -> None: + all_projects = [] + # Iterate through items across all pages, issuing requests as needed. + async for project in client.project.templates_list( + project_id="your-project-id", + limit=10, + ): + all_projects.append(project) + print(all_projects) + + +asyncio.run(main()) +``` + +Alternatively, you can use the `.has_next_page()`, `.next_page_info()`, or `.get_next_page()` methods for more granular control working with pages: + +```python +first_page = await client.project.templates_list( + project_id="your-project-id", + limit=10, +) +if first_page.has_next_page(): + print(f"will fetch next page using these details: {first_page.next_page_info()}") + next_page = await first_page.get_next_page() + print(f"number of items we just fetched: {len(next_page.data)}") + +# Remove `await` for non-async usage. +``` + +Or just work directly with the returned data: + +```python +first_page = await client.project.templates_list( + project_id="your-project-id", + limit=10, +) + +print(f"next page cursor: {first_page.next_cursor}") # => "next page cursor: ..." +for project in first_page.data: + print(project.id) + +# Remove `await` for non-async usage. +``` + ## Handling errors When the library is unable to connect to the API (for example, due to network connection problems or a timeout), a subclass of `unlayer.APIConnectionError` is raised. @@ -136,7 +213,7 @@ client = Unlayer() try: client.project.current_list( - project_id="projectId", + project_id="your-project-id", ) except unlayer.APIConnectionError as e: print("The server could not be reached") @@ -181,7 +258,7 @@ client = Unlayer( # Or, configure per-request: client.with_options(max_retries=5).project.current_list( - project_id="projectId", + project_id="your-project-id", ) ``` @@ -206,7 +283,7 @@ client = Unlayer( # Override per-request: client.with_options(timeout=5.0).project.current_list( - project_id="projectId", + project_id="your-project-id", ) ``` @@ -249,7 +326,7 @@ from unlayer import Unlayer client = Unlayer() response = client.project.with_raw_response.current_list( - project_id="projectId", + project_id="your-project-id", ) print(response.headers.get('X-My-Header')) @@ -269,7 +346,7 @@ To stream the response body, use `.with_streaming_response` instead, which requi ```python with client.project.with_streaming_response.current_list( - project_id="projectId", + project_id="your-project-id", ) as response: print(response.headers.get("X-My-Header")) diff --git a/api.md b/api.md index a04e322..a157e4b 100644 --- a/api.md +++ b/api.md @@ -1,3 +1,21 @@ +# Documents + +Types: + +```python +from unlayer.types import ( + DocumentDocumentsRetrieveResponse, + DocumentGenerateCreateResponse, + DocumentGenerateTemplateTemplateResponse, +) +``` + +Methods: + +- client.documents.documents_retrieve(id, \*\*params) -> DocumentDocumentsRetrieveResponse +- client.documents.generate_create(\*\*params) -> DocumentGenerateCreateResponse +- client.documents.generate_template_template(\*\*params) -> DocumentGenerateTemplateTemplateResponse + # Emails Types: @@ -18,35 +36,37 @@ Methods: - client.emails.send_create(\*\*params) -> EmailSendCreateResponse - client.emails.send_template_template(\*\*params) -> EmailSendTemplateTemplateResponse -# Pages +# Export Types: ```python -from unlayer.types import PageRenderCreateResponse +from unlayer.types import ( + ExportHTMLListResponse, + ExportImageListResponse, + ExportPdfListResponse, + ExportZipListResponse, +) ``` Methods: -- client.pages.render_create(\*\*params) -> PageRenderCreateResponse +- client.export.html_list(\*\*params) -> ExportHTMLListResponse +- client.export.image_list(\*\*params) -> ExportImageListResponse +- client.export.pdf_list(\*\*params) -> ExportPdfListResponse +- client.export.zip_list(\*\*params) -> ExportZipListResponse -# Documents +# Pages Types: ```python -from unlayer.types import ( - DocumentDocumentsRetrieveResponse, - DocumentGenerateCreateResponse, - DocumentGenerateTemplateTemplateResponse, -) +from unlayer.types import PageRenderCreateResponse ``` Methods: -- client.documents.documents_retrieve(id, \*\*params) -> DocumentDocumentsRetrieveResponse -- client.documents.generate_create(\*\*params) -> DocumentGenerateCreateResponse -- client.documents.generate_template_template(\*\*params) -> DocumentGenerateTemplateTemplateResponse +- client.pages.render_create(\*\*params) -> PageRenderCreateResponse # Project @@ -54,10 +74,6 @@ Types: ```python from unlayer.types import ( - ProjectAPIKeysCreateResponse, - ProjectAPIKeysListResponse, - ProjectAPIKeysRetrieveResponse, - ProjectAPIKeysUpdateResponse, ProjectCurrentListResponse, ProjectDomainsCreateResponse, ProjectDomainsListResponse, @@ -67,8 +83,6 @@ from unlayer.types import ( ProjectTemplatesListResponse, ProjectTemplatesRetrieveResponse, ProjectTemplatesUpdateResponse, - ProjectTokensDeleteResponse, - ProjectTokensListResponse, ProjectWorkspacesListResponse, ProjectWorkspacesRetrieveResponse, ) @@ -76,11 +90,6 @@ from unlayer.types import ( Methods: -- client.project.api_keys_create(\*\*params) -> ProjectAPIKeysCreateResponse -- client.project.api_keys_delete(id) -> None -- client.project.api_keys_list(\*\*params) -> ProjectAPIKeysListResponse -- client.project.api_keys_retrieve(id) -> ProjectAPIKeysRetrieveResponse -- client.project.api_keys_update(id, \*\*params) -> ProjectAPIKeysUpdateResponse - client.project.current_list(\*\*params) -> ProjectCurrentListResponse - client.project.domains_create(\*\*params) -> ProjectDomainsCreateResponse - client.project.domains_delete(id) -> None @@ -89,10 +98,8 @@ Methods: - client.project.domains_update(id, \*\*params) -> ProjectDomainsUpdateResponse - client.project.templates_create(\*\*params) -> ProjectTemplatesCreateResponse - client.project.templates_delete(id) -> None -- client.project.templates_list(\*\*params) -> ProjectTemplatesListResponse +- client.project.templates_list(\*\*params) -> SyncCursorPage[ProjectTemplatesListResponse] - client.project.templates_retrieve(id) -> ProjectTemplatesRetrieveResponse - client.project.templates_update(id, \*\*params) -> ProjectTemplatesUpdateResponse -- client.project.tokens_delete(token_id) -> ProjectTokensDeleteResponse -- client.project.tokens_list() -> ProjectTokensListResponse - client.project.workspaces_list() -> ProjectWorkspacesListResponse - client.project.workspaces_retrieve(workspace_id) -> ProjectWorkspacesRetrieveResponse diff --git a/src/unlayer/_client.py b/src/unlayer/_client.py index 70a1935..cdcb8b9 100644 --- a/src/unlayer/_client.py +++ b/src/unlayer/_client.py @@ -31,9 +31,10 @@ ) if TYPE_CHECKING: - from .resources import pages, emails, project, documents + from .resources import pages, emails, export, project, documents from .resources.pages import PagesResource, AsyncPagesResource from .resources.emails import EmailsResource, AsyncEmailsResource + from .resources.export import ExportResource, AsyncExportResource from .resources.project import ProjectResource, AsyncProjectResource from .resources.documents import DocumentsResource, AsyncDocumentsResource @@ -51,6 +52,7 @@ ENVIRONMENTS: Dict[str, str] = { "production": "https://api.unlayer.com", + "stage": "https://api.stage.unlayer.com", "qa": "https://api.qa.unlayer.com", "dev": "https://api.dev.unlayer.com", } @@ -58,15 +60,15 @@ class Unlayer(SyncAPIClient): # client options - api_key: str + access_token: str - _environment: Literal["production", "qa", "dev"] | NotGiven + _environment: Literal["production", "stage", "qa", "dev"] | NotGiven def __init__( self, *, - api_key: str | None = None, - environment: Literal["production", "qa", "dev"] | NotGiven = not_given, + access_token: str | None = None, + environment: Literal["production", "stage", "qa", "dev"] | NotGiven = not_given, base_url: str | httpx.URL | None | NotGiven = not_given, timeout: float | Timeout | None | NotGiven = not_given, max_retries: int = DEFAULT_MAX_RETRIES, @@ -88,15 +90,15 @@ def __init__( ) -> None: """Construct a new synchronous Unlayer client instance. - This automatically infers the `api_key` argument from the `UNLAYER_API_KEY` environment variable if it is not provided. + This automatically infers the `access_token` argument from the `UNLAYER_ACCESS_TOKEN` environment variable if it is not provided. """ - if api_key is None: - api_key = os.environ.get("UNLAYER_API_KEY") - if api_key is None: + if access_token is None: + access_token = os.environ.get("UNLAYER_ACCESS_TOKEN") + if access_token is None: raise UnlayerError( - "The api_key client option must be set either by passing api_key to the client or by setting the UNLAYER_API_KEY environment variable" + "The access_token client option must be set either by passing access_token to the client or by setting the UNLAYER_ACCESS_TOKEN environment variable" ) - self.api_key = api_key + self.access_token = access_token self._environment = environment @@ -135,6 +137,12 @@ def __init__( _strict_response_validation=_strict_response_validation, ) + @cached_property + def documents(self) -> DocumentsResource: + from .resources.documents import DocumentsResource + + return DocumentsResource(self) + @cached_property def emails(self) -> EmailsResource: from .resources.emails import EmailsResource @@ -142,16 +150,16 @@ def emails(self) -> EmailsResource: return EmailsResource(self) @cached_property - def pages(self) -> PagesResource: - from .resources.pages import PagesResource + def export(self) -> ExportResource: + from .resources.export import ExportResource - return PagesResource(self) + return ExportResource(self) @cached_property - def documents(self) -> DocumentsResource: - from .resources.documents import DocumentsResource + def pages(self) -> PagesResource: + from .resources.pages import PagesResource - return DocumentsResource(self) + return PagesResource(self) @cached_property def project(self) -> ProjectResource: @@ -175,8 +183,8 @@ def qs(self) -> Querystring: @property @override def auth_headers(self) -> dict[str, str]: - api_key = self.api_key - return {"Authorization": f"Bearer {api_key}"} + access_token = self.access_token + return {"Authorization": f"Bearer {access_token}"} @property @override @@ -190,8 +198,8 @@ def default_headers(self) -> dict[str, str | Omit]: def copy( self, *, - api_key: str | None = None, - environment: Literal["production", "qa", "dev"] | None = None, + access_token: str | None = None, + environment: Literal["production", "stage", "qa", "dev"] | None = None, base_url: str | httpx.URL | None = None, timeout: float | Timeout | None | NotGiven = not_given, http_client: httpx.Client | None = None, @@ -225,7 +233,7 @@ def copy( http_client = http_client or self._client return self.__class__( - api_key=api_key or self.api_key, + access_token=access_token or self.access_token, base_url=base_url or self.base_url, environment=environment or self._environment, timeout=self.timeout if isinstance(timeout, NotGiven) else timeout, @@ -276,15 +284,15 @@ def _make_status_error( class AsyncUnlayer(AsyncAPIClient): # client options - api_key: str + access_token: str - _environment: Literal["production", "qa", "dev"] | NotGiven + _environment: Literal["production", "stage", "qa", "dev"] | NotGiven def __init__( self, *, - api_key: str | None = None, - environment: Literal["production", "qa", "dev"] | NotGiven = not_given, + access_token: str | None = None, + environment: Literal["production", "stage", "qa", "dev"] | NotGiven = not_given, base_url: str | httpx.URL | None | NotGiven = not_given, timeout: float | Timeout | None | NotGiven = not_given, max_retries: int = DEFAULT_MAX_RETRIES, @@ -306,15 +314,15 @@ def __init__( ) -> None: """Construct a new async AsyncUnlayer client instance. - This automatically infers the `api_key` argument from the `UNLAYER_API_KEY` environment variable if it is not provided. + This automatically infers the `access_token` argument from the `UNLAYER_ACCESS_TOKEN` environment variable if it is not provided. """ - if api_key is None: - api_key = os.environ.get("UNLAYER_API_KEY") - if api_key is None: + if access_token is None: + access_token = os.environ.get("UNLAYER_ACCESS_TOKEN") + if access_token is None: raise UnlayerError( - "The api_key client option must be set either by passing api_key to the client or by setting the UNLAYER_API_KEY environment variable" + "The access_token client option must be set either by passing access_token to the client or by setting the UNLAYER_ACCESS_TOKEN environment variable" ) - self.api_key = api_key + self.access_token = access_token self._environment = environment @@ -353,6 +361,12 @@ def __init__( _strict_response_validation=_strict_response_validation, ) + @cached_property + def documents(self) -> AsyncDocumentsResource: + from .resources.documents import AsyncDocumentsResource + + return AsyncDocumentsResource(self) + @cached_property def emails(self) -> AsyncEmailsResource: from .resources.emails import AsyncEmailsResource @@ -360,16 +374,16 @@ def emails(self) -> AsyncEmailsResource: return AsyncEmailsResource(self) @cached_property - def pages(self) -> AsyncPagesResource: - from .resources.pages import AsyncPagesResource + def export(self) -> AsyncExportResource: + from .resources.export import AsyncExportResource - return AsyncPagesResource(self) + return AsyncExportResource(self) @cached_property - def documents(self) -> AsyncDocumentsResource: - from .resources.documents import AsyncDocumentsResource + def pages(self) -> AsyncPagesResource: + from .resources.pages import AsyncPagesResource - return AsyncDocumentsResource(self) + return AsyncPagesResource(self) @cached_property def project(self) -> AsyncProjectResource: @@ -393,8 +407,8 @@ def qs(self) -> Querystring: @property @override def auth_headers(self) -> dict[str, str]: - api_key = self.api_key - return {"Authorization": f"Bearer {api_key}"} + access_token = self.access_token + return {"Authorization": f"Bearer {access_token}"} @property @override @@ -408,8 +422,8 @@ def default_headers(self) -> dict[str, str | Omit]: def copy( self, *, - api_key: str | None = None, - environment: Literal["production", "qa", "dev"] | None = None, + access_token: str | None = None, + environment: Literal["production", "stage", "qa", "dev"] | None = None, base_url: str | httpx.URL | None = None, timeout: float | Timeout | None | NotGiven = not_given, http_client: httpx.AsyncClient | None = None, @@ -443,7 +457,7 @@ def copy( http_client = http_client or self._client return self.__class__( - api_key=api_key or self.api_key, + access_token=access_token or self.access_token, base_url=base_url or self.base_url, environment=environment or self._environment, timeout=self.timeout if isinstance(timeout, NotGiven) else timeout, @@ -498,6 +512,12 @@ class UnlayerWithRawResponse: def __init__(self, client: Unlayer) -> None: self._client = client + @cached_property + def documents(self) -> documents.DocumentsResourceWithRawResponse: + from .resources.documents import DocumentsResourceWithRawResponse + + return DocumentsResourceWithRawResponse(self._client.documents) + @cached_property def emails(self) -> emails.EmailsResourceWithRawResponse: from .resources.emails import EmailsResourceWithRawResponse @@ -505,16 +525,16 @@ def emails(self) -> emails.EmailsResourceWithRawResponse: return EmailsResourceWithRawResponse(self._client.emails) @cached_property - def pages(self) -> pages.PagesResourceWithRawResponse: - from .resources.pages import PagesResourceWithRawResponse + def export(self) -> export.ExportResourceWithRawResponse: + from .resources.export import ExportResourceWithRawResponse - return PagesResourceWithRawResponse(self._client.pages) + return ExportResourceWithRawResponse(self._client.export) @cached_property - def documents(self) -> documents.DocumentsResourceWithRawResponse: - from .resources.documents import DocumentsResourceWithRawResponse + def pages(self) -> pages.PagesResourceWithRawResponse: + from .resources.pages import PagesResourceWithRawResponse - return DocumentsResourceWithRawResponse(self._client.documents) + return PagesResourceWithRawResponse(self._client.pages) @cached_property def project(self) -> project.ProjectResourceWithRawResponse: @@ -529,6 +549,12 @@ class AsyncUnlayerWithRawResponse: def __init__(self, client: AsyncUnlayer) -> None: self._client = client + @cached_property + def documents(self) -> documents.AsyncDocumentsResourceWithRawResponse: + from .resources.documents import AsyncDocumentsResourceWithRawResponse + + return AsyncDocumentsResourceWithRawResponse(self._client.documents) + @cached_property def emails(self) -> emails.AsyncEmailsResourceWithRawResponse: from .resources.emails import AsyncEmailsResourceWithRawResponse @@ -536,16 +562,16 @@ def emails(self) -> emails.AsyncEmailsResourceWithRawResponse: return AsyncEmailsResourceWithRawResponse(self._client.emails) @cached_property - def pages(self) -> pages.AsyncPagesResourceWithRawResponse: - from .resources.pages import AsyncPagesResourceWithRawResponse + def export(self) -> export.AsyncExportResourceWithRawResponse: + from .resources.export import AsyncExportResourceWithRawResponse - return AsyncPagesResourceWithRawResponse(self._client.pages) + return AsyncExportResourceWithRawResponse(self._client.export) @cached_property - def documents(self) -> documents.AsyncDocumentsResourceWithRawResponse: - from .resources.documents import AsyncDocumentsResourceWithRawResponse + def pages(self) -> pages.AsyncPagesResourceWithRawResponse: + from .resources.pages import AsyncPagesResourceWithRawResponse - return AsyncDocumentsResourceWithRawResponse(self._client.documents) + return AsyncPagesResourceWithRawResponse(self._client.pages) @cached_property def project(self) -> project.AsyncProjectResourceWithRawResponse: @@ -560,6 +586,12 @@ class UnlayerWithStreamedResponse: def __init__(self, client: Unlayer) -> None: self._client = client + @cached_property + def documents(self) -> documents.DocumentsResourceWithStreamingResponse: + from .resources.documents import DocumentsResourceWithStreamingResponse + + return DocumentsResourceWithStreamingResponse(self._client.documents) + @cached_property def emails(self) -> emails.EmailsResourceWithStreamingResponse: from .resources.emails import EmailsResourceWithStreamingResponse @@ -567,16 +599,16 @@ def emails(self) -> emails.EmailsResourceWithStreamingResponse: return EmailsResourceWithStreamingResponse(self._client.emails) @cached_property - def pages(self) -> pages.PagesResourceWithStreamingResponse: - from .resources.pages import PagesResourceWithStreamingResponse + def export(self) -> export.ExportResourceWithStreamingResponse: + from .resources.export import ExportResourceWithStreamingResponse - return PagesResourceWithStreamingResponse(self._client.pages) + return ExportResourceWithStreamingResponse(self._client.export) @cached_property - def documents(self) -> documents.DocumentsResourceWithStreamingResponse: - from .resources.documents import DocumentsResourceWithStreamingResponse + def pages(self) -> pages.PagesResourceWithStreamingResponse: + from .resources.pages import PagesResourceWithStreamingResponse - return DocumentsResourceWithStreamingResponse(self._client.documents) + return PagesResourceWithStreamingResponse(self._client.pages) @cached_property def project(self) -> project.ProjectResourceWithStreamingResponse: @@ -591,6 +623,12 @@ class AsyncUnlayerWithStreamedResponse: def __init__(self, client: AsyncUnlayer) -> None: self._client = client + @cached_property + def documents(self) -> documents.AsyncDocumentsResourceWithStreamingResponse: + from .resources.documents import AsyncDocumentsResourceWithStreamingResponse + + return AsyncDocumentsResourceWithStreamingResponse(self._client.documents) + @cached_property def emails(self) -> emails.AsyncEmailsResourceWithStreamingResponse: from .resources.emails import AsyncEmailsResourceWithStreamingResponse @@ -598,16 +636,16 @@ def emails(self) -> emails.AsyncEmailsResourceWithStreamingResponse: return AsyncEmailsResourceWithStreamingResponse(self._client.emails) @cached_property - def pages(self) -> pages.AsyncPagesResourceWithStreamingResponse: - from .resources.pages import AsyncPagesResourceWithStreamingResponse + def export(self) -> export.AsyncExportResourceWithStreamingResponse: + from .resources.export import AsyncExportResourceWithStreamingResponse - return AsyncPagesResourceWithStreamingResponse(self._client.pages) + return AsyncExportResourceWithStreamingResponse(self._client.export) @cached_property - def documents(self) -> documents.AsyncDocumentsResourceWithStreamingResponse: - from .resources.documents import AsyncDocumentsResourceWithStreamingResponse + def pages(self) -> pages.AsyncPagesResourceWithStreamingResponse: + from .resources.pages import AsyncPagesResourceWithStreamingResponse - return AsyncDocumentsResourceWithStreamingResponse(self._client.documents) + return AsyncPagesResourceWithStreamingResponse(self._client.pages) @cached_property def project(self) -> project.AsyncProjectResourceWithStreamingResponse: diff --git a/src/unlayer/pagination.py b/src/unlayer/pagination.py new file mode 100644 index 0000000..7dc91ef --- /dev/null +++ b/src/unlayer/pagination.py @@ -0,0 +1,68 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List, Generic, TypeVar, Optional +from typing_extensions import override + +from ._base_client import BasePage, PageInfo, BaseSyncPage, BaseAsyncPage + +__all__ = ["SyncCursorPage", "AsyncCursorPage"] + +_T = TypeVar("_T") + + +class SyncCursorPage(BaseSyncPage[_T], BasePage[_T], Generic[_T]): + data: List[_T] + next_cursor: Optional[str] = None + has_more: Optional[bool] = None + + @override + def _get_page_items(self) -> List[_T]: + data = self.data + if not data: + return [] + return data + + @override + def has_next_page(self) -> bool: + has_more = self.has_more + if has_more is not None and has_more is False: + return False + + return super().has_next_page() + + @override + def next_page_info(self) -> Optional[PageInfo]: + next_cursor = self.next_cursor + if not next_cursor: + return None + + return PageInfo(params={"cursor": next_cursor}) + + +class AsyncCursorPage(BaseAsyncPage[_T], BasePage[_T], Generic[_T]): + data: List[_T] + next_cursor: Optional[str] = None + has_more: Optional[bool] = None + + @override + def _get_page_items(self) -> List[_T]: + data = self.data + if not data: + return [] + return data + + @override + def has_next_page(self) -> bool: + has_more = self.has_more + if has_more is not None and has_more is False: + return False + + return super().has_next_page() + + @override + def next_page_info(self) -> Optional[PageInfo]: + next_cursor = self.next_cursor + if not next_cursor: + return None + + return PageInfo(params={"cursor": next_cursor}) diff --git a/src/unlayer/resources/__init__.py b/src/unlayer/resources/__init__.py index 98d5378..bf494f8 100644 --- a/src/unlayer/resources/__init__.py +++ b/src/unlayer/resources/__init__.py @@ -16,6 +16,14 @@ EmailsResourceWithStreamingResponse, AsyncEmailsResourceWithStreamingResponse, ) +from .export import ( + ExportResource, + AsyncExportResource, + ExportResourceWithRawResponse, + AsyncExportResourceWithRawResponse, + ExportResourceWithStreamingResponse, + AsyncExportResourceWithStreamingResponse, +) from .project import ( ProjectResource, AsyncProjectResource, @@ -34,24 +42,30 @@ ) __all__ = [ + "DocumentsResource", + "AsyncDocumentsResource", + "DocumentsResourceWithRawResponse", + "AsyncDocumentsResourceWithRawResponse", + "DocumentsResourceWithStreamingResponse", + "AsyncDocumentsResourceWithStreamingResponse", "EmailsResource", "AsyncEmailsResource", "EmailsResourceWithRawResponse", "AsyncEmailsResourceWithRawResponse", "EmailsResourceWithStreamingResponse", "AsyncEmailsResourceWithStreamingResponse", + "ExportResource", + "AsyncExportResource", + "ExportResourceWithRawResponse", + "AsyncExportResourceWithRawResponse", + "ExportResourceWithStreamingResponse", + "AsyncExportResourceWithStreamingResponse", "PagesResource", "AsyncPagesResource", "PagesResourceWithRawResponse", "AsyncPagesResourceWithRawResponse", "PagesResourceWithStreamingResponse", "AsyncPagesResourceWithStreamingResponse", - "DocumentsResource", - "AsyncDocumentsResource", - "DocumentsResourceWithRawResponse", - "AsyncDocumentsResourceWithRawResponse", - "DocumentsResourceWithStreamingResponse", - "AsyncDocumentsResourceWithStreamingResponse", "ProjectResource", "AsyncProjectResource", "ProjectResourceWithRawResponse", diff --git a/src/unlayer/resources/documents.py b/src/unlayer/resources/documents.py index 011d412..f02c2fe 100644 --- a/src/unlayer/resources/documents.py +++ b/src/unlayer/resources/documents.py @@ -53,7 +53,7 @@ def documents_retrieve( self, id: str, *, - project_id: str | Omit = omit, + project_id: str, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, @@ -65,7 +65,7 @@ def documents_retrieve( Retrieve details of a previously generated document. Args: - project_id: The project ID (required for PAT auth, not needed for API Key auth) + project_id: The project ID extra_headers: Send extra headers @@ -94,8 +94,8 @@ def documents_retrieve( def generate_create( self, *, - design: Dict[str, object], - project_id: str | Omit = omit, + project_id: str, + design: Dict[str, object] | Omit = omit, filename: str | Omit = omit, html: str | Omit = omit, merge_tags: Dict[str, str] | Omit = omit, @@ -111,9 +111,9 @@ def generate_create( Generate PDF document from JSON design, HTML content, or URL. Args: - design: Proprietary design format JSON + project_id: The project ID - project_id: The project ID (required for PAT auth, not needed for API Key auth) + design: Proprietary design format JSON filename: Optional filename for the generated PDF @@ -158,8 +158,8 @@ def generate_create( def generate_template_template( self, *, + project_id: str, template_id: str, - project_id: str | Omit = omit, filename: str | Omit = omit, merge_tags: Dict[str, str] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. @@ -173,9 +173,9 @@ def generate_template_template( Generate PDF document from an existing template with merge tags. Args: - template_id: ID of the template to use for generation + project_id: The project ID - project_id: The project ID (required for PAT auth, not needed for API Key auth) + template_id: ID of the template to use for generation filename: Optional filename for the generated PDF @@ -237,7 +237,7 @@ async def documents_retrieve( self, id: str, *, - project_id: str | Omit = omit, + project_id: str, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, @@ -249,7 +249,7 @@ async def documents_retrieve( Retrieve details of a previously generated document. Args: - project_id: The project ID (required for PAT auth, not needed for API Key auth) + project_id: The project ID extra_headers: Send extra headers @@ -278,8 +278,8 @@ async def documents_retrieve( async def generate_create( self, *, - design: Dict[str, object], - project_id: str | Omit = omit, + project_id: str, + design: Dict[str, object] | Omit = omit, filename: str | Omit = omit, html: str | Omit = omit, merge_tags: Dict[str, str] | Omit = omit, @@ -295,9 +295,9 @@ async def generate_create( Generate PDF document from JSON design, HTML content, or URL. Args: - design: Proprietary design format JSON + project_id: The project ID - project_id: The project ID (required for PAT auth, not needed for API Key auth) + design: Proprietary design format JSON filename: Optional filename for the generated PDF @@ -342,8 +342,8 @@ async def generate_create( async def generate_template_template( self, *, + project_id: str, template_id: str, - project_id: str | Omit = omit, filename: str | Omit = omit, merge_tags: Dict[str, str] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. @@ -357,9 +357,9 @@ async def generate_template_template( Generate PDF document from an existing template with merge tags. Args: - template_id: ID of the template to use for generation + project_id: The project ID - project_id: The project ID (required for PAT auth, not needed for API Key auth) + template_id: ID of the template to use for generation filename: Optional filename for the generated PDF diff --git a/src/unlayer/resources/emails.py b/src/unlayer/resources/emails.py index 2341242..3fb37a3 100644 --- a/src/unlayer/resources/emails.py +++ b/src/unlayer/resources/emails.py @@ -55,7 +55,7 @@ def retrieve( self, id: str, *, - project_id: str | Omit = omit, + project_id: str, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, @@ -67,7 +67,7 @@ def retrieve( Retrieve details of a previously sent email. Args: - project_id: The project ID (required for PAT auth, not needed for API Key auth) + project_id: The project ID extra_headers: Send extra headers @@ -94,8 +94,8 @@ def retrieve( def render_create( self, *, + project_id: str, design: Dict[str, object], - project_id: str | Omit = omit, merge_tags: Dict[str, str] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. @@ -108,9 +108,9 @@ def render_create( Convert design JSON to HTML with optional merge tags. Args: - design: Proprietary design format JSON + project_id: The project ID - project_id: The project ID (required for PAT auth, not needed for API Key auth) + design: Proprietary design format JSON merge_tags: Optional merge tags for personalization @@ -144,9 +144,9 @@ def render_create( def send_create( self, *, - design: Dict[str, object], + project_id: str, to: str, - project_id: str | Omit = omit, + design: Dict[str, object] | Omit = omit, html: str | Omit = omit, merge_tags: Dict[str, str] | Omit = omit, subject: str | Omit = omit, @@ -161,11 +161,11 @@ def send_create( Send email with design JSON or HTML content. Args: - design: Proprietary design format JSON + project_id: The project ID to: Recipient email address - project_id: The project ID (required for PAT auth, not needed for API Key auth) + design: Proprietary design format JSON html: HTML content to send @@ -185,8 +185,8 @@ def send_create( "/emails/v1/send", body=maybe_transform( { - "design": design, "to": to, + "design": design, "html": html, "merge_tags": merge_tags, "subject": subject, @@ -206,9 +206,9 @@ def send_create( def send_template_template( self, *, + project_id: str, template_id: str, to: str, - project_id: str | Omit = omit, merge_tags: Dict[str, str] | Omit = omit, subject: str | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. @@ -222,12 +222,12 @@ def send_template_template( Send email using an existing template with merge tags. Args: + project_id: The project ID + template_id: ID of the template to use to: Recipient email address - project_id: The project ID (required for PAT auth, not needed for API Key auth) - merge_tags: Optional merge tags for personalization subject: Email subject line (optional, uses template default if not provided) @@ -288,7 +288,7 @@ async def retrieve( self, id: str, *, - project_id: str | Omit = omit, + project_id: str, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, @@ -300,7 +300,7 @@ async def retrieve( Retrieve details of a previously sent email. Args: - project_id: The project ID (required for PAT auth, not needed for API Key auth) + project_id: The project ID extra_headers: Send extra headers @@ -329,8 +329,8 @@ async def retrieve( async def render_create( self, *, + project_id: str, design: Dict[str, object], - project_id: str | Omit = omit, merge_tags: Dict[str, str] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. @@ -343,9 +343,9 @@ async def render_create( Convert design JSON to HTML with optional merge tags. Args: - design: Proprietary design format JSON + project_id: The project ID - project_id: The project ID (required for PAT auth, not needed for API Key auth) + design: Proprietary design format JSON merge_tags: Optional merge tags for personalization @@ -381,9 +381,9 @@ async def render_create( async def send_create( self, *, - design: Dict[str, object], + project_id: str, to: str, - project_id: str | Omit = omit, + design: Dict[str, object] | Omit = omit, html: str | Omit = omit, merge_tags: Dict[str, str] | Omit = omit, subject: str | Omit = omit, @@ -398,11 +398,11 @@ async def send_create( Send email with design JSON or HTML content. Args: - design: Proprietary design format JSON + project_id: The project ID to: Recipient email address - project_id: The project ID (required for PAT auth, not needed for API Key auth) + design: Proprietary design format JSON html: HTML content to send @@ -422,8 +422,8 @@ async def send_create( "/emails/v1/send", body=await async_maybe_transform( { - "design": design, "to": to, + "design": design, "html": html, "merge_tags": merge_tags, "subject": subject, @@ -445,9 +445,9 @@ async def send_create( async def send_template_template( self, *, + project_id: str, template_id: str, to: str, - project_id: str | Omit = omit, merge_tags: Dict[str, str] | Omit = omit, subject: str | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. @@ -461,12 +461,12 @@ async def send_template_template( Send email using an existing template with merge tags. Args: + project_id: The project ID + template_id: ID of the template to use to: Recipient email address - project_id: The project ID (required for PAT auth, not needed for API Key auth) - merge_tags: Optional merge tags for personalization subject: Email subject line (optional, uses template default if not provided) diff --git a/src/unlayer/resources/export.py b/src/unlayer/resources/export.py new file mode 100644 index 0000000..35e810f --- /dev/null +++ b/src/unlayer/resources/export.py @@ -0,0 +1,442 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import httpx + +from ..types import export_pdf_list_params, export_zip_list_params, export_html_list_params, export_image_list_params +from .._types import Body, Query, Headers, NotGiven, not_given +from .._utils import maybe_transform, async_maybe_transform +from .._compat import cached_property +from .._resource import SyncAPIResource, AsyncAPIResource +from .._response import ( + to_raw_response_wrapper, + to_streamed_response_wrapper, + async_to_raw_response_wrapper, + async_to_streamed_response_wrapper, +) +from .._base_client import make_request_options +from ..types.export_pdf_list_response import ExportPdfListResponse +from ..types.export_zip_list_response import ExportZipListResponse +from ..types.export_html_list_response import ExportHTMLListResponse +from ..types.export_image_list_response import ExportImageListResponse + +__all__ = ["ExportResource", "AsyncExportResource"] + + +class ExportResource(SyncAPIResource): + @cached_property + def with_raw_response(self) -> ExportResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/stainless-sdks/unlayer-python#accessing-raw-response-data-eg-headers + """ + return ExportResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> ExportResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/stainless-sdks/unlayer-python#with_streaming_response + """ + return ExportResourceWithStreamingResponse(self) + + def html_list( + self, + *, + project_id: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> ExportHTMLListResponse: + """ + Export design to HTML. + + Args: + project_id: The project ID + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._get( + "/export/v3/html", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform({"project_id": project_id}, export_html_list_params.ExportHTMLListParams), + ), + cast_to=ExportHTMLListResponse, + ) + + def image_list( + self, + *, + project_id: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> ExportImageListResponse: + """ + Export design to image. + + Args: + project_id: The project ID + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._get( + "/export/v3/image", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform({"project_id": project_id}, export_image_list_params.ExportImageListParams), + ), + cast_to=ExportImageListResponse, + ) + + def pdf_list( + self, + *, + project_id: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> ExportPdfListResponse: + """ + Export design to PDF. + + Args: + project_id: The project ID + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._get( + "/export/v3/pdf", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform({"project_id": project_id}, export_pdf_list_params.ExportPdfListParams), + ), + cast_to=ExportPdfListResponse, + ) + + def zip_list( + self, + *, + project_id: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> ExportZipListResponse: + """ + Export design to ZIP archive. + + Args: + project_id: The project ID + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._get( + "/export/v3/zip", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform({"project_id": project_id}, export_zip_list_params.ExportZipListParams), + ), + cast_to=ExportZipListResponse, + ) + + +class AsyncExportResource(AsyncAPIResource): + @cached_property + def with_raw_response(self) -> AsyncExportResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/stainless-sdks/unlayer-python#accessing-raw-response-data-eg-headers + """ + return AsyncExportResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncExportResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/stainless-sdks/unlayer-python#with_streaming_response + """ + return AsyncExportResourceWithStreamingResponse(self) + + async def html_list( + self, + *, + project_id: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> ExportHTMLListResponse: + """ + Export design to HTML. + + Args: + project_id: The project ID + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return await self._get( + "/export/v3/html", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=await async_maybe_transform( + {"project_id": project_id}, export_html_list_params.ExportHTMLListParams + ), + ), + cast_to=ExportHTMLListResponse, + ) + + async def image_list( + self, + *, + project_id: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> ExportImageListResponse: + """ + Export design to image. + + Args: + project_id: The project ID + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return await self._get( + "/export/v3/image", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=await async_maybe_transform( + {"project_id": project_id}, export_image_list_params.ExportImageListParams + ), + ), + cast_to=ExportImageListResponse, + ) + + async def pdf_list( + self, + *, + project_id: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> ExportPdfListResponse: + """ + Export design to PDF. + + Args: + project_id: The project ID + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return await self._get( + "/export/v3/pdf", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=await async_maybe_transform( + {"project_id": project_id}, export_pdf_list_params.ExportPdfListParams + ), + ), + cast_to=ExportPdfListResponse, + ) + + async def zip_list( + self, + *, + project_id: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> ExportZipListResponse: + """ + Export design to ZIP archive. + + Args: + project_id: The project ID + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return await self._get( + "/export/v3/zip", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=await async_maybe_transform( + {"project_id": project_id}, export_zip_list_params.ExportZipListParams + ), + ), + cast_to=ExportZipListResponse, + ) + + +class ExportResourceWithRawResponse: + def __init__(self, export: ExportResource) -> None: + self._export = export + + self.html_list = to_raw_response_wrapper( + export.html_list, + ) + self.image_list = to_raw_response_wrapper( + export.image_list, + ) + self.pdf_list = to_raw_response_wrapper( + export.pdf_list, + ) + self.zip_list = to_raw_response_wrapper( + export.zip_list, + ) + + +class AsyncExportResourceWithRawResponse: + def __init__(self, export: AsyncExportResource) -> None: + self._export = export + + self.html_list = async_to_raw_response_wrapper( + export.html_list, + ) + self.image_list = async_to_raw_response_wrapper( + export.image_list, + ) + self.pdf_list = async_to_raw_response_wrapper( + export.pdf_list, + ) + self.zip_list = async_to_raw_response_wrapper( + export.zip_list, + ) + + +class ExportResourceWithStreamingResponse: + def __init__(self, export: ExportResource) -> None: + self._export = export + + self.html_list = to_streamed_response_wrapper( + export.html_list, + ) + self.image_list = to_streamed_response_wrapper( + export.image_list, + ) + self.pdf_list = to_streamed_response_wrapper( + export.pdf_list, + ) + self.zip_list = to_streamed_response_wrapper( + export.zip_list, + ) + + +class AsyncExportResourceWithStreamingResponse: + def __init__(self, export: AsyncExportResource) -> None: + self._export = export + + self.html_list = async_to_streamed_response_wrapper( + export.html_list, + ) + self.image_list = async_to_streamed_response_wrapper( + export.image_list, + ) + self.pdf_list = async_to_streamed_response_wrapper( + export.pdf_list, + ) + self.zip_list = async_to_streamed_response_wrapper( + export.zip_list, + ) diff --git a/src/unlayer/resources/pages.py b/src/unlayer/resources/pages.py index 1aa21ca..349e648 100644 --- a/src/unlayer/resources/pages.py +++ b/src/unlayer/resources/pages.py @@ -46,8 +46,8 @@ def with_streaming_response(self) -> PagesResourceWithStreamingResponse: def render_create( self, *, + project_id: str, design: Dict[str, object], - project_id: str | Omit = omit, merge_tags: Dict[str, str] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. @@ -60,9 +60,9 @@ def render_create( Convert page design JSON to HTML with optional merge tags. Args: - design: Proprietary design format JSON + project_id: The project ID - project_id: The project ID (required for PAT auth, not needed for API Key auth) + design: Proprietary design format JSON merge_tags: Optional merge tags for personalization @@ -117,8 +117,8 @@ def with_streaming_response(self) -> AsyncPagesResourceWithStreamingResponse: async def render_create( self, *, + project_id: str, design: Dict[str, object], - project_id: str | Omit = omit, merge_tags: Dict[str, str] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. @@ -131,9 +131,9 @@ async def render_create( Convert page design JSON to HTML with optional merge tags. Args: - design: Proprietary design format JSON + project_id: The project ID - project_id: The project ID (required for PAT auth, not needed for API Key auth) + design: Proprietary design format JSON merge_tags: Optional merge tags for personalization diff --git a/src/unlayer/resources/project.py b/src/unlayer/resources/project.py index a74e7c9..694a37d 100644 --- a/src/unlayer/resources/project.py +++ b/src/unlayer/resources/project.py @@ -2,21 +2,20 @@ from __future__ import annotations +from typing_extensions import Literal + import httpx from ..types import ( project_current_list_params, project_domains_list_params, - project_api_keys_list_params, project_domains_create_params, project_domains_update_params, project_templates_list_params, - project_api_keys_create_params, - project_api_keys_update_params, project_templates_create_params, project_templates_update_params, ) -from .._types import Body, Omit, Query, Headers, NoneType, NotGiven, SequenceNotStr, omit, not_given +from .._types import Body, Omit, Query, Headers, NoneType, NotGiven, omit, not_given from .._utils import maybe_transform, async_maybe_transform from .._compat import cached_property from .._resource import SyncAPIResource, AsyncAPIResource @@ -26,22 +25,17 @@ async_to_raw_response_wrapper, async_to_streamed_response_wrapper, ) -from .._base_client import make_request_options -from ..types.project_tokens_list_response import ProjectTokensListResponse +from ..pagination import SyncCursorPage, AsyncCursorPage +from .._base_client import AsyncPaginator, make_request_options from ..types.project_current_list_response import ProjectCurrentListResponse from ..types.project_domains_list_response import ProjectDomainsListResponse -from ..types.project_api_keys_list_response import ProjectAPIKeysListResponse -from ..types.project_tokens_delete_response import ProjectTokensDeleteResponse from ..types.project_domains_create_response import ProjectDomainsCreateResponse from ..types.project_domains_update_response import ProjectDomainsUpdateResponse from ..types.project_templates_list_response import ProjectTemplatesListResponse -from ..types.project_api_keys_create_response import ProjectAPIKeysCreateResponse -from ..types.project_api_keys_update_response import ProjectAPIKeysUpdateResponse from ..types.project_workspaces_list_response import ProjectWorkspacesListResponse from ..types.project_domains_retrieve_response import ProjectDomainsRetrieveResponse from ..types.project_templates_create_response import ProjectTemplatesCreateResponse from ..types.project_templates_update_response import ProjectTemplatesUpdateResponse -from ..types.project_api_keys_retrieve_response import ProjectAPIKeysRetrieveResponse from ..types.project_templates_retrieve_response import ProjectTemplatesRetrieveResponse from ..types.project_workspaces_retrieve_response import ProjectWorkspacesRetrieveResponse @@ -68,214 +62,6 @@ def with_streaming_response(self) -> ProjectResourceWithStreamingResponse: """ return ProjectResourceWithStreamingResponse(self) - def api_keys_create( - self, - *, - project_id: str, - name: str, - domains: SequenceNotStr[str] | Omit = omit, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> ProjectAPIKeysCreateResponse: - """ - Create a new API key for the project. - - Args: - project_id: The project ID to create API key for - - name: Name for the API key - - domains: Allowed domains for this API key - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - return self._post( - "/project/v1/api-keys", - body=maybe_transform( - { - "name": name, - "domains": domains, - }, - project_api_keys_create_params.ProjectAPIKeysCreateParams, - ), - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query=maybe_transform( - {"project_id": project_id}, project_api_keys_create_params.ProjectAPIKeysCreateParams - ), - ), - cast_to=ProjectAPIKeysCreateResponse, - ) - - def api_keys_delete( - self, - id: str, - *, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> None: - """ - Revoke API key. - - Args: - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - if not id: - raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") - extra_headers = {"Accept": "*/*", **(extra_headers or {})} - return self._delete( - f"/project/v1/api-keys/{id}", - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=NoneType, - ) - - def api_keys_list( - self, - *, - project_id: str, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> ProjectAPIKeysListResponse: - """ - List all API keys for the project. - - Args: - project_id: The project ID to get API keys for - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - return self._get( - "/project/v1/api-keys", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query=maybe_transform( - {"project_id": project_id}, project_api_keys_list_params.ProjectAPIKeysListParams - ), - ), - cast_to=ProjectAPIKeysListResponse, - ) - - def api_keys_retrieve( - self, - id: str, - *, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> ProjectAPIKeysRetrieveResponse: - """ - Get API key details by ID. - - Args: - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - if not id: - raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") - return self._get( - f"/project/v1/api-keys/{id}", - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=ProjectAPIKeysRetrieveResponse, - ) - - def api_keys_update( - self, - id: str, - *, - active: bool | Omit = omit, - domains: SequenceNotStr[str] | Omit = omit, - name: str | Omit = omit, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> ProjectAPIKeysUpdateResponse: - """ - Update API key settings. - - Args: - active: Whether the API key is active - - domains: Updated allowed domains - - name: Updated name for the API key - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - if not id: - raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") - return self._put( - f"/project/v1/api-keys/{id}", - body=maybe_transform( - { - "active": active, - "domains": domains, - "name": name, - }, - project_api_keys_update_params.ProjectAPIKeysUpdateParams, - ), - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=ProjectAPIKeysUpdateResponse, - ) - def current_list( self, *, @@ -329,7 +115,7 @@ def domains_create( Add a new domain to the project. Args: - project_id: The project ID to add domain to + project_id: The project ID domain: Domain name to add @@ -405,7 +191,7 @@ def domains_list( List all domains for the project. Args: - project_id: The project ID to get domains for + project_id: The project ID extra_headers: Send extra headers @@ -502,8 +288,7 @@ def templates_create( *, project_id: str, name: str, - body: str | Omit = omit, - subject: str | Omit = omit, + display_mode: Literal["email", "web", "document"] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, @@ -515,13 +300,11 @@ def templates_create( Create a new project template. Args: - project_id: The project ID to create template for + project_id: The project ID to create the template in name: Template name - body: Email body content - - subject: Email subject line + display_mode: Template type/display mode extra_headers: Send extra headers @@ -536,8 +319,7 @@ def templates_create( body=maybe_transform( { "name": name, - "body": body, - "subject": subject, + "display_mode": display_mode, }, project_templates_create_params.ProjectTemplatesCreateParams, ), @@ -591,18 +373,32 @@ def templates_list( self, *, project_id: str, + cursor: str | Omit = omit, + display_mode: Literal["email", "web", "document"] | Omit = omit, + limit: int | Omit = omit, + name: str | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> ProjectTemplatesListResponse: - """ - Get all project templates. + ) -> SyncCursorPage[ProjectTemplatesListResponse]: + """List project templates with cursor-based pagination. + + Returns templates in + descending order by update time. Args: - project_id: The project ID to get templates for + project_id: The project ID to list templates for + + cursor: Pagination cursor from previous response + + display_mode: Filter by template type + + limit: Number of templates to return (1-100) + + name: Filter by name (case-insensitive search) extra_headers: Send extra headers @@ -612,18 +408,26 @@ def templates_list( timeout: Override the client-level default timeout for this request, in seconds """ - return self._get( + return self._get_api_list( "/project/v1/templates", + page=SyncCursorPage[ProjectTemplatesListResponse], options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout, query=maybe_transform( - {"project_id": project_id}, project_templates_list_params.ProjectTemplatesListParams + { + "project_id": project_id, + "cursor": cursor, + "display_mode": display_mode, + "limit": limit, + "name": name, + }, + project_templates_list_params.ProjectTemplatesListParams, ), ), - cast_to=ProjectTemplatesListResponse, + model=ProjectTemplatesListResponse, ) def templates_retrieve( @@ -709,59 +513,6 @@ def templates_update( cast_to=ProjectTemplatesUpdateResponse, ) - def tokens_delete( - self, - token_id: str, - *, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> ProjectTokensDeleteResponse: - """Delete a personal access token. - - You can only delete your own tokens. - - Args: - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - if not token_id: - raise ValueError(f"Expected a non-empty value for `token_id` but received {token_id!r}") - return self._delete( - f"/project/v1/tokens/{token_id}", - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=ProjectTokensDeleteResponse, - ) - - def tokens_list( - self, - *, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> ProjectTokensListResponse: - """List all personal access tokens for the authenticated user.""" - return self._get( - "/project/v1/tokens", - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=ProjectTokensListResponse, - ) - def workspaces_list( self, *, @@ -835,214 +586,6 @@ def with_streaming_response(self) -> AsyncProjectResourceWithStreamingResponse: """ return AsyncProjectResourceWithStreamingResponse(self) - async def api_keys_create( - self, - *, - project_id: str, - name: str, - domains: SequenceNotStr[str] | Omit = omit, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> ProjectAPIKeysCreateResponse: - """ - Create a new API key for the project. - - Args: - project_id: The project ID to create API key for - - name: Name for the API key - - domains: Allowed domains for this API key - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - return await self._post( - "/project/v1/api-keys", - body=await async_maybe_transform( - { - "name": name, - "domains": domains, - }, - project_api_keys_create_params.ProjectAPIKeysCreateParams, - ), - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query=await async_maybe_transform( - {"project_id": project_id}, project_api_keys_create_params.ProjectAPIKeysCreateParams - ), - ), - cast_to=ProjectAPIKeysCreateResponse, - ) - - async def api_keys_delete( - self, - id: str, - *, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> None: - """ - Revoke API key. - - Args: - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - if not id: - raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") - extra_headers = {"Accept": "*/*", **(extra_headers or {})} - return await self._delete( - f"/project/v1/api-keys/{id}", - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=NoneType, - ) - - async def api_keys_list( - self, - *, - project_id: str, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> ProjectAPIKeysListResponse: - """ - List all API keys for the project. - - Args: - project_id: The project ID to get API keys for - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - return await self._get( - "/project/v1/api-keys", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query=await async_maybe_transform( - {"project_id": project_id}, project_api_keys_list_params.ProjectAPIKeysListParams - ), - ), - cast_to=ProjectAPIKeysListResponse, - ) - - async def api_keys_retrieve( - self, - id: str, - *, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> ProjectAPIKeysRetrieveResponse: - """ - Get API key details by ID. - - Args: - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - if not id: - raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") - return await self._get( - f"/project/v1/api-keys/{id}", - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=ProjectAPIKeysRetrieveResponse, - ) - - async def api_keys_update( - self, - id: str, - *, - active: bool | Omit = omit, - domains: SequenceNotStr[str] | Omit = omit, - name: str | Omit = omit, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> ProjectAPIKeysUpdateResponse: - """ - Update API key settings. - - Args: - active: Whether the API key is active - - domains: Updated allowed domains - - name: Updated name for the API key - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - if not id: - raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") - return await self._put( - f"/project/v1/api-keys/{id}", - body=await async_maybe_transform( - { - "active": active, - "domains": domains, - "name": name, - }, - project_api_keys_update_params.ProjectAPIKeysUpdateParams, - ), - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=ProjectAPIKeysUpdateResponse, - ) - async def current_list( self, *, @@ -1098,7 +641,7 @@ async def domains_create( Add a new domain to the project. Args: - project_id: The project ID to add domain to + project_id: The project ID domain: Domain name to add @@ -1176,7 +719,7 @@ async def domains_list( List all domains for the project. Args: - project_id: The project ID to get domains for + project_id: The project ID extra_headers: Send extra headers @@ -1277,8 +820,7 @@ async def templates_create( *, project_id: str, name: str, - body: str | Omit = omit, - subject: str | Omit = omit, + display_mode: Literal["email", "web", "document"] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, @@ -1290,13 +832,11 @@ async def templates_create( Create a new project template. Args: - project_id: The project ID to create template for + project_id: The project ID to create the template in name: Template name - body: Email body content - - subject: Email subject line + display_mode: Template type/display mode extra_headers: Send extra headers @@ -1311,8 +851,7 @@ async def templates_create( body=await async_maybe_transform( { "name": name, - "body": body, - "subject": subject, + "display_mode": display_mode, }, project_templates_create_params.ProjectTemplatesCreateParams, ), @@ -1362,22 +901,36 @@ async def templates_delete( cast_to=NoneType, ) - async def templates_list( + def templates_list( self, *, project_id: str, + cursor: str | Omit = omit, + display_mode: Literal["email", "web", "document"] | Omit = omit, + limit: int | Omit = omit, + name: str | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> ProjectTemplatesListResponse: - """ - Get all project templates. + ) -> AsyncPaginator[ProjectTemplatesListResponse, AsyncCursorPage[ProjectTemplatesListResponse]]: + """List project templates with cursor-based pagination. + + Returns templates in + descending order by update time. Args: - project_id: The project ID to get templates for + project_id: The project ID to list templates for + + cursor: Pagination cursor from previous response + + display_mode: Filter by template type + + limit: Number of templates to return (1-100) + + name: Filter by name (case-insensitive search) extra_headers: Send extra headers @@ -1387,18 +940,26 @@ async def templates_list( timeout: Override the client-level default timeout for this request, in seconds """ - return await self._get( + return self._get_api_list( "/project/v1/templates", + page=AsyncCursorPage[ProjectTemplatesListResponse], options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout, - query=await async_maybe_transform( - {"project_id": project_id}, project_templates_list_params.ProjectTemplatesListParams + query=maybe_transform( + { + "project_id": project_id, + "cursor": cursor, + "display_mode": display_mode, + "limit": limit, + "name": name, + }, + project_templates_list_params.ProjectTemplatesListParams, ), ), - cast_to=ProjectTemplatesListResponse, + model=ProjectTemplatesListResponse, ) async def templates_retrieve( @@ -1484,59 +1045,6 @@ async def templates_update( cast_to=ProjectTemplatesUpdateResponse, ) - async def tokens_delete( - self, - token_id: str, - *, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> ProjectTokensDeleteResponse: - """Delete a personal access token. - - You can only delete your own tokens. - - Args: - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - if not token_id: - raise ValueError(f"Expected a non-empty value for `token_id` but received {token_id!r}") - return await self._delete( - f"/project/v1/tokens/{token_id}", - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=ProjectTokensDeleteResponse, - ) - - async def tokens_list( - self, - *, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> ProjectTokensListResponse: - """List all personal access tokens for the authenticated user.""" - return await self._get( - "/project/v1/tokens", - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=ProjectTokensListResponse, - ) - async def workspaces_list( self, *, @@ -1594,21 +1102,6 @@ class ProjectResourceWithRawResponse: def __init__(self, project: ProjectResource) -> None: self._project = project - self.api_keys_create = to_raw_response_wrapper( - project.api_keys_create, - ) - self.api_keys_delete = to_raw_response_wrapper( - project.api_keys_delete, - ) - self.api_keys_list = to_raw_response_wrapper( - project.api_keys_list, - ) - self.api_keys_retrieve = to_raw_response_wrapper( - project.api_keys_retrieve, - ) - self.api_keys_update = to_raw_response_wrapper( - project.api_keys_update, - ) self.current_list = to_raw_response_wrapper( project.current_list, ) @@ -1642,12 +1135,6 @@ def __init__(self, project: ProjectResource) -> None: self.templates_update = to_raw_response_wrapper( project.templates_update, ) - self.tokens_delete = to_raw_response_wrapper( - project.tokens_delete, - ) - self.tokens_list = to_raw_response_wrapper( - project.tokens_list, - ) self.workspaces_list = to_raw_response_wrapper( project.workspaces_list, ) @@ -1660,21 +1147,6 @@ class AsyncProjectResourceWithRawResponse: def __init__(self, project: AsyncProjectResource) -> None: self._project = project - self.api_keys_create = async_to_raw_response_wrapper( - project.api_keys_create, - ) - self.api_keys_delete = async_to_raw_response_wrapper( - project.api_keys_delete, - ) - self.api_keys_list = async_to_raw_response_wrapper( - project.api_keys_list, - ) - self.api_keys_retrieve = async_to_raw_response_wrapper( - project.api_keys_retrieve, - ) - self.api_keys_update = async_to_raw_response_wrapper( - project.api_keys_update, - ) self.current_list = async_to_raw_response_wrapper( project.current_list, ) @@ -1708,12 +1180,6 @@ def __init__(self, project: AsyncProjectResource) -> None: self.templates_update = async_to_raw_response_wrapper( project.templates_update, ) - self.tokens_delete = async_to_raw_response_wrapper( - project.tokens_delete, - ) - self.tokens_list = async_to_raw_response_wrapper( - project.tokens_list, - ) self.workspaces_list = async_to_raw_response_wrapper( project.workspaces_list, ) @@ -1726,21 +1192,6 @@ class ProjectResourceWithStreamingResponse: def __init__(self, project: ProjectResource) -> None: self._project = project - self.api_keys_create = to_streamed_response_wrapper( - project.api_keys_create, - ) - self.api_keys_delete = to_streamed_response_wrapper( - project.api_keys_delete, - ) - self.api_keys_list = to_streamed_response_wrapper( - project.api_keys_list, - ) - self.api_keys_retrieve = to_streamed_response_wrapper( - project.api_keys_retrieve, - ) - self.api_keys_update = to_streamed_response_wrapper( - project.api_keys_update, - ) self.current_list = to_streamed_response_wrapper( project.current_list, ) @@ -1774,12 +1225,6 @@ def __init__(self, project: ProjectResource) -> None: self.templates_update = to_streamed_response_wrapper( project.templates_update, ) - self.tokens_delete = to_streamed_response_wrapper( - project.tokens_delete, - ) - self.tokens_list = to_streamed_response_wrapper( - project.tokens_list, - ) self.workspaces_list = to_streamed_response_wrapper( project.workspaces_list, ) @@ -1792,21 +1237,6 @@ class AsyncProjectResourceWithStreamingResponse: def __init__(self, project: AsyncProjectResource) -> None: self._project = project - self.api_keys_create = async_to_streamed_response_wrapper( - project.api_keys_create, - ) - self.api_keys_delete = async_to_streamed_response_wrapper( - project.api_keys_delete, - ) - self.api_keys_list = async_to_streamed_response_wrapper( - project.api_keys_list, - ) - self.api_keys_retrieve = async_to_streamed_response_wrapper( - project.api_keys_retrieve, - ) - self.api_keys_update = async_to_streamed_response_wrapper( - project.api_keys_update, - ) self.current_list = async_to_streamed_response_wrapper( project.current_list, ) @@ -1840,12 +1270,6 @@ def __init__(self, project: AsyncProjectResource) -> None: self.templates_update = async_to_streamed_response_wrapper( project.templates_update, ) - self.tokens_delete = async_to_streamed_response_wrapper( - project.tokens_delete, - ) - self.tokens_list = async_to_streamed_response_wrapper( - project.tokens_list, - ) self.workspaces_list = async_to_streamed_response_wrapper( project.workspaces_list, ) diff --git a/src/unlayer/types/__init__.py b/src/unlayer/types/__init__.py index ad295c1..702bb51 100644 --- a/src/unlayer/types/__init__.py +++ b/src/unlayer/types/__init__.py @@ -3,41 +3,40 @@ from __future__ import annotations from .email_retrieve_params import EmailRetrieveParams as EmailRetrieveParams +from .export_pdf_list_params import ExportPdfListParams as ExportPdfListParams +from .export_zip_list_params import ExportZipListParams as ExportZipListParams from .email_retrieve_response import EmailRetrieveResponse as EmailRetrieveResponse +from .export_html_list_params import ExportHTMLListParams as ExportHTMLListParams from .email_send_create_params import EmailSendCreateParams as EmailSendCreateParams +from .export_image_list_params import ExportImageListParams as ExportImageListParams +from .export_pdf_list_response import ExportPdfListResponse as ExportPdfListResponse +from .export_zip_list_response import ExportZipListResponse as ExportZipListResponse +from .export_html_list_response import ExportHTMLListResponse as ExportHTMLListResponse from .page_render_create_params import PageRenderCreateParams as PageRenderCreateParams from .email_render_create_params import EmailRenderCreateParams as EmailRenderCreateParams from .email_send_create_response import EmailSendCreateResponse as EmailSendCreateResponse +from .export_image_list_response import ExportImageListResponse as ExportImageListResponse from .page_render_create_response import PageRenderCreateResponse as PageRenderCreateResponse from .project_current_list_params import ProjectCurrentListParams as ProjectCurrentListParams from .project_domains_list_params import ProjectDomainsListParams as ProjectDomainsListParams from .email_render_create_response import EmailRenderCreateResponse as EmailRenderCreateResponse -from .project_api_keys_list_params import ProjectAPIKeysListParams as ProjectAPIKeysListParams -from .project_tokens_list_response import ProjectTokensListResponse as ProjectTokensListResponse from .project_current_list_response import ProjectCurrentListResponse as ProjectCurrentListResponse from .project_domains_create_params import ProjectDomainsCreateParams as ProjectDomainsCreateParams from .project_domains_list_response import ProjectDomainsListResponse as ProjectDomainsListResponse from .project_domains_update_params import ProjectDomainsUpdateParams as ProjectDomainsUpdateParams from .project_templates_list_params import ProjectTemplatesListParams as ProjectTemplatesListParams -from .project_api_keys_create_params import ProjectAPIKeysCreateParams as ProjectAPIKeysCreateParams -from .project_api_keys_list_response import ProjectAPIKeysListResponse as ProjectAPIKeysListResponse -from .project_api_keys_update_params import ProjectAPIKeysUpdateParams as ProjectAPIKeysUpdateParams -from .project_tokens_delete_response import ProjectTokensDeleteResponse as ProjectTokensDeleteResponse from .document_generate_create_params import DocumentGenerateCreateParams as DocumentGenerateCreateParams from .project_domains_create_response import ProjectDomainsCreateResponse as ProjectDomainsCreateResponse from .project_domains_update_response import ProjectDomainsUpdateResponse as ProjectDomainsUpdateResponse from .project_templates_create_params import ProjectTemplatesCreateParams as ProjectTemplatesCreateParams from .project_templates_list_response import ProjectTemplatesListResponse as ProjectTemplatesListResponse from .project_templates_update_params import ProjectTemplatesUpdateParams as ProjectTemplatesUpdateParams -from .project_api_keys_create_response import ProjectAPIKeysCreateResponse as ProjectAPIKeysCreateResponse -from .project_api_keys_update_response import ProjectAPIKeysUpdateResponse as ProjectAPIKeysUpdateResponse from .project_workspaces_list_response import ProjectWorkspacesListResponse as ProjectWorkspacesListResponse from .document_generate_create_response import DocumentGenerateCreateResponse as DocumentGenerateCreateResponse from .project_domains_retrieve_response import ProjectDomainsRetrieveResponse as ProjectDomainsRetrieveResponse from .project_templates_create_response import ProjectTemplatesCreateResponse as ProjectTemplatesCreateResponse from .project_templates_update_response import ProjectTemplatesUpdateResponse as ProjectTemplatesUpdateResponse from .document_documents_retrieve_params import DocumentDocumentsRetrieveParams as DocumentDocumentsRetrieveParams -from .project_api_keys_retrieve_response import ProjectAPIKeysRetrieveResponse as ProjectAPIKeysRetrieveResponse from .email_send_template_template_params import EmailSendTemplateTemplateParams as EmailSendTemplateTemplateParams from .project_templates_retrieve_response import ProjectTemplatesRetrieveResponse as ProjectTemplatesRetrieveResponse from .document_documents_retrieve_response import DocumentDocumentsRetrieveResponse as DocumentDocumentsRetrieveResponse diff --git a/src/unlayer/types/document_documents_retrieve_params.py b/src/unlayer/types/document_documents_retrieve_params.py index 87efae6..a66ceb0 100644 --- a/src/unlayer/types/document_documents_retrieve_params.py +++ b/src/unlayer/types/document_documents_retrieve_params.py @@ -2,7 +2,7 @@ from __future__ import annotations -from typing_extensions import Annotated, TypedDict +from typing_extensions import Required, Annotated, TypedDict from .._utils import PropertyInfo @@ -10,5 +10,5 @@ class DocumentDocumentsRetrieveParams(TypedDict, total=False): - project_id: Annotated[str, PropertyInfo(alias="projectId")] - """The project ID (required for PAT auth, not needed for API Key auth)""" + project_id: Required[Annotated[str, PropertyInfo(alias="projectId")]] + """The project ID""" diff --git a/src/unlayer/types/document_documents_retrieve_response.py b/src/unlayer/types/document_documents_retrieve_response.py index 68c5360..9f5810d 100644 --- a/src/unlayer/types/document_documents_retrieve_response.py +++ b/src/unlayer/types/document_documents_retrieve_response.py @@ -8,10 +8,10 @@ from .._models import BaseModel -__all__ = ["DocumentDocumentsRetrieveResponse"] +__all__ = ["DocumentDocumentsRetrieveResponse", "Data"] -class DocumentDocumentsRetrieveResponse(BaseModel): +class Data(BaseModel): id: Optional[str] = None """Document ID""" @@ -35,3 +35,7 @@ class DocumentDocumentsRetrieveResponse(BaseModel): status: Optional[Literal["generating", "completed", "failed"]] = None """Current document status""" + + +class DocumentDocumentsRetrieveResponse(BaseModel): + data: Optional[Data] = None diff --git a/src/unlayer/types/document_generate_create_params.py b/src/unlayer/types/document_generate_create_params.py index b2b5ead..179a52c 100644 --- a/src/unlayer/types/document_generate_create_params.py +++ b/src/unlayer/types/document_generate_create_params.py @@ -11,11 +11,11 @@ class DocumentGenerateCreateParams(TypedDict, total=False): - design: Required[Dict[str, object]] - """Proprietary design format JSON""" + project_id: Required[Annotated[str, PropertyInfo(alias="projectId")]] + """The project ID""" - project_id: Annotated[str, PropertyInfo(alias="projectId")] - """The project ID (required for PAT auth, not needed for API Key auth)""" + design: Dict[str, object] + """Proprietary design format JSON""" filename: str """Optional filename for the generated PDF""" diff --git a/src/unlayer/types/document_generate_create_response.py b/src/unlayer/types/document_generate_create_response.py index 60919cc..0d1de0a 100644 --- a/src/unlayer/types/document_generate_create_response.py +++ b/src/unlayer/types/document_generate_create_response.py @@ -7,10 +7,10 @@ from .._models import BaseModel -__all__ = ["DocumentGenerateCreateResponse"] +__all__ = ["DocumentGenerateCreateResponse", "Data"] -class DocumentGenerateCreateResponse(BaseModel): +class Data(BaseModel): document_id: Optional[str] = FieldInfo(alias="documentId", default=None) """Unique document identifier""" @@ -21,3 +21,7 @@ class DocumentGenerateCreateResponse(BaseModel): """URL to download the generated PDF""" status: Optional[Literal["generating", "completed", "failed"]] = None + + +class DocumentGenerateCreateResponse(BaseModel): + data: Optional[Data] = None diff --git a/src/unlayer/types/document_generate_template_template_params.py b/src/unlayer/types/document_generate_template_template_params.py index 6c73081..05cae54 100644 --- a/src/unlayer/types/document_generate_template_template_params.py +++ b/src/unlayer/types/document_generate_template_template_params.py @@ -11,12 +11,12 @@ class DocumentGenerateTemplateTemplateParams(TypedDict, total=False): + project_id: Required[Annotated[str, PropertyInfo(alias="projectId")]] + """The project ID""" + template_id: Required[Annotated[str, PropertyInfo(alias="templateId")]] """ID of the template to use for generation""" - project_id: Annotated[str, PropertyInfo(alias="projectId")] - """The project ID (required for PAT auth, not needed for API Key auth)""" - filename: str """Optional filename for the generated PDF""" diff --git a/src/unlayer/types/document_generate_template_template_response.py b/src/unlayer/types/document_generate_template_template_response.py index fcf7073..b3cfea9 100644 --- a/src/unlayer/types/document_generate_template_template_response.py +++ b/src/unlayer/types/document_generate_template_template_response.py @@ -7,10 +7,10 @@ from .._models import BaseModel -__all__ = ["DocumentGenerateTemplateTemplateResponse"] +__all__ = ["DocumentGenerateTemplateTemplateResponse", "Data"] -class DocumentGenerateTemplateTemplateResponse(BaseModel): +class Data(BaseModel): document_id: Optional[str] = FieldInfo(alias="documentId", default=None) """Unique document identifier""" @@ -21,3 +21,7 @@ class DocumentGenerateTemplateTemplateResponse(BaseModel): """URL to download the generated PDF""" status: Optional[Literal["generating", "completed", "failed"]] = None + + +class DocumentGenerateTemplateTemplateResponse(BaseModel): + data: Optional[Data] = None diff --git a/src/unlayer/types/email_render_create_params.py b/src/unlayer/types/email_render_create_params.py index 07f8137..5d37c88 100644 --- a/src/unlayer/types/email_render_create_params.py +++ b/src/unlayer/types/email_render_create_params.py @@ -11,11 +11,11 @@ class EmailRenderCreateParams(TypedDict, total=False): + project_id: Required[Annotated[str, PropertyInfo(alias="projectId")]] + """The project ID""" + design: Required[Dict[str, object]] """Proprietary design format JSON""" - project_id: Annotated[str, PropertyInfo(alias="projectId")] - """The project ID (required for PAT auth, not needed for API Key auth)""" - merge_tags: Annotated[Dict[str, str], PropertyInfo(alias="mergeTags")] """Optional merge tags for personalization""" diff --git a/src/unlayer/types/email_render_create_response.py b/src/unlayer/types/email_render_create_response.py index e41d301..b974796 100644 --- a/src/unlayer/types/email_render_create_response.py +++ b/src/unlayer/types/email_render_create_response.py @@ -4,9 +4,13 @@ from .._models import BaseModel -__all__ = ["EmailRenderCreateResponse"] +__all__ = ["EmailRenderCreateResponse", "Data"] -class EmailRenderCreateResponse(BaseModel): +class Data(BaseModel): html: Optional[str] = None """Rendered HTML content""" + + +class EmailRenderCreateResponse(BaseModel): + data: Optional[Data] = None diff --git a/src/unlayer/types/email_retrieve_params.py b/src/unlayer/types/email_retrieve_params.py index 8325207..be172a5 100644 --- a/src/unlayer/types/email_retrieve_params.py +++ b/src/unlayer/types/email_retrieve_params.py @@ -2,7 +2,7 @@ from __future__ import annotations -from typing_extensions import Annotated, TypedDict +from typing_extensions import Required, Annotated, TypedDict from .._utils import PropertyInfo @@ -10,5 +10,5 @@ class EmailRetrieveParams(TypedDict, total=False): - project_id: Annotated[str, PropertyInfo(alias="projectId")] - """The project ID (required for PAT auth, not needed for API Key auth)""" + project_id: Required[Annotated[str, PropertyInfo(alias="projectId")]] + """The project ID""" diff --git a/src/unlayer/types/email_retrieve_response.py b/src/unlayer/types/email_retrieve_response.py index 55c62c4..ea25e57 100644 --- a/src/unlayer/types/email_retrieve_response.py +++ b/src/unlayer/types/email_retrieve_response.py @@ -8,10 +8,10 @@ from .._models import BaseModel -__all__ = ["EmailRetrieveResponse"] +__all__ = ["EmailRetrieveResponse", "Data"] -class EmailRetrieveResponse(BaseModel): +class Data(BaseModel): id: Optional[str] = None """Email message ID""" @@ -29,3 +29,7 @@ class EmailRetrieveResponse(BaseModel): to: Optional[str] = None """Recipient email address""" + + +class EmailRetrieveResponse(BaseModel): + data: Optional[Data] = None diff --git a/src/unlayer/types/email_send_create_params.py b/src/unlayer/types/email_send_create_params.py index db174a5..161e747 100644 --- a/src/unlayer/types/email_send_create_params.py +++ b/src/unlayer/types/email_send_create_params.py @@ -11,14 +11,14 @@ class EmailSendCreateParams(TypedDict, total=False): - design: Required[Dict[str, object]] - """Proprietary design format JSON""" + project_id: Required[Annotated[str, PropertyInfo(alias="projectId")]] + """The project ID""" to: Required[str] """Recipient email address""" - project_id: Annotated[str, PropertyInfo(alias="projectId")] - """The project ID (required for PAT auth, not needed for API Key auth)""" + design: Dict[str, object] + """Proprietary design format JSON""" html: str """HTML content to send""" diff --git a/src/unlayer/types/email_send_create_response.py b/src/unlayer/types/email_send_create_response.py index 97c2daf..0a6e1d0 100644 --- a/src/unlayer/types/email_send_create_response.py +++ b/src/unlayer/types/email_send_create_response.py @@ -7,11 +7,15 @@ from .._models import BaseModel -__all__ = ["EmailSendCreateResponse"] +__all__ = ["EmailSendCreateResponse", "Data"] -class EmailSendCreateResponse(BaseModel): +class Data(BaseModel): message_id: Optional[str] = FieldInfo(alias="messageId", default=None) """Unique message identifier""" status: Optional[Literal["sent", "queued", "failed"]] = None + + +class EmailSendCreateResponse(BaseModel): + data: Optional[Data] = None diff --git a/src/unlayer/types/email_send_template_template_params.py b/src/unlayer/types/email_send_template_template_params.py index 3f959de..ba4ae42 100644 --- a/src/unlayer/types/email_send_template_template_params.py +++ b/src/unlayer/types/email_send_template_template_params.py @@ -11,15 +11,15 @@ class EmailSendTemplateTemplateParams(TypedDict, total=False): + project_id: Required[Annotated[str, PropertyInfo(alias="projectId")]] + """The project ID""" + template_id: Required[Annotated[str, PropertyInfo(alias="templateId")]] """ID of the template to use""" to: Required[str] """Recipient email address""" - project_id: Annotated[str, PropertyInfo(alias="projectId")] - """The project ID (required for PAT auth, not needed for API Key auth)""" - merge_tags: Annotated[Dict[str, str], PropertyInfo(alias="mergeTags")] """Optional merge tags for personalization""" diff --git a/src/unlayer/types/email_send_template_template_response.py b/src/unlayer/types/email_send_template_template_response.py index 03b91aa..166d1d5 100644 --- a/src/unlayer/types/email_send_template_template_response.py +++ b/src/unlayer/types/email_send_template_template_response.py @@ -7,11 +7,15 @@ from .._models import BaseModel -__all__ = ["EmailSendTemplateTemplateResponse"] +__all__ = ["EmailSendTemplateTemplateResponse", "Data"] -class EmailSendTemplateTemplateResponse(BaseModel): +class Data(BaseModel): message_id: Optional[str] = FieldInfo(alias="messageId", default=None) """Unique message identifier""" status: Optional[Literal["sent", "queued", "failed"]] = None + + +class EmailSendTemplateTemplateResponse(BaseModel): + data: Optional[Data] = None diff --git a/src/unlayer/types/project_api_keys_list_params.py b/src/unlayer/types/export_html_list_params.py similarity index 67% rename from src/unlayer/types/project_api_keys_list_params.py rename to src/unlayer/types/export_html_list_params.py index 223b4fc..d9f8019 100644 --- a/src/unlayer/types/project_api_keys_list_params.py +++ b/src/unlayer/types/export_html_list_params.py @@ -6,9 +6,9 @@ from .._utils import PropertyInfo -__all__ = ["ProjectAPIKeysListParams"] +__all__ = ["ExportHTMLListParams"] -class ProjectAPIKeysListParams(TypedDict, total=False): +class ExportHTMLListParams(TypedDict, total=False): project_id: Required[Annotated[str, PropertyInfo(alias="projectId")]] - """The project ID to get API keys for""" + """The project ID""" diff --git a/src/unlayer/types/export_html_list_response.py b/src/unlayer/types/export_html_list_response.py new file mode 100644 index 0000000..c1de57c --- /dev/null +++ b/src/unlayer/types/export_html_list_response.py @@ -0,0 +1,15 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional + +from .._models import BaseModel + +__all__ = ["ExportHTMLListResponse", "Data"] + + +class Data(BaseModel): + success: Optional[bool] = None + + +class ExportHTMLListResponse(BaseModel): + data: Optional[Data] = None diff --git a/src/unlayer/types/export_image_list_params.py b/src/unlayer/types/export_image_list_params.py new file mode 100644 index 0000000..0b93da0 --- /dev/null +++ b/src/unlayer/types/export_image_list_params.py @@ -0,0 +1,14 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Required, Annotated, TypedDict + +from .._utils import PropertyInfo + +__all__ = ["ExportImageListParams"] + + +class ExportImageListParams(TypedDict, total=False): + project_id: Required[Annotated[str, PropertyInfo(alias="projectId")]] + """The project ID""" diff --git a/src/unlayer/types/export_image_list_response.py b/src/unlayer/types/export_image_list_response.py new file mode 100644 index 0000000..a856217 --- /dev/null +++ b/src/unlayer/types/export_image_list_response.py @@ -0,0 +1,15 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional + +from .._models import BaseModel + +__all__ = ["ExportImageListResponse", "Data"] + + +class Data(BaseModel): + success: Optional[bool] = None + + +class ExportImageListResponse(BaseModel): + data: Optional[Data] = None diff --git a/src/unlayer/types/export_pdf_list_params.py b/src/unlayer/types/export_pdf_list_params.py new file mode 100644 index 0000000..1975940 --- /dev/null +++ b/src/unlayer/types/export_pdf_list_params.py @@ -0,0 +1,14 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Required, Annotated, TypedDict + +from .._utils import PropertyInfo + +__all__ = ["ExportPdfListParams"] + + +class ExportPdfListParams(TypedDict, total=False): + project_id: Required[Annotated[str, PropertyInfo(alias="projectId")]] + """The project ID""" diff --git a/src/unlayer/types/project_tokens_delete_response.py b/src/unlayer/types/export_pdf_list_response.py similarity index 57% rename from src/unlayer/types/project_tokens_delete_response.py rename to src/unlayer/types/export_pdf_list_response.py index 8067027..1a96402 100644 --- a/src/unlayer/types/project_tokens_delete_response.py +++ b/src/unlayer/types/export_pdf_list_response.py @@ -4,10 +4,12 @@ from .._models import BaseModel -__all__ = ["ProjectTokensDeleteResponse"] +__all__ = ["ExportPdfListResponse", "Data"] -class ProjectTokensDeleteResponse(BaseModel): - message: Optional[str] = None - +class Data(BaseModel): success: Optional[bool] = None + + +class ExportPdfListResponse(BaseModel): + data: Optional[Data] = None diff --git a/src/unlayer/types/export_zip_list_params.py b/src/unlayer/types/export_zip_list_params.py new file mode 100644 index 0000000..bf140f9 --- /dev/null +++ b/src/unlayer/types/export_zip_list_params.py @@ -0,0 +1,14 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Required, Annotated, TypedDict + +from .._utils import PropertyInfo + +__all__ = ["ExportZipListParams"] + + +class ExportZipListParams(TypedDict, total=False): + project_id: Required[Annotated[str, PropertyInfo(alias="projectId")]] + """The project ID""" diff --git a/src/unlayer/types/export_zip_list_response.py b/src/unlayer/types/export_zip_list_response.py new file mode 100644 index 0000000..d1e07f9 --- /dev/null +++ b/src/unlayer/types/export_zip_list_response.py @@ -0,0 +1,15 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Optional + +from .._models import BaseModel + +__all__ = ["ExportZipListResponse", "Data"] + + +class Data(BaseModel): + success: Optional[bool] = None + + +class ExportZipListResponse(BaseModel): + data: Optional[Data] = None diff --git a/src/unlayer/types/page_render_create_params.py b/src/unlayer/types/page_render_create_params.py index 446924c..afe7f65 100644 --- a/src/unlayer/types/page_render_create_params.py +++ b/src/unlayer/types/page_render_create_params.py @@ -11,11 +11,11 @@ class PageRenderCreateParams(TypedDict, total=False): + project_id: Required[Annotated[str, PropertyInfo(alias="projectId")]] + """The project ID""" + design: Required[Dict[str, object]] """Proprietary design format JSON""" - project_id: Annotated[str, PropertyInfo(alias="projectId")] - """The project ID (required for PAT auth, not needed for API Key auth)""" - merge_tags: Annotated[Dict[str, str], PropertyInfo(alias="mergeTags")] """Optional merge tags for personalization""" diff --git a/src/unlayer/types/page_render_create_response.py b/src/unlayer/types/page_render_create_response.py index 385ed69..bf22f78 100644 --- a/src/unlayer/types/page_render_create_response.py +++ b/src/unlayer/types/page_render_create_response.py @@ -4,9 +4,13 @@ from .._models import BaseModel -__all__ = ["PageRenderCreateResponse"] +__all__ = ["PageRenderCreateResponse", "Data"] -class PageRenderCreateResponse(BaseModel): +class Data(BaseModel): html: Optional[str] = None """Rendered HTML content""" + + +class PageRenderCreateResponse(BaseModel): + data: Optional[Data] = None diff --git a/src/unlayer/types/project_api_keys_create_params.py b/src/unlayer/types/project_api_keys_create_params.py deleted file mode 100644 index e98c8a8..0000000 --- a/src/unlayer/types/project_api_keys_create_params.py +++ /dev/null @@ -1,21 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing_extensions import Required, Annotated, TypedDict - -from .._types import SequenceNotStr -from .._utils import PropertyInfo - -__all__ = ["ProjectAPIKeysCreateParams"] - - -class ProjectAPIKeysCreateParams(TypedDict, total=False): - project_id: Required[Annotated[str, PropertyInfo(alias="projectId")]] - """The project ID to create API key for""" - - name: Required[str] - """Name for the API key""" - - domains: SequenceNotStr[str] - """Allowed domains for this API key""" diff --git a/src/unlayer/types/project_api_keys_create_response.py b/src/unlayer/types/project_api_keys_create_response.py deleted file mode 100644 index ac406c2..0000000 --- a/src/unlayer/types/project_api_keys_create_response.py +++ /dev/null @@ -1,28 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing import List, Optional -from datetime import datetime - -from pydantic import Field as FieldInfo - -from .._models import BaseModel - -__all__ = ["ProjectAPIKeysCreateResponse", "Data"] - - -class Data(BaseModel): - id: Optional[str] = None - - active: Optional[bool] = None - - created_at: Optional[datetime] = FieldInfo(alias="createdAt", default=None) - - domains: Optional[List[str]] = None - - key: Optional[str] = None - - name: Optional[str] = None - - -class ProjectAPIKeysCreateResponse(BaseModel): - data: Optional[Data] = None diff --git a/src/unlayer/types/project_api_keys_list_response.py b/src/unlayer/types/project_api_keys_list_response.py deleted file mode 100644 index 515b854..0000000 --- a/src/unlayer/types/project_api_keys_list_response.py +++ /dev/null @@ -1,30 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing import List, Optional -from datetime import datetime - -from pydantic import Field as FieldInfo - -from .._models import BaseModel - -__all__ = ["ProjectAPIKeysListResponse", "Data"] - - -class Data(BaseModel): - id: Optional[str] = None - - active: Optional[bool] = None - - created_at: Optional[datetime] = FieldInfo(alias="createdAt", default=None) - - domains: Optional[List[str]] = None - - key: Optional[str] = None - - last_used: Optional[datetime] = FieldInfo(alias="lastUsed", default=None) - - name: Optional[str] = None - - -class ProjectAPIKeysListResponse(BaseModel): - data: Optional[List[Data]] = None diff --git a/src/unlayer/types/project_api_keys_retrieve_response.py b/src/unlayer/types/project_api_keys_retrieve_response.py deleted file mode 100644 index f03855f..0000000 --- a/src/unlayer/types/project_api_keys_retrieve_response.py +++ /dev/null @@ -1,30 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing import List, Optional -from datetime import datetime - -from pydantic import Field as FieldInfo - -from .._models import BaseModel - -__all__ = ["ProjectAPIKeysRetrieveResponse", "Data"] - - -class Data(BaseModel): - id: Optional[str] = None - - active: Optional[bool] = None - - created_at: Optional[datetime] = FieldInfo(alias="createdAt", default=None) - - domains: Optional[List[str]] = None - - key: Optional[str] = None - - last_used: Optional[datetime] = FieldInfo(alias="lastUsed", default=None) - - name: Optional[str] = None - - -class ProjectAPIKeysRetrieveResponse(BaseModel): - data: Optional[Data] = None diff --git a/src/unlayer/types/project_api_keys_update_params.py b/src/unlayer/types/project_api_keys_update_params.py deleted file mode 100644 index a9e2b65..0000000 --- a/src/unlayer/types/project_api_keys_update_params.py +++ /dev/null @@ -1,20 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing_extensions import TypedDict - -from .._types import SequenceNotStr - -__all__ = ["ProjectAPIKeysUpdateParams"] - - -class ProjectAPIKeysUpdateParams(TypedDict, total=False): - active: bool - """Whether the API key is active""" - - domains: SequenceNotStr[str] - """Updated allowed domains""" - - name: str - """Updated name for the API key""" diff --git a/src/unlayer/types/project_api_keys_update_response.py b/src/unlayer/types/project_api_keys_update_response.py deleted file mode 100644 index 437ccb7..0000000 --- a/src/unlayer/types/project_api_keys_update_response.py +++ /dev/null @@ -1,30 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing import List, Optional -from datetime import datetime - -from pydantic import Field as FieldInfo - -from .._models import BaseModel - -__all__ = ["ProjectAPIKeysUpdateResponse", "Data"] - - -class Data(BaseModel): - id: Optional[str] = None - - active: Optional[bool] = None - - created_at: Optional[datetime] = FieldInfo(alias="createdAt", default=None) - - domains: Optional[List[str]] = None - - key: Optional[str] = None - - last_used: Optional[datetime] = FieldInfo(alias="lastUsed", default=None) - - name: Optional[str] = None - - -class ProjectAPIKeysUpdateResponse(BaseModel): - data: Optional[Data] = None diff --git a/src/unlayer/types/project_domains_create_params.py b/src/unlayer/types/project_domains_create_params.py index c1f3f95..2e5d607 100644 --- a/src/unlayer/types/project_domains_create_params.py +++ b/src/unlayer/types/project_domains_create_params.py @@ -11,7 +11,7 @@ class ProjectDomainsCreateParams(TypedDict, total=False): project_id: Required[Annotated[str, PropertyInfo(alias="projectId")]] - """The project ID to add domain to""" + """The project ID""" domain: Required[str] """Domain name to add""" diff --git a/src/unlayer/types/project_domains_list_params.py b/src/unlayer/types/project_domains_list_params.py index 6c3ba7a..1081c4f 100644 --- a/src/unlayer/types/project_domains_list_params.py +++ b/src/unlayer/types/project_domains_list_params.py @@ -11,4 +11,4 @@ class ProjectDomainsListParams(TypedDict, total=False): project_id: Required[Annotated[str, PropertyInfo(alias="projectId")]] - """The project ID to get domains for""" + """The project ID""" diff --git a/src/unlayer/types/project_templates_create_params.py b/src/unlayer/types/project_templates_create_params.py index 23d879d..8dfa036 100644 --- a/src/unlayer/types/project_templates_create_params.py +++ b/src/unlayer/types/project_templates_create_params.py @@ -2,7 +2,7 @@ from __future__ import annotations -from typing_extensions import Required, Annotated, TypedDict +from typing_extensions import Literal, Required, Annotated, TypedDict from .._utils import PropertyInfo @@ -11,13 +11,10 @@ class ProjectTemplatesCreateParams(TypedDict, total=False): project_id: Required[Annotated[str, PropertyInfo(alias="projectId")]] - """The project ID to create template for""" + """The project ID to create the template in""" name: Required[str] """Template name""" - body: str - """Email body content""" - - subject: str - """Email subject line""" + display_mode: Annotated[Literal["email", "web", "document"], PropertyInfo(alias="displayMode")] + """Template type/display mode""" diff --git a/src/unlayer/types/project_templates_create_response.py b/src/unlayer/types/project_templates_create_response.py index b5b309f..fb383ef 100644 --- a/src/unlayer/types/project_templates_create_response.py +++ b/src/unlayer/types/project_templates_create_response.py @@ -2,6 +2,7 @@ from typing import Optional from datetime import datetime +from typing_extensions import Literal from pydantic import Field as FieldInfo @@ -12,14 +13,15 @@ class Data(BaseModel): id: Optional[str] = None - - body: Optional[str] = None + """Template ID""" created_at: Optional[datetime] = FieldInfo(alias="createdAt", default=None) - name: Optional[str] = None + display_mode: Optional[Literal["email", "web", "document"]] = FieldInfo(alias="displayMode", default=None) + """Template type/display mode""" - subject: Optional[str] = None + name: Optional[str] = None + """Template name""" updated_at: Optional[datetime] = FieldInfo(alias="updatedAt", default=None) diff --git a/src/unlayer/types/project_templates_list_params.py b/src/unlayer/types/project_templates_list_params.py index 335c4ed..18e5945 100644 --- a/src/unlayer/types/project_templates_list_params.py +++ b/src/unlayer/types/project_templates_list_params.py @@ -2,7 +2,7 @@ from __future__ import annotations -from typing_extensions import Required, Annotated, TypedDict +from typing_extensions import Literal, Required, Annotated, TypedDict from .._utils import PropertyInfo @@ -11,4 +11,16 @@ class ProjectTemplatesListParams(TypedDict, total=False): project_id: Required[Annotated[str, PropertyInfo(alias="projectId")]] - """The project ID to get templates for""" + """The project ID to list templates for""" + + cursor: str + """Pagination cursor from previous response""" + + display_mode: Annotated[Literal["email", "web", "document"], PropertyInfo(alias="displayMode")] + """Filter by template type""" + + limit: int + """Number of templates to return (1-100)""" + + name: str + """Filter by name (case-insensitive search)""" diff --git a/src/unlayer/types/project_templates_list_response.py b/src/unlayer/types/project_templates_list_response.py index 72ab756..ece59f7 100644 --- a/src/unlayer/types/project_templates_list_response.py +++ b/src/unlayer/types/project_templates_list_response.py @@ -1,28 +1,26 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. -from typing import List, Optional +from typing import Optional from datetime import datetime +from typing_extensions import Literal from pydantic import Field as FieldInfo from .._models import BaseModel -__all__ = ["ProjectTemplatesListResponse", "Data"] +__all__ = ["ProjectTemplatesListResponse"] -class Data(BaseModel): +class ProjectTemplatesListResponse(BaseModel): id: Optional[str] = None - - body: Optional[str] = None + """Template ID""" created_at: Optional[datetime] = FieldInfo(alias="createdAt", default=None) - name: Optional[str] = None + display_mode: Optional[Literal["email", "web", "document"]] = FieldInfo(alias="displayMode", default=None) + """Template type/display mode""" - subject: Optional[str] = None + name: Optional[str] = None + """Template name""" updated_at: Optional[datetime] = FieldInfo(alias="updatedAt", default=None) - - -class ProjectTemplatesListResponse(BaseModel): - data: Optional[List[Data]] = None diff --git a/src/unlayer/types/project_tokens_list_response.py b/src/unlayer/types/project_tokens_list_response.py deleted file mode 100644 index 9fe5b8c..0000000 --- a/src/unlayer/types/project_tokens_list_response.py +++ /dev/null @@ -1,32 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing import List, Optional -from datetime import datetime - -from pydantic import Field as FieldInfo - -from .._models import BaseModel - -__all__ = ["ProjectTokensListResponse", "Data"] - - -class Data(BaseModel): - id: Optional[float] = None - - created_at: Optional[datetime] = FieldInfo(alias="createdAt", default=None) - - expires_at: Optional[datetime] = FieldInfo(alias="expiresAt", default=None) - - last_used_at: Optional[datetime] = FieldInfo(alias="lastUsedAt", default=None) - - name: Optional[str] = None - - scope: Optional[str] = None - - workspace_id: Optional[float] = FieldInfo(alias="workspaceId", default=None) - - workspace_name: Optional[str] = FieldInfo(alias="workspaceName", default=None) - - -class ProjectTokensListResponse(BaseModel): - data: Optional[List[Data]] = None diff --git a/tests/api_resources/test_documents.py b/tests/api_resources/test_documents.py index b732e07..39f9035 100644 --- a/tests/api_resources/test_documents.py +++ b/tests/api_resources/test_documents.py @@ -23,13 +23,6 @@ class TestDocuments: @parametrize def test_method_documents_retrieve(self, client: Unlayer) -> None: - document = client.documents.documents_retrieve( - id="id", - ) - assert_matches_type(DocumentDocumentsRetrieveResponse, document, path=["response"]) - - @parametrize - def test_method_documents_retrieve_with_all_params(self, client: Unlayer) -> None: document = client.documents.documents_retrieve( id="id", project_id="projectId", @@ -40,6 +33,7 @@ def test_method_documents_retrieve_with_all_params(self, client: Unlayer) -> Non def test_raw_response_documents_retrieve(self, client: Unlayer) -> None: response = client.documents.with_raw_response.documents_retrieve( id="id", + project_id="projectId", ) assert response.is_closed is True @@ -51,6 +45,7 @@ def test_raw_response_documents_retrieve(self, client: Unlayer) -> None: def test_streaming_response_documents_retrieve(self, client: Unlayer) -> None: with client.documents.with_streaming_response.documents_retrieve( id="id", + project_id="projectId", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" @@ -65,26 +60,21 @@ def test_path_params_documents_retrieve(self, client: Unlayer) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): client.documents.with_raw_response.documents_retrieve( id="", + project_id="projectId", ) @parametrize def test_method_generate_create(self, client: Unlayer) -> None: document = client.documents.generate_create( - design={ - "counters": "bar", - "body": "bar", - }, + project_id="projectId", ) assert_matches_type(DocumentGenerateCreateResponse, document, path=["response"]) @parametrize def test_method_generate_create_with_all_params(self, client: Unlayer) -> None: document = client.documents.generate_create( - design={ - "counters": "bar", - "body": "bar", - }, project_id="projectId", + design={"foo": "bar"}, filename="filename", html="html", merge_tags={"foo": "string"}, @@ -95,10 +85,7 @@ def test_method_generate_create_with_all_params(self, client: Unlayer) -> None: @parametrize def test_raw_response_generate_create(self, client: Unlayer) -> None: response = client.documents.with_raw_response.generate_create( - design={ - "counters": "bar", - "body": "bar", - }, + project_id="projectId", ) assert response.is_closed is True @@ -109,10 +96,7 @@ def test_raw_response_generate_create(self, client: Unlayer) -> None: @parametrize def test_streaming_response_generate_create(self, client: Unlayer) -> None: with client.documents.with_streaming_response.generate_create( - design={ - "counters": "bar", - "body": "bar", - }, + project_id="projectId", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" @@ -125,6 +109,7 @@ def test_streaming_response_generate_create(self, client: Unlayer) -> None: @parametrize def test_method_generate_template_template(self, client: Unlayer) -> None: document = client.documents.generate_template_template( + project_id="projectId", template_id="templateId", ) assert_matches_type(DocumentGenerateTemplateTemplateResponse, document, path=["response"]) @@ -132,8 +117,8 @@ def test_method_generate_template_template(self, client: Unlayer) -> None: @parametrize def test_method_generate_template_template_with_all_params(self, client: Unlayer) -> None: document = client.documents.generate_template_template( - template_id="templateId", project_id="projectId", + template_id="templateId", filename="filename", merge_tags={"foo": "string"}, ) @@ -142,6 +127,7 @@ def test_method_generate_template_template_with_all_params(self, client: Unlayer @parametrize def test_raw_response_generate_template_template(self, client: Unlayer) -> None: response = client.documents.with_raw_response.generate_template_template( + project_id="projectId", template_id="templateId", ) @@ -153,6 +139,7 @@ def test_raw_response_generate_template_template(self, client: Unlayer) -> None: @parametrize def test_streaming_response_generate_template_template(self, client: Unlayer) -> None: with client.documents.with_streaming_response.generate_template_template( + project_id="projectId", template_id="templateId", ) as response: assert not response.is_closed @@ -171,13 +158,6 @@ class TestAsyncDocuments: @parametrize async def test_method_documents_retrieve(self, async_client: AsyncUnlayer) -> None: - document = await async_client.documents.documents_retrieve( - id="id", - ) - assert_matches_type(DocumentDocumentsRetrieveResponse, document, path=["response"]) - - @parametrize - async def test_method_documents_retrieve_with_all_params(self, async_client: AsyncUnlayer) -> None: document = await async_client.documents.documents_retrieve( id="id", project_id="projectId", @@ -188,6 +168,7 @@ async def test_method_documents_retrieve_with_all_params(self, async_client: Asy async def test_raw_response_documents_retrieve(self, async_client: AsyncUnlayer) -> None: response = await async_client.documents.with_raw_response.documents_retrieve( id="id", + project_id="projectId", ) assert response.is_closed is True @@ -199,6 +180,7 @@ async def test_raw_response_documents_retrieve(self, async_client: AsyncUnlayer) async def test_streaming_response_documents_retrieve(self, async_client: AsyncUnlayer) -> None: async with async_client.documents.with_streaming_response.documents_retrieve( id="id", + project_id="projectId", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" @@ -213,26 +195,21 @@ async def test_path_params_documents_retrieve(self, async_client: AsyncUnlayer) with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): await async_client.documents.with_raw_response.documents_retrieve( id="", + project_id="projectId", ) @parametrize async def test_method_generate_create(self, async_client: AsyncUnlayer) -> None: document = await async_client.documents.generate_create( - design={ - "counters": "bar", - "body": "bar", - }, + project_id="projectId", ) assert_matches_type(DocumentGenerateCreateResponse, document, path=["response"]) @parametrize async def test_method_generate_create_with_all_params(self, async_client: AsyncUnlayer) -> None: document = await async_client.documents.generate_create( - design={ - "counters": "bar", - "body": "bar", - }, project_id="projectId", + design={"foo": "bar"}, filename="filename", html="html", merge_tags={"foo": "string"}, @@ -243,10 +220,7 @@ async def test_method_generate_create_with_all_params(self, async_client: AsyncU @parametrize async def test_raw_response_generate_create(self, async_client: AsyncUnlayer) -> None: response = await async_client.documents.with_raw_response.generate_create( - design={ - "counters": "bar", - "body": "bar", - }, + project_id="projectId", ) assert response.is_closed is True @@ -257,10 +231,7 @@ async def test_raw_response_generate_create(self, async_client: AsyncUnlayer) -> @parametrize async def test_streaming_response_generate_create(self, async_client: AsyncUnlayer) -> None: async with async_client.documents.with_streaming_response.generate_create( - design={ - "counters": "bar", - "body": "bar", - }, + project_id="projectId", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" @@ -273,6 +244,7 @@ async def test_streaming_response_generate_create(self, async_client: AsyncUnlay @parametrize async def test_method_generate_template_template(self, async_client: AsyncUnlayer) -> None: document = await async_client.documents.generate_template_template( + project_id="projectId", template_id="templateId", ) assert_matches_type(DocumentGenerateTemplateTemplateResponse, document, path=["response"]) @@ -280,8 +252,8 @@ async def test_method_generate_template_template(self, async_client: AsyncUnlaye @parametrize async def test_method_generate_template_template_with_all_params(self, async_client: AsyncUnlayer) -> None: document = await async_client.documents.generate_template_template( - template_id="templateId", project_id="projectId", + template_id="templateId", filename="filename", merge_tags={"foo": "string"}, ) @@ -290,6 +262,7 @@ async def test_method_generate_template_template_with_all_params(self, async_cli @parametrize async def test_raw_response_generate_template_template(self, async_client: AsyncUnlayer) -> None: response = await async_client.documents.with_raw_response.generate_template_template( + project_id="projectId", template_id="templateId", ) @@ -301,6 +274,7 @@ async def test_raw_response_generate_template_template(self, async_client: Async @parametrize async def test_streaming_response_generate_template_template(self, async_client: AsyncUnlayer) -> None: async with async_client.documents.with_streaming_response.generate_template_template( + project_id="projectId", template_id="templateId", ) as response: assert not response.is_closed diff --git a/tests/api_resources/test_emails.py b/tests/api_resources/test_emails.py index e005b81..c14a734 100644 --- a/tests/api_resources/test_emails.py +++ b/tests/api_resources/test_emails.py @@ -24,13 +24,6 @@ class TestEmails: @parametrize def test_method_retrieve(self, client: Unlayer) -> None: - email = client.emails.retrieve( - id="id", - ) - assert_matches_type(EmailRetrieveResponse, email, path=["response"]) - - @parametrize - def test_method_retrieve_with_all_params(self, client: Unlayer) -> None: email = client.emails.retrieve( id="id", project_id="projectId", @@ -41,6 +34,7 @@ def test_method_retrieve_with_all_params(self, client: Unlayer) -> None: def test_raw_response_retrieve(self, client: Unlayer) -> None: response = client.emails.with_raw_response.retrieve( id="id", + project_id="projectId", ) assert response.is_closed is True @@ -52,6 +46,7 @@ def test_raw_response_retrieve(self, client: Unlayer) -> None: def test_streaming_response_retrieve(self, client: Unlayer) -> None: with client.emails.with_streaming_response.retrieve( id="id", + project_id="projectId", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" @@ -66,26 +61,22 @@ def test_path_params_retrieve(self, client: Unlayer) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): client.emails.with_raw_response.retrieve( id="", + project_id="projectId", ) @parametrize def test_method_render_create(self, client: Unlayer) -> None: email = client.emails.render_create( - design={ - "counters": "bar", - "body": "bar", - }, + project_id="projectId", + design={"foo": "bar"}, ) assert_matches_type(EmailRenderCreateResponse, email, path=["response"]) @parametrize def test_method_render_create_with_all_params(self, client: Unlayer) -> None: email = client.emails.render_create( - design={ - "counters": "bar", - "body": "bar", - }, project_id="projectId", + design={"foo": "bar"}, merge_tags={"foo": "string"}, ) assert_matches_type(EmailRenderCreateResponse, email, path=["response"]) @@ -93,10 +84,8 @@ def test_method_render_create_with_all_params(self, client: Unlayer) -> None: @parametrize def test_raw_response_render_create(self, client: Unlayer) -> None: response = client.emails.with_raw_response.render_create( - design={ - "counters": "bar", - "body": "bar", - }, + project_id="projectId", + design={"foo": "bar"}, ) assert response.is_closed is True @@ -107,10 +96,8 @@ def test_raw_response_render_create(self, client: Unlayer) -> None: @parametrize def test_streaming_response_render_create(self, client: Unlayer) -> None: with client.emails.with_streaming_response.render_create( - design={ - "counters": "bar", - "body": "bar", - }, + project_id="projectId", + design={"foo": "bar"}, ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" @@ -123,37 +110,28 @@ def test_streaming_response_render_create(self, client: Unlayer) -> None: @parametrize def test_method_send_create(self, client: Unlayer) -> None: email = client.emails.send_create( - design={ - "counters": "bar", - "body": "bar", - }, - to="test@example.com", + project_id="projectId", + to="dev@stainless.com", ) assert_matches_type(EmailSendCreateResponse, email, path=["response"]) @parametrize def test_method_send_create_with_all_params(self, client: Unlayer) -> None: email = client.emails.send_create( - design={ - "counters": "bar", - "body": "bar", - }, - to="test@example.com", project_id="projectId", + to="dev@stainless.com", + design={"foo": "bar"}, html="html", merge_tags={"foo": "string"}, - subject="Test", + subject="subject", ) assert_matches_type(EmailSendCreateResponse, email, path=["response"]) @parametrize def test_raw_response_send_create(self, client: Unlayer) -> None: response = client.emails.with_raw_response.send_create( - design={ - "counters": "bar", - "body": "bar", - }, - to="test@example.com", + project_id="projectId", + to="dev@stainless.com", ) assert response.is_closed is True @@ -164,11 +142,8 @@ def test_raw_response_send_create(self, client: Unlayer) -> None: @parametrize def test_streaming_response_send_create(self, client: Unlayer) -> None: with client.emails.with_streaming_response.send_create( - design={ - "counters": "bar", - "body": "bar", - }, - to="test@example.com", + project_id="projectId", + to="dev@stainless.com", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" @@ -181,6 +156,7 @@ def test_streaming_response_send_create(self, client: Unlayer) -> None: @parametrize def test_method_send_template_template(self, client: Unlayer) -> None: email = client.emails.send_template_template( + project_id="projectId", template_id="templateId", to="dev@stainless.com", ) @@ -189,9 +165,9 @@ def test_method_send_template_template(self, client: Unlayer) -> None: @parametrize def test_method_send_template_template_with_all_params(self, client: Unlayer) -> None: email = client.emails.send_template_template( + project_id="projectId", template_id="templateId", to="dev@stainless.com", - project_id="projectId", merge_tags={"foo": "string"}, subject="subject", ) @@ -200,6 +176,7 @@ def test_method_send_template_template_with_all_params(self, client: Unlayer) -> @parametrize def test_raw_response_send_template_template(self, client: Unlayer) -> None: response = client.emails.with_raw_response.send_template_template( + project_id="projectId", template_id="templateId", to="dev@stainless.com", ) @@ -212,6 +189,7 @@ def test_raw_response_send_template_template(self, client: Unlayer) -> None: @parametrize def test_streaming_response_send_template_template(self, client: Unlayer) -> None: with client.emails.with_streaming_response.send_template_template( + project_id="projectId", template_id="templateId", to="dev@stainless.com", ) as response: @@ -231,13 +209,6 @@ class TestAsyncEmails: @parametrize async def test_method_retrieve(self, async_client: AsyncUnlayer) -> None: - email = await async_client.emails.retrieve( - id="id", - ) - assert_matches_type(EmailRetrieveResponse, email, path=["response"]) - - @parametrize - async def test_method_retrieve_with_all_params(self, async_client: AsyncUnlayer) -> None: email = await async_client.emails.retrieve( id="id", project_id="projectId", @@ -248,6 +219,7 @@ async def test_method_retrieve_with_all_params(self, async_client: AsyncUnlayer) async def test_raw_response_retrieve(self, async_client: AsyncUnlayer) -> None: response = await async_client.emails.with_raw_response.retrieve( id="id", + project_id="projectId", ) assert response.is_closed is True @@ -259,6 +231,7 @@ async def test_raw_response_retrieve(self, async_client: AsyncUnlayer) -> None: async def test_streaming_response_retrieve(self, async_client: AsyncUnlayer) -> None: async with async_client.emails.with_streaming_response.retrieve( id="id", + project_id="projectId", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" @@ -273,26 +246,22 @@ async def test_path_params_retrieve(self, async_client: AsyncUnlayer) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): await async_client.emails.with_raw_response.retrieve( id="", + project_id="projectId", ) @parametrize async def test_method_render_create(self, async_client: AsyncUnlayer) -> None: email = await async_client.emails.render_create( - design={ - "counters": "bar", - "body": "bar", - }, + project_id="projectId", + design={"foo": "bar"}, ) assert_matches_type(EmailRenderCreateResponse, email, path=["response"]) @parametrize async def test_method_render_create_with_all_params(self, async_client: AsyncUnlayer) -> None: email = await async_client.emails.render_create( - design={ - "counters": "bar", - "body": "bar", - }, project_id="projectId", + design={"foo": "bar"}, merge_tags={"foo": "string"}, ) assert_matches_type(EmailRenderCreateResponse, email, path=["response"]) @@ -300,10 +269,8 @@ async def test_method_render_create_with_all_params(self, async_client: AsyncUnl @parametrize async def test_raw_response_render_create(self, async_client: AsyncUnlayer) -> None: response = await async_client.emails.with_raw_response.render_create( - design={ - "counters": "bar", - "body": "bar", - }, + project_id="projectId", + design={"foo": "bar"}, ) assert response.is_closed is True @@ -314,10 +281,8 @@ async def test_raw_response_render_create(self, async_client: AsyncUnlayer) -> N @parametrize async def test_streaming_response_render_create(self, async_client: AsyncUnlayer) -> None: async with async_client.emails.with_streaming_response.render_create( - design={ - "counters": "bar", - "body": "bar", - }, + project_id="projectId", + design={"foo": "bar"}, ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" @@ -330,37 +295,28 @@ async def test_streaming_response_render_create(self, async_client: AsyncUnlayer @parametrize async def test_method_send_create(self, async_client: AsyncUnlayer) -> None: email = await async_client.emails.send_create( - design={ - "counters": "bar", - "body": "bar", - }, - to="test@example.com", + project_id="projectId", + to="dev@stainless.com", ) assert_matches_type(EmailSendCreateResponse, email, path=["response"]) @parametrize async def test_method_send_create_with_all_params(self, async_client: AsyncUnlayer) -> None: email = await async_client.emails.send_create( - design={ - "counters": "bar", - "body": "bar", - }, - to="test@example.com", project_id="projectId", + to="dev@stainless.com", + design={"foo": "bar"}, html="html", merge_tags={"foo": "string"}, - subject="Test", + subject="subject", ) assert_matches_type(EmailSendCreateResponse, email, path=["response"]) @parametrize async def test_raw_response_send_create(self, async_client: AsyncUnlayer) -> None: response = await async_client.emails.with_raw_response.send_create( - design={ - "counters": "bar", - "body": "bar", - }, - to="test@example.com", + project_id="projectId", + to="dev@stainless.com", ) assert response.is_closed is True @@ -371,11 +327,8 @@ async def test_raw_response_send_create(self, async_client: AsyncUnlayer) -> Non @parametrize async def test_streaming_response_send_create(self, async_client: AsyncUnlayer) -> None: async with async_client.emails.with_streaming_response.send_create( - design={ - "counters": "bar", - "body": "bar", - }, - to="test@example.com", + project_id="projectId", + to="dev@stainless.com", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" @@ -388,6 +341,7 @@ async def test_streaming_response_send_create(self, async_client: AsyncUnlayer) @parametrize async def test_method_send_template_template(self, async_client: AsyncUnlayer) -> None: email = await async_client.emails.send_template_template( + project_id="projectId", template_id="templateId", to="dev@stainless.com", ) @@ -396,9 +350,9 @@ async def test_method_send_template_template(self, async_client: AsyncUnlayer) - @parametrize async def test_method_send_template_template_with_all_params(self, async_client: AsyncUnlayer) -> None: email = await async_client.emails.send_template_template( + project_id="projectId", template_id="templateId", to="dev@stainless.com", - project_id="projectId", merge_tags={"foo": "string"}, subject="subject", ) @@ -407,6 +361,7 @@ async def test_method_send_template_template_with_all_params(self, async_client: @parametrize async def test_raw_response_send_template_template(self, async_client: AsyncUnlayer) -> None: response = await async_client.emails.with_raw_response.send_template_template( + project_id="projectId", template_id="templateId", to="dev@stainless.com", ) @@ -419,6 +374,7 @@ async def test_raw_response_send_template_template(self, async_client: AsyncUnla @parametrize async def test_streaming_response_send_template_template(self, async_client: AsyncUnlayer) -> None: async with async_client.emails.with_streaming_response.send_template_template( + project_id="projectId", template_id="templateId", to="dev@stainless.com", ) as response: diff --git a/tests/api_resources/test_export.py b/tests/api_resources/test_export.py new file mode 100644 index 0000000..f1bce90 --- /dev/null +++ b/tests/api_resources/test_export.py @@ -0,0 +1,277 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import os +from typing import Any, cast + +import pytest + +from unlayer import Unlayer, AsyncUnlayer +from tests.utils import assert_matches_type +from unlayer.types import ( + ExportPdfListResponse, + ExportZipListResponse, + ExportHTMLListResponse, + ExportImageListResponse, +) + +base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") + + +class TestExport: + parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) + + @parametrize + def test_method_html_list(self, client: Unlayer) -> None: + export = client.export.html_list( + project_id="projectId", + ) + assert_matches_type(ExportHTMLListResponse, export, path=["response"]) + + @parametrize + def test_raw_response_html_list(self, client: Unlayer) -> None: + response = client.export.with_raw_response.html_list( + project_id="projectId", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + export = response.parse() + assert_matches_type(ExportHTMLListResponse, export, path=["response"]) + + @parametrize + def test_streaming_response_html_list(self, client: Unlayer) -> None: + with client.export.with_streaming_response.html_list( + project_id="projectId", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + export = response.parse() + assert_matches_type(ExportHTMLListResponse, export, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_method_image_list(self, client: Unlayer) -> None: + export = client.export.image_list( + project_id="projectId", + ) + assert_matches_type(ExportImageListResponse, export, path=["response"]) + + @parametrize + def test_raw_response_image_list(self, client: Unlayer) -> None: + response = client.export.with_raw_response.image_list( + project_id="projectId", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + export = response.parse() + assert_matches_type(ExportImageListResponse, export, path=["response"]) + + @parametrize + def test_streaming_response_image_list(self, client: Unlayer) -> None: + with client.export.with_streaming_response.image_list( + project_id="projectId", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + export = response.parse() + assert_matches_type(ExportImageListResponse, export, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_method_pdf_list(self, client: Unlayer) -> None: + export = client.export.pdf_list( + project_id="projectId", + ) + assert_matches_type(ExportPdfListResponse, export, path=["response"]) + + @parametrize + def test_raw_response_pdf_list(self, client: Unlayer) -> None: + response = client.export.with_raw_response.pdf_list( + project_id="projectId", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + export = response.parse() + assert_matches_type(ExportPdfListResponse, export, path=["response"]) + + @parametrize + def test_streaming_response_pdf_list(self, client: Unlayer) -> None: + with client.export.with_streaming_response.pdf_list( + project_id="projectId", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + export = response.parse() + assert_matches_type(ExportPdfListResponse, export, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_method_zip_list(self, client: Unlayer) -> None: + export = client.export.zip_list( + project_id="projectId", + ) + assert_matches_type(ExportZipListResponse, export, path=["response"]) + + @parametrize + def test_raw_response_zip_list(self, client: Unlayer) -> None: + response = client.export.with_raw_response.zip_list( + project_id="projectId", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + export = response.parse() + assert_matches_type(ExportZipListResponse, export, path=["response"]) + + @parametrize + def test_streaming_response_zip_list(self, client: Unlayer) -> None: + with client.export.with_streaming_response.zip_list( + project_id="projectId", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + export = response.parse() + assert_matches_type(ExportZipListResponse, export, path=["response"]) + + assert cast(Any, response.is_closed) is True + + +class TestAsyncExport: + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) + + @parametrize + async def test_method_html_list(self, async_client: AsyncUnlayer) -> None: + export = await async_client.export.html_list( + project_id="projectId", + ) + assert_matches_type(ExportHTMLListResponse, export, path=["response"]) + + @parametrize + async def test_raw_response_html_list(self, async_client: AsyncUnlayer) -> None: + response = await async_client.export.with_raw_response.html_list( + project_id="projectId", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + export = await response.parse() + assert_matches_type(ExportHTMLListResponse, export, path=["response"]) + + @parametrize + async def test_streaming_response_html_list(self, async_client: AsyncUnlayer) -> None: + async with async_client.export.with_streaming_response.html_list( + project_id="projectId", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + export = await response.parse() + assert_matches_type(ExportHTMLListResponse, export, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_method_image_list(self, async_client: AsyncUnlayer) -> None: + export = await async_client.export.image_list( + project_id="projectId", + ) + assert_matches_type(ExportImageListResponse, export, path=["response"]) + + @parametrize + async def test_raw_response_image_list(self, async_client: AsyncUnlayer) -> None: + response = await async_client.export.with_raw_response.image_list( + project_id="projectId", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + export = await response.parse() + assert_matches_type(ExportImageListResponse, export, path=["response"]) + + @parametrize + async def test_streaming_response_image_list(self, async_client: AsyncUnlayer) -> None: + async with async_client.export.with_streaming_response.image_list( + project_id="projectId", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + export = await response.parse() + assert_matches_type(ExportImageListResponse, export, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_method_pdf_list(self, async_client: AsyncUnlayer) -> None: + export = await async_client.export.pdf_list( + project_id="projectId", + ) + assert_matches_type(ExportPdfListResponse, export, path=["response"]) + + @parametrize + async def test_raw_response_pdf_list(self, async_client: AsyncUnlayer) -> None: + response = await async_client.export.with_raw_response.pdf_list( + project_id="projectId", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + export = await response.parse() + assert_matches_type(ExportPdfListResponse, export, path=["response"]) + + @parametrize + async def test_streaming_response_pdf_list(self, async_client: AsyncUnlayer) -> None: + async with async_client.export.with_streaming_response.pdf_list( + project_id="projectId", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + export = await response.parse() + assert_matches_type(ExportPdfListResponse, export, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_method_zip_list(self, async_client: AsyncUnlayer) -> None: + export = await async_client.export.zip_list( + project_id="projectId", + ) + assert_matches_type(ExportZipListResponse, export, path=["response"]) + + @parametrize + async def test_raw_response_zip_list(self, async_client: AsyncUnlayer) -> None: + response = await async_client.export.with_raw_response.zip_list( + project_id="projectId", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + export = await response.parse() + assert_matches_type(ExportZipListResponse, export, path=["response"]) + + @parametrize + async def test_streaming_response_zip_list(self, async_client: AsyncUnlayer) -> None: + async with async_client.export.with_streaming_response.zip_list( + project_id="projectId", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + export = await response.parse() + assert_matches_type(ExportZipListResponse, export, path=["response"]) + + assert cast(Any, response.is_closed) is True diff --git a/tests/api_resources/test_pages.py b/tests/api_resources/test_pages.py index a0444c6..f98c891 100644 --- a/tests/api_resources/test_pages.py +++ b/tests/api_resources/test_pages.py @@ -20,21 +20,16 @@ class TestPages: @parametrize def test_method_render_create(self, client: Unlayer) -> None: page = client.pages.render_create( - design={ - "counters": "bar", - "body": "bar", - }, + project_id="projectId", + design={"foo": "bar"}, ) assert_matches_type(PageRenderCreateResponse, page, path=["response"]) @parametrize def test_method_render_create_with_all_params(self, client: Unlayer) -> None: page = client.pages.render_create( - design={ - "counters": "bar", - "body": "bar", - }, project_id="projectId", + design={"foo": "bar"}, merge_tags={"foo": "string"}, ) assert_matches_type(PageRenderCreateResponse, page, path=["response"]) @@ -42,10 +37,8 @@ def test_method_render_create_with_all_params(self, client: Unlayer) -> None: @parametrize def test_raw_response_render_create(self, client: Unlayer) -> None: response = client.pages.with_raw_response.render_create( - design={ - "counters": "bar", - "body": "bar", - }, + project_id="projectId", + design={"foo": "bar"}, ) assert response.is_closed is True @@ -56,10 +49,8 @@ def test_raw_response_render_create(self, client: Unlayer) -> None: @parametrize def test_streaming_response_render_create(self, client: Unlayer) -> None: with client.pages.with_streaming_response.render_create( - design={ - "counters": "bar", - "body": "bar", - }, + project_id="projectId", + design={"foo": "bar"}, ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" @@ -78,21 +69,16 @@ class TestAsyncPages: @parametrize async def test_method_render_create(self, async_client: AsyncUnlayer) -> None: page = await async_client.pages.render_create( - design={ - "counters": "bar", - "body": "bar", - }, + project_id="projectId", + design={"foo": "bar"}, ) assert_matches_type(PageRenderCreateResponse, page, path=["response"]) @parametrize async def test_method_render_create_with_all_params(self, async_client: AsyncUnlayer) -> None: page = await async_client.pages.render_create( - design={ - "counters": "bar", - "body": "bar", - }, project_id="projectId", + design={"foo": "bar"}, merge_tags={"foo": "string"}, ) assert_matches_type(PageRenderCreateResponse, page, path=["response"]) @@ -100,10 +86,8 @@ async def test_method_render_create_with_all_params(self, async_client: AsyncUnl @parametrize async def test_raw_response_render_create(self, async_client: AsyncUnlayer) -> None: response = await async_client.pages.with_raw_response.render_create( - design={ - "counters": "bar", - "body": "bar", - }, + project_id="projectId", + design={"foo": "bar"}, ) assert response.is_closed is True @@ -114,10 +98,8 @@ async def test_raw_response_render_create(self, async_client: AsyncUnlayer) -> N @parametrize async def test_streaming_response_render_create(self, async_client: AsyncUnlayer) -> None: async with async_client.pages.with_streaming_response.render_create( - design={ - "counters": "bar", - "body": "bar", - }, + project_id="projectId", + design={"foo": "bar"}, ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" diff --git a/tests/api_resources/test_project.py b/tests/api_resources/test_project.py index d2c1d77..080eb71 100644 --- a/tests/api_resources/test_project.py +++ b/tests/api_resources/test_project.py @@ -10,24 +10,19 @@ from unlayer import Unlayer, AsyncUnlayer from tests.utils import assert_matches_type from unlayer.types import ( - ProjectTokensListResponse, - ProjectAPIKeysListResponse, ProjectCurrentListResponse, ProjectDomainsListResponse, - ProjectTokensDeleteResponse, - ProjectAPIKeysCreateResponse, - ProjectAPIKeysUpdateResponse, ProjectDomainsCreateResponse, ProjectDomainsUpdateResponse, ProjectTemplatesListResponse, ProjectWorkspacesListResponse, - ProjectAPIKeysRetrieveResponse, ProjectDomainsRetrieveResponse, ProjectTemplatesCreateResponse, ProjectTemplatesUpdateResponse, ProjectTemplatesRetrieveResponse, ProjectWorkspacesRetrieveResponse, ) +from unlayer.pagination import SyncCursorPage, AsyncCursorPage base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") @@ -35,204 +30,6 @@ class TestProject: parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) - @parametrize - def test_method_api_keys_create(self, client: Unlayer) -> None: - project = client.project.api_keys_create( - project_id="projectId", - name="name", - ) - assert_matches_type(ProjectAPIKeysCreateResponse, project, path=["response"]) - - @parametrize - def test_method_api_keys_create_with_all_params(self, client: Unlayer) -> None: - project = client.project.api_keys_create( - project_id="projectId", - name="name", - domains=["string"], - ) - assert_matches_type(ProjectAPIKeysCreateResponse, project, path=["response"]) - - @parametrize - def test_raw_response_api_keys_create(self, client: Unlayer) -> None: - response = client.project.with_raw_response.api_keys_create( - project_id="projectId", - name="name", - ) - - assert response.is_closed is True - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - project = response.parse() - assert_matches_type(ProjectAPIKeysCreateResponse, project, path=["response"]) - - @parametrize - def test_streaming_response_api_keys_create(self, client: Unlayer) -> None: - with client.project.with_streaming_response.api_keys_create( - project_id="projectId", - name="name", - ) as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - - project = response.parse() - assert_matches_type(ProjectAPIKeysCreateResponse, project, path=["response"]) - - assert cast(Any, response.is_closed) is True - - @parametrize - def test_method_api_keys_delete(self, client: Unlayer) -> None: - project = client.project.api_keys_delete( - "id", - ) - assert project is None - - @parametrize - def test_raw_response_api_keys_delete(self, client: Unlayer) -> None: - response = client.project.with_raw_response.api_keys_delete( - "id", - ) - - assert response.is_closed is True - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - project = response.parse() - assert project is None - - @parametrize - def test_streaming_response_api_keys_delete(self, client: Unlayer) -> None: - with client.project.with_streaming_response.api_keys_delete( - "id", - ) as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - - project = response.parse() - assert project is None - - assert cast(Any, response.is_closed) is True - - @parametrize - def test_path_params_api_keys_delete(self, client: Unlayer) -> None: - with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): - client.project.with_raw_response.api_keys_delete( - "", - ) - - @parametrize - def test_method_api_keys_list(self, client: Unlayer) -> None: - project = client.project.api_keys_list( - project_id="projectId", - ) - assert_matches_type(ProjectAPIKeysListResponse, project, path=["response"]) - - @parametrize - def test_raw_response_api_keys_list(self, client: Unlayer) -> None: - response = client.project.with_raw_response.api_keys_list( - project_id="projectId", - ) - - assert response.is_closed is True - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - project = response.parse() - assert_matches_type(ProjectAPIKeysListResponse, project, path=["response"]) - - @parametrize - def test_streaming_response_api_keys_list(self, client: Unlayer) -> None: - with client.project.with_streaming_response.api_keys_list( - project_id="projectId", - ) as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - - project = response.parse() - assert_matches_type(ProjectAPIKeysListResponse, project, path=["response"]) - - assert cast(Any, response.is_closed) is True - - @parametrize - def test_method_api_keys_retrieve(self, client: Unlayer) -> None: - project = client.project.api_keys_retrieve( - "id", - ) - assert_matches_type(ProjectAPIKeysRetrieveResponse, project, path=["response"]) - - @parametrize - def test_raw_response_api_keys_retrieve(self, client: Unlayer) -> None: - response = client.project.with_raw_response.api_keys_retrieve( - "id", - ) - - assert response.is_closed is True - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - project = response.parse() - assert_matches_type(ProjectAPIKeysRetrieveResponse, project, path=["response"]) - - @parametrize - def test_streaming_response_api_keys_retrieve(self, client: Unlayer) -> None: - with client.project.with_streaming_response.api_keys_retrieve( - "id", - ) as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - - project = response.parse() - assert_matches_type(ProjectAPIKeysRetrieveResponse, project, path=["response"]) - - assert cast(Any, response.is_closed) is True - - @parametrize - def test_path_params_api_keys_retrieve(self, client: Unlayer) -> None: - with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): - client.project.with_raw_response.api_keys_retrieve( - "", - ) - - @parametrize - def test_method_api_keys_update(self, client: Unlayer) -> None: - project = client.project.api_keys_update( - id="id", - ) - assert_matches_type(ProjectAPIKeysUpdateResponse, project, path=["response"]) - - @parametrize - def test_method_api_keys_update_with_all_params(self, client: Unlayer) -> None: - project = client.project.api_keys_update( - id="id", - active=True, - domains=["string"], - name="name", - ) - assert_matches_type(ProjectAPIKeysUpdateResponse, project, path=["response"]) - - @parametrize - def test_raw_response_api_keys_update(self, client: Unlayer) -> None: - response = client.project.with_raw_response.api_keys_update( - id="id", - ) - - assert response.is_closed is True - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - project = response.parse() - assert_matches_type(ProjectAPIKeysUpdateResponse, project, path=["response"]) - - @parametrize - def test_streaming_response_api_keys_update(self, client: Unlayer) -> None: - with client.project.with_streaming_response.api_keys_update( - id="id", - ) as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - - project = response.parse() - assert_matches_type(ProjectAPIKeysUpdateResponse, project, path=["response"]) - - assert cast(Any, response.is_closed) is True - - @parametrize - def test_path_params_api_keys_update(self, client: Unlayer) -> None: - with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): - client.project.with_raw_response.api_keys_update( - id="", - ) - @parametrize def test_method_current_list(self, client: Unlayer) -> None: project = client.project.current_list( @@ -464,8 +261,7 @@ def test_method_templates_create_with_all_params(self, client: Unlayer) -> None: project = client.project.templates_create( project_id="projectId", name="name", - body="body", - subject="subject", + display_mode="email", ) assert_matches_type(ProjectTemplatesCreateResponse, project, path=["response"]) @@ -538,7 +334,18 @@ def test_method_templates_list(self, client: Unlayer) -> None: project = client.project.templates_list( project_id="projectId", ) - assert_matches_type(ProjectTemplatesListResponse, project, path=["response"]) + assert_matches_type(SyncCursorPage[ProjectTemplatesListResponse], project, path=["response"]) + + @parametrize + def test_method_templates_list_with_all_params(self, client: Unlayer) -> None: + project = client.project.templates_list( + project_id="projectId", + cursor="cursor", + display_mode="email", + limit=1, + name="name", + ) + assert_matches_type(SyncCursorPage[ProjectTemplatesListResponse], project, path=["response"]) @parametrize def test_raw_response_templates_list(self, client: Unlayer) -> None: @@ -549,7 +356,7 @@ def test_raw_response_templates_list(self, client: Unlayer) -> None: assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" project = response.parse() - assert_matches_type(ProjectTemplatesListResponse, project, path=["response"]) + assert_matches_type(SyncCursorPage[ProjectTemplatesListResponse], project, path=["response"]) @parametrize def test_streaming_response_templates_list(self, client: Unlayer) -> None: @@ -560,7 +367,7 @@ def test_streaming_response_templates_list(self, client: Unlayer) -> None: assert response.http_request.headers.get("X-Stainless-Lang") == "python" project = response.parse() - assert_matches_type(ProjectTemplatesListResponse, project, path=["response"]) + assert_matches_type(SyncCursorPage[ProjectTemplatesListResponse], project, path=["response"]) assert cast(Any, response.is_closed) is True @@ -650,69 +457,6 @@ def test_path_params_templates_update(self, client: Unlayer) -> None: id="", ) - @parametrize - def test_method_tokens_delete(self, client: Unlayer) -> None: - project = client.project.tokens_delete( - "tokenId", - ) - assert_matches_type(ProjectTokensDeleteResponse, project, path=["response"]) - - @parametrize - def test_raw_response_tokens_delete(self, client: Unlayer) -> None: - response = client.project.with_raw_response.tokens_delete( - "tokenId", - ) - - assert response.is_closed is True - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - project = response.parse() - assert_matches_type(ProjectTokensDeleteResponse, project, path=["response"]) - - @parametrize - def test_streaming_response_tokens_delete(self, client: Unlayer) -> None: - with client.project.with_streaming_response.tokens_delete( - "tokenId", - ) as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - - project = response.parse() - assert_matches_type(ProjectTokensDeleteResponse, project, path=["response"]) - - assert cast(Any, response.is_closed) is True - - @parametrize - def test_path_params_tokens_delete(self, client: Unlayer) -> None: - with pytest.raises(ValueError, match=r"Expected a non-empty value for `token_id` but received ''"): - client.project.with_raw_response.tokens_delete( - "", - ) - - @parametrize - def test_method_tokens_list(self, client: Unlayer) -> None: - project = client.project.tokens_list() - assert_matches_type(ProjectTokensListResponse, project, path=["response"]) - - @parametrize - def test_raw_response_tokens_list(self, client: Unlayer) -> None: - response = client.project.with_raw_response.tokens_list() - - assert response.is_closed is True - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - project = response.parse() - assert_matches_type(ProjectTokensListResponse, project, path=["response"]) - - @parametrize - def test_streaming_response_tokens_list(self, client: Unlayer) -> None: - with client.project.with_streaming_response.tokens_list() as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - - project = response.parse() - assert_matches_type(ProjectTokensListResponse, project, path=["response"]) - - assert cast(Any, response.is_closed) is True - @parametrize def test_method_workspaces_list(self, client: Unlayer) -> None: project = client.project.workspaces_list() @@ -782,204 +526,6 @@ class TestAsyncProject: "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] ) - @parametrize - async def test_method_api_keys_create(self, async_client: AsyncUnlayer) -> None: - project = await async_client.project.api_keys_create( - project_id="projectId", - name="name", - ) - assert_matches_type(ProjectAPIKeysCreateResponse, project, path=["response"]) - - @parametrize - async def test_method_api_keys_create_with_all_params(self, async_client: AsyncUnlayer) -> None: - project = await async_client.project.api_keys_create( - project_id="projectId", - name="name", - domains=["string"], - ) - assert_matches_type(ProjectAPIKeysCreateResponse, project, path=["response"]) - - @parametrize - async def test_raw_response_api_keys_create(self, async_client: AsyncUnlayer) -> None: - response = await async_client.project.with_raw_response.api_keys_create( - project_id="projectId", - name="name", - ) - - assert response.is_closed is True - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - project = await response.parse() - assert_matches_type(ProjectAPIKeysCreateResponse, project, path=["response"]) - - @parametrize - async def test_streaming_response_api_keys_create(self, async_client: AsyncUnlayer) -> None: - async with async_client.project.with_streaming_response.api_keys_create( - project_id="projectId", - name="name", - ) as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - - project = await response.parse() - assert_matches_type(ProjectAPIKeysCreateResponse, project, path=["response"]) - - assert cast(Any, response.is_closed) is True - - @parametrize - async def test_method_api_keys_delete(self, async_client: AsyncUnlayer) -> None: - project = await async_client.project.api_keys_delete( - "id", - ) - assert project is None - - @parametrize - async def test_raw_response_api_keys_delete(self, async_client: AsyncUnlayer) -> None: - response = await async_client.project.with_raw_response.api_keys_delete( - "id", - ) - - assert response.is_closed is True - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - project = await response.parse() - assert project is None - - @parametrize - async def test_streaming_response_api_keys_delete(self, async_client: AsyncUnlayer) -> None: - async with async_client.project.with_streaming_response.api_keys_delete( - "id", - ) as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - - project = await response.parse() - assert project is None - - assert cast(Any, response.is_closed) is True - - @parametrize - async def test_path_params_api_keys_delete(self, async_client: AsyncUnlayer) -> None: - with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): - await async_client.project.with_raw_response.api_keys_delete( - "", - ) - - @parametrize - async def test_method_api_keys_list(self, async_client: AsyncUnlayer) -> None: - project = await async_client.project.api_keys_list( - project_id="projectId", - ) - assert_matches_type(ProjectAPIKeysListResponse, project, path=["response"]) - - @parametrize - async def test_raw_response_api_keys_list(self, async_client: AsyncUnlayer) -> None: - response = await async_client.project.with_raw_response.api_keys_list( - project_id="projectId", - ) - - assert response.is_closed is True - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - project = await response.parse() - assert_matches_type(ProjectAPIKeysListResponse, project, path=["response"]) - - @parametrize - async def test_streaming_response_api_keys_list(self, async_client: AsyncUnlayer) -> None: - async with async_client.project.with_streaming_response.api_keys_list( - project_id="projectId", - ) as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - - project = await response.parse() - assert_matches_type(ProjectAPIKeysListResponse, project, path=["response"]) - - assert cast(Any, response.is_closed) is True - - @parametrize - async def test_method_api_keys_retrieve(self, async_client: AsyncUnlayer) -> None: - project = await async_client.project.api_keys_retrieve( - "id", - ) - assert_matches_type(ProjectAPIKeysRetrieveResponse, project, path=["response"]) - - @parametrize - async def test_raw_response_api_keys_retrieve(self, async_client: AsyncUnlayer) -> None: - response = await async_client.project.with_raw_response.api_keys_retrieve( - "id", - ) - - assert response.is_closed is True - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - project = await response.parse() - assert_matches_type(ProjectAPIKeysRetrieveResponse, project, path=["response"]) - - @parametrize - async def test_streaming_response_api_keys_retrieve(self, async_client: AsyncUnlayer) -> None: - async with async_client.project.with_streaming_response.api_keys_retrieve( - "id", - ) as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - - project = await response.parse() - assert_matches_type(ProjectAPIKeysRetrieveResponse, project, path=["response"]) - - assert cast(Any, response.is_closed) is True - - @parametrize - async def test_path_params_api_keys_retrieve(self, async_client: AsyncUnlayer) -> None: - with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): - await async_client.project.with_raw_response.api_keys_retrieve( - "", - ) - - @parametrize - async def test_method_api_keys_update(self, async_client: AsyncUnlayer) -> None: - project = await async_client.project.api_keys_update( - id="id", - ) - assert_matches_type(ProjectAPIKeysUpdateResponse, project, path=["response"]) - - @parametrize - async def test_method_api_keys_update_with_all_params(self, async_client: AsyncUnlayer) -> None: - project = await async_client.project.api_keys_update( - id="id", - active=True, - domains=["string"], - name="name", - ) - assert_matches_type(ProjectAPIKeysUpdateResponse, project, path=["response"]) - - @parametrize - async def test_raw_response_api_keys_update(self, async_client: AsyncUnlayer) -> None: - response = await async_client.project.with_raw_response.api_keys_update( - id="id", - ) - - assert response.is_closed is True - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - project = await response.parse() - assert_matches_type(ProjectAPIKeysUpdateResponse, project, path=["response"]) - - @parametrize - async def test_streaming_response_api_keys_update(self, async_client: AsyncUnlayer) -> None: - async with async_client.project.with_streaming_response.api_keys_update( - id="id", - ) as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - - project = await response.parse() - assert_matches_type(ProjectAPIKeysUpdateResponse, project, path=["response"]) - - assert cast(Any, response.is_closed) is True - - @parametrize - async def test_path_params_api_keys_update(self, async_client: AsyncUnlayer) -> None: - with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): - await async_client.project.with_raw_response.api_keys_update( - id="", - ) - @parametrize async def test_method_current_list(self, async_client: AsyncUnlayer) -> None: project = await async_client.project.current_list( @@ -1211,8 +757,7 @@ async def test_method_templates_create_with_all_params(self, async_client: Async project = await async_client.project.templates_create( project_id="projectId", name="name", - body="body", - subject="subject", + display_mode="email", ) assert_matches_type(ProjectTemplatesCreateResponse, project, path=["response"]) @@ -1285,7 +830,18 @@ async def test_method_templates_list(self, async_client: AsyncUnlayer) -> None: project = await async_client.project.templates_list( project_id="projectId", ) - assert_matches_type(ProjectTemplatesListResponse, project, path=["response"]) + assert_matches_type(AsyncCursorPage[ProjectTemplatesListResponse], project, path=["response"]) + + @parametrize + async def test_method_templates_list_with_all_params(self, async_client: AsyncUnlayer) -> None: + project = await async_client.project.templates_list( + project_id="projectId", + cursor="cursor", + display_mode="email", + limit=1, + name="name", + ) + assert_matches_type(AsyncCursorPage[ProjectTemplatesListResponse], project, path=["response"]) @parametrize async def test_raw_response_templates_list(self, async_client: AsyncUnlayer) -> None: @@ -1296,7 +852,7 @@ async def test_raw_response_templates_list(self, async_client: AsyncUnlayer) -> assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" project = await response.parse() - assert_matches_type(ProjectTemplatesListResponse, project, path=["response"]) + assert_matches_type(AsyncCursorPage[ProjectTemplatesListResponse], project, path=["response"]) @parametrize async def test_streaming_response_templates_list(self, async_client: AsyncUnlayer) -> None: @@ -1307,7 +863,7 @@ async def test_streaming_response_templates_list(self, async_client: AsyncUnlaye assert response.http_request.headers.get("X-Stainless-Lang") == "python" project = await response.parse() - assert_matches_type(ProjectTemplatesListResponse, project, path=["response"]) + assert_matches_type(AsyncCursorPage[ProjectTemplatesListResponse], project, path=["response"]) assert cast(Any, response.is_closed) is True @@ -1397,69 +953,6 @@ async def test_path_params_templates_update(self, async_client: AsyncUnlayer) -> id="", ) - @parametrize - async def test_method_tokens_delete(self, async_client: AsyncUnlayer) -> None: - project = await async_client.project.tokens_delete( - "tokenId", - ) - assert_matches_type(ProjectTokensDeleteResponse, project, path=["response"]) - - @parametrize - async def test_raw_response_tokens_delete(self, async_client: AsyncUnlayer) -> None: - response = await async_client.project.with_raw_response.tokens_delete( - "tokenId", - ) - - assert response.is_closed is True - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - project = await response.parse() - assert_matches_type(ProjectTokensDeleteResponse, project, path=["response"]) - - @parametrize - async def test_streaming_response_tokens_delete(self, async_client: AsyncUnlayer) -> None: - async with async_client.project.with_streaming_response.tokens_delete( - "tokenId", - ) as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - - project = await response.parse() - assert_matches_type(ProjectTokensDeleteResponse, project, path=["response"]) - - assert cast(Any, response.is_closed) is True - - @parametrize - async def test_path_params_tokens_delete(self, async_client: AsyncUnlayer) -> None: - with pytest.raises(ValueError, match=r"Expected a non-empty value for `token_id` but received ''"): - await async_client.project.with_raw_response.tokens_delete( - "", - ) - - @parametrize - async def test_method_tokens_list(self, async_client: AsyncUnlayer) -> None: - project = await async_client.project.tokens_list() - assert_matches_type(ProjectTokensListResponse, project, path=["response"]) - - @parametrize - async def test_raw_response_tokens_list(self, async_client: AsyncUnlayer) -> None: - response = await async_client.project.with_raw_response.tokens_list() - - assert response.is_closed is True - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - project = await response.parse() - assert_matches_type(ProjectTokensListResponse, project, path=["response"]) - - @parametrize - async def test_streaming_response_tokens_list(self, async_client: AsyncUnlayer) -> None: - async with async_client.project.with_streaming_response.tokens_list() as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - - project = await response.parse() - assert_matches_type(ProjectTokensListResponse, project, path=["response"]) - - assert cast(Any, response.is_closed) is True - @parametrize async def test_method_workspaces_list(self, async_client: AsyncUnlayer) -> None: project = await async_client.project.workspaces_list() diff --git a/tests/conftest.py b/tests/conftest.py index fc6ba41..0fed985 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -45,7 +45,7 @@ def pytest_collection_modifyitems(items: list[pytest.Function]) -> None: base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") -api_key = "My API Key" +access_token = "My Access Token" @pytest.fixture(scope="session") @@ -54,7 +54,7 @@ def client(request: FixtureRequest) -> Iterator[Unlayer]: if not isinstance(strict, bool): raise TypeError(f"Unexpected fixture parameter type {type(strict)}, expected {bool}") - with Unlayer(base_url=base_url, api_key=api_key, _strict_response_validation=strict) as client: + with Unlayer(base_url=base_url, access_token=access_token, _strict_response_validation=strict) as client: yield client @@ -79,6 +79,6 @@ async def async_client(request: FixtureRequest) -> AsyncIterator[AsyncUnlayer]: raise TypeError(f"Unexpected fixture parameter type {type(param)}, expected bool or dict") async with AsyncUnlayer( - base_url=base_url, api_key=api_key, _strict_response_validation=strict, http_client=http_client + base_url=base_url, access_token=access_token, _strict_response_validation=strict, http_client=http_client ) as client: yield client diff --git a/tests/test_client.py b/tests/test_client.py index 3ee68fe..cd37743 100644 --- a/tests/test_client.py +++ b/tests/test_client.py @@ -39,7 +39,7 @@ T = TypeVar("T") base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") -api_key = "My API Key" +access_token = "My Access Token" def _get_params(client: BaseClient[Any, Any]) -> dict[str, str]: @@ -136,9 +136,9 @@ def test_copy(self, client: Unlayer) -> None: copied = client.copy() assert id(copied) != id(client) - copied = client.copy(api_key="another My API Key") - assert copied.api_key == "another My API Key" - assert client.api_key == "My API Key" + copied = client.copy(access_token="another My Access Token") + assert copied.access_token == "another My Access Token" + assert client.access_token == "My Access Token" def test_copy_default_options(self, client: Unlayer) -> None: # options that have a default are overridden correctly @@ -158,7 +158,10 @@ def test_copy_default_options(self, client: Unlayer) -> None: def test_copy_default_headers(self) -> None: client = Unlayer( - base_url=base_url, api_key=api_key, _strict_response_validation=True, default_headers={"X-Foo": "bar"} + base_url=base_url, + access_token=access_token, + _strict_response_validation=True, + default_headers={"X-Foo": "bar"}, ) assert client.default_headers["X-Foo"] == "bar" @@ -193,7 +196,7 @@ def test_copy_default_headers(self) -> None: def test_copy_default_query(self) -> None: client = Unlayer( - base_url=base_url, api_key=api_key, _strict_response_validation=True, default_query={"foo": "bar"} + base_url=base_url, access_token=access_token, _strict_response_validation=True, default_query={"foo": "bar"} ) assert _get_params(client)["foo"] == "bar" @@ -318,7 +321,9 @@ def test_request_timeout(self, client: Unlayer) -> None: assert timeout == httpx.Timeout(100.0) def test_client_timeout_option(self) -> None: - client = Unlayer(base_url=base_url, api_key=api_key, _strict_response_validation=True, timeout=httpx.Timeout(0)) + client = Unlayer( + base_url=base_url, access_token=access_token, _strict_response_validation=True, timeout=httpx.Timeout(0) + ) request = client._build_request(FinalRequestOptions(method="get", url="/foo")) timeout = httpx.Timeout(**request.extensions["timeout"]) # type: ignore @@ -330,7 +335,7 @@ def test_http_client_timeout_option(self) -> None: # custom timeout given to the httpx client should be used with httpx.Client(timeout=None) as http_client: client = Unlayer( - base_url=base_url, api_key=api_key, _strict_response_validation=True, http_client=http_client + base_url=base_url, access_token=access_token, _strict_response_validation=True, http_client=http_client ) request = client._build_request(FinalRequestOptions(method="get", url="/foo")) @@ -342,7 +347,7 @@ def test_http_client_timeout_option(self) -> None: # no timeout given to the httpx client should not use the httpx default with httpx.Client() as http_client: client = Unlayer( - base_url=base_url, api_key=api_key, _strict_response_validation=True, http_client=http_client + base_url=base_url, access_token=access_token, _strict_response_validation=True, http_client=http_client ) request = client._build_request(FinalRequestOptions(method="get", url="/foo")) @@ -354,7 +359,7 @@ def test_http_client_timeout_option(self) -> None: # explicitly passing the default timeout currently results in it being ignored with httpx.Client(timeout=HTTPX_DEFAULT_TIMEOUT) as http_client: client = Unlayer( - base_url=base_url, api_key=api_key, _strict_response_validation=True, http_client=http_client + base_url=base_url, access_token=access_token, _strict_response_validation=True, http_client=http_client ) request = client._build_request(FinalRequestOptions(method="get", url="/foo")) @@ -368,14 +373,17 @@ async def test_invalid_http_client(self) -> None: async with httpx.AsyncClient() as http_client: Unlayer( base_url=base_url, - api_key=api_key, + access_token=access_token, _strict_response_validation=True, http_client=cast(Any, http_client), ) def test_default_headers_option(self) -> None: test_client = Unlayer( - base_url=base_url, api_key=api_key, _strict_response_validation=True, default_headers={"X-Foo": "bar"} + base_url=base_url, + access_token=access_token, + _strict_response_validation=True, + default_headers={"X-Foo": "bar"}, ) request = test_client._build_request(FinalRequestOptions(method="get", url="/foo")) assert request.headers.get("x-foo") == "bar" @@ -383,7 +391,7 @@ def test_default_headers_option(self) -> None: test_client2 = Unlayer( base_url=base_url, - api_key=api_key, + access_token=access_token, _strict_response_validation=True, default_headers={ "X-Foo": "stainless", @@ -398,18 +406,21 @@ def test_default_headers_option(self) -> None: test_client2.close() def test_validate_headers(self) -> None: - client = Unlayer(base_url=base_url, api_key=api_key, _strict_response_validation=True) + client = Unlayer(base_url=base_url, access_token=access_token, _strict_response_validation=True) request = client._build_request(FinalRequestOptions(method="get", url="/foo")) - assert request.headers.get("Authorization") == f"Bearer {api_key}" + assert request.headers.get("Authorization") == f"Bearer {access_token}" with pytest.raises(UnlayerError): - with update_env(**{"UNLAYER_API_KEY": Omit()}): - client2 = Unlayer(base_url=base_url, api_key=None, _strict_response_validation=True) + with update_env(**{"UNLAYER_ACCESS_TOKEN": Omit()}): + client2 = Unlayer(base_url=base_url, access_token=None, _strict_response_validation=True) _ = client2 def test_default_query_option(self) -> None: client = Unlayer( - base_url=base_url, api_key=api_key, _strict_response_validation=True, default_query={"query_param": "bar"} + base_url=base_url, + access_token=access_token, + _strict_response_validation=True, + default_query={"query_param": "bar"}, ) request = client._build_request(FinalRequestOptions(method="get", url="/foo")) url = httpx.URL(request.url) @@ -581,7 +592,7 @@ def mock_handler(request: httpx.Request) -> httpx.Response: with Unlayer( base_url=base_url, - api_key=api_key, + access_token=access_token, _strict_response_validation=True, http_client=httpx.Client(transport=MockTransport(handler=mock_handler)), ) as client: @@ -675,7 +686,9 @@ class Model(BaseModel): assert response.foo == 2 def test_base_url_setter(self) -> None: - client = Unlayer(base_url="https://example.com/from_init", api_key=api_key, _strict_response_validation=True) + client = Unlayer( + base_url="https://example.com/from_init", access_token=access_token, _strict_response_validation=True + ) assert client.base_url == "https://example.com/from_init/" client.base_url = "https://example.com/from_setter" # type: ignore[assignment] @@ -686,15 +699,17 @@ def test_base_url_setter(self) -> None: def test_base_url_env(self) -> None: with update_env(UNLAYER_BASE_URL="http://localhost:5000/from/env"): - client = Unlayer(api_key=api_key, _strict_response_validation=True) + client = Unlayer(access_token=access_token, _strict_response_validation=True) assert client.base_url == "http://localhost:5000/from/env/" # explicit environment arg requires explicitness with update_env(UNLAYER_BASE_URL="http://localhost:5000/from/env"): with pytest.raises(ValueError, match=r"you must pass base_url=None"): - Unlayer(api_key=api_key, _strict_response_validation=True, environment="production") + Unlayer(access_token=access_token, _strict_response_validation=True, environment="production") - client = Unlayer(base_url=None, api_key=api_key, _strict_response_validation=True, environment="production") + client = Unlayer( + base_url=None, access_token=access_token, _strict_response_validation=True, environment="production" + ) assert str(client.base_url).startswith("https://api.unlayer.com") client.close() @@ -702,10 +717,14 @@ def test_base_url_env(self) -> None: @pytest.mark.parametrize( "client", [ - Unlayer(base_url="http://localhost:5000/custom/path/", api_key=api_key, _strict_response_validation=True), Unlayer( base_url="http://localhost:5000/custom/path/", - api_key=api_key, + access_token=access_token, + _strict_response_validation=True, + ), + Unlayer( + base_url="http://localhost:5000/custom/path/", + access_token=access_token, _strict_response_validation=True, http_client=httpx.Client(), ), @@ -726,10 +745,14 @@ def test_base_url_trailing_slash(self, client: Unlayer) -> None: @pytest.mark.parametrize( "client", [ - Unlayer(base_url="http://localhost:5000/custom/path/", api_key=api_key, _strict_response_validation=True), Unlayer( base_url="http://localhost:5000/custom/path/", - api_key=api_key, + access_token=access_token, + _strict_response_validation=True, + ), + Unlayer( + base_url="http://localhost:5000/custom/path/", + access_token=access_token, _strict_response_validation=True, http_client=httpx.Client(), ), @@ -750,10 +773,14 @@ def test_base_url_no_trailing_slash(self, client: Unlayer) -> None: @pytest.mark.parametrize( "client", [ - Unlayer(base_url="http://localhost:5000/custom/path/", api_key=api_key, _strict_response_validation=True), Unlayer( base_url="http://localhost:5000/custom/path/", - api_key=api_key, + access_token=access_token, + _strict_response_validation=True, + ), + Unlayer( + base_url="http://localhost:5000/custom/path/", + access_token=access_token, _strict_response_validation=True, http_client=httpx.Client(), ), @@ -772,7 +799,7 @@ def test_absolute_request_url(self, client: Unlayer) -> None: client.close() def test_copied_client_does_not_close_http(self) -> None: - test_client = Unlayer(base_url=base_url, api_key=api_key, _strict_response_validation=True) + test_client = Unlayer(base_url=base_url, access_token=access_token, _strict_response_validation=True) assert not test_client.is_closed() copied = test_client.copy() @@ -783,7 +810,7 @@ def test_copied_client_does_not_close_http(self) -> None: assert not test_client.is_closed() def test_client_context_manager(self) -> None: - test_client = Unlayer(base_url=base_url, api_key=api_key, _strict_response_validation=True) + test_client = Unlayer(base_url=base_url, access_token=access_token, _strict_response_validation=True) with test_client as c2: assert c2 is test_client assert not c2.is_closed() @@ -804,7 +831,12 @@ class Model(BaseModel): def test_client_max_retries_validation(self) -> None: with pytest.raises(TypeError, match=r"max_retries cannot be None"): - Unlayer(base_url=base_url, api_key=api_key, _strict_response_validation=True, max_retries=cast(Any, None)) + Unlayer( + base_url=base_url, + access_token=access_token, + _strict_response_validation=True, + max_retries=cast(Any, None), + ) @pytest.mark.respx(base_url=base_url) def test_received_text_for_expected_json(self, respx_mock: MockRouter) -> None: @@ -813,12 +845,12 @@ class Model(BaseModel): respx_mock.get("/foo").mock(return_value=httpx.Response(200, text="my-custom-format")) - strict_client = Unlayer(base_url=base_url, api_key=api_key, _strict_response_validation=True) + strict_client = Unlayer(base_url=base_url, access_token=access_token, _strict_response_validation=True) with pytest.raises(APIResponseValidationError): strict_client.get("/foo", cast_to=Model) - non_strict_client = Unlayer(base_url=base_url, api_key=api_key, _strict_response_validation=False) + non_strict_client = Unlayer(base_url=base_url, access_token=access_token, _strict_response_validation=False) response = non_strict_client.get("/foo", cast_to=Model) assert isinstance(response, str) # type: ignore[unreachable] @@ -1029,9 +1061,9 @@ def test_copy(self, async_client: AsyncUnlayer) -> None: copied = async_client.copy() assert id(copied) != id(async_client) - copied = async_client.copy(api_key="another My API Key") - assert copied.api_key == "another My API Key" - assert async_client.api_key == "My API Key" + copied = async_client.copy(access_token="another My Access Token") + assert copied.access_token == "another My Access Token" + assert async_client.access_token == "My Access Token" def test_copy_default_options(self, async_client: AsyncUnlayer) -> None: # options that have a default are overridden correctly @@ -1051,7 +1083,10 @@ def test_copy_default_options(self, async_client: AsyncUnlayer) -> None: async def test_copy_default_headers(self) -> None: client = AsyncUnlayer( - base_url=base_url, api_key=api_key, _strict_response_validation=True, default_headers={"X-Foo": "bar"} + base_url=base_url, + access_token=access_token, + _strict_response_validation=True, + default_headers={"X-Foo": "bar"}, ) assert client.default_headers["X-Foo"] == "bar" @@ -1086,7 +1121,7 @@ async def test_copy_default_headers(self) -> None: async def test_copy_default_query(self) -> None: client = AsyncUnlayer( - base_url=base_url, api_key=api_key, _strict_response_validation=True, default_query={"foo": "bar"} + base_url=base_url, access_token=access_token, _strict_response_validation=True, default_query={"foo": "bar"} ) assert _get_params(client)["foo"] == "bar" @@ -1214,7 +1249,7 @@ async def test_request_timeout(self, async_client: AsyncUnlayer) -> None: async def test_client_timeout_option(self) -> None: client = AsyncUnlayer( - base_url=base_url, api_key=api_key, _strict_response_validation=True, timeout=httpx.Timeout(0) + base_url=base_url, access_token=access_token, _strict_response_validation=True, timeout=httpx.Timeout(0) ) request = client._build_request(FinalRequestOptions(method="get", url="/foo")) @@ -1227,7 +1262,7 @@ async def test_http_client_timeout_option(self) -> None: # custom timeout given to the httpx client should be used async with httpx.AsyncClient(timeout=None) as http_client: client = AsyncUnlayer( - base_url=base_url, api_key=api_key, _strict_response_validation=True, http_client=http_client + base_url=base_url, access_token=access_token, _strict_response_validation=True, http_client=http_client ) request = client._build_request(FinalRequestOptions(method="get", url="/foo")) @@ -1239,7 +1274,7 @@ async def test_http_client_timeout_option(self) -> None: # no timeout given to the httpx client should not use the httpx default async with httpx.AsyncClient() as http_client: client = AsyncUnlayer( - base_url=base_url, api_key=api_key, _strict_response_validation=True, http_client=http_client + base_url=base_url, access_token=access_token, _strict_response_validation=True, http_client=http_client ) request = client._build_request(FinalRequestOptions(method="get", url="/foo")) @@ -1251,7 +1286,7 @@ async def test_http_client_timeout_option(self) -> None: # explicitly passing the default timeout currently results in it being ignored async with httpx.AsyncClient(timeout=HTTPX_DEFAULT_TIMEOUT) as http_client: client = AsyncUnlayer( - base_url=base_url, api_key=api_key, _strict_response_validation=True, http_client=http_client + base_url=base_url, access_token=access_token, _strict_response_validation=True, http_client=http_client ) request = client._build_request(FinalRequestOptions(method="get", url="/foo")) @@ -1265,14 +1300,17 @@ def test_invalid_http_client(self) -> None: with httpx.Client() as http_client: AsyncUnlayer( base_url=base_url, - api_key=api_key, + access_token=access_token, _strict_response_validation=True, http_client=cast(Any, http_client), ) async def test_default_headers_option(self) -> None: test_client = AsyncUnlayer( - base_url=base_url, api_key=api_key, _strict_response_validation=True, default_headers={"X-Foo": "bar"} + base_url=base_url, + access_token=access_token, + _strict_response_validation=True, + default_headers={"X-Foo": "bar"}, ) request = test_client._build_request(FinalRequestOptions(method="get", url="/foo")) assert request.headers.get("x-foo") == "bar" @@ -1280,7 +1318,7 @@ async def test_default_headers_option(self) -> None: test_client2 = AsyncUnlayer( base_url=base_url, - api_key=api_key, + access_token=access_token, _strict_response_validation=True, default_headers={ "X-Foo": "stainless", @@ -1295,18 +1333,21 @@ async def test_default_headers_option(self) -> None: await test_client2.close() def test_validate_headers(self) -> None: - client = AsyncUnlayer(base_url=base_url, api_key=api_key, _strict_response_validation=True) + client = AsyncUnlayer(base_url=base_url, access_token=access_token, _strict_response_validation=True) request = client._build_request(FinalRequestOptions(method="get", url="/foo")) - assert request.headers.get("Authorization") == f"Bearer {api_key}" + assert request.headers.get("Authorization") == f"Bearer {access_token}" with pytest.raises(UnlayerError): - with update_env(**{"UNLAYER_API_KEY": Omit()}): - client2 = AsyncUnlayer(base_url=base_url, api_key=None, _strict_response_validation=True) + with update_env(**{"UNLAYER_ACCESS_TOKEN": Omit()}): + client2 = AsyncUnlayer(base_url=base_url, access_token=None, _strict_response_validation=True) _ = client2 async def test_default_query_option(self) -> None: client = AsyncUnlayer( - base_url=base_url, api_key=api_key, _strict_response_validation=True, default_query={"query_param": "bar"} + base_url=base_url, + access_token=access_token, + _strict_response_validation=True, + default_query={"query_param": "bar"}, ) request = client._build_request(FinalRequestOptions(method="get", url="/foo")) url = httpx.URL(request.url) @@ -1478,7 +1519,7 @@ async def mock_handler(request: httpx.Request) -> httpx.Response: async with AsyncUnlayer( base_url=base_url, - api_key=api_key, + access_token=access_token, _strict_response_validation=True, http_client=httpx.AsyncClient(transport=MockTransport(handler=mock_handler)), ) as client: @@ -1577,7 +1618,7 @@ class Model(BaseModel): async def test_base_url_setter(self) -> None: client = AsyncUnlayer( - base_url="https://example.com/from_init", api_key=api_key, _strict_response_validation=True + base_url="https://example.com/from_init", access_token=access_token, _strict_response_validation=True ) assert client.base_url == "https://example.com/from_init/" @@ -1589,16 +1630,16 @@ async def test_base_url_setter(self) -> None: async def test_base_url_env(self) -> None: with update_env(UNLAYER_BASE_URL="http://localhost:5000/from/env"): - client = AsyncUnlayer(api_key=api_key, _strict_response_validation=True) + client = AsyncUnlayer(access_token=access_token, _strict_response_validation=True) assert client.base_url == "http://localhost:5000/from/env/" # explicit environment arg requires explicitness with update_env(UNLAYER_BASE_URL="http://localhost:5000/from/env"): with pytest.raises(ValueError, match=r"you must pass base_url=None"): - AsyncUnlayer(api_key=api_key, _strict_response_validation=True, environment="production") + AsyncUnlayer(access_token=access_token, _strict_response_validation=True, environment="production") client = AsyncUnlayer( - base_url=None, api_key=api_key, _strict_response_validation=True, environment="production" + base_url=None, access_token=access_token, _strict_response_validation=True, environment="production" ) assert str(client.base_url).startswith("https://api.unlayer.com") @@ -1608,11 +1649,13 @@ async def test_base_url_env(self) -> None: "client", [ AsyncUnlayer( - base_url="http://localhost:5000/custom/path/", api_key=api_key, _strict_response_validation=True + base_url="http://localhost:5000/custom/path/", + access_token=access_token, + _strict_response_validation=True, ), AsyncUnlayer( base_url="http://localhost:5000/custom/path/", - api_key=api_key, + access_token=access_token, _strict_response_validation=True, http_client=httpx.AsyncClient(), ), @@ -1634,11 +1677,13 @@ async def test_base_url_trailing_slash(self, client: AsyncUnlayer) -> None: "client", [ AsyncUnlayer( - base_url="http://localhost:5000/custom/path/", api_key=api_key, _strict_response_validation=True + base_url="http://localhost:5000/custom/path/", + access_token=access_token, + _strict_response_validation=True, ), AsyncUnlayer( base_url="http://localhost:5000/custom/path/", - api_key=api_key, + access_token=access_token, _strict_response_validation=True, http_client=httpx.AsyncClient(), ), @@ -1660,11 +1705,13 @@ async def test_base_url_no_trailing_slash(self, client: AsyncUnlayer) -> None: "client", [ AsyncUnlayer( - base_url="http://localhost:5000/custom/path/", api_key=api_key, _strict_response_validation=True + base_url="http://localhost:5000/custom/path/", + access_token=access_token, + _strict_response_validation=True, ), AsyncUnlayer( base_url="http://localhost:5000/custom/path/", - api_key=api_key, + access_token=access_token, _strict_response_validation=True, http_client=httpx.AsyncClient(), ), @@ -1683,7 +1730,7 @@ async def test_absolute_request_url(self, client: AsyncUnlayer) -> None: await client.close() async def test_copied_client_does_not_close_http(self) -> None: - test_client = AsyncUnlayer(base_url=base_url, api_key=api_key, _strict_response_validation=True) + test_client = AsyncUnlayer(base_url=base_url, access_token=access_token, _strict_response_validation=True) assert not test_client.is_closed() copied = test_client.copy() @@ -1695,7 +1742,7 @@ async def test_copied_client_does_not_close_http(self) -> None: assert not test_client.is_closed() async def test_client_context_manager(self) -> None: - test_client = AsyncUnlayer(base_url=base_url, api_key=api_key, _strict_response_validation=True) + test_client = AsyncUnlayer(base_url=base_url, access_token=access_token, _strict_response_validation=True) async with test_client as c2: assert c2 is test_client assert not c2.is_closed() @@ -1717,7 +1764,10 @@ class Model(BaseModel): async def test_client_max_retries_validation(self) -> None: with pytest.raises(TypeError, match=r"max_retries cannot be None"): AsyncUnlayer( - base_url=base_url, api_key=api_key, _strict_response_validation=True, max_retries=cast(Any, None) + base_url=base_url, + access_token=access_token, + _strict_response_validation=True, + max_retries=cast(Any, None), ) @pytest.mark.respx(base_url=base_url) @@ -1727,12 +1777,14 @@ class Model(BaseModel): respx_mock.get("/foo").mock(return_value=httpx.Response(200, text="my-custom-format")) - strict_client = AsyncUnlayer(base_url=base_url, api_key=api_key, _strict_response_validation=True) + strict_client = AsyncUnlayer(base_url=base_url, access_token=access_token, _strict_response_validation=True) with pytest.raises(APIResponseValidationError): await strict_client.get("/foo", cast_to=Model) - non_strict_client = AsyncUnlayer(base_url=base_url, api_key=api_key, _strict_response_validation=False) + non_strict_client = AsyncUnlayer( + base_url=base_url, access_token=access_token, _strict_response_validation=False + ) response = await non_strict_client.get("/foo", cast_to=Model) assert isinstance(response, str) # type: ignore[unreachable] From f06f86b60ebaf6430611c9aea8f404aea6a07bd1 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Sat, 24 Jan 2026 03:50:04 +0000 Subject: [PATCH 23/62] chore(ci): upgrade `actions/github-script` --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index af33300..e5caf73 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -63,7 +63,7 @@ jobs: - name: Get GitHub OIDC Token if: github.repository == 'stainless-sdks/unlayer-python' id: github-oidc - uses: actions/github-script@v6 + uses: actions/github-script@v8 with: script: core.setOutput('github_token', await core.getIDToken()); From 2b6796219eff6d3a26b8436d0a07a32d32a9622d Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Fri, 30 Jan 2026 03:48:30 +0000 Subject: [PATCH 24/62] feat(client): add custom JSON encoder for extended type support --- src/unlayer/_base_client.py | 7 +- src/unlayer/_compat.py | 6 +- src/unlayer/_utils/_json.py | 35 ++++++++++ tests/test_utils/test_json.py | 126 ++++++++++++++++++++++++++++++++++ 4 files changed, 169 insertions(+), 5 deletions(-) create mode 100644 src/unlayer/_utils/_json.py create mode 100644 tests/test_utils/test_json.py diff --git a/src/unlayer/_base_client.py b/src/unlayer/_base_client.py index 7f21ba1..c199ad7 100644 --- a/src/unlayer/_base_client.py +++ b/src/unlayer/_base_client.py @@ -86,6 +86,7 @@ APIConnectionError, APIResponseValidationError, ) +from ._utils._json import openapi_dumps log: logging.Logger = logging.getLogger(__name__) @@ -554,8 +555,10 @@ def _build_request( kwargs["content"] = options.content elif isinstance(json_data, bytes): kwargs["content"] = json_data - else: - kwargs["json"] = json_data if is_given(json_data) else None + elif not files: + # Don't set content when JSON is sent as multipart/form-data, + # since httpx's content param overrides other body arguments + kwargs["content"] = openapi_dumps(json_data) if is_given(json_data) and json_data is not None else None kwargs["files"] = files else: headers.pop("Content-Type", None) diff --git a/src/unlayer/_compat.py b/src/unlayer/_compat.py index bdef67f..786ff42 100644 --- a/src/unlayer/_compat.py +++ b/src/unlayer/_compat.py @@ -139,6 +139,7 @@ def model_dump( exclude_defaults: bool = False, warnings: bool = True, mode: Literal["json", "python"] = "python", + by_alias: bool | None = None, ) -> dict[str, Any]: if (not PYDANTIC_V1) or hasattr(model, "model_dump"): return model.model_dump( @@ -148,13 +149,12 @@ def model_dump( exclude_defaults=exclude_defaults, # warnings are not supported in Pydantic v1 warnings=True if PYDANTIC_V1 else warnings, + by_alias=by_alias, ) return cast( "dict[str, Any]", model.dict( # pyright: ignore[reportDeprecated, reportUnnecessaryCast] - exclude=exclude, - exclude_unset=exclude_unset, - exclude_defaults=exclude_defaults, + exclude=exclude, exclude_unset=exclude_unset, exclude_defaults=exclude_defaults, by_alias=bool(by_alias) ), ) diff --git a/src/unlayer/_utils/_json.py b/src/unlayer/_utils/_json.py new file mode 100644 index 0000000..6058421 --- /dev/null +++ b/src/unlayer/_utils/_json.py @@ -0,0 +1,35 @@ +import json +from typing import Any +from datetime import datetime +from typing_extensions import override + +import pydantic + +from .._compat import model_dump + + +def openapi_dumps(obj: Any) -> bytes: + """ + Serialize an object to UTF-8 encoded JSON bytes. + + Extends the standard json.dumps with support for additional types + commonly used in the SDK, such as `datetime`, `pydantic.BaseModel`, etc. + """ + return json.dumps( + obj, + cls=_CustomEncoder, + # Uses the same defaults as httpx's JSON serialization + ensure_ascii=False, + separators=(",", ":"), + allow_nan=False, + ).encode() + + +class _CustomEncoder(json.JSONEncoder): + @override + def default(self, o: Any) -> Any: + if isinstance(o, datetime): + return o.isoformat() + if isinstance(o, pydantic.BaseModel): + return model_dump(o, exclude_unset=True, mode="json", by_alias=True) + return super().default(o) diff --git a/tests/test_utils/test_json.py b/tests/test_utils/test_json.py new file mode 100644 index 0000000..fb0d85b --- /dev/null +++ b/tests/test_utils/test_json.py @@ -0,0 +1,126 @@ +from __future__ import annotations + +import datetime +from typing import Union + +import pydantic + +from unlayer import _compat +from unlayer._utils._json import openapi_dumps + + +class TestOpenapiDumps: + def test_basic(self) -> None: + data = {"key": "value", "number": 42} + json_bytes = openapi_dumps(data) + assert json_bytes == b'{"key":"value","number":42}' + + def test_datetime_serialization(self) -> None: + dt = datetime.datetime(2023, 1, 1, 12, 0, 0) + data = {"datetime": dt} + json_bytes = openapi_dumps(data) + assert json_bytes == b'{"datetime":"2023-01-01T12:00:00"}' + + def test_pydantic_model_serialization(self) -> None: + class User(pydantic.BaseModel): + first_name: str + last_name: str + age: int + + model_instance = User(first_name="John", last_name="Kramer", age=83) + data = {"model": model_instance} + json_bytes = openapi_dumps(data) + assert json_bytes == b'{"model":{"first_name":"John","last_name":"Kramer","age":83}}' + + def test_pydantic_model_with_default_values(self) -> None: + class User(pydantic.BaseModel): + name: str + role: str = "user" + active: bool = True + score: int = 0 + + model_instance = User(name="Alice") + data = {"model": model_instance} + json_bytes = openapi_dumps(data) + assert json_bytes == b'{"model":{"name":"Alice"}}' + + def test_pydantic_model_with_default_values_overridden(self) -> None: + class User(pydantic.BaseModel): + name: str + role: str = "user" + active: bool = True + + model_instance = User(name="Bob", role="admin", active=False) + data = {"model": model_instance} + json_bytes = openapi_dumps(data) + assert json_bytes == b'{"model":{"name":"Bob","role":"admin","active":false}}' + + def test_pydantic_model_with_alias(self) -> None: + class User(pydantic.BaseModel): + first_name: str = pydantic.Field(alias="firstName") + last_name: str = pydantic.Field(alias="lastName") + + model_instance = User(firstName="John", lastName="Doe") + data = {"model": model_instance} + json_bytes = openapi_dumps(data) + assert json_bytes == b'{"model":{"firstName":"John","lastName":"Doe"}}' + + def test_pydantic_model_with_alias_and_default(self) -> None: + class User(pydantic.BaseModel): + user_name: str = pydantic.Field(alias="userName") + user_role: str = pydantic.Field(default="member", alias="userRole") + is_active: bool = pydantic.Field(default=True, alias="isActive") + + model_instance = User(userName="charlie") + data = {"model": model_instance} + json_bytes = openapi_dumps(data) + assert json_bytes == b'{"model":{"userName":"charlie"}}' + + model_with_overrides = User(userName="diana", userRole="admin", isActive=False) + data = {"model": model_with_overrides} + json_bytes = openapi_dumps(data) + assert json_bytes == b'{"model":{"userName":"diana","userRole":"admin","isActive":false}}' + + def test_pydantic_model_with_nested_models_and_defaults(self) -> None: + class Address(pydantic.BaseModel): + street: str + city: str = "Unknown" + + class User(pydantic.BaseModel): + name: str + address: Address + verified: bool = False + + if _compat.PYDANTIC_V1: + # to handle forward references in Pydantic v1 + User.update_forward_refs(**locals()) # type: ignore[reportDeprecated] + + address = Address(street="123 Main St") + user = User(name="Diana", address=address) + data = {"user": user} + json_bytes = openapi_dumps(data) + assert json_bytes == b'{"user":{"name":"Diana","address":{"street":"123 Main St"}}}' + + address_with_city = Address(street="456 Oak Ave", city="Boston") + user_verified = User(name="Eve", address=address_with_city, verified=True) + data = {"user": user_verified} + json_bytes = openapi_dumps(data) + assert ( + json_bytes == b'{"user":{"name":"Eve","address":{"street":"456 Oak Ave","city":"Boston"},"verified":true}}' + ) + + def test_pydantic_model_with_optional_fields(self) -> None: + class User(pydantic.BaseModel): + name: str + email: Union[str, None] + phone: Union[str, None] + + model_with_none = User(name="Eve", email=None, phone=None) + data = {"model": model_with_none} + json_bytes = openapi_dumps(data) + assert json_bytes == b'{"model":{"name":"Eve","email":null,"phone":null}}' + + model_with_values = User(name="Frank", email="frank@example.com", phone=None) + data = {"model": model_with_values} + json_bytes = openapi_dumps(data) + assert json_bytes == b'{"model":{"name":"Frank","email":"frank@example.com","phone":null}}' From beae8f7ed153ab35de3d67dbf5a0dfdb61bac50d Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Fri, 30 Jan 2026 16:24:39 +0000 Subject: [PATCH 25/62] codegen metadata --- .stats.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.stats.yml b/.stats.yml index ead84b6..2bb500f 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 25 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/unlayer%2Funlayer-42ce261f6dec6bdbfe9b98a3835cbb95c67440a58023646fe03ab0aa0745d671.yml -openapi_spec_hash: 0f18f2d5ea38b837ccc0156ea80d85cd +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/unlayer%2Funlayer-077d753eb6d4f805b2f84bf887289213f91c1da5f5a3a4e1e36e1da3689efb46.yml +openapi_spec_hash: b210022cf72d9a38fe1baee599054518 config_hash: ae90f2806448a012e25917d01699b3a5 From 9b33ae9584e688d299e8ceb8b2ad3b77fe8cebb4 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 10 Feb 2026 03:39:28 +0000 Subject: [PATCH 26/62] chore(internal): bump dependencies --- requirements-dev.lock | 20 ++++++++++---------- requirements.lock | 8 ++++---- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/requirements-dev.lock b/requirements-dev.lock index 0571b5a..3f3e2ec 100644 --- a/requirements-dev.lock +++ b/requirements-dev.lock @@ -12,14 +12,14 @@ -e file:. aiohappyeyeballs==2.6.1 # via aiohttp -aiohttp==3.13.2 +aiohttp==3.13.3 # via httpx-aiohttp # via unlayer aiosignal==1.4.0 # via aiohttp annotated-types==0.7.0 # via pydantic -anyio==4.12.0 +anyio==4.12.1 # via httpx # via unlayer argcomplete==3.6.3 @@ -31,7 +31,7 @@ attrs==25.4.0 # via nox backports-asyncio-runner==1.2.0 # via pytest-asyncio -certifi==2025.11.12 +certifi==2026.1.4 # via httpcore # via httpx colorlog==6.10.1 @@ -61,7 +61,7 @@ httpx==0.28.1 # via httpx-aiohttp # via respx # via unlayer -httpx-aiohttp==0.1.9 +httpx-aiohttp==0.1.12 # via unlayer humanize==4.13.0 # via nox @@ -69,7 +69,7 @@ idna==3.11 # via anyio # via httpx # via yarl -importlib-metadata==8.7.0 +importlib-metadata==8.7.1 iniconfig==2.1.0 # via pytest markdown-it-py==3.0.0 @@ -82,14 +82,14 @@ multidict==6.7.0 mypy==1.17.0 mypy-extensions==1.1.0 # via mypy -nodeenv==1.9.1 +nodeenv==1.10.0 # via pyright nox==2025.11.12 packaging==25.0 # via dependency-groups # via nox # via pytest -pathspec==0.12.1 +pathspec==1.0.3 # via mypy platformdirs==4.4.0 # via virtualenv @@ -115,13 +115,13 @@ python-dateutil==2.9.0.post0 # via time-machine respx==0.22.0 rich==14.2.0 -ruff==0.14.7 +ruff==0.14.13 six==1.17.0 # via python-dateutil sniffio==1.3.1 # via unlayer time-machine==2.19.0 -tomli==2.3.0 +tomli==2.4.0 # via dependency-groups # via mypy # via nox @@ -141,7 +141,7 @@ typing-extensions==4.15.0 # via virtualenv typing-inspection==0.4.2 # via pydantic -virtualenv==20.35.4 +virtualenv==20.36.1 # via nox yarl==1.22.0 # via aiohttp diff --git a/requirements.lock b/requirements.lock index 32753cc..559f353 100644 --- a/requirements.lock +++ b/requirements.lock @@ -12,21 +12,21 @@ -e file:. aiohappyeyeballs==2.6.1 # via aiohttp -aiohttp==3.13.2 +aiohttp==3.13.3 # via httpx-aiohttp # via unlayer aiosignal==1.4.0 # via aiohttp annotated-types==0.7.0 # via pydantic -anyio==4.12.0 +anyio==4.12.1 # via httpx # via unlayer async-timeout==5.0.1 # via aiohttp attrs==25.4.0 # via aiohttp -certifi==2025.11.12 +certifi==2026.1.4 # via httpcore # via httpx distro==1.9.0 @@ -43,7 +43,7 @@ httpcore==1.0.9 httpx==0.28.1 # via httpx-aiohttp # via unlayer -httpx-aiohttp==0.1.9 +httpx-aiohttp==0.1.12 # via unlayer idna==3.11 # via anyio From 5bc0b6c24b1711d7c94f6b7be95293f6bc9d0093 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Thu, 12 Feb 2026 04:06:11 +0000 Subject: [PATCH 27/62] chore(internal): fix lint error on Python 3.14 --- src/unlayer/_utils/_compat.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/unlayer/_utils/_compat.py b/src/unlayer/_utils/_compat.py index dd70323..2c70b29 100644 --- a/src/unlayer/_utils/_compat.py +++ b/src/unlayer/_utils/_compat.py @@ -26,7 +26,7 @@ def is_union(tp: Optional[Type[Any]]) -> bool: else: import types - return tp is Union or tp is types.UnionType + return tp is Union or tp is types.UnionType # type: ignore[comparison-overlap] def is_typeddict(tp: Type[Any]) -> bool: From 926dbe588410dad131e6e3bff1377587e7763c5a Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Fri, 13 Feb 2026 03:31:33 +0000 Subject: [PATCH 28/62] chore: format all `api.md` files --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index b1d1c1c..2aa32b4 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -69,7 +69,7 @@ format = { chain = [ # run formatting again to fix any inconsistencies when imports are stripped "format:ruff", ]} -"format:docs" = "python scripts/utils/ruffen-docs.py README.md api.md" +"format:docs" = "bash -c 'python scripts/utils/ruffen-docs.py README.md $(find . -type f -name api.md)'" "format:ruff" = "ruff format" "lint" = { chain = [ From fc5ef8336de8aabf961d0b15b70f1910b714d9d5 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Sun, 15 Feb 2026 14:23:41 +0000 Subject: [PATCH 29/62] feat(api): api update --- .stats.yml | 4 +- README.md | 114 +- api.md | 247 +++- src/unlayer/_client.py | 49 +- src/unlayer/resources/__init__.py | 14 + src/unlayer/resources/convert/__init__.py | 47 + src/unlayer/resources/convert/convert.py | 134 ++ .../resources/convert/full_to_simple.py | 183 +++ .../resources/convert/simple_to_full.py | 183 +++ src/unlayer/resources/documents.py | 457 ------ src/unlayer/resources/documents/__init__.py | 47 + src/unlayer/resources/documents/documents.py | 245 ++++ src/unlayer/resources/documents/generate.py | 227 +++ .../resources/documents/generate_template.py | 213 +++ src/unlayer/resources/emails.py | 575 -------- src/unlayer/resources/emails/__init__.py | 61 + src/unlayer/resources/emails/emails.py | 277 ++++ src/unlayer/resources/emails/render.py | 201 +++ src/unlayer/resources/emails/send.py | 225 +++ src/unlayer/resources/emails/send_template.py | 219 +++ src/unlayer/resources/export.py | 442 ------ src/unlayer/resources/export/__init__.py | 75 + src/unlayer/resources/export/export.py | 198 +++ src/unlayer/resources/export/html.py | 173 +++ src/unlayer/resources/export/image.py | 175 +++ src/unlayer/resources/export/pdf.py | 173 +++ src/unlayer/resources/export/zip.py | 173 +++ src/unlayer/resources/pages/__init__.py | 33 + src/unlayer/resources/pages/pages.py | 102 ++ .../resources/{pages.py => pages/render.py} | 100 +- src/unlayer/resources/project.py | 1278 ----------------- src/unlayer/resources/project/__init__.py | 75 + src/unlayer/resources/project/current.py | 175 +++ src/unlayer/resources/project/domains.py | 514 +++++++ src/unlayer/resources/project/project.py | 198 +++ src/unlayer/resources/project/templates.py | 608 ++++++++ src/unlayer/resources/project/workspaces.py | 214 +++ src/unlayer/types/__init__.py | 48 +- src/unlayer/types/convert/__init__.py | 8 + .../convert/full_to_simple_create_params.py | 29 + .../convert/full_to_simple_create_response.py | 18 + .../convert/simple_to_full_create_params.py | 37 + .../convert/simple_to_full_create_response.py | 18 + .../document_documents_retrieve_params.py | 14 - ..._params.py => document_retrieve_params.py} | 4 +- ...ponse.py => document_retrieve_response.py} | 4 +- src/unlayer/types/documents/__init__.py | 8 + .../generate_create_params.py} | 6 +- .../generate_create_response.py} | 6 +- .../generate_template_create_params.py} | 6 +- .../generate_template_create_response.py} | 6 +- src/unlayer/types/emails/__init__.py | 10 + .../render_create_params.py} | 6 +- .../render_create_response.py} | 6 +- .../send_create_params.py} | 6 +- .../send_create_response.py} | 6 +- .../send_template_create_params.py} | 6 +- .../send_template_create_response.py} | 6 +- src/unlayer/types/export/__init__.py | 12 + .../html_retrieve_params.py} | 6 +- .../html_retrieve_response.py} | 6 +- .../image_retrieve_params.py} | 6 +- .../image_retrieve_response.py} | 6 +- .../pdf_retrieve_params.py} | 6 +- .../pdf_retrieve_response.py} | 6 +- .../types/export/zip_retrieve_params.py | 14 + .../zip_retrieve_response.py} | 6 +- src/unlayer/types/pages/__init__.py | 6 + .../render_create_params.py} | 6 +- .../render_create_response.py} | 6 +- src/unlayer/types/project/__init__.py | 22 + .../types/project/current_retrieve_params.py | 14 + .../current_retrieve_response.py} | 6 +- .../domain_create_params.py} | 6 +- .../domain_create_response.py} | 6 +- .../types/project/domain_list_params.py | 14 + .../domain_list_response.py} | 6 +- .../domain_retrieve_response.py} | 6 +- .../domain_update_params.py} | 4 +- .../domain_update_response.py} | 6 +- .../template_create_params.py} | 6 +- .../template_create_response.py} | 6 +- .../template_list_params.py} | 6 +- .../template_list_response.py} | 18 +- .../template_retrieve_response.py} | 6 +- .../template_update_params.py} | 4 +- .../template_update_response.py} | 6 +- .../workspace_list_response.py} | 6 +- .../workspace_retrieve_response.py} | 6 +- .../types/project_current_list_params.py | 14 - .../types/project_domains_list_params.py | 14 - tests/api_resources/convert/__init__.py | 1 + .../convert/test_full_to_simple.py | 112 ++ .../convert/test_simple_to_full.py | 120 ++ tests/api_resources/documents/__init__.py | 1 + .../api_resources/documents/test_generate.py | 110 ++ .../documents/test_generate_template.py | 112 ++ tests/api_resources/emails/__init__.py | 1 + tests/api_resources/emails/test_render.py | 110 ++ tests/api_resources/emails/test_send.py | 116 ++ .../emails/test_send_template.py | 120 ++ tests/api_resources/export/__init__.py | 1 + tests/api_resources/export/test_html.py | 86 ++ tests/api_resources/export/test_image.py | 86 ++ tests/api_resources/export/test_pdf.py | 86 ++ tests/api_resources/export/test_zip.py | 86 ++ tests/api_resources/pages/__init__.py | 1 + .../{test_pages.py => pages/test_render.py} | 62 +- tests/api_resources/project/__init__.py | 1 + tests/api_resources/project/test_current.py | 86 ++ tests/api_resources/project/test_domains.py | 403 ++++++ tests/api_resources/project/test_templates.py | 447 ++++++ .../api_resources/project/test_workspaces.py | 150 ++ tests/api_resources/test_documents.py | 224 +-- tests/api_resources/test_emails.py | 281 +--- tests/api_resources/test_export.py | 277 ---- tests/api_resources/test_project.py | 1017 ------------- tests/test_client.py | 48 +- 118 files changed, 8121 insertions(+), 4978 deletions(-) create mode 100644 src/unlayer/resources/convert/__init__.py create mode 100644 src/unlayer/resources/convert/convert.py create mode 100644 src/unlayer/resources/convert/full_to_simple.py create mode 100644 src/unlayer/resources/convert/simple_to_full.py delete mode 100644 src/unlayer/resources/documents.py create mode 100644 src/unlayer/resources/documents/__init__.py create mode 100644 src/unlayer/resources/documents/documents.py create mode 100644 src/unlayer/resources/documents/generate.py create mode 100644 src/unlayer/resources/documents/generate_template.py delete mode 100644 src/unlayer/resources/emails.py create mode 100644 src/unlayer/resources/emails/__init__.py create mode 100644 src/unlayer/resources/emails/emails.py create mode 100644 src/unlayer/resources/emails/render.py create mode 100644 src/unlayer/resources/emails/send.py create mode 100644 src/unlayer/resources/emails/send_template.py delete mode 100644 src/unlayer/resources/export.py create mode 100644 src/unlayer/resources/export/__init__.py create mode 100644 src/unlayer/resources/export/export.py create mode 100644 src/unlayer/resources/export/html.py create mode 100644 src/unlayer/resources/export/image.py create mode 100644 src/unlayer/resources/export/pdf.py create mode 100644 src/unlayer/resources/export/zip.py create mode 100644 src/unlayer/resources/pages/__init__.py create mode 100644 src/unlayer/resources/pages/pages.py rename src/unlayer/resources/{pages.py => pages/render.py} (65%) delete mode 100644 src/unlayer/resources/project.py create mode 100644 src/unlayer/resources/project/__init__.py create mode 100644 src/unlayer/resources/project/current.py create mode 100644 src/unlayer/resources/project/domains.py create mode 100644 src/unlayer/resources/project/project.py create mode 100644 src/unlayer/resources/project/templates.py create mode 100644 src/unlayer/resources/project/workspaces.py create mode 100644 src/unlayer/types/convert/__init__.py create mode 100644 src/unlayer/types/convert/full_to_simple_create_params.py create mode 100644 src/unlayer/types/convert/full_to_simple_create_response.py create mode 100644 src/unlayer/types/convert/simple_to_full_create_params.py create mode 100644 src/unlayer/types/convert/simple_to_full_create_response.py delete mode 100644 src/unlayer/types/document_documents_retrieve_params.py rename src/unlayer/types/{export_zip_list_params.py => document_retrieve_params.py} (77%) rename src/unlayer/types/{document_documents_retrieve_response.py => document_retrieve_response.py} (91%) create mode 100644 src/unlayer/types/documents/__init__.py rename src/unlayer/types/{document_generate_create_params.py => documents/generate_create_params.py} (83%) rename src/unlayer/types/{document_generate_create_response.py => documents/generate_create_response.py} (82%) rename src/unlayer/types/{document_generate_template_template_params.py => documents/generate_template_create_params.py} (80%) rename src/unlayer/types/{document_generate_template_template_response.py => documents/generate_template_create_response.py} (80%) create mode 100644 src/unlayer/types/emails/__init__.py rename src/unlayer/types/{page_render_create_params.py => emails/render_create_params.py} (80%) rename src/unlayer/types/{page_render_create_response.py => emails/render_create_response.py} (66%) rename src/unlayer/types/{email_send_create_params.py => emails/send_create_params.py} (84%) rename src/unlayer/types/{email_send_create_response.py => emails/send_create_response.py} (78%) rename src/unlayer/types/{email_send_template_template_params.py => emails/send_template_create_params.py} (83%) rename src/unlayer/types/{email_send_template_template_response.py => emails/send_template_create_response.py} (75%) create mode 100644 src/unlayer/types/export/__init__.py rename src/unlayer/types/{export_pdf_list_params.py => export/html_retrieve_params.py} (70%) rename src/unlayer/types/{export_zip_list_response.py => export/html_retrieve_response.py} (64%) rename src/unlayer/types/{export_html_list_params.py => export/image_retrieve_params.py} (70%) rename src/unlayer/types/{export_html_list_response.py => export/image_retrieve_response.py} (64%) rename src/unlayer/types/{export_image_list_params.py => export/pdf_retrieve_params.py} (70%) rename src/unlayer/types/{export_pdf_list_response.py => export/pdf_retrieve_response.py} (64%) create mode 100644 src/unlayer/types/export/zip_retrieve_params.py rename src/unlayer/types/{export_image_list_response.py => export/zip_retrieve_response.py} (63%) create mode 100644 src/unlayer/types/pages/__init__.py rename src/unlayer/types/{email_render_create_params.py => pages/render_create_params.py} (80%) rename src/unlayer/types/{email_render_create_response.py => pages/render_create_response.py} (65%) create mode 100644 src/unlayer/types/project/__init__.py create mode 100644 src/unlayer/types/project/current_retrieve_params.py rename src/unlayer/types/{project_current_list_response.py => project/current_retrieve_response.py} (79%) rename src/unlayer/types/{project_domains_create_params.py => project/domain_create_params.py} (72%) rename src/unlayer/types/{project_domains_update_response.py => project/domain_create_response.py} (78%) create mode 100644 src/unlayer/types/project/domain_list_params.py rename src/unlayer/types/{project_domains_list_response.py => project/domain_list_response.py} (81%) rename src/unlayer/types/{project_domains_create_response.py => project/domain_retrieve_response.py} (78%) rename src/unlayer/types/{project_domains_update_params.py => project/domain_update_params.py} (68%) rename src/unlayer/types/{project_domains_retrieve_response.py => project/domain_update_response.py} (77%) rename src/unlayer/types/{project_templates_create_params.py => project/template_create_params.py} (78%) rename src/unlayer/types/{project_templates_create_response.py => project/template_create_response.py} (83%) rename src/unlayer/types/{project_templates_list_params.py => project/template_list_params.py} (83%) rename src/unlayer/types/{project_templates_list_response.py => project/template_list_response.py} (62%) rename src/unlayer/types/{project_templates_update_response.py => project/template_retrieve_response.py} (80%) rename src/unlayer/types/{project_templates_update_params.py => project/template_update_params.py} (75%) rename src/unlayer/types/{project_templates_retrieve_response.py => project/template_update_response.py} (79%) rename src/unlayer/types/{project_workspaces_list_response.py => project/workspace_list_response.py} (65%) rename src/unlayer/types/{project_workspaces_retrieve_response.py => project/workspace_retrieve_response.py} (73%) delete mode 100644 src/unlayer/types/project_current_list_params.py delete mode 100644 src/unlayer/types/project_domains_list_params.py create mode 100644 tests/api_resources/convert/__init__.py create mode 100644 tests/api_resources/convert/test_full_to_simple.py create mode 100644 tests/api_resources/convert/test_simple_to_full.py create mode 100644 tests/api_resources/documents/__init__.py create mode 100644 tests/api_resources/documents/test_generate.py create mode 100644 tests/api_resources/documents/test_generate_template.py create mode 100644 tests/api_resources/emails/__init__.py create mode 100644 tests/api_resources/emails/test_render.py create mode 100644 tests/api_resources/emails/test_send.py create mode 100644 tests/api_resources/emails/test_send_template.py create mode 100644 tests/api_resources/export/__init__.py create mode 100644 tests/api_resources/export/test_html.py create mode 100644 tests/api_resources/export/test_image.py create mode 100644 tests/api_resources/export/test_pdf.py create mode 100644 tests/api_resources/export/test_zip.py create mode 100644 tests/api_resources/pages/__init__.py rename tests/api_resources/{test_pages.py => pages/test_render.py} (51%) create mode 100644 tests/api_resources/project/__init__.py create mode 100644 tests/api_resources/project/test_current.py create mode 100644 tests/api_resources/project/test_domains.py create mode 100644 tests/api_resources/project/test_templates.py create mode 100644 tests/api_resources/project/test_workspaces.py delete mode 100644 tests/api_resources/test_export.py delete mode 100644 tests/api_resources/test_project.py diff --git a/.stats.yml b/.stats.yml index 2bb500f..c33024a 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ -configured_endpoints: 25 +configured_endpoints: 27 openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/unlayer%2Funlayer-077d753eb6d4f805b2f84bf887289213f91c1da5f5a3a4e1e36e1da3689efb46.yml openapi_spec_hash: b210022cf72d9a38fe1baee599054518 -config_hash: ae90f2806448a012e25917d01699b3a5 +config_hash: 15a2f2b4c1b498b9b314d587d6a331d0 diff --git a/README.md b/README.md index afacda3..2849fff 100644 --- a/README.md +++ b/README.md @@ -37,10 +37,10 @@ client = Unlayer( environment="stage", ) -response = client.project.current_list( - project_id="your-project-id", +full_to_simple = client.convert.full_to_simple.create( + design={"body": {}}, ) -print(response.data) +print(full_to_simple.data) ``` While you can provide a `access_token` keyword argument, @@ -65,10 +65,10 @@ client = AsyncUnlayer( async def main() -> None: - response = await client.project.current_list( - project_id="your-project-id", + full_to_simple = await client.convert.full_to_simple.create( + design={"body": {}}, ) - print(response.data) + print(full_to_simple.data) asyncio.run(main()) @@ -103,10 +103,10 @@ async def main() -> None: ), # This is the default and can be omitted http_client=DefaultAioHttpClient(), ) as client: - response = await client.project.current_list( - project_id="your-project-id", + full_to_simple = await client.convert.full_to_simple.create( + design={"body": {}}, ) - print(response.data) + print(full_to_simple.data) asyncio.run(main()) @@ -121,79 +121,19 @@ Nested request parameters are [TypedDicts](https://docs.python.org/3/library/typ Typed requests and responses provide autocomplete and documentation within your editor. If you would like to see type errors in VS Code to help catch bugs earlier, set `python.analysis.typeCheckingMode` to `basic`. -## Pagination +## Nested params -List methods in the Unlayer API are paginated. - -This library provides auto-paginating iterators with each list response, so you do not have to request successive pages manually: +Nested parameters are dictionaries, typed using `TypedDict`, for example: ```python from unlayer import Unlayer client = Unlayer() -all_projects = [] -# Automatically fetches more pages as needed. -for project in client.project.templates_list( - project_id="your-project-id", - limit=10, -): - # Do something with project here - all_projects.append(project) -print(all_projects) -``` - -Or, asynchronously: - -```python -import asyncio -from unlayer import AsyncUnlayer - -client = AsyncUnlayer() - - -async def main() -> None: - all_projects = [] - # Iterate through items across all pages, issuing requests as needed. - async for project in client.project.templates_list( - project_id="your-project-id", - limit=10, - ): - all_projects.append(project) - print(all_projects) - - -asyncio.run(main()) -``` - -Alternatively, you can use the `.has_next_page()`, `.next_page_info()`, or `.get_next_page()` methods for more granular control working with pages: - -```python -first_page = await client.project.templates_list( - project_id="your-project-id", - limit=10, +full_to_simple = client.convert.full_to_simple.create( + design={"body": {}}, ) -if first_page.has_next_page(): - print(f"will fetch next page using these details: {first_page.next_page_info()}") - next_page = await first_page.get_next_page() - print(f"number of items we just fetched: {len(next_page.data)}") - -# Remove `await` for non-async usage. -``` - -Or just work directly with the returned data: - -```python -first_page = await client.project.templates_list( - project_id="your-project-id", - limit=10, -) - -print(f"next page cursor: {first_page.next_cursor}") # => "next page cursor: ..." -for project in first_page.data: - print(project.id) - -# Remove `await` for non-async usage. +print(full_to_simple.design) ``` ## Handling errors @@ -212,8 +152,8 @@ from unlayer import Unlayer client = Unlayer() try: - client.project.current_list( - project_id="your-project-id", + client.convert.full_to_simple.create( + design={"body": {}}, ) except unlayer.APIConnectionError as e: print("The server could not be reached") @@ -257,8 +197,8 @@ client = Unlayer( ) # Or, configure per-request: -client.with_options(max_retries=5).project.current_list( - project_id="your-project-id", +client.with_options(max_retries=5).convert.full_to_simple.create( + design={"body": {}}, ) ``` @@ -282,8 +222,8 @@ client = Unlayer( ) # Override per-request: -client.with_options(timeout=5.0).project.current_list( - project_id="your-project-id", +client.with_options(timeout=5.0).convert.full_to_simple.create( + design={"body": {}}, ) ``` @@ -325,13 +265,15 @@ The "raw" Response object can be accessed by prefixing `.with_raw_response.` to from unlayer import Unlayer client = Unlayer() -response = client.project.with_raw_response.current_list( - project_id="your-project-id", +response = client.convert.full_to_simple.with_raw_response.create( + design={ + "body": {} + }, ) print(response.headers.get('X-My-Header')) -project = response.parse() # get the object that `project.current_list()` would have returned -print(project.data) +full_to_simple = response.parse() # get the object that `convert.full_to_simple.create()` would have returned +print(full_to_simple.data) ``` These methods return an [`APIResponse`](https://github.com/stainless-sdks/unlayer-python/tree/main/src/unlayer/_response.py) object. @@ -345,8 +287,8 @@ The above interface eagerly reads the full response body when you make the reque To stream the response body, use `.with_streaming_response` instead, which requires a context manager and only reads the response body once you call `.read()`, `.text()`, `.json()`, `.iter_bytes()`, `.iter_text()`, `.iter_lines()` or `.parse()`. In the async client, these are async methods. ```python -with client.project.with_streaming_response.current_list( - project_id="your-project-id", +with client.convert.full_to_simple.with_streaming_response.create( + design={"body": {}}, ) as response: print(response.headers.get("X-My-Header")) diff --git a/api.md b/api.md index a157e4b..050a5df 100644 --- a/api.md +++ b/api.md @@ -1,105 +1,242 @@ +# Convert + +## FullToSimple + +Types: + +```python +from unlayer.types.convert import FullToSimpleCreateResponse +``` + +Methods: + +- client.convert.full_to_simple.create(\*\*params) -> FullToSimpleCreateResponse + +## SimpleToFull + +Types: + +```python +from unlayer.types.convert import SimpleToFullCreateResponse +``` + +Methods: + +- client.convert.simple_to_full.create(\*\*params) -> SimpleToFullCreateResponse + # Documents Types: ```python -from unlayer.types import ( - DocumentDocumentsRetrieveResponse, - DocumentGenerateCreateResponse, - DocumentGenerateTemplateTemplateResponse, -) +from unlayer.types import DocumentRetrieveResponse +``` + +Methods: + +- client.documents.retrieve(id, \*\*params) -> DocumentRetrieveResponse + +## Generate + +Types: + +```python +from unlayer.types.documents import GenerateCreateResponse ``` Methods: -- client.documents.documents_retrieve(id, \*\*params) -> DocumentDocumentsRetrieveResponse -- client.documents.generate_create(\*\*params) -> DocumentGenerateCreateResponse -- client.documents.generate_template_template(\*\*params) -> DocumentGenerateTemplateTemplateResponse +- client.documents.generate.create(\*\*params) -> GenerateCreateResponse + +## GenerateTemplate + +Types: + +```python +from unlayer.types.documents import GenerateTemplateCreateResponse +``` + +Methods: + +- client.documents.generate_template.create(\*\*params) -> GenerateTemplateCreateResponse # Emails Types: ```python -from unlayer.types import ( - EmailRetrieveResponse, - EmailRenderCreateResponse, - EmailSendCreateResponse, - EmailSendTemplateTemplateResponse, -) +from unlayer.types import EmailRetrieveResponse +``` + +Methods: + +- client.emails.retrieve(id, \*\*params) -> EmailRetrieveResponse + +## Render + +Types: + +```python +from unlayer.types.emails import RenderCreateResponse ``` Methods: -- client.emails.retrieve(id, \*\*params) -> EmailRetrieveResponse -- client.emails.render_create(\*\*params) -> EmailRenderCreateResponse -- client.emails.send_create(\*\*params) -> EmailSendCreateResponse -- client.emails.send_template_template(\*\*params) -> EmailSendTemplateTemplateResponse +- client.emails.render.create(\*\*params) -> RenderCreateResponse + +## Send + +Types: + +```python +from unlayer.types.emails import SendCreateResponse +``` + +Methods: + +- client.emails.send.create(\*\*params) -> SendCreateResponse + +## SendTemplate + +Types: + +```python +from unlayer.types.emails import SendTemplateCreateResponse +``` + +Methods: + +- client.emails.send_template.create(\*\*params) -> SendTemplateCreateResponse # Export +## HTML + Types: ```python -from unlayer.types import ( - ExportHTMLListResponse, - ExportImageListResponse, - ExportPdfListResponse, - ExportZipListResponse, -) +from unlayer.types.export import HTMLRetrieveResponse +``` + +Methods: + +- client.export.html.retrieve(\*\*params) -> HTMLRetrieveResponse + +## Image + +Types: + +```python +from unlayer.types.export import ImageRetrieveResponse +``` + +Methods: + +- client.export.image.retrieve(\*\*params) -> ImageRetrieveResponse + +## Pdf + +Types: + +```python +from unlayer.types.export import PdfRetrieveResponse +``` + +Methods: + +- client.export.pdf.retrieve(\*\*params) -> PdfRetrieveResponse + +## Zip + +Types: + +```python +from unlayer.types.export import ZipRetrieveResponse ``` Methods: -- client.export.html_list(\*\*params) -> ExportHTMLListResponse -- client.export.image_list(\*\*params) -> ExportImageListResponse -- client.export.pdf_list(\*\*params) -> ExportPdfListResponse -- client.export.zip_list(\*\*params) -> ExportZipListResponse +- client.export.zip.retrieve(\*\*params) -> ZipRetrieveResponse # Pages +## Render + Types: ```python -from unlayer.types import PageRenderCreateResponse +from unlayer.types.pages import RenderCreateResponse ``` Methods: -- client.pages.render_create(\*\*params) -> PageRenderCreateResponse +- client.pages.render.create(\*\*params) -> RenderCreateResponse # Project +## Current + +Types: + +```python +from unlayer.types.project import CurrentRetrieveResponse +``` + +Methods: + +- client.project.current.retrieve(\*\*params) -> CurrentRetrieveResponse + +## Domains + Types: ```python -from unlayer.types import ( - ProjectCurrentListResponse, - ProjectDomainsCreateResponse, - ProjectDomainsListResponse, - ProjectDomainsRetrieveResponse, - ProjectDomainsUpdateResponse, - ProjectTemplatesCreateResponse, - ProjectTemplatesListResponse, - ProjectTemplatesRetrieveResponse, - ProjectTemplatesUpdateResponse, - ProjectWorkspacesListResponse, - ProjectWorkspacesRetrieveResponse, +from unlayer.types.project import ( + DomainCreateResponse, + DomainRetrieveResponse, + DomainUpdateResponse, + DomainListResponse, ) ``` Methods: -- client.project.current_list(\*\*params) -> ProjectCurrentListResponse -- client.project.domains_create(\*\*params) -> ProjectDomainsCreateResponse -- client.project.domains_delete(id) -> None -- client.project.domains_list(\*\*params) -> ProjectDomainsListResponse -- client.project.domains_retrieve(id) -> ProjectDomainsRetrieveResponse -- client.project.domains_update(id, \*\*params) -> ProjectDomainsUpdateResponse -- client.project.templates_create(\*\*params) -> ProjectTemplatesCreateResponse -- client.project.templates_delete(id) -> None -- client.project.templates_list(\*\*params) -> SyncCursorPage[ProjectTemplatesListResponse] -- client.project.templates_retrieve(id) -> ProjectTemplatesRetrieveResponse -- client.project.templates_update(id, \*\*params) -> ProjectTemplatesUpdateResponse -- client.project.workspaces_list() -> ProjectWorkspacesListResponse -- client.project.workspaces_retrieve(workspace_id) -> ProjectWorkspacesRetrieveResponse +- client.project.domains.create(\*\*params) -> DomainCreateResponse +- client.project.domains.retrieve(id) -> DomainRetrieveResponse +- client.project.domains.update(id, \*\*params) -> DomainUpdateResponse +- client.project.domains.list(\*\*params) -> DomainListResponse +- client.project.domains.delete(id) -> None + +## Templates + +Types: + +```python +from unlayer.types.project import ( + TemplateCreateResponse, + TemplateRetrieveResponse, + TemplateUpdateResponse, + TemplateListResponse, +) +``` + +Methods: + +- client.project.templates.create(\*\*params) -> TemplateCreateResponse +- client.project.templates.retrieve(id) -> TemplateRetrieveResponse +- client.project.templates.update(id, \*\*params) -> TemplateUpdateResponse +- client.project.templates.list(\*\*params) -> TemplateListResponse +- client.project.templates.delete(id) -> None + +## Workspaces + +Types: + +```python +from unlayer.types.project import WorkspaceRetrieveResponse, WorkspaceListResponse +``` + +Methods: + +- client.project.workspaces.retrieve(workspace_id) -> WorkspaceRetrieveResponse +- client.project.workspaces.list() -> WorkspaceListResponse diff --git a/src/unlayer/_client.py b/src/unlayer/_client.py index cdcb8b9..afabf32 100644 --- a/src/unlayer/_client.py +++ b/src/unlayer/_client.py @@ -31,12 +31,13 @@ ) if TYPE_CHECKING: - from .resources import pages, emails, export, project, documents - from .resources.pages import PagesResource, AsyncPagesResource - from .resources.emails import EmailsResource, AsyncEmailsResource - from .resources.export import ExportResource, AsyncExportResource - from .resources.project import ProjectResource, AsyncProjectResource - from .resources.documents import DocumentsResource, AsyncDocumentsResource + from .resources import pages, emails, export, convert, project, documents + from .resources.pages.pages import PagesResource, AsyncPagesResource + from .resources.emails.emails import EmailsResource, AsyncEmailsResource + from .resources.export.export import ExportResource, AsyncExportResource + from .resources.convert.convert import ConvertResource, AsyncConvertResource + from .resources.project.project import ProjectResource, AsyncProjectResource + from .resources.documents.documents import DocumentsResource, AsyncDocumentsResource __all__ = [ "ENVIRONMENTS", @@ -137,6 +138,12 @@ def __init__( _strict_response_validation=_strict_response_validation, ) + @cached_property + def convert(self) -> ConvertResource: + from .resources.convert import ConvertResource + + return ConvertResource(self) + @cached_property def documents(self) -> DocumentsResource: from .resources.documents import DocumentsResource @@ -361,6 +368,12 @@ def __init__( _strict_response_validation=_strict_response_validation, ) + @cached_property + def convert(self) -> AsyncConvertResource: + from .resources.convert import AsyncConvertResource + + return AsyncConvertResource(self) + @cached_property def documents(self) -> AsyncDocumentsResource: from .resources.documents import AsyncDocumentsResource @@ -512,6 +525,12 @@ class UnlayerWithRawResponse: def __init__(self, client: Unlayer) -> None: self._client = client + @cached_property + def convert(self) -> convert.ConvertResourceWithRawResponse: + from .resources.convert import ConvertResourceWithRawResponse + + return ConvertResourceWithRawResponse(self._client.convert) + @cached_property def documents(self) -> documents.DocumentsResourceWithRawResponse: from .resources.documents import DocumentsResourceWithRawResponse @@ -549,6 +568,12 @@ class AsyncUnlayerWithRawResponse: def __init__(self, client: AsyncUnlayer) -> None: self._client = client + @cached_property + def convert(self) -> convert.AsyncConvertResourceWithRawResponse: + from .resources.convert import AsyncConvertResourceWithRawResponse + + return AsyncConvertResourceWithRawResponse(self._client.convert) + @cached_property def documents(self) -> documents.AsyncDocumentsResourceWithRawResponse: from .resources.documents import AsyncDocumentsResourceWithRawResponse @@ -586,6 +611,12 @@ class UnlayerWithStreamedResponse: def __init__(self, client: Unlayer) -> None: self._client = client + @cached_property + def convert(self) -> convert.ConvertResourceWithStreamingResponse: + from .resources.convert import ConvertResourceWithStreamingResponse + + return ConvertResourceWithStreamingResponse(self._client.convert) + @cached_property def documents(self) -> documents.DocumentsResourceWithStreamingResponse: from .resources.documents import DocumentsResourceWithStreamingResponse @@ -623,6 +654,12 @@ class AsyncUnlayerWithStreamedResponse: def __init__(self, client: AsyncUnlayer) -> None: self._client = client + @cached_property + def convert(self) -> convert.AsyncConvertResourceWithStreamingResponse: + from .resources.convert import AsyncConvertResourceWithStreamingResponse + + return AsyncConvertResourceWithStreamingResponse(self._client.convert) + @cached_property def documents(self) -> documents.AsyncDocumentsResourceWithStreamingResponse: from .resources.documents import AsyncDocumentsResourceWithStreamingResponse diff --git a/src/unlayer/resources/__init__.py b/src/unlayer/resources/__init__.py index bf494f8..4c7d818 100644 --- a/src/unlayer/resources/__init__.py +++ b/src/unlayer/resources/__init__.py @@ -24,6 +24,14 @@ ExportResourceWithStreamingResponse, AsyncExportResourceWithStreamingResponse, ) +from .convert import ( + ConvertResource, + AsyncConvertResource, + ConvertResourceWithRawResponse, + AsyncConvertResourceWithRawResponse, + ConvertResourceWithStreamingResponse, + AsyncConvertResourceWithStreamingResponse, +) from .project import ( ProjectResource, AsyncProjectResource, @@ -42,6 +50,12 @@ ) __all__ = [ + "ConvertResource", + "AsyncConvertResource", + "ConvertResourceWithRawResponse", + "AsyncConvertResourceWithRawResponse", + "ConvertResourceWithStreamingResponse", + "AsyncConvertResourceWithStreamingResponse", "DocumentsResource", "AsyncDocumentsResource", "DocumentsResourceWithRawResponse", diff --git a/src/unlayer/resources/convert/__init__.py b/src/unlayer/resources/convert/__init__.py new file mode 100644 index 0000000..a2626b5 --- /dev/null +++ b/src/unlayer/resources/convert/__init__.py @@ -0,0 +1,47 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from .convert import ( + ConvertResource, + AsyncConvertResource, + ConvertResourceWithRawResponse, + AsyncConvertResourceWithRawResponse, + ConvertResourceWithStreamingResponse, + AsyncConvertResourceWithStreamingResponse, +) +from .full_to_simple import ( + FullToSimpleResource, + AsyncFullToSimpleResource, + FullToSimpleResourceWithRawResponse, + AsyncFullToSimpleResourceWithRawResponse, + FullToSimpleResourceWithStreamingResponse, + AsyncFullToSimpleResourceWithStreamingResponse, +) +from .simple_to_full import ( + SimpleToFullResource, + AsyncSimpleToFullResource, + SimpleToFullResourceWithRawResponse, + AsyncSimpleToFullResourceWithRawResponse, + SimpleToFullResourceWithStreamingResponse, + AsyncSimpleToFullResourceWithStreamingResponse, +) + +__all__ = [ + "FullToSimpleResource", + "AsyncFullToSimpleResource", + "FullToSimpleResourceWithRawResponse", + "AsyncFullToSimpleResourceWithRawResponse", + "FullToSimpleResourceWithStreamingResponse", + "AsyncFullToSimpleResourceWithStreamingResponse", + "SimpleToFullResource", + "AsyncSimpleToFullResource", + "SimpleToFullResourceWithRawResponse", + "AsyncSimpleToFullResourceWithRawResponse", + "SimpleToFullResourceWithStreamingResponse", + "AsyncSimpleToFullResourceWithStreamingResponse", + "ConvertResource", + "AsyncConvertResource", + "ConvertResourceWithRawResponse", + "AsyncConvertResourceWithRawResponse", + "ConvertResourceWithStreamingResponse", + "AsyncConvertResourceWithStreamingResponse", +] diff --git a/src/unlayer/resources/convert/convert.py b/src/unlayer/resources/convert/convert.py new file mode 100644 index 0000000..c303200 --- /dev/null +++ b/src/unlayer/resources/convert/convert.py @@ -0,0 +1,134 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from ..._compat import cached_property +from ..._resource import SyncAPIResource, AsyncAPIResource +from .full_to_simple import ( + FullToSimpleResource, + AsyncFullToSimpleResource, + FullToSimpleResourceWithRawResponse, + AsyncFullToSimpleResourceWithRawResponse, + FullToSimpleResourceWithStreamingResponse, + AsyncFullToSimpleResourceWithStreamingResponse, +) +from .simple_to_full import ( + SimpleToFullResource, + AsyncSimpleToFullResource, + SimpleToFullResourceWithRawResponse, + AsyncSimpleToFullResourceWithRawResponse, + SimpleToFullResourceWithStreamingResponse, + AsyncSimpleToFullResourceWithStreamingResponse, +) + +__all__ = ["ConvertResource", "AsyncConvertResource"] + + +class ConvertResource(SyncAPIResource): + @cached_property + def full_to_simple(self) -> FullToSimpleResource: + return FullToSimpleResource(self._client) + + @cached_property + def simple_to_full(self) -> SimpleToFullResource: + return SimpleToFullResource(self._client) + + @cached_property + def with_raw_response(self) -> ConvertResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/stainless-sdks/unlayer-python#accessing-raw-response-data-eg-headers + """ + return ConvertResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> ConvertResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/stainless-sdks/unlayer-python#with_streaming_response + """ + return ConvertResourceWithStreamingResponse(self) + + +class AsyncConvertResource(AsyncAPIResource): + @cached_property + def full_to_simple(self) -> AsyncFullToSimpleResource: + return AsyncFullToSimpleResource(self._client) + + @cached_property + def simple_to_full(self) -> AsyncSimpleToFullResource: + return AsyncSimpleToFullResource(self._client) + + @cached_property + def with_raw_response(self) -> AsyncConvertResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/stainless-sdks/unlayer-python#accessing-raw-response-data-eg-headers + """ + return AsyncConvertResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncConvertResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/stainless-sdks/unlayer-python#with_streaming_response + """ + return AsyncConvertResourceWithStreamingResponse(self) + + +class ConvertResourceWithRawResponse: + def __init__(self, convert: ConvertResource) -> None: + self._convert = convert + + @cached_property + def full_to_simple(self) -> FullToSimpleResourceWithRawResponse: + return FullToSimpleResourceWithRawResponse(self._convert.full_to_simple) + + @cached_property + def simple_to_full(self) -> SimpleToFullResourceWithRawResponse: + return SimpleToFullResourceWithRawResponse(self._convert.simple_to_full) + + +class AsyncConvertResourceWithRawResponse: + def __init__(self, convert: AsyncConvertResource) -> None: + self._convert = convert + + @cached_property + def full_to_simple(self) -> AsyncFullToSimpleResourceWithRawResponse: + return AsyncFullToSimpleResourceWithRawResponse(self._convert.full_to_simple) + + @cached_property + def simple_to_full(self) -> AsyncSimpleToFullResourceWithRawResponse: + return AsyncSimpleToFullResourceWithRawResponse(self._convert.simple_to_full) + + +class ConvertResourceWithStreamingResponse: + def __init__(self, convert: ConvertResource) -> None: + self._convert = convert + + @cached_property + def full_to_simple(self) -> FullToSimpleResourceWithStreamingResponse: + return FullToSimpleResourceWithStreamingResponse(self._convert.full_to_simple) + + @cached_property + def simple_to_full(self) -> SimpleToFullResourceWithStreamingResponse: + return SimpleToFullResourceWithStreamingResponse(self._convert.simple_to_full) + + +class AsyncConvertResourceWithStreamingResponse: + def __init__(self, convert: AsyncConvertResource) -> None: + self._convert = convert + + @cached_property + def full_to_simple(self) -> AsyncFullToSimpleResourceWithStreamingResponse: + return AsyncFullToSimpleResourceWithStreamingResponse(self._convert.full_to_simple) + + @cached_property + def simple_to_full(self) -> AsyncSimpleToFullResourceWithStreamingResponse: + return AsyncSimpleToFullResourceWithStreamingResponse(self._convert.simple_to_full) diff --git a/src/unlayer/resources/convert/full_to_simple.py b/src/unlayer/resources/convert/full_to_simple.py new file mode 100644 index 0000000..9f5d104 --- /dev/null +++ b/src/unlayer/resources/convert/full_to_simple.py @@ -0,0 +1,183 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal + +import httpx + +from ..._types import Body, Omit, Query, Headers, NotGiven, omit, not_given +from ..._utils import maybe_transform, async_maybe_transform +from ..._compat import cached_property +from ..._resource import SyncAPIResource, AsyncAPIResource +from ..._response import ( + to_raw_response_wrapper, + to_streamed_response_wrapper, + async_to_raw_response_wrapper, + async_to_streamed_response_wrapper, +) +from ..._base_client import make_request_options +from ...types.convert import full_to_simple_create_params +from ...types.convert.full_to_simple_create_response import FullToSimpleCreateResponse + +__all__ = ["FullToSimpleResource", "AsyncFullToSimpleResource"] + + +class FullToSimpleResource(SyncAPIResource): + @cached_property + def with_raw_response(self) -> FullToSimpleResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/stainless-sdks/unlayer-python#accessing-raw-response-data-eg-headers + """ + return FullToSimpleResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> FullToSimpleResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/stainless-sdks/unlayer-python#with_streaming_response + """ + return FullToSimpleResourceWithStreamingResponse(self) + + def create( + self, + *, + design: full_to_simple_create_params.Design, + display_mode: Literal["email", "web", "popup", "document"] | Omit = omit, + include_default_values: bool | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> FullToSimpleCreateResponse: + """ + Convert design json from Full to Simple schema. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._post( + "/convert/full-to-simple", + body=maybe_transform( + { + "design": design, + "display_mode": display_mode, + "include_default_values": include_default_values, + }, + full_to_simple_create_params.FullToSimpleCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=FullToSimpleCreateResponse, + ) + + +class AsyncFullToSimpleResource(AsyncAPIResource): + @cached_property + def with_raw_response(self) -> AsyncFullToSimpleResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/stainless-sdks/unlayer-python#accessing-raw-response-data-eg-headers + """ + return AsyncFullToSimpleResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncFullToSimpleResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/stainless-sdks/unlayer-python#with_streaming_response + """ + return AsyncFullToSimpleResourceWithStreamingResponse(self) + + async def create( + self, + *, + design: full_to_simple_create_params.Design, + display_mode: Literal["email", "web", "popup", "document"] | Omit = omit, + include_default_values: bool | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> FullToSimpleCreateResponse: + """ + Convert design json from Full to Simple schema. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return await self._post( + "/convert/full-to-simple", + body=await async_maybe_transform( + { + "design": design, + "display_mode": display_mode, + "include_default_values": include_default_values, + }, + full_to_simple_create_params.FullToSimpleCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=FullToSimpleCreateResponse, + ) + + +class FullToSimpleResourceWithRawResponse: + def __init__(self, full_to_simple: FullToSimpleResource) -> None: + self._full_to_simple = full_to_simple + + self.create = to_raw_response_wrapper( + full_to_simple.create, + ) + + +class AsyncFullToSimpleResourceWithRawResponse: + def __init__(self, full_to_simple: AsyncFullToSimpleResource) -> None: + self._full_to_simple = full_to_simple + + self.create = async_to_raw_response_wrapper( + full_to_simple.create, + ) + + +class FullToSimpleResourceWithStreamingResponse: + def __init__(self, full_to_simple: FullToSimpleResource) -> None: + self._full_to_simple = full_to_simple + + self.create = to_streamed_response_wrapper( + full_to_simple.create, + ) + + +class AsyncFullToSimpleResourceWithStreamingResponse: + def __init__(self, full_to_simple: AsyncFullToSimpleResource) -> None: + self._full_to_simple = full_to_simple + + self.create = async_to_streamed_response_wrapper( + full_to_simple.create, + ) diff --git a/src/unlayer/resources/convert/simple_to_full.py b/src/unlayer/resources/convert/simple_to_full.py new file mode 100644 index 0000000..dca40ad --- /dev/null +++ b/src/unlayer/resources/convert/simple_to_full.py @@ -0,0 +1,183 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal + +import httpx + +from ..._types import Body, Omit, Query, Headers, NotGiven, omit, not_given +from ..._utils import maybe_transform, async_maybe_transform +from ..._compat import cached_property +from ..._resource import SyncAPIResource, AsyncAPIResource +from ..._response import ( + to_raw_response_wrapper, + to_streamed_response_wrapper, + async_to_raw_response_wrapper, + async_to_streamed_response_wrapper, +) +from ..._base_client import make_request_options +from ...types.convert import simple_to_full_create_params +from ...types.convert.simple_to_full_create_response import SimpleToFullCreateResponse + +__all__ = ["SimpleToFullResource", "AsyncSimpleToFullResource"] + + +class SimpleToFullResource(SyncAPIResource): + @cached_property + def with_raw_response(self) -> SimpleToFullResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/stainless-sdks/unlayer-python#accessing-raw-response-data-eg-headers + """ + return SimpleToFullResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> SimpleToFullResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/stainless-sdks/unlayer-python#with_streaming_response + """ + return SimpleToFullResourceWithStreamingResponse(self) + + def create( + self, + *, + design: simple_to_full_create_params.Design, + display_mode: Literal["email", "web", "popup", "document"] | Omit = omit, + include_default_values: bool | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> SimpleToFullCreateResponse: + """ + Convert design json from Simple to Full schema. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._post( + "/convert/simple-to-full", + body=maybe_transform( + { + "design": design, + "display_mode": display_mode, + "include_default_values": include_default_values, + }, + simple_to_full_create_params.SimpleToFullCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=SimpleToFullCreateResponse, + ) + + +class AsyncSimpleToFullResource(AsyncAPIResource): + @cached_property + def with_raw_response(self) -> AsyncSimpleToFullResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/stainless-sdks/unlayer-python#accessing-raw-response-data-eg-headers + """ + return AsyncSimpleToFullResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncSimpleToFullResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/stainless-sdks/unlayer-python#with_streaming_response + """ + return AsyncSimpleToFullResourceWithStreamingResponse(self) + + async def create( + self, + *, + design: simple_to_full_create_params.Design, + display_mode: Literal["email", "web", "popup", "document"] | Omit = omit, + include_default_values: bool | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> SimpleToFullCreateResponse: + """ + Convert design json from Simple to Full schema. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return await self._post( + "/convert/simple-to-full", + body=await async_maybe_transform( + { + "design": design, + "display_mode": display_mode, + "include_default_values": include_default_values, + }, + simple_to_full_create_params.SimpleToFullCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=SimpleToFullCreateResponse, + ) + + +class SimpleToFullResourceWithRawResponse: + def __init__(self, simple_to_full: SimpleToFullResource) -> None: + self._simple_to_full = simple_to_full + + self.create = to_raw_response_wrapper( + simple_to_full.create, + ) + + +class AsyncSimpleToFullResourceWithRawResponse: + def __init__(self, simple_to_full: AsyncSimpleToFullResource) -> None: + self._simple_to_full = simple_to_full + + self.create = async_to_raw_response_wrapper( + simple_to_full.create, + ) + + +class SimpleToFullResourceWithStreamingResponse: + def __init__(self, simple_to_full: SimpleToFullResource) -> None: + self._simple_to_full = simple_to_full + + self.create = to_streamed_response_wrapper( + simple_to_full.create, + ) + + +class AsyncSimpleToFullResourceWithStreamingResponse: + def __init__(self, simple_to_full: AsyncSimpleToFullResource) -> None: + self._simple_to_full = simple_to_full + + self.create = async_to_streamed_response_wrapper( + simple_to_full.create, + ) diff --git a/src/unlayer/resources/documents.py b/src/unlayer/resources/documents.py deleted file mode 100644 index f02c2fe..0000000 --- a/src/unlayer/resources/documents.py +++ /dev/null @@ -1,457 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing import Dict - -import httpx - -from ..types import ( - document_generate_create_params, - document_documents_retrieve_params, - document_generate_template_template_params, -) -from .._types import Body, Omit, Query, Headers, NotGiven, omit, not_given -from .._utils import maybe_transform, async_maybe_transform -from .._compat import cached_property -from .._resource import SyncAPIResource, AsyncAPIResource -from .._response import ( - to_raw_response_wrapper, - to_streamed_response_wrapper, - async_to_raw_response_wrapper, - async_to_streamed_response_wrapper, -) -from .._base_client import make_request_options -from ..types.document_generate_create_response import DocumentGenerateCreateResponse -from ..types.document_documents_retrieve_response import DocumentDocumentsRetrieveResponse -from ..types.document_generate_template_template_response import DocumentGenerateTemplateTemplateResponse - -__all__ = ["DocumentsResource", "AsyncDocumentsResource"] - - -class DocumentsResource(SyncAPIResource): - @cached_property - def with_raw_response(self) -> DocumentsResourceWithRawResponse: - """ - This property can be used as a prefix for any HTTP method call to return - the raw response object instead of the parsed content. - - For more information, see https://www.github.com/stainless-sdks/unlayer-python#accessing-raw-response-data-eg-headers - """ - return DocumentsResourceWithRawResponse(self) - - @cached_property - def with_streaming_response(self) -> DocumentsResourceWithStreamingResponse: - """ - An alternative to `.with_raw_response` that doesn't eagerly read the response body. - - For more information, see https://www.github.com/stainless-sdks/unlayer-python#with_streaming_response - """ - return DocumentsResourceWithStreamingResponse(self) - - def documents_retrieve( - self, - id: str, - *, - project_id: str, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> DocumentDocumentsRetrieveResponse: - """ - Retrieve details of a previously generated document. - - Args: - project_id: The project ID - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - if not id: - raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") - return self._get( - f"/documents/v1/documents/{id}", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query=maybe_transform( - {"project_id": project_id}, document_documents_retrieve_params.DocumentDocumentsRetrieveParams - ), - ), - cast_to=DocumentDocumentsRetrieveResponse, - ) - - def generate_create( - self, - *, - project_id: str, - design: Dict[str, object] | Omit = omit, - filename: str | Omit = omit, - html: str | Omit = omit, - merge_tags: Dict[str, str] | Omit = omit, - url: str | Omit = omit, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> DocumentGenerateCreateResponse: - """ - Generate PDF document from JSON design, HTML content, or URL. - - Args: - project_id: The project ID - - design: Proprietary design format JSON - - filename: Optional filename for the generated PDF - - html: HTML content to convert to PDF - - merge_tags: Optional merge tags for personalization - - url: URL to convert to PDF - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - return self._post( - "/documents/v1/generate", - body=maybe_transform( - { - "design": design, - "filename": filename, - "html": html, - "merge_tags": merge_tags, - "url": url, - }, - document_generate_create_params.DocumentGenerateCreateParams, - ), - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query=maybe_transform( - {"project_id": project_id}, document_generate_create_params.DocumentGenerateCreateParams - ), - ), - cast_to=DocumentGenerateCreateResponse, - ) - - def generate_template_template( - self, - *, - project_id: str, - template_id: str, - filename: str | Omit = omit, - merge_tags: Dict[str, str] | Omit = omit, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> DocumentGenerateTemplateTemplateResponse: - """ - Generate PDF document from an existing template with merge tags. - - Args: - project_id: The project ID - - template_id: ID of the template to use for generation - - filename: Optional filename for the generated PDF - - merge_tags: Optional merge tags for personalization - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - return self._post( - "/documents/v1/generate/template", - body=maybe_transform( - { - "template_id": template_id, - "filename": filename, - "merge_tags": merge_tags, - }, - document_generate_template_template_params.DocumentGenerateTemplateTemplateParams, - ), - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query=maybe_transform( - {"project_id": project_id}, - document_generate_template_template_params.DocumentGenerateTemplateTemplateParams, - ), - ), - cast_to=DocumentGenerateTemplateTemplateResponse, - ) - - -class AsyncDocumentsResource(AsyncAPIResource): - @cached_property - def with_raw_response(self) -> AsyncDocumentsResourceWithRawResponse: - """ - This property can be used as a prefix for any HTTP method call to return - the raw response object instead of the parsed content. - - For more information, see https://www.github.com/stainless-sdks/unlayer-python#accessing-raw-response-data-eg-headers - """ - return AsyncDocumentsResourceWithRawResponse(self) - - @cached_property - def with_streaming_response(self) -> AsyncDocumentsResourceWithStreamingResponse: - """ - An alternative to `.with_raw_response` that doesn't eagerly read the response body. - - For more information, see https://www.github.com/stainless-sdks/unlayer-python#with_streaming_response - """ - return AsyncDocumentsResourceWithStreamingResponse(self) - - async def documents_retrieve( - self, - id: str, - *, - project_id: str, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> DocumentDocumentsRetrieveResponse: - """ - Retrieve details of a previously generated document. - - Args: - project_id: The project ID - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - if not id: - raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") - return await self._get( - f"/documents/v1/documents/{id}", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query=await async_maybe_transform( - {"project_id": project_id}, document_documents_retrieve_params.DocumentDocumentsRetrieveParams - ), - ), - cast_to=DocumentDocumentsRetrieveResponse, - ) - - async def generate_create( - self, - *, - project_id: str, - design: Dict[str, object] | Omit = omit, - filename: str | Omit = omit, - html: str | Omit = omit, - merge_tags: Dict[str, str] | Omit = omit, - url: str | Omit = omit, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> DocumentGenerateCreateResponse: - """ - Generate PDF document from JSON design, HTML content, or URL. - - Args: - project_id: The project ID - - design: Proprietary design format JSON - - filename: Optional filename for the generated PDF - - html: HTML content to convert to PDF - - merge_tags: Optional merge tags for personalization - - url: URL to convert to PDF - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - return await self._post( - "/documents/v1/generate", - body=await async_maybe_transform( - { - "design": design, - "filename": filename, - "html": html, - "merge_tags": merge_tags, - "url": url, - }, - document_generate_create_params.DocumentGenerateCreateParams, - ), - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query=await async_maybe_transform( - {"project_id": project_id}, document_generate_create_params.DocumentGenerateCreateParams - ), - ), - cast_to=DocumentGenerateCreateResponse, - ) - - async def generate_template_template( - self, - *, - project_id: str, - template_id: str, - filename: str | Omit = omit, - merge_tags: Dict[str, str] | Omit = omit, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> DocumentGenerateTemplateTemplateResponse: - """ - Generate PDF document from an existing template with merge tags. - - Args: - project_id: The project ID - - template_id: ID of the template to use for generation - - filename: Optional filename for the generated PDF - - merge_tags: Optional merge tags for personalization - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - return await self._post( - "/documents/v1/generate/template", - body=await async_maybe_transform( - { - "template_id": template_id, - "filename": filename, - "merge_tags": merge_tags, - }, - document_generate_template_template_params.DocumentGenerateTemplateTemplateParams, - ), - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query=await async_maybe_transform( - {"project_id": project_id}, - document_generate_template_template_params.DocumentGenerateTemplateTemplateParams, - ), - ), - cast_to=DocumentGenerateTemplateTemplateResponse, - ) - - -class DocumentsResourceWithRawResponse: - def __init__(self, documents: DocumentsResource) -> None: - self._documents = documents - - self.documents_retrieve = to_raw_response_wrapper( - documents.documents_retrieve, - ) - self.generate_create = to_raw_response_wrapper( - documents.generate_create, - ) - self.generate_template_template = to_raw_response_wrapper( - documents.generate_template_template, - ) - - -class AsyncDocumentsResourceWithRawResponse: - def __init__(self, documents: AsyncDocumentsResource) -> None: - self._documents = documents - - self.documents_retrieve = async_to_raw_response_wrapper( - documents.documents_retrieve, - ) - self.generate_create = async_to_raw_response_wrapper( - documents.generate_create, - ) - self.generate_template_template = async_to_raw_response_wrapper( - documents.generate_template_template, - ) - - -class DocumentsResourceWithStreamingResponse: - def __init__(self, documents: DocumentsResource) -> None: - self._documents = documents - - self.documents_retrieve = to_streamed_response_wrapper( - documents.documents_retrieve, - ) - self.generate_create = to_streamed_response_wrapper( - documents.generate_create, - ) - self.generate_template_template = to_streamed_response_wrapper( - documents.generate_template_template, - ) - - -class AsyncDocumentsResourceWithStreamingResponse: - def __init__(self, documents: AsyncDocumentsResource) -> None: - self._documents = documents - - self.documents_retrieve = async_to_streamed_response_wrapper( - documents.documents_retrieve, - ) - self.generate_create = async_to_streamed_response_wrapper( - documents.generate_create, - ) - self.generate_template_template = async_to_streamed_response_wrapper( - documents.generate_template_template, - ) diff --git a/src/unlayer/resources/documents/__init__.py b/src/unlayer/resources/documents/__init__.py new file mode 100644 index 0000000..027ba26 --- /dev/null +++ b/src/unlayer/resources/documents/__init__.py @@ -0,0 +1,47 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from .generate import ( + GenerateResource, + AsyncGenerateResource, + GenerateResourceWithRawResponse, + AsyncGenerateResourceWithRawResponse, + GenerateResourceWithStreamingResponse, + AsyncGenerateResourceWithStreamingResponse, +) +from .documents import ( + DocumentsResource, + AsyncDocumentsResource, + DocumentsResourceWithRawResponse, + AsyncDocumentsResourceWithRawResponse, + DocumentsResourceWithStreamingResponse, + AsyncDocumentsResourceWithStreamingResponse, +) +from .generate_template import ( + GenerateTemplateResource, + AsyncGenerateTemplateResource, + GenerateTemplateResourceWithRawResponse, + AsyncGenerateTemplateResourceWithRawResponse, + GenerateTemplateResourceWithStreamingResponse, + AsyncGenerateTemplateResourceWithStreamingResponse, +) + +__all__ = [ + "GenerateResource", + "AsyncGenerateResource", + "GenerateResourceWithRawResponse", + "AsyncGenerateResourceWithRawResponse", + "GenerateResourceWithStreamingResponse", + "AsyncGenerateResourceWithStreamingResponse", + "GenerateTemplateResource", + "AsyncGenerateTemplateResource", + "GenerateTemplateResourceWithRawResponse", + "AsyncGenerateTemplateResourceWithRawResponse", + "GenerateTemplateResourceWithStreamingResponse", + "AsyncGenerateTemplateResourceWithStreamingResponse", + "DocumentsResource", + "AsyncDocumentsResource", + "DocumentsResourceWithRawResponse", + "AsyncDocumentsResourceWithRawResponse", + "DocumentsResourceWithStreamingResponse", + "AsyncDocumentsResourceWithStreamingResponse", +] diff --git a/src/unlayer/resources/documents/documents.py b/src/unlayer/resources/documents/documents.py new file mode 100644 index 0000000..4a52db8 --- /dev/null +++ b/src/unlayer/resources/documents/documents.py @@ -0,0 +1,245 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import httpx + +from ...types import document_retrieve_params +from ..._types import Body, Query, Headers, NotGiven, not_given +from ..._utils import maybe_transform, async_maybe_transform +from .generate import ( + GenerateResource, + AsyncGenerateResource, + GenerateResourceWithRawResponse, + AsyncGenerateResourceWithRawResponse, + GenerateResourceWithStreamingResponse, + AsyncGenerateResourceWithStreamingResponse, +) +from ..._compat import cached_property +from ..._resource import SyncAPIResource, AsyncAPIResource +from ..._response import ( + to_raw_response_wrapper, + to_streamed_response_wrapper, + async_to_raw_response_wrapper, + async_to_streamed_response_wrapper, +) +from ..._base_client import make_request_options +from .generate_template import ( + GenerateTemplateResource, + AsyncGenerateTemplateResource, + GenerateTemplateResourceWithRawResponse, + AsyncGenerateTemplateResourceWithRawResponse, + GenerateTemplateResourceWithStreamingResponse, + AsyncGenerateTemplateResourceWithStreamingResponse, +) +from ...types.document_retrieve_response import DocumentRetrieveResponse + +__all__ = ["DocumentsResource", "AsyncDocumentsResource"] + + +class DocumentsResource(SyncAPIResource): + @cached_property + def generate(self) -> GenerateResource: + return GenerateResource(self._client) + + @cached_property + def generate_template(self) -> GenerateTemplateResource: + return GenerateTemplateResource(self._client) + + @cached_property + def with_raw_response(self) -> DocumentsResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/stainless-sdks/unlayer-python#accessing-raw-response-data-eg-headers + """ + return DocumentsResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> DocumentsResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/stainless-sdks/unlayer-python#with_streaming_response + """ + return DocumentsResourceWithStreamingResponse(self) + + def retrieve( + self, + id: str, + *, + project_id: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> DocumentRetrieveResponse: + """ + Retrieve details of a previously generated document. + + Args: + project_id: The project ID + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not id: + raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") + return self._get( + f"/documents/v1/documents/{id}", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform({"project_id": project_id}, document_retrieve_params.DocumentRetrieveParams), + ), + cast_to=DocumentRetrieveResponse, + ) + + +class AsyncDocumentsResource(AsyncAPIResource): + @cached_property + def generate(self) -> AsyncGenerateResource: + return AsyncGenerateResource(self._client) + + @cached_property + def generate_template(self) -> AsyncGenerateTemplateResource: + return AsyncGenerateTemplateResource(self._client) + + @cached_property + def with_raw_response(self) -> AsyncDocumentsResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/stainless-sdks/unlayer-python#accessing-raw-response-data-eg-headers + """ + return AsyncDocumentsResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncDocumentsResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/stainless-sdks/unlayer-python#with_streaming_response + """ + return AsyncDocumentsResourceWithStreamingResponse(self) + + async def retrieve( + self, + id: str, + *, + project_id: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> DocumentRetrieveResponse: + """ + Retrieve details of a previously generated document. + + Args: + project_id: The project ID + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not id: + raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") + return await self._get( + f"/documents/v1/documents/{id}", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=await async_maybe_transform( + {"project_id": project_id}, document_retrieve_params.DocumentRetrieveParams + ), + ), + cast_to=DocumentRetrieveResponse, + ) + + +class DocumentsResourceWithRawResponse: + def __init__(self, documents: DocumentsResource) -> None: + self._documents = documents + + self.retrieve = to_raw_response_wrapper( + documents.retrieve, + ) + + @cached_property + def generate(self) -> GenerateResourceWithRawResponse: + return GenerateResourceWithRawResponse(self._documents.generate) + + @cached_property + def generate_template(self) -> GenerateTemplateResourceWithRawResponse: + return GenerateTemplateResourceWithRawResponse(self._documents.generate_template) + + +class AsyncDocumentsResourceWithRawResponse: + def __init__(self, documents: AsyncDocumentsResource) -> None: + self._documents = documents + + self.retrieve = async_to_raw_response_wrapper( + documents.retrieve, + ) + + @cached_property + def generate(self) -> AsyncGenerateResourceWithRawResponse: + return AsyncGenerateResourceWithRawResponse(self._documents.generate) + + @cached_property + def generate_template(self) -> AsyncGenerateTemplateResourceWithRawResponse: + return AsyncGenerateTemplateResourceWithRawResponse(self._documents.generate_template) + + +class DocumentsResourceWithStreamingResponse: + def __init__(self, documents: DocumentsResource) -> None: + self._documents = documents + + self.retrieve = to_streamed_response_wrapper( + documents.retrieve, + ) + + @cached_property + def generate(self) -> GenerateResourceWithStreamingResponse: + return GenerateResourceWithStreamingResponse(self._documents.generate) + + @cached_property + def generate_template(self) -> GenerateTemplateResourceWithStreamingResponse: + return GenerateTemplateResourceWithStreamingResponse(self._documents.generate_template) + + +class AsyncDocumentsResourceWithStreamingResponse: + def __init__(self, documents: AsyncDocumentsResource) -> None: + self._documents = documents + + self.retrieve = async_to_streamed_response_wrapper( + documents.retrieve, + ) + + @cached_property + def generate(self) -> AsyncGenerateResourceWithStreamingResponse: + return AsyncGenerateResourceWithStreamingResponse(self._documents.generate) + + @cached_property + def generate_template(self) -> AsyncGenerateTemplateResourceWithStreamingResponse: + return AsyncGenerateTemplateResourceWithStreamingResponse(self._documents.generate_template) diff --git a/src/unlayer/resources/documents/generate.py b/src/unlayer/resources/documents/generate.py new file mode 100644 index 0000000..871ef14 --- /dev/null +++ b/src/unlayer/resources/documents/generate.py @@ -0,0 +1,227 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Dict + +import httpx + +from ..._types import Body, Omit, Query, Headers, NotGiven, omit, not_given +from ..._utils import maybe_transform, async_maybe_transform +from ..._compat import cached_property +from ..._resource import SyncAPIResource, AsyncAPIResource +from ..._response import ( + to_raw_response_wrapper, + to_streamed_response_wrapper, + async_to_raw_response_wrapper, + async_to_streamed_response_wrapper, +) +from ..._base_client import make_request_options +from ...types.documents import generate_create_params +from ...types.documents.generate_create_response import GenerateCreateResponse + +__all__ = ["GenerateResource", "AsyncGenerateResource"] + + +class GenerateResource(SyncAPIResource): + @cached_property + def with_raw_response(self) -> GenerateResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/stainless-sdks/unlayer-python#accessing-raw-response-data-eg-headers + """ + return GenerateResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> GenerateResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/stainless-sdks/unlayer-python#with_streaming_response + """ + return GenerateResourceWithStreamingResponse(self) + + def create( + self, + *, + project_id: str, + design: Dict[str, object] | Omit = omit, + filename: str | Omit = omit, + html: str | Omit = omit, + merge_tags: Dict[str, str] | Omit = omit, + url: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> GenerateCreateResponse: + """ + Generate PDF document from JSON design, HTML content, or URL. + + Args: + project_id: The project ID + + design: Proprietary design format JSON + + filename: Optional filename for the generated PDF + + html: HTML content to convert to PDF + + merge_tags: Optional merge tags for personalization + + url: URL to convert to PDF + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._post( + "/documents/v1/generate", + body=maybe_transform( + { + "design": design, + "filename": filename, + "html": html, + "merge_tags": merge_tags, + "url": url, + }, + generate_create_params.GenerateCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform({"project_id": project_id}, generate_create_params.GenerateCreateParams), + ), + cast_to=GenerateCreateResponse, + ) + + +class AsyncGenerateResource(AsyncAPIResource): + @cached_property + def with_raw_response(self) -> AsyncGenerateResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/stainless-sdks/unlayer-python#accessing-raw-response-data-eg-headers + """ + return AsyncGenerateResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncGenerateResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/stainless-sdks/unlayer-python#with_streaming_response + """ + return AsyncGenerateResourceWithStreamingResponse(self) + + async def create( + self, + *, + project_id: str, + design: Dict[str, object] | Omit = omit, + filename: str | Omit = omit, + html: str | Omit = omit, + merge_tags: Dict[str, str] | Omit = omit, + url: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> GenerateCreateResponse: + """ + Generate PDF document from JSON design, HTML content, or URL. + + Args: + project_id: The project ID + + design: Proprietary design format JSON + + filename: Optional filename for the generated PDF + + html: HTML content to convert to PDF + + merge_tags: Optional merge tags for personalization + + url: URL to convert to PDF + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return await self._post( + "/documents/v1/generate", + body=await async_maybe_transform( + { + "design": design, + "filename": filename, + "html": html, + "merge_tags": merge_tags, + "url": url, + }, + generate_create_params.GenerateCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=await async_maybe_transform( + {"project_id": project_id}, generate_create_params.GenerateCreateParams + ), + ), + cast_to=GenerateCreateResponse, + ) + + +class GenerateResourceWithRawResponse: + def __init__(self, generate: GenerateResource) -> None: + self._generate = generate + + self.create = to_raw_response_wrapper( + generate.create, + ) + + +class AsyncGenerateResourceWithRawResponse: + def __init__(self, generate: AsyncGenerateResource) -> None: + self._generate = generate + + self.create = async_to_raw_response_wrapper( + generate.create, + ) + + +class GenerateResourceWithStreamingResponse: + def __init__(self, generate: GenerateResource) -> None: + self._generate = generate + + self.create = to_streamed_response_wrapper( + generate.create, + ) + + +class AsyncGenerateResourceWithStreamingResponse: + def __init__(self, generate: AsyncGenerateResource) -> None: + self._generate = generate + + self.create = async_to_streamed_response_wrapper( + generate.create, + ) diff --git a/src/unlayer/resources/documents/generate_template.py b/src/unlayer/resources/documents/generate_template.py new file mode 100644 index 0000000..74defa8 --- /dev/null +++ b/src/unlayer/resources/documents/generate_template.py @@ -0,0 +1,213 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Dict + +import httpx + +from ..._types import Body, Omit, Query, Headers, NotGiven, omit, not_given +from ..._utils import maybe_transform, async_maybe_transform +from ..._compat import cached_property +from ..._resource import SyncAPIResource, AsyncAPIResource +from ..._response import ( + to_raw_response_wrapper, + to_streamed_response_wrapper, + async_to_raw_response_wrapper, + async_to_streamed_response_wrapper, +) +from ..._base_client import make_request_options +from ...types.documents import generate_template_create_params +from ...types.documents.generate_template_create_response import GenerateTemplateCreateResponse + +__all__ = ["GenerateTemplateResource", "AsyncGenerateTemplateResource"] + + +class GenerateTemplateResource(SyncAPIResource): + @cached_property + def with_raw_response(self) -> GenerateTemplateResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/stainless-sdks/unlayer-python#accessing-raw-response-data-eg-headers + """ + return GenerateTemplateResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> GenerateTemplateResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/stainless-sdks/unlayer-python#with_streaming_response + """ + return GenerateTemplateResourceWithStreamingResponse(self) + + def create( + self, + *, + project_id: str, + template_id: str, + filename: str | Omit = omit, + merge_tags: Dict[str, str] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> GenerateTemplateCreateResponse: + """ + Generate PDF document from an existing template with merge tags. + + Args: + project_id: The project ID + + template_id: ID of the template to use for generation + + filename: Optional filename for the generated PDF + + merge_tags: Optional merge tags for personalization + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._post( + "/documents/v1/generate/template", + body=maybe_transform( + { + "template_id": template_id, + "filename": filename, + "merge_tags": merge_tags, + }, + generate_template_create_params.GenerateTemplateCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + {"project_id": project_id}, generate_template_create_params.GenerateTemplateCreateParams + ), + ), + cast_to=GenerateTemplateCreateResponse, + ) + + +class AsyncGenerateTemplateResource(AsyncAPIResource): + @cached_property + def with_raw_response(self) -> AsyncGenerateTemplateResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/stainless-sdks/unlayer-python#accessing-raw-response-data-eg-headers + """ + return AsyncGenerateTemplateResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncGenerateTemplateResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/stainless-sdks/unlayer-python#with_streaming_response + """ + return AsyncGenerateTemplateResourceWithStreamingResponse(self) + + async def create( + self, + *, + project_id: str, + template_id: str, + filename: str | Omit = omit, + merge_tags: Dict[str, str] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> GenerateTemplateCreateResponse: + """ + Generate PDF document from an existing template with merge tags. + + Args: + project_id: The project ID + + template_id: ID of the template to use for generation + + filename: Optional filename for the generated PDF + + merge_tags: Optional merge tags for personalization + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return await self._post( + "/documents/v1/generate/template", + body=await async_maybe_transform( + { + "template_id": template_id, + "filename": filename, + "merge_tags": merge_tags, + }, + generate_template_create_params.GenerateTemplateCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=await async_maybe_transform( + {"project_id": project_id}, generate_template_create_params.GenerateTemplateCreateParams + ), + ), + cast_to=GenerateTemplateCreateResponse, + ) + + +class GenerateTemplateResourceWithRawResponse: + def __init__(self, generate_template: GenerateTemplateResource) -> None: + self._generate_template = generate_template + + self.create = to_raw_response_wrapper( + generate_template.create, + ) + + +class AsyncGenerateTemplateResourceWithRawResponse: + def __init__(self, generate_template: AsyncGenerateTemplateResource) -> None: + self._generate_template = generate_template + + self.create = async_to_raw_response_wrapper( + generate_template.create, + ) + + +class GenerateTemplateResourceWithStreamingResponse: + def __init__(self, generate_template: GenerateTemplateResource) -> None: + self._generate_template = generate_template + + self.create = to_streamed_response_wrapper( + generate_template.create, + ) + + +class AsyncGenerateTemplateResourceWithStreamingResponse: + def __init__(self, generate_template: AsyncGenerateTemplateResource) -> None: + self._generate_template = generate_template + + self.create = async_to_streamed_response_wrapper( + generate_template.create, + ) diff --git a/src/unlayer/resources/emails.py b/src/unlayer/resources/emails.py deleted file mode 100644 index 3fb37a3..0000000 --- a/src/unlayer/resources/emails.py +++ /dev/null @@ -1,575 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing import Dict - -import httpx - -from ..types import ( - email_retrieve_params, - email_send_create_params, - email_render_create_params, - email_send_template_template_params, -) -from .._types import Body, Omit, Query, Headers, NotGiven, omit, not_given -from .._utils import maybe_transform, async_maybe_transform -from .._compat import cached_property -from .._resource import SyncAPIResource, AsyncAPIResource -from .._response import ( - to_raw_response_wrapper, - to_streamed_response_wrapper, - async_to_raw_response_wrapper, - async_to_streamed_response_wrapper, -) -from .._base_client import make_request_options -from ..types.email_retrieve_response import EmailRetrieveResponse -from ..types.email_send_create_response import EmailSendCreateResponse -from ..types.email_render_create_response import EmailRenderCreateResponse -from ..types.email_send_template_template_response import EmailSendTemplateTemplateResponse - -__all__ = ["EmailsResource", "AsyncEmailsResource"] - - -class EmailsResource(SyncAPIResource): - @cached_property - def with_raw_response(self) -> EmailsResourceWithRawResponse: - """ - This property can be used as a prefix for any HTTP method call to return - the raw response object instead of the parsed content. - - For more information, see https://www.github.com/stainless-sdks/unlayer-python#accessing-raw-response-data-eg-headers - """ - return EmailsResourceWithRawResponse(self) - - @cached_property - def with_streaming_response(self) -> EmailsResourceWithStreamingResponse: - """ - An alternative to `.with_raw_response` that doesn't eagerly read the response body. - - For more information, see https://www.github.com/stainless-sdks/unlayer-python#with_streaming_response - """ - return EmailsResourceWithStreamingResponse(self) - - def retrieve( - self, - id: str, - *, - project_id: str, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> EmailRetrieveResponse: - """ - Retrieve details of a previously sent email. - - Args: - project_id: The project ID - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - if not id: - raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") - return self._get( - f"/emails/v1/emails/{id}", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query=maybe_transform({"project_id": project_id}, email_retrieve_params.EmailRetrieveParams), - ), - cast_to=EmailRetrieveResponse, - ) - - def render_create( - self, - *, - project_id: str, - design: Dict[str, object], - merge_tags: Dict[str, str] | Omit = omit, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> EmailRenderCreateResponse: - """ - Convert design JSON to HTML with optional merge tags. - - Args: - project_id: The project ID - - design: Proprietary design format JSON - - merge_tags: Optional merge tags for personalization - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - return self._post( - "/emails/v1/render", - body=maybe_transform( - { - "design": design, - "merge_tags": merge_tags, - }, - email_render_create_params.EmailRenderCreateParams, - ), - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query=maybe_transform({"project_id": project_id}, email_render_create_params.EmailRenderCreateParams), - ), - cast_to=EmailRenderCreateResponse, - ) - - def send_create( - self, - *, - project_id: str, - to: str, - design: Dict[str, object] | Omit = omit, - html: str | Omit = omit, - merge_tags: Dict[str, str] | Omit = omit, - subject: str | Omit = omit, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> EmailSendCreateResponse: - """ - Send email with design JSON or HTML content. - - Args: - project_id: The project ID - - to: Recipient email address - - design: Proprietary design format JSON - - html: HTML content to send - - merge_tags: Optional merge tags for personalization - - subject: Email subject line - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - return self._post( - "/emails/v1/send", - body=maybe_transform( - { - "to": to, - "design": design, - "html": html, - "merge_tags": merge_tags, - "subject": subject, - }, - email_send_create_params.EmailSendCreateParams, - ), - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query=maybe_transform({"project_id": project_id}, email_send_create_params.EmailSendCreateParams), - ), - cast_to=EmailSendCreateResponse, - ) - - def send_template_template( - self, - *, - project_id: str, - template_id: str, - to: str, - merge_tags: Dict[str, str] | Omit = omit, - subject: str | Omit = omit, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> EmailSendTemplateTemplateResponse: - """ - Send email using an existing template with merge tags. - - Args: - project_id: The project ID - - template_id: ID of the template to use - - to: Recipient email address - - merge_tags: Optional merge tags for personalization - - subject: Email subject line (optional, uses template default if not provided) - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - return self._post( - "/emails/v1/send/template", - body=maybe_transform( - { - "template_id": template_id, - "to": to, - "merge_tags": merge_tags, - "subject": subject, - }, - email_send_template_template_params.EmailSendTemplateTemplateParams, - ), - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query=maybe_transform( - {"project_id": project_id}, email_send_template_template_params.EmailSendTemplateTemplateParams - ), - ), - cast_to=EmailSendTemplateTemplateResponse, - ) - - -class AsyncEmailsResource(AsyncAPIResource): - @cached_property - def with_raw_response(self) -> AsyncEmailsResourceWithRawResponse: - """ - This property can be used as a prefix for any HTTP method call to return - the raw response object instead of the parsed content. - - For more information, see https://www.github.com/stainless-sdks/unlayer-python#accessing-raw-response-data-eg-headers - """ - return AsyncEmailsResourceWithRawResponse(self) - - @cached_property - def with_streaming_response(self) -> AsyncEmailsResourceWithStreamingResponse: - """ - An alternative to `.with_raw_response` that doesn't eagerly read the response body. - - For more information, see https://www.github.com/stainless-sdks/unlayer-python#with_streaming_response - """ - return AsyncEmailsResourceWithStreamingResponse(self) - - async def retrieve( - self, - id: str, - *, - project_id: str, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> EmailRetrieveResponse: - """ - Retrieve details of a previously sent email. - - Args: - project_id: The project ID - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - if not id: - raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") - return await self._get( - f"/emails/v1/emails/{id}", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query=await async_maybe_transform( - {"project_id": project_id}, email_retrieve_params.EmailRetrieveParams - ), - ), - cast_to=EmailRetrieveResponse, - ) - - async def render_create( - self, - *, - project_id: str, - design: Dict[str, object], - merge_tags: Dict[str, str] | Omit = omit, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> EmailRenderCreateResponse: - """ - Convert design JSON to HTML with optional merge tags. - - Args: - project_id: The project ID - - design: Proprietary design format JSON - - merge_tags: Optional merge tags for personalization - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - return await self._post( - "/emails/v1/render", - body=await async_maybe_transform( - { - "design": design, - "merge_tags": merge_tags, - }, - email_render_create_params.EmailRenderCreateParams, - ), - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query=await async_maybe_transform( - {"project_id": project_id}, email_render_create_params.EmailRenderCreateParams - ), - ), - cast_to=EmailRenderCreateResponse, - ) - - async def send_create( - self, - *, - project_id: str, - to: str, - design: Dict[str, object] | Omit = omit, - html: str | Omit = omit, - merge_tags: Dict[str, str] | Omit = omit, - subject: str | Omit = omit, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> EmailSendCreateResponse: - """ - Send email with design JSON or HTML content. - - Args: - project_id: The project ID - - to: Recipient email address - - design: Proprietary design format JSON - - html: HTML content to send - - merge_tags: Optional merge tags for personalization - - subject: Email subject line - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - return await self._post( - "/emails/v1/send", - body=await async_maybe_transform( - { - "to": to, - "design": design, - "html": html, - "merge_tags": merge_tags, - "subject": subject, - }, - email_send_create_params.EmailSendCreateParams, - ), - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query=await async_maybe_transform( - {"project_id": project_id}, email_send_create_params.EmailSendCreateParams - ), - ), - cast_to=EmailSendCreateResponse, - ) - - async def send_template_template( - self, - *, - project_id: str, - template_id: str, - to: str, - merge_tags: Dict[str, str] | Omit = omit, - subject: str | Omit = omit, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> EmailSendTemplateTemplateResponse: - """ - Send email using an existing template with merge tags. - - Args: - project_id: The project ID - - template_id: ID of the template to use - - to: Recipient email address - - merge_tags: Optional merge tags for personalization - - subject: Email subject line (optional, uses template default if not provided) - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - return await self._post( - "/emails/v1/send/template", - body=await async_maybe_transform( - { - "template_id": template_id, - "to": to, - "merge_tags": merge_tags, - "subject": subject, - }, - email_send_template_template_params.EmailSendTemplateTemplateParams, - ), - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query=await async_maybe_transform( - {"project_id": project_id}, email_send_template_template_params.EmailSendTemplateTemplateParams - ), - ), - cast_to=EmailSendTemplateTemplateResponse, - ) - - -class EmailsResourceWithRawResponse: - def __init__(self, emails: EmailsResource) -> None: - self._emails = emails - - self.retrieve = to_raw_response_wrapper( - emails.retrieve, - ) - self.render_create = to_raw_response_wrapper( - emails.render_create, - ) - self.send_create = to_raw_response_wrapper( - emails.send_create, - ) - self.send_template_template = to_raw_response_wrapper( - emails.send_template_template, - ) - - -class AsyncEmailsResourceWithRawResponse: - def __init__(self, emails: AsyncEmailsResource) -> None: - self._emails = emails - - self.retrieve = async_to_raw_response_wrapper( - emails.retrieve, - ) - self.render_create = async_to_raw_response_wrapper( - emails.render_create, - ) - self.send_create = async_to_raw_response_wrapper( - emails.send_create, - ) - self.send_template_template = async_to_raw_response_wrapper( - emails.send_template_template, - ) - - -class EmailsResourceWithStreamingResponse: - def __init__(self, emails: EmailsResource) -> None: - self._emails = emails - - self.retrieve = to_streamed_response_wrapper( - emails.retrieve, - ) - self.render_create = to_streamed_response_wrapper( - emails.render_create, - ) - self.send_create = to_streamed_response_wrapper( - emails.send_create, - ) - self.send_template_template = to_streamed_response_wrapper( - emails.send_template_template, - ) - - -class AsyncEmailsResourceWithStreamingResponse: - def __init__(self, emails: AsyncEmailsResource) -> None: - self._emails = emails - - self.retrieve = async_to_streamed_response_wrapper( - emails.retrieve, - ) - self.render_create = async_to_streamed_response_wrapper( - emails.render_create, - ) - self.send_create = async_to_streamed_response_wrapper( - emails.send_create, - ) - self.send_template_template = async_to_streamed_response_wrapper( - emails.send_template_template, - ) diff --git a/src/unlayer/resources/emails/__init__.py b/src/unlayer/resources/emails/__init__.py new file mode 100644 index 0000000..4e678d4 --- /dev/null +++ b/src/unlayer/resources/emails/__init__.py @@ -0,0 +1,61 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from .send import ( + SendResource, + AsyncSendResource, + SendResourceWithRawResponse, + AsyncSendResourceWithRawResponse, + SendResourceWithStreamingResponse, + AsyncSendResourceWithStreamingResponse, +) +from .emails import ( + EmailsResource, + AsyncEmailsResource, + EmailsResourceWithRawResponse, + AsyncEmailsResourceWithRawResponse, + EmailsResourceWithStreamingResponse, + AsyncEmailsResourceWithStreamingResponse, +) +from .render import ( + RenderResource, + AsyncRenderResource, + RenderResourceWithRawResponse, + AsyncRenderResourceWithRawResponse, + RenderResourceWithStreamingResponse, + AsyncRenderResourceWithStreamingResponse, +) +from .send_template import ( + SendTemplateResource, + AsyncSendTemplateResource, + SendTemplateResourceWithRawResponse, + AsyncSendTemplateResourceWithRawResponse, + SendTemplateResourceWithStreamingResponse, + AsyncSendTemplateResourceWithStreamingResponse, +) + +__all__ = [ + "RenderResource", + "AsyncRenderResource", + "RenderResourceWithRawResponse", + "AsyncRenderResourceWithRawResponse", + "RenderResourceWithStreamingResponse", + "AsyncRenderResourceWithStreamingResponse", + "SendResource", + "AsyncSendResource", + "SendResourceWithRawResponse", + "AsyncSendResourceWithRawResponse", + "SendResourceWithStreamingResponse", + "AsyncSendResourceWithStreamingResponse", + "SendTemplateResource", + "AsyncSendTemplateResource", + "SendTemplateResourceWithRawResponse", + "AsyncSendTemplateResourceWithRawResponse", + "SendTemplateResourceWithStreamingResponse", + "AsyncSendTemplateResourceWithStreamingResponse", + "EmailsResource", + "AsyncEmailsResource", + "EmailsResourceWithRawResponse", + "AsyncEmailsResourceWithRawResponse", + "EmailsResourceWithStreamingResponse", + "AsyncEmailsResourceWithStreamingResponse", +] diff --git a/src/unlayer/resources/emails/emails.py b/src/unlayer/resources/emails/emails.py new file mode 100644 index 0000000..607c23f --- /dev/null +++ b/src/unlayer/resources/emails/emails.py @@ -0,0 +1,277 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import httpx + +from .send import ( + SendResource, + AsyncSendResource, + SendResourceWithRawResponse, + AsyncSendResourceWithRawResponse, + SendResourceWithStreamingResponse, + AsyncSendResourceWithStreamingResponse, +) +from .render import ( + RenderResource, + AsyncRenderResource, + RenderResourceWithRawResponse, + AsyncRenderResourceWithRawResponse, + RenderResourceWithStreamingResponse, + AsyncRenderResourceWithStreamingResponse, +) +from ...types import email_retrieve_params +from ..._types import Body, Query, Headers, NotGiven, not_given +from ..._utils import maybe_transform, async_maybe_transform +from ..._compat import cached_property +from ..._resource import SyncAPIResource, AsyncAPIResource +from ..._response import ( + to_raw_response_wrapper, + to_streamed_response_wrapper, + async_to_raw_response_wrapper, + async_to_streamed_response_wrapper, +) +from .send_template import ( + SendTemplateResource, + AsyncSendTemplateResource, + SendTemplateResourceWithRawResponse, + AsyncSendTemplateResourceWithRawResponse, + SendTemplateResourceWithStreamingResponse, + AsyncSendTemplateResourceWithStreamingResponse, +) +from ..._base_client import make_request_options +from ...types.email_retrieve_response import EmailRetrieveResponse + +__all__ = ["EmailsResource", "AsyncEmailsResource"] + + +class EmailsResource(SyncAPIResource): + @cached_property + def render(self) -> RenderResource: + return RenderResource(self._client) + + @cached_property + def send(self) -> SendResource: + return SendResource(self._client) + + @cached_property + def send_template(self) -> SendTemplateResource: + return SendTemplateResource(self._client) + + @cached_property + def with_raw_response(self) -> EmailsResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/stainless-sdks/unlayer-python#accessing-raw-response-data-eg-headers + """ + return EmailsResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> EmailsResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/stainless-sdks/unlayer-python#with_streaming_response + """ + return EmailsResourceWithStreamingResponse(self) + + def retrieve( + self, + id: str, + *, + project_id: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> EmailRetrieveResponse: + """ + Retrieve details of a previously sent email. + + Args: + project_id: The project ID + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not id: + raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") + return self._get( + f"/emails/v1/emails/{id}", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform({"project_id": project_id}, email_retrieve_params.EmailRetrieveParams), + ), + cast_to=EmailRetrieveResponse, + ) + + +class AsyncEmailsResource(AsyncAPIResource): + @cached_property + def render(self) -> AsyncRenderResource: + return AsyncRenderResource(self._client) + + @cached_property + def send(self) -> AsyncSendResource: + return AsyncSendResource(self._client) + + @cached_property + def send_template(self) -> AsyncSendTemplateResource: + return AsyncSendTemplateResource(self._client) + + @cached_property + def with_raw_response(self) -> AsyncEmailsResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/stainless-sdks/unlayer-python#accessing-raw-response-data-eg-headers + """ + return AsyncEmailsResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncEmailsResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/stainless-sdks/unlayer-python#with_streaming_response + """ + return AsyncEmailsResourceWithStreamingResponse(self) + + async def retrieve( + self, + id: str, + *, + project_id: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> EmailRetrieveResponse: + """ + Retrieve details of a previously sent email. + + Args: + project_id: The project ID + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not id: + raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") + return await self._get( + f"/emails/v1/emails/{id}", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=await async_maybe_transform( + {"project_id": project_id}, email_retrieve_params.EmailRetrieveParams + ), + ), + cast_to=EmailRetrieveResponse, + ) + + +class EmailsResourceWithRawResponse: + def __init__(self, emails: EmailsResource) -> None: + self._emails = emails + + self.retrieve = to_raw_response_wrapper( + emails.retrieve, + ) + + @cached_property + def render(self) -> RenderResourceWithRawResponse: + return RenderResourceWithRawResponse(self._emails.render) + + @cached_property + def send(self) -> SendResourceWithRawResponse: + return SendResourceWithRawResponse(self._emails.send) + + @cached_property + def send_template(self) -> SendTemplateResourceWithRawResponse: + return SendTemplateResourceWithRawResponse(self._emails.send_template) + + +class AsyncEmailsResourceWithRawResponse: + def __init__(self, emails: AsyncEmailsResource) -> None: + self._emails = emails + + self.retrieve = async_to_raw_response_wrapper( + emails.retrieve, + ) + + @cached_property + def render(self) -> AsyncRenderResourceWithRawResponse: + return AsyncRenderResourceWithRawResponse(self._emails.render) + + @cached_property + def send(self) -> AsyncSendResourceWithRawResponse: + return AsyncSendResourceWithRawResponse(self._emails.send) + + @cached_property + def send_template(self) -> AsyncSendTemplateResourceWithRawResponse: + return AsyncSendTemplateResourceWithRawResponse(self._emails.send_template) + + +class EmailsResourceWithStreamingResponse: + def __init__(self, emails: EmailsResource) -> None: + self._emails = emails + + self.retrieve = to_streamed_response_wrapper( + emails.retrieve, + ) + + @cached_property + def render(self) -> RenderResourceWithStreamingResponse: + return RenderResourceWithStreamingResponse(self._emails.render) + + @cached_property + def send(self) -> SendResourceWithStreamingResponse: + return SendResourceWithStreamingResponse(self._emails.send) + + @cached_property + def send_template(self) -> SendTemplateResourceWithStreamingResponse: + return SendTemplateResourceWithStreamingResponse(self._emails.send_template) + + +class AsyncEmailsResourceWithStreamingResponse: + def __init__(self, emails: AsyncEmailsResource) -> None: + self._emails = emails + + self.retrieve = async_to_streamed_response_wrapper( + emails.retrieve, + ) + + @cached_property + def render(self) -> AsyncRenderResourceWithStreamingResponse: + return AsyncRenderResourceWithStreamingResponse(self._emails.render) + + @cached_property + def send(self) -> AsyncSendResourceWithStreamingResponse: + return AsyncSendResourceWithStreamingResponse(self._emails.send) + + @cached_property + def send_template(self) -> AsyncSendTemplateResourceWithStreamingResponse: + return AsyncSendTemplateResourceWithStreamingResponse(self._emails.send_template) diff --git a/src/unlayer/resources/emails/render.py b/src/unlayer/resources/emails/render.py new file mode 100644 index 0000000..7181e1a --- /dev/null +++ b/src/unlayer/resources/emails/render.py @@ -0,0 +1,201 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Dict + +import httpx + +from ..._types import Body, Omit, Query, Headers, NotGiven, omit, not_given +from ..._utils import maybe_transform, async_maybe_transform +from ..._compat import cached_property +from ..._resource import SyncAPIResource, AsyncAPIResource +from ..._response import ( + to_raw_response_wrapper, + to_streamed_response_wrapper, + async_to_raw_response_wrapper, + async_to_streamed_response_wrapper, +) +from ..._base_client import make_request_options +from ...types.emails import render_create_params +from ...types.emails.render_create_response import RenderCreateResponse + +__all__ = ["RenderResource", "AsyncRenderResource"] + + +class RenderResource(SyncAPIResource): + @cached_property + def with_raw_response(self) -> RenderResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/stainless-sdks/unlayer-python#accessing-raw-response-data-eg-headers + """ + return RenderResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> RenderResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/stainless-sdks/unlayer-python#with_streaming_response + """ + return RenderResourceWithStreamingResponse(self) + + def create( + self, + *, + project_id: str, + design: Dict[str, object], + merge_tags: Dict[str, str] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> RenderCreateResponse: + """ + Convert design JSON to HTML with optional merge tags. + + Args: + project_id: The project ID + + design: Proprietary design format JSON + + merge_tags: Optional merge tags for personalization + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._post( + "/emails/v1/render", + body=maybe_transform( + { + "design": design, + "merge_tags": merge_tags, + }, + render_create_params.RenderCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform({"project_id": project_id}, render_create_params.RenderCreateParams), + ), + cast_to=RenderCreateResponse, + ) + + +class AsyncRenderResource(AsyncAPIResource): + @cached_property + def with_raw_response(self) -> AsyncRenderResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/stainless-sdks/unlayer-python#accessing-raw-response-data-eg-headers + """ + return AsyncRenderResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncRenderResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/stainless-sdks/unlayer-python#with_streaming_response + """ + return AsyncRenderResourceWithStreamingResponse(self) + + async def create( + self, + *, + project_id: str, + design: Dict[str, object], + merge_tags: Dict[str, str] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> RenderCreateResponse: + """ + Convert design JSON to HTML with optional merge tags. + + Args: + project_id: The project ID + + design: Proprietary design format JSON + + merge_tags: Optional merge tags for personalization + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return await self._post( + "/emails/v1/render", + body=await async_maybe_transform( + { + "design": design, + "merge_tags": merge_tags, + }, + render_create_params.RenderCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=await async_maybe_transform({"project_id": project_id}, render_create_params.RenderCreateParams), + ), + cast_to=RenderCreateResponse, + ) + + +class RenderResourceWithRawResponse: + def __init__(self, render: RenderResource) -> None: + self._render = render + + self.create = to_raw_response_wrapper( + render.create, + ) + + +class AsyncRenderResourceWithRawResponse: + def __init__(self, render: AsyncRenderResource) -> None: + self._render = render + + self.create = async_to_raw_response_wrapper( + render.create, + ) + + +class RenderResourceWithStreamingResponse: + def __init__(self, render: RenderResource) -> None: + self._render = render + + self.create = to_streamed_response_wrapper( + render.create, + ) + + +class AsyncRenderResourceWithStreamingResponse: + def __init__(self, render: AsyncRenderResource) -> None: + self._render = render + + self.create = async_to_streamed_response_wrapper( + render.create, + ) diff --git a/src/unlayer/resources/emails/send.py b/src/unlayer/resources/emails/send.py new file mode 100644 index 0000000..dc53469 --- /dev/null +++ b/src/unlayer/resources/emails/send.py @@ -0,0 +1,225 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Dict + +import httpx + +from ..._types import Body, Omit, Query, Headers, NotGiven, omit, not_given +from ..._utils import maybe_transform, async_maybe_transform +from ..._compat import cached_property +from ..._resource import SyncAPIResource, AsyncAPIResource +from ..._response import ( + to_raw_response_wrapper, + to_streamed_response_wrapper, + async_to_raw_response_wrapper, + async_to_streamed_response_wrapper, +) +from ..._base_client import make_request_options +from ...types.emails import send_create_params +from ...types.emails.send_create_response import SendCreateResponse + +__all__ = ["SendResource", "AsyncSendResource"] + + +class SendResource(SyncAPIResource): + @cached_property + def with_raw_response(self) -> SendResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/stainless-sdks/unlayer-python#accessing-raw-response-data-eg-headers + """ + return SendResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> SendResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/stainless-sdks/unlayer-python#with_streaming_response + """ + return SendResourceWithStreamingResponse(self) + + def create( + self, + *, + project_id: str, + to: str, + design: Dict[str, object] | Omit = omit, + html: str | Omit = omit, + merge_tags: Dict[str, str] | Omit = omit, + subject: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> SendCreateResponse: + """ + Send email with design JSON or HTML content. + + Args: + project_id: The project ID + + to: Recipient email address + + design: Proprietary design format JSON + + html: HTML content to send + + merge_tags: Optional merge tags for personalization + + subject: Email subject line + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._post( + "/emails/v1/send", + body=maybe_transform( + { + "to": to, + "design": design, + "html": html, + "merge_tags": merge_tags, + "subject": subject, + }, + send_create_params.SendCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform({"project_id": project_id}, send_create_params.SendCreateParams), + ), + cast_to=SendCreateResponse, + ) + + +class AsyncSendResource(AsyncAPIResource): + @cached_property + def with_raw_response(self) -> AsyncSendResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/stainless-sdks/unlayer-python#accessing-raw-response-data-eg-headers + """ + return AsyncSendResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncSendResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/stainless-sdks/unlayer-python#with_streaming_response + """ + return AsyncSendResourceWithStreamingResponse(self) + + async def create( + self, + *, + project_id: str, + to: str, + design: Dict[str, object] | Omit = omit, + html: str | Omit = omit, + merge_tags: Dict[str, str] | Omit = omit, + subject: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> SendCreateResponse: + """ + Send email with design JSON or HTML content. + + Args: + project_id: The project ID + + to: Recipient email address + + design: Proprietary design format JSON + + html: HTML content to send + + merge_tags: Optional merge tags for personalization + + subject: Email subject line + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return await self._post( + "/emails/v1/send", + body=await async_maybe_transform( + { + "to": to, + "design": design, + "html": html, + "merge_tags": merge_tags, + "subject": subject, + }, + send_create_params.SendCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=await async_maybe_transform({"project_id": project_id}, send_create_params.SendCreateParams), + ), + cast_to=SendCreateResponse, + ) + + +class SendResourceWithRawResponse: + def __init__(self, send: SendResource) -> None: + self._send = send + + self.create = to_raw_response_wrapper( + send.create, + ) + + +class AsyncSendResourceWithRawResponse: + def __init__(self, send: AsyncSendResource) -> None: + self._send = send + + self.create = async_to_raw_response_wrapper( + send.create, + ) + + +class SendResourceWithStreamingResponse: + def __init__(self, send: SendResource) -> None: + self._send = send + + self.create = to_streamed_response_wrapper( + send.create, + ) + + +class AsyncSendResourceWithStreamingResponse: + def __init__(self, send: AsyncSendResource) -> None: + self._send = send + + self.create = async_to_streamed_response_wrapper( + send.create, + ) diff --git a/src/unlayer/resources/emails/send_template.py b/src/unlayer/resources/emails/send_template.py new file mode 100644 index 0000000..80e99ce --- /dev/null +++ b/src/unlayer/resources/emails/send_template.py @@ -0,0 +1,219 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Dict + +import httpx + +from ..._types import Body, Omit, Query, Headers, NotGiven, omit, not_given +from ..._utils import maybe_transform, async_maybe_transform +from ..._compat import cached_property +from ..._resource import SyncAPIResource, AsyncAPIResource +from ..._response import ( + to_raw_response_wrapper, + to_streamed_response_wrapper, + async_to_raw_response_wrapper, + async_to_streamed_response_wrapper, +) +from ..._base_client import make_request_options +from ...types.emails import send_template_create_params +from ...types.emails.send_template_create_response import SendTemplateCreateResponse + +__all__ = ["SendTemplateResource", "AsyncSendTemplateResource"] + + +class SendTemplateResource(SyncAPIResource): + @cached_property + def with_raw_response(self) -> SendTemplateResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/stainless-sdks/unlayer-python#accessing-raw-response-data-eg-headers + """ + return SendTemplateResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> SendTemplateResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/stainless-sdks/unlayer-python#with_streaming_response + """ + return SendTemplateResourceWithStreamingResponse(self) + + def create( + self, + *, + project_id: str, + template_id: str, + to: str, + merge_tags: Dict[str, str] | Omit = omit, + subject: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> SendTemplateCreateResponse: + """ + Send email using an existing template with merge tags. + + Args: + project_id: The project ID + + template_id: ID of the template to use + + to: Recipient email address + + merge_tags: Optional merge tags for personalization + + subject: Email subject line (optional, uses template default if not provided) + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._post( + "/emails/v1/send/template", + body=maybe_transform( + { + "template_id": template_id, + "to": to, + "merge_tags": merge_tags, + "subject": subject, + }, + send_template_create_params.SendTemplateCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform({"project_id": project_id}, send_template_create_params.SendTemplateCreateParams), + ), + cast_to=SendTemplateCreateResponse, + ) + + +class AsyncSendTemplateResource(AsyncAPIResource): + @cached_property + def with_raw_response(self) -> AsyncSendTemplateResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/stainless-sdks/unlayer-python#accessing-raw-response-data-eg-headers + """ + return AsyncSendTemplateResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncSendTemplateResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/stainless-sdks/unlayer-python#with_streaming_response + """ + return AsyncSendTemplateResourceWithStreamingResponse(self) + + async def create( + self, + *, + project_id: str, + template_id: str, + to: str, + merge_tags: Dict[str, str] | Omit = omit, + subject: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> SendTemplateCreateResponse: + """ + Send email using an existing template with merge tags. + + Args: + project_id: The project ID + + template_id: ID of the template to use + + to: Recipient email address + + merge_tags: Optional merge tags for personalization + + subject: Email subject line (optional, uses template default if not provided) + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return await self._post( + "/emails/v1/send/template", + body=await async_maybe_transform( + { + "template_id": template_id, + "to": to, + "merge_tags": merge_tags, + "subject": subject, + }, + send_template_create_params.SendTemplateCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=await async_maybe_transform( + {"project_id": project_id}, send_template_create_params.SendTemplateCreateParams + ), + ), + cast_to=SendTemplateCreateResponse, + ) + + +class SendTemplateResourceWithRawResponse: + def __init__(self, send_template: SendTemplateResource) -> None: + self._send_template = send_template + + self.create = to_raw_response_wrapper( + send_template.create, + ) + + +class AsyncSendTemplateResourceWithRawResponse: + def __init__(self, send_template: AsyncSendTemplateResource) -> None: + self._send_template = send_template + + self.create = async_to_raw_response_wrapper( + send_template.create, + ) + + +class SendTemplateResourceWithStreamingResponse: + def __init__(self, send_template: SendTemplateResource) -> None: + self._send_template = send_template + + self.create = to_streamed_response_wrapper( + send_template.create, + ) + + +class AsyncSendTemplateResourceWithStreamingResponse: + def __init__(self, send_template: AsyncSendTemplateResource) -> None: + self._send_template = send_template + + self.create = async_to_streamed_response_wrapper( + send_template.create, + ) diff --git a/src/unlayer/resources/export.py b/src/unlayer/resources/export.py deleted file mode 100644 index 35e810f..0000000 --- a/src/unlayer/resources/export.py +++ /dev/null @@ -1,442 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -import httpx - -from ..types import export_pdf_list_params, export_zip_list_params, export_html_list_params, export_image_list_params -from .._types import Body, Query, Headers, NotGiven, not_given -from .._utils import maybe_transform, async_maybe_transform -from .._compat import cached_property -from .._resource import SyncAPIResource, AsyncAPIResource -from .._response import ( - to_raw_response_wrapper, - to_streamed_response_wrapper, - async_to_raw_response_wrapper, - async_to_streamed_response_wrapper, -) -from .._base_client import make_request_options -from ..types.export_pdf_list_response import ExportPdfListResponse -from ..types.export_zip_list_response import ExportZipListResponse -from ..types.export_html_list_response import ExportHTMLListResponse -from ..types.export_image_list_response import ExportImageListResponse - -__all__ = ["ExportResource", "AsyncExportResource"] - - -class ExportResource(SyncAPIResource): - @cached_property - def with_raw_response(self) -> ExportResourceWithRawResponse: - """ - This property can be used as a prefix for any HTTP method call to return - the raw response object instead of the parsed content. - - For more information, see https://www.github.com/stainless-sdks/unlayer-python#accessing-raw-response-data-eg-headers - """ - return ExportResourceWithRawResponse(self) - - @cached_property - def with_streaming_response(self) -> ExportResourceWithStreamingResponse: - """ - An alternative to `.with_raw_response` that doesn't eagerly read the response body. - - For more information, see https://www.github.com/stainless-sdks/unlayer-python#with_streaming_response - """ - return ExportResourceWithStreamingResponse(self) - - def html_list( - self, - *, - project_id: str, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> ExportHTMLListResponse: - """ - Export design to HTML. - - Args: - project_id: The project ID - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - return self._get( - "/export/v3/html", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query=maybe_transform({"project_id": project_id}, export_html_list_params.ExportHTMLListParams), - ), - cast_to=ExportHTMLListResponse, - ) - - def image_list( - self, - *, - project_id: str, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> ExportImageListResponse: - """ - Export design to image. - - Args: - project_id: The project ID - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - return self._get( - "/export/v3/image", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query=maybe_transform({"project_id": project_id}, export_image_list_params.ExportImageListParams), - ), - cast_to=ExportImageListResponse, - ) - - def pdf_list( - self, - *, - project_id: str, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> ExportPdfListResponse: - """ - Export design to PDF. - - Args: - project_id: The project ID - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - return self._get( - "/export/v3/pdf", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query=maybe_transform({"project_id": project_id}, export_pdf_list_params.ExportPdfListParams), - ), - cast_to=ExportPdfListResponse, - ) - - def zip_list( - self, - *, - project_id: str, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> ExportZipListResponse: - """ - Export design to ZIP archive. - - Args: - project_id: The project ID - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - return self._get( - "/export/v3/zip", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query=maybe_transform({"project_id": project_id}, export_zip_list_params.ExportZipListParams), - ), - cast_to=ExportZipListResponse, - ) - - -class AsyncExportResource(AsyncAPIResource): - @cached_property - def with_raw_response(self) -> AsyncExportResourceWithRawResponse: - """ - This property can be used as a prefix for any HTTP method call to return - the raw response object instead of the parsed content. - - For more information, see https://www.github.com/stainless-sdks/unlayer-python#accessing-raw-response-data-eg-headers - """ - return AsyncExportResourceWithRawResponse(self) - - @cached_property - def with_streaming_response(self) -> AsyncExportResourceWithStreamingResponse: - """ - An alternative to `.with_raw_response` that doesn't eagerly read the response body. - - For more information, see https://www.github.com/stainless-sdks/unlayer-python#with_streaming_response - """ - return AsyncExportResourceWithStreamingResponse(self) - - async def html_list( - self, - *, - project_id: str, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> ExportHTMLListResponse: - """ - Export design to HTML. - - Args: - project_id: The project ID - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - return await self._get( - "/export/v3/html", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query=await async_maybe_transform( - {"project_id": project_id}, export_html_list_params.ExportHTMLListParams - ), - ), - cast_to=ExportHTMLListResponse, - ) - - async def image_list( - self, - *, - project_id: str, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> ExportImageListResponse: - """ - Export design to image. - - Args: - project_id: The project ID - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - return await self._get( - "/export/v3/image", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query=await async_maybe_transform( - {"project_id": project_id}, export_image_list_params.ExportImageListParams - ), - ), - cast_to=ExportImageListResponse, - ) - - async def pdf_list( - self, - *, - project_id: str, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> ExportPdfListResponse: - """ - Export design to PDF. - - Args: - project_id: The project ID - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - return await self._get( - "/export/v3/pdf", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query=await async_maybe_transform( - {"project_id": project_id}, export_pdf_list_params.ExportPdfListParams - ), - ), - cast_to=ExportPdfListResponse, - ) - - async def zip_list( - self, - *, - project_id: str, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> ExportZipListResponse: - """ - Export design to ZIP archive. - - Args: - project_id: The project ID - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - return await self._get( - "/export/v3/zip", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query=await async_maybe_transform( - {"project_id": project_id}, export_zip_list_params.ExportZipListParams - ), - ), - cast_to=ExportZipListResponse, - ) - - -class ExportResourceWithRawResponse: - def __init__(self, export: ExportResource) -> None: - self._export = export - - self.html_list = to_raw_response_wrapper( - export.html_list, - ) - self.image_list = to_raw_response_wrapper( - export.image_list, - ) - self.pdf_list = to_raw_response_wrapper( - export.pdf_list, - ) - self.zip_list = to_raw_response_wrapper( - export.zip_list, - ) - - -class AsyncExportResourceWithRawResponse: - def __init__(self, export: AsyncExportResource) -> None: - self._export = export - - self.html_list = async_to_raw_response_wrapper( - export.html_list, - ) - self.image_list = async_to_raw_response_wrapper( - export.image_list, - ) - self.pdf_list = async_to_raw_response_wrapper( - export.pdf_list, - ) - self.zip_list = async_to_raw_response_wrapper( - export.zip_list, - ) - - -class ExportResourceWithStreamingResponse: - def __init__(self, export: ExportResource) -> None: - self._export = export - - self.html_list = to_streamed_response_wrapper( - export.html_list, - ) - self.image_list = to_streamed_response_wrapper( - export.image_list, - ) - self.pdf_list = to_streamed_response_wrapper( - export.pdf_list, - ) - self.zip_list = to_streamed_response_wrapper( - export.zip_list, - ) - - -class AsyncExportResourceWithStreamingResponse: - def __init__(self, export: AsyncExportResource) -> None: - self._export = export - - self.html_list = async_to_streamed_response_wrapper( - export.html_list, - ) - self.image_list = async_to_streamed_response_wrapper( - export.image_list, - ) - self.pdf_list = async_to_streamed_response_wrapper( - export.pdf_list, - ) - self.zip_list = async_to_streamed_response_wrapper( - export.zip_list, - ) diff --git a/src/unlayer/resources/export/__init__.py b/src/unlayer/resources/export/__init__.py new file mode 100644 index 0000000..8c58890 --- /dev/null +++ b/src/unlayer/resources/export/__init__.py @@ -0,0 +1,75 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from .pdf import ( + PdfResource, + AsyncPdfResource, + PdfResourceWithRawResponse, + AsyncPdfResourceWithRawResponse, + PdfResourceWithStreamingResponse, + AsyncPdfResourceWithStreamingResponse, +) +from .zip import ( + ZipResource, + AsyncZipResource, + ZipResourceWithRawResponse, + AsyncZipResourceWithRawResponse, + ZipResourceWithStreamingResponse, + AsyncZipResourceWithStreamingResponse, +) +from .html import ( + HTMLResource, + AsyncHTMLResource, + HTMLResourceWithRawResponse, + AsyncHTMLResourceWithRawResponse, + HTMLResourceWithStreamingResponse, + AsyncHTMLResourceWithStreamingResponse, +) +from .image import ( + ImageResource, + AsyncImageResource, + ImageResourceWithRawResponse, + AsyncImageResourceWithRawResponse, + ImageResourceWithStreamingResponse, + AsyncImageResourceWithStreamingResponse, +) +from .export import ( + ExportResource, + AsyncExportResource, + ExportResourceWithRawResponse, + AsyncExportResourceWithRawResponse, + ExportResourceWithStreamingResponse, + AsyncExportResourceWithStreamingResponse, +) + +__all__ = [ + "HTMLResource", + "AsyncHTMLResource", + "HTMLResourceWithRawResponse", + "AsyncHTMLResourceWithRawResponse", + "HTMLResourceWithStreamingResponse", + "AsyncHTMLResourceWithStreamingResponse", + "ImageResource", + "AsyncImageResource", + "ImageResourceWithRawResponse", + "AsyncImageResourceWithRawResponse", + "ImageResourceWithStreamingResponse", + "AsyncImageResourceWithStreamingResponse", + "PdfResource", + "AsyncPdfResource", + "PdfResourceWithRawResponse", + "AsyncPdfResourceWithRawResponse", + "PdfResourceWithStreamingResponse", + "AsyncPdfResourceWithStreamingResponse", + "ZipResource", + "AsyncZipResource", + "ZipResourceWithRawResponse", + "AsyncZipResourceWithRawResponse", + "ZipResourceWithStreamingResponse", + "AsyncZipResourceWithStreamingResponse", + "ExportResource", + "AsyncExportResource", + "ExportResourceWithRawResponse", + "AsyncExportResourceWithRawResponse", + "ExportResourceWithStreamingResponse", + "AsyncExportResourceWithStreamingResponse", +] diff --git a/src/unlayer/resources/export/export.py b/src/unlayer/resources/export/export.py new file mode 100644 index 0000000..3e38faf --- /dev/null +++ b/src/unlayer/resources/export/export.py @@ -0,0 +1,198 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from .pdf import ( + PdfResource, + AsyncPdfResource, + PdfResourceWithRawResponse, + AsyncPdfResourceWithRawResponse, + PdfResourceWithStreamingResponse, + AsyncPdfResourceWithStreamingResponse, +) +from .zip import ( + ZipResource, + AsyncZipResource, + ZipResourceWithRawResponse, + AsyncZipResourceWithRawResponse, + ZipResourceWithStreamingResponse, + AsyncZipResourceWithStreamingResponse, +) +from .html import ( + HTMLResource, + AsyncHTMLResource, + HTMLResourceWithRawResponse, + AsyncHTMLResourceWithRawResponse, + HTMLResourceWithStreamingResponse, + AsyncHTMLResourceWithStreamingResponse, +) +from .image import ( + ImageResource, + AsyncImageResource, + ImageResourceWithRawResponse, + AsyncImageResourceWithRawResponse, + ImageResourceWithStreamingResponse, + AsyncImageResourceWithStreamingResponse, +) +from ..._compat import cached_property +from ..._resource import SyncAPIResource, AsyncAPIResource + +__all__ = ["ExportResource", "AsyncExportResource"] + + +class ExportResource(SyncAPIResource): + @cached_property + def html(self) -> HTMLResource: + return HTMLResource(self._client) + + @cached_property + def image(self) -> ImageResource: + return ImageResource(self._client) + + @cached_property + def pdf(self) -> PdfResource: + return PdfResource(self._client) + + @cached_property + def zip(self) -> ZipResource: + return ZipResource(self._client) + + @cached_property + def with_raw_response(self) -> ExportResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/stainless-sdks/unlayer-python#accessing-raw-response-data-eg-headers + """ + return ExportResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> ExportResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/stainless-sdks/unlayer-python#with_streaming_response + """ + return ExportResourceWithStreamingResponse(self) + + +class AsyncExportResource(AsyncAPIResource): + @cached_property + def html(self) -> AsyncHTMLResource: + return AsyncHTMLResource(self._client) + + @cached_property + def image(self) -> AsyncImageResource: + return AsyncImageResource(self._client) + + @cached_property + def pdf(self) -> AsyncPdfResource: + return AsyncPdfResource(self._client) + + @cached_property + def zip(self) -> AsyncZipResource: + return AsyncZipResource(self._client) + + @cached_property + def with_raw_response(self) -> AsyncExportResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/stainless-sdks/unlayer-python#accessing-raw-response-data-eg-headers + """ + return AsyncExportResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncExportResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/stainless-sdks/unlayer-python#with_streaming_response + """ + return AsyncExportResourceWithStreamingResponse(self) + + +class ExportResourceWithRawResponse: + def __init__(self, export: ExportResource) -> None: + self._export = export + + @cached_property + def html(self) -> HTMLResourceWithRawResponse: + return HTMLResourceWithRawResponse(self._export.html) + + @cached_property + def image(self) -> ImageResourceWithRawResponse: + return ImageResourceWithRawResponse(self._export.image) + + @cached_property + def pdf(self) -> PdfResourceWithRawResponse: + return PdfResourceWithRawResponse(self._export.pdf) + + @cached_property + def zip(self) -> ZipResourceWithRawResponse: + return ZipResourceWithRawResponse(self._export.zip) + + +class AsyncExportResourceWithRawResponse: + def __init__(self, export: AsyncExportResource) -> None: + self._export = export + + @cached_property + def html(self) -> AsyncHTMLResourceWithRawResponse: + return AsyncHTMLResourceWithRawResponse(self._export.html) + + @cached_property + def image(self) -> AsyncImageResourceWithRawResponse: + return AsyncImageResourceWithRawResponse(self._export.image) + + @cached_property + def pdf(self) -> AsyncPdfResourceWithRawResponse: + return AsyncPdfResourceWithRawResponse(self._export.pdf) + + @cached_property + def zip(self) -> AsyncZipResourceWithRawResponse: + return AsyncZipResourceWithRawResponse(self._export.zip) + + +class ExportResourceWithStreamingResponse: + def __init__(self, export: ExportResource) -> None: + self._export = export + + @cached_property + def html(self) -> HTMLResourceWithStreamingResponse: + return HTMLResourceWithStreamingResponse(self._export.html) + + @cached_property + def image(self) -> ImageResourceWithStreamingResponse: + return ImageResourceWithStreamingResponse(self._export.image) + + @cached_property + def pdf(self) -> PdfResourceWithStreamingResponse: + return PdfResourceWithStreamingResponse(self._export.pdf) + + @cached_property + def zip(self) -> ZipResourceWithStreamingResponse: + return ZipResourceWithStreamingResponse(self._export.zip) + + +class AsyncExportResourceWithStreamingResponse: + def __init__(self, export: AsyncExportResource) -> None: + self._export = export + + @cached_property + def html(self) -> AsyncHTMLResourceWithStreamingResponse: + return AsyncHTMLResourceWithStreamingResponse(self._export.html) + + @cached_property + def image(self) -> AsyncImageResourceWithStreamingResponse: + return AsyncImageResourceWithStreamingResponse(self._export.image) + + @cached_property + def pdf(self) -> AsyncPdfResourceWithStreamingResponse: + return AsyncPdfResourceWithStreamingResponse(self._export.pdf) + + @cached_property + def zip(self) -> AsyncZipResourceWithStreamingResponse: + return AsyncZipResourceWithStreamingResponse(self._export.zip) diff --git a/src/unlayer/resources/export/html.py b/src/unlayer/resources/export/html.py new file mode 100644 index 0000000..12c3164 --- /dev/null +++ b/src/unlayer/resources/export/html.py @@ -0,0 +1,173 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import httpx + +from ..._types import Body, Query, Headers, NotGiven, not_given +from ..._utils import maybe_transform, async_maybe_transform +from ..._compat import cached_property +from ..._resource import SyncAPIResource, AsyncAPIResource +from ..._response import ( + to_raw_response_wrapper, + to_streamed_response_wrapper, + async_to_raw_response_wrapper, + async_to_streamed_response_wrapper, +) +from ..._base_client import make_request_options +from ...types.export import html_retrieve_params +from ...types.export.html_retrieve_response import HTMLRetrieveResponse + +__all__ = ["HTMLResource", "AsyncHTMLResource"] + + +class HTMLResource(SyncAPIResource): + @cached_property + def with_raw_response(self) -> HTMLResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/stainless-sdks/unlayer-python#accessing-raw-response-data-eg-headers + """ + return HTMLResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> HTMLResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/stainless-sdks/unlayer-python#with_streaming_response + """ + return HTMLResourceWithStreamingResponse(self) + + def retrieve( + self, + *, + project_id: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> HTMLRetrieveResponse: + """ + Export design to HTML. + + Args: + project_id: The project ID + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._get( + "/export/v3/html", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform({"project_id": project_id}, html_retrieve_params.HTMLRetrieveParams), + ), + cast_to=HTMLRetrieveResponse, + ) + + +class AsyncHTMLResource(AsyncAPIResource): + @cached_property + def with_raw_response(self) -> AsyncHTMLResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/stainless-sdks/unlayer-python#accessing-raw-response-data-eg-headers + """ + return AsyncHTMLResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncHTMLResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/stainless-sdks/unlayer-python#with_streaming_response + """ + return AsyncHTMLResourceWithStreamingResponse(self) + + async def retrieve( + self, + *, + project_id: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> HTMLRetrieveResponse: + """ + Export design to HTML. + + Args: + project_id: The project ID + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return await self._get( + "/export/v3/html", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=await async_maybe_transform({"project_id": project_id}, html_retrieve_params.HTMLRetrieveParams), + ), + cast_to=HTMLRetrieveResponse, + ) + + +class HTMLResourceWithRawResponse: + def __init__(self, html: HTMLResource) -> None: + self._html = html + + self.retrieve = to_raw_response_wrapper( + html.retrieve, + ) + + +class AsyncHTMLResourceWithRawResponse: + def __init__(self, html: AsyncHTMLResource) -> None: + self._html = html + + self.retrieve = async_to_raw_response_wrapper( + html.retrieve, + ) + + +class HTMLResourceWithStreamingResponse: + def __init__(self, html: HTMLResource) -> None: + self._html = html + + self.retrieve = to_streamed_response_wrapper( + html.retrieve, + ) + + +class AsyncHTMLResourceWithStreamingResponse: + def __init__(self, html: AsyncHTMLResource) -> None: + self._html = html + + self.retrieve = async_to_streamed_response_wrapper( + html.retrieve, + ) diff --git a/src/unlayer/resources/export/image.py b/src/unlayer/resources/export/image.py new file mode 100644 index 0000000..98db152 --- /dev/null +++ b/src/unlayer/resources/export/image.py @@ -0,0 +1,175 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import httpx + +from ..._types import Body, Query, Headers, NotGiven, not_given +from ..._utils import maybe_transform, async_maybe_transform +from ..._compat import cached_property +from ..._resource import SyncAPIResource, AsyncAPIResource +from ..._response import ( + to_raw_response_wrapper, + to_streamed_response_wrapper, + async_to_raw_response_wrapper, + async_to_streamed_response_wrapper, +) +from ..._base_client import make_request_options +from ...types.export import image_retrieve_params +from ...types.export.image_retrieve_response import ImageRetrieveResponse + +__all__ = ["ImageResource", "AsyncImageResource"] + + +class ImageResource(SyncAPIResource): + @cached_property + def with_raw_response(self) -> ImageResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/stainless-sdks/unlayer-python#accessing-raw-response-data-eg-headers + """ + return ImageResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> ImageResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/stainless-sdks/unlayer-python#with_streaming_response + """ + return ImageResourceWithStreamingResponse(self) + + def retrieve( + self, + *, + project_id: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> ImageRetrieveResponse: + """ + Export design to image. + + Args: + project_id: The project ID + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._get( + "/export/v3/image", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform({"project_id": project_id}, image_retrieve_params.ImageRetrieveParams), + ), + cast_to=ImageRetrieveResponse, + ) + + +class AsyncImageResource(AsyncAPIResource): + @cached_property + def with_raw_response(self) -> AsyncImageResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/stainless-sdks/unlayer-python#accessing-raw-response-data-eg-headers + """ + return AsyncImageResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncImageResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/stainless-sdks/unlayer-python#with_streaming_response + """ + return AsyncImageResourceWithStreamingResponse(self) + + async def retrieve( + self, + *, + project_id: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> ImageRetrieveResponse: + """ + Export design to image. + + Args: + project_id: The project ID + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return await self._get( + "/export/v3/image", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=await async_maybe_transform( + {"project_id": project_id}, image_retrieve_params.ImageRetrieveParams + ), + ), + cast_to=ImageRetrieveResponse, + ) + + +class ImageResourceWithRawResponse: + def __init__(self, image: ImageResource) -> None: + self._image = image + + self.retrieve = to_raw_response_wrapper( + image.retrieve, + ) + + +class AsyncImageResourceWithRawResponse: + def __init__(self, image: AsyncImageResource) -> None: + self._image = image + + self.retrieve = async_to_raw_response_wrapper( + image.retrieve, + ) + + +class ImageResourceWithStreamingResponse: + def __init__(self, image: ImageResource) -> None: + self._image = image + + self.retrieve = to_streamed_response_wrapper( + image.retrieve, + ) + + +class AsyncImageResourceWithStreamingResponse: + def __init__(self, image: AsyncImageResource) -> None: + self._image = image + + self.retrieve = async_to_streamed_response_wrapper( + image.retrieve, + ) diff --git a/src/unlayer/resources/export/pdf.py b/src/unlayer/resources/export/pdf.py new file mode 100644 index 0000000..17dd60b --- /dev/null +++ b/src/unlayer/resources/export/pdf.py @@ -0,0 +1,173 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import httpx + +from ..._types import Body, Query, Headers, NotGiven, not_given +from ..._utils import maybe_transform, async_maybe_transform +from ..._compat import cached_property +from ..._resource import SyncAPIResource, AsyncAPIResource +from ..._response import ( + to_raw_response_wrapper, + to_streamed_response_wrapper, + async_to_raw_response_wrapper, + async_to_streamed_response_wrapper, +) +from ..._base_client import make_request_options +from ...types.export import pdf_retrieve_params +from ...types.export.pdf_retrieve_response import PdfRetrieveResponse + +__all__ = ["PdfResource", "AsyncPdfResource"] + + +class PdfResource(SyncAPIResource): + @cached_property + def with_raw_response(self) -> PdfResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/stainless-sdks/unlayer-python#accessing-raw-response-data-eg-headers + """ + return PdfResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> PdfResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/stainless-sdks/unlayer-python#with_streaming_response + """ + return PdfResourceWithStreamingResponse(self) + + def retrieve( + self, + *, + project_id: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> PdfRetrieveResponse: + """ + Export design to PDF. + + Args: + project_id: The project ID + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._get( + "/export/v3/pdf", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform({"project_id": project_id}, pdf_retrieve_params.PdfRetrieveParams), + ), + cast_to=PdfRetrieveResponse, + ) + + +class AsyncPdfResource(AsyncAPIResource): + @cached_property + def with_raw_response(self) -> AsyncPdfResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/stainless-sdks/unlayer-python#accessing-raw-response-data-eg-headers + """ + return AsyncPdfResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncPdfResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/stainless-sdks/unlayer-python#with_streaming_response + """ + return AsyncPdfResourceWithStreamingResponse(self) + + async def retrieve( + self, + *, + project_id: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> PdfRetrieveResponse: + """ + Export design to PDF. + + Args: + project_id: The project ID + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return await self._get( + "/export/v3/pdf", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=await async_maybe_transform({"project_id": project_id}, pdf_retrieve_params.PdfRetrieveParams), + ), + cast_to=PdfRetrieveResponse, + ) + + +class PdfResourceWithRawResponse: + def __init__(self, pdf: PdfResource) -> None: + self._pdf = pdf + + self.retrieve = to_raw_response_wrapper( + pdf.retrieve, + ) + + +class AsyncPdfResourceWithRawResponse: + def __init__(self, pdf: AsyncPdfResource) -> None: + self._pdf = pdf + + self.retrieve = async_to_raw_response_wrapper( + pdf.retrieve, + ) + + +class PdfResourceWithStreamingResponse: + def __init__(self, pdf: PdfResource) -> None: + self._pdf = pdf + + self.retrieve = to_streamed_response_wrapper( + pdf.retrieve, + ) + + +class AsyncPdfResourceWithStreamingResponse: + def __init__(self, pdf: AsyncPdfResource) -> None: + self._pdf = pdf + + self.retrieve = async_to_streamed_response_wrapper( + pdf.retrieve, + ) diff --git a/src/unlayer/resources/export/zip.py b/src/unlayer/resources/export/zip.py new file mode 100644 index 0000000..cf42fc0 --- /dev/null +++ b/src/unlayer/resources/export/zip.py @@ -0,0 +1,173 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import httpx + +from ..._types import Body, Query, Headers, NotGiven, not_given +from ..._utils import maybe_transform, async_maybe_transform +from ..._compat import cached_property +from ..._resource import SyncAPIResource, AsyncAPIResource +from ..._response import ( + to_raw_response_wrapper, + to_streamed_response_wrapper, + async_to_raw_response_wrapper, + async_to_streamed_response_wrapper, +) +from ..._base_client import make_request_options +from ...types.export import zip_retrieve_params +from ...types.export.zip_retrieve_response import ZipRetrieveResponse + +__all__ = ["ZipResource", "AsyncZipResource"] + + +class ZipResource(SyncAPIResource): + @cached_property + def with_raw_response(self) -> ZipResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/stainless-sdks/unlayer-python#accessing-raw-response-data-eg-headers + """ + return ZipResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> ZipResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/stainless-sdks/unlayer-python#with_streaming_response + """ + return ZipResourceWithStreamingResponse(self) + + def retrieve( + self, + *, + project_id: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> ZipRetrieveResponse: + """ + Export design to ZIP archive. + + Args: + project_id: The project ID + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._get( + "/export/v3/zip", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform({"project_id": project_id}, zip_retrieve_params.ZipRetrieveParams), + ), + cast_to=ZipRetrieveResponse, + ) + + +class AsyncZipResource(AsyncAPIResource): + @cached_property + def with_raw_response(self) -> AsyncZipResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/stainless-sdks/unlayer-python#accessing-raw-response-data-eg-headers + """ + return AsyncZipResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncZipResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/stainless-sdks/unlayer-python#with_streaming_response + """ + return AsyncZipResourceWithStreamingResponse(self) + + async def retrieve( + self, + *, + project_id: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> ZipRetrieveResponse: + """ + Export design to ZIP archive. + + Args: + project_id: The project ID + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return await self._get( + "/export/v3/zip", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=await async_maybe_transform({"project_id": project_id}, zip_retrieve_params.ZipRetrieveParams), + ), + cast_to=ZipRetrieveResponse, + ) + + +class ZipResourceWithRawResponse: + def __init__(self, zip: ZipResource) -> None: + self._zip = zip + + self.retrieve = to_raw_response_wrapper( + zip.retrieve, + ) + + +class AsyncZipResourceWithRawResponse: + def __init__(self, zip: AsyncZipResource) -> None: + self._zip = zip + + self.retrieve = async_to_raw_response_wrapper( + zip.retrieve, + ) + + +class ZipResourceWithStreamingResponse: + def __init__(self, zip: ZipResource) -> None: + self._zip = zip + + self.retrieve = to_streamed_response_wrapper( + zip.retrieve, + ) + + +class AsyncZipResourceWithStreamingResponse: + def __init__(self, zip: AsyncZipResource) -> None: + self._zip = zip + + self.retrieve = async_to_streamed_response_wrapper( + zip.retrieve, + ) diff --git a/src/unlayer/resources/pages/__init__.py b/src/unlayer/resources/pages/__init__.py new file mode 100644 index 0000000..5cc5e81 --- /dev/null +++ b/src/unlayer/resources/pages/__init__.py @@ -0,0 +1,33 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from .pages import ( + PagesResource, + AsyncPagesResource, + PagesResourceWithRawResponse, + AsyncPagesResourceWithRawResponse, + PagesResourceWithStreamingResponse, + AsyncPagesResourceWithStreamingResponse, +) +from .render import ( + RenderResource, + AsyncRenderResource, + RenderResourceWithRawResponse, + AsyncRenderResourceWithRawResponse, + RenderResourceWithStreamingResponse, + AsyncRenderResourceWithStreamingResponse, +) + +__all__ = [ + "RenderResource", + "AsyncRenderResource", + "RenderResourceWithRawResponse", + "AsyncRenderResourceWithRawResponse", + "RenderResourceWithStreamingResponse", + "AsyncRenderResourceWithStreamingResponse", + "PagesResource", + "AsyncPagesResource", + "PagesResourceWithRawResponse", + "AsyncPagesResourceWithRawResponse", + "PagesResourceWithStreamingResponse", + "AsyncPagesResourceWithStreamingResponse", +] diff --git a/src/unlayer/resources/pages/pages.py b/src/unlayer/resources/pages/pages.py new file mode 100644 index 0000000..4fa0583 --- /dev/null +++ b/src/unlayer/resources/pages/pages.py @@ -0,0 +1,102 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from .render import ( + RenderResource, + AsyncRenderResource, + RenderResourceWithRawResponse, + AsyncRenderResourceWithRawResponse, + RenderResourceWithStreamingResponse, + AsyncRenderResourceWithStreamingResponse, +) +from ..._compat import cached_property +from ..._resource import SyncAPIResource, AsyncAPIResource + +__all__ = ["PagesResource", "AsyncPagesResource"] + + +class PagesResource(SyncAPIResource): + @cached_property + def render(self) -> RenderResource: + return RenderResource(self._client) + + @cached_property + def with_raw_response(self) -> PagesResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/stainless-sdks/unlayer-python#accessing-raw-response-data-eg-headers + """ + return PagesResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> PagesResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/stainless-sdks/unlayer-python#with_streaming_response + """ + return PagesResourceWithStreamingResponse(self) + + +class AsyncPagesResource(AsyncAPIResource): + @cached_property + def render(self) -> AsyncRenderResource: + return AsyncRenderResource(self._client) + + @cached_property + def with_raw_response(self) -> AsyncPagesResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/stainless-sdks/unlayer-python#accessing-raw-response-data-eg-headers + """ + return AsyncPagesResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncPagesResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/stainless-sdks/unlayer-python#with_streaming_response + """ + return AsyncPagesResourceWithStreamingResponse(self) + + +class PagesResourceWithRawResponse: + def __init__(self, pages: PagesResource) -> None: + self._pages = pages + + @cached_property + def render(self) -> RenderResourceWithRawResponse: + return RenderResourceWithRawResponse(self._pages.render) + + +class AsyncPagesResourceWithRawResponse: + def __init__(self, pages: AsyncPagesResource) -> None: + self._pages = pages + + @cached_property + def render(self) -> AsyncRenderResourceWithRawResponse: + return AsyncRenderResourceWithRawResponse(self._pages.render) + + +class PagesResourceWithStreamingResponse: + def __init__(self, pages: PagesResource) -> None: + self._pages = pages + + @cached_property + def render(self) -> RenderResourceWithStreamingResponse: + return RenderResourceWithStreamingResponse(self._pages.render) + + +class AsyncPagesResourceWithStreamingResponse: + def __init__(self, pages: AsyncPagesResource) -> None: + self._pages = pages + + @cached_property + def render(self) -> AsyncRenderResourceWithStreamingResponse: + return AsyncRenderResourceWithStreamingResponse(self._pages.render) diff --git a/src/unlayer/resources/pages.py b/src/unlayer/resources/pages/render.py similarity index 65% rename from src/unlayer/resources/pages.py rename to src/unlayer/resources/pages/render.py index 349e648..9ad9b9f 100644 --- a/src/unlayer/resources/pages.py +++ b/src/unlayer/resources/pages/render.py @@ -6,44 +6,44 @@ import httpx -from ..types import page_render_create_params -from .._types import Body, Omit, Query, Headers, NotGiven, omit, not_given -from .._utils import maybe_transform, async_maybe_transform -from .._compat import cached_property -from .._resource import SyncAPIResource, AsyncAPIResource -from .._response import ( +from ..._types import Body, Omit, Query, Headers, NotGiven, omit, not_given +from ..._utils import maybe_transform, async_maybe_transform +from ..._compat import cached_property +from ..._resource import SyncAPIResource, AsyncAPIResource +from ..._response import ( to_raw_response_wrapper, to_streamed_response_wrapper, async_to_raw_response_wrapper, async_to_streamed_response_wrapper, ) -from .._base_client import make_request_options -from ..types.page_render_create_response import PageRenderCreateResponse +from ...types.pages import render_create_params +from ..._base_client import make_request_options +from ...types.pages.render_create_response import RenderCreateResponse -__all__ = ["PagesResource", "AsyncPagesResource"] +__all__ = ["RenderResource", "AsyncRenderResource"] -class PagesResource(SyncAPIResource): +class RenderResource(SyncAPIResource): @cached_property - def with_raw_response(self) -> PagesResourceWithRawResponse: + def with_raw_response(self) -> RenderResourceWithRawResponse: """ This property can be used as a prefix for any HTTP method call to return the raw response object instead of the parsed content. For more information, see https://www.github.com/stainless-sdks/unlayer-python#accessing-raw-response-data-eg-headers """ - return PagesResourceWithRawResponse(self) + return RenderResourceWithRawResponse(self) @cached_property - def with_streaming_response(self) -> PagesResourceWithStreamingResponse: + def with_streaming_response(self) -> RenderResourceWithStreamingResponse: """ An alternative to `.with_raw_response` that doesn't eagerly read the response body. For more information, see https://www.github.com/stainless-sdks/unlayer-python#with_streaming_response """ - return PagesResourceWithStreamingResponse(self) + return RenderResourceWithStreamingResponse(self) - def render_create( + def create( self, *, project_id: str, @@ -55,7 +55,7 @@ def render_create( extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> PageRenderCreateResponse: + ) -> RenderCreateResponse: """ Convert page design JSON to HTML with optional merge tags. @@ -81,40 +81,40 @@ def render_create( "design": design, "merge_tags": merge_tags, }, - page_render_create_params.PageRenderCreateParams, + render_create_params.RenderCreateParams, ), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout, - query=maybe_transform({"project_id": project_id}, page_render_create_params.PageRenderCreateParams), + query=maybe_transform({"project_id": project_id}, render_create_params.RenderCreateParams), ), - cast_to=PageRenderCreateResponse, + cast_to=RenderCreateResponse, ) -class AsyncPagesResource(AsyncAPIResource): +class AsyncRenderResource(AsyncAPIResource): @cached_property - def with_raw_response(self) -> AsyncPagesResourceWithRawResponse: + def with_raw_response(self) -> AsyncRenderResourceWithRawResponse: """ This property can be used as a prefix for any HTTP method call to return the raw response object instead of the parsed content. For more information, see https://www.github.com/stainless-sdks/unlayer-python#accessing-raw-response-data-eg-headers """ - return AsyncPagesResourceWithRawResponse(self) + return AsyncRenderResourceWithRawResponse(self) @cached_property - def with_streaming_response(self) -> AsyncPagesResourceWithStreamingResponse: + def with_streaming_response(self) -> AsyncRenderResourceWithStreamingResponse: """ An alternative to `.with_raw_response` that doesn't eagerly read the response body. For more information, see https://www.github.com/stainless-sdks/unlayer-python#with_streaming_response """ - return AsyncPagesResourceWithStreamingResponse(self) + return AsyncRenderResourceWithStreamingResponse(self) - async def render_create( + async def create( self, *, project_id: str, @@ -126,7 +126,7 @@ async def render_create( extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> PageRenderCreateResponse: + ) -> RenderCreateResponse: """ Convert page design JSON to HTML with optional merge tags. @@ -152,52 +152,50 @@ async def render_create( "design": design, "merge_tags": merge_tags, }, - page_render_create_params.PageRenderCreateParams, + render_create_params.RenderCreateParams, ), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout, - query=await async_maybe_transform( - {"project_id": project_id}, page_render_create_params.PageRenderCreateParams - ), + query=await async_maybe_transform({"project_id": project_id}, render_create_params.RenderCreateParams), ), - cast_to=PageRenderCreateResponse, + cast_to=RenderCreateResponse, ) -class PagesResourceWithRawResponse: - def __init__(self, pages: PagesResource) -> None: - self._pages = pages +class RenderResourceWithRawResponse: + def __init__(self, render: RenderResource) -> None: + self._render = render - self.render_create = to_raw_response_wrapper( - pages.render_create, + self.create = to_raw_response_wrapper( + render.create, ) -class AsyncPagesResourceWithRawResponse: - def __init__(self, pages: AsyncPagesResource) -> None: - self._pages = pages +class AsyncRenderResourceWithRawResponse: + def __init__(self, render: AsyncRenderResource) -> None: + self._render = render - self.render_create = async_to_raw_response_wrapper( - pages.render_create, + self.create = async_to_raw_response_wrapper( + render.create, ) -class PagesResourceWithStreamingResponse: - def __init__(self, pages: PagesResource) -> None: - self._pages = pages +class RenderResourceWithStreamingResponse: + def __init__(self, render: RenderResource) -> None: + self._render = render - self.render_create = to_streamed_response_wrapper( - pages.render_create, + self.create = to_streamed_response_wrapper( + render.create, ) -class AsyncPagesResourceWithStreamingResponse: - def __init__(self, pages: AsyncPagesResource) -> None: - self._pages = pages +class AsyncRenderResourceWithStreamingResponse: + def __init__(self, render: AsyncRenderResource) -> None: + self._render = render - self.render_create = async_to_streamed_response_wrapper( - pages.render_create, + self.create = async_to_streamed_response_wrapper( + render.create, ) diff --git a/src/unlayer/resources/project.py b/src/unlayer/resources/project.py deleted file mode 100644 index 694a37d..0000000 --- a/src/unlayer/resources/project.py +++ /dev/null @@ -1,1278 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing_extensions import Literal - -import httpx - -from ..types import ( - project_current_list_params, - project_domains_list_params, - project_domains_create_params, - project_domains_update_params, - project_templates_list_params, - project_templates_create_params, - project_templates_update_params, -) -from .._types import Body, Omit, Query, Headers, NoneType, NotGiven, omit, not_given -from .._utils import maybe_transform, async_maybe_transform -from .._compat import cached_property -from .._resource import SyncAPIResource, AsyncAPIResource -from .._response import ( - to_raw_response_wrapper, - to_streamed_response_wrapper, - async_to_raw_response_wrapper, - async_to_streamed_response_wrapper, -) -from ..pagination import SyncCursorPage, AsyncCursorPage -from .._base_client import AsyncPaginator, make_request_options -from ..types.project_current_list_response import ProjectCurrentListResponse -from ..types.project_domains_list_response import ProjectDomainsListResponse -from ..types.project_domains_create_response import ProjectDomainsCreateResponse -from ..types.project_domains_update_response import ProjectDomainsUpdateResponse -from ..types.project_templates_list_response import ProjectTemplatesListResponse -from ..types.project_workspaces_list_response import ProjectWorkspacesListResponse -from ..types.project_domains_retrieve_response import ProjectDomainsRetrieveResponse -from ..types.project_templates_create_response import ProjectTemplatesCreateResponse -from ..types.project_templates_update_response import ProjectTemplatesUpdateResponse -from ..types.project_templates_retrieve_response import ProjectTemplatesRetrieveResponse -from ..types.project_workspaces_retrieve_response import ProjectWorkspacesRetrieveResponse - -__all__ = ["ProjectResource", "AsyncProjectResource"] - - -class ProjectResource(SyncAPIResource): - @cached_property - def with_raw_response(self) -> ProjectResourceWithRawResponse: - """ - This property can be used as a prefix for any HTTP method call to return - the raw response object instead of the parsed content. - - For more information, see https://www.github.com/stainless-sdks/unlayer-python#accessing-raw-response-data-eg-headers - """ - return ProjectResourceWithRawResponse(self) - - @cached_property - def with_streaming_response(self) -> ProjectResourceWithStreamingResponse: - """ - An alternative to `.with_raw_response` that doesn't eagerly read the response body. - - For more information, see https://www.github.com/stainless-sdks/unlayer-python#with_streaming_response - """ - return ProjectResourceWithStreamingResponse(self) - - def current_list( - self, - *, - project_id: str, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> ProjectCurrentListResponse: - """ - Get project details for the specified project. - - Args: - project_id: The project ID - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - return self._get( - "/project/v1/current", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query=maybe_transform({"project_id": project_id}, project_current_list_params.ProjectCurrentListParams), - ), - cast_to=ProjectCurrentListResponse, - ) - - def domains_create( - self, - *, - project_id: str, - domain: str, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> ProjectDomainsCreateResponse: - """ - Add a new domain to the project. - - Args: - project_id: The project ID - - domain: Domain name to add - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - return self._post( - "/project/v1/domains", - body=maybe_transform({"domain": domain}, project_domains_create_params.ProjectDomainsCreateParams), - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query=maybe_transform( - {"project_id": project_id}, project_domains_create_params.ProjectDomainsCreateParams - ), - ), - cast_to=ProjectDomainsCreateResponse, - ) - - def domains_delete( - self, - id: str, - *, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> None: - """ - Remove domain from project. - - Args: - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - if not id: - raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") - extra_headers = {"Accept": "*/*", **(extra_headers or {})} - return self._delete( - f"/project/v1/domains/{id}", - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=NoneType, - ) - - def domains_list( - self, - *, - project_id: str, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> ProjectDomainsListResponse: - """ - List all domains for the project. - - Args: - project_id: The project ID - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - return self._get( - "/project/v1/domains", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query=maybe_transform({"project_id": project_id}, project_domains_list_params.ProjectDomainsListParams), - ), - cast_to=ProjectDomainsListResponse, - ) - - def domains_retrieve( - self, - id: str, - *, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> ProjectDomainsRetrieveResponse: - """ - Get domain details by ID. - - Args: - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - if not id: - raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") - return self._get( - f"/project/v1/domains/{id}", - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=ProjectDomainsRetrieveResponse, - ) - - def domains_update( - self, - id: str, - *, - domain: str | Omit = omit, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> ProjectDomainsUpdateResponse: - """ - Update domain settings. - - Args: - domain: Updated domain name - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - if not id: - raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") - return self._put( - f"/project/v1/domains/{id}", - body=maybe_transform({"domain": domain}, project_domains_update_params.ProjectDomainsUpdateParams), - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=ProjectDomainsUpdateResponse, - ) - - def templates_create( - self, - *, - project_id: str, - name: str, - display_mode: Literal["email", "web", "document"] | Omit = omit, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> ProjectTemplatesCreateResponse: - """ - Create a new project template. - - Args: - project_id: The project ID to create the template in - - name: Template name - - display_mode: Template type/display mode - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - return self._post( - "/project/v1/templates", - body=maybe_transform( - { - "name": name, - "display_mode": display_mode, - }, - project_templates_create_params.ProjectTemplatesCreateParams, - ), - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query=maybe_transform( - {"project_id": project_id}, project_templates_create_params.ProjectTemplatesCreateParams - ), - ), - cast_to=ProjectTemplatesCreateResponse, - ) - - def templates_delete( - self, - id: str, - *, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> None: - """ - Delete project template. - - Args: - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - if not id: - raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") - extra_headers = {"Accept": "*/*", **(extra_headers or {})} - return self._delete( - f"/project/v1/templates/{id}", - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=NoneType, - ) - - def templates_list( - self, - *, - project_id: str, - cursor: str | Omit = omit, - display_mode: Literal["email", "web", "document"] | Omit = omit, - limit: int | Omit = omit, - name: str | Omit = omit, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> SyncCursorPage[ProjectTemplatesListResponse]: - """List project templates with cursor-based pagination. - - Returns templates in - descending order by update time. - - Args: - project_id: The project ID to list templates for - - cursor: Pagination cursor from previous response - - display_mode: Filter by template type - - limit: Number of templates to return (1-100) - - name: Filter by name (case-insensitive search) - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - return self._get_api_list( - "/project/v1/templates", - page=SyncCursorPage[ProjectTemplatesListResponse], - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query=maybe_transform( - { - "project_id": project_id, - "cursor": cursor, - "display_mode": display_mode, - "limit": limit, - "name": name, - }, - project_templates_list_params.ProjectTemplatesListParams, - ), - ), - model=ProjectTemplatesListResponse, - ) - - def templates_retrieve( - self, - id: str, - *, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> ProjectTemplatesRetrieveResponse: - """ - Get project template by ID. - - Args: - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - if not id: - raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") - return self._get( - f"/project/v1/templates/{id}", - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=ProjectTemplatesRetrieveResponse, - ) - - def templates_update( - self, - id: str, - *, - body: str | Omit = omit, - name: str | Omit = omit, - subject: str | Omit = omit, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> ProjectTemplatesUpdateResponse: - """ - Update project template. - - Args: - body: Updated email body content - - name: Updated template name - - subject: Updated email subject line - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - if not id: - raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") - return self._put( - f"/project/v1/templates/{id}", - body=maybe_transform( - { - "body": body, - "name": name, - "subject": subject, - }, - project_templates_update_params.ProjectTemplatesUpdateParams, - ), - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=ProjectTemplatesUpdateResponse, - ) - - def workspaces_list( - self, - *, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> ProjectWorkspacesListResponse: - """Get all workspaces accessible by the current token.""" - return self._get( - "/project/v1/workspaces", - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=ProjectWorkspacesListResponse, - ) - - def workspaces_retrieve( - self, - workspace_id: str, - *, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> ProjectWorkspacesRetrieveResponse: - """ - Get a specific workspace by ID with its projects. - - Args: - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - if not workspace_id: - raise ValueError(f"Expected a non-empty value for `workspace_id` but received {workspace_id!r}") - return self._get( - f"/project/v1/workspaces/{workspace_id}", - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=ProjectWorkspacesRetrieveResponse, - ) - - -class AsyncProjectResource(AsyncAPIResource): - @cached_property - def with_raw_response(self) -> AsyncProjectResourceWithRawResponse: - """ - This property can be used as a prefix for any HTTP method call to return - the raw response object instead of the parsed content. - - For more information, see https://www.github.com/stainless-sdks/unlayer-python#accessing-raw-response-data-eg-headers - """ - return AsyncProjectResourceWithRawResponse(self) - - @cached_property - def with_streaming_response(self) -> AsyncProjectResourceWithStreamingResponse: - """ - An alternative to `.with_raw_response` that doesn't eagerly read the response body. - - For more information, see https://www.github.com/stainless-sdks/unlayer-python#with_streaming_response - """ - return AsyncProjectResourceWithStreamingResponse(self) - - async def current_list( - self, - *, - project_id: str, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> ProjectCurrentListResponse: - """ - Get project details for the specified project. - - Args: - project_id: The project ID - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - return await self._get( - "/project/v1/current", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query=await async_maybe_transform( - {"project_id": project_id}, project_current_list_params.ProjectCurrentListParams - ), - ), - cast_to=ProjectCurrentListResponse, - ) - - async def domains_create( - self, - *, - project_id: str, - domain: str, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> ProjectDomainsCreateResponse: - """ - Add a new domain to the project. - - Args: - project_id: The project ID - - domain: Domain name to add - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - return await self._post( - "/project/v1/domains", - body=await async_maybe_transform( - {"domain": domain}, project_domains_create_params.ProjectDomainsCreateParams - ), - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query=await async_maybe_transform( - {"project_id": project_id}, project_domains_create_params.ProjectDomainsCreateParams - ), - ), - cast_to=ProjectDomainsCreateResponse, - ) - - async def domains_delete( - self, - id: str, - *, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> None: - """ - Remove domain from project. - - Args: - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - if not id: - raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") - extra_headers = {"Accept": "*/*", **(extra_headers or {})} - return await self._delete( - f"/project/v1/domains/{id}", - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=NoneType, - ) - - async def domains_list( - self, - *, - project_id: str, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> ProjectDomainsListResponse: - """ - List all domains for the project. - - Args: - project_id: The project ID - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - return await self._get( - "/project/v1/domains", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query=await async_maybe_transform( - {"project_id": project_id}, project_domains_list_params.ProjectDomainsListParams - ), - ), - cast_to=ProjectDomainsListResponse, - ) - - async def domains_retrieve( - self, - id: str, - *, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> ProjectDomainsRetrieveResponse: - """ - Get domain details by ID. - - Args: - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - if not id: - raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") - return await self._get( - f"/project/v1/domains/{id}", - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=ProjectDomainsRetrieveResponse, - ) - - async def domains_update( - self, - id: str, - *, - domain: str | Omit = omit, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> ProjectDomainsUpdateResponse: - """ - Update domain settings. - - Args: - domain: Updated domain name - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - if not id: - raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") - return await self._put( - f"/project/v1/domains/{id}", - body=await async_maybe_transform( - {"domain": domain}, project_domains_update_params.ProjectDomainsUpdateParams - ), - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=ProjectDomainsUpdateResponse, - ) - - async def templates_create( - self, - *, - project_id: str, - name: str, - display_mode: Literal["email", "web", "document"] | Omit = omit, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> ProjectTemplatesCreateResponse: - """ - Create a new project template. - - Args: - project_id: The project ID to create the template in - - name: Template name - - display_mode: Template type/display mode - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - return await self._post( - "/project/v1/templates", - body=await async_maybe_transform( - { - "name": name, - "display_mode": display_mode, - }, - project_templates_create_params.ProjectTemplatesCreateParams, - ), - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query=await async_maybe_transform( - {"project_id": project_id}, project_templates_create_params.ProjectTemplatesCreateParams - ), - ), - cast_to=ProjectTemplatesCreateResponse, - ) - - async def templates_delete( - self, - id: str, - *, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> None: - """ - Delete project template. - - Args: - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - if not id: - raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") - extra_headers = {"Accept": "*/*", **(extra_headers or {})} - return await self._delete( - f"/project/v1/templates/{id}", - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=NoneType, - ) - - def templates_list( - self, - *, - project_id: str, - cursor: str | Omit = omit, - display_mode: Literal["email", "web", "document"] | Omit = omit, - limit: int | Omit = omit, - name: str | Omit = omit, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> AsyncPaginator[ProjectTemplatesListResponse, AsyncCursorPage[ProjectTemplatesListResponse]]: - """List project templates with cursor-based pagination. - - Returns templates in - descending order by update time. - - Args: - project_id: The project ID to list templates for - - cursor: Pagination cursor from previous response - - display_mode: Filter by template type - - limit: Number of templates to return (1-100) - - name: Filter by name (case-insensitive search) - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - return self._get_api_list( - "/project/v1/templates", - page=AsyncCursorPage[ProjectTemplatesListResponse], - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query=maybe_transform( - { - "project_id": project_id, - "cursor": cursor, - "display_mode": display_mode, - "limit": limit, - "name": name, - }, - project_templates_list_params.ProjectTemplatesListParams, - ), - ), - model=ProjectTemplatesListResponse, - ) - - async def templates_retrieve( - self, - id: str, - *, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> ProjectTemplatesRetrieveResponse: - """ - Get project template by ID. - - Args: - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - if not id: - raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") - return await self._get( - f"/project/v1/templates/{id}", - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=ProjectTemplatesRetrieveResponse, - ) - - async def templates_update( - self, - id: str, - *, - body: str | Omit = omit, - name: str | Omit = omit, - subject: str | Omit = omit, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> ProjectTemplatesUpdateResponse: - """ - Update project template. - - Args: - body: Updated email body content - - name: Updated template name - - subject: Updated email subject line - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - if not id: - raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") - return await self._put( - f"/project/v1/templates/{id}", - body=await async_maybe_transform( - { - "body": body, - "name": name, - "subject": subject, - }, - project_templates_update_params.ProjectTemplatesUpdateParams, - ), - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=ProjectTemplatesUpdateResponse, - ) - - async def workspaces_list( - self, - *, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> ProjectWorkspacesListResponse: - """Get all workspaces accessible by the current token.""" - return await self._get( - "/project/v1/workspaces", - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=ProjectWorkspacesListResponse, - ) - - async def workspaces_retrieve( - self, - workspace_id: str, - *, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> ProjectWorkspacesRetrieveResponse: - """ - Get a specific workspace by ID with its projects. - - Args: - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - if not workspace_id: - raise ValueError(f"Expected a non-empty value for `workspace_id` but received {workspace_id!r}") - return await self._get( - f"/project/v1/workspaces/{workspace_id}", - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=ProjectWorkspacesRetrieveResponse, - ) - - -class ProjectResourceWithRawResponse: - def __init__(self, project: ProjectResource) -> None: - self._project = project - - self.current_list = to_raw_response_wrapper( - project.current_list, - ) - self.domains_create = to_raw_response_wrapper( - project.domains_create, - ) - self.domains_delete = to_raw_response_wrapper( - project.domains_delete, - ) - self.domains_list = to_raw_response_wrapper( - project.domains_list, - ) - self.domains_retrieve = to_raw_response_wrapper( - project.domains_retrieve, - ) - self.domains_update = to_raw_response_wrapper( - project.domains_update, - ) - self.templates_create = to_raw_response_wrapper( - project.templates_create, - ) - self.templates_delete = to_raw_response_wrapper( - project.templates_delete, - ) - self.templates_list = to_raw_response_wrapper( - project.templates_list, - ) - self.templates_retrieve = to_raw_response_wrapper( - project.templates_retrieve, - ) - self.templates_update = to_raw_response_wrapper( - project.templates_update, - ) - self.workspaces_list = to_raw_response_wrapper( - project.workspaces_list, - ) - self.workspaces_retrieve = to_raw_response_wrapper( - project.workspaces_retrieve, - ) - - -class AsyncProjectResourceWithRawResponse: - def __init__(self, project: AsyncProjectResource) -> None: - self._project = project - - self.current_list = async_to_raw_response_wrapper( - project.current_list, - ) - self.domains_create = async_to_raw_response_wrapper( - project.domains_create, - ) - self.domains_delete = async_to_raw_response_wrapper( - project.domains_delete, - ) - self.domains_list = async_to_raw_response_wrapper( - project.domains_list, - ) - self.domains_retrieve = async_to_raw_response_wrapper( - project.domains_retrieve, - ) - self.domains_update = async_to_raw_response_wrapper( - project.domains_update, - ) - self.templates_create = async_to_raw_response_wrapper( - project.templates_create, - ) - self.templates_delete = async_to_raw_response_wrapper( - project.templates_delete, - ) - self.templates_list = async_to_raw_response_wrapper( - project.templates_list, - ) - self.templates_retrieve = async_to_raw_response_wrapper( - project.templates_retrieve, - ) - self.templates_update = async_to_raw_response_wrapper( - project.templates_update, - ) - self.workspaces_list = async_to_raw_response_wrapper( - project.workspaces_list, - ) - self.workspaces_retrieve = async_to_raw_response_wrapper( - project.workspaces_retrieve, - ) - - -class ProjectResourceWithStreamingResponse: - def __init__(self, project: ProjectResource) -> None: - self._project = project - - self.current_list = to_streamed_response_wrapper( - project.current_list, - ) - self.domains_create = to_streamed_response_wrapper( - project.domains_create, - ) - self.domains_delete = to_streamed_response_wrapper( - project.domains_delete, - ) - self.domains_list = to_streamed_response_wrapper( - project.domains_list, - ) - self.domains_retrieve = to_streamed_response_wrapper( - project.domains_retrieve, - ) - self.domains_update = to_streamed_response_wrapper( - project.domains_update, - ) - self.templates_create = to_streamed_response_wrapper( - project.templates_create, - ) - self.templates_delete = to_streamed_response_wrapper( - project.templates_delete, - ) - self.templates_list = to_streamed_response_wrapper( - project.templates_list, - ) - self.templates_retrieve = to_streamed_response_wrapper( - project.templates_retrieve, - ) - self.templates_update = to_streamed_response_wrapper( - project.templates_update, - ) - self.workspaces_list = to_streamed_response_wrapper( - project.workspaces_list, - ) - self.workspaces_retrieve = to_streamed_response_wrapper( - project.workspaces_retrieve, - ) - - -class AsyncProjectResourceWithStreamingResponse: - def __init__(self, project: AsyncProjectResource) -> None: - self._project = project - - self.current_list = async_to_streamed_response_wrapper( - project.current_list, - ) - self.domains_create = async_to_streamed_response_wrapper( - project.domains_create, - ) - self.domains_delete = async_to_streamed_response_wrapper( - project.domains_delete, - ) - self.domains_list = async_to_streamed_response_wrapper( - project.domains_list, - ) - self.domains_retrieve = async_to_streamed_response_wrapper( - project.domains_retrieve, - ) - self.domains_update = async_to_streamed_response_wrapper( - project.domains_update, - ) - self.templates_create = async_to_streamed_response_wrapper( - project.templates_create, - ) - self.templates_delete = async_to_streamed_response_wrapper( - project.templates_delete, - ) - self.templates_list = async_to_streamed_response_wrapper( - project.templates_list, - ) - self.templates_retrieve = async_to_streamed_response_wrapper( - project.templates_retrieve, - ) - self.templates_update = async_to_streamed_response_wrapper( - project.templates_update, - ) - self.workspaces_list = async_to_streamed_response_wrapper( - project.workspaces_list, - ) - self.workspaces_retrieve = async_to_streamed_response_wrapper( - project.workspaces_retrieve, - ) diff --git a/src/unlayer/resources/project/__init__.py b/src/unlayer/resources/project/__init__.py new file mode 100644 index 0000000..076105e --- /dev/null +++ b/src/unlayer/resources/project/__init__.py @@ -0,0 +1,75 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from .current import ( + CurrentResource, + AsyncCurrentResource, + CurrentResourceWithRawResponse, + AsyncCurrentResourceWithRawResponse, + CurrentResourceWithStreamingResponse, + AsyncCurrentResourceWithStreamingResponse, +) +from .domains import ( + DomainsResource, + AsyncDomainsResource, + DomainsResourceWithRawResponse, + AsyncDomainsResourceWithRawResponse, + DomainsResourceWithStreamingResponse, + AsyncDomainsResourceWithStreamingResponse, +) +from .project import ( + ProjectResource, + AsyncProjectResource, + ProjectResourceWithRawResponse, + AsyncProjectResourceWithRawResponse, + ProjectResourceWithStreamingResponse, + AsyncProjectResourceWithStreamingResponse, +) +from .templates import ( + TemplatesResource, + AsyncTemplatesResource, + TemplatesResourceWithRawResponse, + AsyncTemplatesResourceWithRawResponse, + TemplatesResourceWithStreamingResponse, + AsyncTemplatesResourceWithStreamingResponse, +) +from .workspaces import ( + WorkspacesResource, + AsyncWorkspacesResource, + WorkspacesResourceWithRawResponse, + AsyncWorkspacesResourceWithRawResponse, + WorkspacesResourceWithStreamingResponse, + AsyncWorkspacesResourceWithStreamingResponse, +) + +__all__ = [ + "CurrentResource", + "AsyncCurrentResource", + "CurrentResourceWithRawResponse", + "AsyncCurrentResourceWithRawResponse", + "CurrentResourceWithStreamingResponse", + "AsyncCurrentResourceWithStreamingResponse", + "DomainsResource", + "AsyncDomainsResource", + "DomainsResourceWithRawResponse", + "AsyncDomainsResourceWithRawResponse", + "DomainsResourceWithStreamingResponse", + "AsyncDomainsResourceWithStreamingResponse", + "TemplatesResource", + "AsyncTemplatesResource", + "TemplatesResourceWithRawResponse", + "AsyncTemplatesResourceWithRawResponse", + "TemplatesResourceWithStreamingResponse", + "AsyncTemplatesResourceWithStreamingResponse", + "WorkspacesResource", + "AsyncWorkspacesResource", + "WorkspacesResourceWithRawResponse", + "AsyncWorkspacesResourceWithRawResponse", + "WorkspacesResourceWithStreamingResponse", + "AsyncWorkspacesResourceWithStreamingResponse", + "ProjectResource", + "AsyncProjectResource", + "ProjectResourceWithRawResponse", + "AsyncProjectResourceWithRawResponse", + "ProjectResourceWithStreamingResponse", + "AsyncProjectResourceWithStreamingResponse", +] diff --git a/src/unlayer/resources/project/current.py b/src/unlayer/resources/project/current.py new file mode 100644 index 0000000..29fd2c1 --- /dev/null +++ b/src/unlayer/resources/project/current.py @@ -0,0 +1,175 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import httpx + +from ..._types import Body, Query, Headers, NotGiven, not_given +from ..._utils import maybe_transform, async_maybe_transform +from ..._compat import cached_property +from ..._resource import SyncAPIResource, AsyncAPIResource +from ..._response import ( + to_raw_response_wrapper, + to_streamed_response_wrapper, + async_to_raw_response_wrapper, + async_to_streamed_response_wrapper, +) +from ..._base_client import make_request_options +from ...types.project import current_retrieve_params +from ...types.project.current_retrieve_response import CurrentRetrieveResponse + +__all__ = ["CurrentResource", "AsyncCurrentResource"] + + +class CurrentResource(SyncAPIResource): + @cached_property + def with_raw_response(self) -> CurrentResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/stainless-sdks/unlayer-python#accessing-raw-response-data-eg-headers + """ + return CurrentResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> CurrentResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/stainless-sdks/unlayer-python#with_streaming_response + """ + return CurrentResourceWithStreamingResponse(self) + + def retrieve( + self, + *, + project_id: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> CurrentRetrieveResponse: + """ + Get project details for the specified project. + + Args: + project_id: The project ID + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._get( + "/project/v1/current", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform({"project_id": project_id}, current_retrieve_params.CurrentRetrieveParams), + ), + cast_to=CurrentRetrieveResponse, + ) + + +class AsyncCurrentResource(AsyncAPIResource): + @cached_property + def with_raw_response(self) -> AsyncCurrentResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/stainless-sdks/unlayer-python#accessing-raw-response-data-eg-headers + """ + return AsyncCurrentResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncCurrentResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/stainless-sdks/unlayer-python#with_streaming_response + """ + return AsyncCurrentResourceWithStreamingResponse(self) + + async def retrieve( + self, + *, + project_id: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> CurrentRetrieveResponse: + """ + Get project details for the specified project. + + Args: + project_id: The project ID + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return await self._get( + "/project/v1/current", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=await async_maybe_transform( + {"project_id": project_id}, current_retrieve_params.CurrentRetrieveParams + ), + ), + cast_to=CurrentRetrieveResponse, + ) + + +class CurrentResourceWithRawResponse: + def __init__(self, current: CurrentResource) -> None: + self._current = current + + self.retrieve = to_raw_response_wrapper( + current.retrieve, + ) + + +class AsyncCurrentResourceWithRawResponse: + def __init__(self, current: AsyncCurrentResource) -> None: + self._current = current + + self.retrieve = async_to_raw_response_wrapper( + current.retrieve, + ) + + +class CurrentResourceWithStreamingResponse: + def __init__(self, current: CurrentResource) -> None: + self._current = current + + self.retrieve = to_streamed_response_wrapper( + current.retrieve, + ) + + +class AsyncCurrentResourceWithStreamingResponse: + def __init__(self, current: AsyncCurrentResource) -> None: + self._current = current + + self.retrieve = async_to_streamed_response_wrapper( + current.retrieve, + ) diff --git a/src/unlayer/resources/project/domains.py b/src/unlayer/resources/project/domains.py new file mode 100644 index 0000000..844d474 --- /dev/null +++ b/src/unlayer/resources/project/domains.py @@ -0,0 +1,514 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import httpx + +from ..._types import Body, Omit, Query, Headers, NoneType, NotGiven, omit, not_given +from ..._utils import maybe_transform, async_maybe_transform +from ..._compat import cached_property +from ..._resource import SyncAPIResource, AsyncAPIResource +from ..._response import ( + to_raw_response_wrapper, + to_streamed_response_wrapper, + async_to_raw_response_wrapper, + async_to_streamed_response_wrapper, +) +from ..._base_client import make_request_options +from ...types.project import domain_list_params, domain_create_params, domain_update_params +from ...types.project.domain_list_response import DomainListResponse +from ...types.project.domain_create_response import DomainCreateResponse +from ...types.project.domain_update_response import DomainUpdateResponse +from ...types.project.domain_retrieve_response import DomainRetrieveResponse + +__all__ = ["DomainsResource", "AsyncDomainsResource"] + + +class DomainsResource(SyncAPIResource): + @cached_property + def with_raw_response(self) -> DomainsResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/stainless-sdks/unlayer-python#accessing-raw-response-data-eg-headers + """ + return DomainsResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> DomainsResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/stainless-sdks/unlayer-python#with_streaming_response + """ + return DomainsResourceWithStreamingResponse(self) + + def create( + self, + *, + project_id: str, + domain: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> DomainCreateResponse: + """ + Add a new domain to the project. + + Args: + project_id: The project ID + + domain: Domain name to add + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._post( + "/project/v1/domains", + body=maybe_transform({"domain": domain}, domain_create_params.DomainCreateParams), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform({"project_id": project_id}, domain_create_params.DomainCreateParams), + ), + cast_to=DomainCreateResponse, + ) + + def retrieve( + self, + id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> DomainRetrieveResponse: + """ + Get domain details by ID. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not id: + raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") + return self._get( + f"/project/v1/domains/{id}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=DomainRetrieveResponse, + ) + + def update( + self, + id: str, + *, + domain: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> DomainUpdateResponse: + """ + Update domain settings. + + Args: + domain: Updated domain name + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not id: + raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") + return self._put( + f"/project/v1/domains/{id}", + body=maybe_transform({"domain": domain}, domain_update_params.DomainUpdateParams), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=DomainUpdateResponse, + ) + + def list( + self, + *, + project_id: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> DomainListResponse: + """ + List all domains for the project. + + Args: + project_id: The project ID + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._get( + "/project/v1/domains", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform({"project_id": project_id}, domain_list_params.DomainListParams), + ), + cast_to=DomainListResponse, + ) + + def delete( + self, + id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> None: + """ + Remove domain from project. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not id: + raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") + extra_headers = {"Accept": "*/*", **(extra_headers or {})} + return self._delete( + f"/project/v1/domains/{id}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=NoneType, + ) + + +class AsyncDomainsResource(AsyncAPIResource): + @cached_property + def with_raw_response(self) -> AsyncDomainsResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/stainless-sdks/unlayer-python#accessing-raw-response-data-eg-headers + """ + return AsyncDomainsResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncDomainsResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/stainless-sdks/unlayer-python#with_streaming_response + """ + return AsyncDomainsResourceWithStreamingResponse(self) + + async def create( + self, + *, + project_id: str, + domain: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> DomainCreateResponse: + """ + Add a new domain to the project. + + Args: + project_id: The project ID + + domain: Domain name to add + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return await self._post( + "/project/v1/domains", + body=await async_maybe_transform({"domain": domain}, domain_create_params.DomainCreateParams), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=await async_maybe_transform({"project_id": project_id}, domain_create_params.DomainCreateParams), + ), + cast_to=DomainCreateResponse, + ) + + async def retrieve( + self, + id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> DomainRetrieveResponse: + """ + Get domain details by ID. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not id: + raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") + return await self._get( + f"/project/v1/domains/{id}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=DomainRetrieveResponse, + ) + + async def update( + self, + id: str, + *, + domain: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> DomainUpdateResponse: + """ + Update domain settings. + + Args: + domain: Updated domain name + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not id: + raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") + return await self._put( + f"/project/v1/domains/{id}", + body=await async_maybe_transform({"domain": domain}, domain_update_params.DomainUpdateParams), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=DomainUpdateResponse, + ) + + async def list( + self, + *, + project_id: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> DomainListResponse: + """ + List all domains for the project. + + Args: + project_id: The project ID + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return await self._get( + "/project/v1/domains", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=await async_maybe_transform({"project_id": project_id}, domain_list_params.DomainListParams), + ), + cast_to=DomainListResponse, + ) + + async def delete( + self, + id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> None: + """ + Remove domain from project. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not id: + raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") + extra_headers = {"Accept": "*/*", **(extra_headers or {})} + return await self._delete( + f"/project/v1/domains/{id}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=NoneType, + ) + + +class DomainsResourceWithRawResponse: + def __init__(self, domains: DomainsResource) -> None: + self._domains = domains + + self.create = to_raw_response_wrapper( + domains.create, + ) + self.retrieve = to_raw_response_wrapper( + domains.retrieve, + ) + self.update = to_raw_response_wrapper( + domains.update, + ) + self.list = to_raw_response_wrapper( + domains.list, + ) + self.delete = to_raw_response_wrapper( + domains.delete, + ) + + +class AsyncDomainsResourceWithRawResponse: + def __init__(self, domains: AsyncDomainsResource) -> None: + self._domains = domains + + self.create = async_to_raw_response_wrapper( + domains.create, + ) + self.retrieve = async_to_raw_response_wrapper( + domains.retrieve, + ) + self.update = async_to_raw_response_wrapper( + domains.update, + ) + self.list = async_to_raw_response_wrapper( + domains.list, + ) + self.delete = async_to_raw_response_wrapper( + domains.delete, + ) + + +class DomainsResourceWithStreamingResponse: + def __init__(self, domains: DomainsResource) -> None: + self._domains = domains + + self.create = to_streamed_response_wrapper( + domains.create, + ) + self.retrieve = to_streamed_response_wrapper( + domains.retrieve, + ) + self.update = to_streamed_response_wrapper( + domains.update, + ) + self.list = to_streamed_response_wrapper( + domains.list, + ) + self.delete = to_streamed_response_wrapper( + domains.delete, + ) + + +class AsyncDomainsResourceWithStreamingResponse: + def __init__(self, domains: AsyncDomainsResource) -> None: + self._domains = domains + + self.create = async_to_streamed_response_wrapper( + domains.create, + ) + self.retrieve = async_to_streamed_response_wrapper( + domains.retrieve, + ) + self.update = async_to_streamed_response_wrapper( + domains.update, + ) + self.list = async_to_streamed_response_wrapper( + domains.list, + ) + self.delete = async_to_streamed_response_wrapper( + domains.delete, + ) diff --git a/src/unlayer/resources/project/project.py b/src/unlayer/resources/project/project.py new file mode 100644 index 0000000..3884df6 --- /dev/null +++ b/src/unlayer/resources/project/project.py @@ -0,0 +1,198 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from .current import ( + CurrentResource, + AsyncCurrentResource, + CurrentResourceWithRawResponse, + AsyncCurrentResourceWithRawResponse, + CurrentResourceWithStreamingResponse, + AsyncCurrentResourceWithStreamingResponse, +) +from .domains import ( + DomainsResource, + AsyncDomainsResource, + DomainsResourceWithRawResponse, + AsyncDomainsResourceWithRawResponse, + DomainsResourceWithStreamingResponse, + AsyncDomainsResourceWithStreamingResponse, +) +from ..._compat import cached_property +from .templates import ( + TemplatesResource, + AsyncTemplatesResource, + TemplatesResourceWithRawResponse, + AsyncTemplatesResourceWithRawResponse, + TemplatesResourceWithStreamingResponse, + AsyncTemplatesResourceWithStreamingResponse, +) +from .workspaces import ( + WorkspacesResource, + AsyncWorkspacesResource, + WorkspacesResourceWithRawResponse, + AsyncWorkspacesResourceWithRawResponse, + WorkspacesResourceWithStreamingResponse, + AsyncWorkspacesResourceWithStreamingResponse, +) +from ..._resource import SyncAPIResource, AsyncAPIResource + +__all__ = ["ProjectResource", "AsyncProjectResource"] + + +class ProjectResource(SyncAPIResource): + @cached_property + def current(self) -> CurrentResource: + return CurrentResource(self._client) + + @cached_property + def domains(self) -> DomainsResource: + return DomainsResource(self._client) + + @cached_property + def templates(self) -> TemplatesResource: + return TemplatesResource(self._client) + + @cached_property + def workspaces(self) -> WorkspacesResource: + return WorkspacesResource(self._client) + + @cached_property + def with_raw_response(self) -> ProjectResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/stainless-sdks/unlayer-python#accessing-raw-response-data-eg-headers + """ + return ProjectResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> ProjectResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/stainless-sdks/unlayer-python#with_streaming_response + """ + return ProjectResourceWithStreamingResponse(self) + + +class AsyncProjectResource(AsyncAPIResource): + @cached_property + def current(self) -> AsyncCurrentResource: + return AsyncCurrentResource(self._client) + + @cached_property + def domains(self) -> AsyncDomainsResource: + return AsyncDomainsResource(self._client) + + @cached_property + def templates(self) -> AsyncTemplatesResource: + return AsyncTemplatesResource(self._client) + + @cached_property + def workspaces(self) -> AsyncWorkspacesResource: + return AsyncWorkspacesResource(self._client) + + @cached_property + def with_raw_response(self) -> AsyncProjectResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/stainless-sdks/unlayer-python#accessing-raw-response-data-eg-headers + """ + return AsyncProjectResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncProjectResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/stainless-sdks/unlayer-python#with_streaming_response + """ + return AsyncProjectResourceWithStreamingResponse(self) + + +class ProjectResourceWithRawResponse: + def __init__(self, project: ProjectResource) -> None: + self._project = project + + @cached_property + def current(self) -> CurrentResourceWithRawResponse: + return CurrentResourceWithRawResponse(self._project.current) + + @cached_property + def domains(self) -> DomainsResourceWithRawResponse: + return DomainsResourceWithRawResponse(self._project.domains) + + @cached_property + def templates(self) -> TemplatesResourceWithRawResponse: + return TemplatesResourceWithRawResponse(self._project.templates) + + @cached_property + def workspaces(self) -> WorkspacesResourceWithRawResponse: + return WorkspacesResourceWithRawResponse(self._project.workspaces) + + +class AsyncProjectResourceWithRawResponse: + def __init__(self, project: AsyncProjectResource) -> None: + self._project = project + + @cached_property + def current(self) -> AsyncCurrentResourceWithRawResponse: + return AsyncCurrentResourceWithRawResponse(self._project.current) + + @cached_property + def domains(self) -> AsyncDomainsResourceWithRawResponse: + return AsyncDomainsResourceWithRawResponse(self._project.domains) + + @cached_property + def templates(self) -> AsyncTemplatesResourceWithRawResponse: + return AsyncTemplatesResourceWithRawResponse(self._project.templates) + + @cached_property + def workspaces(self) -> AsyncWorkspacesResourceWithRawResponse: + return AsyncWorkspacesResourceWithRawResponse(self._project.workspaces) + + +class ProjectResourceWithStreamingResponse: + def __init__(self, project: ProjectResource) -> None: + self._project = project + + @cached_property + def current(self) -> CurrentResourceWithStreamingResponse: + return CurrentResourceWithStreamingResponse(self._project.current) + + @cached_property + def domains(self) -> DomainsResourceWithStreamingResponse: + return DomainsResourceWithStreamingResponse(self._project.domains) + + @cached_property + def templates(self) -> TemplatesResourceWithStreamingResponse: + return TemplatesResourceWithStreamingResponse(self._project.templates) + + @cached_property + def workspaces(self) -> WorkspacesResourceWithStreamingResponse: + return WorkspacesResourceWithStreamingResponse(self._project.workspaces) + + +class AsyncProjectResourceWithStreamingResponse: + def __init__(self, project: AsyncProjectResource) -> None: + self._project = project + + @cached_property + def current(self) -> AsyncCurrentResourceWithStreamingResponse: + return AsyncCurrentResourceWithStreamingResponse(self._project.current) + + @cached_property + def domains(self) -> AsyncDomainsResourceWithStreamingResponse: + return AsyncDomainsResourceWithStreamingResponse(self._project.domains) + + @cached_property + def templates(self) -> AsyncTemplatesResourceWithStreamingResponse: + return AsyncTemplatesResourceWithStreamingResponse(self._project.templates) + + @cached_property + def workspaces(self) -> AsyncWorkspacesResourceWithStreamingResponse: + return AsyncWorkspacesResourceWithStreamingResponse(self._project.workspaces) diff --git a/src/unlayer/resources/project/templates.py b/src/unlayer/resources/project/templates.py new file mode 100644 index 0000000..b2978ee --- /dev/null +++ b/src/unlayer/resources/project/templates.py @@ -0,0 +1,608 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Literal + +import httpx + +from ..._types import Body, Omit, Query, Headers, NoneType, NotGiven, omit, not_given +from ..._utils import maybe_transform, async_maybe_transform +from ..._compat import cached_property +from ..._resource import SyncAPIResource, AsyncAPIResource +from ..._response import ( + to_raw_response_wrapper, + to_streamed_response_wrapper, + async_to_raw_response_wrapper, + async_to_streamed_response_wrapper, +) +from ..._base_client import make_request_options +from ...types.project import template_list_params, template_create_params, template_update_params +from ...types.project.template_list_response import TemplateListResponse +from ...types.project.template_create_response import TemplateCreateResponse +from ...types.project.template_update_response import TemplateUpdateResponse +from ...types.project.template_retrieve_response import TemplateRetrieveResponse + +__all__ = ["TemplatesResource", "AsyncTemplatesResource"] + + +class TemplatesResource(SyncAPIResource): + @cached_property + def with_raw_response(self) -> TemplatesResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/stainless-sdks/unlayer-python#accessing-raw-response-data-eg-headers + """ + return TemplatesResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> TemplatesResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/stainless-sdks/unlayer-python#with_streaming_response + """ + return TemplatesResourceWithStreamingResponse(self) + + def create( + self, + *, + project_id: str, + name: str, + display_mode: Literal["email", "web", "document"] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> TemplateCreateResponse: + """ + Create a new project template. + + Args: + project_id: The project ID to create the template in + + name: Template name + + display_mode: Template type/display mode + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._post( + "/project/v1/templates", + body=maybe_transform( + { + "name": name, + "display_mode": display_mode, + }, + template_create_params.TemplateCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform({"project_id": project_id}, template_create_params.TemplateCreateParams), + ), + cast_to=TemplateCreateResponse, + ) + + def retrieve( + self, + id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> TemplateRetrieveResponse: + """ + Get project template by ID. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not id: + raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") + return self._get( + f"/project/v1/templates/{id}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=TemplateRetrieveResponse, + ) + + def update( + self, + id: str, + *, + body: str | Omit = omit, + name: str | Omit = omit, + subject: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> TemplateUpdateResponse: + """ + Update project template. + + Args: + body: Updated email body content + + name: Updated template name + + subject: Updated email subject line + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not id: + raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") + return self._put( + f"/project/v1/templates/{id}", + body=maybe_transform( + { + "body": body, + "name": name, + "subject": subject, + }, + template_update_params.TemplateUpdateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=TemplateUpdateResponse, + ) + + def list( + self, + *, + project_id: str, + cursor: str | Omit = omit, + display_mode: Literal["email", "web", "document"] | Omit = omit, + limit: int | Omit = omit, + name: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> TemplateListResponse: + """List project templates with cursor-based pagination. + + Returns templates in + descending order by update time. + + Args: + project_id: The project ID to list templates for + + cursor: Pagination cursor from previous response + + display_mode: Filter by template type + + limit: Number of templates to return (1-100) + + name: Filter by name (case-insensitive search) + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._get( + "/project/v1/templates", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "project_id": project_id, + "cursor": cursor, + "display_mode": display_mode, + "limit": limit, + "name": name, + }, + template_list_params.TemplateListParams, + ), + ), + cast_to=TemplateListResponse, + ) + + def delete( + self, + id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> None: + """ + Delete project template. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not id: + raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") + extra_headers = {"Accept": "*/*", **(extra_headers or {})} + return self._delete( + f"/project/v1/templates/{id}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=NoneType, + ) + + +class AsyncTemplatesResource(AsyncAPIResource): + @cached_property + def with_raw_response(self) -> AsyncTemplatesResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/stainless-sdks/unlayer-python#accessing-raw-response-data-eg-headers + """ + return AsyncTemplatesResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncTemplatesResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/stainless-sdks/unlayer-python#with_streaming_response + """ + return AsyncTemplatesResourceWithStreamingResponse(self) + + async def create( + self, + *, + project_id: str, + name: str, + display_mode: Literal["email", "web", "document"] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> TemplateCreateResponse: + """ + Create a new project template. + + Args: + project_id: The project ID to create the template in + + name: Template name + + display_mode: Template type/display mode + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return await self._post( + "/project/v1/templates", + body=await async_maybe_transform( + { + "name": name, + "display_mode": display_mode, + }, + template_create_params.TemplateCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=await async_maybe_transform( + {"project_id": project_id}, template_create_params.TemplateCreateParams + ), + ), + cast_to=TemplateCreateResponse, + ) + + async def retrieve( + self, + id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> TemplateRetrieveResponse: + """ + Get project template by ID. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not id: + raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") + return await self._get( + f"/project/v1/templates/{id}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=TemplateRetrieveResponse, + ) + + async def update( + self, + id: str, + *, + body: str | Omit = omit, + name: str | Omit = omit, + subject: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> TemplateUpdateResponse: + """ + Update project template. + + Args: + body: Updated email body content + + name: Updated template name + + subject: Updated email subject line + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not id: + raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") + return await self._put( + f"/project/v1/templates/{id}", + body=await async_maybe_transform( + { + "body": body, + "name": name, + "subject": subject, + }, + template_update_params.TemplateUpdateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=TemplateUpdateResponse, + ) + + async def list( + self, + *, + project_id: str, + cursor: str | Omit = omit, + display_mode: Literal["email", "web", "document"] | Omit = omit, + limit: int | Omit = omit, + name: str | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> TemplateListResponse: + """List project templates with cursor-based pagination. + + Returns templates in + descending order by update time. + + Args: + project_id: The project ID to list templates for + + cursor: Pagination cursor from previous response + + display_mode: Filter by template type + + limit: Number of templates to return (1-100) + + name: Filter by name (case-insensitive search) + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return await self._get( + "/project/v1/templates", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=await async_maybe_transform( + { + "project_id": project_id, + "cursor": cursor, + "display_mode": display_mode, + "limit": limit, + "name": name, + }, + template_list_params.TemplateListParams, + ), + ), + cast_to=TemplateListResponse, + ) + + async def delete( + self, + id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> None: + """ + Delete project template. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not id: + raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") + extra_headers = {"Accept": "*/*", **(extra_headers or {})} + return await self._delete( + f"/project/v1/templates/{id}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=NoneType, + ) + + +class TemplatesResourceWithRawResponse: + def __init__(self, templates: TemplatesResource) -> None: + self._templates = templates + + self.create = to_raw_response_wrapper( + templates.create, + ) + self.retrieve = to_raw_response_wrapper( + templates.retrieve, + ) + self.update = to_raw_response_wrapper( + templates.update, + ) + self.list = to_raw_response_wrapper( + templates.list, + ) + self.delete = to_raw_response_wrapper( + templates.delete, + ) + + +class AsyncTemplatesResourceWithRawResponse: + def __init__(self, templates: AsyncTemplatesResource) -> None: + self._templates = templates + + self.create = async_to_raw_response_wrapper( + templates.create, + ) + self.retrieve = async_to_raw_response_wrapper( + templates.retrieve, + ) + self.update = async_to_raw_response_wrapper( + templates.update, + ) + self.list = async_to_raw_response_wrapper( + templates.list, + ) + self.delete = async_to_raw_response_wrapper( + templates.delete, + ) + + +class TemplatesResourceWithStreamingResponse: + def __init__(self, templates: TemplatesResource) -> None: + self._templates = templates + + self.create = to_streamed_response_wrapper( + templates.create, + ) + self.retrieve = to_streamed_response_wrapper( + templates.retrieve, + ) + self.update = to_streamed_response_wrapper( + templates.update, + ) + self.list = to_streamed_response_wrapper( + templates.list, + ) + self.delete = to_streamed_response_wrapper( + templates.delete, + ) + + +class AsyncTemplatesResourceWithStreamingResponse: + def __init__(self, templates: AsyncTemplatesResource) -> None: + self._templates = templates + + self.create = async_to_streamed_response_wrapper( + templates.create, + ) + self.retrieve = async_to_streamed_response_wrapper( + templates.retrieve, + ) + self.update = async_to_streamed_response_wrapper( + templates.update, + ) + self.list = async_to_streamed_response_wrapper( + templates.list, + ) + self.delete = async_to_streamed_response_wrapper( + templates.delete, + ) diff --git a/src/unlayer/resources/project/workspaces.py b/src/unlayer/resources/project/workspaces.py new file mode 100644 index 0000000..988c696 --- /dev/null +++ b/src/unlayer/resources/project/workspaces.py @@ -0,0 +1,214 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import httpx + +from ..._types import Body, Query, Headers, NotGiven, not_given +from ..._compat import cached_property +from ..._resource import SyncAPIResource, AsyncAPIResource +from ..._response import ( + to_raw_response_wrapper, + to_streamed_response_wrapper, + async_to_raw_response_wrapper, + async_to_streamed_response_wrapper, +) +from ..._base_client import make_request_options +from ...types.project.workspace_list_response import WorkspaceListResponse +from ...types.project.workspace_retrieve_response import WorkspaceRetrieveResponse + +__all__ = ["WorkspacesResource", "AsyncWorkspacesResource"] + + +class WorkspacesResource(SyncAPIResource): + @cached_property + def with_raw_response(self) -> WorkspacesResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/stainless-sdks/unlayer-python#accessing-raw-response-data-eg-headers + """ + return WorkspacesResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> WorkspacesResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/stainless-sdks/unlayer-python#with_streaming_response + """ + return WorkspacesResourceWithStreamingResponse(self) + + def retrieve( + self, + workspace_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> WorkspaceRetrieveResponse: + """ + Get a specific workspace by ID with its projects. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not workspace_id: + raise ValueError(f"Expected a non-empty value for `workspace_id` but received {workspace_id!r}") + return self._get( + f"/project/v1/workspaces/{workspace_id}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=WorkspaceRetrieveResponse, + ) + + def list( + self, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> WorkspaceListResponse: + """Get all workspaces accessible by the current token.""" + return self._get( + "/project/v1/workspaces", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=WorkspaceListResponse, + ) + + +class AsyncWorkspacesResource(AsyncAPIResource): + @cached_property + def with_raw_response(self) -> AsyncWorkspacesResourceWithRawResponse: + """ + This property can be used as a prefix for any HTTP method call to return + the raw response object instead of the parsed content. + + For more information, see https://www.github.com/stainless-sdks/unlayer-python#accessing-raw-response-data-eg-headers + """ + return AsyncWorkspacesResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncWorkspacesResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/stainless-sdks/unlayer-python#with_streaming_response + """ + return AsyncWorkspacesResourceWithStreamingResponse(self) + + async def retrieve( + self, + workspace_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> WorkspaceRetrieveResponse: + """ + Get a specific workspace by ID with its projects. + + Args: + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not workspace_id: + raise ValueError(f"Expected a non-empty value for `workspace_id` but received {workspace_id!r}") + return await self._get( + f"/project/v1/workspaces/{workspace_id}", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=WorkspaceRetrieveResponse, + ) + + async def list( + self, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> WorkspaceListResponse: + """Get all workspaces accessible by the current token.""" + return await self._get( + "/project/v1/workspaces", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=WorkspaceListResponse, + ) + + +class WorkspacesResourceWithRawResponse: + def __init__(self, workspaces: WorkspacesResource) -> None: + self._workspaces = workspaces + + self.retrieve = to_raw_response_wrapper( + workspaces.retrieve, + ) + self.list = to_raw_response_wrapper( + workspaces.list, + ) + + +class AsyncWorkspacesResourceWithRawResponse: + def __init__(self, workspaces: AsyncWorkspacesResource) -> None: + self._workspaces = workspaces + + self.retrieve = async_to_raw_response_wrapper( + workspaces.retrieve, + ) + self.list = async_to_raw_response_wrapper( + workspaces.list, + ) + + +class WorkspacesResourceWithStreamingResponse: + def __init__(self, workspaces: WorkspacesResource) -> None: + self._workspaces = workspaces + + self.retrieve = to_streamed_response_wrapper( + workspaces.retrieve, + ) + self.list = to_streamed_response_wrapper( + workspaces.list, + ) + + +class AsyncWorkspacesResourceWithStreamingResponse: + def __init__(self, workspaces: AsyncWorkspacesResource) -> None: + self._workspaces = workspaces + + self.retrieve = async_to_streamed_response_wrapper( + workspaces.retrieve, + ) + self.list = async_to_streamed_response_wrapper( + workspaces.list, + ) diff --git a/src/unlayer/types/__init__.py b/src/unlayer/types/__init__.py index 702bb51..7c21943 100644 --- a/src/unlayer/types/__init__.py +++ b/src/unlayer/types/__init__.py @@ -3,50 +3,6 @@ from __future__ import annotations from .email_retrieve_params import EmailRetrieveParams as EmailRetrieveParams -from .export_pdf_list_params import ExportPdfListParams as ExportPdfListParams -from .export_zip_list_params import ExportZipListParams as ExportZipListParams from .email_retrieve_response import EmailRetrieveResponse as EmailRetrieveResponse -from .export_html_list_params import ExportHTMLListParams as ExportHTMLListParams -from .email_send_create_params import EmailSendCreateParams as EmailSendCreateParams -from .export_image_list_params import ExportImageListParams as ExportImageListParams -from .export_pdf_list_response import ExportPdfListResponse as ExportPdfListResponse -from .export_zip_list_response import ExportZipListResponse as ExportZipListResponse -from .export_html_list_response import ExportHTMLListResponse as ExportHTMLListResponse -from .page_render_create_params import PageRenderCreateParams as PageRenderCreateParams -from .email_render_create_params import EmailRenderCreateParams as EmailRenderCreateParams -from .email_send_create_response import EmailSendCreateResponse as EmailSendCreateResponse -from .export_image_list_response import ExportImageListResponse as ExportImageListResponse -from .page_render_create_response import PageRenderCreateResponse as PageRenderCreateResponse -from .project_current_list_params import ProjectCurrentListParams as ProjectCurrentListParams -from .project_domains_list_params import ProjectDomainsListParams as ProjectDomainsListParams -from .email_render_create_response import EmailRenderCreateResponse as EmailRenderCreateResponse -from .project_current_list_response import ProjectCurrentListResponse as ProjectCurrentListResponse -from .project_domains_create_params import ProjectDomainsCreateParams as ProjectDomainsCreateParams -from .project_domains_list_response import ProjectDomainsListResponse as ProjectDomainsListResponse -from .project_domains_update_params import ProjectDomainsUpdateParams as ProjectDomainsUpdateParams -from .project_templates_list_params import ProjectTemplatesListParams as ProjectTemplatesListParams -from .document_generate_create_params import DocumentGenerateCreateParams as DocumentGenerateCreateParams -from .project_domains_create_response import ProjectDomainsCreateResponse as ProjectDomainsCreateResponse -from .project_domains_update_response import ProjectDomainsUpdateResponse as ProjectDomainsUpdateResponse -from .project_templates_create_params import ProjectTemplatesCreateParams as ProjectTemplatesCreateParams -from .project_templates_list_response import ProjectTemplatesListResponse as ProjectTemplatesListResponse -from .project_templates_update_params import ProjectTemplatesUpdateParams as ProjectTemplatesUpdateParams -from .project_workspaces_list_response import ProjectWorkspacesListResponse as ProjectWorkspacesListResponse -from .document_generate_create_response import DocumentGenerateCreateResponse as DocumentGenerateCreateResponse -from .project_domains_retrieve_response import ProjectDomainsRetrieveResponse as ProjectDomainsRetrieveResponse -from .project_templates_create_response import ProjectTemplatesCreateResponse as ProjectTemplatesCreateResponse -from .project_templates_update_response import ProjectTemplatesUpdateResponse as ProjectTemplatesUpdateResponse -from .document_documents_retrieve_params import DocumentDocumentsRetrieveParams as DocumentDocumentsRetrieveParams -from .email_send_template_template_params import EmailSendTemplateTemplateParams as EmailSendTemplateTemplateParams -from .project_templates_retrieve_response import ProjectTemplatesRetrieveResponse as ProjectTemplatesRetrieveResponse -from .document_documents_retrieve_response import DocumentDocumentsRetrieveResponse as DocumentDocumentsRetrieveResponse -from .project_workspaces_retrieve_response import ProjectWorkspacesRetrieveResponse as ProjectWorkspacesRetrieveResponse -from .email_send_template_template_response import ( - EmailSendTemplateTemplateResponse as EmailSendTemplateTemplateResponse, -) -from .document_generate_template_template_params import ( - DocumentGenerateTemplateTemplateParams as DocumentGenerateTemplateTemplateParams, -) -from .document_generate_template_template_response import ( - DocumentGenerateTemplateTemplateResponse as DocumentGenerateTemplateTemplateResponse, -) +from .document_retrieve_params import DocumentRetrieveParams as DocumentRetrieveParams +from .document_retrieve_response import DocumentRetrieveResponse as DocumentRetrieveResponse diff --git a/src/unlayer/types/convert/__init__.py b/src/unlayer/types/convert/__init__.py new file mode 100644 index 0000000..df3d8f6 --- /dev/null +++ b/src/unlayer/types/convert/__init__.py @@ -0,0 +1,8 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from .full_to_simple_create_params import FullToSimpleCreateParams as FullToSimpleCreateParams +from .simple_to_full_create_params import SimpleToFullCreateParams as SimpleToFullCreateParams +from .full_to_simple_create_response import FullToSimpleCreateResponse as FullToSimpleCreateResponse +from .simple_to_full_create_response import SimpleToFullCreateResponse as SimpleToFullCreateResponse diff --git a/src/unlayer/types/convert/full_to_simple_create_params.py b/src/unlayer/types/convert/full_to_simple_create_params.py new file mode 100644 index 0000000..9ee1844 --- /dev/null +++ b/src/unlayer/types/convert/full_to_simple_create_params.py @@ -0,0 +1,29 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Dict, Union +from typing_extensions import Literal, Required, Annotated, TypeAlias, TypedDict + +from ..._utils import PropertyInfo + +__all__ = ["FullToSimpleCreateParams", "Design"] + + +class FullToSimpleCreateParams(TypedDict, total=False): + design: Required[Design] + + display_mode: Annotated[Literal["email", "web", "popup", "document"], PropertyInfo(alias="displayMode")] + + include_default_values: Annotated[bool, PropertyInfo(alias="includeDefaultValues")] + + +class DesignTyped(TypedDict, total=False): + body: Required[object] + + counters: object + + schema_version: Annotated[float, PropertyInfo(alias="schemaVersion")] + + +Design: TypeAlias = Union[DesignTyped, Dict[str, object]] diff --git a/src/unlayer/types/convert/full_to_simple_create_response.py b/src/unlayer/types/convert/full_to_simple_create_response.py new file mode 100644 index 0000000..7ded325 --- /dev/null +++ b/src/unlayer/types/convert/full_to_simple_create_response.py @@ -0,0 +1,18 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Dict, Optional +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["FullToSimpleCreateResponse", "Data"] + + +class Data(BaseModel): + design: Optional[Dict[str, object]] = None + + +class FullToSimpleCreateResponse(BaseModel): + data: Optional[Data] = None + + success: Optional[Literal[True]] = None diff --git a/src/unlayer/types/convert/simple_to_full_create_params.py b/src/unlayer/types/convert/simple_to_full_create_params.py new file mode 100644 index 0000000..814afae --- /dev/null +++ b/src/unlayer/types/convert/simple_to_full_create_params.py @@ -0,0 +1,37 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Dict, Union +from typing_extensions import Literal, Required, Annotated, TypeAlias, TypedDict + +from ..._utils import PropertyInfo + +__all__ = ["SimpleToFullCreateParams", "Design", "Design_Conversion"] + + +class SimpleToFullCreateParams(TypedDict, total=False): + design: Required[Design] + + display_mode: Annotated[Literal["email", "web", "popup", "document"], PropertyInfo(alias="displayMode")] + + include_default_values: Annotated[bool, PropertyInfo(alias="includeDefaultValues")] + + +class Design_Conversion(TypedDict, total=False): + data: str + + version: float + + +class DesignTyped(TypedDict, total=False): + body: Required[object] + + _conversion: Design_Conversion + + counters: object + + schema_version: Annotated[float, PropertyInfo(alias="schemaVersion")] + + +Design: TypeAlias = Union[DesignTyped, Dict[str, object]] diff --git a/src/unlayer/types/convert/simple_to_full_create_response.py b/src/unlayer/types/convert/simple_to_full_create_response.py new file mode 100644 index 0000000..d224f92 --- /dev/null +++ b/src/unlayer/types/convert/simple_to_full_create_response.py @@ -0,0 +1,18 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import Dict, Optional +from typing_extensions import Literal + +from ..._models import BaseModel + +__all__ = ["SimpleToFullCreateResponse", "Data"] + + +class Data(BaseModel): + design: Optional[Dict[str, object]] = None + + +class SimpleToFullCreateResponse(BaseModel): + data: Optional[Data] = None + + success: Optional[Literal[True]] = None diff --git a/src/unlayer/types/document_documents_retrieve_params.py b/src/unlayer/types/document_documents_retrieve_params.py deleted file mode 100644 index a66ceb0..0000000 --- a/src/unlayer/types/document_documents_retrieve_params.py +++ /dev/null @@ -1,14 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing_extensions import Required, Annotated, TypedDict - -from .._utils import PropertyInfo - -__all__ = ["DocumentDocumentsRetrieveParams"] - - -class DocumentDocumentsRetrieveParams(TypedDict, total=False): - project_id: Required[Annotated[str, PropertyInfo(alias="projectId")]] - """The project ID""" diff --git a/src/unlayer/types/export_zip_list_params.py b/src/unlayer/types/document_retrieve_params.py similarity index 77% rename from src/unlayer/types/export_zip_list_params.py rename to src/unlayer/types/document_retrieve_params.py index bf140f9..4985fb6 100644 --- a/src/unlayer/types/export_zip_list_params.py +++ b/src/unlayer/types/document_retrieve_params.py @@ -6,9 +6,9 @@ from .._utils import PropertyInfo -__all__ = ["ExportZipListParams"] +__all__ = ["DocumentRetrieveParams"] -class ExportZipListParams(TypedDict, total=False): +class DocumentRetrieveParams(TypedDict, total=False): project_id: Required[Annotated[str, PropertyInfo(alias="projectId")]] """The project ID""" diff --git a/src/unlayer/types/document_documents_retrieve_response.py b/src/unlayer/types/document_retrieve_response.py similarity index 91% rename from src/unlayer/types/document_documents_retrieve_response.py rename to src/unlayer/types/document_retrieve_response.py index 9f5810d..25ac839 100644 --- a/src/unlayer/types/document_documents_retrieve_response.py +++ b/src/unlayer/types/document_retrieve_response.py @@ -8,7 +8,7 @@ from .._models import BaseModel -__all__ = ["DocumentDocumentsRetrieveResponse", "Data"] +__all__ = ["DocumentRetrieveResponse", "Data"] class Data(BaseModel): @@ -37,5 +37,5 @@ class Data(BaseModel): """Current document status""" -class DocumentDocumentsRetrieveResponse(BaseModel): +class DocumentRetrieveResponse(BaseModel): data: Optional[Data] = None diff --git a/src/unlayer/types/documents/__init__.py b/src/unlayer/types/documents/__init__.py new file mode 100644 index 0000000..1797dcf --- /dev/null +++ b/src/unlayer/types/documents/__init__.py @@ -0,0 +1,8 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from .generate_create_params import GenerateCreateParams as GenerateCreateParams +from .generate_create_response import GenerateCreateResponse as GenerateCreateResponse +from .generate_template_create_params import GenerateTemplateCreateParams as GenerateTemplateCreateParams +from .generate_template_create_response import GenerateTemplateCreateResponse as GenerateTemplateCreateResponse diff --git a/src/unlayer/types/document_generate_create_params.py b/src/unlayer/types/documents/generate_create_params.py similarity index 83% rename from src/unlayer/types/document_generate_create_params.py rename to src/unlayer/types/documents/generate_create_params.py index 179a52c..fd4c4a9 100644 --- a/src/unlayer/types/document_generate_create_params.py +++ b/src/unlayer/types/documents/generate_create_params.py @@ -5,12 +5,12 @@ from typing import Dict from typing_extensions import Required, Annotated, TypedDict -from .._utils import PropertyInfo +from ..._utils import PropertyInfo -__all__ = ["DocumentGenerateCreateParams"] +__all__ = ["GenerateCreateParams"] -class DocumentGenerateCreateParams(TypedDict, total=False): +class GenerateCreateParams(TypedDict, total=False): project_id: Required[Annotated[str, PropertyInfo(alias="projectId")]] """The project ID""" diff --git a/src/unlayer/types/document_generate_create_response.py b/src/unlayer/types/documents/generate_create_response.py similarity index 82% rename from src/unlayer/types/document_generate_create_response.py rename to src/unlayer/types/documents/generate_create_response.py index 0d1de0a..62f45b3 100644 --- a/src/unlayer/types/document_generate_create_response.py +++ b/src/unlayer/types/documents/generate_create_response.py @@ -5,9 +5,9 @@ from pydantic import Field as FieldInfo -from .._models import BaseModel +from ..._models import BaseModel -__all__ = ["DocumentGenerateCreateResponse", "Data"] +__all__ = ["GenerateCreateResponse", "Data"] class Data(BaseModel): @@ -23,5 +23,5 @@ class Data(BaseModel): status: Optional[Literal["generating", "completed", "failed"]] = None -class DocumentGenerateCreateResponse(BaseModel): +class GenerateCreateResponse(BaseModel): data: Optional[Data] = None diff --git a/src/unlayer/types/document_generate_template_template_params.py b/src/unlayer/types/documents/generate_template_create_params.py similarity index 80% rename from src/unlayer/types/document_generate_template_template_params.py rename to src/unlayer/types/documents/generate_template_create_params.py index 05cae54..5d1b5c2 100644 --- a/src/unlayer/types/document_generate_template_template_params.py +++ b/src/unlayer/types/documents/generate_template_create_params.py @@ -5,12 +5,12 @@ from typing import Dict from typing_extensions import Required, Annotated, TypedDict -from .._utils import PropertyInfo +from ..._utils import PropertyInfo -__all__ = ["DocumentGenerateTemplateTemplateParams"] +__all__ = ["GenerateTemplateCreateParams"] -class DocumentGenerateTemplateTemplateParams(TypedDict, total=False): +class GenerateTemplateCreateParams(TypedDict, total=False): project_id: Required[Annotated[str, PropertyInfo(alias="projectId")]] """The project ID""" diff --git a/src/unlayer/types/document_generate_template_template_response.py b/src/unlayer/types/documents/generate_template_create_response.py similarity index 80% rename from src/unlayer/types/document_generate_template_template_response.py rename to src/unlayer/types/documents/generate_template_create_response.py index b3cfea9..efc369b 100644 --- a/src/unlayer/types/document_generate_template_template_response.py +++ b/src/unlayer/types/documents/generate_template_create_response.py @@ -5,9 +5,9 @@ from pydantic import Field as FieldInfo -from .._models import BaseModel +from ..._models import BaseModel -__all__ = ["DocumentGenerateTemplateTemplateResponse", "Data"] +__all__ = ["GenerateTemplateCreateResponse", "Data"] class Data(BaseModel): @@ -23,5 +23,5 @@ class Data(BaseModel): status: Optional[Literal["generating", "completed", "failed"]] = None -class DocumentGenerateTemplateTemplateResponse(BaseModel): +class GenerateTemplateCreateResponse(BaseModel): data: Optional[Data] = None diff --git a/src/unlayer/types/emails/__init__.py b/src/unlayer/types/emails/__init__.py new file mode 100644 index 0000000..ee72bf7 --- /dev/null +++ b/src/unlayer/types/emails/__init__.py @@ -0,0 +1,10 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from .send_create_params import SendCreateParams as SendCreateParams +from .render_create_params import RenderCreateParams as RenderCreateParams +from .send_create_response import SendCreateResponse as SendCreateResponse +from .render_create_response import RenderCreateResponse as RenderCreateResponse +from .send_template_create_params import SendTemplateCreateParams as SendTemplateCreateParams +from .send_template_create_response import SendTemplateCreateResponse as SendTemplateCreateResponse diff --git a/src/unlayer/types/page_render_create_params.py b/src/unlayer/types/emails/render_create_params.py similarity index 80% rename from src/unlayer/types/page_render_create_params.py rename to src/unlayer/types/emails/render_create_params.py index afe7f65..476e8c2 100644 --- a/src/unlayer/types/page_render_create_params.py +++ b/src/unlayer/types/emails/render_create_params.py @@ -5,12 +5,12 @@ from typing import Dict from typing_extensions import Required, Annotated, TypedDict -from .._utils import PropertyInfo +from ..._utils import PropertyInfo -__all__ = ["PageRenderCreateParams"] +__all__ = ["RenderCreateParams"] -class PageRenderCreateParams(TypedDict, total=False): +class RenderCreateParams(TypedDict, total=False): project_id: Required[Annotated[str, PropertyInfo(alias="projectId")]] """The project ID""" diff --git a/src/unlayer/types/page_render_create_response.py b/src/unlayer/types/emails/render_create_response.py similarity index 66% rename from src/unlayer/types/page_render_create_response.py rename to src/unlayer/types/emails/render_create_response.py index bf22f78..280635d 100644 --- a/src/unlayer/types/page_render_create_response.py +++ b/src/unlayer/types/emails/render_create_response.py @@ -2,9 +2,9 @@ from typing import Optional -from .._models import BaseModel +from ..._models import BaseModel -__all__ = ["PageRenderCreateResponse", "Data"] +__all__ = ["RenderCreateResponse", "Data"] class Data(BaseModel): @@ -12,5 +12,5 @@ class Data(BaseModel): """Rendered HTML content""" -class PageRenderCreateResponse(BaseModel): +class RenderCreateResponse(BaseModel): data: Optional[Data] = None diff --git a/src/unlayer/types/email_send_create_params.py b/src/unlayer/types/emails/send_create_params.py similarity index 84% rename from src/unlayer/types/email_send_create_params.py rename to src/unlayer/types/emails/send_create_params.py index 161e747..4e2c35a 100644 --- a/src/unlayer/types/email_send_create_params.py +++ b/src/unlayer/types/emails/send_create_params.py @@ -5,12 +5,12 @@ from typing import Dict from typing_extensions import Required, Annotated, TypedDict -from .._utils import PropertyInfo +from ..._utils import PropertyInfo -__all__ = ["EmailSendCreateParams"] +__all__ = ["SendCreateParams"] -class EmailSendCreateParams(TypedDict, total=False): +class SendCreateParams(TypedDict, total=False): project_id: Required[Annotated[str, PropertyInfo(alias="projectId")]] """The project ID""" diff --git a/src/unlayer/types/email_send_create_response.py b/src/unlayer/types/emails/send_create_response.py similarity index 78% rename from src/unlayer/types/email_send_create_response.py rename to src/unlayer/types/emails/send_create_response.py index 0a6e1d0..ec8826a 100644 --- a/src/unlayer/types/email_send_create_response.py +++ b/src/unlayer/types/emails/send_create_response.py @@ -5,9 +5,9 @@ from pydantic import Field as FieldInfo -from .._models import BaseModel +from ..._models import BaseModel -__all__ = ["EmailSendCreateResponse", "Data"] +__all__ = ["SendCreateResponse", "Data"] class Data(BaseModel): @@ -17,5 +17,5 @@ class Data(BaseModel): status: Optional[Literal["sent", "queued", "failed"]] = None -class EmailSendCreateResponse(BaseModel): +class SendCreateResponse(BaseModel): data: Optional[Data] = None diff --git a/src/unlayer/types/email_send_template_template_params.py b/src/unlayer/types/emails/send_template_create_params.py similarity index 83% rename from src/unlayer/types/email_send_template_template_params.py rename to src/unlayer/types/emails/send_template_create_params.py index ba4ae42..a5eaa22 100644 --- a/src/unlayer/types/email_send_template_template_params.py +++ b/src/unlayer/types/emails/send_template_create_params.py @@ -5,12 +5,12 @@ from typing import Dict from typing_extensions import Required, Annotated, TypedDict -from .._utils import PropertyInfo +from ..._utils import PropertyInfo -__all__ = ["EmailSendTemplateTemplateParams"] +__all__ = ["SendTemplateCreateParams"] -class EmailSendTemplateTemplateParams(TypedDict, total=False): +class SendTemplateCreateParams(TypedDict, total=False): project_id: Required[Annotated[str, PropertyInfo(alias="projectId")]] """The project ID""" diff --git a/src/unlayer/types/email_send_template_template_response.py b/src/unlayer/types/emails/send_template_create_response.py similarity index 75% rename from src/unlayer/types/email_send_template_template_response.py rename to src/unlayer/types/emails/send_template_create_response.py index 166d1d5..4206737 100644 --- a/src/unlayer/types/email_send_template_template_response.py +++ b/src/unlayer/types/emails/send_template_create_response.py @@ -5,9 +5,9 @@ from pydantic import Field as FieldInfo -from .._models import BaseModel +from ..._models import BaseModel -__all__ = ["EmailSendTemplateTemplateResponse", "Data"] +__all__ = ["SendTemplateCreateResponse", "Data"] class Data(BaseModel): @@ -17,5 +17,5 @@ class Data(BaseModel): status: Optional[Literal["sent", "queued", "failed"]] = None -class EmailSendTemplateTemplateResponse(BaseModel): +class SendTemplateCreateResponse(BaseModel): data: Optional[Data] = None diff --git a/src/unlayer/types/export/__init__.py b/src/unlayer/types/export/__init__.py new file mode 100644 index 0000000..a6ed49c --- /dev/null +++ b/src/unlayer/types/export/__init__.py @@ -0,0 +1,12 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from .pdf_retrieve_params import PdfRetrieveParams as PdfRetrieveParams +from .zip_retrieve_params import ZipRetrieveParams as ZipRetrieveParams +from .html_retrieve_params import HTMLRetrieveParams as HTMLRetrieveParams +from .image_retrieve_params import ImageRetrieveParams as ImageRetrieveParams +from .pdf_retrieve_response import PdfRetrieveResponse as PdfRetrieveResponse +from .zip_retrieve_response import ZipRetrieveResponse as ZipRetrieveResponse +from .html_retrieve_response import HTMLRetrieveResponse as HTMLRetrieveResponse +from .image_retrieve_response import ImageRetrieveResponse as ImageRetrieveResponse diff --git a/src/unlayer/types/export_pdf_list_params.py b/src/unlayer/types/export/html_retrieve_params.py similarity index 70% rename from src/unlayer/types/export_pdf_list_params.py rename to src/unlayer/types/export/html_retrieve_params.py index 1975940..df7d2ec 100644 --- a/src/unlayer/types/export_pdf_list_params.py +++ b/src/unlayer/types/export/html_retrieve_params.py @@ -4,11 +4,11 @@ from typing_extensions import Required, Annotated, TypedDict -from .._utils import PropertyInfo +from ..._utils import PropertyInfo -__all__ = ["ExportPdfListParams"] +__all__ = ["HTMLRetrieveParams"] -class ExportPdfListParams(TypedDict, total=False): +class HTMLRetrieveParams(TypedDict, total=False): project_id: Required[Annotated[str, PropertyInfo(alias="projectId")]] """The project ID""" diff --git a/src/unlayer/types/export_zip_list_response.py b/src/unlayer/types/export/html_retrieve_response.py similarity index 64% rename from src/unlayer/types/export_zip_list_response.py rename to src/unlayer/types/export/html_retrieve_response.py index d1e07f9..d20cbf3 100644 --- a/src/unlayer/types/export_zip_list_response.py +++ b/src/unlayer/types/export/html_retrieve_response.py @@ -2,14 +2,14 @@ from typing import Optional -from .._models import BaseModel +from ..._models import BaseModel -__all__ = ["ExportZipListResponse", "Data"] +__all__ = ["HTMLRetrieveResponse", "Data"] class Data(BaseModel): success: Optional[bool] = None -class ExportZipListResponse(BaseModel): +class HTMLRetrieveResponse(BaseModel): data: Optional[Data] = None diff --git a/src/unlayer/types/export_html_list_params.py b/src/unlayer/types/export/image_retrieve_params.py similarity index 70% rename from src/unlayer/types/export_html_list_params.py rename to src/unlayer/types/export/image_retrieve_params.py index d9f8019..737083e 100644 --- a/src/unlayer/types/export_html_list_params.py +++ b/src/unlayer/types/export/image_retrieve_params.py @@ -4,11 +4,11 @@ from typing_extensions import Required, Annotated, TypedDict -from .._utils import PropertyInfo +from ..._utils import PropertyInfo -__all__ = ["ExportHTMLListParams"] +__all__ = ["ImageRetrieveParams"] -class ExportHTMLListParams(TypedDict, total=False): +class ImageRetrieveParams(TypedDict, total=False): project_id: Required[Annotated[str, PropertyInfo(alias="projectId")]] """The project ID""" diff --git a/src/unlayer/types/export_html_list_response.py b/src/unlayer/types/export/image_retrieve_response.py similarity index 64% rename from src/unlayer/types/export_html_list_response.py rename to src/unlayer/types/export/image_retrieve_response.py index c1de57c..66b5cbb 100644 --- a/src/unlayer/types/export_html_list_response.py +++ b/src/unlayer/types/export/image_retrieve_response.py @@ -2,14 +2,14 @@ from typing import Optional -from .._models import BaseModel +from ..._models import BaseModel -__all__ = ["ExportHTMLListResponse", "Data"] +__all__ = ["ImageRetrieveResponse", "Data"] class Data(BaseModel): success: Optional[bool] = None -class ExportHTMLListResponse(BaseModel): +class ImageRetrieveResponse(BaseModel): data: Optional[Data] = None diff --git a/src/unlayer/types/export_image_list_params.py b/src/unlayer/types/export/pdf_retrieve_params.py similarity index 70% rename from src/unlayer/types/export_image_list_params.py rename to src/unlayer/types/export/pdf_retrieve_params.py index 0b93da0..4f048f7 100644 --- a/src/unlayer/types/export_image_list_params.py +++ b/src/unlayer/types/export/pdf_retrieve_params.py @@ -4,11 +4,11 @@ from typing_extensions import Required, Annotated, TypedDict -from .._utils import PropertyInfo +from ..._utils import PropertyInfo -__all__ = ["ExportImageListParams"] +__all__ = ["PdfRetrieveParams"] -class ExportImageListParams(TypedDict, total=False): +class PdfRetrieveParams(TypedDict, total=False): project_id: Required[Annotated[str, PropertyInfo(alias="projectId")]] """The project ID""" diff --git a/src/unlayer/types/export_pdf_list_response.py b/src/unlayer/types/export/pdf_retrieve_response.py similarity index 64% rename from src/unlayer/types/export_pdf_list_response.py rename to src/unlayer/types/export/pdf_retrieve_response.py index 1a96402..3d538fe 100644 --- a/src/unlayer/types/export_pdf_list_response.py +++ b/src/unlayer/types/export/pdf_retrieve_response.py @@ -2,14 +2,14 @@ from typing import Optional -from .._models import BaseModel +from ..._models import BaseModel -__all__ = ["ExportPdfListResponse", "Data"] +__all__ = ["PdfRetrieveResponse", "Data"] class Data(BaseModel): success: Optional[bool] = None -class ExportPdfListResponse(BaseModel): +class PdfRetrieveResponse(BaseModel): data: Optional[Data] = None diff --git a/src/unlayer/types/export/zip_retrieve_params.py b/src/unlayer/types/export/zip_retrieve_params.py new file mode 100644 index 0000000..d5f0c83 --- /dev/null +++ b/src/unlayer/types/export/zip_retrieve_params.py @@ -0,0 +1,14 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Required, Annotated, TypedDict + +from ..._utils import PropertyInfo + +__all__ = ["ZipRetrieveParams"] + + +class ZipRetrieveParams(TypedDict, total=False): + project_id: Required[Annotated[str, PropertyInfo(alias="projectId")]] + """The project ID""" diff --git a/src/unlayer/types/export_image_list_response.py b/src/unlayer/types/export/zip_retrieve_response.py similarity index 63% rename from src/unlayer/types/export_image_list_response.py rename to src/unlayer/types/export/zip_retrieve_response.py index a856217..61409b7 100644 --- a/src/unlayer/types/export_image_list_response.py +++ b/src/unlayer/types/export/zip_retrieve_response.py @@ -2,14 +2,14 @@ from typing import Optional -from .._models import BaseModel +from ..._models import BaseModel -__all__ = ["ExportImageListResponse", "Data"] +__all__ = ["ZipRetrieveResponse", "Data"] class Data(BaseModel): success: Optional[bool] = None -class ExportImageListResponse(BaseModel): +class ZipRetrieveResponse(BaseModel): data: Optional[Data] = None diff --git a/src/unlayer/types/pages/__init__.py b/src/unlayer/types/pages/__init__.py new file mode 100644 index 0000000..51096d8 --- /dev/null +++ b/src/unlayer/types/pages/__init__.py @@ -0,0 +1,6 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from .render_create_params import RenderCreateParams as RenderCreateParams +from .render_create_response import RenderCreateResponse as RenderCreateResponse diff --git a/src/unlayer/types/email_render_create_params.py b/src/unlayer/types/pages/render_create_params.py similarity index 80% rename from src/unlayer/types/email_render_create_params.py rename to src/unlayer/types/pages/render_create_params.py index 5d37c88..476e8c2 100644 --- a/src/unlayer/types/email_render_create_params.py +++ b/src/unlayer/types/pages/render_create_params.py @@ -5,12 +5,12 @@ from typing import Dict from typing_extensions import Required, Annotated, TypedDict -from .._utils import PropertyInfo +from ..._utils import PropertyInfo -__all__ = ["EmailRenderCreateParams"] +__all__ = ["RenderCreateParams"] -class EmailRenderCreateParams(TypedDict, total=False): +class RenderCreateParams(TypedDict, total=False): project_id: Required[Annotated[str, PropertyInfo(alias="projectId")]] """The project ID""" diff --git a/src/unlayer/types/email_render_create_response.py b/src/unlayer/types/pages/render_create_response.py similarity index 65% rename from src/unlayer/types/email_render_create_response.py rename to src/unlayer/types/pages/render_create_response.py index b974796..280635d 100644 --- a/src/unlayer/types/email_render_create_response.py +++ b/src/unlayer/types/pages/render_create_response.py @@ -2,9 +2,9 @@ from typing import Optional -from .._models import BaseModel +from ..._models import BaseModel -__all__ = ["EmailRenderCreateResponse", "Data"] +__all__ = ["RenderCreateResponse", "Data"] class Data(BaseModel): @@ -12,5 +12,5 @@ class Data(BaseModel): """Rendered HTML content""" -class EmailRenderCreateResponse(BaseModel): +class RenderCreateResponse(BaseModel): data: Optional[Data] = None diff --git a/src/unlayer/types/project/__init__.py b/src/unlayer/types/project/__init__.py new file mode 100644 index 0000000..a7ec1d6 --- /dev/null +++ b/src/unlayer/types/project/__init__.py @@ -0,0 +1,22 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from .domain_list_params import DomainListParams as DomainListParams +from .domain_create_params import DomainCreateParams as DomainCreateParams +from .domain_list_response import DomainListResponse as DomainListResponse +from .domain_update_params import DomainUpdateParams as DomainUpdateParams +from .template_list_params import TemplateListParams as TemplateListParams +from .domain_create_response import DomainCreateResponse as DomainCreateResponse +from .domain_update_response import DomainUpdateResponse as DomainUpdateResponse +from .template_create_params import TemplateCreateParams as TemplateCreateParams +from .template_list_response import TemplateListResponse as TemplateListResponse +from .template_update_params import TemplateUpdateParams as TemplateUpdateParams +from .current_retrieve_params import CurrentRetrieveParams as CurrentRetrieveParams +from .workspace_list_response import WorkspaceListResponse as WorkspaceListResponse +from .domain_retrieve_response import DomainRetrieveResponse as DomainRetrieveResponse +from .template_create_response import TemplateCreateResponse as TemplateCreateResponse +from .template_update_response import TemplateUpdateResponse as TemplateUpdateResponse +from .current_retrieve_response import CurrentRetrieveResponse as CurrentRetrieveResponse +from .template_retrieve_response import TemplateRetrieveResponse as TemplateRetrieveResponse +from .workspace_retrieve_response import WorkspaceRetrieveResponse as WorkspaceRetrieveResponse diff --git a/src/unlayer/types/project/current_retrieve_params.py b/src/unlayer/types/project/current_retrieve_params.py new file mode 100644 index 0000000..1b39477 --- /dev/null +++ b/src/unlayer/types/project/current_retrieve_params.py @@ -0,0 +1,14 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Required, Annotated, TypedDict + +from ..._utils import PropertyInfo + +__all__ = ["CurrentRetrieveParams"] + + +class CurrentRetrieveParams(TypedDict, total=False): + project_id: Required[Annotated[str, PropertyInfo(alias="projectId")]] + """The project ID""" diff --git a/src/unlayer/types/project_current_list_response.py b/src/unlayer/types/project/current_retrieve_response.py similarity index 79% rename from src/unlayer/types/project_current_list_response.py rename to src/unlayer/types/project/current_retrieve_response.py index 17aaf94..ce9ef0e 100644 --- a/src/unlayer/types/project_current_list_response.py +++ b/src/unlayer/types/project/current_retrieve_response.py @@ -5,9 +5,9 @@ from pydantic import Field as FieldInfo -from .._models import BaseModel +from ..._models import BaseModel -__all__ = ["ProjectCurrentListResponse", "Data", "DataWorkspace"] +__all__ = ["CurrentRetrieveResponse", "Data", "DataWorkspace"] class DataWorkspace(BaseModel): @@ -28,5 +28,5 @@ class Data(BaseModel): workspace: Optional[DataWorkspace] = None -class ProjectCurrentListResponse(BaseModel): +class CurrentRetrieveResponse(BaseModel): data: Optional[Data] = None diff --git a/src/unlayer/types/project_domains_create_params.py b/src/unlayer/types/project/domain_create_params.py similarity index 72% rename from src/unlayer/types/project_domains_create_params.py rename to src/unlayer/types/project/domain_create_params.py index 2e5d607..b556ee2 100644 --- a/src/unlayer/types/project_domains_create_params.py +++ b/src/unlayer/types/project/domain_create_params.py @@ -4,12 +4,12 @@ from typing_extensions import Required, Annotated, TypedDict -from .._utils import PropertyInfo +from ..._utils import PropertyInfo -__all__ = ["ProjectDomainsCreateParams"] +__all__ = ["DomainCreateParams"] -class ProjectDomainsCreateParams(TypedDict, total=False): +class DomainCreateParams(TypedDict, total=False): project_id: Required[Annotated[str, PropertyInfo(alias="projectId")]] """The project ID""" diff --git a/src/unlayer/types/project_domains_update_response.py b/src/unlayer/types/project/domain_create_response.py similarity index 78% rename from src/unlayer/types/project_domains_update_response.py rename to src/unlayer/types/project/domain_create_response.py index 0bb78ed..9b19209 100644 --- a/src/unlayer/types/project_domains_update_response.py +++ b/src/unlayer/types/project/domain_create_response.py @@ -5,9 +5,9 @@ from pydantic import Field as FieldInfo -from .._models import BaseModel +from ..._models import BaseModel -__all__ = ["ProjectDomainsUpdateResponse", "Data"] +__all__ = ["DomainCreateResponse", "Data"] class Data(BaseModel): @@ -22,5 +22,5 @@ class Data(BaseModel): verified: Optional[bool] = None -class ProjectDomainsUpdateResponse(BaseModel): +class DomainCreateResponse(BaseModel): data: Optional[Data] = None diff --git a/src/unlayer/types/project/domain_list_params.py b/src/unlayer/types/project/domain_list_params.py new file mode 100644 index 0000000..176b6cf --- /dev/null +++ b/src/unlayer/types/project/domain_list_params.py @@ -0,0 +1,14 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Required, Annotated, TypedDict + +from ..._utils import PropertyInfo + +__all__ = ["DomainListParams"] + + +class DomainListParams(TypedDict, total=False): + project_id: Required[Annotated[str, PropertyInfo(alias="projectId")]] + """The project ID""" diff --git a/src/unlayer/types/project_domains_list_response.py b/src/unlayer/types/project/domain_list_response.py similarity index 81% rename from src/unlayer/types/project_domains_list_response.py rename to src/unlayer/types/project/domain_list_response.py index c0b0d06..4635854 100644 --- a/src/unlayer/types/project_domains_list_response.py +++ b/src/unlayer/types/project/domain_list_response.py @@ -6,9 +6,9 @@ from pydantic import Field as FieldInfo -from .._models import BaseModel +from ..._models import BaseModel -__all__ = ["ProjectDomainsListResponse", "Data"] +__all__ = ["DomainListResponse", "Data"] class Data(BaseModel): @@ -23,5 +23,5 @@ class Data(BaseModel): verified: Optional[bool] = None -class ProjectDomainsListResponse(BaseModel): +class DomainListResponse(BaseModel): data: Optional[List[Data]] = None diff --git a/src/unlayer/types/project_domains_create_response.py b/src/unlayer/types/project/domain_retrieve_response.py similarity index 78% rename from src/unlayer/types/project_domains_create_response.py rename to src/unlayer/types/project/domain_retrieve_response.py index 530a679..024743e 100644 --- a/src/unlayer/types/project_domains_create_response.py +++ b/src/unlayer/types/project/domain_retrieve_response.py @@ -5,9 +5,9 @@ from pydantic import Field as FieldInfo -from .._models import BaseModel +from ..._models import BaseModel -__all__ = ["ProjectDomainsCreateResponse", "Data"] +__all__ = ["DomainRetrieveResponse", "Data"] class Data(BaseModel): @@ -22,5 +22,5 @@ class Data(BaseModel): verified: Optional[bool] = None -class ProjectDomainsCreateResponse(BaseModel): +class DomainRetrieveResponse(BaseModel): data: Optional[Data] = None diff --git a/src/unlayer/types/project_domains_update_params.py b/src/unlayer/types/project/domain_update_params.py similarity index 68% rename from src/unlayer/types/project_domains_update_params.py rename to src/unlayer/types/project/domain_update_params.py index a9f7599..f4b6146 100644 --- a/src/unlayer/types/project_domains_update_params.py +++ b/src/unlayer/types/project/domain_update_params.py @@ -4,9 +4,9 @@ from typing_extensions import TypedDict -__all__ = ["ProjectDomainsUpdateParams"] +__all__ = ["DomainUpdateParams"] -class ProjectDomainsUpdateParams(TypedDict, total=False): +class DomainUpdateParams(TypedDict, total=False): domain: str """Updated domain name""" diff --git a/src/unlayer/types/project_domains_retrieve_response.py b/src/unlayer/types/project/domain_update_response.py similarity index 77% rename from src/unlayer/types/project_domains_retrieve_response.py rename to src/unlayer/types/project/domain_update_response.py index 1d446b7..3c13593 100644 --- a/src/unlayer/types/project_domains_retrieve_response.py +++ b/src/unlayer/types/project/domain_update_response.py @@ -5,9 +5,9 @@ from pydantic import Field as FieldInfo -from .._models import BaseModel +from ..._models import BaseModel -__all__ = ["ProjectDomainsRetrieveResponse", "Data"] +__all__ = ["DomainUpdateResponse", "Data"] class Data(BaseModel): @@ -22,5 +22,5 @@ class Data(BaseModel): verified: Optional[bool] = None -class ProjectDomainsRetrieveResponse(BaseModel): +class DomainUpdateResponse(BaseModel): data: Optional[Data] = None diff --git a/src/unlayer/types/project_templates_create_params.py b/src/unlayer/types/project/template_create_params.py similarity index 78% rename from src/unlayer/types/project_templates_create_params.py rename to src/unlayer/types/project/template_create_params.py index 8dfa036..57a358c 100644 --- a/src/unlayer/types/project_templates_create_params.py +++ b/src/unlayer/types/project/template_create_params.py @@ -4,12 +4,12 @@ from typing_extensions import Literal, Required, Annotated, TypedDict -from .._utils import PropertyInfo +from ..._utils import PropertyInfo -__all__ = ["ProjectTemplatesCreateParams"] +__all__ = ["TemplateCreateParams"] -class ProjectTemplatesCreateParams(TypedDict, total=False): +class TemplateCreateParams(TypedDict, total=False): project_id: Required[Annotated[str, PropertyInfo(alias="projectId")]] """The project ID to create the template in""" diff --git a/src/unlayer/types/project_templates_create_response.py b/src/unlayer/types/project/template_create_response.py similarity index 83% rename from src/unlayer/types/project_templates_create_response.py rename to src/unlayer/types/project/template_create_response.py index fb383ef..98ad4cd 100644 --- a/src/unlayer/types/project_templates_create_response.py +++ b/src/unlayer/types/project/template_create_response.py @@ -6,9 +6,9 @@ from pydantic import Field as FieldInfo -from .._models import BaseModel +from ..._models import BaseModel -__all__ = ["ProjectTemplatesCreateResponse", "Data"] +__all__ = ["TemplateCreateResponse", "Data"] class Data(BaseModel): @@ -26,5 +26,5 @@ class Data(BaseModel): updated_at: Optional[datetime] = FieldInfo(alias="updatedAt", default=None) -class ProjectTemplatesCreateResponse(BaseModel): +class TemplateCreateResponse(BaseModel): data: Optional[Data] = None diff --git a/src/unlayer/types/project_templates_list_params.py b/src/unlayer/types/project/template_list_params.py similarity index 83% rename from src/unlayer/types/project_templates_list_params.py rename to src/unlayer/types/project/template_list_params.py index 18e5945..ddf1d00 100644 --- a/src/unlayer/types/project_templates_list_params.py +++ b/src/unlayer/types/project/template_list_params.py @@ -4,12 +4,12 @@ from typing_extensions import Literal, Required, Annotated, TypedDict -from .._utils import PropertyInfo +from ..._utils import PropertyInfo -__all__ = ["ProjectTemplatesListParams"] +__all__ = ["TemplateListParams"] -class ProjectTemplatesListParams(TypedDict, total=False): +class TemplateListParams(TypedDict, total=False): project_id: Required[Annotated[str, PropertyInfo(alias="projectId")]] """The project ID to list templates for""" diff --git a/src/unlayer/types/project_templates_list_response.py b/src/unlayer/types/project/template_list_response.py similarity index 62% rename from src/unlayer/types/project_templates_list_response.py rename to src/unlayer/types/project/template_list_response.py index ece59f7..9d7f483 100644 --- a/src/unlayer/types/project_templates_list_response.py +++ b/src/unlayer/types/project/template_list_response.py @@ -1,17 +1,17 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. -from typing import Optional +from typing import List, Optional from datetime import datetime from typing_extensions import Literal from pydantic import Field as FieldInfo -from .._models import BaseModel +from ..._models import BaseModel -__all__ = ["ProjectTemplatesListResponse"] +__all__ = ["TemplateListResponse", "Data"] -class ProjectTemplatesListResponse(BaseModel): +class Data(BaseModel): id: Optional[str] = None """Template ID""" @@ -24,3 +24,13 @@ class ProjectTemplatesListResponse(BaseModel): """Template name""" updated_at: Optional[datetime] = FieldInfo(alias="updatedAt", default=None) + + +class TemplateListResponse(BaseModel): + data: List[Data] + + has_more: bool + """Whether there are more results after this page""" + + next_cursor: Optional[str] = None + """Cursor for the next page. Null if no more results.""" diff --git a/src/unlayer/types/project_templates_update_response.py b/src/unlayer/types/project/template_retrieve_response.py similarity index 80% rename from src/unlayer/types/project_templates_update_response.py rename to src/unlayer/types/project/template_retrieve_response.py index 565e90c..96725c0 100644 --- a/src/unlayer/types/project_templates_update_response.py +++ b/src/unlayer/types/project/template_retrieve_response.py @@ -5,9 +5,9 @@ from pydantic import Field as FieldInfo -from .._models import BaseModel +from ..._models import BaseModel -__all__ = ["ProjectTemplatesUpdateResponse", "Data"] +__all__ = ["TemplateRetrieveResponse", "Data"] class Data(BaseModel): @@ -24,5 +24,5 @@ class Data(BaseModel): updated_at: Optional[datetime] = FieldInfo(alias="updatedAt", default=None) -class ProjectTemplatesUpdateResponse(BaseModel): +class TemplateRetrieveResponse(BaseModel): data: Optional[Data] = None diff --git a/src/unlayer/types/project_templates_update_params.py b/src/unlayer/types/project/template_update_params.py similarity index 75% rename from src/unlayer/types/project_templates_update_params.py rename to src/unlayer/types/project/template_update_params.py index f539067..c1d457b 100644 --- a/src/unlayer/types/project_templates_update_params.py +++ b/src/unlayer/types/project/template_update_params.py @@ -4,10 +4,10 @@ from typing_extensions import TypedDict -__all__ = ["ProjectTemplatesUpdateParams"] +__all__ = ["TemplateUpdateParams"] -class ProjectTemplatesUpdateParams(TypedDict, total=False): +class TemplateUpdateParams(TypedDict, total=False): body: str """Updated email body content""" diff --git a/src/unlayer/types/project_templates_retrieve_response.py b/src/unlayer/types/project/template_update_response.py similarity index 79% rename from src/unlayer/types/project_templates_retrieve_response.py rename to src/unlayer/types/project/template_update_response.py index 25fd87b..3838d13 100644 --- a/src/unlayer/types/project_templates_retrieve_response.py +++ b/src/unlayer/types/project/template_update_response.py @@ -5,9 +5,9 @@ from pydantic import Field as FieldInfo -from .._models import BaseModel +from ..._models import BaseModel -__all__ = ["ProjectTemplatesRetrieveResponse", "Data"] +__all__ = ["TemplateUpdateResponse", "Data"] class Data(BaseModel): @@ -24,5 +24,5 @@ class Data(BaseModel): updated_at: Optional[datetime] = FieldInfo(alias="updatedAt", default=None) -class ProjectTemplatesRetrieveResponse(BaseModel): +class TemplateUpdateResponse(BaseModel): data: Optional[Data] = None diff --git a/src/unlayer/types/project_workspaces_list_response.py b/src/unlayer/types/project/workspace_list_response.py similarity index 65% rename from src/unlayer/types/project_workspaces_list_response.py rename to src/unlayer/types/project/workspace_list_response.py index 9df5c1a..4cbc5a6 100644 --- a/src/unlayer/types/project_workspaces_list_response.py +++ b/src/unlayer/types/project/workspace_list_response.py @@ -2,9 +2,9 @@ from typing import List, Optional -from .._models import BaseModel +from ..._models import BaseModel -__all__ = ["ProjectWorkspacesListResponse", "Data"] +__all__ = ["WorkspaceListResponse", "Data"] class Data(BaseModel): @@ -13,5 +13,5 @@ class Data(BaseModel): name: Optional[str] = None -class ProjectWorkspacesListResponse(BaseModel): +class WorkspaceListResponse(BaseModel): data: Optional[List[Data]] = None diff --git a/src/unlayer/types/project_workspaces_retrieve_response.py b/src/unlayer/types/project/workspace_retrieve_response.py similarity index 73% rename from src/unlayer/types/project_workspaces_retrieve_response.py rename to src/unlayer/types/project/workspace_retrieve_response.py index 030b0d8..a3d24bc 100644 --- a/src/unlayer/types/project_workspaces_retrieve_response.py +++ b/src/unlayer/types/project/workspace_retrieve_response.py @@ -2,9 +2,9 @@ from typing import List, Optional -from .._models import BaseModel +from ..._models import BaseModel -__all__ = ["ProjectWorkspacesRetrieveResponse", "Data", "DataProject"] +__all__ = ["WorkspaceRetrieveResponse", "Data", "DataProject"] class DataProject(BaseModel): @@ -23,5 +23,5 @@ class Data(BaseModel): projects: Optional[List[DataProject]] = None -class ProjectWorkspacesRetrieveResponse(BaseModel): +class WorkspaceRetrieveResponse(BaseModel): data: Optional[Data] = None diff --git a/src/unlayer/types/project_current_list_params.py b/src/unlayer/types/project_current_list_params.py deleted file mode 100644 index 97ea047..0000000 --- a/src/unlayer/types/project_current_list_params.py +++ /dev/null @@ -1,14 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing_extensions import Required, Annotated, TypedDict - -from .._utils import PropertyInfo - -__all__ = ["ProjectCurrentListParams"] - - -class ProjectCurrentListParams(TypedDict, total=False): - project_id: Required[Annotated[str, PropertyInfo(alias="projectId")]] - """The project ID""" diff --git a/src/unlayer/types/project_domains_list_params.py b/src/unlayer/types/project_domains_list_params.py deleted file mode 100644 index 1081c4f..0000000 --- a/src/unlayer/types/project_domains_list_params.py +++ /dev/null @@ -1,14 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing_extensions import Required, Annotated, TypedDict - -from .._utils import PropertyInfo - -__all__ = ["ProjectDomainsListParams"] - - -class ProjectDomainsListParams(TypedDict, total=False): - project_id: Required[Annotated[str, PropertyInfo(alias="projectId")]] - """The project ID""" diff --git a/tests/api_resources/convert/__init__.py b/tests/api_resources/convert/__init__.py new file mode 100644 index 0000000..fd8019a --- /dev/null +++ b/tests/api_resources/convert/__init__.py @@ -0,0 +1 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. diff --git a/tests/api_resources/convert/test_full_to_simple.py b/tests/api_resources/convert/test_full_to_simple.py new file mode 100644 index 0000000..685f0d4 --- /dev/null +++ b/tests/api_resources/convert/test_full_to_simple.py @@ -0,0 +1,112 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import os +from typing import Any, cast + +import pytest + +from unlayer import Unlayer, AsyncUnlayer +from tests.utils import assert_matches_type +from unlayer.types.convert import FullToSimpleCreateResponse + +base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") + + +class TestFullToSimple: + parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) + + @parametrize + def test_method_create(self, client: Unlayer) -> None: + full_to_simple = client.convert.full_to_simple.create( + design={"body": {}}, + ) + assert_matches_type(FullToSimpleCreateResponse, full_to_simple, path=["response"]) + + @parametrize + def test_method_create_with_all_params(self, client: Unlayer) -> None: + full_to_simple = client.convert.full_to_simple.create( + design={ + "body": {}, + "counters": {}, + "schema_version": 0, + }, + display_mode="email", + include_default_values=True, + ) + assert_matches_type(FullToSimpleCreateResponse, full_to_simple, path=["response"]) + + @parametrize + def test_raw_response_create(self, client: Unlayer) -> None: + response = client.convert.full_to_simple.with_raw_response.create( + design={"body": {}}, + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + full_to_simple = response.parse() + assert_matches_type(FullToSimpleCreateResponse, full_to_simple, path=["response"]) + + @parametrize + def test_streaming_response_create(self, client: Unlayer) -> None: + with client.convert.full_to_simple.with_streaming_response.create( + design={"body": {}}, + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + full_to_simple = response.parse() + assert_matches_type(FullToSimpleCreateResponse, full_to_simple, path=["response"]) + + assert cast(Any, response.is_closed) is True + + +class TestAsyncFullToSimple: + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) + + @parametrize + async def test_method_create(self, async_client: AsyncUnlayer) -> None: + full_to_simple = await async_client.convert.full_to_simple.create( + design={"body": {}}, + ) + assert_matches_type(FullToSimpleCreateResponse, full_to_simple, path=["response"]) + + @parametrize + async def test_method_create_with_all_params(self, async_client: AsyncUnlayer) -> None: + full_to_simple = await async_client.convert.full_to_simple.create( + design={ + "body": {}, + "counters": {}, + "schema_version": 0, + }, + display_mode="email", + include_default_values=True, + ) + assert_matches_type(FullToSimpleCreateResponse, full_to_simple, path=["response"]) + + @parametrize + async def test_raw_response_create(self, async_client: AsyncUnlayer) -> None: + response = await async_client.convert.full_to_simple.with_raw_response.create( + design={"body": {}}, + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + full_to_simple = await response.parse() + assert_matches_type(FullToSimpleCreateResponse, full_to_simple, path=["response"]) + + @parametrize + async def test_streaming_response_create(self, async_client: AsyncUnlayer) -> None: + async with async_client.convert.full_to_simple.with_streaming_response.create( + design={"body": {}}, + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + full_to_simple = await response.parse() + assert_matches_type(FullToSimpleCreateResponse, full_to_simple, path=["response"]) + + assert cast(Any, response.is_closed) is True diff --git a/tests/api_resources/convert/test_simple_to_full.py b/tests/api_resources/convert/test_simple_to_full.py new file mode 100644 index 0000000..e6d94f5 --- /dev/null +++ b/tests/api_resources/convert/test_simple_to_full.py @@ -0,0 +1,120 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import os +from typing import Any, cast + +import pytest + +from unlayer import Unlayer, AsyncUnlayer +from tests.utils import assert_matches_type +from unlayer.types.convert import SimpleToFullCreateResponse + +base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") + + +class TestSimpleToFull: + parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) + + @parametrize + def test_method_create(self, client: Unlayer) -> None: + simple_to_full = client.convert.simple_to_full.create( + design={"body": {}}, + ) + assert_matches_type(SimpleToFullCreateResponse, simple_to_full, path=["response"]) + + @parametrize + def test_method_create_with_all_params(self, client: Unlayer) -> None: + simple_to_full = client.convert.simple_to_full.create( + design={ + "body": {}, + "_conversion": { + "data": "data", + "version": 0, + }, + "counters": {}, + "schema_version": 0, + }, + display_mode="email", + include_default_values=True, + ) + assert_matches_type(SimpleToFullCreateResponse, simple_to_full, path=["response"]) + + @parametrize + def test_raw_response_create(self, client: Unlayer) -> None: + response = client.convert.simple_to_full.with_raw_response.create( + design={"body": {}}, + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + simple_to_full = response.parse() + assert_matches_type(SimpleToFullCreateResponse, simple_to_full, path=["response"]) + + @parametrize + def test_streaming_response_create(self, client: Unlayer) -> None: + with client.convert.simple_to_full.with_streaming_response.create( + design={"body": {}}, + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + simple_to_full = response.parse() + assert_matches_type(SimpleToFullCreateResponse, simple_to_full, path=["response"]) + + assert cast(Any, response.is_closed) is True + + +class TestAsyncSimpleToFull: + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) + + @parametrize + async def test_method_create(self, async_client: AsyncUnlayer) -> None: + simple_to_full = await async_client.convert.simple_to_full.create( + design={"body": {}}, + ) + assert_matches_type(SimpleToFullCreateResponse, simple_to_full, path=["response"]) + + @parametrize + async def test_method_create_with_all_params(self, async_client: AsyncUnlayer) -> None: + simple_to_full = await async_client.convert.simple_to_full.create( + design={ + "body": {}, + "_conversion": { + "data": "data", + "version": 0, + }, + "counters": {}, + "schema_version": 0, + }, + display_mode="email", + include_default_values=True, + ) + assert_matches_type(SimpleToFullCreateResponse, simple_to_full, path=["response"]) + + @parametrize + async def test_raw_response_create(self, async_client: AsyncUnlayer) -> None: + response = await async_client.convert.simple_to_full.with_raw_response.create( + design={"body": {}}, + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + simple_to_full = await response.parse() + assert_matches_type(SimpleToFullCreateResponse, simple_to_full, path=["response"]) + + @parametrize + async def test_streaming_response_create(self, async_client: AsyncUnlayer) -> None: + async with async_client.convert.simple_to_full.with_streaming_response.create( + design={"body": {}}, + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + simple_to_full = await response.parse() + assert_matches_type(SimpleToFullCreateResponse, simple_to_full, path=["response"]) + + assert cast(Any, response.is_closed) is True diff --git a/tests/api_resources/documents/__init__.py b/tests/api_resources/documents/__init__.py new file mode 100644 index 0000000..fd8019a --- /dev/null +++ b/tests/api_resources/documents/__init__.py @@ -0,0 +1 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. diff --git a/tests/api_resources/documents/test_generate.py b/tests/api_resources/documents/test_generate.py new file mode 100644 index 0000000..a76551f --- /dev/null +++ b/tests/api_resources/documents/test_generate.py @@ -0,0 +1,110 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import os +from typing import Any, cast + +import pytest + +from unlayer import Unlayer, AsyncUnlayer +from tests.utils import assert_matches_type +from unlayer.types.documents import GenerateCreateResponse + +base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") + + +class TestGenerate: + parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) + + @parametrize + def test_method_create(self, client: Unlayer) -> None: + generate = client.documents.generate.create( + project_id="projectId", + ) + assert_matches_type(GenerateCreateResponse, generate, path=["response"]) + + @parametrize + def test_method_create_with_all_params(self, client: Unlayer) -> None: + generate = client.documents.generate.create( + project_id="projectId", + design={"foo": "bar"}, + filename="filename", + html="html", + merge_tags={"foo": "string"}, + url="https://example.com", + ) + assert_matches_type(GenerateCreateResponse, generate, path=["response"]) + + @parametrize + def test_raw_response_create(self, client: Unlayer) -> None: + response = client.documents.generate.with_raw_response.create( + project_id="projectId", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + generate = response.parse() + assert_matches_type(GenerateCreateResponse, generate, path=["response"]) + + @parametrize + def test_streaming_response_create(self, client: Unlayer) -> None: + with client.documents.generate.with_streaming_response.create( + project_id="projectId", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + generate = response.parse() + assert_matches_type(GenerateCreateResponse, generate, path=["response"]) + + assert cast(Any, response.is_closed) is True + + +class TestAsyncGenerate: + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) + + @parametrize + async def test_method_create(self, async_client: AsyncUnlayer) -> None: + generate = await async_client.documents.generate.create( + project_id="projectId", + ) + assert_matches_type(GenerateCreateResponse, generate, path=["response"]) + + @parametrize + async def test_method_create_with_all_params(self, async_client: AsyncUnlayer) -> None: + generate = await async_client.documents.generate.create( + project_id="projectId", + design={"foo": "bar"}, + filename="filename", + html="html", + merge_tags={"foo": "string"}, + url="https://example.com", + ) + assert_matches_type(GenerateCreateResponse, generate, path=["response"]) + + @parametrize + async def test_raw_response_create(self, async_client: AsyncUnlayer) -> None: + response = await async_client.documents.generate.with_raw_response.create( + project_id="projectId", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + generate = await response.parse() + assert_matches_type(GenerateCreateResponse, generate, path=["response"]) + + @parametrize + async def test_streaming_response_create(self, async_client: AsyncUnlayer) -> None: + async with async_client.documents.generate.with_streaming_response.create( + project_id="projectId", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + generate = await response.parse() + assert_matches_type(GenerateCreateResponse, generate, path=["response"]) + + assert cast(Any, response.is_closed) is True diff --git a/tests/api_resources/documents/test_generate_template.py b/tests/api_resources/documents/test_generate_template.py new file mode 100644 index 0000000..28d8da9 --- /dev/null +++ b/tests/api_resources/documents/test_generate_template.py @@ -0,0 +1,112 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import os +from typing import Any, cast + +import pytest + +from unlayer import Unlayer, AsyncUnlayer +from tests.utils import assert_matches_type +from unlayer.types.documents import GenerateTemplateCreateResponse + +base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") + + +class TestGenerateTemplate: + parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) + + @parametrize + def test_method_create(self, client: Unlayer) -> None: + generate_template = client.documents.generate_template.create( + project_id="projectId", + template_id="templateId", + ) + assert_matches_type(GenerateTemplateCreateResponse, generate_template, path=["response"]) + + @parametrize + def test_method_create_with_all_params(self, client: Unlayer) -> None: + generate_template = client.documents.generate_template.create( + project_id="projectId", + template_id="templateId", + filename="filename", + merge_tags={"foo": "string"}, + ) + assert_matches_type(GenerateTemplateCreateResponse, generate_template, path=["response"]) + + @parametrize + def test_raw_response_create(self, client: Unlayer) -> None: + response = client.documents.generate_template.with_raw_response.create( + project_id="projectId", + template_id="templateId", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + generate_template = response.parse() + assert_matches_type(GenerateTemplateCreateResponse, generate_template, path=["response"]) + + @parametrize + def test_streaming_response_create(self, client: Unlayer) -> None: + with client.documents.generate_template.with_streaming_response.create( + project_id="projectId", + template_id="templateId", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + generate_template = response.parse() + assert_matches_type(GenerateTemplateCreateResponse, generate_template, path=["response"]) + + assert cast(Any, response.is_closed) is True + + +class TestAsyncGenerateTemplate: + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) + + @parametrize + async def test_method_create(self, async_client: AsyncUnlayer) -> None: + generate_template = await async_client.documents.generate_template.create( + project_id="projectId", + template_id="templateId", + ) + assert_matches_type(GenerateTemplateCreateResponse, generate_template, path=["response"]) + + @parametrize + async def test_method_create_with_all_params(self, async_client: AsyncUnlayer) -> None: + generate_template = await async_client.documents.generate_template.create( + project_id="projectId", + template_id="templateId", + filename="filename", + merge_tags={"foo": "string"}, + ) + assert_matches_type(GenerateTemplateCreateResponse, generate_template, path=["response"]) + + @parametrize + async def test_raw_response_create(self, async_client: AsyncUnlayer) -> None: + response = await async_client.documents.generate_template.with_raw_response.create( + project_id="projectId", + template_id="templateId", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + generate_template = await response.parse() + assert_matches_type(GenerateTemplateCreateResponse, generate_template, path=["response"]) + + @parametrize + async def test_streaming_response_create(self, async_client: AsyncUnlayer) -> None: + async with async_client.documents.generate_template.with_streaming_response.create( + project_id="projectId", + template_id="templateId", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + generate_template = await response.parse() + assert_matches_type(GenerateTemplateCreateResponse, generate_template, path=["response"]) + + assert cast(Any, response.is_closed) is True diff --git a/tests/api_resources/emails/__init__.py b/tests/api_resources/emails/__init__.py new file mode 100644 index 0000000..fd8019a --- /dev/null +++ b/tests/api_resources/emails/__init__.py @@ -0,0 +1 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. diff --git a/tests/api_resources/emails/test_render.py b/tests/api_resources/emails/test_render.py new file mode 100644 index 0000000..0a3ee1a --- /dev/null +++ b/tests/api_resources/emails/test_render.py @@ -0,0 +1,110 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import os +from typing import Any, cast + +import pytest + +from unlayer import Unlayer, AsyncUnlayer +from tests.utils import assert_matches_type +from unlayer.types.emails import RenderCreateResponse + +base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") + + +class TestRender: + parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) + + @parametrize + def test_method_create(self, client: Unlayer) -> None: + render = client.emails.render.create( + project_id="projectId", + design={"foo": "bar"}, + ) + assert_matches_type(RenderCreateResponse, render, path=["response"]) + + @parametrize + def test_method_create_with_all_params(self, client: Unlayer) -> None: + render = client.emails.render.create( + project_id="projectId", + design={"foo": "bar"}, + merge_tags={"foo": "string"}, + ) + assert_matches_type(RenderCreateResponse, render, path=["response"]) + + @parametrize + def test_raw_response_create(self, client: Unlayer) -> None: + response = client.emails.render.with_raw_response.create( + project_id="projectId", + design={"foo": "bar"}, + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + render = response.parse() + assert_matches_type(RenderCreateResponse, render, path=["response"]) + + @parametrize + def test_streaming_response_create(self, client: Unlayer) -> None: + with client.emails.render.with_streaming_response.create( + project_id="projectId", + design={"foo": "bar"}, + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + render = response.parse() + assert_matches_type(RenderCreateResponse, render, path=["response"]) + + assert cast(Any, response.is_closed) is True + + +class TestAsyncRender: + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) + + @parametrize + async def test_method_create(self, async_client: AsyncUnlayer) -> None: + render = await async_client.emails.render.create( + project_id="projectId", + design={"foo": "bar"}, + ) + assert_matches_type(RenderCreateResponse, render, path=["response"]) + + @parametrize + async def test_method_create_with_all_params(self, async_client: AsyncUnlayer) -> None: + render = await async_client.emails.render.create( + project_id="projectId", + design={"foo": "bar"}, + merge_tags={"foo": "string"}, + ) + assert_matches_type(RenderCreateResponse, render, path=["response"]) + + @parametrize + async def test_raw_response_create(self, async_client: AsyncUnlayer) -> None: + response = await async_client.emails.render.with_raw_response.create( + project_id="projectId", + design={"foo": "bar"}, + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + render = await response.parse() + assert_matches_type(RenderCreateResponse, render, path=["response"]) + + @parametrize + async def test_streaming_response_create(self, async_client: AsyncUnlayer) -> None: + async with async_client.emails.render.with_streaming_response.create( + project_id="projectId", + design={"foo": "bar"}, + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + render = await response.parse() + assert_matches_type(RenderCreateResponse, render, path=["response"]) + + assert cast(Any, response.is_closed) is True diff --git a/tests/api_resources/emails/test_send.py b/tests/api_resources/emails/test_send.py new file mode 100644 index 0000000..5f3f6e2 --- /dev/null +++ b/tests/api_resources/emails/test_send.py @@ -0,0 +1,116 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import os +from typing import Any, cast + +import pytest + +from unlayer import Unlayer, AsyncUnlayer +from tests.utils import assert_matches_type +from unlayer.types.emails import SendCreateResponse + +base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") + + +class TestSend: + parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) + + @parametrize + def test_method_create(self, client: Unlayer) -> None: + send = client.emails.send.create( + project_id="projectId", + to="dev@stainless.com", + ) + assert_matches_type(SendCreateResponse, send, path=["response"]) + + @parametrize + def test_method_create_with_all_params(self, client: Unlayer) -> None: + send = client.emails.send.create( + project_id="projectId", + to="dev@stainless.com", + design={"foo": "bar"}, + html="html", + merge_tags={"foo": "string"}, + subject="subject", + ) + assert_matches_type(SendCreateResponse, send, path=["response"]) + + @parametrize + def test_raw_response_create(self, client: Unlayer) -> None: + response = client.emails.send.with_raw_response.create( + project_id="projectId", + to="dev@stainless.com", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + send = response.parse() + assert_matches_type(SendCreateResponse, send, path=["response"]) + + @parametrize + def test_streaming_response_create(self, client: Unlayer) -> None: + with client.emails.send.with_streaming_response.create( + project_id="projectId", + to="dev@stainless.com", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + send = response.parse() + assert_matches_type(SendCreateResponse, send, path=["response"]) + + assert cast(Any, response.is_closed) is True + + +class TestAsyncSend: + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) + + @parametrize + async def test_method_create(self, async_client: AsyncUnlayer) -> None: + send = await async_client.emails.send.create( + project_id="projectId", + to="dev@stainless.com", + ) + assert_matches_type(SendCreateResponse, send, path=["response"]) + + @parametrize + async def test_method_create_with_all_params(self, async_client: AsyncUnlayer) -> None: + send = await async_client.emails.send.create( + project_id="projectId", + to="dev@stainless.com", + design={"foo": "bar"}, + html="html", + merge_tags={"foo": "string"}, + subject="subject", + ) + assert_matches_type(SendCreateResponse, send, path=["response"]) + + @parametrize + async def test_raw_response_create(self, async_client: AsyncUnlayer) -> None: + response = await async_client.emails.send.with_raw_response.create( + project_id="projectId", + to="dev@stainless.com", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + send = await response.parse() + assert_matches_type(SendCreateResponse, send, path=["response"]) + + @parametrize + async def test_streaming_response_create(self, async_client: AsyncUnlayer) -> None: + async with async_client.emails.send.with_streaming_response.create( + project_id="projectId", + to="dev@stainless.com", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + send = await response.parse() + assert_matches_type(SendCreateResponse, send, path=["response"]) + + assert cast(Any, response.is_closed) is True diff --git a/tests/api_resources/emails/test_send_template.py b/tests/api_resources/emails/test_send_template.py new file mode 100644 index 0000000..8ddda13 --- /dev/null +++ b/tests/api_resources/emails/test_send_template.py @@ -0,0 +1,120 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import os +from typing import Any, cast + +import pytest + +from unlayer import Unlayer, AsyncUnlayer +from tests.utils import assert_matches_type +from unlayer.types.emails import SendTemplateCreateResponse + +base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") + + +class TestSendTemplate: + parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) + + @parametrize + def test_method_create(self, client: Unlayer) -> None: + send_template = client.emails.send_template.create( + project_id="projectId", + template_id="templateId", + to="dev@stainless.com", + ) + assert_matches_type(SendTemplateCreateResponse, send_template, path=["response"]) + + @parametrize + def test_method_create_with_all_params(self, client: Unlayer) -> None: + send_template = client.emails.send_template.create( + project_id="projectId", + template_id="templateId", + to="dev@stainless.com", + merge_tags={"foo": "string"}, + subject="subject", + ) + assert_matches_type(SendTemplateCreateResponse, send_template, path=["response"]) + + @parametrize + def test_raw_response_create(self, client: Unlayer) -> None: + response = client.emails.send_template.with_raw_response.create( + project_id="projectId", + template_id="templateId", + to="dev@stainless.com", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + send_template = response.parse() + assert_matches_type(SendTemplateCreateResponse, send_template, path=["response"]) + + @parametrize + def test_streaming_response_create(self, client: Unlayer) -> None: + with client.emails.send_template.with_streaming_response.create( + project_id="projectId", + template_id="templateId", + to="dev@stainless.com", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + send_template = response.parse() + assert_matches_type(SendTemplateCreateResponse, send_template, path=["response"]) + + assert cast(Any, response.is_closed) is True + + +class TestAsyncSendTemplate: + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) + + @parametrize + async def test_method_create(self, async_client: AsyncUnlayer) -> None: + send_template = await async_client.emails.send_template.create( + project_id="projectId", + template_id="templateId", + to="dev@stainless.com", + ) + assert_matches_type(SendTemplateCreateResponse, send_template, path=["response"]) + + @parametrize + async def test_method_create_with_all_params(self, async_client: AsyncUnlayer) -> None: + send_template = await async_client.emails.send_template.create( + project_id="projectId", + template_id="templateId", + to="dev@stainless.com", + merge_tags={"foo": "string"}, + subject="subject", + ) + assert_matches_type(SendTemplateCreateResponse, send_template, path=["response"]) + + @parametrize + async def test_raw_response_create(self, async_client: AsyncUnlayer) -> None: + response = await async_client.emails.send_template.with_raw_response.create( + project_id="projectId", + template_id="templateId", + to="dev@stainless.com", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + send_template = await response.parse() + assert_matches_type(SendTemplateCreateResponse, send_template, path=["response"]) + + @parametrize + async def test_streaming_response_create(self, async_client: AsyncUnlayer) -> None: + async with async_client.emails.send_template.with_streaming_response.create( + project_id="projectId", + template_id="templateId", + to="dev@stainless.com", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + send_template = await response.parse() + assert_matches_type(SendTemplateCreateResponse, send_template, path=["response"]) + + assert cast(Any, response.is_closed) is True diff --git a/tests/api_resources/export/__init__.py b/tests/api_resources/export/__init__.py new file mode 100644 index 0000000..fd8019a --- /dev/null +++ b/tests/api_resources/export/__init__.py @@ -0,0 +1 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. diff --git a/tests/api_resources/export/test_html.py b/tests/api_resources/export/test_html.py new file mode 100644 index 0000000..a0897ea --- /dev/null +++ b/tests/api_resources/export/test_html.py @@ -0,0 +1,86 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import os +from typing import Any, cast + +import pytest + +from unlayer import Unlayer, AsyncUnlayer +from tests.utils import assert_matches_type +from unlayer.types.export import HTMLRetrieveResponse + +base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") + + +class TestHTML: + parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) + + @parametrize + def test_method_retrieve(self, client: Unlayer) -> None: + html = client.export.html.retrieve( + project_id="projectId", + ) + assert_matches_type(HTMLRetrieveResponse, html, path=["response"]) + + @parametrize + def test_raw_response_retrieve(self, client: Unlayer) -> None: + response = client.export.html.with_raw_response.retrieve( + project_id="projectId", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + html = response.parse() + assert_matches_type(HTMLRetrieveResponse, html, path=["response"]) + + @parametrize + def test_streaming_response_retrieve(self, client: Unlayer) -> None: + with client.export.html.with_streaming_response.retrieve( + project_id="projectId", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + html = response.parse() + assert_matches_type(HTMLRetrieveResponse, html, path=["response"]) + + assert cast(Any, response.is_closed) is True + + +class TestAsyncHTML: + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) + + @parametrize + async def test_method_retrieve(self, async_client: AsyncUnlayer) -> None: + html = await async_client.export.html.retrieve( + project_id="projectId", + ) + assert_matches_type(HTMLRetrieveResponse, html, path=["response"]) + + @parametrize + async def test_raw_response_retrieve(self, async_client: AsyncUnlayer) -> None: + response = await async_client.export.html.with_raw_response.retrieve( + project_id="projectId", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + html = await response.parse() + assert_matches_type(HTMLRetrieveResponse, html, path=["response"]) + + @parametrize + async def test_streaming_response_retrieve(self, async_client: AsyncUnlayer) -> None: + async with async_client.export.html.with_streaming_response.retrieve( + project_id="projectId", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + html = await response.parse() + assert_matches_type(HTMLRetrieveResponse, html, path=["response"]) + + assert cast(Any, response.is_closed) is True diff --git a/tests/api_resources/export/test_image.py b/tests/api_resources/export/test_image.py new file mode 100644 index 0000000..890db5c --- /dev/null +++ b/tests/api_resources/export/test_image.py @@ -0,0 +1,86 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import os +from typing import Any, cast + +import pytest + +from unlayer import Unlayer, AsyncUnlayer +from tests.utils import assert_matches_type +from unlayer.types.export import ImageRetrieveResponse + +base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") + + +class TestImage: + parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) + + @parametrize + def test_method_retrieve(self, client: Unlayer) -> None: + image = client.export.image.retrieve( + project_id="projectId", + ) + assert_matches_type(ImageRetrieveResponse, image, path=["response"]) + + @parametrize + def test_raw_response_retrieve(self, client: Unlayer) -> None: + response = client.export.image.with_raw_response.retrieve( + project_id="projectId", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + image = response.parse() + assert_matches_type(ImageRetrieveResponse, image, path=["response"]) + + @parametrize + def test_streaming_response_retrieve(self, client: Unlayer) -> None: + with client.export.image.with_streaming_response.retrieve( + project_id="projectId", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + image = response.parse() + assert_matches_type(ImageRetrieveResponse, image, path=["response"]) + + assert cast(Any, response.is_closed) is True + + +class TestAsyncImage: + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) + + @parametrize + async def test_method_retrieve(self, async_client: AsyncUnlayer) -> None: + image = await async_client.export.image.retrieve( + project_id="projectId", + ) + assert_matches_type(ImageRetrieveResponse, image, path=["response"]) + + @parametrize + async def test_raw_response_retrieve(self, async_client: AsyncUnlayer) -> None: + response = await async_client.export.image.with_raw_response.retrieve( + project_id="projectId", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + image = await response.parse() + assert_matches_type(ImageRetrieveResponse, image, path=["response"]) + + @parametrize + async def test_streaming_response_retrieve(self, async_client: AsyncUnlayer) -> None: + async with async_client.export.image.with_streaming_response.retrieve( + project_id="projectId", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + image = await response.parse() + assert_matches_type(ImageRetrieveResponse, image, path=["response"]) + + assert cast(Any, response.is_closed) is True diff --git a/tests/api_resources/export/test_pdf.py b/tests/api_resources/export/test_pdf.py new file mode 100644 index 0000000..e06d306 --- /dev/null +++ b/tests/api_resources/export/test_pdf.py @@ -0,0 +1,86 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import os +from typing import Any, cast + +import pytest + +from unlayer import Unlayer, AsyncUnlayer +from tests.utils import assert_matches_type +from unlayer.types.export import PdfRetrieveResponse + +base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") + + +class TestPdf: + parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) + + @parametrize + def test_method_retrieve(self, client: Unlayer) -> None: + pdf = client.export.pdf.retrieve( + project_id="projectId", + ) + assert_matches_type(PdfRetrieveResponse, pdf, path=["response"]) + + @parametrize + def test_raw_response_retrieve(self, client: Unlayer) -> None: + response = client.export.pdf.with_raw_response.retrieve( + project_id="projectId", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + pdf = response.parse() + assert_matches_type(PdfRetrieveResponse, pdf, path=["response"]) + + @parametrize + def test_streaming_response_retrieve(self, client: Unlayer) -> None: + with client.export.pdf.with_streaming_response.retrieve( + project_id="projectId", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + pdf = response.parse() + assert_matches_type(PdfRetrieveResponse, pdf, path=["response"]) + + assert cast(Any, response.is_closed) is True + + +class TestAsyncPdf: + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) + + @parametrize + async def test_method_retrieve(self, async_client: AsyncUnlayer) -> None: + pdf = await async_client.export.pdf.retrieve( + project_id="projectId", + ) + assert_matches_type(PdfRetrieveResponse, pdf, path=["response"]) + + @parametrize + async def test_raw_response_retrieve(self, async_client: AsyncUnlayer) -> None: + response = await async_client.export.pdf.with_raw_response.retrieve( + project_id="projectId", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + pdf = await response.parse() + assert_matches_type(PdfRetrieveResponse, pdf, path=["response"]) + + @parametrize + async def test_streaming_response_retrieve(self, async_client: AsyncUnlayer) -> None: + async with async_client.export.pdf.with_streaming_response.retrieve( + project_id="projectId", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + pdf = await response.parse() + assert_matches_type(PdfRetrieveResponse, pdf, path=["response"]) + + assert cast(Any, response.is_closed) is True diff --git a/tests/api_resources/export/test_zip.py b/tests/api_resources/export/test_zip.py new file mode 100644 index 0000000..ca8795c --- /dev/null +++ b/tests/api_resources/export/test_zip.py @@ -0,0 +1,86 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import os +from typing import Any, cast + +import pytest + +from unlayer import Unlayer, AsyncUnlayer +from tests.utils import assert_matches_type +from unlayer.types.export import ZipRetrieveResponse + +base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") + + +class TestZip: + parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) + + @parametrize + def test_method_retrieve(self, client: Unlayer) -> None: + zip = client.export.zip.retrieve( + project_id="projectId", + ) + assert_matches_type(ZipRetrieveResponse, zip, path=["response"]) + + @parametrize + def test_raw_response_retrieve(self, client: Unlayer) -> None: + response = client.export.zip.with_raw_response.retrieve( + project_id="projectId", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + zip = response.parse() + assert_matches_type(ZipRetrieveResponse, zip, path=["response"]) + + @parametrize + def test_streaming_response_retrieve(self, client: Unlayer) -> None: + with client.export.zip.with_streaming_response.retrieve( + project_id="projectId", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + zip = response.parse() + assert_matches_type(ZipRetrieveResponse, zip, path=["response"]) + + assert cast(Any, response.is_closed) is True + + +class TestAsyncZip: + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) + + @parametrize + async def test_method_retrieve(self, async_client: AsyncUnlayer) -> None: + zip = await async_client.export.zip.retrieve( + project_id="projectId", + ) + assert_matches_type(ZipRetrieveResponse, zip, path=["response"]) + + @parametrize + async def test_raw_response_retrieve(self, async_client: AsyncUnlayer) -> None: + response = await async_client.export.zip.with_raw_response.retrieve( + project_id="projectId", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + zip = await response.parse() + assert_matches_type(ZipRetrieveResponse, zip, path=["response"]) + + @parametrize + async def test_streaming_response_retrieve(self, async_client: AsyncUnlayer) -> None: + async with async_client.export.zip.with_streaming_response.retrieve( + project_id="projectId", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + zip = await response.parse() + assert_matches_type(ZipRetrieveResponse, zip, path=["response"]) + + assert cast(Any, response.is_closed) is True diff --git a/tests/api_resources/pages/__init__.py b/tests/api_resources/pages/__init__.py new file mode 100644 index 0000000..fd8019a --- /dev/null +++ b/tests/api_resources/pages/__init__.py @@ -0,0 +1 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. diff --git a/tests/api_resources/test_pages.py b/tests/api_resources/pages/test_render.py similarity index 51% rename from tests/api_resources/test_pages.py rename to tests/api_resources/pages/test_render.py index f98c891..5b19dee 100644 --- a/tests/api_resources/test_pages.py +++ b/tests/api_resources/pages/test_render.py @@ -9,102 +9,102 @@ from unlayer import Unlayer, AsyncUnlayer from tests.utils import assert_matches_type -from unlayer.types import PageRenderCreateResponse +from unlayer.types.pages import RenderCreateResponse base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") -class TestPages: +class TestRender: parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) @parametrize - def test_method_render_create(self, client: Unlayer) -> None: - page = client.pages.render_create( + def test_method_create(self, client: Unlayer) -> None: + render = client.pages.render.create( project_id="projectId", design={"foo": "bar"}, ) - assert_matches_type(PageRenderCreateResponse, page, path=["response"]) + assert_matches_type(RenderCreateResponse, render, path=["response"]) @parametrize - def test_method_render_create_with_all_params(self, client: Unlayer) -> None: - page = client.pages.render_create( + def test_method_create_with_all_params(self, client: Unlayer) -> None: + render = client.pages.render.create( project_id="projectId", design={"foo": "bar"}, merge_tags={"foo": "string"}, ) - assert_matches_type(PageRenderCreateResponse, page, path=["response"]) + assert_matches_type(RenderCreateResponse, render, path=["response"]) @parametrize - def test_raw_response_render_create(self, client: Unlayer) -> None: - response = client.pages.with_raw_response.render_create( + def test_raw_response_create(self, client: Unlayer) -> None: + response = client.pages.render.with_raw_response.create( project_id="projectId", design={"foo": "bar"}, ) assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" - page = response.parse() - assert_matches_type(PageRenderCreateResponse, page, path=["response"]) + render = response.parse() + assert_matches_type(RenderCreateResponse, render, path=["response"]) @parametrize - def test_streaming_response_render_create(self, client: Unlayer) -> None: - with client.pages.with_streaming_response.render_create( + def test_streaming_response_create(self, client: Unlayer) -> None: + with client.pages.render.with_streaming_response.create( project_id="projectId", design={"foo": "bar"}, ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" - page = response.parse() - assert_matches_type(PageRenderCreateResponse, page, path=["response"]) + render = response.parse() + assert_matches_type(RenderCreateResponse, render, path=["response"]) assert cast(Any, response.is_closed) is True -class TestAsyncPages: +class TestAsyncRender: parametrize = pytest.mark.parametrize( "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] ) @parametrize - async def test_method_render_create(self, async_client: AsyncUnlayer) -> None: - page = await async_client.pages.render_create( + async def test_method_create(self, async_client: AsyncUnlayer) -> None: + render = await async_client.pages.render.create( project_id="projectId", design={"foo": "bar"}, ) - assert_matches_type(PageRenderCreateResponse, page, path=["response"]) + assert_matches_type(RenderCreateResponse, render, path=["response"]) @parametrize - async def test_method_render_create_with_all_params(self, async_client: AsyncUnlayer) -> None: - page = await async_client.pages.render_create( + async def test_method_create_with_all_params(self, async_client: AsyncUnlayer) -> None: + render = await async_client.pages.render.create( project_id="projectId", design={"foo": "bar"}, merge_tags={"foo": "string"}, ) - assert_matches_type(PageRenderCreateResponse, page, path=["response"]) + assert_matches_type(RenderCreateResponse, render, path=["response"]) @parametrize - async def test_raw_response_render_create(self, async_client: AsyncUnlayer) -> None: - response = await async_client.pages.with_raw_response.render_create( + async def test_raw_response_create(self, async_client: AsyncUnlayer) -> None: + response = await async_client.pages.render.with_raw_response.create( project_id="projectId", design={"foo": "bar"}, ) assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" - page = await response.parse() - assert_matches_type(PageRenderCreateResponse, page, path=["response"]) + render = await response.parse() + assert_matches_type(RenderCreateResponse, render, path=["response"]) @parametrize - async def test_streaming_response_render_create(self, async_client: AsyncUnlayer) -> None: - async with async_client.pages.with_streaming_response.render_create( + async def test_streaming_response_create(self, async_client: AsyncUnlayer) -> None: + async with async_client.pages.render.with_streaming_response.create( project_id="projectId", design={"foo": "bar"}, ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" - page = await response.parse() - assert_matches_type(PageRenderCreateResponse, page, path=["response"]) + render = await response.parse() + assert_matches_type(RenderCreateResponse, render, path=["response"]) assert cast(Any, response.is_closed) is True diff --git a/tests/api_resources/project/__init__.py b/tests/api_resources/project/__init__.py new file mode 100644 index 0000000..fd8019a --- /dev/null +++ b/tests/api_resources/project/__init__.py @@ -0,0 +1 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. diff --git a/tests/api_resources/project/test_current.py b/tests/api_resources/project/test_current.py new file mode 100644 index 0000000..7fd4430 --- /dev/null +++ b/tests/api_resources/project/test_current.py @@ -0,0 +1,86 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import os +from typing import Any, cast + +import pytest + +from unlayer import Unlayer, AsyncUnlayer +from tests.utils import assert_matches_type +from unlayer.types.project import CurrentRetrieveResponse + +base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") + + +class TestCurrent: + parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) + + @parametrize + def test_method_retrieve(self, client: Unlayer) -> None: + current = client.project.current.retrieve( + project_id="projectId", + ) + assert_matches_type(CurrentRetrieveResponse, current, path=["response"]) + + @parametrize + def test_raw_response_retrieve(self, client: Unlayer) -> None: + response = client.project.current.with_raw_response.retrieve( + project_id="projectId", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + current = response.parse() + assert_matches_type(CurrentRetrieveResponse, current, path=["response"]) + + @parametrize + def test_streaming_response_retrieve(self, client: Unlayer) -> None: + with client.project.current.with_streaming_response.retrieve( + project_id="projectId", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + current = response.parse() + assert_matches_type(CurrentRetrieveResponse, current, path=["response"]) + + assert cast(Any, response.is_closed) is True + + +class TestAsyncCurrent: + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) + + @parametrize + async def test_method_retrieve(self, async_client: AsyncUnlayer) -> None: + current = await async_client.project.current.retrieve( + project_id="projectId", + ) + assert_matches_type(CurrentRetrieveResponse, current, path=["response"]) + + @parametrize + async def test_raw_response_retrieve(self, async_client: AsyncUnlayer) -> None: + response = await async_client.project.current.with_raw_response.retrieve( + project_id="projectId", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + current = await response.parse() + assert_matches_type(CurrentRetrieveResponse, current, path=["response"]) + + @parametrize + async def test_streaming_response_retrieve(self, async_client: AsyncUnlayer) -> None: + async with async_client.project.current.with_streaming_response.retrieve( + project_id="projectId", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + current = await response.parse() + assert_matches_type(CurrentRetrieveResponse, current, path=["response"]) + + assert cast(Any, response.is_closed) is True diff --git a/tests/api_resources/project/test_domains.py b/tests/api_resources/project/test_domains.py new file mode 100644 index 0000000..ea80f6c --- /dev/null +++ b/tests/api_resources/project/test_domains.py @@ -0,0 +1,403 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import os +from typing import Any, cast + +import pytest + +from unlayer import Unlayer, AsyncUnlayer +from tests.utils import assert_matches_type +from unlayer.types.project import ( + DomainListResponse, + DomainCreateResponse, + DomainUpdateResponse, + DomainRetrieveResponse, +) + +base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") + + +class TestDomains: + parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) + + @parametrize + def test_method_create(self, client: Unlayer) -> None: + domain = client.project.domains.create( + project_id="projectId", + domain="domain", + ) + assert_matches_type(DomainCreateResponse, domain, path=["response"]) + + @parametrize + def test_raw_response_create(self, client: Unlayer) -> None: + response = client.project.domains.with_raw_response.create( + project_id="projectId", + domain="domain", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + domain = response.parse() + assert_matches_type(DomainCreateResponse, domain, path=["response"]) + + @parametrize + def test_streaming_response_create(self, client: Unlayer) -> None: + with client.project.domains.with_streaming_response.create( + project_id="projectId", + domain="domain", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + domain = response.parse() + assert_matches_type(DomainCreateResponse, domain, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_method_retrieve(self, client: Unlayer) -> None: + domain = client.project.domains.retrieve( + "id", + ) + assert_matches_type(DomainRetrieveResponse, domain, path=["response"]) + + @parametrize + def test_raw_response_retrieve(self, client: Unlayer) -> None: + response = client.project.domains.with_raw_response.retrieve( + "id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + domain = response.parse() + assert_matches_type(DomainRetrieveResponse, domain, path=["response"]) + + @parametrize + def test_streaming_response_retrieve(self, client: Unlayer) -> None: + with client.project.domains.with_streaming_response.retrieve( + "id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + domain = response.parse() + assert_matches_type(DomainRetrieveResponse, domain, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_path_params_retrieve(self, client: Unlayer) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): + client.project.domains.with_raw_response.retrieve( + "", + ) + + @parametrize + def test_method_update(self, client: Unlayer) -> None: + domain = client.project.domains.update( + id="id", + ) + assert_matches_type(DomainUpdateResponse, domain, path=["response"]) + + @parametrize + def test_method_update_with_all_params(self, client: Unlayer) -> None: + domain = client.project.domains.update( + id="id", + domain="domain", + ) + assert_matches_type(DomainUpdateResponse, domain, path=["response"]) + + @parametrize + def test_raw_response_update(self, client: Unlayer) -> None: + response = client.project.domains.with_raw_response.update( + id="id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + domain = response.parse() + assert_matches_type(DomainUpdateResponse, domain, path=["response"]) + + @parametrize + def test_streaming_response_update(self, client: Unlayer) -> None: + with client.project.domains.with_streaming_response.update( + id="id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + domain = response.parse() + assert_matches_type(DomainUpdateResponse, domain, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_path_params_update(self, client: Unlayer) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): + client.project.domains.with_raw_response.update( + id="", + ) + + @parametrize + def test_method_list(self, client: Unlayer) -> None: + domain = client.project.domains.list( + project_id="projectId", + ) + assert_matches_type(DomainListResponse, domain, path=["response"]) + + @parametrize + def test_raw_response_list(self, client: Unlayer) -> None: + response = client.project.domains.with_raw_response.list( + project_id="projectId", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + domain = response.parse() + assert_matches_type(DomainListResponse, domain, path=["response"]) + + @parametrize + def test_streaming_response_list(self, client: Unlayer) -> None: + with client.project.domains.with_streaming_response.list( + project_id="projectId", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + domain = response.parse() + assert_matches_type(DomainListResponse, domain, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_method_delete(self, client: Unlayer) -> None: + domain = client.project.domains.delete( + "id", + ) + assert domain is None + + @parametrize + def test_raw_response_delete(self, client: Unlayer) -> None: + response = client.project.domains.with_raw_response.delete( + "id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + domain = response.parse() + assert domain is None + + @parametrize + def test_streaming_response_delete(self, client: Unlayer) -> None: + with client.project.domains.with_streaming_response.delete( + "id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + domain = response.parse() + assert domain is None + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_path_params_delete(self, client: Unlayer) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): + client.project.domains.with_raw_response.delete( + "", + ) + + +class TestAsyncDomains: + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) + + @parametrize + async def test_method_create(self, async_client: AsyncUnlayer) -> None: + domain = await async_client.project.domains.create( + project_id="projectId", + domain="domain", + ) + assert_matches_type(DomainCreateResponse, domain, path=["response"]) + + @parametrize + async def test_raw_response_create(self, async_client: AsyncUnlayer) -> None: + response = await async_client.project.domains.with_raw_response.create( + project_id="projectId", + domain="domain", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + domain = await response.parse() + assert_matches_type(DomainCreateResponse, domain, path=["response"]) + + @parametrize + async def test_streaming_response_create(self, async_client: AsyncUnlayer) -> None: + async with async_client.project.domains.with_streaming_response.create( + project_id="projectId", + domain="domain", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + domain = await response.parse() + assert_matches_type(DomainCreateResponse, domain, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_method_retrieve(self, async_client: AsyncUnlayer) -> None: + domain = await async_client.project.domains.retrieve( + "id", + ) + assert_matches_type(DomainRetrieveResponse, domain, path=["response"]) + + @parametrize + async def test_raw_response_retrieve(self, async_client: AsyncUnlayer) -> None: + response = await async_client.project.domains.with_raw_response.retrieve( + "id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + domain = await response.parse() + assert_matches_type(DomainRetrieveResponse, domain, path=["response"]) + + @parametrize + async def test_streaming_response_retrieve(self, async_client: AsyncUnlayer) -> None: + async with async_client.project.domains.with_streaming_response.retrieve( + "id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + domain = await response.parse() + assert_matches_type(DomainRetrieveResponse, domain, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_path_params_retrieve(self, async_client: AsyncUnlayer) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): + await async_client.project.domains.with_raw_response.retrieve( + "", + ) + + @parametrize + async def test_method_update(self, async_client: AsyncUnlayer) -> None: + domain = await async_client.project.domains.update( + id="id", + ) + assert_matches_type(DomainUpdateResponse, domain, path=["response"]) + + @parametrize + async def test_method_update_with_all_params(self, async_client: AsyncUnlayer) -> None: + domain = await async_client.project.domains.update( + id="id", + domain="domain", + ) + assert_matches_type(DomainUpdateResponse, domain, path=["response"]) + + @parametrize + async def test_raw_response_update(self, async_client: AsyncUnlayer) -> None: + response = await async_client.project.domains.with_raw_response.update( + id="id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + domain = await response.parse() + assert_matches_type(DomainUpdateResponse, domain, path=["response"]) + + @parametrize + async def test_streaming_response_update(self, async_client: AsyncUnlayer) -> None: + async with async_client.project.domains.with_streaming_response.update( + id="id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + domain = await response.parse() + assert_matches_type(DomainUpdateResponse, domain, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_path_params_update(self, async_client: AsyncUnlayer) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): + await async_client.project.domains.with_raw_response.update( + id="", + ) + + @parametrize + async def test_method_list(self, async_client: AsyncUnlayer) -> None: + domain = await async_client.project.domains.list( + project_id="projectId", + ) + assert_matches_type(DomainListResponse, domain, path=["response"]) + + @parametrize + async def test_raw_response_list(self, async_client: AsyncUnlayer) -> None: + response = await async_client.project.domains.with_raw_response.list( + project_id="projectId", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + domain = await response.parse() + assert_matches_type(DomainListResponse, domain, path=["response"]) + + @parametrize + async def test_streaming_response_list(self, async_client: AsyncUnlayer) -> None: + async with async_client.project.domains.with_streaming_response.list( + project_id="projectId", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + domain = await response.parse() + assert_matches_type(DomainListResponse, domain, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_method_delete(self, async_client: AsyncUnlayer) -> None: + domain = await async_client.project.domains.delete( + "id", + ) + assert domain is None + + @parametrize + async def test_raw_response_delete(self, async_client: AsyncUnlayer) -> None: + response = await async_client.project.domains.with_raw_response.delete( + "id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + domain = await response.parse() + assert domain is None + + @parametrize + async def test_streaming_response_delete(self, async_client: AsyncUnlayer) -> None: + async with async_client.project.domains.with_streaming_response.delete( + "id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + domain = await response.parse() + assert domain is None + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_path_params_delete(self, async_client: AsyncUnlayer) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): + await async_client.project.domains.with_raw_response.delete( + "", + ) diff --git a/tests/api_resources/project/test_templates.py b/tests/api_resources/project/test_templates.py new file mode 100644 index 0000000..3aa52c6 --- /dev/null +++ b/tests/api_resources/project/test_templates.py @@ -0,0 +1,447 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import os +from typing import Any, cast + +import pytest + +from unlayer import Unlayer, AsyncUnlayer +from tests.utils import assert_matches_type +from unlayer.types.project import ( + TemplateListResponse, + TemplateCreateResponse, + TemplateUpdateResponse, + TemplateRetrieveResponse, +) + +base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") + + +class TestTemplates: + parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) + + @parametrize + def test_method_create(self, client: Unlayer) -> None: + template = client.project.templates.create( + project_id="projectId", + name="name", + ) + assert_matches_type(TemplateCreateResponse, template, path=["response"]) + + @parametrize + def test_method_create_with_all_params(self, client: Unlayer) -> None: + template = client.project.templates.create( + project_id="projectId", + name="name", + display_mode="email", + ) + assert_matches_type(TemplateCreateResponse, template, path=["response"]) + + @parametrize + def test_raw_response_create(self, client: Unlayer) -> None: + response = client.project.templates.with_raw_response.create( + project_id="projectId", + name="name", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + template = response.parse() + assert_matches_type(TemplateCreateResponse, template, path=["response"]) + + @parametrize + def test_streaming_response_create(self, client: Unlayer) -> None: + with client.project.templates.with_streaming_response.create( + project_id="projectId", + name="name", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + template = response.parse() + assert_matches_type(TemplateCreateResponse, template, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_method_retrieve(self, client: Unlayer) -> None: + template = client.project.templates.retrieve( + "id", + ) + assert_matches_type(TemplateRetrieveResponse, template, path=["response"]) + + @parametrize + def test_raw_response_retrieve(self, client: Unlayer) -> None: + response = client.project.templates.with_raw_response.retrieve( + "id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + template = response.parse() + assert_matches_type(TemplateRetrieveResponse, template, path=["response"]) + + @parametrize + def test_streaming_response_retrieve(self, client: Unlayer) -> None: + with client.project.templates.with_streaming_response.retrieve( + "id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + template = response.parse() + assert_matches_type(TemplateRetrieveResponse, template, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_path_params_retrieve(self, client: Unlayer) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): + client.project.templates.with_raw_response.retrieve( + "", + ) + + @parametrize + def test_method_update(self, client: Unlayer) -> None: + template = client.project.templates.update( + id="id", + ) + assert_matches_type(TemplateUpdateResponse, template, path=["response"]) + + @parametrize + def test_method_update_with_all_params(self, client: Unlayer) -> None: + template = client.project.templates.update( + id="id", + body="body", + name="name", + subject="subject", + ) + assert_matches_type(TemplateUpdateResponse, template, path=["response"]) + + @parametrize + def test_raw_response_update(self, client: Unlayer) -> None: + response = client.project.templates.with_raw_response.update( + id="id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + template = response.parse() + assert_matches_type(TemplateUpdateResponse, template, path=["response"]) + + @parametrize + def test_streaming_response_update(self, client: Unlayer) -> None: + with client.project.templates.with_streaming_response.update( + id="id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + template = response.parse() + assert_matches_type(TemplateUpdateResponse, template, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_path_params_update(self, client: Unlayer) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): + client.project.templates.with_raw_response.update( + id="", + ) + + @parametrize + def test_method_list(self, client: Unlayer) -> None: + template = client.project.templates.list( + project_id="projectId", + ) + assert_matches_type(TemplateListResponse, template, path=["response"]) + + @parametrize + def test_method_list_with_all_params(self, client: Unlayer) -> None: + template = client.project.templates.list( + project_id="projectId", + cursor="cursor", + display_mode="email", + limit=1, + name="name", + ) + assert_matches_type(TemplateListResponse, template, path=["response"]) + + @parametrize + def test_raw_response_list(self, client: Unlayer) -> None: + response = client.project.templates.with_raw_response.list( + project_id="projectId", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + template = response.parse() + assert_matches_type(TemplateListResponse, template, path=["response"]) + + @parametrize + def test_streaming_response_list(self, client: Unlayer) -> None: + with client.project.templates.with_streaming_response.list( + project_id="projectId", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + template = response.parse() + assert_matches_type(TemplateListResponse, template, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_method_delete(self, client: Unlayer) -> None: + template = client.project.templates.delete( + "id", + ) + assert template is None + + @parametrize + def test_raw_response_delete(self, client: Unlayer) -> None: + response = client.project.templates.with_raw_response.delete( + "id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + template = response.parse() + assert template is None + + @parametrize + def test_streaming_response_delete(self, client: Unlayer) -> None: + with client.project.templates.with_streaming_response.delete( + "id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + template = response.parse() + assert template is None + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_path_params_delete(self, client: Unlayer) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): + client.project.templates.with_raw_response.delete( + "", + ) + + +class TestAsyncTemplates: + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) + + @parametrize + async def test_method_create(self, async_client: AsyncUnlayer) -> None: + template = await async_client.project.templates.create( + project_id="projectId", + name="name", + ) + assert_matches_type(TemplateCreateResponse, template, path=["response"]) + + @parametrize + async def test_method_create_with_all_params(self, async_client: AsyncUnlayer) -> None: + template = await async_client.project.templates.create( + project_id="projectId", + name="name", + display_mode="email", + ) + assert_matches_type(TemplateCreateResponse, template, path=["response"]) + + @parametrize + async def test_raw_response_create(self, async_client: AsyncUnlayer) -> None: + response = await async_client.project.templates.with_raw_response.create( + project_id="projectId", + name="name", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + template = await response.parse() + assert_matches_type(TemplateCreateResponse, template, path=["response"]) + + @parametrize + async def test_streaming_response_create(self, async_client: AsyncUnlayer) -> None: + async with async_client.project.templates.with_streaming_response.create( + project_id="projectId", + name="name", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + template = await response.parse() + assert_matches_type(TemplateCreateResponse, template, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_method_retrieve(self, async_client: AsyncUnlayer) -> None: + template = await async_client.project.templates.retrieve( + "id", + ) + assert_matches_type(TemplateRetrieveResponse, template, path=["response"]) + + @parametrize + async def test_raw_response_retrieve(self, async_client: AsyncUnlayer) -> None: + response = await async_client.project.templates.with_raw_response.retrieve( + "id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + template = await response.parse() + assert_matches_type(TemplateRetrieveResponse, template, path=["response"]) + + @parametrize + async def test_streaming_response_retrieve(self, async_client: AsyncUnlayer) -> None: + async with async_client.project.templates.with_streaming_response.retrieve( + "id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + template = await response.parse() + assert_matches_type(TemplateRetrieveResponse, template, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_path_params_retrieve(self, async_client: AsyncUnlayer) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): + await async_client.project.templates.with_raw_response.retrieve( + "", + ) + + @parametrize + async def test_method_update(self, async_client: AsyncUnlayer) -> None: + template = await async_client.project.templates.update( + id="id", + ) + assert_matches_type(TemplateUpdateResponse, template, path=["response"]) + + @parametrize + async def test_method_update_with_all_params(self, async_client: AsyncUnlayer) -> None: + template = await async_client.project.templates.update( + id="id", + body="body", + name="name", + subject="subject", + ) + assert_matches_type(TemplateUpdateResponse, template, path=["response"]) + + @parametrize + async def test_raw_response_update(self, async_client: AsyncUnlayer) -> None: + response = await async_client.project.templates.with_raw_response.update( + id="id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + template = await response.parse() + assert_matches_type(TemplateUpdateResponse, template, path=["response"]) + + @parametrize + async def test_streaming_response_update(self, async_client: AsyncUnlayer) -> None: + async with async_client.project.templates.with_streaming_response.update( + id="id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + template = await response.parse() + assert_matches_type(TemplateUpdateResponse, template, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_path_params_update(self, async_client: AsyncUnlayer) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): + await async_client.project.templates.with_raw_response.update( + id="", + ) + + @parametrize + async def test_method_list(self, async_client: AsyncUnlayer) -> None: + template = await async_client.project.templates.list( + project_id="projectId", + ) + assert_matches_type(TemplateListResponse, template, path=["response"]) + + @parametrize + async def test_method_list_with_all_params(self, async_client: AsyncUnlayer) -> None: + template = await async_client.project.templates.list( + project_id="projectId", + cursor="cursor", + display_mode="email", + limit=1, + name="name", + ) + assert_matches_type(TemplateListResponse, template, path=["response"]) + + @parametrize + async def test_raw_response_list(self, async_client: AsyncUnlayer) -> None: + response = await async_client.project.templates.with_raw_response.list( + project_id="projectId", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + template = await response.parse() + assert_matches_type(TemplateListResponse, template, path=["response"]) + + @parametrize + async def test_streaming_response_list(self, async_client: AsyncUnlayer) -> None: + async with async_client.project.templates.with_streaming_response.list( + project_id="projectId", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + template = await response.parse() + assert_matches_type(TemplateListResponse, template, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_method_delete(self, async_client: AsyncUnlayer) -> None: + template = await async_client.project.templates.delete( + "id", + ) + assert template is None + + @parametrize + async def test_raw_response_delete(self, async_client: AsyncUnlayer) -> None: + response = await async_client.project.templates.with_raw_response.delete( + "id", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + template = await response.parse() + assert template is None + + @parametrize + async def test_streaming_response_delete(self, async_client: AsyncUnlayer) -> None: + async with async_client.project.templates.with_streaming_response.delete( + "id", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + template = await response.parse() + assert template is None + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_path_params_delete(self, async_client: AsyncUnlayer) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): + await async_client.project.templates.with_raw_response.delete( + "", + ) diff --git a/tests/api_resources/project/test_workspaces.py b/tests/api_resources/project/test_workspaces.py new file mode 100644 index 0000000..05b504b --- /dev/null +++ b/tests/api_resources/project/test_workspaces.py @@ -0,0 +1,150 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import os +from typing import Any, cast + +import pytest + +from unlayer import Unlayer, AsyncUnlayer +from tests.utils import assert_matches_type +from unlayer.types.project import WorkspaceListResponse, WorkspaceRetrieveResponse + +base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") + + +class TestWorkspaces: + parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) + + @parametrize + def test_method_retrieve(self, client: Unlayer) -> None: + workspace = client.project.workspaces.retrieve( + "workspaceId", + ) + assert_matches_type(WorkspaceRetrieveResponse, workspace, path=["response"]) + + @parametrize + def test_raw_response_retrieve(self, client: Unlayer) -> None: + response = client.project.workspaces.with_raw_response.retrieve( + "workspaceId", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + workspace = response.parse() + assert_matches_type(WorkspaceRetrieveResponse, workspace, path=["response"]) + + @parametrize + def test_streaming_response_retrieve(self, client: Unlayer) -> None: + with client.project.workspaces.with_streaming_response.retrieve( + "workspaceId", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + workspace = response.parse() + assert_matches_type(WorkspaceRetrieveResponse, workspace, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_path_params_retrieve(self, client: Unlayer) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `workspace_id` but received ''"): + client.project.workspaces.with_raw_response.retrieve( + "", + ) + + @parametrize + def test_method_list(self, client: Unlayer) -> None: + workspace = client.project.workspaces.list() + assert_matches_type(WorkspaceListResponse, workspace, path=["response"]) + + @parametrize + def test_raw_response_list(self, client: Unlayer) -> None: + response = client.project.workspaces.with_raw_response.list() + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + workspace = response.parse() + assert_matches_type(WorkspaceListResponse, workspace, path=["response"]) + + @parametrize + def test_streaming_response_list(self, client: Unlayer) -> None: + with client.project.workspaces.with_streaming_response.list() as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + workspace = response.parse() + assert_matches_type(WorkspaceListResponse, workspace, path=["response"]) + + assert cast(Any, response.is_closed) is True + + +class TestAsyncWorkspaces: + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) + + @parametrize + async def test_method_retrieve(self, async_client: AsyncUnlayer) -> None: + workspace = await async_client.project.workspaces.retrieve( + "workspaceId", + ) + assert_matches_type(WorkspaceRetrieveResponse, workspace, path=["response"]) + + @parametrize + async def test_raw_response_retrieve(self, async_client: AsyncUnlayer) -> None: + response = await async_client.project.workspaces.with_raw_response.retrieve( + "workspaceId", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + workspace = await response.parse() + assert_matches_type(WorkspaceRetrieveResponse, workspace, path=["response"]) + + @parametrize + async def test_streaming_response_retrieve(self, async_client: AsyncUnlayer) -> None: + async with async_client.project.workspaces.with_streaming_response.retrieve( + "workspaceId", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + workspace = await response.parse() + assert_matches_type(WorkspaceRetrieveResponse, workspace, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_path_params_retrieve(self, async_client: AsyncUnlayer) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `workspace_id` but received ''"): + await async_client.project.workspaces.with_raw_response.retrieve( + "", + ) + + @parametrize + async def test_method_list(self, async_client: AsyncUnlayer) -> None: + workspace = await async_client.project.workspaces.list() + assert_matches_type(WorkspaceListResponse, workspace, path=["response"]) + + @parametrize + async def test_raw_response_list(self, async_client: AsyncUnlayer) -> None: + response = await async_client.project.workspaces.with_raw_response.list() + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + workspace = await response.parse() + assert_matches_type(WorkspaceListResponse, workspace, path=["response"]) + + @parametrize + async def test_streaming_response_list(self, async_client: AsyncUnlayer) -> None: + async with async_client.project.workspaces.with_streaming_response.list() as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + workspace = await response.parse() + assert_matches_type(WorkspaceListResponse, workspace, path=["response"]) + + assert cast(Any, response.is_closed) is True diff --git a/tests/api_resources/test_documents.py b/tests/api_resources/test_documents.py index 39f9035..f8ae227 100644 --- a/tests/api_resources/test_documents.py +++ b/tests/api_resources/test_documents.py @@ -9,11 +9,7 @@ from unlayer import Unlayer, AsyncUnlayer from tests.utils import assert_matches_type -from unlayer.types import ( - DocumentGenerateCreateResponse, - DocumentDocumentsRetrieveResponse, - DocumentGenerateTemplateTemplateResponse, -) +from unlayer.types import DocumentRetrieveResponse base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") @@ -22,16 +18,16 @@ class TestDocuments: parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) @parametrize - def test_method_documents_retrieve(self, client: Unlayer) -> None: - document = client.documents.documents_retrieve( + def test_method_retrieve(self, client: Unlayer) -> None: + document = client.documents.retrieve( id="id", project_id="projectId", ) - assert_matches_type(DocumentDocumentsRetrieveResponse, document, path=["response"]) + assert_matches_type(DocumentRetrieveResponse, document, path=["response"]) @parametrize - def test_raw_response_documents_retrieve(self, client: Unlayer) -> None: - response = client.documents.with_raw_response.documents_retrieve( + def test_raw_response_retrieve(self, client: Unlayer) -> None: + response = client.documents.with_raw_response.retrieve( id="id", project_id="projectId", ) @@ -39,11 +35,11 @@ def test_raw_response_documents_retrieve(self, client: Unlayer) -> None: assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" document = response.parse() - assert_matches_type(DocumentDocumentsRetrieveResponse, document, path=["response"]) + assert_matches_type(DocumentRetrieveResponse, document, path=["response"]) @parametrize - def test_streaming_response_documents_retrieve(self, client: Unlayer) -> None: - with client.documents.with_streaming_response.documents_retrieve( + def test_streaming_response_retrieve(self, client: Unlayer) -> None: + with client.documents.with_streaming_response.retrieve( id="id", project_id="projectId", ) as response: @@ -51,105 +47,18 @@ def test_streaming_response_documents_retrieve(self, client: Unlayer) -> None: assert response.http_request.headers.get("X-Stainless-Lang") == "python" document = response.parse() - assert_matches_type(DocumentDocumentsRetrieveResponse, document, path=["response"]) + assert_matches_type(DocumentRetrieveResponse, document, path=["response"]) assert cast(Any, response.is_closed) is True @parametrize - def test_path_params_documents_retrieve(self, client: Unlayer) -> None: + def test_path_params_retrieve(self, client: Unlayer) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): - client.documents.with_raw_response.documents_retrieve( + client.documents.with_raw_response.retrieve( id="", project_id="projectId", ) - @parametrize - def test_method_generate_create(self, client: Unlayer) -> None: - document = client.documents.generate_create( - project_id="projectId", - ) - assert_matches_type(DocumentGenerateCreateResponse, document, path=["response"]) - - @parametrize - def test_method_generate_create_with_all_params(self, client: Unlayer) -> None: - document = client.documents.generate_create( - project_id="projectId", - design={"foo": "bar"}, - filename="filename", - html="html", - merge_tags={"foo": "string"}, - url="https://example.com", - ) - assert_matches_type(DocumentGenerateCreateResponse, document, path=["response"]) - - @parametrize - def test_raw_response_generate_create(self, client: Unlayer) -> None: - response = client.documents.with_raw_response.generate_create( - project_id="projectId", - ) - - assert response.is_closed is True - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - document = response.parse() - assert_matches_type(DocumentGenerateCreateResponse, document, path=["response"]) - - @parametrize - def test_streaming_response_generate_create(self, client: Unlayer) -> None: - with client.documents.with_streaming_response.generate_create( - project_id="projectId", - ) as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - - document = response.parse() - assert_matches_type(DocumentGenerateCreateResponse, document, path=["response"]) - - assert cast(Any, response.is_closed) is True - - @parametrize - def test_method_generate_template_template(self, client: Unlayer) -> None: - document = client.documents.generate_template_template( - project_id="projectId", - template_id="templateId", - ) - assert_matches_type(DocumentGenerateTemplateTemplateResponse, document, path=["response"]) - - @parametrize - def test_method_generate_template_template_with_all_params(self, client: Unlayer) -> None: - document = client.documents.generate_template_template( - project_id="projectId", - template_id="templateId", - filename="filename", - merge_tags={"foo": "string"}, - ) - assert_matches_type(DocumentGenerateTemplateTemplateResponse, document, path=["response"]) - - @parametrize - def test_raw_response_generate_template_template(self, client: Unlayer) -> None: - response = client.documents.with_raw_response.generate_template_template( - project_id="projectId", - template_id="templateId", - ) - - assert response.is_closed is True - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - document = response.parse() - assert_matches_type(DocumentGenerateTemplateTemplateResponse, document, path=["response"]) - - @parametrize - def test_streaming_response_generate_template_template(self, client: Unlayer) -> None: - with client.documents.with_streaming_response.generate_template_template( - project_id="projectId", - template_id="templateId", - ) as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - - document = response.parse() - assert_matches_type(DocumentGenerateTemplateTemplateResponse, document, path=["response"]) - - assert cast(Any, response.is_closed) is True - class TestAsyncDocuments: parametrize = pytest.mark.parametrize( @@ -157,16 +66,16 @@ class TestAsyncDocuments: ) @parametrize - async def test_method_documents_retrieve(self, async_client: AsyncUnlayer) -> None: - document = await async_client.documents.documents_retrieve( + async def test_method_retrieve(self, async_client: AsyncUnlayer) -> None: + document = await async_client.documents.retrieve( id="id", project_id="projectId", ) - assert_matches_type(DocumentDocumentsRetrieveResponse, document, path=["response"]) + assert_matches_type(DocumentRetrieveResponse, document, path=["response"]) @parametrize - async def test_raw_response_documents_retrieve(self, async_client: AsyncUnlayer) -> None: - response = await async_client.documents.with_raw_response.documents_retrieve( + async def test_raw_response_retrieve(self, async_client: AsyncUnlayer) -> None: + response = await async_client.documents.with_raw_response.retrieve( id="id", project_id="projectId", ) @@ -174,11 +83,11 @@ async def test_raw_response_documents_retrieve(self, async_client: AsyncUnlayer) assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" document = await response.parse() - assert_matches_type(DocumentDocumentsRetrieveResponse, document, path=["response"]) + assert_matches_type(DocumentRetrieveResponse, document, path=["response"]) @parametrize - async def test_streaming_response_documents_retrieve(self, async_client: AsyncUnlayer) -> None: - async with async_client.documents.with_streaming_response.documents_retrieve( + async def test_streaming_response_retrieve(self, async_client: AsyncUnlayer) -> None: + async with async_client.documents.with_streaming_response.retrieve( id="id", project_id="projectId", ) as response: @@ -186,101 +95,14 @@ async def test_streaming_response_documents_retrieve(self, async_client: AsyncUn assert response.http_request.headers.get("X-Stainless-Lang") == "python" document = await response.parse() - assert_matches_type(DocumentDocumentsRetrieveResponse, document, path=["response"]) + assert_matches_type(DocumentRetrieveResponse, document, path=["response"]) assert cast(Any, response.is_closed) is True @parametrize - async def test_path_params_documents_retrieve(self, async_client: AsyncUnlayer) -> None: + async def test_path_params_retrieve(self, async_client: AsyncUnlayer) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): - await async_client.documents.with_raw_response.documents_retrieve( + await async_client.documents.with_raw_response.retrieve( id="", project_id="projectId", ) - - @parametrize - async def test_method_generate_create(self, async_client: AsyncUnlayer) -> None: - document = await async_client.documents.generate_create( - project_id="projectId", - ) - assert_matches_type(DocumentGenerateCreateResponse, document, path=["response"]) - - @parametrize - async def test_method_generate_create_with_all_params(self, async_client: AsyncUnlayer) -> None: - document = await async_client.documents.generate_create( - project_id="projectId", - design={"foo": "bar"}, - filename="filename", - html="html", - merge_tags={"foo": "string"}, - url="https://example.com", - ) - assert_matches_type(DocumentGenerateCreateResponse, document, path=["response"]) - - @parametrize - async def test_raw_response_generate_create(self, async_client: AsyncUnlayer) -> None: - response = await async_client.documents.with_raw_response.generate_create( - project_id="projectId", - ) - - assert response.is_closed is True - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - document = await response.parse() - assert_matches_type(DocumentGenerateCreateResponse, document, path=["response"]) - - @parametrize - async def test_streaming_response_generate_create(self, async_client: AsyncUnlayer) -> None: - async with async_client.documents.with_streaming_response.generate_create( - project_id="projectId", - ) as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - - document = await response.parse() - assert_matches_type(DocumentGenerateCreateResponse, document, path=["response"]) - - assert cast(Any, response.is_closed) is True - - @parametrize - async def test_method_generate_template_template(self, async_client: AsyncUnlayer) -> None: - document = await async_client.documents.generate_template_template( - project_id="projectId", - template_id="templateId", - ) - assert_matches_type(DocumentGenerateTemplateTemplateResponse, document, path=["response"]) - - @parametrize - async def test_method_generate_template_template_with_all_params(self, async_client: AsyncUnlayer) -> None: - document = await async_client.documents.generate_template_template( - project_id="projectId", - template_id="templateId", - filename="filename", - merge_tags={"foo": "string"}, - ) - assert_matches_type(DocumentGenerateTemplateTemplateResponse, document, path=["response"]) - - @parametrize - async def test_raw_response_generate_template_template(self, async_client: AsyncUnlayer) -> None: - response = await async_client.documents.with_raw_response.generate_template_template( - project_id="projectId", - template_id="templateId", - ) - - assert response.is_closed is True - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - document = await response.parse() - assert_matches_type(DocumentGenerateTemplateTemplateResponse, document, path=["response"]) - - @parametrize - async def test_streaming_response_generate_template_template(self, async_client: AsyncUnlayer) -> None: - async with async_client.documents.with_streaming_response.generate_template_template( - project_id="projectId", - template_id="templateId", - ) as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - - document = await response.parse() - assert_matches_type(DocumentGenerateTemplateTemplateResponse, document, path=["response"]) - - assert cast(Any, response.is_closed) is True diff --git a/tests/api_resources/test_emails.py b/tests/api_resources/test_emails.py index c14a734..a34022a 100644 --- a/tests/api_resources/test_emails.py +++ b/tests/api_resources/test_emails.py @@ -9,12 +9,7 @@ from unlayer import Unlayer, AsyncUnlayer from tests.utils import assert_matches_type -from unlayer.types import ( - EmailRetrieveResponse, - EmailSendCreateResponse, - EmailRenderCreateResponse, - EmailSendTemplateTemplateResponse, -) +from unlayer.types import EmailRetrieveResponse base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") @@ -64,143 +59,6 @@ def test_path_params_retrieve(self, client: Unlayer) -> None: project_id="projectId", ) - @parametrize - def test_method_render_create(self, client: Unlayer) -> None: - email = client.emails.render_create( - project_id="projectId", - design={"foo": "bar"}, - ) - assert_matches_type(EmailRenderCreateResponse, email, path=["response"]) - - @parametrize - def test_method_render_create_with_all_params(self, client: Unlayer) -> None: - email = client.emails.render_create( - project_id="projectId", - design={"foo": "bar"}, - merge_tags={"foo": "string"}, - ) - assert_matches_type(EmailRenderCreateResponse, email, path=["response"]) - - @parametrize - def test_raw_response_render_create(self, client: Unlayer) -> None: - response = client.emails.with_raw_response.render_create( - project_id="projectId", - design={"foo": "bar"}, - ) - - assert response.is_closed is True - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - email = response.parse() - assert_matches_type(EmailRenderCreateResponse, email, path=["response"]) - - @parametrize - def test_streaming_response_render_create(self, client: Unlayer) -> None: - with client.emails.with_streaming_response.render_create( - project_id="projectId", - design={"foo": "bar"}, - ) as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - - email = response.parse() - assert_matches_type(EmailRenderCreateResponse, email, path=["response"]) - - assert cast(Any, response.is_closed) is True - - @parametrize - def test_method_send_create(self, client: Unlayer) -> None: - email = client.emails.send_create( - project_id="projectId", - to="dev@stainless.com", - ) - assert_matches_type(EmailSendCreateResponse, email, path=["response"]) - - @parametrize - def test_method_send_create_with_all_params(self, client: Unlayer) -> None: - email = client.emails.send_create( - project_id="projectId", - to="dev@stainless.com", - design={"foo": "bar"}, - html="html", - merge_tags={"foo": "string"}, - subject="subject", - ) - assert_matches_type(EmailSendCreateResponse, email, path=["response"]) - - @parametrize - def test_raw_response_send_create(self, client: Unlayer) -> None: - response = client.emails.with_raw_response.send_create( - project_id="projectId", - to="dev@stainless.com", - ) - - assert response.is_closed is True - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - email = response.parse() - assert_matches_type(EmailSendCreateResponse, email, path=["response"]) - - @parametrize - def test_streaming_response_send_create(self, client: Unlayer) -> None: - with client.emails.with_streaming_response.send_create( - project_id="projectId", - to="dev@stainless.com", - ) as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - - email = response.parse() - assert_matches_type(EmailSendCreateResponse, email, path=["response"]) - - assert cast(Any, response.is_closed) is True - - @parametrize - def test_method_send_template_template(self, client: Unlayer) -> None: - email = client.emails.send_template_template( - project_id="projectId", - template_id="templateId", - to="dev@stainless.com", - ) - assert_matches_type(EmailSendTemplateTemplateResponse, email, path=["response"]) - - @parametrize - def test_method_send_template_template_with_all_params(self, client: Unlayer) -> None: - email = client.emails.send_template_template( - project_id="projectId", - template_id="templateId", - to="dev@stainless.com", - merge_tags={"foo": "string"}, - subject="subject", - ) - assert_matches_type(EmailSendTemplateTemplateResponse, email, path=["response"]) - - @parametrize - def test_raw_response_send_template_template(self, client: Unlayer) -> None: - response = client.emails.with_raw_response.send_template_template( - project_id="projectId", - template_id="templateId", - to="dev@stainless.com", - ) - - assert response.is_closed is True - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - email = response.parse() - assert_matches_type(EmailSendTemplateTemplateResponse, email, path=["response"]) - - @parametrize - def test_streaming_response_send_template_template(self, client: Unlayer) -> None: - with client.emails.with_streaming_response.send_template_template( - project_id="projectId", - template_id="templateId", - to="dev@stainless.com", - ) as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - - email = response.parse() - assert_matches_type(EmailSendTemplateTemplateResponse, email, path=["response"]) - - assert cast(Any, response.is_closed) is True - class TestAsyncEmails: parametrize = pytest.mark.parametrize( @@ -248,140 +106,3 @@ async def test_path_params_retrieve(self, async_client: AsyncUnlayer) -> None: id="", project_id="projectId", ) - - @parametrize - async def test_method_render_create(self, async_client: AsyncUnlayer) -> None: - email = await async_client.emails.render_create( - project_id="projectId", - design={"foo": "bar"}, - ) - assert_matches_type(EmailRenderCreateResponse, email, path=["response"]) - - @parametrize - async def test_method_render_create_with_all_params(self, async_client: AsyncUnlayer) -> None: - email = await async_client.emails.render_create( - project_id="projectId", - design={"foo": "bar"}, - merge_tags={"foo": "string"}, - ) - assert_matches_type(EmailRenderCreateResponse, email, path=["response"]) - - @parametrize - async def test_raw_response_render_create(self, async_client: AsyncUnlayer) -> None: - response = await async_client.emails.with_raw_response.render_create( - project_id="projectId", - design={"foo": "bar"}, - ) - - assert response.is_closed is True - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - email = await response.parse() - assert_matches_type(EmailRenderCreateResponse, email, path=["response"]) - - @parametrize - async def test_streaming_response_render_create(self, async_client: AsyncUnlayer) -> None: - async with async_client.emails.with_streaming_response.render_create( - project_id="projectId", - design={"foo": "bar"}, - ) as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - - email = await response.parse() - assert_matches_type(EmailRenderCreateResponse, email, path=["response"]) - - assert cast(Any, response.is_closed) is True - - @parametrize - async def test_method_send_create(self, async_client: AsyncUnlayer) -> None: - email = await async_client.emails.send_create( - project_id="projectId", - to="dev@stainless.com", - ) - assert_matches_type(EmailSendCreateResponse, email, path=["response"]) - - @parametrize - async def test_method_send_create_with_all_params(self, async_client: AsyncUnlayer) -> None: - email = await async_client.emails.send_create( - project_id="projectId", - to="dev@stainless.com", - design={"foo": "bar"}, - html="html", - merge_tags={"foo": "string"}, - subject="subject", - ) - assert_matches_type(EmailSendCreateResponse, email, path=["response"]) - - @parametrize - async def test_raw_response_send_create(self, async_client: AsyncUnlayer) -> None: - response = await async_client.emails.with_raw_response.send_create( - project_id="projectId", - to="dev@stainless.com", - ) - - assert response.is_closed is True - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - email = await response.parse() - assert_matches_type(EmailSendCreateResponse, email, path=["response"]) - - @parametrize - async def test_streaming_response_send_create(self, async_client: AsyncUnlayer) -> None: - async with async_client.emails.with_streaming_response.send_create( - project_id="projectId", - to="dev@stainless.com", - ) as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - - email = await response.parse() - assert_matches_type(EmailSendCreateResponse, email, path=["response"]) - - assert cast(Any, response.is_closed) is True - - @parametrize - async def test_method_send_template_template(self, async_client: AsyncUnlayer) -> None: - email = await async_client.emails.send_template_template( - project_id="projectId", - template_id="templateId", - to="dev@stainless.com", - ) - assert_matches_type(EmailSendTemplateTemplateResponse, email, path=["response"]) - - @parametrize - async def test_method_send_template_template_with_all_params(self, async_client: AsyncUnlayer) -> None: - email = await async_client.emails.send_template_template( - project_id="projectId", - template_id="templateId", - to="dev@stainless.com", - merge_tags={"foo": "string"}, - subject="subject", - ) - assert_matches_type(EmailSendTemplateTemplateResponse, email, path=["response"]) - - @parametrize - async def test_raw_response_send_template_template(self, async_client: AsyncUnlayer) -> None: - response = await async_client.emails.with_raw_response.send_template_template( - project_id="projectId", - template_id="templateId", - to="dev@stainless.com", - ) - - assert response.is_closed is True - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - email = await response.parse() - assert_matches_type(EmailSendTemplateTemplateResponse, email, path=["response"]) - - @parametrize - async def test_streaming_response_send_template_template(self, async_client: AsyncUnlayer) -> None: - async with async_client.emails.with_streaming_response.send_template_template( - project_id="projectId", - template_id="templateId", - to="dev@stainless.com", - ) as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - - email = await response.parse() - assert_matches_type(EmailSendTemplateTemplateResponse, email, path=["response"]) - - assert cast(Any, response.is_closed) is True diff --git a/tests/api_resources/test_export.py b/tests/api_resources/test_export.py deleted file mode 100644 index f1bce90..0000000 --- a/tests/api_resources/test_export.py +++ /dev/null @@ -1,277 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -import os -from typing import Any, cast - -import pytest - -from unlayer import Unlayer, AsyncUnlayer -from tests.utils import assert_matches_type -from unlayer.types import ( - ExportPdfListResponse, - ExportZipListResponse, - ExportHTMLListResponse, - ExportImageListResponse, -) - -base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") - - -class TestExport: - parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) - - @parametrize - def test_method_html_list(self, client: Unlayer) -> None: - export = client.export.html_list( - project_id="projectId", - ) - assert_matches_type(ExportHTMLListResponse, export, path=["response"]) - - @parametrize - def test_raw_response_html_list(self, client: Unlayer) -> None: - response = client.export.with_raw_response.html_list( - project_id="projectId", - ) - - assert response.is_closed is True - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - export = response.parse() - assert_matches_type(ExportHTMLListResponse, export, path=["response"]) - - @parametrize - def test_streaming_response_html_list(self, client: Unlayer) -> None: - with client.export.with_streaming_response.html_list( - project_id="projectId", - ) as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - - export = response.parse() - assert_matches_type(ExportHTMLListResponse, export, path=["response"]) - - assert cast(Any, response.is_closed) is True - - @parametrize - def test_method_image_list(self, client: Unlayer) -> None: - export = client.export.image_list( - project_id="projectId", - ) - assert_matches_type(ExportImageListResponse, export, path=["response"]) - - @parametrize - def test_raw_response_image_list(self, client: Unlayer) -> None: - response = client.export.with_raw_response.image_list( - project_id="projectId", - ) - - assert response.is_closed is True - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - export = response.parse() - assert_matches_type(ExportImageListResponse, export, path=["response"]) - - @parametrize - def test_streaming_response_image_list(self, client: Unlayer) -> None: - with client.export.with_streaming_response.image_list( - project_id="projectId", - ) as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - - export = response.parse() - assert_matches_type(ExportImageListResponse, export, path=["response"]) - - assert cast(Any, response.is_closed) is True - - @parametrize - def test_method_pdf_list(self, client: Unlayer) -> None: - export = client.export.pdf_list( - project_id="projectId", - ) - assert_matches_type(ExportPdfListResponse, export, path=["response"]) - - @parametrize - def test_raw_response_pdf_list(self, client: Unlayer) -> None: - response = client.export.with_raw_response.pdf_list( - project_id="projectId", - ) - - assert response.is_closed is True - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - export = response.parse() - assert_matches_type(ExportPdfListResponse, export, path=["response"]) - - @parametrize - def test_streaming_response_pdf_list(self, client: Unlayer) -> None: - with client.export.with_streaming_response.pdf_list( - project_id="projectId", - ) as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - - export = response.parse() - assert_matches_type(ExportPdfListResponse, export, path=["response"]) - - assert cast(Any, response.is_closed) is True - - @parametrize - def test_method_zip_list(self, client: Unlayer) -> None: - export = client.export.zip_list( - project_id="projectId", - ) - assert_matches_type(ExportZipListResponse, export, path=["response"]) - - @parametrize - def test_raw_response_zip_list(self, client: Unlayer) -> None: - response = client.export.with_raw_response.zip_list( - project_id="projectId", - ) - - assert response.is_closed is True - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - export = response.parse() - assert_matches_type(ExportZipListResponse, export, path=["response"]) - - @parametrize - def test_streaming_response_zip_list(self, client: Unlayer) -> None: - with client.export.with_streaming_response.zip_list( - project_id="projectId", - ) as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - - export = response.parse() - assert_matches_type(ExportZipListResponse, export, path=["response"]) - - assert cast(Any, response.is_closed) is True - - -class TestAsyncExport: - parametrize = pytest.mark.parametrize( - "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] - ) - - @parametrize - async def test_method_html_list(self, async_client: AsyncUnlayer) -> None: - export = await async_client.export.html_list( - project_id="projectId", - ) - assert_matches_type(ExportHTMLListResponse, export, path=["response"]) - - @parametrize - async def test_raw_response_html_list(self, async_client: AsyncUnlayer) -> None: - response = await async_client.export.with_raw_response.html_list( - project_id="projectId", - ) - - assert response.is_closed is True - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - export = await response.parse() - assert_matches_type(ExportHTMLListResponse, export, path=["response"]) - - @parametrize - async def test_streaming_response_html_list(self, async_client: AsyncUnlayer) -> None: - async with async_client.export.with_streaming_response.html_list( - project_id="projectId", - ) as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - - export = await response.parse() - assert_matches_type(ExportHTMLListResponse, export, path=["response"]) - - assert cast(Any, response.is_closed) is True - - @parametrize - async def test_method_image_list(self, async_client: AsyncUnlayer) -> None: - export = await async_client.export.image_list( - project_id="projectId", - ) - assert_matches_type(ExportImageListResponse, export, path=["response"]) - - @parametrize - async def test_raw_response_image_list(self, async_client: AsyncUnlayer) -> None: - response = await async_client.export.with_raw_response.image_list( - project_id="projectId", - ) - - assert response.is_closed is True - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - export = await response.parse() - assert_matches_type(ExportImageListResponse, export, path=["response"]) - - @parametrize - async def test_streaming_response_image_list(self, async_client: AsyncUnlayer) -> None: - async with async_client.export.with_streaming_response.image_list( - project_id="projectId", - ) as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - - export = await response.parse() - assert_matches_type(ExportImageListResponse, export, path=["response"]) - - assert cast(Any, response.is_closed) is True - - @parametrize - async def test_method_pdf_list(self, async_client: AsyncUnlayer) -> None: - export = await async_client.export.pdf_list( - project_id="projectId", - ) - assert_matches_type(ExportPdfListResponse, export, path=["response"]) - - @parametrize - async def test_raw_response_pdf_list(self, async_client: AsyncUnlayer) -> None: - response = await async_client.export.with_raw_response.pdf_list( - project_id="projectId", - ) - - assert response.is_closed is True - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - export = await response.parse() - assert_matches_type(ExportPdfListResponse, export, path=["response"]) - - @parametrize - async def test_streaming_response_pdf_list(self, async_client: AsyncUnlayer) -> None: - async with async_client.export.with_streaming_response.pdf_list( - project_id="projectId", - ) as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - - export = await response.parse() - assert_matches_type(ExportPdfListResponse, export, path=["response"]) - - assert cast(Any, response.is_closed) is True - - @parametrize - async def test_method_zip_list(self, async_client: AsyncUnlayer) -> None: - export = await async_client.export.zip_list( - project_id="projectId", - ) - assert_matches_type(ExportZipListResponse, export, path=["response"]) - - @parametrize - async def test_raw_response_zip_list(self, async_client: AsyncUnlayer) -> None: - response = await async_client.export.with_raw_response.zip_list( - project_id="projectId", - ) - - assert response.is_closed is True - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - export = await response.parse() - assert_matches_type(ExportZipListResponse, export, path=["response"]) - - @parametrize - async def test_streaming_response_zip_list(self, async_client: AsyncUnlayer) -> None: - async with async_client.export.with_streaming_response.zip_list( - project_id="projectId", - ) as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - - export = await response.parse() - assert_matches_type(ExportZipListResponse, export, path=["response"]) - - assert cast(Any, response.is_closed) is True diff --git a/tests/api_resources/test_project.py b/tests/api_resources/test_project.py deleted file mode 100644 index 080eb71..0000000 --- a/tests/api_resources/test_project.py +++ /dev/null @@ -1,1017 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -import os -from typing import Any, cast - -import pytest - -from unlayer import Unlayer, AsyncUnlayer -from tests.utils import assert_matches_type -from unlayer.types import ( - ProjectCurrentListResponse, - ProjectDomainsListResponse, - ProjectDomainsCreateResponse, - ProjectDomainsUpdateResponse, - ProjectTemplatesListResponse, - ProjectWorkspacesListResponse, - ProjectDomainsRetrieveResponse, - ProjectTemplatesCreateResponse, - ProjectTemplatesUpdateResponse, - ProjectTemplatesRetrieveResponse, - ProjectWorkspacesRetrieveResponse, -) -from unlayer.pagination import SyncCursorPage, AsyncCursorPage - -base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") - - -class TestProject: - parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) - - @parametrize - def test_method_current_list(self, client: Unlayer) -> None: - project = client.project.current_list( - project_id="projectId", - ) - assert_matches_type(ProjectCurrentListResponse, project, path=["response"]) - - @parametrize - def test_raw_response_current_list(self, client: Unlayer) -> None: - response = client.project.with_raw_response.current_list( - project_id="projectId", - ) - - assert response.is_closed is True - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - project = response.parse() - assert_matches_type(ProjectCurrentListResponse, project, path=["response"]) - - @parametrize - def test_streaming_response_current_list(self, client: Unlayer) -> None: - with client.project.with_streaming_response.current_list( - project_id="projectId", - ) as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - - project = response.parse() - assert_matches_type(ProjectCurrentListResponse, project, path=["response"]) - - assert cast(Any, response.is_closed) is True - - @parametrize - def test_method_domains_create(self, client: Unlayer) -> None: - project = client.project.domains_create( - project_id="projectId", - domain="domain", - ) - assert_matches_type(ProjectDomainsCreateResponse, project, path=["response"]) - - @parametrize - def test_raw_response_domains_create(self, client: Unlayer) -> None: - response = client.project.with_raw_response.domains_create( - project_id="projectId", - domain="domain", - ) - - assert response.is_closed is True - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - project = response.parse() - assert_matches_type(ProjectDomainsCreateResponse, project, path=["response"]) - - @parametrize - def test_streaming_response_domains_create(self, client: Unlayer) -> None: - with client.project.with_streaming_response.domains_create( - project_id="projectId", - domain="domain", - ) as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - - project = response.parse() - assert_matches_type(ProjectDomainsCreateResponse, project, path=["response"]) - - assert cast(Any, response.is_closed) is True - - @parametrize - def test_method_domains_delete(self, client: Unlayer) -> None: - project = client.project.domains_delete( - "id", - ) - assert project is None - - @parametrize - def test_raw_response_domains_delete(self, client: Unlayer) -> None: - response = client.project.with_raw_response.domains_delete( - "id", - ) - - assert response.is_closed is True - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - project = response.parse() - assert project is None - - @parametrize - def test_streaming_response_domains_delete(self, client: Unlayer) -> None: - with client.project.with_streaming_response.domains_delete( - "id", - ) as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - - project = response.parse() - assert project is None - - assert cast(Any, response.is_closed) is True - - @parametrize - def test_path_params_domains_delete(self, client: Unlayer) -> None: - with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): - client.project.with_raw_response.domains_delete( - "", - ) - - @parametrize - def test_method_domains_list(self, client: Unlayer) -> None: - project = client.project.domains_list( - project_id="projectId", - ) - assert_matches_type(ProjectDomainsListResponse, project, path=["response"]) - - @parametrize - def test_raw_response_domains_list(self, client: Unlayer) -> None: - response = client.project.with_raw_response.domains_list( - project_id="projectId", - ) - - assert response.is_closed is True - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - project = response.parse() - assert_matches_type(ProjectDomainsListResponse, project, path=["response"]) - - @parametrize - def test_streaming_response_domains_list(self, client: Unlayer) -> None: - with client.project.with_streaming_response.domains_list( - project_id="projectId", - ) as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - - project = response.parse() - assert_matches_type(ProjectDomainsListResponse, project, path=["response"]) - - assert cast(Any, response.is_closed) is True - - @parametrize - def test_method_domains_retrieve(self, client: Unlayer) -> None: - project = client.project.domains_retrieve( - "id", - ) - assert_matches_type(ProjectDomainsRetrieveResponse, project, path=["response"]) - - @parametrize - def test_raw_response_domains_retrieve(self, client: Unlayer) -> None: - response = client.project.with_raw_response.domains_retrieve( - "id", - ) - - assert response.is_closed is True - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - project = response.parse() - assert_matches_type(ProjectDomainsRetrieveResponse, project, path=["response"]) - - @parametrize - def test_streaming_response_domains_retrieve(self, client: Unlayer) -> None: - with client.project.with_streaming_response.domains_retrieve( - "id", - ) as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - - project = response.parse() - assert_matches_type(ProjectDomainsRetrieveResponse, project, path=["response"]) - - assert cast(Any, response.is_closed) is True - - @parametrize - def test_path_params_domains_retrieve(self, client: Unlayer) -> None: - with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): - client.project.with_raw_response.domains_retrieve( - "", - ) - - @parametrize - def test_method_domains_update(self, client: Unlayer) -> None: - project = client.project.domains_update( - id="id", - ) - assert_matches_type(ProjectDomainsUpdateResponse, project, path=["response"]) - - @parametrize - def test_method_domains_update_with_all_params(self, client: Unlayer) -> None: - project = client.project.domains_update( - id="id", - domain="domain", - ) - assert_matches_type(ProjectDomainsUpdateResponse, project, path=["response"]) - - @parametrize - def test_raw_response_domains_update(self, client: Unlayer) -> None: - response = client.project.with_raw_response.domains_update( - id="id", - ) - - assert response.is_closed is True - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - project = response.parse() - assert_matches_type(ProjectDomainsUpdateResponse, project, path=["response"]) - - @parametrize - def test_streaming_response_domains_update(self, client: Unlayer) -> None: - with client.project.with_streaming_response.domains_update( - id="id", - ) as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - - project = response.parse() - assert_matches_type(ProjectDomainsUpdateResponse, project, path=["response"]) - - assert cast(Any, response.is_closed) is True - - @parametrize - def test_path_params_domains_update(self, client: Unlayer) -> None: - with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): - client.project.with_raw_response.domains_update( - id="", - ) - - @parametrize - def test_method_templates_create(self, client: Unlayer) -> None: - project = client.project.templates_create( - project_id="projectId", - name="name", - ) - assert_matches_type(ProjectTemplatesCreateResponse, project, path=["response"]) - - @parametrize - def test_method_templates_create_with_all_params(self, client: Unlayer) -> None: - project = client.project.templates_create( - project_id="projectId", - name="name", - display_mode="email", - ) - assert_matches_type(ProjectTemplatesCreateResponse, project, path=["response"]) - - @parametrize - def test_raw_response_templates_create(self, client: Unlayer) -> None: - response = client.project.with_raw_response.templates_create( - project_id="projectId", - name="name", - ) - - assert response.is_closed is True - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - project = response.parse() - assert_matches_type(ProjectTemplatesCreateResponse, project, path=["response"]) - - @parametrize - def test_streaming_response_templates_create(self, client: Unlayer) -> None: - with client.project.with_streaming_response.templates_create( - project_id="projectId", - name="name", - ) as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - - project = response.parse() - assert_matches_type(ProjectTemplatesCreateResponse, project, path=["response"]) - - assert cast(Any, response.is_closed) is True - - @parametrize - def test_method_templates_delete(self, client: Unlayer) -> None: - project = client.project.templates_delete( - "id", - ) - assert project is None - - @parametrize - def test_raw_response_templates_delete(self, client: Unlayer) -> None: - response = client.project.with_raw_response.templates_delete( - "id", - ) - - assert response.is_closed is True - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - project = response.parse() - assert project is None - - @parametrize - def test_streaming_response_templates_delete(self, client: Unlayer) -> None: - with client.project.with_streaming_response.templates_delete( - "id", - ) as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - - project = response.parse() - assert project is None - - assert cast(Any, response.is_closed) is True - - @parametrize - def test_path_params_templates_delete(self, client: Unlayer) -> None: - with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): - client.project.with_raw_response.templates_delete( - "", - ) - - @parametrize - def test_method_templates_list(self, client: Unlayer) -> None: - project = client.project.templates_list( - project_id="projectId", - ) - assert_matches_type(SyncCursorPage[ProjectTemplatesListResponse], project, path=["response"]) - - @parametrize - def test_method_templates_list_with_all_params(self, client: Unlayer) -> None: - project = client.project.templates_list( - project_id="projectId", - cursor="cursor", - display_mode="email", - limit=1, - name="name", - ) - assert_matches_type(SyncCursorPage[ProjectTemplatesListResponse], project, path=["response"]) - - @parametrize - def test_raw_response_templates_list(self, client: Unlayer) -> None: - response = client.project.with_raw_response.templates_list( - project_id="projectId", - ) - - assert response.is_closed is True - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - project = response.parse() - assert_matches_type(SyncCursorPage[ProjectTemplatesListResponse], project, path=["response"]) - - @parametrize - def test_streaming_response_templates_list(self, client: Unlayer) -> None: - with client.project.with_streaming_response.templates_list( - project_id="projectId", - ) as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - - project = response.parse() - assert_matches_type(SyncCursorPage[ProjectTemplatesListResponse], project, path=["response"]) - - assert cast(Any, response.is_closed) is True - - @parametrize - def test_method_templates_retrieve(self, client: Unlayer) -> None: - project = client.project.templates_retrieve( - "id", - ) - assert_matches_type(ProjectTemplatesRetrieveResponse, project, path=["response"]) - - @parametrize - def test_raw_response_templates_retrieve(self, client: Unlayer) -> None: - response = client.project.with_raw_response.templates_retrieve( - "id", - ) - - assert response.is_closed is True - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - project = response.parse() - assert_matches_type(ProjectTemplatesRetrieveResponse, project, path=["response"]) - - @parametrize - def test_streaming_response_templates_retrieve(self, client: Unlayer) -> None: - with client.project.with_streaming_response.templates_retrieve( - "id", - ) as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - - project = response.parse() - assert_matches_type(ProjectTemplatesRetrieveResponse, project, path=["response"]) - - assert cast(Any, response.is_closed) is True - - @parametrize - def test_path_params_templates_retrieve(self, client: Unlayer) -> None: - with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): - client.project.with_raw_response.templates_retrieve( - "", - ) - - @parametrize - def test_method_templates_update(self, client: Unlayer) -> None: - project = client.project.templates_update( - id="id", - ) - assert_matches_type(ProjectTemplatesUpdateResponse, project, path=["response"]) - - @parametrize - def test_method_templates_update_with_all_params(self, client: Unlayer) -> None: - project = client.project.templates_update( - id="id", - body="body", - name="name", - subject="subject", - ) - assert_matches_type(ProjectTemplatesUpdateResponse, project, path=["response"]) - - @parametrize - def test_raw_response_templates_update(self, client: Unlayer) -> None: - response = client.project.with_raw_response.templates_update( - id="id", - ) - - assert response.is_closed is True - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - project = response.parse() - assert_matches_type(ProjectTemplatesUpdateResponse, project, path=["response"]) - - @parametrize - def test_streaming_response_templates_update(self, client: Unlayer) -> None: - with client.project.with_streaming_response.templates_update( - id="id", - ) as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - - project = response.parse() - assert_matches_type(ProjectTemplatesUpdateResponse, project, path=["response"]) - - assert cast(Any, response.is_closed) is True - - @parametrize - def test_path_params_templates_update(self, client: Unlayer) -> None: - with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): - client.project.with_raw_response.templates_update( - id="", - ) - - @parametrize - def test_method_workspaces_list(self, client: Unlayer) -> None: - project = client.project.workspaces_list() - assert_matches_type(ProjectWorkspacesListResponse, project, path=["response"]) - - @parametrize - def test_raw_response_workspaces_list(self, client: Unlayer) -> None: - response = client.project.with_raw_response.workspaces_list() - - assert response.is_closed is True - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - project = response.parse() - assert_matches_type(ProjectWorkspacesListResponse, project, path=["response"]) - - @parametrize - def test_streaming_response_workspaces_list(self, client: Unlayer) -> None: - with client.project.with_streaming_response.workspaces_list() as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - - project = response.parse() - assert_matches_type(ProjectWorkspacesListResponse, project, path=["response"]) - - assert cast(Any, response.is_closed) is True - - @parametrize - def test_method_workspaces_retrieve(self, client: Unlayer) -> None: - project = client.project.workspaces_retrieve( - "workspaceId", - ) - assert_matches_type(ProjectWorkspacesRetrieveResponse, project, path=["response"]) - - @parametrize - def test_raw_response_workspaces_retrieve(self, client: Unlayer) -> None: - response = client.project.with_raw_response.workspaces_retrieve( - "workspaceId", - ) - - assert response.is_closed is True - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - project = response.parse() - assert_matches_type(ProjectWorkspacesRetrieveResponse, project, path=["response"]) - - @parametrize - def test_streaming_response_workspaces_retrieve(self, client: Unlayer) -> None: - with client.project.with_streaming_response.workspaces_retrieve( - "workspaceId", - ) as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - - project = response.parse() - assert_matches_type(ProjectWorkspacesRetrieveResponse, project, path=["response"]) - - assert cast(Any, response.is_closed) is True - - @parametrize - def test_path_params_workspaces_retrieve(self, client: Unlayer) -> None: - with pytest.raises(ValueError, match=r"Expected a non-empty value for `workspace_id` but received ''"): - client.project.with_raw_response.workspaces_retrieve( - "", - ) - - -class TestAsyncProject: - parametrize = pytest.mark.parametrize( - "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] - ) - - @parametrize - async def test_method_current_list(self, async_client: AsyncUnlayer) -> None: - project = await async_client.project.current_list( - project_id="projectId", - ) - assert_matches_type(ProjectCurrentListResponse, project, path=["response"]) - - @parametrize - async def test_raw_response_current_list(self, async_client: AsyncUnlayer) -> None: - response = await async_client.project.with_raw_response.current_list( - project_id="projectId", - ) - - assert response.is_closed is True - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - project = await response.parse() - assert_matches_type(ProjectCurrentListResponse, project, path=["response"]) - - @parametrize - async def test_streaming_response_current_list(self, async_client: AsyncUnlayer) -> None: - async with async_client.project.with_streaming_response.current_list( - project_id="projectId", - ) as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - - project = await response.parse() - assert_matches_type(ProjectCurrentListResponse, project, path=["response"]) - - assert cast(Any, response.is_closed) is True - - @parametrize - async def test_method_domains_create(self, async_client: AsyncUnlayer) -> None: - project = await async_client.project.domains_create( - project_id="projectId", - domain="domain", - ) - assert_matches_type(ProjectDomainsCreateResponse, project, path=["response"]) - - @parametrize - async def test_raw_response_domains_create(self, async_client: AsyncUnlayer) -> None: - response = await async_client.project.with_raw_response.domains_create( - project_id="projectId", - domain="domain", - ) - - assert response.is_closed is True - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - project = await response.parse() - assert_matches_type(ProjectDomainsCreateResponse, project, path=["response"]) - - @parametrize - async def test_streaming_response_domains_create(self, async_client: AsyncUnlayer) -> None: - async with async_client.project.with_streaming_response.domains_create( - project_id="projectId", - domain="domain", - ) as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - - project = await response.parse() - assert_matches_type(ProjectDomainsCreateResponse, project, path=["response"]) - - assert cast(Any, response.is_closed) is True - - @parametrize - async def test_method_domains_delete(self, async_client: AsyncUnlayer) -> None: - project = await async_client.project.domains_delete( - "id", - ) - assert project is None - - @parametrize - async def test_raw_response_domains_delete(self, async_client: AsyncUnlayer) -> None: - response = await async_client.project.with_raw_response.domains_delete( - "id", - ) - - assert response.is_closed is True - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - project = await response.parse() - assert project is None - - @parametrize - async def test_streaming_response_domains_delete(self, async_client: AsyncUnlayer) -> None: - async with async_client.project.with_streaming_response.domains_delete( - "id", - ) as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - - project = await response.parse() - assert project is None - - assert cast(Any, response.is_closed) is True - - @parametrize - async def test_path_params_domains_delete(self, async_client: AsyncUnlayer) -> None: - with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): - await async_client.project.with_raw_response.domains_delete( - "", - ) - - @parametrize - async def test_method_domains_list(self, async_client: AsyncUnlayer) -> None: - project = await async_client.project.domains_list( - project_id="projectId", - ) - assert_matches_type(ProjectDomainsListResponse, project, path=["response"]) - - @parametrize - async def test_raw_response_domains_list(self, async_client: AsyncUnlayer) -> None: - response = await async_client.project.with_raw_response.domains_list( - project_id="projectId", - ) - - assert response.is_closed is True - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - project = await response.parse() - assert_matches_type(ProjectDomainsListResponse, project, path=["response"]) - - @parametrize - async def test_streaming_response_domains_list(self, async_client: AsyncUnlayer) -> None: - async with async_client.project.with_streaming_response.domains_list( - project_id="projectId", - ) as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - - project = await response.parse() - assert_matches_type(ProjectDomainsListResponse, project, path=["response"]) - - assert cast(Any, response.is_closed) is True - - @parametrize - async def test_method_domains_retrieve(self, async_client: AsyncUnlayer) -> None: - project = await async_client.project.domains_retrieve( - "id", - ) - assert_matches_type(ProjectDomainsRetrieveResponse, project, path=["response"]) - - @parametrize - async def test_raw_response_domains_retrieve(self, async_client: AsyncUnlayer) -> None: - response = await async_client.project.with_raw_response.domains_retrieve( - "id", - ) - - assert response.is_closed is True - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - project = await response.parse() - assert_matches_type(ProjectDomainsRetrieveResponse, project, path=["response"]) - - @parametrize - async def test_streaming_response_domains_retrieve(self, async_client: AsyncUnlayer) -> None: - async with async_client.project.with_streaming_response.domains_retrieve( - "id", - ) as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - - project = await response.parse() - assert_matches_type(ProjectDomainsRetrieveResponse, project, path=["response"]) - - assert cast(Any, response.is_closed) is True - - @parametrize - async def test_path_params_domains_retrieve(self, async_client: AsyncUnlayer) -> None: - with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): - await async_client.project.with_raw_response.domains_retrieve( - "", - ) - - @parametrize - async def test_method_domains_update(self, async_client: AsyncUnlayer) -> None: - project = await async_client.project.domains_update( - id="id", - ) - assert_matches_type(ProjectDomainsUpdateResponse, project, path=["response"]) - - @parametrize - async def test_method_domains_update_with_all_params(self, async_client: AsyncUnlayer) -> None: - project = await async_client.project.domains_update( - id="id", - domain="domain", - ) - assert_matches_type(ProjectDomainsUpdateResponse, project, path=["response"]) - - @parametrize - async def test_raw_response_domains_update(self, async_client: AsyncUnlayer) -> None: - response = await async_client.project.with_raw_response.domains_update( - id="id", - ) - - assert response.is_closed is True - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - project = await response.parse() - assert_matches_type(ProjectDomainsUpdateResponse, project, path=["response"]) - - @parametrize - async def test_streaming_response_domains_update(self, async_client: AsyncUnlayer) -> None: - async with async_client.project.with_streaming_response.domains_update( - id="id", - ) as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - - project = await response.parse() - assert_matches_type(ProjectDomainsUpdateResponse, project, path=["response"]) - - assert cast(Any, response.is_closed) is True - - @parametrize - async def test_path_params_domains_update(self, async_client: AsyncUnlayer) -> None: - with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): - await async_client.project.with_raw_response.domains_update( - id="", - ) - - @parametrize - async def test_method_templates_create(self, async_client: AsyncUnlayer) -> None: - project = await async_client.project.templates_create( - project_id="projectId", - name="name", - ) - assert_matches_type(ProjectTemplatesCreateResponse, project, path=["response"]) - - @parametrize - async def test_method_templates_create_with_all_params(self, async_client: AsyncUnlayer) -> None: - project = await async_client.project.templates_create( - project_id="projectId", - name="name", - display_mode="email", - ) - assert_matches_type(ProjectTemplatesCreateResponse, project, path=["response"]) - - @parametrize - async def test_raw_response_templates_create(self, async_client: AsyncUnlayer) -> None: - response = await async_client.project.with_raw_response.templates_create( - project_id="projectId", - name="name", - ) - - assert response.is_closed is True - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - project = await response.parse() - assert_matches_type(ProjectTemplatesCreateResponse, project, path=["response"]) - - @parametrize - async def test_streaming_response_templates_create(self, async_client: AsyncUnlayer) -> None: - async with async_client.project.with_streaming_response.templates_create( - project_id="projectId", - name="name", - ) as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - - project = await response.parse() - assert_matches_type(ProjectTemplatesCreateResponse, project, path=["response"]) - - assert cast(Any, response.is_closed) is True - - @parametrize - async def test_method_templates_delete(self, async_client: AsyncUnlayer) -> None: - project = await async_client.project.templates_delete( - "id", - ) - assert project is None - - @parametrize - async def test_raw_response_templates_delete(self, async_client: AsyncUnlayer) -> None: - response = await async_client.project.with_raw_response.templates_delete( - "id", - ) - - assert response.is_closed is True - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - project = await response.parse() - assert project is None - - @parametrize - async def test_streaming_response_templates_delete(self, async_client: AsyncUnlayer) -> None: - async with async_client.project.with_streaming_response.templates_delete( - "id", - ) as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - - project = await response.parse() - assert project is None - - assert cast(Any, response.is_closed) is True - - @parametrize - async def test_path_params_templates_delete(self, async_client: AsyncUnlayer) -> None: - with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): - await async_client.project.with_raw_response.templates_delete( - "", - ) - - @parametrize - async def test_method_templates_list(self, async_client: AsyncUnlayer) -> None: - project = await async_client.project.templates_list( - project_id="projectId", - ) - assert_matches_type(AsyncCursorPage[ProjectTemplatesListResponse], project, path=["response"]) - - @parametrize - async def test_method_templates_list_with_all_params(self, async_client: AsyncUnlayer) -> None: - project = await async_client.project.templates_list( - project_id="projectId", - cursor="cursor", - display_mode="email", - limit=1, - name="name", - ) - assert_matches_type(AsyncCursorPage[ProjectTemplatesListResponse], project, path=["response"]) - - @parametrize - async def test_raw_response_templates_list(self, async_client: AsyncUnlayer) -> None: - response = await async_client.project.with_raw_response.templates_list( - project_id="projectId", - ) - - assert response.is_closed is True - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - project = await response.parse() - assert_matches_type(AsyncCursorPage[ProjectTemplatesListResponse], project, path=["response"]) - - @parametrize - async def test_streaming_response_templates_list(self, async_client: AsyncUnlayer) -> None: - async with async_client.project.with_streaming_response.templates_list( - project_id="projectId", - ) as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - - project = await response.parse() - assert_matches_type(AsyncCursorPage[ProjectTemplatesListResponse], project, path=["response"]) - - assert cast(Any, response.is_closed) is True - - @parametrize - async def test_method_templates_retrieve(self, async_client: AsyncUnlayer) -> None: - project = await async_client.project.templates_retrieve( - "id", - ) - assert_matches_type(ProjectTemplatesRetrieveResponse, project, path=["response"]) - - @parametrize - async def test_raw_response_templates_retrieve(self, async_client: AsyncUnlayer) -> None: - response = await async_client.project.with_raw_response.templates_retrieve( - "id", - ) - - assert response.is_closed is True - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - project = await response.parse() - assert_matches_type(ProjectTemplatesRetrieveResponse, project, path=["response"]) - - @parametrize - async def test_streaming_response_templates_retrieve(self, async_client: AsyncUnlayer) -> None: - async with async_client.project.with_streaming_response.templates_retrieve( - "id", - ) as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - - project = await response.parse() - assert_matches_type(ProjectTemplatesRetrieveResponse, project, path=["response"]) - - assert cast(Any, response.is_closed) is True - - @parametrize - async def test_path_params_templates_retrieve(self, async_client: AsyncUnlayer) -> None: - with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): - await async_client.project.with_raw_response.templates_retrieve( - "", - ) - - @parametrize - async def test_method_templates_update(self, async_client: AsyncUnlayer) -> None: - project = await async_client.project.templates_update( - id="id", - ) - assert_matches_type(ProjectTemplatesUpdateResponse, project, path=["response"]) - - @parametrize - async def test_method_templates_update_with_all_params(self, async_client: AsyncUnlayer) -> None: - project = await async_client.project.templates_update( - id="id", - body="body", - name="name", - subject="subject", - ) - assert_matches_type(ProjectTemplatesUpdateResponse, project, path=["response"]) - - @parametrize - async def test_raw_response_templates_update(self, async_client: AsyncUnlayer) -> None: - response = await async_client.project.with_raw_response.templates_update( - id="id", - ) - - assert response.is_closed is True - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - project = await response.parse() - assert_matches_type(ProjectTemplatesUpdateResponse, project, path=["response"]) - - @parametrize - async def test_streaming_response_templates_update(self, async_client: AsyncUnlayer) -> None: - async with async_client.project.with_streaming_response.templates_update( - id="id", - ) as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - - project = await response.parse() - assert_matches_type(ProjectTemplatesUpdateResponse, project, path=["response"]) - - assert cast(Any, response.is_closed) is True - - @parametrize - async def test_path_params_templates_update(self, async_client: AsyncUnlayer) -> None: - with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): - await async_client.project.with_raw_response.templates_update( - id="", - ) - - @parametrize - async def test_method_workspaces_list(self, async_client: AsyncUnlayer) -> None: - project = await async_client.project.workspaces_list() - assert_matches_type(ProjectWorkspacesListResponse, project, path=["response"]) - - @parametrize - async def test_raw_response_workspaces_list(self, async_client: AsyncUnlayer) -> None: - response = await async_client.project.with_raw_response.workspaces_list() - - assert response.is_closed is True - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - project = await response.parse() - assert_matches_type(ProjectWorkspacesListResponse, project, path=["response"]) - - @parametrize - async def test_streaming_response_workspaces_list(self, async_client: AsyncUnlayer) -> None: - async with async_client.project.with_streaming_response.workspaces_list() as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - - project = await response.parse() - assert_matches_type(ProjectWorkspacesListResponse, project, path=["response"]) - - assert cast(Any, response.is_closed) is True - - @parametrize - async def test_method_workspaces_retrieve(self, async_client: AsyncUnlayer) -> None: - project = await async_client.project.workspaces_retrieve( - "workspaceId", - ) - assert_matches_type(ProjectWorkspacesRetrieveResponse, project, path=["response"]) - - @parametrize - async def test_raw_response_workspaces_retrieve(self, async_client: AsyncUnlayer) -> None: - response = await async_client.project.with_raw_response.workspaces_retrieve( - "workspaceId", - ) - - assert response.is_closed is True - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - project = await response.parse() - assert_matches_type(ProjectWorkspacesRetrieveResponse, project, path=["response"]) - - @parametrize - async def test_streaming_response_workspaces_retrieve(self, async_client: AsyncUnlayer) -> None: - async with async_client.project.with_streaming_response.workspaces_retrieve( - "workspaceId", - ) as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - - project = await response.parse() - assert_matches_type(ProjectWorkspacesRetrieveResponse, project, path=["response"]) - - assert cast(Any, response.is_closed) is True - - @parametrize - async def test_path_params_workspaces_retrieve(self, async_client: AsyncUnlayer) -> None: - with pytest.raises(ValueError, match=r"Expected a non-empty value for `workspace_id` but received ''"): - await async_client.project.with_raw_response.workspaces_retrieve( - "", - ) diff --git a/tests/test_client.py b/tests/test_client.py index cd37743..10efb22 100644 --- a/tests/test_client.py +++ b/tests/test_client.py @@ -891,20 +891,20 @@ def test_parse_retry_after_header( @mock.patch("unlayer._base_client.BaseClient._calculate_retry_timeout", _low_retry_timeout) @pytest.mark.respx(base_url=base_url) def test_retrying_timeout_errors_doesnt_leak(self, respx_mock: MockRouter, client: Unlayer) -> None: - respx_mock.get("/project/v1/current").mock(side_effect=httpx.TimeoutException("Test timeout error")) + respx_mock.post("/convert/full-to-simple").mock(side_effect=httpx.TimeoutException("Test timeout error")) with pytest.raises(APITimeoutError): - client.project.with_streaming_response.current_list(project_id="projectId").__enter__() + client.convert.full_to_simple.with_streaming_response.create(design={"body": {}}).__enter__() assert _get_open_connections(client) == 0 @mock.patch("unlayer._base_client.BaseClient._calculate_retry_timeout", _low_retry_timeout) @pytest.mark.respx(base_url=base_url) def test_retrying_status_errors_doesnt_leak(self, respx_mock: MockRouter, client: Unlayer) -> None: - respx_mock.get("/project/v1/current").mock(return_value=httpx.Response(500)) + respx_mock.post("/convert/full-to-simple").mock(return_value=httpx.Response(500)) with pytest.raises(APIStatusError): - client.project.with_streaming_response.current_list(project_id="projectId").__enter__() + client.convert.full_to_simple.with_streaming_response.create(design={"body": {}}).__enter__() assert _get_open_connections(client) == 0 @pytest.mark.parametrize("failures_before_success", [0, 2, 4]) @@ -931,9 +931,9 @@ def retry_handler(_request: httpx.Request) -> httpx.Response: return httpx.Response(500) return httpx.Response(200) - respx_mock.get("/project/v1/current").mock(side_effect=retry_handler) + respx_mock.post("/convert/full-to-simple").mock(side_effect=retry_handler) - response = client.project.with_raw_response.current_list(project_id="projectId") + response = client.convert.full_to_simple.with_raw_response.create(design={"body": {}}) assert response.retries_taken == failures_before_success assert int(response.http_request.headers.get("x-stainless-retry-count")) == failures_before_success @@ -955,10 +955,10 @@ def retry_handler(_request: httpx.Request) -> httpx.Response: return httpx.Response(500) return httpx.Response(200) - respx_mock.get("/project/v1/current").mock(side_effect=retry_handler) + respx_mock.post("/convert/full-to-simple").mock(side_effect=retry_handler) - response = client.project.with_raw_response.current_list( - project_id="projectId", extra_headers={"x-stainless-retry-count": Omit()} + response = client.convert.full_to_simple.with_raw_response.create( + design={"body": {}}, extra_headers={"x-stainless-retry-count": Omit()} ) assert len(response.http_request.headers.get_list("x-stainless-retry-count")) == 0 @@ -980,10 +980,10 @@ def retry_handler(_request: httpx.Request) -> httpx.Response: return httpx.Response(500) return httpx.Response(200) - respx_mock.get("/project/v1/current").mock(side_effect=retry_handler) + respx_mock.post("/convert/full-to-simple").mock(side_effect=retry_handler) - response = client.project.with_raw_response.current_list( - project_id="projectId", extra_headers={"x-stainless-retry-count": "42"} + response = client.convert.full_to_simple.with_raw_response.create( + design={"body": {}}, extra_headers={"x-stainless-retry-count": "42"} ) assert response.http_request.headers.get("x-stainless-retry-count") == "42" @@ -1827,20 +1827,20 @@ async def test_parse_retry_after_header( async def test_retrying_timeout_errors_doesnt_leak( self, respx_mock: MockRouter, async_client: AsyncUnlayer ) -> None: - respx_mock.get("/project/v1/current").mock(side_effect=httpx.TimeoutException("Test timeout error")) + respx_mock.post("/convert/full-to-simple").mock(side_effect=httpx.TimeoutException("Test timeout error")) with pytest.raises(APITimeoutError): - await async_client.project.with_streaming_response.current_list(project_id="projectId").__aenter__() + await async_client.convert.full_to_simple.with_streaming_response.create(design={"body": {}}).__aenter__() assert _get_open_connections(async_client) == 0 @mock.patch("unlayer._base_client.BaseClient._calculate_retry_timeout", _low_retry_timeout) @pytest.mark.respx(base_url=base_url) async def test_retrying_status_errors_doesnt_leak(self, respx_mock: MockRouter, async_client: AsyncUnlayer) -> None: - respx_mock.get("/project/v1/current").mock(return_value=httpx.Response(500)) + respx_mock.post("/convert/full-to-simple").mock(return_value=httpx.Response(500)) with pytest.raises(APIStatusError): - await async_client.project.with_streaming_response.current_list(project_id="projectId").__aenter__() + await async_client.convert.full_to_simple.with_streaming_response.create(design={"body": {}}).__aenter__() assert _get_open_connections(async_client) == 0 @pytest.mark.parametrize("failures_before_success", [0, 2, 4]) @@ -1867,9 +1867,9 @@ def retry_handler(_request: httpx.Request) -> httpx.Response: return httpx.Response(500) return httpx.Response(200) - respx_mock.get("/project/v1/current").mock(side_effect=retry_handler) + respx_mock.post("/convert/full-to-simple").mock(side_effect=retry_handler) - response = await client.project.with_raw_response.current_list(project_id="projectId") + response = await client.convert.full_to_simple.with_raw_response.create(design={"body": {}}) assert response.retries_taken == failures_before_success assert int(response.http_request.headers.get("x-stainless-retry-count")) == failures_before_success @@ -1891,10 +1891,10 @@ def retry_handler(_request: httpx.Request) -> httpx.Response: return httpx.Response(500) return httpx.Response(200) - respx_mock.get("/project/v1/current").mock(side_effect=retry_handler) + respx_mock.post("/convert/full-to-simple").mock(side_effect=retry_handler) - response = await client.project.with_raw_response.current_list( - project_id="projectId", extra_headers={"x-stainless-retry-count": Omit()} + response = await client.convert.full_to_simple.with_raw_response.create( + design={"body": {}}, extra_headers={"x-stainless-retry-count": Omit()} ) assert len(response.http_request.headers.get_list("x-stainless-retry-count")) == 0 @@ -1916,10 +1916,10 @@ def retry_handler(_request: httpx.Request) -> httpx.Response: return httpx.Response(500) return httpx.Response(200) - respx_mock.get("/project/v1/current").mock(side_effect=retry_handler) + respx_mock.post("/convert/full-to-simple").mock(side_effect=retry_handler) - response = await client.project.with_raw_response.current_list( - project_id="projectId", extra_headers={"x-stainless-retry-count": "42"} + response = await client.convert.full_to_simple.with_raw_response.create( + design={"body": {}}, extra_headers={"x-stainless-retry-count": "42"} ) assert response.http_request.headers.get("x-stainless-retry-count") == "42" From 348075ae44e483244dcb55f5f4cd552b944edbf7 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Sun, 15 Feb 2026 15:30:01 +0000 Subject: [PATCH 30/62] feat(api): api update --- .stats.yml | 8 +- README.md | 121 ++++- api.md | 201 +------ src/unlayer/_client.py | 187 ++----- src/unlayer/resources/__init__.py | 68 +-- .../resources/convert/full_to_simple.py | 14 +- .../resources/convert/simple_to_full.py | 4 +- src/unlayer/resources/documents/__init__.py | 47 -- src/unlayer/resources/documents/documents.py | 245 --------- src/unlayer/resources/documents/generate.py | 227 -------- .../resources/documents/generate_template.py | 213 -------- src/unlayer/resources/emails/__init__.py | 61 --- src/unlayer/resources/emails/emails.py | 277 ---------- src/unlayer/resources/emails/render.py | 201 ------- src/unlayer/resources/emails/send.py | 225 -------- src/unlayer/resources/emails/send_template.py | 219 -------- src/unlayer/resources/export/__init__.py | 75 --- src/unlayer/resources/export/export.py | 198 ------- src/unlayer/resources/export/html.py | 173 ------ src/unlayer/resources/export/image.py | 175 ------ src/unlayer/resources/export/pdf.py | 173 ------ src/unlayer/resources/export/zip.py | 173 ------ src/unlayer/resources/pages/__init__.py | 33 -- src/unlayer/resources/pages/pages.py | 102 ---- src/unlayer/resources/pages/render.py | 201 ------- src/unlayer/resources/project/__init__.py | 42 -- src/unlayer/resources/project/current.py | 175 ------ src/unlayer/resources/project/domains.py | 514 ------------------ src/unlayer/resources/project/project.py | 191 +++---- src/unlayer/resources/project/templates.py | 359 ++---------- .../resources/{project => }/workspaces.py | 22 +- src/unlayer/types/__init__.py | 8 +- .../convert/full_to_simple_create_params.py | 11 +- .../convert/simple_to_full_create_params.py | 4 +- src/unlayer/types/document_retrieve_params.py | 14 - .../types/document_retrieve_response.py | 41 -- src/unlayer/types/documents/__init__.py | 8 - .../types/documents/generate_create_params.py | 30 - .../documents/generate_create_response.py | 27 - .../generate_template_create_params.py | 24 - .../generate_template_create_response.py | 27 - src/unlayer/types/email_retrieve_response.py | 35 -- src/unlayer/types/emails/__init__.py | 10 - .../types/emails/render_create_params.py | 21 - .../types/emails/render_create_response.py | 16 - .../types/emails/send_create_params.py | 30 - .../types/emails/send_create_response.py | 21 - .../emails/send_template_create_params.py | 27 - .../emails/send_template_create_response.py | 21 - src/unlayer/types/export/__init__.py | 12 - .../types/export/html_retrieve_params.py | 14 - .../types/export/html_retrieve_response.py | 15 - .../types/export/image_retrieve_params.py | 14 - .../types/export/image_retrieve_response.py | 15 - .../types/export/pdf_retrieve_response.py | 15 - .../types/export/zip_retrieve_params.py | 14 - .../types/export/zip_retrieve_response.py | 15 - src/unlayer/types/pages/__init__.py | 6 - .../types/pages/render_create_params.py | 21 - .../types/pages/render_create_response.py | 16 - src/unlayer/types/project/__init__.py | 16 +- .../types/project/current_retrieve_params.py | 14 - .../types/project/domain_create_params.py | 17 - .../types/project/domain_create_response.py | 26 - .../types/project/domain_list_params.py | 14 - .../types/project/domain_list_response.py | 27 - .../types/project/domain_retrieve_response.py | 26 - .../types/project/domain_update_params.py | 12 - .../types/project/domain_update_response.py | 26 - .../types/project/template_create_params.py | 20 - .../types/project/template_create_response.py | 30 - .../types/project/template_list_response.py | 16 +- .../template_retrieve_params.py} | 4 +- .../project/template_retrieve_response.py | 13 +- .../types/project/template_update_params.py | 18 - .../types/project/template_update_response.py | 28 - ...e_params.py => project_retrieve_params.py} | 4 +- ...sponse.py => project_retrieve_response.py} | 10 +- .../{project => }/workspace_list_response.py | 2 +- .../workspace_retrieve_response.py | 2 +- .../convert/test_full_to_simple.py | 22 +- .../convert/test_simple_to_full.py | 20 +- tests/api_resources/documents/__init__.py | 1 - .../api_resources/documents/test_generate.py | 110 ---- .../documents/test_generate_template.py | 112 ---- tests/api_resources/emails/__init__.py | 1 - tests/api_resources/emails/test_render.py | 110 ---- tests/api_resources/emails/test_send.py | 116 ---- .../emails/test_send_template.py | 120 ---- tests/api_resources/export/__init__.py | 1 - tests/api_resources/export/test_html.py | 86 --- tests/api_resources/export/test_image.py | 86 --- tests/api_resources/export/test_pdf.py | 86 --- tests/api_resources/export/test_zip.py | 86 --- tests/api_resources/pages/__init__.py | 1 - tests/api_resources/pages/test_render.py | 110 ---- tests/api_resources/project/test_domains.py | 403 -------------- tests/api_resources/project/test_templates.py | 297 +--------- tests/api_resources/test_documents.py | 108 ---- tests/api_resources/test_emails.py | 108 ---- .../test_current.py => test_project.py} | 38 +- .../{project => }/test_workspaces.py | 30 +- tests/test_client.py | 48 +- 103 files changed, 466 insertions(+), 7415 deletions(-) delete mode 100644 src/unlayer/resources/documents/__init__.py delete mode 100644 src/unlayer/resources/documents/documents.py delete mode 100644 src/unlayer/resources/documents/generate.py delete mode 100644 src/unlayer/resources/documents/generate_template.py delete mode 100644 src/unlayer/resources/emails/__init__.py delete mode 100644 src/unlayer/resources/emails/emails.py delete mode 100644 src/unlayer/resources/emails/render.py delete mode 100644 src/unlayer/resources/emails/send.py delete mode 100644 src/unlayer/resources/emails/send_template.py delete mode 100644 src/unlayer/resources/export/__init__.py delete mode 100644 src/unlayer/resources/export/export.py delete mode 100644 src/unlayer/resources/export/html.py delete mode 100644 src/unlayer/resources/export/image.py delete mode 100644 src/unlayer/resources/export/pdf.py delete mode 100644 src/unlayer/resources/export/zip.py delete mode 100644 src/unlayer/resources/pages/__init__.py delete mode 100644 src/unlayer/resources/pages/pages.py delete mode 100644 src/unlayer/resources/pages/render.py delete mode 100644 src/unlayer/resources/project/current.py delete mode 100644 src/unlayer/resources/project/domains.py rename src/unlayer/resources/{project => }/workspaces.py (93%) delete mode 100644 src/unlayer/types/document_retrieve_params.py delete mode 100644 src/unlayer/types/document_retrieve_response.py delete mode 100644 src/unlayer/types/documents/__init__.py delete mode 100644 src/unlayer/types/documents/generate_create_params.py delete mode 100644 src/unlayer/types/documents/generate_create_response.py delete mode 100644 src/unlayer/types/documents/generate_template_create_params.py delete mode 100644 src/unlayer/types/documents/generate_template_create_response.py delete mode 100644 src/unlayer/types/email_retrieve_response.py delete mode 100644 src/unlayer/types/emails/__init__.py delete mode 100644 src/unlayer/types/emails/render_create_params.py delete mode 100644 src/unlayer/types/emails/render_create_response.py delete mode 100644 src/unlayer/types/emails/send_create_params.py delete mode 100644 src/unlayer/types/emails/send_create_response.py delete mode 100644 src/unlayer/types/emails/send_template_create_params.py delete mode 100644 src/unlayer/types/emails/send_template_create_response.py delete mode 100644 src/unlayer/types/export/__init__.py delete mode 100644 src/unlayer/types/export/html_retrieve_params.py delete mode 100644 src/unlayer/types/export/html_retrieve_response.py delete mode 100644 src/unlayer/types/export/image_retrieve_params.py delete mode 100644 src/unlayer/types/export/image_retrieve_response.py delete mode 100644 src/unlayer/types/export/pdf_retrieve_response.py delete mode 100644 src/unlayer/types/export/zip_retrieve_params.py delete mode 100644 src/unlayer/types/export/zip_retrieve_response.py delete mode 100644 src/unlayer/types/pages/__init__.py delete mode 100644 src/unlayer/types/pages/render_create_params.py delete mode 100644 src/unlayer/types/pages/render_create_response.py delete mode 100644 src/unlayer/types/project/current_retrieve_params.py delete mode 100644 src/unlayer/types/project/domain_create_params.py delete mode 100644 src/unlayer/types/project/domain_create_response.py delete mode 100644 src/unlayer/types/project/domain_list_params.py delete mode 100644 src/unlayer/types/project/domain_list_response.py delete mode 100644 src/unlayer/types/project/domain_retrieve_response.py delete mode 100644 src/unlayer/types/project/domain_update_params.py delete mode 100644 src/unlayer/types/project/domain_update_response.py delete mode 100644 src/unlayer/types/project/template_create_params.py delete mode 100644 src/unlayer/types/project/template_create_response.py rename src/unlayer/types/{export/pdf_retrieve_params.py => project/template_retrieve_params.py} (77%) delete mode 100644 src/unlayer/types/project/template_update_params.py delete mode 100644 src/unlayer/types/project/template_update_response.py rename src/unlayer/types/{email_retrieve_params.py => project_retrieve_params.py} (78%) rename src/unlayer/types/{project/current_retrieve_response.py => project_retrieve_response.py} (68%) rename src/unlayer/types/{project => }/workspace_list_response.py (91%) rename src/unlayer/types/{project => }/workspace_retrieve_response.py (94%) delete mode 100644 tests/api_resources/documents/__init__.py delete mode 100644 tests/api_resources/documents/test_generate.py delete mode 100644 tests/api_resources/documents/test_generate_template.py delete mode 100644 tests/api_resources/emails/__init__.py delete mode 100644 tests/api_resources/emails/test_render.py delete mode 100644 tests/api_resources/emails/test_send.py delete mode 100644 tests/api_resources/emails/test_send_template.py delete mode 100644 tests/api_resources/export/__init__.py delete mode 100644 tests/api_resources/export/test_html.py delete mode 100644 tests/api_resources/export/test_image.py delete mode 100644 tests/api_resources/export/test_pdf.py delete mode 100644 tests/api_resources/export/test_zip.py delete mode 100644 tests/api_resources/pages/__init__.py delete mode 100644 tests/api_resources/pages/test_render.py delete mode 100644 tests/api_resources/project/test_domains.py delete mode 100644 tests/api_resources/test_documents.py delete mode 100644 tests/api_resources/test_emails.py rename tests/api_resources/{project/test_current.py => test_project.py} (67%) rename tests/api_resources/{project => }/test_workspaces.py (81%) diff --git a/.stats.yml b/.stats.yml index c33024a..43eb3f0 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ -configured_endpoints: 27 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/unlayer%2Funlayer-077d753eb6d4f805b2f84bf887289213f91c1da5f5a3a4e1e36e1da3689efb46.yml -openapi_spec_hash: b210022cf72d9a38fe1baee599054518 -config_hash: 15a2f2b4c1b498b9b314d587d6a331d0 +configured_endpoints: 7 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/unlayer%2Funlayer-579dff50df9d2d3be275dc58817917d2efec68100883d139ae7d62908a24e5d6.yml +openapi_spec_hash: 8583074e5ea7cc31410a42c2c4550d7c +config_hash: 3c023f8805c0765c987ddcee566aabef diff --git a/README.md b/README.md index 2849fff..ef1d20e 100644 --- a/README.md +++ b/README.md @@ -37,10 +37,10 @@ client = Unlayer( environment="stage", ) -full_to_simple = client.convert.full_to_simple.create( - design={"body": {}}, +project = client.project.retrieve( + project_id="your-project-id", ) -print(full_to_simple.data) +print(project.data) ``` While you can provide a `access_token` keyword argument, @@ -65,10 +65,10 @@ client = AsyncUnlayer( async def main() -> None: - full_to_simple = await client.convert.full_to_simple.create( - design={"body": {}}, + project = await client.project.retrieve( + project_id="your-project-id", ) - print(full_to_simple.data) + print(project.data) asyncio.run(main()) @@ -103,10 +103,10 @@ async def main() -> None: ), # This is the default and can be omitted http_client=DefaultAioHttpClient(), ) as client: - full_to_simple = await client.convert.full_to_simple.create( - design={"body": {}}, + project = await client.project.retrieve( + project_id="your-project-id", ) - print(full_to_simple.data) + print(project.data) asyncio.run(main()) @@ -121,6 +121,81 @@ Nested request parameters are [TypedDicts](https://docs.python.org/3/library/typ Typed requests and responses provide autocomplete and documentation within your editor. If you would like to see type errors in VS Code to help catch bugs earlier, set `python.analysis.typeCheckingMode` to `basic`. +## Pagination + +List methods in the Unlayer API are paginated. + +This library provides auto-paginating iterators with each list response, so you do not have to request successive pages manually: + +```python +from unlayer import Unlayer + +client = Unlayer() + +all_templates = [] +# Automatically fetches more pages as needed. +for template in client.project.templates.list( + project_id="your-project-id", + limit=10, +): + # Do something with template here + all_templates.append(template) +print(all_templates) +``` + +Or, asynchronously: + +```python +import asyncio +from unlayer import AsyncUnlayer + +client = AsyncUnlayer() + + +async def main() -> None: + all_templates = [] + # Iterate through items across all pages, issuing requests as needed. + async for template in client.project.templates.list( + project_id="your-project-id", + limit=10, + ): + all_templates.append(template) + print(all_templates) + + +asyncio.run(main()) +``` + +Alternatively, you can use the `.has_next_page()`, `.next_page_info()`, or `.get_next_page()` methods for more granular control working with pages: + +```python +first_page = await client.project.templates.list( + project_id="your-project-id", + limit=10, +) +if first_page.has_next_page(): + print(f"will fetch next page using these details: {first_page.next_page_info()}") + next_page = await first_page.get_next_page() + print(f"number of items we just fetched: {len(next_page.data)}") + +# Remove `await` for non-async usage. +``` + +Or just work directly with the returned data: + +```python +first_page = await client.project.templates.list( + project_id="your-project-id", + limit=10, +) + +print(f"next page cursor: {first_page.next_cursor}") # => "next page cursor: ..." +for template in first_page.data: + print(template.id) + +# Remove `await` for non-async usage. +``` + ## Nested params Nested parameters are dictionaries, typed using `TypedDict`, for example: @@ -131,7 +206,7 @@ from unlayer import Unlayer client = Unlayer() full_to_simple = client.convert.full_to_simple.create( - design={"body": {}}, + design={"body": {"foo": "bar"}}, ) print(full_to_simple.design) ``` @@ -152,8 +227,8 @@ from unlayer import Unlayer client = Unlayer() try: - client.convert.full_to_simple.create( - design={"body": {}}, + client.project.retrieve( + project_id="your-project-id", ) except unlayer.APIConnectionError as e: print("The server could not be reached") @@ -197,8 +272,8 @@ client = Unlayer( ) # Or, configure per-request: -client.with_options(max_retries=5).convert.full_to_simple.create( - design={"body": {}}, +client.with_options(max_retries=5).project.retrieve( + project_id="your-project-id", ) ``` @@ -222,8 +297,8 @@ client = Unlayer( ) # Override per-request: -client.with_options(timeout=5.0).convert.full_to_simple.create( - design={"body": {}}, +client.with_options(timeout=5.0).project.retrieve( + project_id="your-project-id", ) ``` @@ -265,15 +340,13 @@ The "raw" Response object can be accessed by prefixing `.with_raw_response.` to from unlayer import Unlayer client = Unlayer() -response = client.convert.full_to_simple.with_raw_response.create( - design={ - "body": {} - }, +response = client.project.with_raw_response.retrieve( + project_id="your-project-id", ) print(response.headers.get('X-My-Header')) -full_to_simple = response.parse() # get the object that `convert.full_to_simple.create()` would have returned -print(full_to_simple.data) +project = response.parse() # get the object that `project.retrieve()` would have returned +print(project.data) ``` These methods return an [`APIResponse`](https://github.com/stainless-sdks/unlayer-python/tree/main/src/unlayer/_response.py) object. @@ -287,8 +360,8 @@ The above interface eagerly reads the full response body when you make the reque To stream the response body, use `.with_streaming_response` instead, which requires a context manager and only reads the response body once you call `.read()`, `.text()`, `.json()`, `.iter_bytes()`, `.iter_text()`, `.iter_lines()` or `.parse()`. In the async client, these are async methods. ```python -with client.convert.full_to_simple.with_streaming_response.create( - design={"body": {}}, +with client.project.with_streaming_response.retrieve( + project_id="your-project-id", ) as response: print(response.headers.get("X-My-Header")) diff --git a/api.md b/api.md index 050a5df..3e85079 100644 --- a/api.md +++ b/api.md @@ -10,7 +10,7 @@ from unlayer.types.convert import FullToSimpleCreateResponse Methods: -- client.convert.full_to_simple.create(\*\*params) -> FullToSimpleCreateResponse +- client.convert.full_to_simple.create(\*\*params) -> FullToSimpleCreateResponse ## SimpleToFull @@ -22,221 +22,42 @@ from unlayer.types.convert import SimpleToFullCreateResponse Methods: -- client.convert.simple_to_full.create(\*\*params) -> SimpleToFullCreateResponse - -# Documents - -Types: - -```python -from unlayer.types import DocumentRetrieveResponse -``` - -Methods: - -- client.documents.retrieve(id, \*\*params) -> DocumentRetrieveResponse - -## Generate - -Types: - -```python -from unlayer.types.documents import GenerateCreateResponse -``` - -Methods: - -- client.documents.generate.create(\*\*params) -> GenerateCreateResponse - -## GenerateTemplate - -Types: - -```python -from unlayer.types.documents import GenerateTemplateCreateResponse -``` - -Methods: - -- client.documents.generate_template.create(\*\*params) -> GenerateTemplateCreateResponse - -# Emails - -Types: - -```python -from unlayer.types import EmailRetrieveResponse -``` - -Methods: - -- client.emails.retrieve(id, \*\*params) -> EmailRetrieveResponse - -## Render - -Types: - -```python -from unlayer.types.emails import RenderCreateResponse -``` - -Methods: - -- client.emails.render.create(\*\*params) -> RenderCreateResponse - -## Send - -Types: - -```python -from unlayer.types.emails import SendCreateResponse -``` - -Methods: - -- client.emails.send.create(\*\*params) -> SendCreateResponse - -## SendTemplate - -Types: - -```python -from unlayer.types.emails import SendTemplateCreateResponse -``` - -Methods: - -- client.emails.send_template.create(\*\*params) -> SendTemplateCreateResponse - -# Export - -## HTML - -Types: - -```python -from unlayer.types.export import HTMLRetrieveResponse -``` - -Methods: - -- client.export.html.retrieve(\*\*params) -> HTMLRetrieveResponse - -## Image - -Types: - -```python -from unlayer.types.export import ImageRetrieveResponse -``` - -Methods: - -- client.export.image.retrieve(\*\*params) -> ImageRetrieveResponse - -## Pdf - -Types: - -```python -from unlayer.types.export import PdfRetrieveResponse -``` - -Methods: - -- client.export.pdf.retrieve(\*\*params) -> PdfRetrieveResponse - -## Zip - -Types: - -```python -from unlayer.types.export import ZipRetrieveResponse -``` - -Methods: - -- client.export.zip.retrieve(\*\*params) -> ZipRetrieveResponse - -# Pages - -## Render - -Types: - -```python -from unlayer.types.pages import RenderCreateResponse -``` - -Methods: - -- client.pages.render.create(\*\*params) -> RenderCreateResponse +- client.convert.simple_to_full.create(\*\*params) -> SimpleToFullCreateResponse # Project -## Current - -Types: - -```python -from unlayer.types.project import CurrentRetrieveResponse -``` - -Methods: - -- client.project.current.retrieve(\*\*params) -> CurrentRetrieveResponse - -## Domains - Types: ```python -from unlayer.types.project import ( - DomainCreateResponse, - DomainRetrieveResponse, - DomainUpdateResponse, - DomainListResponse, -) +from unlayer.types import ProjectRetrieveResponse ``` Methods: -- client.project.domains.create(\*\*params) -> DomainCreateResponse -- client.project.domains.retrieve(id) -> DomainRetrieveResponse -- client.project.domains.update(id, \*\*params) -> DomainUpdateResponse -- client.project.domains.list(\*\*params) -> DomainListResponse -- client.project.domains.delete(id) -> None +- client.project.retrieve(\*\*params) -> ProjectRetrieveResponse ## Templates Types: ```python -from unlayer.types.project import ( - TemplateCreateResponse, - TemplateRetrieveResponse, - TemplateUpdateResponse, - TemplateListResponse, -) +from unlayer.types.project import TemplateRetrieveResponse, TemplateListResponse ``` Methods: -- client.project.templates.create(\*\*params) -> TemplateCreateResponse -- client.project.templates.retrieve(id) -> TemplateRetrieveResponse -- client.project.templates.update(id, \*\*params) -> TemplateUpdateResponse -- client.project.templates.list(\*\*params) -> TemplateListResponse -- client.project.templates.delete(id) -> None +- client.project.templates.retrieve(id, \*\*params) -> TemplateRetrieveResponse +- client.project.templates.list(\*\*params) -> SyncCursorPage[TemplateListResponse] -## Workspaces +# Workspaces Types: ```python -from unlayer.types.project import WorkspaceRetrieveResponse, WorkspaceListResponse +from unlayer.types import WorkspaceRetrieveResponse, WorkspaceListResponse ``` Methods: -- client.project.workspaces.retrieve(workspace_id) -> WorkspaceRetrieveResponse -- client.project.workspaces.list() -> WorkspaceListResponse +- client.workspaces.retrieve(workspace_id) -> WorkspaceRetrieveResponse +- client.workspaces.list() -> WorkspaceListResponse diff --git a/src/unlayer/_client.py b/src/unlayer/_client.py index afabf32..e702e10 100644 --- a/src/unlayer/_client.py +++ b/src/unlayer/_client.py @@ -31,13 +31,10 @@ ) if TYPE_CHECKING: - from .resources import pages, emails, export, convert, project, documents - from .resources.pages.pages import PagesResource, AsyncPagesResource - from .resources.emails.emails import EmailsResource, AsyncEmailsResource - from .resources.export.export import ExportResource, AsyncExportResource + from .resources import convert, project, workspaces + from .resources.workspaces import WorkspacesResource, AsyncWorkspacesResource from .resources.convert.convert import ConvertResource, AsyncConvertResource from .resources.project.project import ProjectResource, AsyncProjectResource - from .resources.documents.documents import DocumentsResource, AsyncDocumentsResource __all__ = [ "ENVIRONMENTS", @@ -144,36 +141,18 @@ def convert(self) -> ConvertResource: return ConvertResource(self) - @cached_property - def documents(self) -> DocumentsResource: - from .resources.documents import DocumentsResource - - return DocumentsResource(self) - - @cached_property - def emails(self) -> EmailsResource: - from .resources.emails import EmailsResource - - return EmailsResource(self) - - @cached_property - def export(self) -> ExportResource: - from .resources.export import ExportResource - - return ExportResource(self) - - @cached_property - def pages(self) -> PagesResource: - from .resources.pages import PagesResource - - return PagesResource(self) - @cached_property def project(self) -> ProjectResource: from .resources.project import ProjectResource return ProjectResource(self) + @cached_property + def workspaces(self) -> WorkspacesResource: + from .resources.workspaces import WorkspacesResource + + return WorkspacesResource(self) + @cached_property def with_raw_response(self) -> UnlayerWithRawResponse: return UnlayerWithRawResponse(self) @@ -374,36 +353,18 @@ def convert(self) -> AsyncConvertResource: return AsyncConvertResource(self) - @cached_property - def documents(self) -> AsyncDocumentsResource: - from .resources.documents import AsyncDocumentsResource - - return AsyncDocumentsResource(self) - - @cached_property - def emails(self) -> AsyncEmailsResource: - from .resources.emails import AsyncEmailsResource - - return AsyncEmailsResource(self) - - @cached_property - def export(self) -> AsyncExportResource: - from .resources.export import AsyncExportResource - - return AsyncExportResource(self) - - @cached_property - def pages(self) -> AsyncPagesResource: - from .resources.pages import AsyncPagesResource - - return AsyncPagesResource(self) - @cached_property def project(self) -> AsyncProjectResource: from .resources.project import AsyncProjectResource return AsyncProjectResource(self) + @cached_property + def workspaces(self) -> AsyncWorkspacesResource: + from .resources.workspaces import AsyncWorkspacesResource + + return AsyncWorkspacesResource(self) + @cached_property def with_raw_response(self) -> AsyncUnlayerWithRawResponse: return AsyncUnlayerWithRawResponse(self) @@ -531,36 +492,18 @@ def convert(self) -> convert.ConvertResourceWithRawResponse: return ConvertResourceWithRawResponse(self._client.convert) - @cached_property - def documents(self) -> documents.DocumentsResourceWithRawResponse: - from .resources.documents import DocumentsResourceWithRawResponse - - return DocumentsResourceWithRawResponse(self._client.documents) - - @cached_property - def emails(self) -> emails.EmailsResourceWithRawResponse: - from .resources.emails import EmailsResourceWithRawResponse - - return EmailsResourceWithRawResponse(self._client.emails) - - @cached_property - def export(self) -> export.ExportResourceWithRawResponse: - from .resources.export import ExportResourceWithRawResponse - - return ExportResourceWithRawResponse(self._client.export) - - @cached_property - def pages(self) -> pages.PagesResourceWithRawResponse: - from .resources.pages import PagesResourceWithRawResponse - - return PagesResourceWithRawResponse(self._client.pages) - @cached_property def project(self) -> project.ProjectResourceWithRawResponse: from .resources.project import ProjectResourceWithRawResponse return ProjectResourceWithRawResponse(self._client.project) + @cached_property + def workspaces(self) -> workspaces.WorkspacesResourceWithRawResponse: + from .resources.workspaces import WorkspacesResourceWithRawResponse + + return WorkspacesResourceWithRawResponse(self._client.workspaces) + class AsyncUnlayerWithRawResponse: _client: AsyncUnlayer @@ -574,36 +517,18 @@ def convert(self) -> convert.AsyncConvertResourceWithRawResponse: return AsyncConvertResourceWithRawResponse(self._client.convert) - @cached_property - def documents(self) -> documents.AsyncDocumentsResourceWithRawResponse: - from .resources.documents import AsyncDocumentsResourceWithRawResponse - - return AsyncDocumentsResourceWithRawResponse(self._client.documents) - - @cached_property - def emails(self) -> emails.AsyncEmailsResourceWithRawResponse: - from .resources.emails import AsyncEmailsResourceWithRawResponse - - return AsyncEmailsResourceWithRawResponse(self._client.emails) - - @cached_property - def export(self) -> export.AsyncExportResourceWithRawResponse: - from .resources.export import AsyncExportResourceWithRawResponse - - return AsyncExportResourceWithRawResponse(self._client.export) - - @cached_property - def pages(self) -> pages.AsyncPagesResourceWithRawResponse: - from .resources.pages import AsyncPagesResourceWithRawResponse - - return AsyncPagesResourceWithRawResponse(self._client.pages) - @cached_property def project(self) -> project.AsyncProjectResourceWithRawResponse: from .resources.project import AsyncProjectResourceWithRawResponse return AsyncProjectResourceWithRawResponse(self._client.project) + @cached_property + def workspaces(self) -> workspaces.AsyncWorkspacesResourceWithRawResponse: + from .resources.workspaces import AsyncWorkspacesResourceWithRawResponse + + return AsyncWorkspacesResourceWithRawResponse(self._client.workspaces) + class UnlayerWithStreamedResponse: _client: Unlayer @@ -617,36 +542,18 @@ def convert(self) -> convert.ConvertResourceWithStreamingResponse: return ConvertResourceWithStreamingResponse(self._client.convert) - @cached_property - def documents(self) -> documents.DocumentsResourceWithStreamingResponse: - from .resources.documents import DocumentsResourceWithStreamingResponse - - return DocumentsResourceWithStreamingResponse(self._client.documents) - - @cached_property - def emails(self) -> emails.EmailsResourceWithStreamingResponse: - from .resources.emails import EmailsResourceWithStreamingResponse - - return EmailsResourceWithStreamingResponse(self._client.emails) - - @cached_property - def export(self) -> export.ExportResourceWithStreamingResponse: - from .resources.export import ExportResourceWithStreamingResponse - - return ExportResourceWithStreamingResponse(self._client.export) - - @cached_property - def pages(self) -> pages.PagesResourceWithStreamingResponse: - from .resources.pages import PagesResourceWithStreamingResponse - - return PagesResourceWithStreamingResponse(self._client.pages) - @cached_property def project(self) -> project.ProjectResourceWithStreamingResponse: from .resources.project import ProjectResourceWithStreamingResponse return ProjectResourceWithStreamingResponse(self._client.project) + @cached_property + def workspaces(self) -> workspaces.WorkspacesResourceWithStreamingResponse: + from .resources.workspaces import WorkspacesResourceWithStreamingResponse + + return WorkspacesResourceWithStreamingResponse(self._client.workspaces) + class AsyncUnlayerWithStreamedResponse: _client: AsyncUnlayer @@ -660,36 +567,18 @@ def convert(self) -> convert.AsyncConvertResourceWithStreamingResponse: return AsyncConvertResourceWithStreamingResponse(self._client.convert) - @cached_property - def documents(self) -> documents.AsyncDocumentsResourceWithStreamingResponse: - from .resources.documents import AsyncDocumentsResourceWithStreamingResponse - - return AsyncDocumentsResourceWithStreamingResponse(self._client.documents) - - @cached_property - def emails(self) -> emails.AsyncEmailsResourceWithStreamingResponse: - from .resources.emails import AsyncEmailsResourceWithStreamingResponse - - return AsyncEmailsResourceWithStreamingResponse(self._client.emails) - - @cached_property - def export(self) -> export.AsyncExportResourceWithStreamingResponse: - from .resources.export import AsyncExportResourceWithStreamingResponse - - return AsyncExportResourceWithStreamingResponse(self._client.export) - - @cached_property - def pages(self) -> pages.AsyncPagesResourceWithStreamingResponse: - from .resources.pages import AsyncPagesResourceWithStreamingResponse - - return AsyncPagesResourceWithStreamingResponse(self._client.pages) - @cached_property def project(self) -> project.AsyncProjectResourceWithStreamingResponse: from .resources.project import AsyncProjectResourceWithStreamingResponse return AsyncProjectResourceWithStreamingResponse(self._client.project) + @cached_property + def workspaces(self) -> workspaces.AsyncWorkspacesResourceWithStreamingResponse: + from .resources.workspaces import AsyncWorkspacesResourceWithStreamingResponse + + return AsyncWorkspacesResourceWithStreamingResponse(self._client.workspaces) + Client = Unlayer diff --git a/src/unlayer/resources/__init__.py b/src/unlayer/resources/__init__.py index 4c7d818..8efafd2 100644 --- a/src/unlayer/resources/__init__.py +++ b/src/unlayer/resources/__init__.py @@ -1,29 +1,5 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. -from .pages import ( - PagesResource, - AsyncPagesResource, - PagesResourceWithRawResponse, - AsyncPagesResourceWithRawResponse, - PagesResourceWithStreamingResponse, - AsyncPagesResourceWithStreamingResponse, -) -from .emails import ( - EmailsResource, - AsyncEmailsResource, - EmailsResourceWithRawResponse, - AsyncEmailsResourceWithRawResponse, - EmailsResourceWithStreamingResponse, - AsyncEmailsResourceWithStreamingResponse, -) -from .export import ( - ExportResource, - AsyncExportResource, - ExportResourceWithRawResponse, - AsyncExportResourceWithRawResponse, - ExportResourceWithStreamingResponse, - AsyncExportResourceWithStreamingResponse, -) from .convert import ( ConvertResource, AsyncConvertResource, @@ -40,13 +16,13 @@ ProjectResourceWithStreamingResponse, AsyncProjectResourceWithStreamingResponse, ) -from .documents import ( - DocumentsResource, - AsyncDocumentsResource, - DocumentsResourceWithRawResponse, - AsyncDocumentsResourceWithRawResponse, - DocumentsResourceWithStreamingResponse, - AsyncDocumentsResourceWithStreamingResponse, +from .workspaces import ( + WorkspacesResource, + AsyncWorkspacesResource, + WorkspacesResourceWithRawResponse, + AsyncWorkspacesResourceWithRawResponse, + WorkspacesResourceWithStreamingResponse, + AsyncWorkspacesResourceWithStreamingResponse, ) __all__ = [ @@ -56,34 +32,16 @@ "AsyncConvertResourceWithRawResponse", "ConvertResourceWithStreamingResponse", "AsyncConvertResourceWithStreamingResponse", - "DocumentsResource", - "AsyncDocumentsResource", - "DocumentsResourceWithRawResponse", - "AsyncDocumentsResourceWithRawResponse", - "DocumentsResourceWithStreamingResponse", - "AsyncDocumentsResourceWithStreamingResponse", - "EmailsResource", - "AsyncEmailsResource", - "EmailsResourceWithRawResponse", - "AsyncEmailsResourceWithRawResponse", - "EmailsResourceWithStreamingResponse", - "AsyncEmailsResourceWithStreamingResponse", - "ExportResource", - "AsyncExportResource", - "ExportResourceWithRawResponse", - "AsyncExportResourceWithRawResponse", - "ExportResourceWithStreamingResponse", - "AsyncExportResourceWithStreamingResponse", - "PagesResource", - "AsyncPagesResource", - "PagesResourceWithRawResponse", - "AsyncPagesResourceWithRawResponse", - "PagesResourceWithStreamingResponse", - "AsyncPagesResourceWithStreamingResponse", "ProjectResource", "AsyncProjectResource", "ProjectResourceWithRawResponse", "AsyncProjectResourceWithRawResponse", "ProjectResourceWithStreamingResponse", "AsyncProjectResourceWithStreamingResponse", + "WorkspacesResource", + "AsyncWorkspacesResource", + "WorkspacesResourceWithRawResponse", + "AsyncWorkspacesResourceWithRawResponse", + "WorkspacesResourceWithStreamingResponse", + "AsyncWorkspacesResourceWithStreamingResponse", ] diff --git a/src/unlayer/resources/convert/full_to_simple.py b/src/unlayer/resources/convert/full_to_simple.py index 9f5d104..0d793c7 100644 --- a/src/unlayer/resources/convert/full_to_simple.py +++ b/src/unlayer/resources/convert/full_to_simple.py @@ -48,6 +48,7 @@ def create( *, design: full_to_simple_create_params.Design, display_mode: Literal["email", "web", "popup", "document"] | Omit = omit, + include_conversion: bool | Omit = omit, include_default_values: bool | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. @@ -60,6 +61,9 @@ def create( Convert design json from Full to Simple schema. Args: + include_conversion: When true, includes \\__conversion metadata in the response. This metadata can be + passed to simple-to-full to restore original values without data loss. + extra_headers: Send extra headers extra_query: Add additional query parameters to the request @@ -69,11 +73,12 @@ def create( timeout: Override the client-level default timeout for this request, in seconds """ return self._post( - "/convert/full-to-simple", + "/v3/convert/full-to-simple", body=maybe_transform( { "design": design, "display_mode": display_mode, + "include_conversion": include_conversion, "include_default_values": include_default_values, }, full_to_simple_create_params.FullToSimpleCreateParams, @@ -110,6 +115,7 @@ async def create( *, design: full_to_simple_create_params.Design, display_mode: Literal["email", "web", "popup", "document"] | Omit = omit, + include_conversion: bool | Omit = omit, include_default_values: bool | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. @@ -122,6 +128,9 @@ async def create( Convert design json from Full to Simple schema. Args: + include_conversion: When true, includes \\__conversion metadata in the response. This metadata can be + passed to simple-to-full to restore original values without data loss. + extra_headers: Send extra headers extra_query: Add additional query parameters to the request @@ -131,11 +140,12 @@ async def create( timeout: Override the client-level default timeout for this request, in seconds """ return await self._post( - "/convert/full-to-simple", + "/v3/convert/full-to-simple", body=await async_maybe_transform( { "design": design, "display_mode": display_mode, + "include_conversion": include_conversion, "include_default_values": include_default_values, }, full_to_simple_create_params.FullToSimpleCreateParams, diff --git a/src/unlayer/resources/convert/simple_to_full.py b/src/unlayer/resources/convert/simple_to_full.py index dca40ad..29db149 100644 --- a/src/unlayer/resources/convert/simple_to_full.py +++ b/src/unlayer/resources/convert/simple_to_full.py @@ -69,7 +69,7 @@ def create( timeout: Override the client-level default timeout for this request, in seconds """ return self._post( - "/convert/simple-to-full", + "/v3/convert/simple-to-full", body=maybe_transform( { "design": design, @@ -131,7 +131,7 @@ async def create( timeout: Override the client-level default timeout for this request, in seconds """ return await self._post( - "/convert/simple-to-full", + "/v3/convert/simple-to-full", body=await async_maybe_transform( { "design": design, diff --git a/src/unlayer/resources/documents/__init__.py b/src/unlayer/resources/documents/__init__.py deleted file mode 100644 index 027ba26..0000000 --- a/src/unlayer/resources/documents/__init__.py +++ /dev/null @@ -1,47 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from .generate import ( - GenerateResource, - AsyncGenerateResource, - GenerateResourceWithRawResponse, - AsyncGenerateResourceWithRawResponse, - GenerateResourceWithStreamingResponse, - AsyncGenerateResourceWithStreamingResponse, -) -from .documents import ( - DocumentsResource, - AsyncDocumentsResource, - DocumentsResourceWithRawResponse, - AsyncDocumentsResourceWithRawResponse, - DocumentsResourceWithStreamingResponse, - AsyncDocumentsResourceWithStreamingResponse, -) -from .generate_template import ( - GenerateTemplateResource, - AsyncGenerateTemplateResource, - GenerateTemplateResourceWithRawResponse, - AsyncGenerateTemplateResourceWithRawResponse, - GenerateTemplateResourceWithStreamingResponse, - AsyncGenerateTemplateResourceWithStreamingResponse, -) - -__all__ = [ - "GenerateResource", - "AsyncGenerateResource", - "GenerateResourceWithRawResponse", - "AsyncGenerateResourceWithRawResponse", - "GenerateResourceWithStreamingResponse", - "AsyncGenerateResourceWithStreamingResponse", - "GenerateTemplateResource", - "AsyncGenerateTemplateResource", - "GenerateTemplateResourceWithRawResponse", - "AsyncGenerateTemplateResourceWithRawResponse", - "GenerateTemplateResourceWithStreamingResponse", - "AsyncGenerateTemplateResourceWithStreamingResponse", - "DocumentsResource", - "AsyncDocumentsResource", - "DocumentsResourceWithRawResponse", - "AsyncDocumentsResourceWithRawResponse", - "DocumentsResourceWithStreamingResponse", - "AsyncDocumentsResourceWithStreamingResponse", -] diff --git a/src/unlayer/resources/documents/documents.py b/src/unlayer/resources/documents/documents.py deleted file mode 100644 index 4a52db8..0000000 --- a/src/unlayer/resources/documents/documents.py +++ /dev/null @@ -1,245 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -import httpx - -from ...types import document_retrieve_params -from ..._types import Body, Query, Headers, NotGiven, not_given -from ..._utils import maybe_transform, async_maybe_transform -from .generate import ( - GenerateResource, - AsyncGenerateResource, - GenerateResourceWithRawResponse, - AsyncGenerateResourceWithRawResponse, - GenerateResourceWithStreamingResponse, - AsyncGenerateResourceWithStreamingResponse, -) -from ..._compat import cached_property -from ..._resource import SyncAPIResource, AsyncAPIResource -from ..._response import ( - to_raw_response_wrapper, - to_streamed_response_wrapper, - async_to_raw_response_wrapper, - async_to_streamed_response_wrapper, -) -from ..._base_client import make_request_options -from .generate_template import ( - GenerateTemplateResource, - AsyncGenerateTemplateResource, - GenerateTemplateResourceWithRawResponse, - AsyncGenerateTemplateResourceWithRawResponse, - GenerateTemplateResourceWithStreamingResponse, - AsyncGenerateTemplateResourceWithStreamingResponse, -) -from ...types.document_retrieve_response import DocumentRetrieveResponse - -__all__ = ["DocumentsResource", "AsyncDocumentsResource"] - - -class DocumentsResource(SyncAPIResource): - @cached_property - def generate(self) -> GenerateResource: - return GenerateResource(self._client) - - @cached_property - def generate_template(self) -> GenerateTemplateResource: - return GenerateTemplateResource(self._client) - - @cached_property - def with_raw_response(self) -> DocumentsResourceWithRawResponse: - """ - This property can be used as a prefix for any HTTP method call to return - the raw response object instead of the parsed content. - - For more information, see https://www.github.com/stainless-sdks/unlayer-python#accessing-raw-response-data-eg-headers - """ - return DocumentsResourceWithRawResponse(self) - - @cached_property - def with_streaming_response(self) -> DocumentsResourceWithStreamingResponse: - """ - An alternative to `.with_raw_response` that doesn't eagerly read the response body. - - For more information, see https://www.github.com/stainless-sdks/unlayer-python#with_streaming_response - """ - return DocumentsResourceWithStreamingResponse(self) - - def retrieve( - self, - id: str, - *, - project_id: str, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> DocumentRetrieveResponse: - """ - Retrieve details of a previously generated document. - - Args: - project_id: The project ID - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - if not id: - raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") - return self._get( - f"/documents/v1/documents/{id}", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query=maybe_transform({"project_id": project_id}, document_retrieve_params.DocumentRetrieveParams), - ), - cast_to=DocumentRetrieveResponse, - ) - - -class AsyncDocumentsResource(AsyncAPIResource): - @cached_property - def generate(self) -> AsyncGenerateResource: - return AsyncGenerateResource(self._client) - - @cached_property - def generate_template(self) -> AsyncGenerateTemplateResource: - return AsyncGenerateTemplateResource(self._client) - - @cached_property - def with_raw_response(self) -> AsyncDocumentsResourceWithRawResponse: - """ - This property can be used as a prefix for any HTTP method call to return - the raw response object instead of the parsed content. - - For more information, see https://www.github.com/stainless-sdks/unlayer-python#accessing-raw-response-data-eg-headers - """ - return AsyncDocumentsResourceWithRawResponse(self) - - @cached_property - def with_streaming_response(self) -> AsyncDocumentsResourceWithStreamingResponse: - """ - An alternative to `.with_raw_response` that doesn't eagerly read the response body. - - For more information, see https://www.github.com/stainless-sdks/unlayer-python#with_streaming_response - """ - return AsyncDocumentsResourceWithStreamingResponse(self) - - async def retrieve( - self, - id: str, - *, - project_id: str, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> DocumentRetrieveResponse: - """ - Retrieve details of a previously generated document. - - Args: - project_id: The project ID - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - if not id: - raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") - return await self._get( - f"/documents/v1/documents/{id}", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query=await async_maybe_transform( - {"project_id": project_id}, document_retrieve_params.DocumentRetrieveParams - ), - ), - cast_to=DocumentRetrieveResponse, - ) - - -class DocumentsResourceWithRawResponse: - def __init__(self, documents: DocumentsResource) -> None: - self._documents = documents - - self.retrieve = to_raw_response_wrapper( - documents.retrieve, - ) - - @cached_property - def generate(self) -> GenerateResourceWithRawResponse: - return GenerateResourceWithRawResponse(self._documents.generate) - - @cached_property - def generate_template(self) -> GenerateTemplateResourceWithRawResponse: - return GenerateTemplateResourceWithRawResponse(self._documents.generate_template) - - -class AsyncDocumentsResourceWithRawResponse: - def __init__(self, documents: AsyncDocumentsResource) -> None: - self._documents = documents - - self.retrieve = async_to_raw_response_wrapper( - documents.retrieve, - ) - - @cached_property - def generate(self) -> AsyncGenerateResourceWithRawResponse: - return AsyncGenerateResourceWithRawResponse(self._documents.generate) - - @cached_property - def generate_template(self) -> AsyncGenerateTemplateResourceWithRawResponse: - return AsyncGenerateTemplateResourceWithRawResponse(self._documents.generate_template) - - -class DocumentsResourceWithStreamingResponse: - def __init__(self, documents: DocumentsResource) -> None: - self._documents = documents - - self.retrieve = to_streamed_response_wrapper( - documents.retrieve, - ) - - @cached_property - def generate(self) -> GenerateResourceWithStreamingResponse: - return GenerateResourceWithStreamingResponse(self._documents.generate) - - @cached_property - def generate_template(self) -> GenerateTemplateResourceWithStreamingResponse: - return GenerateTemplateResourceWithStreamingResponse(self._documents.generate_template) - - -class AsyncDocumentsResourceWithStreamingResponse: - def __init__(self, documents: AsyncDocumentsResource) -> None: - self._documents = documents - - self.retrieve = async_to_streamed_response_wrapper( - documents.retrieve, - ) - - @cached_property - def generate(self) -> AsyncGenerateResourceWithStreamingResponse: - return AsyncGenerateResourceWithStreamingResponse(self._documents.generate) - - @cached_property - def generate_template(self) -> AsyncGenerateTemplateResourceWithStreamingResponse: - return AsyncGenerateTemplateResourceWithStreamingResponse(self._documents.generate_template) diff --git a/src/unlayer/resources/documents/generate.py b/src/unlayer/resources/documents/generate.py deleted file mode 100644 index 871ef14..0000000 --- a/src/unlayer/resources/documents/generate.py +++ /dev/null @@ -1,227 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing import Dict - -import httpx - -from ..._types import Body, Omit, Query, Headers, NotGiven, omit, not_given -from ..._utils import maybe_transform, async_maybe_transform -from ..._compat import cached_property -from ..._resource import SyncAPIResource, AsyncAPIResource -from ..._response import ( - to_raw_response_wrapper, - to_streamed_response_wrapper, - async_to_raw_response_wrapper, - async_to_streamed_response_wrapper, -) -from ..._base_client import make_request_options -from ...types.documents import generate_create_params -from ...types.documents.generate_create_response import GenerateCreateResponse - -__all__ = ["GenerateResource", "AsyncGenerateResource"] - - -class GenerateResource(SyncAPIResource): - @cached_property - def with_raw_response(self) -> GenerateResourceWithRawResponse: - """ - This property can be used as a prefix for any HTTP method call to return - the raw response object instead of the parsed content. - - For more information, see https://www.github.com/stainless-sdks/unlayer-python#accessing-raw-response-data-eg-headers - """ - return GenerateResourceWithRawResponse(self) - - @cached_property - def with_streaming_response(self) -> GenerateResourceWithStreamingResponse: - """ - An alternative to `.with_raw_response` that doesn't eagerly read the response body. - - For more information, see https://www.github.com/stainless-sdks/unlayer-python#with_streaming_response - """ - return GenerateResourceWithStreamingResponse(self) - - def create( - self, - *, - project_id: str, - design: Dict[str, object] | Omit = omit, - filename: str | Omit = omit, - html: str | Omit = omit, - merge_tags: Dict[str, str] | Omit = omit, - url: str | Omit = omit, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> GenerateCreateResponse: - """ - Generate PDF document from JSON design, HTML content, or URL. - - Args: - project_id: The project ID - - design: Proprietary design format JSON - - filename: Optional filename for the generated PDF - - html: HTML content to convert to PDF - - merge_tags: Optional merge tags for personalization - - url: URL to convert to PDF - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - return self._post( - "/documents/v1/generate", - body=maybe_transform( - { - "design": design, - "filename": filename, - "html": html, - "merge_tags": merge_tags, - "url": url, - }, - generate_create_params.GenerateCreateParams, - ), - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query=maybe_transform({"project_id": project_id}, generate_create_params.GenerateCreateParams), - ), - cast_to=GenerateCreateResponse, - ) - - -class AsyncGenerateResource(AsyncAPIResource): - @cached_property - def with_raw_response(self) -> AsyncGenerateResourceWithRawResponse: - """ - This property can be used as a prefix for any HTTP method call to return - the raw response object instead of the parsed content. - - For more information, see https://www.github.com/stainless-sdks/unlayer-python#accessing-raw-response-data-eg-headers - """ - return AsyncGenerateResourceWithRawResponse(self) - - @cached_property - def with_streaming_response(self) -> AsyncGenerateResourceWithStreamingResponse: - """ - An alternative to `.with_raw_response` that doesn't eagerly read the response body. - - For more information, see https://www.github.com/stainless-sdks/unlayer-python#with_streaming_response - """ - return AsyncGenerateResourceWithStreamingResponse(self) - - async def create( - self, - *, - project_id: str, - design: Dict[str, object] | Omit = omit, - filename: str | Omit = omit, - html: str | Omit = omit, - merge_tags: Dict[str, str] | Omit = omit, - url: str | Omit = omit, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> GenerateCreateResponse: - """ - Generate PDF document from JSON design, HTML content, or URL. - - Args: - project_id: The project ID - - design: Proprietary design format JSON - - filename: Optional filename for the generated PDF - - html: HTML content to convert to PDF - - merge_tags: Optional merge tags for personalization - - url: URL to convert to PDF - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - return await self._post( - "/documents/v1/generate", - body=await async_maybe_transform( - { - "design": design, - "filename": filename, - "html": html, - "merge_tags": merge_tags, - "url": url, - }, - generate_create_params.GenerateCreateParams, - ), - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query=await async_maybe_transform( - {"project_id": project_id}, generate_create_params.GenerateCreateParams - ), - ), - cast_to=GenerateCreateResponse, - ) - - -class GenerateResourceWithRawResponse: - def __init__(self, generate: GenerateResource) -> None: - self._generate = generate - - self.create = to_raw_response_wrapper( - generate.create, - ) - - -class AsyncGenerateResourceWithRawResponse: - def __init__(self, generate: AsyncGenerateResource) -> None: - self._generate = generate - - self.create = async_to_raw_response_wrapper( - generate.create, - ) - - -class GenerateResourceWithStreamingResponse: - def __init__(self, generate: GenerateResource) -> None: - self._generate = generate - - self.create = to_streamed_response_wrapper( - generate.create, - ) - - -class AsyncGenerateResourceWithStreamingResponse: - def __init__(self, generate: AsyncGenerateResource) -> None: - self._generate = generate - - self.create = async_to_streamed_response_wrapper( - generate.create, - ) diff --git a/src/unlayer/resources/documents/generate_template.py b/src/unlayer/resources/documents/generate_template.py deleted file mode 100644 index 74defa8..0000000 --- a/src/unlayer/resources/documents/generate_template.py +++ /dev/null @@ -1,213 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing import Dict - -import httpx - -from ..._types import Body, Omit, Query, Headers, NotGiven, omit, not_given -from ..._utils import maybe_transform, async_maybe_transform -from ..._compat import cached_property -from ..._resource import SyncAPIResource, AsyncAPIResource -from ..._response import ( - to_raw_response_wrapper, - to_streamed_response_wrapper, - async_to_raw_response_wrapper, - async_to_streamed_response_wrapper, -) -from ..._base_client import make_request_options -from ...types.documents import generate_template_create_params -from ...types.documents.generate_template_create_response import GenerateTemplateCreateResponse - -__all__ = ["GenerateTemplateResource", "AsyncGenerateTemplateResource"] - - -class GenerateTemplateResource(SyncAPIResource): - @cached_property - def with_raw_response(self) -> GenerateTemplateResourceWithRawResponse: - """ - This property can be used as a prefix for any HTTP method call to return - the raw response object instead of the parsed content. - - For more information, see https://www.github.com/stainless-sdks/unlayer-python#accessing-raw-response-data-eg-headers - """ - return GenerateTemplateResourceWithRawResponse(self) - - @cached_property - def with_streaming_response(self) -> GenerateTemplateResourceWithStreamingResponse: - """ - An alternative to `.with_raw_response` that doesn't eagerly read the response body. - - For more information, see https://www.github.com/stainless-sdks/unlayer-python#with_streaming_response - """ - return GenerateTemplateResourceWithStreamingResponse(self) - - def create( - self, - *, - project_id: str, - template_id: str, - filename: str | Omit = omit, - merge_tags: Dict[str, str] | Omit = omit, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> GenerateTemplateCreateResponse: - """ - Generate PDF document from an existing template with merge tags. - - Args: - project_id: The project ID - - template_id: ID of the template to use for generation - - filename: Optional filename for the generated PDF - - merge_tags: Optional merge tags for personalization - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - return self._post( - "/documents/v1/generate/template", - body=maybe_transform( - { - "template_id": template_id, - "filename": filename, - "merge_tags": merge_tags, - }, - generate_template_create_params.GenerateTemplateCreateParams, - ), - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query=maybe_transform( - {"project_id": project_id}, generate_template_create_params.GenerateTemplateCreateParams - ), - ), - cast_to=GenerateTemplateCreateResponse, - ) - - -class AsyncGenerateTemplateResource(AsyncAPIResource): - @cached_property - def with_raw_response(self) -> AsyncGenerateTemplateResourceWithRawResponse: - """ - This property can be used as a prefix for any HTTP method call to return - the raw response object instead of the parsed content. - - For more information, see https://www.github.com/stainless-sdks/unlayer-python#accessing-raw-response-data-eg-headers - """ - return AsyncGenerateTemplateResourceWithRawResponse(self) - - @cached_property - def with_streaming_response(self) -> AsyncGenerateTemplateResourceWithStreamingResponse: - """ - An alternative to `.with_raw_response` that doesn't eagerly read the response body. - - For more information, see https://www.github.com/stainless-sdks/unlayer-python#with_streaming_response - """ - return AsyncGenerateTemplateResourceWithStreamingResponse(self) - - async def create( - self, - *, - project_id: str, - template_id: str, - filename: str | Omit = omit, - merge_tags: Dict[str, str] | Omit = omit, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> GenerateTemplateCreateResponse: - """ - Generate PDF document from an existing template with merge tags. - - Args: - project_id: The project ID - - template_id: ID of the template to use for generation - - filename: Optional filename for the generated PDF - - merge_tags: Optional merge tags for personalization - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - return await self._post( - "/documents/v1/generate/template", - body=await async_maybe_transform( - { - "template_id": template_id, - "filename": filename, - "merge_tags": merge_tags, - }, - generate_template_create_params.GenerateTemplateCreateParams, - ), - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query=await async_maybe_transform( - {"project_id": project_id}, generate_template_create_params.GenerateTemplateCreateParams - ), - ), - cast_to=GenerateTemplateCreateResponse, - ) - - -class GenerateTemplateResourceWithRawResponse: - def __init__(self, generate_template: GenerateTemplateResource) -> None: - self._generate_template = generate_template - - self.create = to_raw_response_wrapper( - generate_template.create, - ) - - -class AsyncGenerateTemplateResourceWithRawResponse: - def __init__(self, generate_template: AsyncGenerateTemplateResource) -> None: - self._generate_template = generate_template - - self.create = async_to_raw_response_wrapper( - generate_template.create, - ) - - -class GenerateTemplateResourceWithStreamingResponse: - def __init__(self, generate_template: GenerateTemplateResource) -> None: - self._generate_template = generate_template - - self.create = to_streamed_response_wrapper( - generate_template.create, - ) - - -class AsyncGenerateTemplateResourceWithStreamingResponse: - def __init__(self, generate_template: AsyncGenerateTemplateResource) -> None: - self._generate_template = generate_template - - self.create = async_to_streamed_response_wrapper( - generate_template.create, - ) diff --git a/src/unlayer/resources/emails/__init__.py b/src/unlayer/resources/emails/__init__.py deleted file mode 100644 index 4e678d4..0000000 --- a/src/unlayer/resources/emails/__init__.py +++ /dev/null @@ -1,61 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from .send import ( - SendResource, - AsyncSendResource, - SendResourceWithRawResponse, - AsyncSendResourceWithRawResponse, - SendResourceWithStreamingResponse, - AsyncSendResourceWithStreamingResponse, -) -from .emails import ( - EmailsResource, - AsyncEmailsResource, - EmailsResourceWithRawResponse, - AsyncEmailsResourceWithRawResponse, - EmailsResourceWithStreamingResponse, - AsyncEmailsResourceWithStreamingResponse, -) -from .render import ( - RenderResource, - AsyncRenderResource, - RenderResourceWithRawResponse, - AsyncRenderResourceWithRawResponse, - RenderResourceWithStreamingResponse, - AsyncRenderResourceWithStreamingResponse, -) -from .send_template import ( - SendTemplateResource, - AsyncSendTemplateResource, - SendTemplateResourceWithRawResponse, - AsyncSendTemplateResourceWithRawResponse, - SendTemplateResourceWithStreamingResponse, - AsyncSendTemplateResourceWithStreamingResponse, -) - -__all__ = [ - "RenderResource", - "AsyncRenderResource", - "RenderResourceWithRawResponse", - "AsyncRenderResourceWithRawResponse", - "RenderResourceWithStreamingResponse", - "AsyncRenderResourceWithStreamingResponse", - "SendResource", - "AsyncSendResource", - "SendResourceWithRawResponse", - "AsyncSendResourceWithRawResponse", - "SendResourceWithStreamingResponse", - "AsyncSendResourceWithStreamingResponse", - "SendTemplateResource", - "AsyncSendTemplateResource", - "SendTemplateResourceWithRawResponse", - "AsyncSendTemplateResourceWithRawResponse", - "SendTemplateResourceWithStreamingResponse", - "AsyncSendTemplateResourceWithStreamingResponse", - "EmailsResource", - "AsyncEmailsResource", - "EmailsResourceWithRawResponse", - "AsyncEmailsResourceWithRawResponse", - "EmailsResourceWithStreamingResponse", - "AsyncEmailsResourceWithStreamingResponse", -] diff --git a/src/unlayer/resources/emails/emails.py b/src/unlayer/resources/emails/emails.py deleted file mode 100644 index 607c23f..0000000 --- a/src/unlayer/resources/emails/emails.py +++ /dev/null @@ -1,277 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -import httpx - -from .send import ( - SendResource, - AsyncSendResource, - SendResourceWithRawResponse, - AsyncSendResourceWithRawResponse, - SendResourceWithStreamingResponse, - AsyncSendResourceWithStreamingResponse, -) -from .render import ( - RenderResource, - AsyncRenderResource, - RenderResourceWithRawResponse, - AsyncRenderResourceWithRawResponse, - RenderResourceWithStreamingResponse, - AsyncRenderResourceWithStreamingResponse, -) -from ...types import email_retrieve_params -from ..._types import Body, Query, Headers, NotGiven, not_given -from ..._utils import maybe_transform, async_maybe_transform -from ..._compat import cached_property -from ..._resource import SyncAPIResource, AsyncAPIResource -from ..._response import ( - to_raw_response_wrapper, - to_streamed_response_wrapper, - async_to_raw_response_wrapper, - async_to_streamed_response_wrapper, -) -from .send_template import ( - SendTemplateResource, - AsyncSendTemplateResource, - SendTemplateResourceWithRawResponse, - AsyncSendTemplateResourceWithRawResponse, - SendTemplateResourceWithStreamingResponse, - AsyncSendTemplateResourceWithStreamingResponse, -) -from ..._base_client import make_request_options -from ...types.email_retrieve_response import EmailRetrieveResponse - -__all__ = ["EmailsResource", "AsyncEmailsResource"] - - -class EmailsResource(SyncAPIResource): - @cached_property - def render(self) -> RenderResource: - return RenderResource(self._client) - - @cached_property - def send(self) -> SendResource: - return SendResource(self._client) - - @cached_property - def send_template(self) -> SendTemplateResource: - return SendTemplateResource(self._client) - - @cached_property - def with_raw_response(self) -> EmailsResourceWithRawResponse: - """ - This property can be used as a prefix for any HTTP method call to return - the raw response object instead of the parsed content. - - For more information, see https://www.github.com/stainless-sdks/unlayer-python#accessing-raw-response-data-eg-headers - """ - return EmailsResourceWithRawResponse(self) - - @cached_property - def with_streaming_response(self) -> EmailsResourceWithStreamingResponse: - """ - An alternative to `.with_raw_response` that doesn't eagerly read the response body. - - For more information, see https://www.github.com/stainless-sdks/unlayer-python#with_streaming_response - """ - return EmailsResourceWithStreamingResponse(self) - - def retrieve( - self, - id: str, - *, - project_id: str, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> EmailRetrieveResponse: - """ - Retrieve details of a previously sent email. - - Args: - project_id: The project ID - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - if not id: - raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") - return self._get( - f"/emails/v1/emails/{id}", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query=maybe_transform({"project_id": project_id}, email_retrieve_params.EmailRetrieveParams), - ), - cast_to=EmailRetrieveResponse, - ) - - -class AsyncEmailsResource(AsyncAPIResource): - @cached_property - def render(self) -> AsyncRenderResource: - return AsyncRenderResource(self._client) - - @cached_property - def send(self) -> AsyncSendResource: - return AsyncSendResource(self._client) - - @cached_property - def send_template(self) -> AsyncSendTemplateResource: - return AsyncSendTemplateResource(self._client) - - @cached_property - def with_raw_response(self) -> AsyncEmailsResourceWithRawResponse: - """ - This property can be used as a prefix for any HTTP method call to return - the raw response object instead of the parsed content. - - For more information, see https://www.github.com/stainless-sdks/unlayer-python#accessing-raw-response-data-eg-headers - """ - return AsyncEmailsResourceWithRawResponse(self) - - @cached_property - def with_streaming_response(self) -> AsyncEmailsResourceWithStreamingResponse: - """ - An alternative to `.with_raw_response` that doesn't eagerly read the response body. - - For more information, see https://www.github.com/stainless-sdks/unlayer-python#with_streaming_response - """ - return AsyncEmailsResourceWithStreamingResponse(self) - - async def retrieve( - self, - id: str, - *, - project_id: str, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> EmailRetrieveResponse: - """ - Retrieve details of a previously sent email. - - Args: - project_id: The project ID - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - if not id: - raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") - return await self._get( - f"/emails/v1/emails/{id}", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query=await async_maybe_transform( - {"project_id": project_id}, email_retrieve_params.EmailRetrieveParams - ), - ), - cast_to=EmailRetrieveResponse, - ) - - -class EmailsResourceWithRawResponse: - def __init__(self, emails: EmailsResource) -> None: - self._emails = emails - - self.retrieve = to_raw_response_wrapper( - emails.retrieve, - ) - - @cached_property - def render(self) -> RenderResourceWithRawResponse: - return RenderResourceWithRawResponse(self._emails.render) - - @cached_property - def send(self) -> SendResourceWithRawResponse: - return SendResourceWithRawResponse(self._emails.send) - - @cached_property - def send_template(self) -> SendTemplateResourceWithRawResponse: - return SendTemplateResourceWithRawResponse(self._emails.send_template) - - -class AsyncEmailsResourceWithRawResponse: - def __init__(self, emails: AsyncEmailsResource) -> None: - self._emails = emails - - self.retrieve = async_to_raw_response_wrapper( - emails.retrieve, - ) - - @cached_property - def render(self) -> AsyncRenderResourceWithRawResponse: - return AsyncRenderResourceWithRawResponse(self._emails.render) - - @cached_property - def send(self) -> AsyncSendResourceWithRawResponse: - return AsyncSendResourceWithRawResponse(self._emails.send) - - @cached_property - def send_template(self) -> AsyncSendTemplateResourceWithRawResponse: - return AsyncSendTemplateResourceWithRawResponse(self._emails.send_template) - - -class EmailsResourceWithStreamingResponse: - def __init__(self, emails: EmailsResource) -> None: - self._emails = emails - - self.retrieve = to_streamed_response_wrapper( - emails.retrieve, - ) - - @cached_property - def render(self) -> RenderResourceWithStreamingResponse: - return RenderResourceWithStreamingResponse(self._emails.render) - - @cached_property - def send(self) -> SendResourceWithStreamingResponse: - return SendResourceWithStreamingResponse(self._emails.send) - - @cached_property - def send_template(self) -> SendTemplateResourceWithStreamingResponse: - return SendTemplateResourceWithStreamingResponse(self._emails.send_template) - - -class AsyncEmailsResourceWithStreamingResponse: - def __init__(self, emails: AsyncEmailsResource) -> None: - self._emails = emails - - self.retrieve = async_to_streamed_response_wrapper( - emails.retrieve, - ) - - @cached_property - def render(self) -> AsyncRenderResourceWithStreamingResponse: - return AsyncRenderResourceWithStreamingResponse(self._emails.render) - - @cached_property - def send(self) -> AsyncSendResourceWithStreamingResponse: - return AsyncSendResourceWithStreamingResponse(self._emails.send) - - @cached_property - def send_template(self) -> AsyncSendTemplateResourceWithStreamingResponse: - return AsyncSendTemplateResourceWithStreamingResponse(self._emails.send_template) diff --git a/src/unlayer/resources/emails/render.py b/src/unlayer/resources/emails/render.py deleted file mode 100644 index 7181e1a..0000000 --- a/src/unlayer/resources/emails/render.py +++ /dev/null @@ -1,201 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing import Dict - -import httpx - -from ..._types import Body, Omit, Query, Headers, NotGiven, omit, not_given -from ..._utils import maybe_transform, async_maybe_transform -from ..._compat import cached_property -from ..._resource import SyncAPIResource, AsyncAPIResource -from ..._response import ( - to_raw_response_wrapper, - to_streamed_response_wrapper, - async_to_raw_response_wrapper, - async_to_streamed_response_wrapper, -) -from ..._base_client import make_request_options -from ...types.emails import render_create_params -from ...types.emails.render_create_response import RenderCreateResponse - -__all__ = ["RenderResource", "AsyncRenderResource"] - - -class RenderResource(SyncAPIResource): - @cached_property - def with_raw_response(self) -> RenderResourceWithRawResponse: - """ - This property can be used as a prefix for any HTTP method call to return - the raw response object instead of the parsed content. - - For more information, see https://www.github.com/stainless-sdks/unlayer-python#accessing-raw-response-data-eg-headers - """ - return RenderResourceWithRawResponse(self) - - @cached_property - def with_streaming_response(self) -> RenderResourceWithStreamingResponse: - """ - An alternative to `.with_raw_response` that doesn't eagerly read the response body. - - For more information, see https://www.github.com/stainless-sdks/unlayer-python#with_streaming_response - """ - return RenderResourceWithStreamingResponse(self) - - def create( - self, - *, - project_id: str, - design: Dict[str, object], - merge_tags: Dict[str, str] | Omit = omit, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> RenderCreateResponse: - """ - Convert design JSON to HTML with optional merge tags. - - Args: - project_id: The project ID - - design: Proprietary design format JSON - - merge_tags: Optional merge tags for personalization - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - return self._post( - "/emails/v1/render", - body=maybe_transform( - { - "design": design, - "merge_tags": merge_tags, - }, - render_create_params.RenderCreateParams, - ), - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query=maybe_transform({"project_id": project_id}, render_create_params.RenderCreateParams), - ), - cast_to=RenderCreateResponse, - ) - - -class AsyncRenderResource(AsyncAPIResource): - @cached_property - def with_raw_response(self) -> AsyncRenderResourceWithRawResponse: - """ - This property can be used as a prefix for any HTTP method call to return - the raw response object instead of the parsed content. - - For more information, see https://www.github.com/stainless-sdks/unlayer-python#accessing-raw-response-data-eg-headers - """ - return AsyncRenderResourceWithRawResponse(self) - - @cached_property - def with_streaming_response(self) -> AsyncRenderResourceWithStreamingResponse: - """ - An alternative to `.with_raw_response` that doesn't eagerly read the response body. - - For more information, see https://www.github.com/stainless-sdks/unlayer-python#with_streaming_response - """ - return AsyncRenderResourceWithStreamingResponse(self) - - async def create( - self, - *, - project_id: str, - design: Dict[str, object], - merge_tags: Dict[str, str] | Omit = omit, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> RenderCreateResponse: - """ - Convert design JSON to HTML with optional merge tags. - - Args: - project_id: The project ID - - design: Proprietary design format JSON - - merge_tags: Optional merge tags for personalization - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - return await self._post( - "/emails/v1/render", - body=await async_maybe_transform( - { - "design": design, - "merge_tags": merge_tags, - }, - render_create_params.RenderCreateParams, - ), - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query=await async_maybe_transform({"project_id": project_id}, render_create_params.RenderCreateParams), - ), - cast_to=RenderCreateResponse, - ) - - -class RenderResourceWithRawResponse: - def __init__(self, render: RenderResource) -> None: - self._render = render - - self.create = to_raw_response_wrapper( - render.create, - ) - - -class AsyncRenderResourceWithRawResponse: - def __init__(self, render: AsyncRenderResource) -> None: - self._render = render - - self.create = async_to_raw_response_wrapper( - render.create, - ) - - -class RenderResourceWithStreamingResponse: - def __init__(self, render: RenderResource) -> None: - self._render = render - - self.create = to_streamed_response_wrapper( - render.create, - ) - - -class AsyncRenderResourceWithStreamingResponse: - def __init__(self, render: AsyncRenderResource) -> None: - self._render = render - - self.create = async_to_streamed_response_wrapper( - render.create, - ) diff --git a/src/unlayer/resources/emails/send.py b/src/unlayer/resources/emails/send.py deleted file mode 100644 index dc53469..0000000 --- a/src/unlayer/resources/emails/send.py +++ /dev/null @@ -1,225 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing import Dict - -import httpx - -from ..._types import Body, Omit, Query, Headers, NotGiven, omit, not_given -from ..._utils import maybe_transform, async_maybe_transform -from ..._compat import cached_property -from ..._resource import SyncAPIResource, AsyncAPIResource -from ..._response import ( - to_raw_response_wrapper, - to_streamed_response_wrapper, - async_to_raw_response_wrapper, - async_to_streamed_response_wrapper, -) -from ..._base_client import make_request_options -from ...types.emails import send_create_params -from ...types.emails.send_create_response import SendCreateResponse - -__all__ = ["SendResource", "AsyncSendResource"] - - -class SendResource(SyncAPIResource): - @cached_property - def with_raw_response(self) -> SendResourceWithRawResponse: - """ - This property can be used as a prefix for any HTTP method call to return - the raw response object instead of the parsed content. - - For more information, see https://www.github.com/stainless-sdks/unlayer-python#accessing-raw-response-data-eg-headers - """ - return SendResourceWithRawResponse(self) - - @cached_property - def with_streaming_response(self) -> SendResourceWithStreamingResponse: - """ - An alternative to `.with_raw_response` that doesn't eagerly read the response body. - - For more information, see https://www.github.com/stainless-sdks/unlayer-python#with_streaming_response - """ - return SendResourceWithStreamingResponse(self) - - def create( - self, - *, - project_id: str, - to: str, - design: Dict[str, object] | Omit = omit, - html: str | Omit = omit, - merge_tags: Dict[str, str] | Omit = omit, - subject: str | Omit = omit, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> SendCreateResponse: - """ - Send email with design JSON or HTML content. - - Args: - project_id: The project ID - - to: Recipient email address - - design: Proprietary design format JSON - - html: HTML content to send - - merge_tags: Optional merge tags for personalization - - subject: Email subject line - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - return self._post( - "/emails/v1/send", - body=maybe_transform( - { - "to": to, - "design": design, - "html": html, - "merge_tags": merge_tags, - "subject": subject, - }, - send_create_params.SendCreateParams, - ), - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query=maybe_transform({"project_id": project_id}, send_create_params.SendCreateParams), - ), - cast_to=SendCreateResponse, - ) - - -class AsyncSendResource(AsyncAPIResource): - @cached_property - def with_raw_response(self) -> AsyncSendResourceWithRawResponse: - """ - This property can be used as a prefix for any HTTP method call to return - the raw response object instead of the parsed content. - - For more information, see https://www.github.com/stainless-sdks/unlayer-python#accessing-raw-response-data-eg-headers - """ - return AsyncSendResourceWithRawResponse(self) - - @cached_property - def with_streaming_response(self) -> AsyncSendResourceWithStreamingResponse: - """ - An alternative to `.with_raw_response` that doesn't eagerly read the response body. - - For more information, see https://www.github.com/stainless-sdks/unlayer-python#with_streaming_response - """ - return AsyncSendResourceWithStreamingResponse(self) - - async def create( - self, - *, - project_id: str, - to: str, - design: Dict[str, object] | Omit = omit, - html: str | Omit = omit, - merge_tags: Dict[str, str] | Omit = omit, - subject: str | Omit = omit, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> SendCreateResponse: - """ - Send email with design JSON or HTML content. - - Args: - project_id: The project ID - - to: Recipient email address - - design: Proprietary design format JSON - - html: HTML content to send - - merge_tags: Optional merge tags for personalization - - subject: Email subject line - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - return await self._post( - "/emails/v1/send", - body=await async_maybe_transform( - { - "to": to, - "design": design, - "html": html, - "merge_tags": merge_tags, - "subject": subject, - }, - send_create_params.SendCreateParams, - ), - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query=await async_maybe_transform({"project_id": project_id}, send_create_params.SendCreateParams), - ), - cast_to=SendCreateResponse, - ) - - -class SendResourceWithRawResponse: - def __init__(self, send: SendResource) -> None: - self._send = send - - self.create = to_raw_response_wrapper( - send.create, - ) - - -class AsyncSendResourceWithRawResponse: - def __init__(self, send: AsyncSendResource) -> None: - self._send = send - - self.create = async_to_raw_response_wrapper( - send.create, - ) - - -class SendResourceWithStreamingResponse: - def __init__(self, send: SendResource) -> None: - self._send = send - - self.create = to_streamed_response_wrapper( - send.create, - ) - - -class AsyncSendResourceWithStreamingResponse: - def __init__(self, send: AsyncSendResource) -> None: - self._send = send - - self.create = async_to_streamed_response_wrapper( - send.create, - ) diff --git a/src/unlayer/resources/emails/send_template.py b/src/unlayer/resources/emails/send_template.py deleted file mode 100644 index 80e99ce..0000000 --- a/src/unlayer/resources/emails/send_template.py +++ /dev/null @@ -1,219 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing import Dict - -import httpx - -from ..._types import Body, Omit, Query, Headers, NotGiven, omit, not_given -from ..._utils import maybe_transform, async_maybe_transform -from ..._compat import cached_property -from ..._resource import SyncAPIResource, AsyncAPIResource -from ..._response import ( - to_raw_response_wrapper, - to_streamed_response_wrapper, - async_to_raw_response_wrapper, - async_to_streamed_response_wrapper, -) -from ..._base_client import make_request_options -from ...types.emails import send_template_create_params -from ...types.emails.send_template_create_response import SendTemplateCreateResponse - -__all__ = ["SendTemplateResource", "AsyncSendTemplateResource"] - - -class SendTemplateResource(SyncAPIResource): - @cached_property - def with_raw_response(self) -> SendTemplateResourceWithRawResponse: - """ - This property can be used as a prefix for any HTTP method call to return - the raw response object instead of the parsed content. - - For more information, see https://www.github.com/stainless-sdks/unlayer-python#accessing-raw-response-data-eg-headers - """ - return SendTemplateResourceWithRawResponse(self) - - @cached_property - def with_streaming_response(self) -> SendTemplateResourceWithStreamingResponse: - """ - An alternative to `.with_raw_response` that doesn't eagerly read the response body. - - For more information, see https://www.github.com/stainless-sdks/unlayer-python#with_streaming_response - """ - return SendTemplateResourceWithStreamingResponse(self) - - def create( - self, - *, - project_id: str, - template_id: str, - to: str, - merge_tags: Dict[str, str] | Omit = omit, - subject: str | Omit = omit, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> SendTemplateCreateResponse: - """ - Send email using an existing template with merge tags. - - Args: - project_id: The project ID - - template_id: ID of the template to use - - to: Recipient email address - - merge_tags: Optional merge tags for personalization - - subject: Email subject line (optional, uses template default if not provided) - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - return self._post( - "/emails/v1/send/template", - body=maybe_transform( - { - "template_id": template_id, - "to": to, - "merge_tags": merge_tags, - "subject": subject, - }, - send_template_create_params.SendTemplateCreateParams, - ), - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query=maybe_transform({"project_id": project_id}, send_template_create_params.SendTemplateCreateParams), - ), - cast_to=SendTemplateCreateResponse, - ) - - -class AsyncSendTemplateResource(AsyncAPIResource): - @cached_property - def with_raw_response(self) -> AsyncSendTemplateResourceWithRawResponse: - """ - This property can be used as a prefix for any HTTP method call to return - the raw response object instead of the parsed content. - - For more information, see https://www.github.com/stainless-sdks/unlayer-python#accessing-raw-response-data-eg-headers - """ - return AsyncSendTemplateResourceWithRawResponse(self) - - @cached_property - def with_streaming_response(self) -> AsyncSendTemplateResourceWithStreamingResponse: - """ - An alternative to `.with_raw_response` that doesn't eagerly read the response body. - - For more information, see https://www.github.com/stainless-sdks/unlayer-python#with_streaming_response - """ - return AsyncSendTemplateResourceWithStreamingResponse(self) - - async def create( - self, - *, - project_id: str, - template_id: str, - to: str, - merge_tags: Dict[str, str] | Omit = omit, - subject: str | Omit = omit, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> SendTemplateCreateResponse: - """ - Send email using an existing template with merge tags. - - Args: - project_id: The project ID - - template_id: ID of the template to use - - to: Recipient email address - - merge_tags: Optional merge tags for personalization - - subject: Email subject line (optional, uses template default if not provided) - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - return await self._post( - "/emails/v1/send/template", - body=await async_maybe_transform( - { - "template_id": template_id, - "to": to, - "merge_tags": merge_tags, - "subject": subject, - }, - send_template_create_params.SendTemplateCreateParams, - ), - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query=await async_maybe_transform( - {"project_id": project_id}, send_template_create_params.SendTemplateCreateParams - ), - ), - cast_to=SendTemplateCreateResponse, - ) - - -class SendTemplateResourceWithRawResponse: - def __init__(self, send_template: SendTemplateResource) -> None: - self._send_template = send_template - - self.create = to_raw_response_wrapper( - send_template.create, - ) - - -class AsyncSendTemplateResourceWithRawResponse: - def __init__(self, send_template: AsyncSendTemplateResource) -> None: - self._send_template = send_template - - self.create = async_to_raw_response_wrapper( - send_template.create, - ) - - -class SendTemplateResourceWithStreamingResponse: - def __init__(self, send_template: SendTemplateResource) -> None: - self._send_template = send_template - - self.create = to_streamed_response_wrapper( - send_template.create, - ) - - -class AsyncSendTemplateResourceWithStreamingResponse: - def __init__(self, send_template: AsyncSendTemplateResource) -> None: - self._send_template = send_template - - self.create = async_to_streamed_response_wrapper( - send_template.create, - ) diff --git a/src/unlayer/resources/export/__init__.py b/src/unlayer/resources/export/__init__.py deleted file mode 100644 index 8c58890..0000000 --- a/src/unlayer/resources/export/__init__.py +++ /dev/null @@ -1,75 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from .pdf import ( - PdfResource, - AsyncPdfResource, - PdfResourceWithRawResponse, - AsyncPdfResourceWithRawResponse, - PdfResourceWithStreamingResponse, - AsyncPdfResourceWithStreamingResponse, -) -from .zip import ( - ZipResource, - AsyncZipResource, - ZipResourceWithRawResponse, - AsyncZipResourceWithRawResponse, - ZipResourceWithStreamingResponse, - AsyncZipResourceWithStreamingResponse, -) -from .html import ( - HTMLResource, - AsyncHTMLResource, - HTMLResourceWithRawResponse, - AsyncHTMLResourceWithRawResponse, - HTMLResourceWithStreamingResponse, - AsyncHTMLResourceWithStreamingResponse, -) -from .image import ( - ImageResource, - AsyncImageResource, - ImageResourceWithRawResponse, - AsyncImageResourceWithRawResponse, - ImageResourceWithStreamingResponse, - AsyncImageResourceWithStreamingResponse, -) -from .export import ( - ExportResource, - AsyncExportResource, - ExportResourceWithRawResponse, - AsyncExportResourceWithRawResponse, - ExportResourceWithStreamingResponse, - AsyncExportResourceWithStreamingResponse, -) - -__all__ = [ - "HTMLResource", - "AsyncHTMLResource", - "HTMLResourceWithRawResponse", - "AsyncHTMLResourceWithRawResponse", - "HTMLResourceWithStreamingResponse", - "AsyncHTMLResourceWithStreamingResponse", - "ImageResource", - "AsyncImageResource", - "ImageResourceWithRawResponse", - "AsyncImageResourceWithRawResponse", - "ImageResourceWithStreamingResponse", - "AsyncImageResourceWithStreamingResponse", - "PdfResource", - "AsyncPdfResource", - "PdfResourceWithRawResponse", - "AsyncPdfResourceWithRawResponse", - "PdfResourceWithStreamingResponse", - "AsyncPdfResourceWithStreamingResponse", - "ZipResource", - "AsyncZipResource", - "ZipResourceWithRawResponse", - "AsyncZipResourceWithRawResponse", - "ZipResourceWithStreamingResponse", - "AsyncZipResourceWithStreamingResponse", - "ExportResource", - "AsyncExportResource", - "ExportResourceWithRawResponse", - "AsyncExportResourceWithRawResponse", - "ExportResourceWithStreamingResponse", - "AsyncExportResourceWithStreamingResponse", -] diff --git a/src/unlayer/resources/export/export.py b/src/unlayer/resources/export/export.py deleted file mode 100644 index 3e38faf..0000000 --- a/src/unlayer/resources/export/export.py +++ /dev/null @@ -1,198 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from .pdf import ( - PdfResource, - AsyncPdfResource, - PdfResourceWithRawResponse, - AsyncPdfResourceWithRawResponse, - PdfResourceWithStreamingResponse, - AsyncPdfResourceWithStreamingResponse, -) -from .zip import ( - ZipResource, - AsyncZipResource, - ZipResourceWithRawResponse, - AsyncZipResourceWithRawResponse, - ZipResourceWithStreamingResponse, - AsyncZipResourceWithStreamingResponse, -) -from .html import ( - HTMLResource, - AsyncHTMLResource, - HTMLResourceWithRawResponse, - AsyncHTMLResourceWithRawResponse, - HTMLResourceWithStreamingResponse, - AsyncHTMLResourceWithStreamingResponse, -) -from .image import ( - ImageResource, - AsyncImageResource, - ImageResourceWithRawResponse, - AsyncImageResourceWithRawResponse, - ImageResourceWithStreamingResponse, - AsyncImageResourceWithStreamingResponse, -) -from ..._compat import cached_property -from ..._resource import SyncAPIResource, AsyncAPIResource - -__all__ = ["ExportResource", "AsyncExportResource"] - - -class ExportResource(SyncAPIResource): - @cached_property - def html(self) -> HTMLResource: - return HTMLResource(self._client) - - @cached_property - def image(self) -> ImageResource: - return ImageResource(self._client) - - @cached_property - def pdf(self) -> PdfResource: - return PdfResource(self._client) - - @cached_property - def zip(self) -> ZipResource: - return ZipResource(self._client) - - @cached_property - def with_raw_response(self) -> ExportResourceWithRawResponse: - """ - This property can be used as a prefix for any HTTP method call to return - the raw response object instead of the parsed content. - - For more information, see https://www.github.com/stainless-sdks/unlayer-python#accessing-raw-response-data-eg-headers - """ - return ExportResourceWithRawResponse(self) - - @cached_property - def with_streaming_response(self) -> ExportResourceWithStreamingResponse: - """ - An alternative to `.with_raw_response` that doesn't eagerly read the response body. - - For more information, see https://www.github.com/stainless-sdks/unlayer-python#with_streaming_response - """ - return ExportResourceWithStreamingResponse(self) - - -class AsyncExportResource(AsyncAPIResource): - @cached_property - def html(self) -> AsyncHTMLResource: - return AsyncHTMLResource(self._client) - - @cached_property - def image(self) -> AsyncImageResource: - return AsyncImageResource(self._client) - - @cached_property - def pdf(self) -> AsyncPdfResource: - return AsyncPdfResource(self._client) - - @cached_property - def zip(self) -> AsyncZipResource: - return AsyncZipResource(self._client) - - @cached_property - def with_raw_response(self) -> AsyncExportResourceWithRawResponse: - """ - This property can be used as a prefix for any HTTP method call to return - the raw response object instead of the parsed content. - - For more information, see https://www.github.com/stainless-sdks/unlayer-python#accessing-raw-response-data-eg-headers - """ - return AsyncExportResourceWithRawResponse(self) - - @cached_property - def with_streaming_response(self) -> AsyncExportResourceWithStreamingResponse: - """ - An alternative to `.with_raw_response` that doesn't eagerly read the response body. - - For more information, see https://www.github.com/stainless-sdks/unlayer-python#with_streaming_response - """ - return AsyncExportResourceWithStreamingResponse(self) - - -class ExportResourceWithRawResponse: - def __init__(self, export: ExportResource) -> None: - self._export = export - - @cached_property - def html(self) -> HTMLResourceWithRawResponse: - return HTMLResourceWithRawResponse(self._export.html) - - @cached_property - def image(self) -> ImageResourceWithRawResponse: - return ImageResourceWithRawResponse(self._export.image) - - @cached_property - def pdf(self) -> PdfResourceWithRawResponse: - return PdfResourceWithRawResponse(self._export.pdf) - - @cached_property - def zip(self) -> ZipResourceWithRawResponse: - return ZipResourceWithRawResponse(self._export.zip) - - -class AsyncExportResourceWithRawResponse: - def __init__(self, export: AsyncExportResource) -> None: - self._export = export - - @cached_property - def html(self) -> AsyncHTMLResourceWithRawResponse: - return AsyncHTMLResourceWithRawResponse(self._export.html) - - @cached_property - def image(self) -> AsyncImageResourceWithRawResponse: - return AsyncImageResourceWithRawResponse(self._export.image) - - @cached_property - def pdf(self) -> AsyncPdfResourceWithRawResponse: - return AsyncPdfResourceWithRawResponse(self._export.pdf) - - @cached_property - def zip(self) -> AsyncZipResourceWithRawResponse: - return AsyncZipResourceWithRawResponse(self._export.zip) - - -class ExportResourceWithStreamingResponse: - def __init__(self, export: ExportResource) -> None: - self._export = export - - @cached_property - def html(self) -> HTMLResourceWithStreamingResponse: - return HTMLResourceWithStreamingResponse(self._export.html) - - @cached_property - def image(self) -> ImageResourceWithStreamingResponse: - return ImageResourceWithStreamingResponse(self._export.image) - - @cached_property - def pdf(self) -> PdfResourceWithStreamingResponse: - return PdfResourceWithStreamingResponse(self._export.pdf) - - @cached_property - def zip(self) -> ZipResourceWithStreamingResponse: - return ZipResourceWithStreamingResponse(self._export.zip) - - -class AsyncExportResourceWithStreamingResponse: - def __init__(self, export: AsyncExportResource) -> None: - self._export = export - - @cached_property - def html(self) -> AsyncHTMLResourceWithStreamingResponse: - return AsyncHTMLResourceWithStreamingResponse(self._export.html) - - @cached_property - def image(self) -> AsyncImageResourceWithStreamingResponse: - return AsyncImageResourceWithStreamingResponse(self._export.image) - - @cached_property - def pdf(self) -> AsyncPdfResourceWithStreamingResponse: - return AsyncPdfResourceWithStreamingResponse(self._export.pdf) - - @cached_property - def zip(self) -> AsyncZipResourceWithStreamingResponse: - return AsyncZipResourceWithStreamingResponse(self._export.zip) diff --git a/src/unlayer/resources/export/html.py b/src/unlayer/resources/export/html.py deleted file mode 100644 index 12c3164..0000000 --- a/src/unlayer/resources/export/html.py +++ /dev/null @@ -1,173 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -import httpx - -from ..._types import Body, Query, Headers, NotGiven, not_given -from ..._utils import maybe_transform, async_maybe_transform -from ..._compat import cached_property -from ..._resource import SyncAPIResource, AsyncAPIResource -from ..._response import ( - to_raw_response_wrapper, - to_streamed_response_wrapper, - async_to_raw_response_wrapper, - async_to_streamed_response_wrapper, -) -from ..._base_client import make_request_options -from ...types.export import html_retrieve_params -from ...types.export.html_retrieve_response import HTMLRetrieveResponse - -__all__ = ["HTMLResource", "AsyncHTMLResource"] - - -class HTMLResource(SyncAPIResource): - @cached_property - def with_raw_response(self) -> HTMLResourceWithRawResponse: - """ - This property can be used as a prefix for any HTTP method call to return - the raw response object instead of the parsed content. - - For more information, see https://www.github.com/stainless-sdks/unlayer-python#accessing-raw-response-data-eg-headers - """ - return HTMLResourceWithRawResponse(self) - - @cached_property - def with_streaming_response(self) -> HTMLResourceWithStreamingResponse: - """ - An alternative to `.with_raw_response` that doesn't eagerly read the response body. - - For more information, see https://www.github.com/stainless-sdks/unlayer-python#with_streaming_response - """ - return HTMLResourceWithStreamingResponse(self) - - def retrieve( - self, - *, - project_id: str, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> HTMLRetrieveResponse: - """ - Export design to HTML. - - Args: - project_id: The project ID - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - return self._get( - "/export/v3/html", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query=maybe_transform({"project_id": project_id}, html_retrieve_params.HTMLRetrieveParams), - ), - cast_to=HTMLRetrieveResponse, - ) - - -class AsyncHTMLResource(AsyncAPIResource): - @cached_property - def with_raw_response(self) -> AsyncHTMLResourceWithRawResponse: - """ - This property can be used as a prefix for any HTTP method call to return - the raw response object instead of the parsed content. - - For more information, see https://www.github.com/stainless-sdks/unlayer-python#accessing-raw-response-data-eg-headers - """ - return AsyncHTMLResourceWithRawResponse(self) - - @cached_property - def with_streaming_response(self) -> AsyncHTMLResourceWithStreamingResponse: - """ - An alternative to `.with_raw_response` that doesn't eagerly read the response body. - - For more information, see https://www.github.com/stainless-sdks/unlayer-python#with_streaming_response - """ - return AsyncHTMLResourceWithStreamingResponse(self) - - async def retrieve( - self, - *, - project_id: str, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> HTMLRetrieveResponse: - """ - Export design to HTML. - - Args: - project_id: The project ID - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - return await self._get( - "/export/v3/html", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query=await async_maybe_transform({"project_id": project_id}, html_retrieve_params.HTMLRetrieveParams), - ), - cast_to=HTMLRetrieveResponse, - ) - - -class HTMLResourceWithRawResponse: - def __init__(self, html: HTMLResource) -> None: - self._html = html - - self.retrieve = to_raw_response_wrapper( - html.retrieve, - ) - - -class AsyncHTMLResourceWithRawResponse: - def __init__(self, html: AsyncHTMLResource) -> None: - self._html = html - - self.retrieve = async_to_raw_response_wrapper( - html.retrieve, - ) - - -class HTMLResourceWithStreamingResponse: - def __init__(self, html: HTMLResource) -> None: - self._html = html - - self.retrieve = to_streamed_response_wrapper( - html.retrieve, - ) - - -class AsyncHTMLResourceWithStreamingResponse: - def __init__(self, html: AsyncHTMLResource) -> None: - self._html = html - - self.retrieve = async_to_streamed_response_wrapper( - html.retrieve, - ) diff --git a/src/unlayer/resources/export/image.py b/src/unlayer/resources/export/image.py deleted file mode 100644 index 98db152..0000000 --- a/src/unlayer/resources/export/image.py +++ /dev/null @@ -1,175 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -import httpx - -from ..._types import Body, Query, Headers, NotGiven, not_given -from ..._utils import maybe_transform, async_maybe_transform -from ..._compat import cached_property -from ..._resource import SyncAPIResource, AsyncAPIResource -from ..._response import ( - to_raw_response_wrapper, - to_streamed_response_wrapper, - async_to_raw_response_wrapper, - async_to_streamed_response_wrapper, -) -from ..._base_client import make_request_options -from ...types.export import image_retrieve_params -from ...types.export.image_retrieve_response import ImageRetrieveResponse - -__all__ = ["ImageResource", "AsyncImageResource"] - - -class ImageResource(SyncAPIResource): - @cached_property - def with_raw_response(self) -> ImageResourceWithRawResponse: - """ - This property can be used as a prefix for any HTTP method call to return - the raw response object instead of the parsed content. - - For more information, see https://www.github.com/stainless-sdks/unlayer-python#accessing-raw-response-data-eg-headers - """ - return ImageResourceWithRawResponse(self) - - @cached_property - def with_streaming_response(self) -> ImageResourceWithStreamingResponse: - """ - An alternative to `.with_raw_response` that doesn't eagerly read the response body. - - For more information, see https://www.github.com/stainless-sdks/unlayer-python#with_streaming_response - """ - return ImageResourceWithStreamingResponse(self) - - def retrieve( - self, - *, - project_id: str, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> ImageRetrieveResponse: - """ - Export design to image. - - Args: - project_id: The project ID - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - return self._get( - "/export/v3/image", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query=maybe_transform({"project_id": project_id}, image_retrieve_params.ImageRetrieveParams), - ), - cast_to=ImageRetrieveResponse, - ) - - -class AsyncImageResource(AsyncAPIResource): - @cached_property - def with_raw_response(self) -> AsyncImageResourceWithRawResponse: - """ - This property can be used as a prefix for any HTTP method call to return - the raw response object instead of the parsed content. - - For more information, see https://www.github.com/stainless-sdks/unlayer-python#accessing-raw-response-data-eg-headers - """ - return AsyncImageResourceWithRawResponse(self) - - @cached_property - def with_streaming_response(self) -> AsyncImageResourceWithStreamingResponse: - """ - An alternative to `.with_raw_response` that doesn't eagerly read the response body. - - For more information, see https://www.github.com/stainless-sdks/unlayer-python#with_streaming_response - """ - return AsyncImageResourceWithStreamingResponse(self) - - async def retrieve( - self, - *, - project_id: str, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> ImageRetrieveResponse: - """ - Export design to image. - - Args: - project_id: The project ID - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - return await self._get( - "/export/v3/image", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query=await async_maybe_transform( - {"project_id": project_id}, image_retrieve_params.ImageRetrieveParams - ), - ), - cast_to=ImageRetrieveResponse, - ) - - -class ImageResourceWithRawResponse: - def __init__(self, image: ImageResource) -> None: - self._image = image - - self.retrieve = to_raw_response_wrapper( - image.retrieve, - ) - - -class AsyncImageResourceWithRawResponse: - def __init__(self, image: AsyncImageResource) -> None: - self._image = image - - self.retrieve = async_to_raw_response_wrapper( - image.retrieve, - ) - - -class ImageResourceWithStreamingResponse: - def __init__(self, image: ImageResource) -> None: - self._image = image - - self.retrieve = to_streamed_response_wrapper( - image.retrieve, - ) - - -class AsyncImageResourceWithStreamingResponse: - def __init__(self, image: AsyncImageResource) -> None: - self._image = image - - self.retrieve = async_to_streamed_response_wrapper( - image.retrieve, - ) diff --git a/src/unlayer/resources/export/pdf.py b/src/unlayer/resources/export/pdf.py deleted file mode 100644 index 17dd60b..0000000 --- a/src/unlayer/resources/export/pdf.py +++ /dev/null @@ -1,173 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -import httpx - -from ..._types import Body, Query, Headers, NotGiven, not_given -from ..._utils import maybe_transform, async_maybe_transform -from ..._compat import cached_property -from ..._resource import SyncAPIResource, AsyncAPIResource -from ..._response import ( - to_raw_response_wrapper, - to_streamed_response_wrapper, - async_to_raw_response_wrapper, - async_to_streamed_response_wrapper, -) -from ..._base_client import make_request_options -from ...types.export import pdf_retrieve_params -from ...types.export.pdf_retrieve_response import PdfRetrieveResponse - -__all__ = ["PdfResource", "AsyncPdfResource"] - - -class PdfResource(SyncAPIResource): - @cached_property - def with_raw_response(self) -> PdfResourceWithRawResponse: - """ - This property can be used as a prefix for any HTTP method call to return - the raw response object instead of the parsed content. - - For more information, see https://www.github.com/stainless-sdks/unlayer-python#accessing-raw-response-data-eg-headers - """ - return PdfResourceWithRawResponse(self) - - @cached_property - def with_streaming_response(self) -> PdfResourceWithStreamingResponse: - """ - An alternative to `.with_raw_response` that doesn't eagerly read the response body. - - For more information, see https://www.github.com/stainless-sdks/unlayer-python#with_streaming_response - """ - return PdfResourceWithStreamingResponse(self) - - def retrieve( - self, - *, - project_id: str, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> PdfRetrieveResponse: - """ - Export design to PDF. - - Args: - project_id: The project ID - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - return self._get( - "/export/v3/pdf", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query=maybe_transform({"project_id": project_id}, pdf_retrieve_params.PdfRetrieveParams), - ), - cast_to=PdfRetrieveResponse, - ) - - -class AsyncPdfResource(AsyncAPIResource): - @cached_property - def with_raw_response(self) -> AsyncPdfResourceWithRawResponse: - """ - This property can be used as a prefix for any HTTP method call to return - the raw response object instead of the parsed content. - - For more information, see https://www.github.com/stainless-sdks/unlayer-python#accessing-raw-response-data-eg-headers - """ - return AsyncPdfResourceWithRawResponse(self) - - @cached_property - def with_streaming_response(self) -> AsyncPdfResourceWithStreamingResponse: - """ - An alternative to `.with_raw_response` that doesn't eagerly read the response body. - - For more information, see https://www.github.com/stainless-sdks/unlayer-python#with_streaming_response - """ - return AsyncPdfResourceWithStreamingResponse(self) - - async def retrieve( - self, - *, - project_id: str, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> PdfRetrieveResponse: - """ - Export design to PDF. - - Args: - project_id: The project ID - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - return await self._get( - "/export/v3/pdf", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query=await async_maybe_transform({"project_id": project_id}, pdf_retrieve_params.PdfRetrieveParams), - ), - cast_to=PdfRetrieveResponse, - ) - - -class PdfResourceWithRawResponse: - def __init__(self, pdf: PdfResource) -> None: - self._pdf = pdf - - self.retrieve = to_raw_response_wrapper( - pdf.retrieve, - ) - - -class AsyncPdfResourceWithRawResponse: - def __init__(self, pdf: AsyncPdfResource) -> None: - self._pdf = pdf - - self.retrieve = async_to_raw_response_wrapper( - pdf.retrieve, - ) - - -class PdfResourceWithStreamingResponse: - def __init__(self, pdf: PdfResource) -> None: - self._pdf = pdf - - self.retrieve = to_streamed_response_wrapper( - pdf.retrieve, - ) - - -class AsyncPdfResourceWithStreamingResponse: - def __init__(self, pdf: AsyncPdfResource) -> None: - self._pdf = pdf - - self.retrieve = async_to_streamed_response_wrapper( - pdf.retrieve, - ) diff --git a/src/unlayer/resources/export/zip.py b/src/unlayer/resources/export/zip.py deleted file mode 100644 index cf42fc0..0000000 --- a/src/unlayer/resources/export/zip.py +++ /dev/null @@ -1,173 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -import httpx - -from ..._types import Body, Query, Headers, NotGiven, not_given -from ..._utils import maybe_transform, async_maybe_transform -from ..._compat import cached_property -from ..._resource import SyncAPIResource, AsyncAPIResource -from ..._response import ( - to_raw_response_wrapper, - to_streamed_response_wrapper, - async_to_raw_response_wrapper, - async_to_streamed_response_wrapper, -) -from ..._base_client import make_request_options -from ...types.export import zip_retrieve_params -from ...types.export.zip_retrieve_response import ZipRetrieveResponse - -__all__ = ["ZipResource", "AsyncZipResource"] - - -class ZipResource(SyncAPIResource): - @cached_property - def with_raw_response(self) -> ZipResourceWithRawResponse: - """ - This property can be used as a prefix for any HTTP method call to return - the raw response object instead of the parsed content. - - For more information, see https://www.github.com/stainless-sdks/unlayer-python#accessing-raw-response-data-eg-headers - """ - return ZipResourceWithRawResponse(self) - - @cached_property - def with_streaming_response(self) -> ZipResourceWithStreamingResponse: - """ - An alternative to `.with_raw_response` that doesn't eagerly read the response body. - - For more information, see https://www.github.com/stainless-sdks/unlayer-python#with_streaming_response - """ - return ZipResourceWithStreamingResponse(self) - - def retrieve( - self, - *, - project_id: str, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> ZipRetrieveResponse: - """ - Export design to ZIP archive. - - Args: - project_id: The project ID - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - return self._get( - "/export/v3/zip", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query=maybe_transform({"project_id": project_id}, zip_retrieve_params.ZipRetrieveParams), - ), - cast_to=ZipRetrieveResponse, - ) - - -class AsyncZipResource(AsyncAPIResource): - @cached_property - def with_raw_response(self) -> AsyncZipResourceWithRawResponse: - """ - This property can be used as a prefix for any HTTP method call to return - the raw response object instead of the parsed content. - - For more information, see https://www.github.com/stainless-sdks/unlayer-python#accessing-raw-response-data-eg-headers - """ - return AsyncZipResourceWithRawResponse(self) - - @cached_property - def with_streaming_response(self) -> AsyncZipResourceWithStreamingResponse: - """ - An alternative to `.with_raw_response` that doesn't eagerly read the response body. - - For more information, see https://www.github.com/stainless-sdks/unlayer-python#with_streaming_response - """ - return AsyncZipResourceWithStreamingResponse(self) - - async def retrieve( - self, - *, - project_id: str, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> ZipRetrieveResponse: - """ - Export design to ZIP archive. - - Args: - project_id: The project ID - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - return await self._get( - "/export/v3/zip", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query=await async_maybe_transform({"project_id": project_id}, zip_retrieve_params.ZipRetrieveParams), - ), - cast_to=ZipRetrieveResponse, - ) - - -class ZipResourceWithRawResponse: - def __init__(self, zip: ZipResource) -> None: - self._zip = zip - - self.retrieve = to_raw_response_wrapper( - zip.retrieve, - ) - - -class AsyncZipResourceWithRawResponse: - def __init__(self, zip: AsyncZipResource) -> None: - self._zip = zip - - self.retrieve = async_to_raw_response_wrapper( - zip.retrieve, - ) - - -class ZipResourceWithStreamingResponse: - def __init__(self, zip: ZipResource) -> None: - self._zip = zip - - self.retrieve = to_streamed_response_wrapper( - zip.retrieve, - ) - - -class AsyncZipResourceWithStreamingResponse: - def __init__(self, zip: AsyncZipResource) -> None: - self._zip = zip - - self.retrieve = async_to_streamed_response_wrapper( - zip.retrieve, - ) diff --git a/src/unlayer/resources/pages/__init__.py b/src/unlayer/resources/pages/__init__.py deleted file mode 100644 index 5cc5e81..0000000 --- a/src/unlayer/resources/pages/__init__.py +++ /dev/null @@ -1,33 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from .pages import ( - PagesResource, - AsyncPagesResource, - PagesResourceWithRawResponse, - AsyncPagesResourceWithRawResponse, - PagesResourceWithStreamingResponse, - AsyncPagesResourceWithStreamingResponse, -) -from .render import ( - RenderResource, - AsyncRenderResource, - RenderResourceWithRawResponse, - AsyncRenderResourceWithRawResponse, - RenderResourceWithStreamingResponse, - AsyncRenderResourceWithStreamingResponse, -) - -__all__ = [ - "RenderResource", - "AsyncRenderResource", - "RenderResourceWithRawResponse", - "AsyncRenderResourceWithRawResponse", - "RenderResourceWithStreamingResponse", - "AsyncRenderResourceWithStreamingResponse", - "PagesResource", - "AsyncPagesResource", - "PagesResourceWithRawResponse", - "AsyncPagesResourceWithRawResponse", - "PagesResourceWithStreamingResponse", - "AsyncPagesResourceWithStreamingResponse", -] diff --git a/src/unlayer/resources/pages/pages.py b/src/unlayer/resources/pages/pages.py deleted file mode 100644 index 4fa0583..0000000 --- a/src/unlayer/resources/pages/pages.py +++ /dev/null @@ -1,102 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from .render import ( - RenderResource, - AsyncRenderResource, - RenderResourceWithRawResponse, - AsyncRenderResourceWithRawResponse, - RenderResourceWithStreamingResponse, - AsyncRenderResourceWithStreamingResponse, -) -from ..._compat import cached_property -from ..._resource import SyncAPIResource, AsyncAPIResource - -__all__ = ["PagesResource", "AsyncPagesResource"] - - -class PagesResource(SyncAPIResource): - @cached_property - def render(self) -> RenderResource: - return RenderResource(self._client) - - @cached_property - def with_raw_response(self) -> PagesResourceWithRawResponse: - """ - This property can be used as a prefix for any HTTP method call to return - the raw response object instead of the parsed content. - - For more information, see https://www.github.com/stainless-sdks/unlayer-python#accessing-raw-response-data-eg-headers - """ - return PagesResourceWithRawResponse(self) - - @cached_property - def with_streaming_response(self) -> PagesResourceWithStreamingResponse: - """ - An alternative to `.with_raw_response` that doesn't eagerly read the response body. - - For more information, see https://www.github.com/stainless-sdks/unlayer-python#with_streaming_response - """ - return PagesResourceWithStreamingResponse(self) - - -class AsyncPagesResource(AsyncAPIResource): - @cached_property - def render(self) -> AsyncRenderResource: - return AsyncRenderResource(self._client) - - @cached_property - def with_raw_response(self) -> AsyncPagesResourceWithRawResponse: - """ - This property can be used as a prefix for any HTTP method call to return - the raw response object instead of the parsed content. - - For more information, see https://www.github.com/stainless-sdks/unlayer-python#accessing-raw-response-data-eg-headers - """ - return AsyncPagesResourceWithRawResponse(self) - - @cached_property - def with_streaming_response(self) -> AsyncPagesResourceWithStreamingResponse: - """ - An alternative to `.with_raw_response` that doesn't eagerly read the response body. - - For more information, see https://www.github.com/stainless-sdks/unlayer-python#with_streaming_response - """ - return AsyncPagesResourceWithStreamingResponse(self) - - -class PagesResourceWithRawResponse: - def __init__(self, pages: PagesResource) -> None: - self._pages = pages - - @cached_property - def render(self) -> RenderResourceWithRawResponse: - return RenderResourceWithRawResponse(self._pages.render) - - -class AsyncPagesResourceWithRawResponse: - def __init__(self, pages: AsyncPagesResource) -> None: - self._pages = pages - - @cached_property - def render(self) -> AsyncRenderResourceWithRawResponse: - return AsyncRenderResourceWithRawResponse(self._pages.render) - - -class PagesResourceWithStreamingResponse: - def __init__(self, pages: PagesResource) -> None: - self._pages = pages - - @cached_property - def render(self) -> RenderResourceWithStreamingResponse: - return RenderResourceWithStreamingResponse(self._pages.render) - - -class AsyncPagesResourceWithStreamingResponse: - def __init__(self, pages: AsyncPagesResource) -> None: - self._pages = pages - - @cached_property - def render(self) -> AsyncRenderResourceWithStreamingResponse: - return AsyncRenderResourceWithStreamingResponse(self._pages.render) diff --git a/src/unlayer/resources/pages/render.py b/src/unlayer/resources/pages/render.py deleted file mode 100644 index 9ad9b9f..0000000 --- a/src/unlayer/resources/pages/render.py +++ /dev/null @@ -1,201 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing import Dict - -import httpx - -from ..._types import Body, Omit, Query, Headers, NotGiven, omit, not_given -from ..._utils import maybe_transform, async_maybe_transform -from ..._compat import cached_property -from ..._resource import SyncAPIResource, AsyncAPIResource -from ..._response import ( - to_raw_response_wrapper, - to_streamed_response_wrapper, - async_to_raw_response_wrapper, - async_to_streamed_response_wrapper, -) -from ...types.pages import render_create_params -from ..._base_client import make_request_options -from ...types.pages.render_create_response import RenderCreateResponse - -__all__ = ["RenderResource", "AsyncRenderResource"] - - -class RenderResource(SyncAPIResource): - @cached_property - def with_raw_response(self) -> RenderResourceWithRawResponse: - """ - This property can be used as a prefix for any HTTP method call to return - the raw response object instead of the parsed content. - - For more information, see https://www.github.com/stainless-sdks/unlayer-python#accessing-raw-response-data-eg-headers - """ - return RenderResourceWithRawResponse(self) - - @cached_property - def with_streaming_response(self) -> RenderResourceWithStreamingResponse: - """ - An alternative to `.with_raw_response` that doesn't eagerly read the response body. - - For more information, see https://www.github.com/stainless-sdks/unlayer-python#with_streaming_response - """ - return RenderResourceWithStreamingResponse(self) - - def create( - self, - *, - project_id: str, - design: Dict[str, object], - merge_tags: Dict[str, str] | Omit = omit, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> RenderCreateResponse: - """ - Convert page design JSON to HTML with optional merge tags. - - Args: - project_id: The project ID - - design: Proprietary design format JSON - - merge_tags: Optional merge tags for personalization - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - return self._post( - "/pages/v1/render", - body=maybe_transform( - { - "design": design, - "merge_tags": merge_tags, - }, - render_create_params.RenderCreateParams, - ), - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query=maybe_transform({"project_id": project_id}, render_create_params.RenderCreateParams), - ), - cast_to=RenderCreateResponse, - ) - - -class AsyncRenderResource(AsyncAPIResource): - @cached_property - def with_raw_response(self) -> AsyncRenderResourceWithRawResponse: - """ - This property can be used as a prefix for any HTTP method call to return - the raw response object instead of the parsed content. - - For more information, see https://www.github.com/stainless-sdks/unlayer-python#accessing-raw-response-data-eg-headers - """ - return AsyncRenderResourceWithRawResponse(self) - - @cached_property - def with_streaming_response(self) -> AsyncRenderResourceWithStreamingResponse: - """ - An alternative to `.with_raw_response` that doesn't eagerly read the response body. - - For more information, see https://www.github.com/stainless-sdks/unlayer-python#with_streaming_response - """ - return AsyncRenderResourceWithStreamingResponse(self) - - async def create( - self, - *, - project_id: str, - design: Dict[str, object], - merge_tags: Dict[str, str] | Omit = omit, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> RenderCreateResponse: - """ - Convert page design JSON to HTML with optional merge tags. - - Args: - project_id: The project ID - - design: Proprietary design format JSON - - merge_tags: Optional merge tags for personalization - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - return await self._post( - "/pages/v1/render", - body=await async_maybe_transform( - { - "design": design, - "merge_tags": merge_tags, - }, - render_create_params.RenderCreateParams, - ), - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query=await async_maybe_transform({"project_id": project_id}, render_create_params.RenderCreateParams), - ), - cast_to=RenderCreateResponse, - ) - - -class RenderResourceWithRawResponse: - def __init__(self, render: RenderResource) -> None: - self._render = render - - self.create = to_raw_response_wrapper( - render.create, - ) - - -class AsyncRenderResourceWithRawResponse: - def __init__(self, render: AsyncRenderResource) -> None: - self._render = render - - self.create = async_to_raw_response_wrapper( - render.create, - ) - - -class RenderResourceWithStreamingResponse: - def __init__(self, render: RenderResource) -> None: - self._render = render - - self.create = to_streamed_response_wrapper( - render.create, - ) - - -class AsyncRenderResourceWithStreamingResponse: - def __init__(self, render: AsyncRenderResource) -> None: - self._render = render - - self.create = async_to_streamed_response_wrapper( - render.create, - ) diff --git a/src/unlayer/resources/project/__init__.py b/src/unlayer/resources/project/__init__.py index 076105e..1df5aef 100644 --- a/src/unlayer/resources/project/__init__.py +++ b/src/unlayer/resources/project/__init__.py @@ -1,21 +1,5 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. -from .current import ( - CurrentResource, - AsyncCurrentResource, - CurrentResourceWithRawResponse, - AsyncCurrentResourceWithRawResponse, - CurrentResourceWithStreamingResponse, - AsyncCurrentResourceWithStreamingResponse, -) -from .domains import ( - DomainsResource, - AsyncDomainsResource, - DomainsResourceWithRawResponse, - AsyncDomainsResourceWithRawResponse, - DomainsResourceWithStreamingResponse, - AsyncDomainsResourceWithStreamingResponse, -) from .project import ( ProjectResource, AsyncProjectResource, @@ -32,40 +16,14 @@ TemplatesResourceWithStreamingResponse, AsyncTemplatesResourceWithStreamingResponse, ) -from .workspaces import ( - WorkspacesResource, - AsyncWorkspacesResource, - WorkspacesResourceWithRawResponse, - AsyncWorkspacesResourceWithRawResponse, - WorkspacesResourceWithStreamingResponse, - AsyncWorkspacesResourceWithStreamingResponse, -) __all__ = [ - "CurrentResource", - "AsyncCurrentResource", - "CurrentResourceWithRawResponse", - "AsyncCurrentResourceWithRawResponse", - "CurrentResourceWithStreamingResponse", - "AsyncCurrentResourceWithStreamingResponse", - "DomainsResource", - "AsyncDomainsResource", - "DomainsResourceWithRawResponse", - "AsyncDomainsResourceWithRawResponse", - "DomainsResourceWithStreamingResponse", - "AsyncDomainsResourceWithStreamingResponse", "TemplatesResource", "AsyncTemplatesResource", "TemplatesResourceWithRawResponse", "AsyncTemplatesResourceWithRawResponse", "TemplatesResourceWithStreamingResponse", "AsyncTemplatesResourceWithStreamingResponse", - "WorkspacesResource", - "AsyncWorkspacesResource", - "WorkspacesResourceWithRawResponse", - "AsyncWorkspacesResourceWithRawResponse", - "WorkspacesResourceWithStreamingResponse", - "AsyncWorkspacesResourceWithStreamingResponse", "ProjectResource", "AsyncProjectResource", "ProjectResourceWithRawResponse", diff --git a/src/unlayer/resources/project/current.py b/src/unlayer/resources/project/current.py deleted file mode 100644 index 29fd2c1..0000000 --- a/src/unlayer/resources/project/current.py +++ /dev/null @@ -1,175 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -import httpx - -from ..._types import Body, Query, Headers, NotGiven, not_given -from ..._utils import maybe_transform, async_maybe_transform -from ..._compat import cached_property -from ..._resource import SyncAPIResource, AsyncAPIResource -from ..._response import ( - to_raw_response_wrapper, - to_streamed_response_wrapper, - async_to_raw_response_wrapper, - async_to_streamed_response_wrapper, -) -from ..._base_client import make_request_options -from ...types.project import current_retrieve_params -from ...types.project.current_retrieve_response import CurrentRetrieveResponse - -__all__ = ["CurrentResource", "AsyncCurrentResource"] - - -class CurrentResource(SyncAPIResource): - @cached_property - def with_raw_response(self) -> CurrentResourceWithRawResponse: - """ - This property can be used as a prefix for any HTTP method call to return - the raw response object instead of the parsed content. - - For more information, see https://www.github.com/stainless-sdks/unlayer-python#accessing-raw-response-data-eg-headers - """ - return CurrentResourceWithRawResponse(self) - - @cached_property - def with_streaming_response(self) -> CurrentResourceWithStreamingResponse: - """ - An alternative to `.with_raw_response` that doesn't eagerly read the response body. - - For more information, see https://www.github.com/stainless-sdks/unlayer-python#with_streaming_response - """ - return CurrentResourceWithStreamingResponse(self) - - def retrieve( - self, - *, - project_id: str, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> CurrentRetrieveResponse: - """ - Get project details for the specified project. - - Args: - project_id: The project ID - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - return self._get( - "/project/v1/current", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query=maybe_transform({"project_id": project_id}, current_retrieve_params.CurrentRetrieveParams), - ), - cast_to=CurrentRetrieveResponse, - ) - - -class AsyncCurrentResource(AsyncAPIResource): - @cached_property - def with_raw_response(self) -> AsyncCurrentResourceWithRawResponse: - """ - This property can be used as a prefix for any HTTP method call to return - the raw response object instead of the parsed content. - - For more information, see https://www.github.com/stainless-sdks/unlayer-python#accessing-raw-response-data-eg-headers - """ - return AsyncCurrentResourceWithRawResponse(self) - - @cached_property - def with_streaming_response(self) -> AsyncCurrentResourceWithStreamingResponse: - """ - An alternative to `.with_raw_response` that doesn't eagerly read the response body. - - For more information, see https://www.github.com/stainless-sdks/unlayer-python#with_streaming_response - """ - return AsyncCurrentResourceWithStreamingResponse(self) - - async def retrieve( - self, - *, - project_id: str, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> CurrentRetrieveResponse: - """ - Get project details for the specified project. - - Args: - project_id: The project ID - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - return await self._get( - "/project/v1/current", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query=await async_maybe_transform( - {"project_id": project_id}, current_retrieve_params.CurrentRetrieveParams - ), - ), - cast_to=CurrentRetrieveResponse, - ) - - -class CurrentResourceWithRawResponse: - def __init__(self, current: CurrentResource) -> None: - self._current = current - - self.retrieve = to_raw_response_wrapper( - current.retrieve, - ) - - -class AsyncCurrentResourceWithRawResponse: - def __init__(self, current: AsyncCurrentResource) -> None: - self._current = current - - self.retrieve = async_to_raw_response_wrapper( - current.retrieve, - ) - - -class CurrentResourceWithStreamingResponse: - def __init__(self, current: CurrentResource) -> None: - self._current = current - - self.retrieve = to_streamed_response_wrapper( - current.retrieve, - ) - - -class AsyncCurrentResourceWithStreamingResponse: - def __init__(self, current: AsyncCurrentResource) -> None: - self._current = current - - self.retrieve = async_to_streamed_response_wrapper( - current.retrieve, - ) diff --git a/src/unlayer/resources/project/domains.py b/src/unlayer/resources/project/domains.py deleted file mode 100644 index 844d474..0000000 --- a/src/unlayer/resources/project/domains.py +++ /dev/null @@ -1,514 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -import httpx - -from ..._types import Body, Omit, Query, Headers, NoneType, NotGiven, omit, not_given -from ..._utils import maybe_transform, async_maybe_transform -from ..._compat import cached_property -from ..._resource import SyncAPIResource, AsyncAPIResource -from ..._response import ( - to_raw_response_wrapper, - to_streamed_response_wrapper, - async_to_raw_response_wrapper, - async_to_streamed_response_wrapper, -) -from ..._base_client import make_request_options -from ...types.project import domain_list_params, domain_create_params, domain_update_params -from ...types.project.domain_list_response import DomainListResponse -from ...types.project.domain_create_response import DomainCreateResponse -from ...types.project.domain_update_response import DomainUpdateResponse -from ...types.project.domain_retrieve_response import DomainRetrieveResponse - -__all__ = ["DomainsResource", "AsyncDomainsResource"] - - -class DomainsResource(SyncAPIResource): - @cached_property - def with_raw_response(self) -> DomainsResourceWithRawResponse: - """ - This property can be used as a prefix for any HTTP method call to return - the raw response object instead of the parsed content. - - For more information, see https://www.github.com/stainless-sdks/unlayer-python#accessing-raw-response-data-eg-headers - """ - return DomainsResourceWithRawResponse(self) - - @cached_property - def with_streaming_response(self) -> DomainsResourceWithStreamingResponse: - """ - An alternative to `.with_raw_response` that doesn't eagerly read the response body. - - For more information, see https://www.github.com/stainless-sdks/unlayer-python#with_streaming_response - """ - return DomainsResourceWithStreamingResponse(self) - - def create( - self, - *, - project_id: str, - domain: str, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> DomainCreateResponse: - """ - Add a new domain to the project. - - Args: - project_id: The project ID - - domain: Domain name to add - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - return self._post( - "/project/v1/domains", - body=maybe_transform({"domain": domain}, domain_create_params.DomainCreateParams), - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query=maybe_transform({"project_id": project_id}, domain_create_params.DomainCreateParams), - ), - cast_to=DomainCreateResponse, - ) - - def retrieve( - self, - id: str, - *, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> DomainRetrieveResponse: - """ - Get domain details by ID. - - Args: - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - if not id: - raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") - return self._get( - f"/project/v1/domains/{id}", - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=DomainRetrieveResponse, - ) - - def update( - self, - id: str, - *, - domain: str | Omit = omit, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> DomainUpdateResponse: - """ - Update domain settings. - - Args: - domain: Updated domain name - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - if not id: - raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") - return self._put( - f"/project/v1/domains/{id}", - body=maybe_transform({"domain": domain}, domain_update_params.DomainUpdateParams), - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=DomainUpdateResponse, - ) - - def list( - self, - *, - project_id: str, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> DomainListResponse: - """ - List all domains for the project. - - Args: - project_id: The project ID - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - return self._get( - "/project/v1/domains", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query=maybe_transform({"project_id": project_id}, domain_list_params.DomainListParams), - ), - cast_to=DomainListResponse, - ) - - def delete( - self, - id: str, - *, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> None: - """ - Remove domain from project. - - Args: - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - if not id: - raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") - extra_headers = {"Accept": "*/*", **(extra_headers or {})} - return self._delete( - f"/project/v1/domains/{id}", - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=NoneType, - ) - - -class AsyncDomainsResource(AsyncAPIResource): - @cached_property - def with_raw_response(self) -> AsyncDomainsResourceWithRawResponse: - """ - This property can be used as a prefix for any HTTP method call to return - the raw response object instead of the parsed content. - - For more information, see https://www.github.com/stainless-sdks/unlayer-python#accessing-raw-response-data-eg-headers - """ - return AsyncDomainsResourceWithRawResponse(self) - - @cached_property - def with_streaming_response(self) -> AsyncDomainsResourceWithStreamingResponse: - """ - An alternative to `.with_raw_response` that doesn't eagerly read the response body. - - For more information, see https://www.github.com/stainless-sdks/unlayer-python#with_streaming_response - """ - return AsyncDomainsResourceWithStreamingResponse(self) - - async def create( - self, - *, - project_id: str, - domain: str, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> DomainCreateResponse: - """ - Add a new domain to the project. - - Args: - project_id: The project ID - - domain: Domain name to add - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - return await self._post( - "/project/v1/domains", - body=await async_maybe_transform({"domain": domain}, domain_create_params.DomainCreateParams), - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query=await async_maybe_transform({"project_id": project_id}, domain_create_params.DomainCreateParams), - ), - cast_to=DomainCreateResponse, - ) - - async def retrieve( - self, - id: str, - *, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> DomainRetrieveResponse: - """ - Get domain details by ID. - - Args: - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - if not id: - raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") - return await self._get( - f"/project/v1/domains/{id}", - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=DomainRetrieveResponse, - ) - - async def update( - self, - id: str, - *, - domain: str | Omit = omit, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> DomainUpdateResponse: - """ - Update domain settings. - - Args: - domain: Updated domain name - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - if not id: - raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") - return await self._put( - f"/project/v1/domains/{id}", - body=await async_maybe_transform({"domain": domain}, domain_update_params.DomainUpdateParams), - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=DomainUpdateResponse, - ) - - async def list( - self, - *, - project_id: str, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> DomainListResponse: - """ - List all domains for the project. - - Args: - project_id: The project ID - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - return await self._get( - "/project/v1/domains", - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query=await async_maybe_transform({"project_id": project_id}, domain_list_params.DomainListParams), - ), - cast_to=DomainListResponse, - ) - - async def delete( - self, - id: str, - *, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> None: - """ - Remove domain from project. - - Args: - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - if not id: - raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") - extra_headers = {"Accept": "*/*", **(extra_headers or {})} - return await self._delete( - f"/project/v1/domains/{id}", - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=NoneType, - ) - - -class DomainsResourceWithRawResponse: - def __init__(self, domains: DomainsResource) -> None: - self._domains = domains - - self.create = to_raw_response_wrapper( - domains.create, - ) - self.retrieve = to_raw_response_wrapper( - domains.retrieve, - ) - self.update = to_raw_response_wrapper( - domains.update, - ) - self.list = to_raw_response_wrapper( - domains.list, - ) - self.delete = to_raw_response_wrapper( - domains.delete, - ) - - -class AsyncDomainsResourceWithRawResponse: - def __init__(self, domains: AsyncDomainsResource) -> None: - self._domains = domains - - self.create = async_to_raw_response_wrapper( - domains.create, - ) - self.retrieve = async_to_raw_response_wrapper( - domains.retrieve, - ) - self.update = async_to_raw_response_wrapper( - domains.update, - ) - self.list = async_to_raw_response_wrapper( - domains.list, - ) - self.delete = async_to_raw_response_wrapper( - domains.delete, - ) - - -class DomainsResourceWithStreamingResponse: - def __init__(self, domains: DomainsResource) -> None: - self._domains = domains - - self.create = to_streamed_response_wrapper( - domains.create, - ) - self.retrieve = to_streamed_response_wrapper( - domains.retrieve, - ) - self.update = to_streamed_response_wrapper( - domains.update, - ) - self.list = to_streamed_response_wrapper( - domains.list, - ) - self.delete = to_streamed_response_wrapper( - domains.delete, - ) - - -class AsyncDomainsResourceWithStreamingResponse: - def __init__(self, domains: AsyncDomainsResource) -> None: - self._domains = domains - - self.create = async_to_streamed_response_wrapper( - domains.create, - ) - self.retrieve = async_to_streamed_response_wrapper( - domains.retrieve, - ) - self.update = async_to_streamed_response_wrapper( - domains.update, - ) - self.list = async_to_streamed_response_wrapper( - domains.list, - ) - self.delete = async_to_streamed_response_wrapper( - domains.delete, - ) diff --git a/src/unlayer/resources/project/project.py b/src/unlayer/resources/project/project.py index 3884df6..8bd4376 100644 --- a/src/unlayer/resources/project/project.py +++ b/src/unlayer/resources/project/project.py @@ -2,22 +2,11 @@ from __future__ import annotations -from .current import ( - CurrentResource, - AsyncCurrentResource, - CurrentResourceWithRawResponse, - AsyncCurrentResourceWithRawResponse, - CurrentResourceWithStreamingResponse, - AsyncCurrentResourceWithStreamingResponse, -) -from .domains import ( - DomainsResource, - AsyncDomainsResource, - DomainsResourceWithRawResponse, - AsyncDomainsResourceWithRawResponse, - DomainsResourceWithStreamingResponse, - AsyncDomainsResourceWithStreamingResponse, -) +import httpx + +from ...types import project_retrieve_params +from ..._types import Body, Query, Headers, NotGiven, not_given +from ..._utils import maybe_transform, async_maybe_transform from ..._compat import cached_property from .templates import ( TemplatesResource, @@ -27,36 +16,24 @@ TemplatesResourceWithStreamingResponse, AsyncTemplatesResourceWithStreamingResponse, ) -from .workspaces import ( - WorkspacesResource, - AsyncWorkspacesResource, - WorkspacesResourceWithRawResponse, - AsyncWorkspacesResourceWithRawResponse, - WorkspacesResourceWithStreamingResponse, - AsyncWorkspacesResourceWithStreamingResponse, -) from ..._resource import SyncAPIResource, AsyncAPIResource +from ..._response import ( + to_raw_response_wrapper, + to_streamed_response_wrapper, + async_to_raw_response_wrapper, + async_to_streamed_response_wrapper, +) +from ..._base_client import make_request_options +from ...types.project_retrieve_response import ProjectRetrieveResponse __all__ = ["ProjectResource", "AsyncProjectResource"] class ProjectResource(SyncAPIResource): - @cached_property - def current(self) -> CurrentResource: - return CurrentResource(self._client) - - @cached_property - def domains(self) -> DomainsResource: - return DomainsResource(self._client) - @cached_property def templates(self) -> TemplatesResource: return TemplatesResource(self._client) - @cached_property - def workspaces(self) -> WorkspacesResource: - return WorkspacesResource(self._client) - @cached_property def with_raw_response(self) -> ProjectResourceWithRawResponse: """ @@ -76,24 +53,49 @@ def with_streaming_response(self) -> ProjectResourceWithStreamingResponse: """ return ProjectResourceWithStreamingResponse(self) + def retrieve( + self, + *, + project_id: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> ProjectRetrieveResponse: + """ + Get project details for the specified project. -class AsyncProjectResource(AsyncAPIResource): - @cached_property - def current(self) -> AsyncCurrentResource: - return AsyncCurrentResource(self._client) + Args: + project_id: The project ID + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return self._get( + "/v3/project", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform({"project_id": project_id}, project_retrieve_params.ProjectRetrieveParams), + ), + cast_to=ProjectRetrieveResponse, + ) - @cached_property - def domains(self) -> AsyncDomainsResource: - return AsyncDomainsResource(self._client) +class AsyncProjectResource(AsyncAPIResource): @cached_property def templates(self) -> AsyncTemplatesResource: return AsyncTemplatesResource(self._client) - @cached_property - def workspaces(self) -> AsyncWorkspacesResource: - return AsyncWorkspacesResource(self._client) - @cached_property def with_raw_response(self) -> AsyncProjectResourceWithRawResponse: """ @@ -113,86 +115,93 @@ def with_streaming_response(self) -> AsyncProjectResourceWithStreamingResponse: """ return AsyncProjectResourceWithStreamingResponse(self) + async def retrieve( + self, + *, + project_id: str, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> ProjectRetrieveResponse: + """ + Get project details for the specified project. + + Args: + project_id: The project ID + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + return await self._get( + "/v3/project", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=await async_maybe_transform( + {"project_id": project_id}, project_retrieve_params.ProjectRetrieveParams + ), + ), + cast_to=ProjectRetrieveResponse, + ) + class ProjectResourceWithRawResponse: def __init__(self, project: ProjectResource) -> None: self._project = project - @cached_property - def current(self) -> CurrentResourceWithRawResponse: - return CurrentResourceWithRawResponse(self._project.current) - - @cached_property - def domains(self) -> DomainsResourceWithRawResponse: - return DomainsResourceWithRawResponse(self._project.domains) + self.retrieve = to_raw_response_wrapper( + project.retrieve, + ) @cached_property def templates(self) -> TemplatesResourceWithRawResponse: return TemplatesResourceWithRawResponse(self._project.templates) - @cached_property - def workspaces(self) -> WorkspacesResourceWithRawResponse: - return WorkspacesResourceWithRawResponse(self._project.workspaces) - class AsyncProjectResourceWithRawResponse: def __init__(self, project: AsyncProjectResource) -> None: self._project = project - @cached_property - def current(self) -> AsyncCurrentResourceWithRawResponse: - return AsyncCurrentResourceWithRawResponse(self._project.current) - - @cached_property - def domains(self) -> AsyncDomainsResourceWithRawResponse: - return AsyncDomainsResourceWithRawResponse(self._project.domains) + self.retrieve = async_to_raw_response_wrapper( + project.retrieve, + ) @cached_property def templates(self) -> AsyncTemplatesResourceWithRawResponse: return AsyncTemplatesResourceWithRawResponse(self._project.templates) - @cached_property - def workspaces(self) -> AsyncWorkspacesResourceWithRawResponse: - return AsyncWorkspacesResourceWithRawResponse(self._project.workspaces) - class ProjectResourceWithStreamingResponse: def __init__(self, project: ProjectResource) -> None: self._project = project - @cached_property - def current(self) -> CurrentResourceWithStreamingResponse: - return CurrentResourceWithStreamingResponse(self._project.current) - - @cached_property - def domains(self) -> DomainsResourceWithStreamingResponse: - return DomainsResourceWithStreamingResponse(self._project.domains) + self.retrieve = to_streamed_response_wrapper( + project.retrieve, + ) @cached_property def templates(self) -> TemplatesResourceWithStreamingResponse: return TemplatesResourceWithStreamingResponse(self._project.templates) - @cached_property - def workspaces(self) -> WorkspacesResourceWithStreamingResponse: - return WorkspacesResourceWithStreamingResponse(self._project.workspaces) - class AsyncProjectResourceWithStreamingResponse: def __init__(self, project: AsyncProjectResource) -> None: self._project = project - @cached_property - def current(self) -> AsyncCurrentResourceWithStreamingResponse: - return AsyncCurrentResourceWithStreamingResponse(self._project.current) - - @cached_property - def domains(self) -> AsyncDomainsResourceWithStreamingResponse: - return AsyncDomainsResourceWithStreamingResponse(self._project.domains) + self.retrieve = async_to_streamed_response_wrapper( + project.retrieve, + ) @cached_property def templates(self) -> AsyncTemplatesResourceWithStreamingResponse: return AsyncTemplatesResourceWithStreamingResponse(self._project.templates) - - @cached_property - def workspaces(self) -> AsyncWorkspacesResourceWithStreamingResponse: - return AsyncWorkspacesResourceWithStreamingResponse(self._project.workspaces) diff --git a/src/unlayer/resources/project/templates.py b/src/unlayer/resources/project/templates.py index b2978ee..e7600ac 100644 --- a/src/unlayer/resources/project/templates.py +++ b/src/unlayer/resources/project/templates.py @@ -6,7 +6,7 @@ import httpx -from ..._types import Body, Omit, Query, Headers, NoneType, NotGiven, omit, not_given +from ..._types import Body, Omit, Query, Headers, NotGiven, omit, not_given from ..._utils import maybe_transform, async_maybe_transform from ..._compat import cached_property from ..._resource import SyncAPIResource, AsyncAPIResource @@ -16,11 +16,10 @@ async_to_raw_response_wrapper, async_to_streamed_response_wrapper, ) -from ..._base_client import make_request_options -from ...types.project import template_list_params, template_create_params, template_update_params +from ...pagination import SyncCursorPage, AsyncCursorPage +from ..._base_client import AsyncPaginator, make_request_options +from ...types.project import template_list_params, template_retrieve_params from ...types.project.template_list_response import TemplateListResponse -from ...types.project.template_create_response import TemplateCreateResponse -from ...types.project.template_update_response import TemplateUpdateResponse from ...types.project.template_retrieve_response import TemplateRetrieveResponse __all__ = ["TemplatesResource", "AsyncTemplatesResource"] @@ -46,60 +45,11 @@ def with_streaming_response(self) -> TemplatesResourceWithStreamingResponse: """ return TemplatesResourceWithStreamingResponse(self) - def create( - self, - *, - project_id: str, - name: str, - display_mode: Literal["email", "web", "document"] | Omit = omit, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> TemplateCreateResponse: - """ - Create a new project template. - - Args: - project_id: The project ID to create the template in - - name: Template name - - display_mode: Template type/display mode - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - return self._post( - "/project/v1/templates", - body=maybe_transform( - { - "name": name, - "display_mode": display_mode, - }, - template_create_params.TemplateCreateParams, - ), - options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query=maybe_transform({"project_id": project_id}, template_create_params.TemplateCreateParams), - ), - cast_to=TemplateCreateResponse, - ) - def retrieve( self, id: str, *, + project_id: str, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, @@ -111,6 +61,8 @@ def retrieve( Get project template by ID. Args: + project_id: The project ID + extra_headers: Send extra headers extra_query: Add additional query parameters to the request @@ -122,63 +74,17 @@ def retrieve( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return self._get( - f"/project/v1/templates/{id}", + f"/v3/project/templates/{id}", options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform({"project_id": project_id}, template_retrieve_params.TemplateRetrieveParams), ), cast_to=TemplateRetrieveResponse, ) - def update( - self, - id: str, - *, - body: str | Omit = omit, - name: str | Omit = omit, - subject: str | Omit = omit, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> TemplateUpdateResponse: - """ - Update project template. - - Args: - body: Updated email body content - - name: Updated template name - - subject: Updated email subject line - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - if not id: - raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") - return self._put( - f"/project/v1/templates/{id}", - body=maybe_transform( - { - "body": body, - "name": name, - "subject": subject, - }, - template_update_params.TemplateUpdateParams, - ), - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=TemplateUpdateResponse, - ) - def list( self, *, @@ -193,7 +99,7 @@ def list( extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> TemplateListResponse: + ) -> SyncCursorPage[TemplateListResponse]: """List project templates with cursor-based pagination. Returns templates in @@ -218,8 +124,9 @@ def list( timeout: Override the client-level default timeout for this request, in seconds """ - return self._get( - "/project/v1/templates", + return self._get_api_list( + "/v3/project/templates", + page=SyncCursorPage[TemplateListResponse], options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, @@ -236,41 +143,7 @@ def list( template_list_params.TemplateListParams, ), ), - cast_to=TemplateListResponse, - ) - - def delete( - self, - id: str, - *, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> None: - """ - Delete project template. - - Args: - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - if not id: - raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") - extra_headers = {"Accept": "*/*", **(extra_headers or {})} - return self._delete( - f"/project/v1/templates/{id}", - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=NoneType, + model=TemplateListResponse, ) @@ -294,28 +167,23 @@ def with_streaming_response(self) -> AsyncTemplatesResourceWithStreamingResponse """ return AsyncTemplatesResourceWithStreamingResponse(self) - async def create( + async def retrieve( self, + id: str, *, project_id: str, - name: str, - display_mode: Literal["email", "web", "document"] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> TemplateCreateResponse: + ) -> TemplateRetrieveResponse: """ - Create a new project template. + Get project template by ID. Args: - project_id: The project ID to create the template in - - name: Template name - - display_mode: Template type/display mode + project_id: The project ID extra_headers: Send extra headers @@ -325,111 +193,23 @@ async def create( timeout: Override the client-level default timeout for this request, in seconds """ - return await self._post( - "/project/v1/templates", - body=await async_maybe_transform( - { - "name": name, - "display_mode": display_mode, - }, - template_create_params.TemplateCreateParams, - ), + if not id: + raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") + return await self._get( + f"/v3/project/templates/{id}", options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout, query=await async_maybe_transform( - {"project_id": project_id}, template_create_params.TemplateCreateParams + {"project_id": project_id}, template_retrieve_params.TemplateRetrieveParams ), ), - cast_to=TemplateCreateResponse, - ) - - async def retrieve( - self, - id: str, - *, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> TemplateRetrieveResponse: - """ - Get project template by ID. - - Args: - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - if not id: - raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") - return await self._get( - f"/project/v1/templates/{id}", - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), cast_to=TemplateRetrieveResponse, ) - async def update( - self, - id: str, - *, - body: str | Omit = omit, - name: str | Omit = omit, - subject: str | Omit = omit, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> TemplateUpdateResponse: - """ - Update project template. - - Args: - body: Updated email body content - - name: Updated template name - - subject: Updated email subject line - - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - if not id: - raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") - return await self._put( - f"/project/v1/templates/{id}", - body=await async_maybe_transform( - { - "body": body, - "name": name, - "subject": subject, - }, - template_update_params.TemplateUpdateParams, - ), - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=TemplateUpdateResponse, - ) - - async def list( + def list( self, *, project_id: str, @@ -443,7 +223,7 @@ async def list( extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> TemplateListResponse: + ) -> AsyncPaginator[TemplateListResponse, AsyncCursorPage[TemplateListResponse]]: """List project templates with cursor-based pagination. Returns templates in @@ -468,14 +248,15 @@ async def list( timeout: Override the client-level default timeout for this request, in seconds """ - return await self._get( - "/project/v1/templates", + return self._get_api_list( + "/v3/project/templates", + page=AsyncCursorPage[TemplateListResponse], options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout, - query=await async_maybe_transform( + query=maybe_transform( { "project_id": project_id, "cursor": cursor, @@ -486,41 +267,7 @@ async def list( template_list_params.TemplateListParams, ), ), - cast_to=TemplateListResponse, - ) - - async def delete( - self, - id: str, - *, - # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. - # The extra values given here take precedence over values defined on the client or passed to this method. - extra_headers: Headers | None = None, - extra_query: Query | None = None, - extra_body: Body | None = None, - timeout: float | httpx.Timeout | None | NotGiven = not_given, - ) -> None: - """ - Delete project template. - - Args: - extra_headers: Send extra headers - - extra_query: Add additional query parameters to the request - - extra_body: Add additional JSON properties to the request - - timeout: Override the client-level default timeout for this request, in seconds - """ - if not id: - raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") - extra_headers = {"Accept": "*/*", **(extra_headers or {})} - return await self._delete( - f"/project/v1/templates/{id}", - options=make_request_options( - extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout - ), - cast_to=NoneType, + model=TemplateListResponse, ) @@ -528,81 +275,45 @@ class TemplatesResourceWithRawResponse: def __init__(self, templates: TemplatesResource) -> None: self._templates = templates - self.create = to_raw_response_wrapper( - templates.create, - ) self.retrieve = to_raw_response_wrapper( templates.retrieve, ) - self.update = to_raw_response_wrapper( - templates.update, - ) self.list = to_raw_response_wrapper( templates.list, ) - self.delete = to_raw_response_wrapper( - templates.delete, - ) class AsyncTemplatesResourceWithRawResponse: def __init__(self, templates: AsyncTemplatesResource) -> None: self._templates = templates - self.create = async_to_raw_response_wrapper( - templates.create, - ) self.retrieve = async_to_raw_response_wrapper( templates.retrieve, ) - self.update = async_to_raw_response_wrapper( - templates.update, - ) self.list = async_to_raw_response_wrapper( templates.list, ) - self.delete = async_to_raw_response_wrapper( - templates.delete, - ) class TemplatesResourceWithStreamingResponse: def __init__(self, templates: TemplatesResource) -> None: self._templates = templates - self.create = to_streamed_response_wrapper( - templates.create, - ) self.retrieve = to_streamed_response_wrapper( templates.retrieve, ) - self.update = to_streamed_response_wrapper( - templates.update, - ) self.list = to_streamed_response_wrapper( templates.list, ) - self.delete = to_streamed_response_wrapper( - templates.delete, - ) class AsyncTemplatesResourceWithStreamingResponse: def __init__(self, templates: AsyncTemplatesResource) -> None: self._templates = templates - self.create = async_to_streamed_response_wrapper( - templates.create, - ) self.retrieve = async_to_streamed_response_wrapper( templates.retrieve, ) - self.update = async_to_streamed_response_wrapper( - templates.update, - ) self.list = async_to_streamed_response_wrapper( templates.list, ) - self.delete = async_to_streamed_response_wrapper( - templates.delete, - ) diff --git a/src/unlayer/resources/project/workspaces.py b/src/unlayer/resources/workspaces.py similarity index 93% rename from src/unlayer/resources/project/workspaces.py rename to src/unlayer/resources/workspaces.py index 988c696..494ccea 100644 --- a/src/unlayer/resources/project/workspaces.py +++ b/src/unlayer/resources/workspaces.py @@ -4,18 +4,18 @@ import httpx -from ..._types import Body, Query, Headers, NotGiven, not_given -from ..._compat import cached_property -from ..._resource import SyncAPIResource, AsyncAPIResource -from ..._response import ( +from .._types import Body, Query, Headers, NotGiven, not_given +from .._compat import cached_property +from .._resource import SyncAPIResource, AsyncAPIResource +from .._response import ( to_raw_response_wrapper, to_streamed_response_wrapper, async_to_raw_response_wrapper, async_to_streamed_response_wrapper, ) -from ..._base_client import make_request_options -from ...types.project.workspace_list_response import WorkspaceListResponse -from ...types.project.workspace_retrieve_response import WorkspaceRetrieveResponse +from .._base_client import make_request_options +from ..types.workspace_list_response import WorkspaceListResponse +from ..types.workspace_retrieve_response import WorkspaceRetrieveResponse __all__ = ["WorkspacesResource", "AsyncWorkspacesResource"] @@ -66,7 +66,7 @@ def retrieve( if not workspace_id: raise ValueError(f"Expected a non-empty value for `workspace_id` but received {workspace_id!r}") return self._get( - f"/project/v1/workspaces/{workspace_id}", + f"/v3/workspaces/{workspace_id}", options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -85,7 +85,7 @@ def list( ) -> WorkspaceListResponse: """Get all workspaces accessible by the current token.""" return self._get( - "/project/v1/workspaces", + "/v3/workspaces", options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -139,7 +139,7 @@ async def retrieve( if not workspace_id: raise ValueError(f"Expected a non-empty value for `workspace_id` but received {workspace_id!r}") return await self._get( - f"/project/v1/workspaces/{workspace_id}", + f"/v3/workspaces/{workspace_id}", options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -158,7 +158,7 @@ async def list( ) -> WorkspaceListResponse: """Get all workspaces accessible by the current token.""" return await self._get( - "/project/v1/workspaces", + "/v3/workspaces", options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), diff --git a/src/unlayer/types/__init__.py b/src/unlayer/types/__init__.py index 7c21943..12ff9bb 100644 --- a/src/unlayer/types/__init__.py +++ b/src/unlayer/types/__init__.py @@ -2,7 +2,7 @@ from __future__ import annotations -from .email_retrieve_params import EmailRetrieveParams as EmailRetrieveParams -from .email_retrieve_response import EmailRetrieveResponse as EmailRetrieveResponse -from .document_retrieve_params import DocumentRetrieveParams as DocumentRetrieveParams -from .document_retrieve_response import DocumentRetrieveResponse as DocumentRetrieveResponse +from .project_retrieve_params import ProjectRetrieveParams as ProjectRetrieveParams +from .workspace_list_response import WorkspaceListResponse as WorkspaceListResponse +from .project_retrieve_response import ProjectRetrieveResponse as ProjectRetrieveResponse +from .workspace_retrieve_response import WorkspaceRetrieveResponse as WorkspaceRetrieveResponse diff --git a/src/unlayer/types/convert/full_to_simple_create_params.py b/src/unlayer/types/convert/full_to_simple_create_params.py index 9ee1844..7038dcf 100644 --- a/src/unlayer/types/convert/full_to_simple_create_params.py +++ b/src/unlayer/types/convert/full_to_simple_create_params.py @@ -15,13 +15,20 @@ class FullToSimpleCreateParams(TypedDict, total=False): display_mode: Annotated[Literal["email", "web", "popup", "document"], PropertyInfo(alias="displayMode")] + include_conversion: Annotated[bool, PropertyInfo(alias="includeConversion")] + """When true, includes \\__conversion metadata in the response. + + This metadata can be passed to simple-to-full to restore original values without + data loss. + """ + include_default_values: Annotated[bool, PropertyInfo(alias="includeDefaultValues")] class DesignTyped(TypedDict, total=False): - body: Required[object] + body: Required[Dict[str, object]] - counters: object + counters: Dict[str, object] schema_version: Annotated[float, PropertyInfo(alias="schemaVersion")] diff --git a/src/unlayer/types/convert/simple_to_full_create_params.py b/src/unlayer/types/convert/simple_to_full_create_params.py index 814afae..247905e 100644 --- a/src/unlayer/types/convert/simple_to_full_create_params.py +++ b/src/unlayer/types/convert/simple_to_full_create_params.py @@ -25,11 +25,11 @@ class Design_Conversion(TypedDict, total=False): class DesignTyped(TypedDict, total=False): - body: Required[object] + body: Required[Dict[str, object]] _conversion: Design_Conversion - counters: object + counters: Dict[str, object] schema_version: Annotated[float, PropertyInfo(alias="schemaVersion")] diff --git a/src/unlayer/types/document_retrieve_params.py b/src/unlayer/types/document_retrieve_params.py deleted file mode 100644 index 4985fb6..0000000 --- a/src/unlayer/types/document_retrieve_params.py +++ /dev/null @@ -1,14 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing_extensions import Required, Annotated, TypedDict - -from .._utils import PropertyInfo - -__all__ = ["DocumentRetrieveParams"] - - -class DocumentRetrieveParams(TypedDict, total=False): - project_id: Required[Annotated[str, PropertyInfo(alias="projectId")]] - """The project ID""" diff --git a/src/unlayer/types/document_retrieve_response.py b/src/unlayer/types/document_retrieve_response.py deleted file mode 100644 index 25ac839..0000000 --- a/src/unlayer/types/document_retrieve_response.py +++ /dev/null @@ -1,41 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing import Optional -from datetime import datetime -from typing_extensions import Literal - -from pydantic import Field as FieldInfo - -from .._models import BaseModel - -__all__ = ["DocumentRetrieveResponse", "Data"] - - -class Data(BaseModel): - id: Optional[str] = None - """Document ID""" - - completed_at: Optional[datetime] = FieldInfo(alias="completedAt", default=None) - """When the document generation was completed""" - - created_at: Optional[datetime] = FieldInfo(alias="createdAt", default=None) - """When the document was created""" - - filename: Optional[str] = None - """Generated filename""" - - file_size: Optional[float] = FieldInfo(alias="fileSize", default=None) - """File size in bytes""" - - page_count: Optional[float] = FieldInfo(alias="pageCount", default=None) - """Number of pages in the PDF""" - - pdf_url: Optional[str] = FieldInfo(alias="pdfUrl", default=None) - """URL to download the PDF""" - - status: Optional[Literal["generating", "completed", "failed"]] = None - """Current document status""" - - -class DocumentRetrieveResponse(BaseModel): - data: Optional[Data] = None diff --git a/src/unlayer/types/documents/__init__.py b/src/unlayer/types/documents/__init__.py deleted file mode 100644 index 1797dcf..0000000 --- a/src/unlayer/types/documents/__init__.py +++ /dev/null @@ -1,8 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from .generate_create_params import GenerateCreateParams as GenerateCreateParams -from .generate_create_response import GenerateCreateResponse as GenerateCreateResponse -from .generate_template_create_params import GenerateTemplateCreateParams as GenerateTemplateCreateParams -from .generate_template_create_response import GenerateTemplateCreateResponse as GenerateTemplateCreateResponse diff --git a/src/unlayer/types/documents/generate_create_params.py b/src/unlayer/types/documents/generate_create_params.py deleted file mode 100644 index fd4c4a9..0000000 --- a/src/unlayer/types/documents/generate_create_params.py +++ /dev/null @@ -1,30 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing import Dict -from typing_extensions import Required, Annotated, TypedDict - -from ..._utils import PropertyInfo - -__all__ = ["GenerateCreateParams"] - - -class GenerateCreateParams(TypedDict, total=False): - project_id: Required[Annotated[str, PropertyInfo(alias="projectId")]] - """The project ID""" - - design: Dict[str, object] - """Proprietary design format JSON""" - - filename: str - """Optional filename for the generated PDF""" - - html: str - """HTML content to convert to PDF""" - - merge_tags: Annotated[Dict[str, str], PropertyInfo(alias="mergeTags")] - """Optional merge tags for personalization""" - - url: str - """URL to convert to PDF""" diff --git a/src/unlayer/types/documents/generate_create_response.py b/src/unlayer/types/documents/generate_create_response.py deleted file mode 100644 index 62f45b3..0000000 --- a/src/unlayer/types/documents/generate_create_response.py +++ /dev/null @@ -1,27 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing import Optional -from typing_extensions import Literal - -from pydantic import Field as FieldInfo - -from ..._models import BaseModel - -__all__ = ["GenerateCreateResponse", "Data"] - - -class Data(BaseModel): - document_id: Optional[str] = FieldInfo(alias="documentId", default=None) - """Unique document identifier""" - - filename: Optional[str] = None - """Generated filename""" - - pdf_url: Optional[str] = FieldInfo(alias="pdfUrl", default=None) - """URL to download the generated PDF""" - - status: Optional[Literal["generating", "completed", "failed"]] = None - - -class GenerateCreateResponse(BaseModel): - data: Optional[Data] = None diff --git a/src/unlayer/types/documents/generate_template_create_params.py b/src/unlayer/types/documents/generate_template_create_params.py deleted file mode 100644 index 5d1b5c2..0000000 --- a/src/unlayer/types/documents/generate_template_create_params.py +++ /dev/null @@ -1,24 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing import Dict -from typing_extensions import Required, Annotated, TypedDict - -from ..._utils import PropertyInfo - -__all__ = ["GenerateTemplateCreateParams"] - - -class GenerateTemplateCreateParams(TypedDict, total=False): - project_id: Required[Annotated[str, PropertyInfo(alias="projectId")]] - """The project ID""" - - template_id: Required[Annotated[str, PropertyInfo(alias="templateId")]] - """ID of the template to use for generation""" - - filename: str - """Optional filename for the generated PDF""" - - merge_tags: Annotated[Dict[str, str], PropertyInfo(alias="mergeTags")] - """Optional merge tags for personalization""" diff --git a/src/unlayer/types/documents/generate_template_create_response.py b/src/unlayer/types/documents/generate_template_create_response.py deleted file mode 100644 index efc369b..0000000 --- a/src/unlayer/types/documents/generate_template_create_response.py +++ /dev/null @@ -1,27 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing import Optional -from typing_extensions import Literal - -from pydantic import Field as FieldInfo - -from ..._models import BaseModel - -__all__ = ["GenerateTemplateCreateResponse", "Data"] - - -class Data(BaseModel): - document_id: Optional[str] = FieldInfo(alias="documentId", default=None) - """Unique document identifier""" - - filename: Optional[str] = None - """Generated filename""" - - pdf_url: Optional[str] = FieldInfo(alias="pdfUrl", default=None) - """URL to download the generated PDF""" - - status: Optional[Literal["generating", "completed", "failed"]] = None - - -class GenerateTemplateCreateResponse(BaseModel): - data: Optional[Data] = None diff --git a/src/unlayer/types/email_retrieve_response.py b/src/unlayer/types/email_retrieve_response.py deleted file mode 100644 index ea25e57..0000000 --- a/src/unlayer/types/email_retrieve_response.py +++ /dev/null @@ -1,35 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing import Optional -from datetime import datetime -from typing_extensions import Literal - -from pydantic import Field as FieldInfo - -from .._models import BaseModel - -__all__ = ["EmailRetrieveResponse", "Data"] - - -class Data(BaseModel): - id: Optional[str] = None - """Email message ID""" - - html: Optional[str] = None - """HTML content of the email (optional)""" - - sent_at: Optional[datetime] = FieldInfo(alias="sentAt", default=None) - """When the email was sent""" - - status: Optional[Literal["sent", "delivered", "opened", "clicked", "bounced", "failed"]] = None - """Current email status""" - - subject: Optional[str] = None - """Email subject line""" - - to: Optional[str] = None - """Recipient email address""" - - -class EmailRetrieveResponse(BaseModel): - data: Optional[Data] = None diff --git a/src/unlayer/types/emails/__init__.py b/src/unlayer/types/emails/__init__.py deleted file mode 100644 index ee72bf7..0000000 --- a/src/unlayer/types/emails/__init__.py +++ /dev/null @@ -1,10 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from .send_create_params import SendCreateParams as SendCreateParams -from .render_create_params import RenderCreateParams as RenderCreateParams -from .send_create_response import SendCreateResponse as SendCreateResponse -from .render_create_response import RenderCreateResponse as RenderCreateResponse -from .send_template_create_params import SendTemplateCreateParams as SendTemplateCreateParams -from .send_template_create_response import SendTemplateCreateResponse as SendTemplateCreateResponse diff --git a/src/unlayer/types/emails/render_create_params.py b/src/unlayer/types/emails/render_create_params.py deleted file mode 100644 index 476e8c2..0000000 --- a/src/unlayer/types/emails/render_create_params.py +++ /dev/null @@ -1,21 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing import Dict -from typing_extensions import Required, Annotated, TypedDict - -from ..._utils import PropertyInfo - -__all__ = ["RenderCreateParams"] - - -class RenderCreateParams(TypedDict, total=False): - project_id: Required[Annotated[str, PropertyInfo(alias="projectId")]] - """The project ID""" - - design: Required[Dict[str, object]] - """Proprietary design format JSON""" - - merge_tags: Annotated[Dict[str, str], PropertyInfo(alias="mergeTags")] - """Optional merge tags for personalization""" diff --git a/src/unlayer/types/emails/render_create_response.py b/src/unlayer/types/emails/render_create_response.py deleted file mode 100644 index 280635d..0000000 --- a/src/unlayer/types/emails/render_create_response.py +++ /dev/null @@ -1,16 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing import Optional - -from ..._models import BaseModel - -__all__ = ["RenderCreateResponse", "Data"] - - -class Data(BaseModel): - html: Optional[str] = None - """Rendered HTML content""" - - -class RenderCreateResponse(BaseModel): - data: Optional[Data] = None diff --git a/src/unlayer/types/emails/send_create_params.py b/src/unlayer/types/emails/send_create_params.py deleted file mode 100644 index 4e2c35a..0000000 --- a/src/unlayer/types/emails/send_create_params.py +++ /dev/null @@ -1,30 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing import Dict -from typing_extensions import Required, Annotated, TypedDict - -from ..._utils import PropertyInfo - -__all__ = ["SendCreateParams"] - - -class SendCreateParams(TypedDict, total=False): - project_id: Required[Annotated[str, PropertyInfo(alias="projectId")]] - """The project ID""" - - to: Required[str] - """Recipient email address""" - - design: Dict[str, object] - """Proprietary design format JSON""" - - html: str - """HTML content to send""" - - merge_tags: Annotated[Dict[str, str], PropertyInfo(alias="mergeTags")] - """Optional merge tags for personalization""" - - subject: str - """Email subject line""" diff --git a/src/unlayer/types/emails/send_create_response.py b/src/unlayer/types/emails/send_create_response.py deleted file mode 100644 index ec8826a..0000000 --- a/src/unlayer/types/emails/send_create_response.py +++ /dev/null @@ -1,21 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing import Optional -from typing_extensions import Literal - -from pydantic import Field as FieldInfo - -from ..._models import BaseModel - -__all__ = ["SendCreateResponse", "Data"] - - -class Data(BaseModel): - message_id: Optional[str] = FieldInfo(alias="messageId", default=None) - """Unique message identifier""" - - status: Optional[Literal["sent", "queued", "failed"]] = None - - -class SendCreateResponse(BaseModel): - data: Optional[Data] = None diff --git a/src/unlayer/types/emails/send_template_create_params.py b/src/unlayer/types/emails/send_template_create_params.py deleted file mode 100644 index a5eaa22..0000000 --- a/src/unlayer/types/emails/send_template_create_params.py +++ /dev/null @@ -1,27 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing import Dict -from typing_extensions import Required, Annotated, TypedDict - -from ..._utils import PropertyInfo - -__all__ = ["SendTemplateCreateParams"] - - -class SendTemplateCreateParams(TypedDict, total=False): - project_id: Required[Annotated[str, PropertyInfo(alias="projectId")]] - """The project ID""" - - template_id: Required[Annotated[str, PropertyInfo(alias="templateId")]] - """ID of the template to use""" - - to: Required[str] - """Recipient email address""" - - merge_tags: Annotated[Dict[str, str], PropertyInfo(alias="mergeTags")] - """Optional merge tags for personalization""" - - subject: str - """Email subject line (optional, uses template default if not provided)""" diff --git a/src/unlayer/types/emails/send_template_create_response.py b/src/unlayer/types/emails/send_template_create_response.py deleted file mode 100644 index 4206737..0000000 --- a/src/unlayer/types/emails/send_template_create_response.py +++ /dev/null @@ -1,21 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing import Optional -from typing_extensions import Literal - -from pydantic import Field as FieldInfo - -from ..._models import BaseModel - -__all__ = ["SendTemplateCreateResponse", "Data"] - - -class Data(BaseModel): - message_id: Optional[str] = FieldInfo(alias="messageId", default=None) - """Unique message identifier""" - - status: Optional[Literal["sent", "queued", "failed"]] = None - - -class SendTemplateCreateResponse(BaseModel): - data: Optional[Data] = None diff --git a/src/unlayer/types/export/__init__.py b/src/unlayer/types/export/__init__.py deleted file mode 100644 index a6ed49c..0000000 --- a/src/unlayer/types/export/__init__.py +++ /dev/null @@ -1,12 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from .pdf_retrieve_params import PdfRetrieveParams as PdfRetrieveParams -from .zip_retrieve_params import ZipRetrieveParams as ZipRetrieveParams -from .html_retrieve_params import HTMLRetrieveParams as HTMLRetrieveParams -from .image_retrieve_params import ImageRetrieveParams as ImageRetrieveParams -from .pdf_retrieve_response import PdfRetrieveResponse as PdfRetrieveResponse -from .zip_retrieve_response import ZipRetrieveResponse as ZipRetrieveResponse -from .html_retrieve_response import HTMLRetrieveResponse as HTMLRetrieveResponse -from .image_retrieve_response import ImageRetrieveResponse as ImageRetrieveResponse diff --git a/src/unlayer/types/export/html_retrieve_params.py b/src/unlayer/types/export/html_retrieve_params.py deleted file mode 100644 index df7d2ec..0000000 --- a/src/unlayer/types/export/html_retrieve_params.py +++ /dev/null @@ -1,14 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing_extensions import Required, Annotated, TypedDict - -from ..._utils import PropertyInfo - -__all__ = ["HTMLRetrieveParams"] - - -class HTMLRetrieveParams(TypedDict, total=False): - project_id: Required[Annotated[str, PropertyInfo(alias="projectId")]] - """The project ID""" diff --git a/src/unlayer/types/export/html_retrieve_response.py b/src/unlayer/types/export/html_retrieve_response.py deleted file mode 100644 index d20cbf3..0000000 --- a/src/unlayer/types/export/html_retrieve_response.py +++ /dev/null @@ -1,15 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing import Optional - -from ..._models import BaseModel - -__all__ = ["HTMLRetrieveResponse", "Data"] - - -class Data(BaseModel): - success: Optional[bool] = None - - -class HTMLRetrieveResponse(BaseModel): - data: Optional[Data] = None diff --git a/src/unlayer/types/export/image_retrieve_params.py b/src/unlayer/types/export/image_retrieve_params.py deleted file mode 100644 index 737083e..0000000 --- a/src/unlayer/types/export/image_retrieve_params.py +++ /dev/null @@ -1,14 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing_extensions import Required, Annotated, TypedDict - -from ..._utils import PropertyInfo - -__all__ = ["ImageRetrieveParams"] - - -class ImageRetrieveParams(TypedDict, total=False): - project_id: Required[Annotated[str, PropertyInfo(alias="projectId")]] - """The project ID""" diff --git a/src/unlayer/types/export/image_retrieve_response.py b/src/unlayer/types/export/image_retrieve_response.py deleted file mode 100644 index 66b5cbb..0000000 --- a/src/unlayer/types/export/image_retrieve_response.py +++ /dev/null @@ -1,15 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing import Optional - -from ..._models import BaseModel - -__all__ = ["ImageRetrieveResponse", "Data"] - - -class Data(BaseModel): - success: Optional[bool] = None - - -class ImageRetrieveResponse(BaseModel): - data: Optional[Data] = None diff --git a/src/unlayer/types/export/pdf_retrieve_response.py b/src/unlayer/types/export/pdf_retrieve_response.py deleted file mode 100644 index 3d538fe..0000000 --- a/src/unlayer/types/export/pdf_retrieve_response.py +++ /dev/null @@ -1,15 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing import Optional - -from ..._models import BaseModel - -__all__ = ["PdfRetrieveResponse", "Data"] - - -class Data(BaseModel): - success: Optional[bool] = None - - -class PdfRetrieveResponse(BaseModel): - data: Optional[Data] = None diff --git a/src/unlayer/types/export/zip_retrieve_params.py b/src/unlayer/types/export/zip_retrieve_params.py deleted file mode 100644 index d5f0c83..0000000 --- a/src/unlayer/types/export/zip_retrieve_params.py +++ /dev/null @@ -1,14 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing_extensions import Required, Annotated, TypedDict - -from ..._utils import PropertyInfo - -__all__ = ["ZipRetrieveParams"] - - -class ZipRetrieveParams(TypedDict, total=False): - project_id: Required[Annotated[str, PropertyInfo(alias="projectId")]] - """The project ID""" diff --git a/src/unlayer/types/export/zip_retrieve_response.py b/src/unlayer/types/export/zip_retrieve_response.py deleted file mode 100644 index 61409b7..0000000 --- a/src/unlayer/types/export/zip_retrieve_response.py +++ /dev/null @@ -1,15 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing import Optional - -from ..._models import BaseModel - -__all__ = ["ZipRetrieveResponse", "Data"] - - -class Data(BaseModel): - success: Optional[bool] = None - - -class ZipRetrieveResponse(BaseModel): - data: Optional[Data] = None diff --git a/src/unlayer/types/pages/__init__.py b/src/unlayer/types/pages/__init__.py deleted file mode 100644 index 51096d8..0000000 --- a/src/unlayer/types/pages/__init__.py +++ /dev/null @@ -1,6 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from .render_create_params import RenderCreateParams as RenderCreateParams -from .render_create_response import RenderCreateResponse as RenderCreateResponse diff --git a/src/unlayer/types/pages/render_create_params.py b/src/unlayer/types/pages/render_create_params.py deleted file mode 100644 index 476e8c2..0000000 --- a/src/unlayer/types/pages/render_create_params.py +++ /dev/null @@ -1,21 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing import Dict -from typing_extensions import Required, Annotated, TypedDict - -from ..._utils import PropertyInfo - -__all__ = ["RenderCreateParams"] - - -class RenderCreateParams(TypedDict, total=False): - project_id: Required[Annotated[str, PropertyInfo(alias="projectId")]] - """The project ID""" - - design: Required[Dict[str, object]] - """Proprietary design format JSON""" - - merge_tags: Annotated[Dict[str, str], PropertyInfo(alias="mergeTags")] - """Optional merge tags for personalization""" diff --git a/src/unlayer/types/pages/render_create_response.py b/src/unlayer/types/pages/render_create_response.py deleted file mode 100644 index 280635d..0000000 --- a/src/unlayer/types/pages/render_create_response.py +++ /dev/null @@ -1,16 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing import Optional - -from ..._models import BaseModel - -__all__ = ["RenderCreateResponse", "Data"] - - -class Data(BaseModel): - html: Optional[str] = None - """Rendered HTML content""" - - -class RenderCreateResponse(BaseModel): - data: Optional[Data] = None diff --git a/src/unlayer/types/project/__init__.py b/src/unlayer/types/project/__init__.py index a7ec1d6..85258c0 100644 --- a/src/unlayer/types/project/__init__.py +++ b/src/unlayer/types/project/__init__.py @@ -2,21 +2,7 @@ from __future__ import annotations -from .domain_list_params import DomainListParams as DomainListParams -from .domain_create_params import DomainCreateParams as DomainCreateParams -from .domain_list_response import DomainListResponse as DomainListResponse -from .domain_update_params import DomainUpdateParams as DomainUpdateParams from .template_list_params import TemplateListParams as TemplateListParams -from .domain_create_response import DomainCreateResponse as DomainCreateResponse -from .domain_update_response import DomainUpdateResponse as DomainUpdateResponse -from .template_create_params import TemplateCreateParams as TemplateCreateParams from .template_list_response import TemplateListResponse as TemplateListResponse -from .template_update_params import TemplateUpdateParams as TemplateUpdateParams -from .current_retrieve_params import CurrentRetrieveParams as CurrentRetrieveParams -from .workspace_list_response import WorkspaceListResponse as WorkspaceListResponse -from .domain_retrieve_response import DomainRetrieveResponse as DomainRetrieveResponse -from .template_create_response import TemplateCreateResponse as TemplateCreateResponse -from .template_update_response import TemplateUpdateResponse as TemplateUpdateResponse -from .current_retrieve_response import CurrentRetrieveResponse as CurrentRetrieveResponse +from .template_retrieve_params import TemplateRetrieveParams as TemplateRetrieveParams from .template_retrieve_response import TemplateRetrieveResponse as TemplateRetrieveResponse -from .workspace_retrieve_response import WorkspaceRetrieveResponse as WorkspaceRetrieveResponse diff --git a/src/unlayer/types/project/current_retrieve_params.py b/src/unlayer/types/project/current_retrieve_params.py deleted file mode 100644 index 1b39477..0000000 --- a/src/unlayer/types/project/current_retrieve_params.py +++ /dev/null @@ -1,14 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing_extensions import Required, Annotated, TypedDict - -from ..._utils import PropertyInfo - -__all__ = ["CurrentRetrieveParams"] - - -class CurrentRetrieveParams(TypedDict, total=False): - project_id: Required[Annotated[str, PropertyInfo(alias="projectId")]] - """The project ID""" diff --git a/src/unlayer/types/project/domain_create_params.py b/src/unlayer/types/project/domain_create_params.py deleted file mode 100644 index b556ee2..0000000 --- a/src/unlayer/types/project/domain_create_params.py +++ /dev/null @@ -1,17 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing_extensions import Required, Annotated, TypedDict - -from ..._utils import PropertyInfo - -__all__ = ["DomainCreateParams"] - - -class DomainCreateParams(TypedDict, total=False): - project_id: Required[Annotated[str, PropertyInfo(alias="projectId")]] - """The project ID""" - - domain: Required[str] - """Domain name to add""" diff --git a/src/unlayer/types/project/domain_create_response.py b/src/unlayer/types/project/domain_create_response.py deleted file mode 100644 index 9b19209..0000000 --- a/src/unlayer/types/project/domain_create_response.py +++ /dev/null @@ -1,26 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing import Optional -from datetime import datetime - -from pydantic import Field as FieldInfo - -from ..._models import BaseModel - -__all__ = ["DomainCreateResponse", "Data"] - - -class Data(BaseModel): - id: Optional[str] = None - - created_at: Optional[datetime] = FieldInfo(alias="createdAt", default=None) - - domain: Optional[str] = None - - status: Optional[str] = None - - verified: Optional[bool] = None - - -class DomainCreateResponse(BaseModel): - data: Optional[Data] = None diff --git a/src/unlayer/types/project/domain_list_params.py b/src/unlayer/types/project/domain_list_params.py deleted file mode 100644 index 176b6cf..0000000 --- a/src/unlayer/types/project/domain_list_params.py +++ /dev/null @@ -1,14 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing_extensions import Required, Annotated, TypedDict - -from ..._utils import PropertyInfo - -__all__ = ["DomainListParams"] - - -class DomainListParams(TypedDict, total=False): - project_id: Required[Annotated[str, PropertyInfo(alias="projectId")]] - """The project ID""" diff --git a/src/unlayer/types/project/domain_list_response.py b/src/unlayer/types/project/domain_list_response.py deleted file mode 100644 index 4635854..0000000 --- a/src/unlayer/types/project/domain_list_response.py +++ /dev/null @@ -1,27 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing import List, Optional -from datetime import datetime -from typing_extensions import Literal - -from pydantic import Field as FieldInfo - -from ..._models import BaseModel - -__all__ = ["DomainListResponse", "Data"] - - -class Data(BaseModel): - id: Optional[str] = None - - created_at: Optional[datetime] = FieldInfo(alias="createdAt", default=None) - - domain: Optional[str] = None - - status: Optional[Literal["active", "pending", "failed"]] = None - - verified: Optional[bool] = None - - -class DomainListResponse(BaseModel): - data: Optional[List[Data]] = None diff --git a/src/unlayer/types/project/domain_retrieve_response.py b/src/unlayer/types/project/domain_retrieve_response.py deleted file mode 100644 index 024743e..0000000 --- a/src/unlayer/types/project/domain_retrieve_response.py +++ /dev/null @@ -1,26 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing import Optional -from datetime import datetime - -from pydantic import Field as FieldInfo - -from ..._models import BaseModel - -__all__ = ["DomainRetrieveResponse", "Data"] - - -class Data(BaseModel): - id: Optional[str] = None - - created_at: Optional[datetime] = FieldInfo(alias="createdAt", default=None) - - domain: Optional[str] = None - - status: Optional[str] = None - - verified: Optional[bool] = None - - -class DomainRetrieveResponse(BaseModel): - data: Optional[Data] = None diff --git a/src/unlayer/types/project/domain_update_params.py b/src/unlayer/types/project/domain_update_params.py deleted file mode 100644 index f4b6146..0000000 --- a/src/unlayer/types/project/domain_update_params.py +++ /dev/null @@ -1,12 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing_extensions import TypedDict - -__all__ = ["DomainUpdateParams"] - - -class DomainUpdateParams(TypedDict, total=False): - domain: str - """Updated domain name""" diff --git a/src/unlayer/types/project/domain_update_response.py b/src/unlayer/types/project/domain_update_response.py deleted file mode 100644 index 3c13593..0000000 --- a/src/unlayer/types/project/domain_update_response.py +++ /dev/null @@ -1,26 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing import Optional -from datetime import datetime - -from pydantic import Field as FieldInfo - -from ..._models import BaseModel - -__all__ = ["DomainUpdateResponse", "Data"] - - -class Data(BaseModel): - id: Optional[str] = None - - created_at: Optional[datetime] = FieldInfo(alias="createdAt", default=None) - - domain: Optional[str] = None - - status: Optional[str] = None - - verified: Optional[bool] = None - - -class DomainUpdateResponse(BaseModel): - data: Optional[Data] = None diff --git a/src/unlayer/types/project/template_create_params.py b/src/unlayer/types/project/template_create_params.py deleted file mode 100644 index 57a358c..0000000 --- a/src/unlayer/types/project/template_create_params.py +++ /dev/null @@ -1,20 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing_extensions import Literal, Required, Annotated, TypedDict - -from ..._utils import PropertyInfo - -__all__ = ["TemplateCreateParams"] - - -class TemplateCreateParams(TypedDict, total=False): - project_id: Required[Annotated[str, PropertyInfo(alias="projectId")]] - """The project ID to create the template in""" - - name: Required[str] - """Template name""" - - display_mode: Annotated[Literal["email", "web", "document"], PropertyInfo(alias="displayMode")] - """Template type/display mode""" diff --git a/src/unlayer/types/project/template_create_response.py b/src/unlayer/types/project/template_create_response.py deleted file mode 100644 index 98ad4cd..0000000 --- a/src/unlayer/types/project/template_create_response.py +++ /dev/null @@ -1,30 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing import Optional -from datetime import datetime -from typing_extensions import Literal - -from pydantic import Field as FieldInfo - -from ..._models import BaseModel - -__all__ = ["TemplateCreateResponse", "Data"] - - -class Data(BaseModel): - id: Optional[str] = None - """Template ID""" - - created_at: Optional[datetime] = FieldInfo(alias="createdAt", default=None) - - display_mode: Optional[Literal["email", "web", "document"]] = FieldInfo(alias="displayMode", default=None) - """Template type/display mode""" - - name: Optional[str] = None - """Template name""" - - updated_at: Optional[datetime] = FieldInfo(alias="updatedAt", default=None) - - -class TemplateCreateResponse(BaseModel): - data: Optional[Data] = None diff --git a/src/unlayer/types/project/template_list_response.py b/src/unlayer/types/project/template_list_response.py index 9d7f483..f306628 100644 --- a/src/unlayer/types/project/template_list_response.py +++ b/src/unlayer/types/project/template_list_response.py @@ -1,6 +1,6 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. -from typing import List, Optional +from typing import Optional from datetime import datetime from typing_extensions import Literal @@ -8,10 +8,10 @@ from ..._models import BaseModel -__all__ = ["TemplateListResponse", "Data"] +__all__ = ["TemplateListResponse"] -class Data(BaseModel): +class TemplateListResponse(BaseModel): id: Optional[str] = None """Template ID""" @@ -24,13 +24,3 @@ class Data(BaseModel): """Template name""" updated_at: Optional[datetime] = FieldInfo(alias="updatedAt", default=None) - - -class TemplateListResponse(BaseModel): - data: List[Data] - - has_more: bool - """Whether there are more results after this page""" - - next_cursor: Optional[str] = None - """Cursor for the next page. Null if no more results.""" diff --git a/src/unlayer/types/export/pdf_retrieve_params.py b/src/unlayer/types/project/template_retrieve_params.py similarity index 77% rename from src/unlayer/types/export/pdf_retrieve_params.py rename to src/unlayer/types/project/template_retrieve_params.py index 4f048f7..ddbe1ee 100644 --- a/src/unlayer/types/export/pdf_retrieve_params.py +++ b/src/unlayer/types/project/template_retrieve_params.py @@ -6,9 +6,9 @@ from ..._utils import PropertyInfo -__all__ = ["PdfRetrieveParams"] +__all__ = ["TemplateRetrieveParams"] -class PdfRetrieveParams(TypedDict, total=False): +class TemplateRetrieveParams(TypedDict, total=False): project_id: Required[Annotated[str, PropertyInfo(alias="projectId")]] """The project ID""" diff --git a/src/unlayer/types/project/template_retrieve_response.py b/src/unlayer/types/project/template_retrieve_response.py index 96725c0..5941d30 100644 --- a/src/unlayer/types/project/template_retrieve_response.py +++ b/src/unlayer/types/project/template_retrieve_response.py @@ -1,7 +1,8 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. -from typing import Optional +from typing import Dict, Optional from datetime import datetime +from typing_extensions import Literal from pydantic import Field as FieldInfo @@ -13,13 +14,15 @@ class Data(BaseModel): id: Optional[str] = None - body: Optional[str] = None - created_at: Optional[datetime] = FieldInfo(alias="createdAt", default=None) - name: Optional[str] = None + design: Optional[Dict[str, object]] = None + + display_mode: Optional[Literal["email", "web", "document"]] = FieldInfo(alias="displayMode", default=None) - subject: Optional[str] = None + html: Optional[str] = None + + name: Optional[str] = None updated_at: Optional[datetime] = FieldInfo(alias="updatedAt", default=None) diff --git a/src/unlayer/types/project/template_update_params.py b/src/unlayer/types/project/template_update_params.py deleted file mode 100644 index c1d457b..0000000 --- a/src/unlayer/types/project/template_update_params.py +++ /dev/null @@ -1,18 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing_extensions import TypedDict - -__all__ = ["TemplateUpdateParams"] - - -class TemplateUpdateParams(TypedDict, total=False): - body: str - """Updated email body content""" - - name: str - """Updated template name""" - - subject: str - """Updated email subject line""" diff --git a/src/unlayer/types/project/template_update_response.py b/src/unlayer/types/project/template_update_response.py deleted file mode 100644 index 3838d13..0000000 --- a/src/unlayer/types/project/template_update_response.py +++ /dev/null @@ -1,28 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from typing import Optional -from datetime import datetime - -from pydantic import Field as FieldInfo - -from ..._models import BaseModel - -__all__ = ["TemplateUpdateResponse", "Data"] - - -class Data(BaseModel): - id: Optional[str] = None - - body: Optional[str] = None - - created_at: Optional[datetime] = FieldInfo(alias="createdAt", default=None) - - name: Optional[str] = None - - subject: Optional[str] = None - - updated_at: Optional[datetime] = FieldInfo(alias="updatedAt", default=None) - - -class TemplateUpdateResponse(BaseModel): - data: Optional[Data] = None diff --git a/src/unlayer/types/email_retrieve_params.py b/src/unlayer/types/project_retrieve_params.py similarity index 78% rename from src/unlayer/types/email_retrieve_params.py rename to src/unlayer/types/project_retrieve_params.py index be172a5..d2299de 100644 --- a/src/unlayer/types/email_retrieve_params.py +++ b/src/unlayer/types/project_retrieve_params.py @@ -6,9 +6,9 @@ from .._utils import PropertyInfo -__all__ = ["EmailRetrieveParams"] +__all__ = ["ProjectRetrieveParams"] -class EmailRetrieveParams(TypedDict, total=False): +class ProjectRetrieveParams(TypedDict, total=False): project_id: Required[Annotated[str, PropertyInfo(alias="projectId")]] """The project ID""" diff --git a/src/unlayer/types/project/current_retrieve_response.py b/src/unlayer/types/project_retrieve_response.py similarity index 68% rename from src/unlayer/types/project/current_retrieve_response.py rename to src/unlayer/types/project_retrieve_response.py index ce9ef0e..3fc4e75 100644 --- a/src/unlayer/types/project/current_retrieve_response.py +++ b/src/unlayer/types/project_retrieve_response.py @@ -5,9 +5,9 @@ from pydantic import Field as FieldInfo -from ..._models import BaseModel +from .._models import BaseModel -__all__ = ["CurrentRetrieveResponse", "Data", "DataWorkspace"] +__all__ = ["ProjectRetrieveResponse", "Data", "DataWorkspace"] class DataWorkspace(BaseModel): @@ -18,15 +18,19 @@ class DataWorkspace(BaseModel): class Data(BaseModel): id: Optional[float] = None + """The project ID.""" created_at: Optional[datetime] = FieldInfo(alias="createdAt", default=None) + """When the project was created.""" name: Optional[str] = None + """The project name.""" status: Optional[str] = None + """The project status.""" workspace: Optional[DataWorkspace] = None -class CurrentRetrieveResponse(BaseModel): +class ProjectRetrieveResponse(BaseModel): data: Optional[Data] = None diff --git a/src/unlayer/types/project/workspace_list_response.py b/src/unlayer/types/workspace_list_response.py similarity index 91% rename from src/unlayer/types/project/workspace_list_response.py rename to src/unlayer/types/workspace_list_response.py index 4cbc5a6..ac30554 100644 --- a/src/unlayer/types/project/workspace_list_response.py +++ b/src/unlayer/types/workspace_list_response.py @@ -2,7 +2,7 @@ from typing import List, Optional -from ..._models import BaseModel +from .._models import BaseModel __all__ = ["WorkspaceListResponse", "Data"] diff --git a/src/unlayer/types/project/workspace_retrieve_response.py b/src/unlayer/types/workspace_retrieve_response.py similarity index 94% rename from src/unlayer/types/project/workspace_retrieve_response.py rename to src/unlayer/types/workspace_retrieve_response.py index a3d24bc..af949b2 100644 --- a/src/unlayer/types/project/workspace_retrieve_response.py +++ b/src/unlayer/types/workspace_retrieve_response.py @@ -2,7 +2,7 @@ from typing import List, Optional -from ..._models import BaseModel +from .._models import BaseModel __all__ = ["WorkspaceRetrieveResponse", "Data", "DataProject"] diff --git a/tests/api_resources/convert/test_full_to_simple.py b/tests/api_resources/convert/test_full_to_simple.py index 685f0d4..aa7577f 100644 --- a/tests/api_resources/convert/test_full_to_simple.py +++ b/tests/api_resources/convert/test_full_to_simple.py @@ -20,7 +20,7 @@ class TestFullToSimple: @parametrize def test_method_create(self, client: Unlayer) -> None: full_to_simple = client.convert.full_to_simple.create( - design={"body": {}}, + design={"body": {"foo": "bar"}}, ) assert_matches_type(FullToSimpleCreateResponse, full_to_simple, path=["response"]) @@ -28,11 +28,12 @@ def test_method_create(self, client: Unlayer) -> None: def test_method_create_with_all_params(self, client: Unlayer) -> None: full_to_simple = client.convert.full_to_simple.create( design={ - "body": {}, - "counters": {}, + "body": {"foo": "bar"}, + "counters": {"foo": "bar"}, "schema_version": 0, }, display_mode="email", + include_conversion=True, include_default_values=True, ) assert_matches_type(FullToSimpleCreateResponse, full_to_simple, path=["response"]) @@ -40,7 +41,7 @@ def test_method_create_with_all_params(self, client: Unlayer) -> None: @parametrize def test_raw_response_create(self, client: Unlayer) -> None: response = client.convert.full_to_simple.with_raw_response.create( - design={"body": {}}, + design={"body": {"foo": "bar"}}, ) assert response.is_closed is True @@ -51,7 +52,7 @@ def test_raw_response_create(self, client: Unlayer) -> None: @parametrize def test_streaming_response_create(self, client: Unlayer) -> None: with client.convert.full_to_simple.with_streaming_response.create( - design={"body": {}}, + design={"body": {"foo": "bar"}}, ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" @@ -70,7 +71,7 @@ class TestAsyncFullToSimple: @parametrize async def test_method_create(self, async_client: AsyncUnlayer) -> None: full_to_simple = await async_client.convert.full_to_simple.create( - design={"body": {}}, + design={"body": {"foo": "bar"}}, ) assert_matches_type(FullToSimpleCreateResponse, full_to_simple, path=["response"]) @@ -78,11 +79,12 @@ async def test_method_create(self, async_client: AsyncUnlayer) -> None: async def test_method_create_with_all_params(self, async_client: AsyncUnlayer) -> None: full_to_simple = await async_client.convert.full_to_simple.create( design={ - "body": {}, - "counters": {}, + "body": {"foo": "bar"}, + "counters": {"foo": "bar"}, "schema_version": 0, }, display_mode="email", + include_conversion=True, include_default_values=True, ) assert_matches_type(FullToSimpleCreateResponse, full_to_simple, path=["response"]) @@ -90,7 +92,7 @@ async def test_method_create_with_all_params(self, async_client: AsyncUnlayer) - @parametrize async def test_raw_response_create(self, async_client: AsyncUnlayer) -> None: response = await async_client.convert.full_to_simple.with_raw_response.create( - design={"body": {}}, + design={"body": {"foo": "bar"}}, ) assert response.is_closed is True @@ -101,7 +103,7 @@ async def test_raw_response_create(self, async_client: AsyncUnlayer) -> None: @parametrize async def test_streaming_response_create(self, async_client: AsyncUnlayer) -> None: async with async_client.convert.full_to_simple.with_streaming_response.create( - design={"body": {}}, + design={"body": {"foo": "bar"}}, ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" diff --git a/tests/api_resources/convert/test_simple_to_full.py b/tests/api_resources/convert/test_simple_to_full.py index e6d94f5..ca15814 100644 --- a/tests/api_resources/convert/test_simple_to_full.py +++ b/tests/api_resources/convert/test_simple_to_full.py @@ -20,7 +20,7 @@ class TestSimpleToFull: @parametrize def test_method_create(self, client: Unlayer) -> None: simple_to_full = client.convert.simple_to_full.create( - design={"body": {}}, + design={"body": {"foo": "bar"}}, ) assert_matches_type(SimpleToFullCreateResponse, simple_to_full, path=["response"]) @@ -28,12 +28,12 @@ def test_method_create(self, client: Unlayer) -> None: def test_method_create_with_all_params(self, client: Unlayer) -> None: simple_to_full = client.convert.simple_to_full.create( design={ - "body": {}, + "body": {"foo": "bar"}, "_conversion": { "data": "data", "version": 0, }, - "counters": {}, + "counters": {"foo": "bar"}, "schema_version": 0, }, display_mode="email", @@ -44,7 +44,7 @@ def test_method_create_with_all_params(self, client: Unlayer) -> None: @parametrize def test_raw_response_create(self, client: Unlayer) -> None: response = client.convert.simple_to_full.with_raw_response.create( - design={"body": {}}, + design={"body": {"foo": "bar"}}, ) assert response.is_closed is True @@ -55,7 +55,7 @@ def test_raw_response_create(self, client: Unlayer) -> None: @parametrize def test_streaming_response_create(self, client: Unlayer) -> None: with client.convert.simple_to_full.with_streaming_response.create( - design={"body": {}}, + design={"body": {"foo": "bar"}}, ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" @@ -74,7 +74,7 @@ class TestAsyncSimpleToFull: @parametrize async def test_method_create(self, async_client: AsyncUnlayer) -> None: simple_to_full = await async_client.convert.simple_to_full.create( - design={"body": {}}, + design={"body": {"foo": "bar"}}, ) assert_matches_type(SimpleToFullCreateResponse, simple_to_full, path=["response"]) @@ -82,12 +82,12 @@ async def test_method_create(self, async_client: AsyncUnlayer) -> None: async def test_method_create_with_all_params(self, async_client: AsyncUnlayer) -> None: simple_to_full = await async_client.convert.simple_to_full.create( design={ - "body": {}, + "body": {"foo": "bar"}, "_conversion": { "data": "data", "version": 0, }, - "counters": {}, + "counters": {"foo": "bar"}, "schema_version": 0, }, display_mode="email", @@ -98,7 +98,7 @@ async def test_method_create_with_all_params(self, async_client: AsyncUnlayer) - @parametrize async def test_raw_response_create(self, async_client: AsyncUnlayer) -> None: response = await async_client.convert.simple_to_full.with_raw_response.create( - design={"body": {}}, + design={"body": {"foo": "bar"}}, ) assert response.is_closed is True @@ -109,7 +109,7 @@ async def test_raw_response_create(self, async_client: AsyncUnlayer) -> None: @parametrize async def test_streaming_response_create(self, async_client: AsyncUnlayer) -> None: async with async_client.convert.simple_to_full.with_streaming_response.create( - design={"body": {}}, + design={"body": {"foo": "bar"}}, ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" diff --git a/tests/api_resources/documents/__init__.py b/tests/api_resources/documents/__init__.py deleted file mode 100644 index fd8019a..0000000 --- a/tests/api_resources/documents/__init__.py +++ /dev/null @@ -1 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. diff --git a/tests/api_resources/documents/test_generate.py b/tests/api_resources/documents/test_generate.py deleted file mode 100644 index a76551f..0000000 --- a/tests/api_resources/documents/test_generate.py +++ /dev/null @@ -1,110 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -import os -from typing import Any, cast - -import pytest - -from unlayer import Unlayer, AsyncUnlayer -from tests.utils import assert_matches_type -from unlayer.types.documents import GenerateCreateResponse - -base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") - - -class TestGenerate: - parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) - - @parametrize - def test_method_create(self, client: Unlayer) -> None: - generate = client.documents.generate.create( - project_id="projectId", - ) - assert_matches_type(GenerateCreateResponse, generate, path=["response"]) - - @parametrize - def test_method_create_with_all_params(self, client: Unlayer) -> None: - generate = client.documents.generate.create( - project_id="projectId", - design={"foo": "bar"}, - filename="filename", - html="html", - merge_tags={"foo": "string"}, - url="https://example.com", - ) - assert_matches_type(GenerateCreateResponse, generate, path=["response"]) - - @parametrize - def test_raw_response_create(self, client: Unlayer) -> None: - response = client.documents.generate.with_raw_response.create( - project_id="projectId", - ) - - assert response.is_closed is True - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - generate = response.parse() - assert_matches_type(GenerateCreateResponse, generate, path=["response"]) - - @parametrize - def test_streaming_response_create(self, client: Unlayer) -> None: - with client.documents.generate.with_streaming_response.create( - project_id="projectId", - ) as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - - generate = response.parse() - assert_matches_type(GenerateCreateResponse, generate, path=["response"]) - - assert cast(Any, response.is_closed) is True - - -class TestAsyncGenerate: - parametrize = pytest.mark.parametrize( - "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] - ) - - @parametrize - async def test_method_create(self, async_client: AsyncUnlayer) -> None: - generate = await async_client.documents.generate.create( - project_id="projectId", - ) - assert_matches_type(GenerateCreateResponse, generate, path=["response"]) - - @parametrize - async def test_method_create_with_all_params(self, async_client: AsyncUnlayer) -> None: - generate = await async_client.documents.generate.create( - project_id="projectId", - design={"foo": "bar"}, - filename="filename", - html="html", - merge_tags={"foo": "string"}, - url="https://example.com", - ) - assert_matches_type(GenerateCreateResponse, generate, path=["response"]) - - @parametrize - async def test_raw_response_create(self, async_client: AsyncUnlayer) -> None: - response = await async_client.documents.generate.with_raw_response.create( - project_id="projectId", - ) - - assert response.is_closed is True - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - generate = await response.parse() - assert_matches_type(GenerateCreateResponse, generate, path=["response"]) - - @parametrize - async def test_streaming_response_create(self, async_client: AsyncUnlayer) -> None: - async with async_client.documents.generate.with_streaming_response.create( - project_id="projectId", - ) as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - - generate = await response.parse() - assert_matches_type(GenerateCreateResponse, generate, path=["response"]) - - assert cast(Any, response.is_closed) is True diff --git a/tests/api_resources/documents/test_generate_template.py b/tests/api_resources/documents/test_generate_template.py deleted file mode 100644 index 28d8da9..0000000 --- a/tests/api_resources/documents/test_generate_template.py +++ /dev/null @@ -1,112 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -import os -from typing import Any, cast - -import pytest - -from unlayer import Unlayer, AsyncUnlayer -from tests.utils import assert_matches_type -from unlayer.types.documents import GenerateTemplateCreateResponse - -base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") - - -class TestGenerateTemplate: - parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) - - @parametrize - def test_method_create(self, client: Unlayer) -> None: - generate_template = client.documents.generate_template.create( - project_id="projectId", - template_id="templateId", - ) - assert_matches_type(GenerateTemplateCreateResponse, generate_template, path=["response"]) - - @parametrize - def test_method_create_with_all_params(self, client: Unlayer) -> None: - generate_template = client.documents.generate_template.create( - project_id="projectId", - template_id="templateId", - filename="filename", - merge_tags={"foo": "string"}, - ) - assert_matches_type(GenerateTemplateCreateResponse, generate_template, path=["response"]) - - @parametrize - def test_raw_response_create(self, client: Unlayer) -> None: - response = client.documents.generate_template.with_raw_response.create( - project_id="projectId", - template_id="templateId", - ) - - assert response.is_closed is True - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - generate_template = response.parse() - assert_matches_type(GenerateTemplateCreateResponse, generate_template, path=["response"]) - - @parametrize - def test_streaming_response_create(self, client: Unlayer) -> None: - with client.documents.generate_template.with_streaming_response.create( - project_id="projectId", - template_id="templateId", - ) as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - - generate_template = response.parse() - assert_matches_type(GenerateTemplateCreateResponse, generate_template, path=["response"]) - - assert cast(Any, response.is_closed) is True - - -class TestAsyncGenerateTemplate: - parametrize = pytest.mark.parametrize( - "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] - ) - - @parametrize - async def test_method_create(self, async_client: AsyncUnlayer) -> None: - generate_template = await async_client.documents.generate_template.create( - project_id="projectId", - template_id="templateId", - ) - assert_matches_type(GenerateTemplateCreateResponse, generate_template, path=["response"]) - - @parametrize - async def test_method_create_with_all_params(self, async_client: AsyncUnlayer) -> None: - generate_template = await async_client.documents.generate_template.create( - project_id="projectId", - template_id="templateId", - filename="filename", - merge_tags={"foo": "string"}, - ) - assert_matches_type(GenerateTemplateCreateResponse, generate_template, path=["response"]) - - @parametrize - async def test_raw_response_create(self, async_client: AsyncUnlayer) -> None: - response = await async_client.documents.generate_template.with_raw_response.create( - project_id="projectId", - template_id="templateId", - ) - - assert response.is_closed is True - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - generate_template = await response.parse() - assert_matches_type(GenerateTemplateCreateResponse, generate_template, path=["response"]) - - @parametrize - async def test_streaming_response_create(self, async_client: AsyncUnlayer) -> None: - async with async_client.documents.generate_template.with_streaming_response.create( - project_id="projectId", - template_id="templateId", - ) as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - - generate_template = await response.parse() - assert_matches_type(GenerateTemplateCreateResponse, generate_template, path=["response"]) - - assert cast(Any, response.is_closed) is True diff --git a/tests/api_resources/emails/__init__.py b/tests/api_resources/emails/__init__.py deleted file mode 100644 index fd8019a..0000000 --- a/tests/api_resources/emails/__init__.py +++ /dev/null @@ -1 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. diff --git a/tests/api_resources/emails/test_render.py b/tests/api_resources/emails/test_render.py deleted file mode 100644 index 0a3ee1a..0000000 --- a/tests/api_resources/emails/test_render.py +++ /dev/null @@ -1,110 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -import os -from typing import Any, cast - -import pytest - -from unlayer import Unlayer, AsyncUnlayer -from tests.utils import assert_matches_type -from unlayer.types.emails import RenderCreateResponse - -base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") - - -class TestRender: - parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) - - @parametrize - def test_method_create(self, client: Unlayer) -> None: - render = client.emails.render.create( - project_id="projectId", - design={"foo": "bar"}, - ) - assert_matches_type(RenderCreateResponse, render, path=["response"]) - - @parametrize - def test_method_create_with_all_params(self, client: Unlayer) -> None: - render = client.emails.render.create( - project_id="projectId", - design={"foo": "bar"}, - merge_tags={"foo": "string"}, - ) - assert_matches_type(RenderCreateResponse, render, path=["response"]) - - @parametrize - def test_raw_response_create(self, client: Unlayer) -> None: - response = client.emails.render.with_raw_response.create( - project_id="projectId", - design={"foo": "bar"}, - ) - - assert response.is_closed is True - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - render = response.parse() - assert_matches_type(RenderCreateResponse, render, path=["response"]) - - @parametrize - def test_streaming_response_create(self, client: Unlayer) -> None: - with client.emails.render.with_streaming_response.create( - project_id="projectId", - design={"foo": "bar"}, - ) as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - - render = response.parse() - assert_matches_type(RenderCreateResponse, render, path=["response"]) - - assert cast(Any, response.is_closed) is True - - -class TestAsyncRender: - parametrize = pytest.mark.parametrize( - "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] - ) - - @parametrize - async def test_method_create(self, async_client: AsyncUnlayer) -> None: - render = await async_client.emails.render.create( - project_id="projectId", - design={"foo": "bar"}, - ) - assert_matches_type(RenderCreateResponse, render, path=["response"]) - - @parametrize - async def test_method_create_with_all_params(self, async_client: AsyncUnlayer) -> None: - render = await async_client.emails.render.create( - project_id="projectId", - design={"foo": "bar"}, - merge_tags={"foo": "string"}, - ) - assert_matches_type(RenderCreateResponse, render, path=["response"]) - - @parametrize - async def test_raw_response_create(self, async_client: AsyncUnlayer) -> None: - response = await async_client.emails.render.with_raw_response.create( - project_id="projectId", - design={"foo": "bar"}, - ) - - assert response.is_closed is True - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - render = await response.parse() - assert_matches_type(RenderCreateResponse, render, path=["response"]) - - @parametrize - async def test_streaming_response_create(self, async_client: AsyncUnlayer) -> None: - async with async_client.emails.render.with_streaming_response.create( - project_id="projectId", - design={"foo": "bar"}, - ) as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - - render = await response.parse() - assert_matches_type(RenderCreateResponse, render, path=["response"]) - - assert cast(Any, response.is_closed) is True diff --git a/tests/api_resources/emails/test_send.py b/tests/api_resources/emails/test_send.py deleted file mode 100644 index 5f3f6e2..0000000 --- a/tests/api_resources/emails/test_send.py +++ /dev/null @@ -1,116 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -import os -from typing import Any, cast - -import pytest - -from unlayer import Unlayer, AsyncUnlayer -from tests.utils import assert_matches_type -from unlayer.types.emails import SendCreateResponse - -base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") - - -class TestSend: - parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) - - @parametrize - def test_method_create(self, client: Unlayer) -> None: - send = client.emails.send.create( - project_id="projectId", - to="dev@stainless.com", - ) - assert_matches_type(SendCreateResponse, send, path=["response"]) - - @parametrize - def test_method_create_with_all_params(self, client: Unlayer) -> None: - send = client.emails.send.create( - project_id="projectId", - to="dev@stainless.com", - design={"foo": "bar"}, - html="html", - merge_tags={"foo": "string"}, - subject="subject", - ) - assert_matches_type(SendCreateResponse, send, path=["response"]) - - @parametrize - def test_raw_response_create(self, client: Unlayer) -> None: - response = client.emails.send.with_raw_response.create( - project_id="projectId", - to="dev@stainless.com", - ) - - assert response.is_closed is True - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - send = response.parse() - assert_matches_type(SendCreateResponse, send, path=["response"]) - - @parametrize - def test_streaming_response_create(self, client: Unlayer) -> None: - with client.emails.send.with_streaming_response.create( - project_id="projectId", - to="dev@stainless.com", - ) as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - - send = response.parse() - assert_matches_type(SendCreateResponse, send, path=["response"]) - - assert cast(Any, response.is_closed) is True - - -class TestAsyncSend: - parametrize = pytest.mark.parametrize( - "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] - ) - - @parametrize - async def test_method_create(self, async_client: AsyncUnlayer) -> None: - send = await async_client.emails.send.create( - project_id="projectId", - to="dev@stainless.com", - ) - assert_matches_type(SendCreateResponse, send, path=["response"]) - - @parametrize - async def test_method_create_with_all_params(self, async_client: AsyncUnlayer) -> None: - send = await async_client.emails.send.create( - project_id="projectId", - to="dev@stainless.com", - design={"foo": "bar"}, - html="html", - merge_tags={"foo": "string"}, - subject="subject", - ) - assert_matches_type(SendCreateResponse, send, path=["response"]) - - @parametrize - async def test_raw_response_create(self, async_client: AsyncUnlayer) -> None: - response = await async_client.emails.send.with_raw_response.create( - project_id="projectId", - to="dev@stainless.com", - ) - - assert response.is_closed is True - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - send = await response.parse() - assert_matches_type(SendCreateResponse, send, path=["response"]) - - @parametrize - async def test_streaming_response_create(self, async_client: AsyncUnlayer) -> None: - async with async_client.emails.send.with_streaming_response.create( - project_id="projectId", - to="dev@stainless.com", - ) as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - - send = await response.parse() - assert_matches_type(SendCreateResponse, send, path=["response"]) - - assert cast(Any, response.is_closed) is True diff --git a/tests/api_resources/emails/test_send_template.py b/tests/api_resources/emails/test_send_template.py deleted file mode 100644 index 8ddda13..0000000 --- a/tests/api_resources/emails/test_send_template.py +++ /dev/null @@ -1,120 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -import os -from typing import Any, cast - -import pytest - -from unlayer import Unlayer, AsyncUnlayer -from tests.utils import assert_matches_type -from unlayer.types.emails import SendTemplateCreateResponse - -base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") - - -class TestSendTemplate: - parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) - - @parametrize - def test_method_create(self, client: Unlayer) -> None: - send_template = client.emails.send_template.create( - project_id="projectId", - template_id="templateId", - to="dev@stainless.com", - ) - assert_matches_type(SendTemplateCreateResponse, send_template, path=["response"]) - - @parametrize - def test_method_create_with_all_params(self, client: Unlayer) -> None: - send_template = client.emails.send_template.create( - project_id="projectId", - template_id="templateId", - to="dev@stainless.com", - merge_tags={"foo": "string"}, - subject="subject", - ) - assert_matches_type(SendTemplateCreateResponse, send_template, path=["response"]) - - @parametrize - def test_raw_response_create(self, client: Unlayer) -> None: - response = client.emails.send_template.with_raw_response.create( - project_id="projectId", - template_id="templateId", - to="dev@stainless.com", - ) - - assert response.is_closed is True - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - send_template = response.parse() - assert_matches_type(SendTemplateCreateResponse, send_template, path=["response"]) - - @parametrize - def test_streaming_response_create(self, client: Unlayer) -> None: - with client.emails.send_template.with_streaming_response.create( - project_id="projectId", - template_id="templateId", - to="dev@stainless.com", - ) as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - - send_template = response.parse() - assert_matches_type(SendTemplateCreateResponse, send_template, path=["response"]) - - assert cast(Any, response.is_closed) is True - - -class TestAsyncSendTemplate: - parametrize = pytest.mark.parametrize( - "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] - ) - - @parametrize - async def test_method_create(self, async_client: AsyncUnlayer) -> None: - send_template = await async_client.emails.send_template.create( - project_id="projectId", - template_id="templateId", - to="dev@stainless.com", - ) - assert_matches_type(SendTemplateCreateResponse, send_template, path=["response"]) - - @parametrize - async def test_method_create_with_all_params(self, async_client: AsyncUnlayer) -> None: - send_template = await async_client.emails.send_template.create( - project_id="projectId", - template_id="templateId", - to="dev@stainless.com", - merge_tags={"foo": "string"}, - subject="subject", - ) - assert_matches_type(SendTemplateCreateResponse, send_template, path=["response"]) - - @parametrize - async def test_raw_response_create(self, async_client: AsyncUnlayer) -> None: - response = await async_client.emails.send_template.with_raw_response.create( - project_id="projectId", - template_id="templateId", - to="dev@stainless.com", - ) - - assert response.is_closed is True - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - send_template = await response.parse() - assert_matches_type(SendTemplateCreateResponse, send_template, path=["response"]) - - @parametrize - async def test_streaming_response_create(self, async_client: AsyncUnlayer) -> None: - async with async_client.emails.send_template.with_streaming_response.create( - project_id="projectId", - template_id="templateId", - to="dev@stainless.com", - ) as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - - send_template = await response.parse() - assert_matches_type(SendTemplateCreateResponse, send_template, path=["response"]) - - assert cast(Any, response.is_closed) is True diff --git a/tests/api_resources/export/__init__.py b/tests/api_resources/export/__init__.py deleted file mode 100644 index fd8019a..0000000 --- a/tests/api_resources/export/__init__.py +++ /dev/null @@ -1 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. diff --git a/tests/api_resources/export/test_html.py b/tests/api_resources/export/test_html.py deleted file mode 100644 index a0897ea..0000000 --- a/tests/api_resources/export/test_html.py +++ /dev/null @@ -1,86 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -import os -from typing import Any, cast - -import pytest - -from unlayer import Unlayer, AsyncUnlayer -from tests.utils import assert_matches_type -from unlayer.types.export import HTMLRetrieveResponse - -base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") - - -class TestHTML: - parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) - - @parametrize - def test_method_retrieve(self, client: Unlayer) -> None: - html = client.export.html.retrieve( - project_id="projectId", - ) - assert_matches_type(HTMLRetrieveResponse, html, path=["response"]) - - @parametrize - def test_raw_response_retrieve(self, client: Unlayer) -> None: - response = client.export.html.with_raw_response.retrieve( - project_id="projectId", - ) - - assert response.is_closed is True - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - html = response.parse() - assert_matches_type(HTMLRetrieveResponse, html, path=["response"]) - - @parametrize - def test_streaming_response_retrieve(self, client: Unlayer) -> None: - with client.export.html.with_streaming_response.retrieve( - project_id="projectId", - ) as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - - html = response.parse() - assert_matches_type(HTMLRetrieveResponse, html, path=["response"]) - - assert cast(Any, response.is_closed) is True - - -class TestAsyncHTML: - parametrize = pytest.mark.parametrize( - "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] - ) - - @parametrize - async def test_method_retrieve(self, async_client: AsyncUnlayer) -> None: - html = await async_client.export.html.retrieve( - project_id="projectId", - ) - assert_matches_type(HTMLRetrieveResponse, html, path=["response"]) - - @parametrize - async def test_raw_response_retrieve(self, async_client: AsyncUnlayer) -> None: - response = await async_client.export.html.with_raw_response.retrieve( - project_id="projectId", - ) - - assert response.is_closed is True - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - html = await response.parse() - assert_matches_type(HTMLRetrieveResponse, html, path=["response"]) - - @parametrize - async def test_streaming_response_retrieve(self, async_client: AsyncUnlayer) -> None: - async with async_client.export.html.with_streaming_response.retrieve( - project_id="projectId", - ) as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - - html = await response.parse() - assert_matches_type(HTMLRetrieveResponse, html, path=["response"]) - - assert cast(Any, response.is_closed) is True diff --git a/tests/api_resources/export/test_image.py b/tests/api_resources/export/test_image.py deleted file mode 100644 index 890db5c..0000000 --- a/tests/api_resources/export/test_image.py +++ /dev/null @@ -1,86 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -import os -from typing import Any, cast - -import pytest - -from unlayer import Unlayer, AsyncUnlayer -from tests.utils import assert_matches_type -from unlayer.types.export import ImageRetrieveResponse - -base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") - - -class TestImage: - parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) - - @parametrize - def test_method_retrieve(self, client: Unlayer) -> None: - image = client.export.image.retrieve( - project_id="projectId", - ) - assert_matches_type(ImageRetrieveResponse, image, path=["response"]) - - @parametrize - def test_raw_response_retrieve(self, client: Unlayer) -> None: - response = client.export.image.with_raw_response.retrieve( - project_id="projectId", - ) - - assert response.is_closed is True - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - image = response.parse() - assert_matches_type(ImageRetrieveResponse, image, path=["response"]) - - @parametrize - def test_streaming_response_retrieve(self, client: Unlayer) -> None: - with client.export.image.with_streaming_response.retrieve( - project_id="projectId", - ) as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - - image = response.parse() - assert_matches_type(ImageRetrieveResponse, image, path=["response"]) - - assert cast(Any, response.is_closed) is True - - -class TestAsyncImage: - parametrize = pytest.mark.parametrize( - "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] - ) - - @parametrize - async def test_method_retrieve(self, async_client: AsyncUnlayer) -> None: - image = await async_client.export.image.retrieve( - project_id="projectId", - ) - assert_matches_type(ImageRetrieveResponse, image, path=["response"]) - - @parametrize - async def test_raw_response_retrieve(self, async_client: AsyncUnlayer) -> None: - response = await async_client.export.image.with_raw_response.retrieve( - project_id="projectId", - ) - - assert response.is_closed is True - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - image = await response.parse() - assert_matches_type(ImageRetrieveResponse, image, path=["response"]) - - @parametrize - async def test_streaming_response_retrieve(self, async_client: AsyncUnlayer) -> None: - async with async_client.export.image.with_streaming_response.retrieve( - project_id="projectId", - ) as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - - image = await response.parse() - assert_matches_type(ImageRetrieveResponse, image, path=["response"]) - - assert cast(Any, response.is_closed) is True diff --git a/tests/api_resources/export/test_pdf.py b/tests/api_resources/export/test_pdf.py deleted file mode 100644 index e06d306..0000000 --- a/tests/api_resources/export/test_pdf.py +++ /dev/null @@ -1,86 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -import os -from typing import Any, cast - -import pytest - -from unlayer import Unlayer, AsyncUnlayer -from tests.utils import assert_matches_type -from unlayer.types.export import PdfRetrieveResponse - -base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") - - -class TestPdf: - parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) - - @parametrize - def test_method_retrieve(self, client: Unlayer) -> None: - pdf = client.export.pdf.retrieve( - project_id="projectId", - ) - assert_matches_type(PdfRetrieveResponse, pdf, path=["response"]) - - @parametrize - def test_raw_response_retrieve(self, client: Unlayer) -> None: - response = client.export.pdf.with_raw_response.retrieve( - project_id="projectId", - ) - - assert response.is_closed is True - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - pdf = response.parse() - assert_matches_type(PdfRetrieveResponse, pdf, path=["response"]) - - @parametrize - def test_streaming_response_retrieve(self, client: Unlayer) -> None: - with client.export.pdf.with_streaming_response.retrieve( - project_id="projectId", - ) as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - - pdf = response.parse() - assert_matches_type(PdfRetrieveResponse, pdf, path=["response"]) - - assert cast(Any, response.is_closed) is True - - -class TestAsyncPdf: - parametrize = pytest.mark.parametrize( - "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] - ) - - @parametrize - async def test_method_retrieve(self, async_client: AsyncUnlayer) -> None: - pdf = await async_client.export.pdf.retrieve( - project_id="projectId", - ) - assert_matches_type(PdfRetrieveResponse, pdf, path=["response"]) - - @parametrize - async def test_raw_response_retrieve(self, async_client: AsyncUnlayer) -> None: - response = await async_client.export.pdf.with_raw_response.retrieve( - project_id="projectId", - ) - - assert response.is_closed is True - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - pdf = await response.parse() - assert_matches_type(PdfRetrieveResponse, pdf, path=["response"]) - - @parametrize - async def test_streaming_response_retrieve(self, async_client: AsyncUnlayer) -> None: - async with async_client.export.pdf.with_streaming_response.retrieve( - project_id="projectId", - ) as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - - pdf = await response.parse() - assert_matches_type(PdfRetrieveResponse, pdf, path=["response"]) - - assert cast(Any, response.is_closed) is True diff --git a/tests/api_resources/export/test_zip.py b/tests/api_resources/export/test_zip.py deleted file mode 100644 index ca8795c..0000000 --- a/tests/api_resources/export/test_zip.py +++ /dev/null @@ -1,86 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -import os -from typing import Any, cast - -import pytest - -from unlayer import Unlayer, AsyncUnlayer -from tests.utils import assert_matches_type -from unlayer.types.export import ZipRetrieveResponse - -base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") - - -class TestZip: - parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) - - @parametrize - def test_method_retrieve(self, client: Unlayer) -> None: - zip = client.export.zip.retrieve( - project_id="projectId", - ) - assert_matches_type(ZipRetrieveResponse, zip, path=["response"]) - - @parametrize - def test_raw_response_retrieve(self, client: Unlayer) -> None: - response = client.export.zip.with_raw_response.retrieve( - project_id="projectId", - ) - - assert response.is_closed is True - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - zip = response.parse() - assert_matches_type(ZipRetrieveResponse, zip, path=["response"]) - - @parametrize - def test_streaming_response_retrieve(self, client: Unlayer) -> None: - with client.export.zip.with_streaming_response.retrieve( - project_id="projectId", - ) as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - - zip = response.parse() - assert_matches_type(ZipRetrieveResponse, zip, path=["response"]) - - assert cast(Any, response.is_closed) is True - - -class TestAsyncZip: - parametrize = pytest.mark.parametrize( - "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] - ) - - @parametrize - async def test_method_retrieve(self, async_client: AsyncUnlayer) -> None: - zip = await async_client.export.zip.retrieve( - project_id="projectId", - ) - assert_matches_type(ZipRetrieveResponse, zip, path=["response"]) - - @parametrize - async def test_raw_response_retrieve(self, async_client: AsyncUnlayer) -> None: - response = await async_client.export.zip.with_raw_response.retrieve( - project_id="projectId", - ) - - assert response.is_closed is True - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - zip = await response.parse() - assert_matches_type(ZipRetrieveResponse, zip, path=["response"]) - - @parametrize - async def test_streaming_response_retrieve(self, async_client: AsyncUnlayer) -> None: - async with async_client.export.zip.with_streaming_response.retrieve( - project_id="projectId", - ) as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - - zip = await response.parse() - assert_matches_type(ZipRetrieveResponse, zip, path=["response"]) - - assert cast(Any, response.is_closed) is True diff --git a/tests/api_resources/pages/__init__.py b/tests/api_resources/pages/__init__.py deleted file mode 100644 index fd8019a..0000000 --- a/tests/api_resources/pages/__init__.py +++ /dev/null @@ -1 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. diff --git a/tests/api_resources/pages/test_render.py b/tests/api_resources/pages/test_render.py deleted file mode 100644 index 5b19dee..0000000 --- a/tests/api_resources/pages/test_render.py +++ /dev/null @@ -1,110 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -import os -from typing import Any, cast - -import pytest - -from unlayer import Unlayer, AsyncUnlayer -from tests.utils import assert_matches_type -from unlayer.types.pages import RenderCreateResponse - -base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") - - -class TestRender: - parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) - - @parametrize - def test_method_create(self, client: Unlayer) -> None: - render = client.pages.render.create( - project_id="projectId", - design={"foo": "bar"}, - ) - assert_matches_type(RenderCreateResponse, render, path=["response"]) - - @parametrize - def test_method_create_with_all_params(self, client: Unlayer) -> None: - render = client.pages.render.create( - project_id="projectId", - design={"foo": "bar"}, - merge_tags={"foo": "string"}, - ) - assert_matches_type(RenderCreateResponse, render, path=["response"]) - - @parametrize - def test_raw_response_create(self, client: Unlayer) -> None: - response = client.pages.render.with_raw_response.create( - project_id="projectId", - design={"foo": "bar"}, - ) - - assert response.is_closed is True - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - render = response.parse() - assert_matches_type(RenderCreateResponse, render, path=["response"]) - - @parametrize - def test_streaming_response_create(self, client: Unlayer) -> None: - with client.pages.render.with_streaming_response.create( - project_id="projectId", - design={"foo": "bar"}, - ) as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - - render = response.parse() - assert_matches_type(RenderCreateResponse, render, path=["response"]) - - assert cast(Any, response.is_closed) is True - - -class TestAsyncRender: - parametrize = pytest.mark.parametrize( - "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] - ) - - @parametrize - async def test_method_create(self, async_client: AsyncUnlayer) -> None: - render = await async_client.pages.render.create( - project_id="projectId", - design={"foo": "bar"}, - ) - assert_matches_type(RenderCreateResponse, render, path=["response"]) - - @parametrize - async def test_method_create_with_all_params(self, async_client: AsyncUnlayer) -> None: - render = await async_client.pages.render.create( - project_id="projectId", - design={"foo": "bar"}, - merge_tags={"foo": "string"}, - ) - assert_matches_type(RenderCreateResponse, render, path=["response"]) - - @parametrize - async def test_raw_response_create(self, async_client: AsyncUnlayer) -> None: - response = await async_client.pages.render.with_raw_response.create( - project_id="projectId", - design={"foo": "bar"}, - ) - - assert response.is_closed is True - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - render = await response.parse() - assert_matches_type(RenderCreateResponse, render, path=["response"]) - - @parametrize - async def test_streaming_response_create(self, async_client: AsyncUnlayer) -> None: - async with async_client.pages.render.with_streaming_response.create( - project_id="projectId", - design={"foo": "bar"}, - ) as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - - render = await response.parse() - assert_matches_type(RenderCreateResponse, render, path=["response"]) - - assert cast(Any, response.is_closed) is True diff --git a/tests/api_resources/project/test_domains.py b/tests/api_resources/project/test_domains.py deleted file mode 100644 index ea80f6c..0000000 --- a/tests/api_resources/project/test_domains.py +++ /dev/null @@ -1,403 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -import os -from typing import Any, cast - -import pytest - -from unlayer import Unlayer, AsyncUnlayer -from tests.utils import assert_matches_type -from unlayer.types.project import ( - DomainListResponse, - DomainCreateResponse, - DomainUpdateResponse, - DomainRetrieveResponse, -) - -base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") - - -class TestDomains: - parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) - - @parametrize - def test_method_create(self, client: Unlayer) -> None: - domain = client.project.domains.create( - project_id="projectId", - domain="domain", - ) - assert_matches_type(DomainCreateResponse, domain, path=["response"]) - - @parametrize - def test_raw_response_create(self, client: Unlayer) -> None: - response = client.project.domains.with_raw_response.create( - project_id="projectId", - domain="domain", - ) - - assert response.is_closed is True - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - domain = response.parse() - assert_matches_type(DomainCreateResponse, domain, path=["response"]) - - @parametrize - def test_streaming_response_create(self, client: Unlayer) -> None: - with client.project.domains.with_streaming_response.create( - project_id="projectId", - domain="domain", - ) as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - - domain = response.parse() - assert_matches_type(DomainCreateResponse, domain, path=["response"]) - - assert cast(Any, response.is_closed) is True - - @parametrize - def test_method_retrieve(self, client: Unlayer) -> None: - domain = client.project.domains.retrieve( - "id", - ) - assert_matches_type(DomainRetrieveResponse, domain, path=["response"]) - - @parametrize - def test_raw_response_retrieve(self, client: Unlayer) -> None: - response = client.project.domains.with_raw_response.retrieve( - "id", - ) - - assert response.is_closed is True - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - domain = response.parse() - assert_matches_type(DomainRetrieveResponse, domain, path=["response"]) - - @parametrize - def test_streaming_response_retrieve(self, client: Unlayer) -> None: - with client.project.domains.with_streaming_response.retrieve( - "id", - ) as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - - domain = response.parse() - assert_matches_type(DomainRetrieveResponse, domain, path=["response"]) - - assert cast(Any, response.is_closed) is True - - @parametrize - def test_path_params_retrieve(self, client: Unlayer) -> None: - with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): - client.project.domains.with_raw_response.retrieve( - "", - ) - - @parametrize - def test_method_update(self, client: Unlayer) -> None: - domain = client.project.domains.update( - id="id", - ) - assert_matches_type(DomainUpdateResponse, domain, path=["response"]) - - @parametrize - def test_method_update_with_all_params(self, client: Unlayer) -> None: - domain = client.project.domains.update( - id="id", - domain="domain", - ) - assert_matches_type(DomainUpdateResponse, domain, path=["response"]) - - @parametrize - def test_raw_response_update(self, client: Unlayer) -> None: - response = client.project.domains.with_raw_response.update( - id="id", - ) - - assert response.is_closed is True - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - domain = response.parse() - assert_matches_type(DomainUpdateResponse, domain, path=["response"]) - - @parametrize - def test_streaming_response_update(self, client: Unlayer) -> None: - with client.project.domains.with_streaming_response.update( - id="id", - ) as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - - domain = response.parse() - assert_matches_type(DomainUpdateResponse, domain, path=["response"]) - - assert cast(Any, response.is_closed) is True - - @parametrize - def test_path_params_update(self, client: Unlayer) -> None: - with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): - client.project.domains.with_raw_response.update( - id="", - ) - - @parametrize - def test_method_list(self, client: Unlayer) -> None: - domain = client.project.domains.list( - project_id="projectId", - ) - assert_matches_type(DomainListResponse, domain, path=["response"]) - - @parametrize - def test_raw_response_list(self, client: Unlayer) -> None: - response = client.project.domains.with_raw_response.list( - project_id="projectId", - ) - - assert response.is_closed is True - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - domain = response.parse() - assert_matches_type(DomainListResponse, domain, path=["response"]) - - @parametrize - def test_streaming_response_list(self, client: Unlayer) -> None: - with client.project.domains.with_streaming_response.list( - project_id="projectId", - ) as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - - domain = response.parse() - assert_matches_type(DomainListResponse, domain, path=["response"]) - - assert cast(Any, response.is_closed) is True - - @parametrize - def test_method_delete(self, client: Unlayer) -> None: - domain = client.project.domains.delete( - "id", - ) - assert domain is None - - @parametrize - def test_raw_response_delete(self, client: Unlayer) -> None: - response = client.project.domains.with_raw_response.delete( - "id", - ) - - assert response.is_closed is True - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - domain = response.parse() - assert domain is None - - @parametrize - def test_streaming_response_delete(self, client: Unlayer) -> None: - with client.project.domains.with_streaming_response.delete( - "id", - ) as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - - domain = response.parse() - assert domain is None - - assert cast(Any, response.is_closed) is True - - @parametrize - def test_path_params_delete(self, client: Unlayer) -> None: - with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): - client.project.domains.with_raw_response.delete( - "", - ) - - -class TestAsyncDomains: - parametrize = pytest.mark.parametrize( - "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] - ) - - @parametrize - async def test_method_create(self, async_client: AsyncUnlayer) -> None: - domain = await async_client.project.domains.create( - project_id="projectId", - domain="domain", - ) - assert_matches_type(DomainCreateResponse, domain, path=["response"]) - - @parametrize - async def test_raw_response_create(self, async_client: AsyncUnlayer) -> None: - response = await async_client.project.domains.with_raw_response.create( - project_id="projectId", - domain="domain", - ) - - assert response.is_closed is True - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - domain = await response.parse() - assert_matches_type(DomainCreateResponse, domain, path=["response"]) - - @parametrize - async def test_streaming_response_create(self, async_client: AsyncUnlayer) -> None: - async with async_client.project.domains.with_streaming_response.create( - project_id="projectId", - domain="domain", - ) as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - - domain = await response.parse() - assert_matches_type(DomainCreateResponse, domain, path=["response"]) - - assert cast(Any, response.is_closed) is True - - @parametrize - async def test_method_retrieve(self, async_client: AsyncUnlayer) -> None: - domain = await async_client.project.domains.retrieve( - "id", - ) - assert_matches_type(DomainRetrieveResponse, domain, path=["response"]) - - @parametrize - async def test_raw_response_retrieve(self, async_client: AsyncUnlayer) -> None: - response = await async_client.project.domains.with_raw_response.retrieve( - "id", - ) - - assert response.is_closed is True - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - domain = await response.parse() - assert_matches_type(DomainRetrieveResponse, domain, path=["response"]) - - @parametrize - async def test_streaming_response_retrieve(self, async_client: AsyncUnlayer) -> None: - async with async_client.project.domains.with_streaming_response.retrieve( - "id", - ) as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - - domain = await response.parse() - assert_matches_type(DomainRetrieveResponse, domain, path=["response"]) - - assert cast(Any, response.is_closed) is True - - @parametrize - async def test_path_params_retrieve(self, async_client: AsyncUnlayer) -> None: - with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): - await async_client.project.domains.with_raw_response.retrieve( - "", - ) - - @parametrize - async def test_method_update(self, async_client: AsyncUnlayer) -> None: - domain = await async_client.project.domains.update( - id="id", - ) - assert_matches_type(DomainUpdateResponse, domain, path=["response"]) - - @parametrize - async def test_method_update_with_all_params(self, async_client: AsyncUnlayer) -> None: - domain = await async_client.project.domains.update( - id="id", - domain="domain", - ) - assert_matches_type(DomainUpdateResponse, domain, path=["response"]) - - @parametrize - async def test_raw_response_update(self, async_client: AsyncUnlayer) -> None: - response = await async_client.project.domains.with_raw_response.update( - id="id", - ) - - assert response.is_closed is True - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - domain = await response.parse() - assert_matches_type(DomainUpdateResponse, domain, path=["response"]) - - @parametrize - async def test_streaming_response_update(self, async_client: AsyncUnlayer) -> None: - async with async_client.project.domains.with_streaming_response.update( - id="id", - ) as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - - domain = await response.parse() - assert_matches_type(DomainUpdateResponse, domain, path=["response"]) - - assert cast(Any, response.is_closed) is True - - @parametrize - async def test_path_params_update(self, async_client: AsyncUnlayer) -> None: - with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): - await async_client.project.domains.with_raw_response.update( - id="", - ) - - @parametrize - async def test_method_list(self, async_client: AsyncUnlayer) -> None: - domain = await async_client.project.domains.list( - project_id="projectId", - ) - assert_matches_type(DomainListResponse, domain, path=["response"]) - - @parametrize - async def test_raw_response_list(self, async_client: AsyncUnlayer) -> None: - response = await async_client.project.domains.with_raw_response.list( - project_id="projectId", - ) - - assert response.is_closed is True - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - domain = await response.parse() - assert_matches_type(DomainListResponse, domain, path=["response"]) - - @parametrize - async def test_streaming_response_list(self, async_client: AsyncUnlayer) -> None: - async with async_client.project.domains.with_streaming_response.list( - project_id="projectId", - ) as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - - domain = await response.parse() - assert_matches_type(DomainListResponse, domain, path=["response"]) - - assert cast(Any, response.is_closed) is True - - @parametrize - async def test_method_delete(self, async_client: AsyncUnlayer) -> None: - domain = await async_client.project.domains.delete( - "id", - ) - assert domain is None - - @parametrize - async def test_raw_response_delete(self, async_client: AsyncUnlayer) -> None: - response = await async_client.project.domains.with_raw_response.delete( - "id", - ) - - assert response.is_closed is True - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - domain = await response.parse() - assert domain is None - - @parametrize - async def test_streaming_response_delete(self, async_client: AsyncUnlayer) -> None: - async with async_client.project.domains.with_streaming_response.delete( - "id", - ) as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - - domain = await response.parse() - assert domain is None - - assert cast(Any, response.is_closed) is True - - @parametrize - async def test_path_params_delete(self, async_client: AsyncUnlayer) -> None: - with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): - await async_client.project.domains.with_raw_response.delete( - "", - ) diff --git a/tests/api_resources/project/test_templates.py b/tests/api_resources/project/test_templates.py index 3aa52c6..3354681 100644 --- a/tests/api_resources/project/test_templates.py +++ b/tests/api_resources/project/test_templates.py @@ -9,10 +9,9 @@ from unlayer import Unlayer, AsyncUnlayer from tests.utils import assert_matches_type +from unlayer.pagination import SyncCursorPage, AsyncCursorPage from unlayer.types.project import ( TemplateListResponse, - TemplateCreateResponse, - TemplateUpdateResponse, TemplateRetrieveResponse, ) @@ -22,60 +21,19 @@ class TestTemplates: parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) - @parametrize - def test_method_create(self, client: Unlayer) -> None: - template = client.project.templates.create( - project_id="projectId", - name="name", - ) - assert_matches_type(TemplateCreateResponse, template, path=["response"]) - - @parametrize - def test_method_create_with_all_params(self, client: Unlayer) -> None: - template = client.project.templates.create( - project_id="projectId", - name="name", - display_mode="email", - ) - assert_matches_type(TemplateCreateResponse, template, path=["response"]) - - @parametrize - def test_raw_response_create(self, client: Unlayer) -> None: - response = client.project.templates.with_raw_response.create( - project_id="projectId", - name="name", - ) - - assert response.is_closed is True - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - template = response.parse() - assert_matches_type(TemplateCreateResponse, template, path=["response"]) - - @parametrize - def test_streaming_response_create(self, client: Unlayer) -> None: - with client.project.templates.with_streaming_response.create( - project_id="projectId", - name="name", - ) as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - - template = response.parse() - assert_matches_type(TemplateCreateResponse, template, path=["response"]) - - assert cast(Any, response.is_closed) is True - @parametrize def test_method_retrieve(self, client: Unlayer) -> None: template = client.project.templates.retrieve( - "id", + id="id", + project_id="projectId", ) assert_matches_type(TemplateRetrieveResponse, template, path=["response"]) @parametrize def test_raw_response_retrieve(self, client: Unlayer) -> None: response = client.project.templates.with_raw_response.retrieve( - "id", + id="id", + project_id="projectId", ) assert response.is_closed is True @@ -86,7 +44,8 @@ def test_raw_response_retrieve(self, client: Unlayer) -> None: @parametrize def test_streaming_response_retrieve(self, client: Unlayer) -> None: with client.project.templates.with_streaming_response.retrieve( - "id", + id="id", + project_id="projectId", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" @@ -100,55 +59,8 @@ def test_streaming_response_retrieve(self, client: Unlayer) -> None: def test_path_params_retrieve(self, client: Unlayer) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): client.project.templates.with_raw_response.retrieve( - "", - ) - - @parametrize - def test_method_update(self, client: Unlayer) -> None: - template = client.project.templates.update( - id="id", - ) - assert_matches_type(TemplateUpdateResponse, template, path=["response"]) - - @parametrize - def test_method_update_with_all_params(self, client: Unlayer) -> None: - template = client.project.templates.update( - id="id", - body="body", - name="name", - subject="subject", - ) - assert_matches_type(TemplateUpdateResponse, template, path=["response"]) - - @parametrize - def test_raw_response_update(self, client: Unlayer) -> None: - response = client.project.templates.with_raw_response.update( - id="id", - ) - - assert response.is_closed is True - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - template = response.parse() - assert_matches_type(TemplateUpdateResponse, template, path=["response"]) - - @parametrize - def test_streaming_response_update(self, client: Unlayer) -> None: - with client.project.templates.with_streaming_response.update( - id="id", - ) as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - - template = response.parse() - assert_matches_type(TemplateUpdateResponse, template, path=["response"]) - - assert cast(Any, response.is_closed) is True - - @parametrize - def test_path_params_update(self, client: Unlayer) -> None: - with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): - client.project.templates.with_raw_response.update( id="", + project_id="projectId", ) @parametrize @@ -156,7 +68,7 @@ def test_method_list(self, client: Unlayer) -> None: template = client.project.templates.list( project_id="projectId", ) - assert_matches_type(TemplateListResponse, template, path=["response"]) + assert_matches_type(SyncCursorPage[TemplateListResponse], template, path=["response"]) @parametrize def test_method_list_with_all_params(self, client: Unlayer) -> None: @@ -167,7 +79,7 @@ def test_method_list_with_all_params(self, client: Unlayer) -> None: limit=1, name="name", ) - assert_matches_type(TemplateListResponse, template, path=["response"]) + assert_matches_type(SyncCursorPage[TemplateListResponse], template, path=["response"]) @parametrize def test_raw_response_list(self, client: Unlayer) -> None: @@ -178,7 +90,7 @@ def test_raw_response_list(self, client: Unlayer) -> None: assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" template = response.parse() - assert_matches_type(TemplateListResponse, template, path=["response"]) + assert_matches_type(SyncCursorPage[TemplateListResponse], template, path=["response"]) @parametrize def test_streaming_response_list(self, client: Unlayer) -> None: @@ -189,108 +101,29 @@ def test_streaming_response_list(self, client: Unlayer) -> None: assert response.http_request.headers.get("X-Stainless-Lang") == "python" template = response.parse() - assert_matches_type(TemplateListResponse, template, path=["response"]) + assert_matches_type(SyncCursorPage[TemplateListResponse], template, path=["response"]) assert cast(Any, response.is_closed) is True - @parametrize - def test_method_delete(self, client: Unlayer) -> None: - template = client.project.templates.delete( - "id", - ) - assert template is None - - @parametrize - def test_raw_response_delete(self, client: Unlayer) -> None: - response = client.project.templates.with_raw_response.delete( - "id", - ) - - assert response.is_closed is True - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - template = response.parse() - assert template is None - - @parametrize - def test_streaming_response_delete(self, client: Unlayer) -> None: - with client.project.templates.with_streaming_response.delete( - "id", - ) as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - - template = response.parse() - assert template is None - - assert cast(Any, response.is_closed) is True - - @parametrize - def test_path_params_delete(self, client: Unlayer) -> None: - with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): - client.project.templates.with_raw_response.delete( - "", - ) - class TestAsyncTemplates: parametrize = pytest.mark.parametrize( "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] ) - @parametrize - async def test_method_create(self, async_client: AsyncUnlayer) -> None: - template = await async_client.project.templates.create( - project_id="projectId", - name="name", - ) - assert_matches_type(TemplateCreateResponse, template, path=["response"]) - - @parametrize - async def test_method_create_with_all_params(self, async_client: AsyncUnlayer) -> None: - template = await async_client.project.templates.create( - project_id="projectId", - name="name", - display_mode="email", - ) - assert_matches_type(TemplateCreateResponse, template, path=["response"]) - - @parametrize - async def test_raw_response_create(self, async_client: AsyncUnlayer) -> None: - response = await async_client.project.templates.with_raw_response.create( - project_id="projectId", - name="name", - ) - - assert response.is_closed is True - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - template = await response.parse() - assert_matches_type(TemplateCreateResponse, template, path=["response"]) - - @parametrize - async def test_streaming_response_create(self, async_client: AsyncUnlayer) -> None: - async with async_client.project.templates.with_streaming_response.create( - project_id="projectId", - name="name", - ) as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - - template = await response.parse() - assert_matches_type(TemplateCreateResponse, template, path=["response"]) - - assert cast(Any, response.is_closed) is True - @parametrize async def test_method_retrieve(self, async_client: AsyncUnlayer) -> None: template = await async_client.project.templates.retrieve( - "id", + id="id", + project_id="projectId", ) assert_matches_type(TemplateRetrieveResponse, template, path=["response"]) @parametrize async def test_raw_response_retrieve(self, async_client: AsyncUnlayer) -> None: response = await async_client.project.templates.with_raw_response.retrieve( - "id", + id="id", + project_id="projectId", ) assert response.is_closed is True @@ -301,7 +134,8 @@ async def test_raw_response_retrieve(self, async_client: AsyncUnlayer) -> None: @parametrize async def test_streaming_response_retrieve(self, async_client: AsyncUnlayer) -> None: async with async_client.project.templates.with_streaming_response.retrieve( - "id", + id="id", + project_id="projectId", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" @@ -315,55 +149,8 @@ async def test_streaming_response_retrieve(self, async_client: AsyncUnlayer) -> async def test_path_params_retrieve(self, async_client: AsyncUnlayer) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): await async_client.project.templates.with_raw_response.retrieve( - "", - ) - - @parametrize - async def test_method_update(self, async_client: AsyncUnlayer) -> None: - template = await async_client.project.templates.update( - id="id", - ) - assert_matches_type(TemplateUpdateResponse, template, path=["response"]) - - @parametrize - async def test_method_update_with_all_params(self, async_client: AsyncUnlayer) -> None: - template = await async_client.project.templates.update( - id="id", - body="body", - name="name", - subject="subject", - ) - assert_matches_type(TemplateUpdateResponse, template, path=["response"]) - - @parametrize - async def test_raw_response_update(self, async_client: AsyncUnlayer) -> None: - response = await async_client.project.templates.with_raw_response.update( - id="id", - ) - - assert response.is_closed is True - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - template = await response.parse() - assert_matches_type(TemplateUpdateResponse, template, path=["response"]) - - @parametrize - async def test_streaming_response_update(self, async_client: AsyncUnlayer) -> None: - async with async_client.project.templates.with_streaming_response.update( - id="id", - ) as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - - template = await response.parse() - assert_matches_type(TemplateUpdateResponse, template, path=["response"]) - - assert cast(Any, response.is_closed) is True - - @parametrize - async def test_path_params_update(self, async_client: AsyncUnlayer) -> None: - with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): - await async_client.project.templates.with_raw_response.update( id="", + project_id="projectId", ) @parametrize @@ -371,7 +158,7 @@ async def test_method_list(self, async_client: AsyncUnlayer) -> None: template = await async_client.project.templates.list( project_id="projectId", ) - assert_matches_type(TemplateListResponse, template, path=["response"]) + assert_matches_type(AsyncCursorPage[TemplateListResponse], template, path=["response"]) @parametrize async def test_method_list_with_all_params(self, async_client: AsyncUnlayer) -> None: @@ -382,7 +169,7 @@ async def test_method_list_with_all_params(self, async_client: AsyncUnlayer) -> limit=1, name="name", ) - assert_matches_type(TemplateListResponse, template, path=["response"]) + assert_matches_type(AsyncCursorPage[TemplateListResponse], template, path=["response"]) @parametrize async def test_raw_response_list(self, async_client: AsyncUnlayer) -> None: @@ -393,7 +180,7 @@ async def test_raw_response_list(self, async_client: AsyncUnlayer) -> None: assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" template = await response.parse() - assert_matches_type(TemplateListResponse, template, path=["response"]) + assert_matches_type(AsyncCursorPage[TemplateListResponse], template, path=["response"]) @parametrize async def test_streaming_response_list(self, async_client: AsyncUnlayer) -> None: @@ -404,44 +191,6 @@ async def test_streaming_response_list(self, async_client: AsyncUnlayer) -> None assert response.http_request.headers.get("X-Stainless-Lang") == "python" template = await response.parse() - assert_matches_type(TemplateListResponse, template, path=["response"]) + assert_matches_type(AsyncCursorPage[TemplateListResponse], template, path=["response"]) assert cast(Any, response.is_closed) is True - - @parametrize - async def test_method_delete(self, async_client: AsyncUnlayer) -> None: - template = await async_client.project.templates.delete( - "id", - ) - assert template is None - - @parametrize - async def test_raw_response_delete(self, async_client: AsyncUnlayer) -> None: - response = await async_client.project.templates.with_raw_response.delete( - "id", - ) - - assert response.is_closed is True - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - template = await response.parse() - assert template is None - - @parametrize - async def test_streaming_response_delete(self, async_client: AsyncUnlayer) -> None: - async with async_client.project.templates.with_streaming_response.delete( - "id", - ) as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - - template = await response.parse() - assert template is None - - assert cast(Any, response.is_closed) is True - - @parametrize - async def test_path_params_delete(self, async_client: AsyncUnlayer) -> None: - with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): - await async_client.project.templates.with_raw_response.delete( - "", - ) diff --git a/tests/api_resources/test_documents.py b/tests/api_resources/test_documents.py deleted file mode 100644 index f8ae227..0000000 --- a/tests/api_resources/test_documents.py +++ /dev/null @@ -1,108 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -import os -from typing import Any, cast - -import pytest - -from unlayer import Unlayer, AsyncUnlayer -from tests.utils import assert_matches_type -from unlayer.types import DocumentRetrieveResponse - -base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") - - -class TestDocuments: - parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) - - @parametrize - def test_method_retrieve(self, client: Unlayer) -> None: - document = client.documents.retrieve( - id="id", - project_id="projectId", - ) - assert_matches_type(DocumentRetrieveResponse, document, path=["response"]) - - @parametrize - def test_raw_response_retrieve(self, client: Unlayer) -> None: - response = client.documents.with_raw_response.retrieve( - id="id", - project_id="projectId", - ) - - assert response.is_closed is True - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - document = response.parse() - assert_matches_type(DocumentRetrieveResponse, document, path=["response"]) - - @parametrize - def test_streaming_response_retrieve(self, client: Unlayer) -> None: - with client.documents.with_streaming_response.retrieve( - id="id", - project_id="projectId", - ) as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - - document = response.parse() - assert_matches_type(DocumentRetrieveResponse, document, path=["response"]) - - assert cast(Any, response.is_closed) is True - - @parametrize - def test_path_params_retrieve(self, client: Unlayer) -> None: - with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): - client.documents.with_raw_response.retrieve( - id="", - project_id="projectId", - ) - - -class TestAsyncDocuments: - parametrize = pytest.mark.parametrize( - "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] - ) - - @parametrize - async def test_method_retrieve(self, async_client: AsyncUnlayer) -> None: - document = await async_client.documents.retrieve( - id="id", - project_id="projectId", - ) - assert_matches_type(DocumentRetrieveResponse, document, path=["response"]) - - @parametrize - async def test_raw_response_retrieve(self, async_client: AsyncUnlayer) -> None: - response = await async_client.documents.with_raw_response.retrieve( - id="id", - project_id="projectId", - ) - - assert response.is_closed is True - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - document = await response.parse() - assert_matches_type(DocumentRetrieveResponse, document, path=["response"]) - - @parametrize - async def test_streaming_response_retrieve(self, async_client: AsyncUnlayer) -> None: - async with async_client.documents.with_streaming_response.retrieve( - id="id", - project_id="projectId", - ) as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - - document = await response.parse() - assert_matches_type(DocumentRetrieveResponse, document, path=["response"]) - - assert cast(Any, response.is_closed) is True - - @parametrize - async def test_path_params_retrieve(self, async_client: AsyncUnlayer) -> None: - with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): - await async_client.documents.with_raw_response.retrieve( - id="", - project_id="projectId", - ) diff --git a/tests/api_resources/test_emails.py b/tests/api_resources/test_emails.py deleted file mode 100644 index a34022a..0000000 --- a/tests/api_resources/test_emails.py +++ /dev/null @@ -1,108 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -import os -from typing import Any, cast - -import pytest - -from unlayer import Unlayer, AsyncUnlayer -from tests.utils import assert_matches_type -from unlayer.types import EmailRetrieveResponse - -base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") - - -class TestEmails: - parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) - - @parametrize - def test_method_retrieve(self, client: Unlayer) -> None: - email = client.emails.retrieve( - id="id", - project_id="projectId", - ) - assert_matches_type(EmailRetrieveResponse, email, path=["response"]) - - @parametrize - def test_raw_response_retrieve(self, client: Unlayer) -> None: - response = client.emails.with_raw_response.retrieve( - id="id", - project_id="projectId", - ) - - assert response.is_closed is True - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - email = response.parse() - assert_matches_type(EmailRetrieveResponse, email, path=["response"]) - - @parametrize - def test_streaming_response_retrieve(self, client: Unlayer) -> None: - with client.emails.with_streaming_response.retrieve( - id="id", - project_id="projectId", - ) as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - - email = response.parse() - assert_matches_type(EmailRetrieveResponse, email, path=["response"]) - - assert cast(Any, response.is_closed) is True - - @parametrize - def test_path_params_retrieve(self, client: Unlayer) -> None: - with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): - client.emails.with_raw_response.retrieve( - id="", - project_id="projectId", - ) - - -class TestAsyncEmails: - parametrize = pytest.mark.parametrize( - "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] - ) - - @parametrize - async def test_method_retrieve(self, async_client: AsyncUnlayer) -> None: - email = await async_client.emails.retrieve( - id="id", - project_id="projectId", - ) - assert_matches_type(EmailRetrieveResponse, email, path=["response"]) - - @parametrize - async def test_raw_response_retrieve(self, async_client: AsyncUnlayer) -> None: - response = await async_client.emails.with_raw_response.retrieve( - id="id", - project_id="projectId", - ) - - assert response.is_closed is True - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - email = await response.parse() - assert_matches_type(EmailRetrieveResponse, email, path=["response"]) - - @parametrize - async def test_streaming_response_retrieve(self, async_client: AsyncUnlayer) -> None: - async with async_client.emails.with_streaming_response.retrieve( - id="id", - project_id="projectId", - ) as response: - assert not response.is_closed - assert response.http_request.headers.get("X-Stainless-Lang") == "python" - - email = await response.parse() - assert_matches_type(EmailRetrieveResponse, email, path=["response"]) - - assert cast(Any, response.is_closed) is True - - @parametrize - async def test_path_params_retrieve(self, async_client: AsyncUnlayer) -> None: - with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): - await async_client.emails.with_raw_response.retrieve( - id="", - project_id="projectId", - ) diff --git a/tests/api_resources/project/test_current.py b/tests/api_resources/test_project.py similarity index 67% rename from tests/api_resources/project/test_current.py rename to tests/api_resources/test_project.py index 7fd4430..5719de6 100644 --- a/tests/api_resources/project/test_current.py +++ b/tests/api_resources/test_project.py @@ -9,78 +9,78 @@ from unlayer import Unlayer, AsyncUnlayer from tests.utils import assert_matches_type -from unlayer.types.project import CurrentRetrieveResponse +from unlayer.types import ProjectRetrieveResponse base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") -class TestCurrent: +class TestProject: parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) @parametrize def test_method_retrieve(self, client: Unlayer) -> None: - current = client.project.current.retrieve( + project = client.project.retrieve( project_id="projectId", ) - assert_matches_type(CurrentRetrieveResponse, current, path=["response"]) + assert_matches_type(ProjectRetrieveResponse, project, path=["response"]) @parametrize def test_raw_response_retrieve(self, client: Unlayer) -> None: - response = client.project.current.with_raw_response.retrieve( + response = client.project.with_raw_response.retrieve( project_id="projectId", ) assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" - current = response.parse() - assert_matches_type(CurrentRetrieveResponse, current, path=["response"]) + project = response.parse() + assert_matches_type(ProjectRetrieveResponse, project, path=["response"]) @parametrize def test_streaming_response_retrieve(self, client: Unlayer) -> None: - with client.project.current.with_streaming_response.retrieve( + with client.project.with_streaming_response.retrieve( project_id="projectId", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" - current = response.parse() - assert_matches_type(CurrentRetrieveResponse, current, path=["response"]) + project = response.parse() + assert_matches_type(ProjectRetrieveResponse, project, path=["response"]) assert cast(Any, response.is_closed) is True -class TestAsyncCurrent: +class TestAsyncProject: parametrize = pytest.mark.parametrize( "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] ) @parametrize async def test_method_retrieve(self, async_client: AsyncUnlayer) -> None: - current = await async_client.project.current.retrieve( + project = await async_client.project.retrieve( project_id="projectId", ) - assert_matches_type(CurrentRetrieveResponse, current, path=["response"]) + assert_matches_type(ProjectRetrieveResponse, project, path=["response"]) @parametrize async def test_raw_response_retrieve(self, async_client: AsyncUnlayer) -> None: - response = await async_client.project.current.with_raw_response.retrieve( + response = await async_client.project.with_raw_response.retrieve( project_id="projectId", ) assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" - current = await response.parse() - assert_matches_type(CurrentRetrieveResponse, current, path=["response"]) + project = await response.parse() + assert_matches_type(ProjectRetrieveResponse, project, path=["response"]) @parametrize async def test_streaming_response_retrieve(self, async_client: AsyncUnlayer) -> None: - async with async_client.project.current.with_streaming_response.retrieve( + async with async_client.project.with_streaming_response.retrieve( project_id="projectId", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" - current = await response.parse() - assert_matches_type(CurrentRetrieveResponse, current, path=["response"]) + project = await response.parse() + assert_matches_type(ProjectRetrieveResponse, project, path=["response"]) assert cast(Any, response.is_closed) is True diff --git a/tests/api_resources/project/test_workspaces.py b/tests/api_resources/test_workspaces.py similarity index 81% rename from tests/api_resources/project/test_workspaces.py rename to tests/api_resources/test_workspaces.py index 05b504b..b97c19d 100644 --- a/tests/api_resources/project/test_workspaces.py +++ b/tests/api_resources/test_workspaces.py @@ -9,7 +9,7 @@ from unlayer import Unlayer, AsyncUnlayer from tests.utils import assert_matches_type -from unlayer.types.project import WorkspaceListResponse, WorkspaceRetrieveResponse +from unlayer.types import WorkspaceListResponse, WorkspaceRetrieveResponse base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") @@ -19,14 +19,14 @@ class TestWorkspaces: @parametrize def test_method_retrieve(self, client: Unlayer) -> None: - workspace = client.project.workspaces.retrieve( + workspace = client.workspaces.retrieve( "workspaceId", ) assert_matches_type(WorkspaceRetrieveResponse, workspace, path=["response"]) @parametrize def test_raw_response_retrieve(self, client: Unlayer) -> None: - response = client.project.workspaces.with_raw_response.retrieve( + response = client.workspaces.with_raw_response.retrieve( "workspaceId", ) @@ -37,7 +37,7 @@ def test_raw_response_retrieve(self, client: Unlayer) -> None: @parametrize def test_streaming_response_retrieve(self, client: Unlayer) -> None: - with client.project.workspaces.with_streaming_response.retrieve( + with client.workspaces.with_streaming_response.retrieve( "workspaceId", ) as response: assert not response.is_closed @@ -51,18 +51,18 @@ def test_streaming_response_retrieve(self, client: Unlayer) -> None: @parametrize def test_path_params_retrieve(self, client: Unlayer) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `workspace_id` but received ''"): - client.project.workspaces.with_raw_response.retrieve( + client.workspaces.with_raw_response.retrieve( "", ) @parametrize def test_method_list(self, client: Unlayer) -> None: - workspace = client.project.workspaces.list() + workspace = client.workspaces.list() assert_matches_type(WorkspaceListResponse, workspace, path=["response"]) @parametrize def test_raw_response_list(self, client: Unlayer) -> None: - response = client.project.workspaces.with_raw_response.list() + response = client.workspaces.with_raw_response.list() assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" @@ -71,7 +71,7 @@ def test_raw_response_list(self, client: Unlayer) -> None: @parametrize def test_streaming_response_list(self, client: Unlayer) -> None: - with client.project.workspaces.with_streaming_response.list() as response: + with client.workspaces.with_streaming_response.list() as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" @@ -88,14 +88,14 @@ class TestAsyncWorkspaces: @parametrize async def test_method_retrieve(self, async_client: AsyncUnlayer) -> None: - workspace = await async_client.project.workspaces.retrieve( + workspace = await async_client.workspaces.retrieve( "workspaceId", ) assert_matches_type(WorkspaceRetrieveResponse, workspace, path=["response"]) @parametrize async def test_raw_response_retrieve(self, async_client: AsyncUnlayer) -> None: - response = await async_client.project.workspaces.with_raw_response.retrieve( + response = await async_client.workspaces.with_raw_response.retrieve( "workspaceId", ) @@ -106,7 +106,7 @@ async def test_raw_response_retrieve(self, async_client: AsyncUnlayer) -> None: @parametrize async def test_streaming_response_retrieve(self, async_client: AsyncUnlayer) -> None: - async with async_client.project.workspaces.with_streaming_response.retrieve( + async with async_client.workspaces.with_streaming_response.retrieve( "workspaceId", ) as response: assert not response.is_closed @@ -120,18 +120,18 @@ async def test_streaming_response_retrieve(self, async_client: AsyncUnlayer) -> @parametrize async def test_path_params_retrieve(self, async_client: AsyncUnlayer) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `workspace_id` but received ''"): - await async_client.project.workspaces.with_raw_response.retrieve( + await async_client.workspaces.with_raw_response.retrieve( "", ) @parametrize async def test_method_list(self, async_client: AsyncUnlayer) -> None: - workspace = await async_client.project.workspaces.list() + workspace = await async_client.workspaces.list() assert_matches_type(WorkspaceListResponse, workspace, path=["response"]) @parametrize async def test_raw_response_list(self, async_client: AsyncUnlayer) -> None: - response = await async_client.project.workspaces.with_raw_response.list() + response = await async_client.workspaces.with_raw_response.list() assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" @@ -140,7 +140,7 @@ async def test_raw_response_list(self, async_client: AsyncUnlayer) -> None: @parametrize async def test_streaming_response_list(self, async_client: AsyncUnlayer) -> None: - async with async_client.project.workspaces.with_streaming_response.list() as response: + async with async_client.workspaces.with_streaming_response.list() as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" diff --git a/tests/test_client.py b/tests/test_client.py index 10efb22..97ed357 100644 --- a/tests/test_client.py +++ b/tests/test_client.py @@ -891,20 +891,20 @@ def test_parse_retry_after_header( @mock.patch("unlayer._base_client.BaseClient._calculate_retry_timeout", _low_retry_timeout) @pytest.mark.respx(base_url=base_url) def test_retrying_timeout_errors_doesnt_leak(self, respx_mock: MockRouter, client: Unlayer) -> None: - respx_mock.post("/convert/full-to-simple").mock(side_effect=httpx.TimeoutException("Test timeout error")) + respx_mock.get("/v3/project").mock(side_effect=httpx.TimeoutException("Test timeout error")) with pytest.raises(APITimeoutError): - client.convert.full_to_simple.with_streaming_response.create(design={"body": {}}).__enter__() + client.project.with_streaming_response.retrieve(project_id="projectId").__enter__() assert _get_open_connections(client) == 0 @mock.patch("unlayer._base_client.BaseClient._calculate_retry_timeout", _low_retry_timeout) @pytest.mark.respx(base_url=base_url) def test_retrying_status_errors_doesnt_leak(self, respx_mock: MockRouter, client: Unlayer) -> None: - respx_mock.post("/convert/full-to-simple").mock(return_value=httpx.Response(500)) + respx_mock.get("/v3/project").mock(return_value=httpx.Response(500)) with pytest.raises(APIStatusError): - client.convert.full_to_simple.with_streaming_response.create(design={"body": {}}).__enter__() + client.project.with_streaming_response.retrieve(project_id="projectId").__enter__() assert _get_open_connections(client) == 0 @pytest.mark.parametrize("failures_before_success", [0, 2, 4]) @@ -931,9 +931,9 @@ def retry_handler(_request: httpx.Request) -> httpx.Response: return httpx.Response(500) return httpx.Response(200) - respx_mock.post("/convert/full-to-simple").mock(side_effect=retry_handler) + respx_mock.get("/v3/project").mock(side_effect=retry_handler) - response = client.convert.full_to_simple.with_raw_response.create(design={"body": {}}) + response = client.project.with_raw_response.retrieve(project_id="projectId") assert response.retries_taken == failures_before_success assert int(response.http_request.headers.get("x-stainless-retry-count")) == failures_before_success @@ -955,10 +955,10 @@ def retry_handler(_request: httpx.Request) -> httpx.Response: return httpx.Response(500) return httpx.Response(200) - respx_mock.post("/convert/full-to-simple").mock(side_effect=retry_handler) + respx_mock.get("/v3/project").mock(side_effect=retry_handler) - response = client.convert.full_to_simple.with_raw_response.create( - design={"body": {}}, extra_headers={"x-stainless-retry-count": Omit()} + response = client.project.with_raw_response.retrieve( + project_id="projectId", extra_headers={"x-stainless-retry-count": Omit()} ) assert len(response.http_request.headers.get_list("x-stainless-retry-count")) == 0 @@ -980,10 +980,10 @@ def retry_handler(_request: httpx.Request) -> httpx.Response: return httpx.Response(500) return httpx.Response(200) - respx_mock.post("/convert/full-to-simple").mock(side_effect=retry_handler) + respx_mock.get("/v3/project").mock(side_effect=retry_handler) - response = client.convert.full_to_simple.with_raw_response.create( - design={"body": {}}, extra_headers={"x-stainless-retry-count": "42"} + response = client.project.with_raw_response.retrieve( + project_id="projectId", extra_headers={"x-stainless-retry-count": "42"} ) assert response.http_request.headers.get("x-stainless-retry-count") == "42" @@ -1827,20 +1827,20 @@ async def test_parse_retry_after_header( async def test_retrying_timeout_errors_doesnt_leak( self, respx_mock: MockRouter, async_client: AsyncUnlayer ) -> None: - respx_mock.post("/convert/full-to-simple").mock(side_effect=httpx.TimeoutException("Test timeout error")) + respx_mock.get("/v3/project").mock(side_effect=httpx.TimeoutException("Test timeout error")) with pytest.raises(APITimeoutError): - await async_client.convert.full_to_simple.with_streaming_response.create(design={"body": {}}).__aenter__() + await async_client.project.with_streaming_response.retrieve(project_id="projectId").__aenter__() assert _get_open_connections(async_client) == 0 @mock.patch("unlayer._base_client.BaseClient._calculate_retry_timeout", _low_retry_timeout) @pytest.mark.respx(base_url=base_url) async def test_retrying_status_errors_doesnt_leak(self, respx_mock: MockRouter, async_client: AsyncUnlayer) -> None: - respx_mock.post("/convert/full-to-simple").mock(return_value=httpx.Response(500)) + respx_mock.get("/v3/project").mock(return_value=httpx.Response(500)) with pytest.raises(APIStatusError): - await async_client.convert.full_to_simple.with_streaming_response.create(design={"body": {}}).__aenter__() + await async_client.project.with_streaming_response.retrieve(project_id="projectId").__aenter__() assert _get_open_connections(async_client) == 0 @pytest.mark.parametrize("failures_before_success", [0, 2, 4]) @@ -1867,9 +1867,9 @@ def retry_handler(_request: httpx.Request) -> httpx.Response: return httpx.Response(500) return httpx.Response(200) - respx_mock.post("/convert/full-to-simple").mock(side_effect=retry_handler) + respx_mock.get("/v3/project").mock(side_effect=retry_handler) - response = await client.convert.full_to_simple.with_raw_response.create(design={"body": {}}) + response = await client.project.with_raw_response.retrieve(project_id="projectId") assert response.retries_taken == failures_before_success assert int(response.http_request.headers.get("x-stainless-retry-count")) == failures_before_success @@ -1891,10 +1891,10 @@ def retry_handler(_request: httpx.Request) -> httpx.Response: return httpx.Response(500) return httpx.Response(200) - respx_mock.post("/convert/full-to-simple").mock(side_effect=retry_handler) + respx_mock.get("/v3/project").mock(side_effect=retry_handler) - response = await client.convert.full_to_simple.with_raw_response.create( - design={"body": {}}, extra_headers={"x-stainless-retry-count": Omit()} + response = await client.project.with_raw_response.retrieve( + project_id="projectId", extra_headers={"x-stainless-retry-count": Omit()} ) assert len(response.http_request.headers.get_list("x-stainless-retry-count")) == 0 @@ -1916,10 +1916,10 @@ def retry_handler(_request: httpx.Request) -> httpx.Response: return httpx.Response(500) return httpx.Response(200) - respx_mock.post("/convert/full-to-simple").mock(side_effect=retry_handler) + respx_mock.get("/v3/project").mock(side_effect=retry_handler) - response = await client.convert.full_to_simple.with_raw_response.create( - design={"body": {}}, extra_headers={"x-stainless-retry-count": "42"} + response = await client.project.with_raw_response.retrieve( + project_id="projectId", extra_headers={"x-stainless-retry-count": "42"} ) assert response.http_request.headers.get("x-stainless-retry-count") == "42" From 72c5f5c4cb7002f3179191a61f3b4d6872ce5dd3 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Mon, 16 Feb 2026 14:40:08 +0000 Subject: [PATCH 31/62] feat(api): api update --- .stats.yml | 6 +-- README.md | 8 ++-- api.md | 10 ++-- src/unlayer/_client.py | 41 +++++++++++++++- src/unlayer/resources/__init__.py | 14 ++++++ .../resources/{project => }/project.py | 48 ++++--------------- src/unlayer/resources/project/__init__.py | 33 ------------- .../resources/{project => }/templates.py | 44 ++++++++--------- src/unlayer/types/__init__.py | 4 ++ src/unlayer/types/project/__init__.py | 8 ---- .../{project => }/template_list_params.py | 2 +- .../{project => }/template_list_response.py | 2 +- .../{project => }/template_retrieve_params.py | 2 +- .../template_retrieve_response.py | 2 +- tests/api_resources/project/__init__.py | 1 - .../{project => }/test_templates.py | 37 +++++++------- 16 files changed, 120 insertions(+), 142 deletions(-) rename src/unlayer/resources/{project => }/project.py (78%) delete mode 100644 src/unlayer/resources/project/__init__.py rename src/unlayer/resources/{project => }/templates.py (90%) delete mode 100644 src/unlayer/types/project/__init__.py rename src/unlayer/types/{project => }/template_list_params.py (95%) rename src/unlayer/types/{project => }/template_list_response.py (95%) rename src/unlayer/types/{project => }/template_retrieve_params.py (91%) rename src/unlayer/types/{project => }/template_retrieve_response.py (96%) delete mode 100644 tests/api_resources/project/__init__.py rename tests/api_resources/{project => }/test_templates.py (84%) diff --git a/.stats.yml b/.stats.yml index 43eb3f0..c827f62 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 7 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/unlayer%2Funlayer-579dff50df9d2d3be275dc58817917d2efec68100883d139ae7d62908a24e5d6.yml -openapi_spec_hash: 8583074e5ea7cc31410a42c2c4550d7c -config_hash: 3c023f8805c0765c987ddcee566aabef +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/unlayer%2Funlayer-e77bc881d5cb6c68be6f3d3861ed021b99f6cde45ee28d3511abe284d888262e.yml +openapi_spec_hash: 7e7fae1b919c5d337e8b22be2a24d3ed +config_hash: ea554d62bfbb5e8ea43ebf0a274dec31 diff --git a/README.md b/README.md index ef1d20e..8e7620c 100644 --- a/README.md +++ b/README.md @@ -134,7 +134,7 @@ client = Unlayer() all_templates = [] # Automatically fetches more pages as needed. -for template in client.project.templates.list( +for template in client.templates.list( project_id="your-project-id", limit=10, ): @@ -155,7 +155,7 @@ client = AsyncUnlayer() async def main() -> None: all_templates = [] # Iterate through items across all pages, issuing requests as needed. - async for template in client.project.templates.list( + async for template in client.templates.list( project_id="your-project-id", limit=10, ): @@ -169,7 +169,7 @@ asyncio.run(main()) Alternatively, you can use the `.has_next_page()`, `.next_page_info()`, or `.get_next_page()` methods for more granular control working with pages: ```python -first_page = await client.project.templates.list( +first_page = await client.templates.list( project_id="your-project-id", limit=10, ) @@ -184,7 +184,7 @@ if first_page.has_next_page(): Or just work directly with the returned data: ```python -first_page = await client.project.templates.list( +first_page = await client.templates.list( project_id="your-project-id", limit=10, ) diff --git a/api.md b/api.md index 3e85079..54f0d00 100644 --- a/api.md +++ b/api.md @@ -34,20 +34,20 @@ from unlayer.types import ProjectRetrieveResponse Methods: -- client.project.retrieve(\*\*params) -> ProjectRetrieveResponse +- client.project.retrieve(\*\*params) -> ProjectRetrieveResponse -## Templates +# Templates Types: ```python -from unlayer.types.project import TemplateRetrieveResponse, TemplateListResponse +from unlayer.types import TemplateRetrieveResponse, TemplateListResponse ``` Methods: -- client.project.templates.retrieve(id, \*\*params) -> TemplateRetrieveResponse -- client.project.templates.list(\*\*params) -> SyncCursorPage[TemplateListResponse] +- client.templates.retrieve(id, \*\*params) -> TemplateRetrieveResponse +- client.templates.list(\*\*params) -> SyncCursorPage[TemplateListResponse] # Workspaces diff --git a/src/unlayer/_client.py b/src/unlayer/_client.py index e702e10..01b60d9 100644 --- a/src/unlayer/_client.py +++ b/src/unlayer/_client.py @@ -31,10 +31,11 @@ ) if TYPE_CHECKING: - from .resources import convert, project, workspaces + from .resources import convert, project, templates, workspaces + from .resources.project import ProjectResource, AsyncProjectResource + from .resources.templates import TemplatesResource, AsyncTemplatesResource from .resources.workspaces import WorkspacesResource, AsyncWorkspacesResource from .resources.convert.convert import ConvertResource, AsyncConvertResource - from .resources.project.project import ProjectResource, AsyncProjectResource __all__ = [ "ENVIRONMENTS", @@ -147,6 +148,12 @@ def project(self) -> ProjectResource: return ProjectResource(self) + @cached_property + def templates(self) -> TemplatesResource: + from .resources.templates import TemplatesResource + + return TemplatesResource(self) + @cached_property def workspaces(self) -> WorkspacesResource: from .resources.workspaces import WorkspacesResource @@ -359,6 +366,12 @@ def project(self) -> AsyncProjectResource: return AsyncProjectResource(self) + @cached_property + def templates(self) -> AsyncTemplatesResource: + from .resources.templates import AsyncTemplatesResource + + return AsyncTemplatesResource(self) + @cached_property def workspaces(self) -> AsyncWorkspacesResource: from .resources.workspaces import AsyncWorkspacesResource @@ -498,6 +511,12 @@ def project(self) -> project.ProjectResourceWithRawResponse: return ProjectResourceWithRawResponse(self._client.project) + @cached_property + def templates(self) -> templates.TemplatesResourceWithRawResponse: + from .resources.templates import TemplatesResourceWithRawResponse + + return TemplatesResourceWithRawResponse(self._client.templates) + @cached_property def workspaces(self) -> workspaces.WorkspacesResourceWithRawResponse: from .resources.workspaces import WorkspacesResourceWithRawResponse @@ -523,6 +542,12 @@ def project(self) -> project.AsyncProjectResourceWithRawResponse: return AsyncProjectResourceWithRawResponse(self._client.project) + @cached_property + def templates(self) -> templates.AsyncTemplatesResourceWithRawResponse: + from .resources.templates import AsyncTemplatesResourceWithRawResponse + + return AsyncTemplatesResourceWithRawResponse(self._client.templates) + @cached_property def workspaces(self) -> workspaces.AsyncWorkspacesResourceWithRawResponse: from .resources.workspaces import AsyncWorkspacesResourceWithRawResponse @@ -548,6 +573,12 @@ def project(self) -> project.ProjectResourceWithStreamingResponse: return ProjectResourceWithStreamingResponse(self._client.project) + @cached_property + def templates(self) -> templates.TemplatesResourceWithStreamingResponse: + from .resources.templates import TemplatesResourceWithStreamingResponse + + return TemplatesResourceWithStreamingResponse(self._client.templates) + @cached_property def workspaces(self) -> workspaces.WorkspacesResourceWithStreamingResponse: from .resources.workspaces import WorkspacesResourceWithStreamingResponse @@ -573,6 +604,12 @@ def project(self) -> project.AsyncProjectResourceWithStreamingResponse: return AsyncProjectResourceWithStreamingResponse(self._client.project) + @cached_property + def templates(self) -> templates.AsyncTemplatesResourceWithStreamingResponse: + from .resources.templates import AsyncTemplatesResourceWithStreamingResponse + + return AsyncTemplatesResourceWithStreamingResponse(self._client.templates) + @cached_property def workspaces(self) -> workspaces.AsyncWorkspacesResourceWithStreamingResponse: from .resources.workspaces import AsyncWorkspacesResourceWithStreamingResponse diff --git a/src/unlayer/resources/__init__.py b/src/unlayer/resources/__init__.py index 8efafd2..347e4de 100644 --- a/src/unlayer/resources/__init__.py +++ b/src/unlayer/resources/__init__.py @@ -16,6 +16,14 @@ ProjectResourceWithStreamingResponse, AsyncProjectResourceWithStreamingResponse, ) +from .templates import ( + TemplatesResource, + AsyncTemplatesResource, + TemplatesResourceWithRawResponse, + AsyncTemplatesResourceWithRawResponse, + TemplatesResourceWithStreamingResponse, + AsyncTemplatesResourceWithStreamingResponse, +) from .workspaces import ( WorkspacesResource, AsyncWorkspacesResource, @@ -38,6 +46,12 @@ "AsyncProjectResourceWithRawResponse", "ProjectResourceWithStreamingResponse", "AsyncProjectResourceWithStreamingResponse", + "TemplatesResource", + "AsyncTemplatesResource", + "TemplatesResourceWithRawResponse", + "AsyncTemplatesResourceWithRawResponse", + "TemplatesResourceWithStreamingResponse", + "AsyncTemplatesResourceWithStreamingResponse", "WorkspacesResource", "AsyncWorkspacesResource", "WorkspacesResourceWithRawResponse", diff --git a/src/unlayer/resources/project/project.py b/src/unlayer/resources/project.py similarity index 78% rename from src/unlayer/resources/project/project.py rename to src/unlayer/resources/project.py index 8bd4376..6ff7918 100644 --- a/src/unlayer/resources/project/project.py +++ b/src/unlayer/resources/project.py @@ -4,36 +4,24 @@ import httpx -from ...types import project_retrieve_params -from ..._types import Body, Query, Headers, NotGiven, not_given -from ..._utils import maybe_transform, async_maybe_transform -from ..._compat import cached_property -from .templates import ( - TemplatesResource, - AsyncTemplatesResource, - TemplatesResourceWithRawResponse, - AsyncTemplatesResourceWithRawResponse, - TemplatesResourceWithStreamingResponse, - AsyncTemplatesResourceWithStreamingResponse, -) -from ..._resource import SyncAPIResource, AsyncAPIResource -from ..._response import ( +from ..types import project_retrieve_params +from .._types import Body, Query, Headers, NotGiven, not_given +from .._utils import maybe_transform, async_maybe_transform +from .._compat import cached_property +from .._resource import SyncAPIResource, AsyncAPIResource +from .._response import ( to_raw_response_wrapper, to_streamed_response_wrapper, async_to_raw_response_wrapper, async_to_streamed_response_wrapper, ) -from ..._base_client import make_request_options -from ...types.project_retrieve_response import ProjectRetrieveResponse +from .._base_client import make_request_options +from ..types.project_retrieve_response import ProjectRetrieveResponse __all__ = ["ProjectResource", "AsyncProjectResource"] class ProjectResource(SyncAPIResource): - @cached_property - def templates(self) -> TemplatesResource: - return TemplatesResource(self._client) - @cached_property def with_raw_response(self) -> ProjectResourceWithRawResponse: """ @@ -92,10 +80,6 @@ def retrieve( class AsyncProjectResource(AsyncAPIResource): - @cached_property - def templates(self) -> AsyncTemplatesResource: - return AsyncTemplatesResource(self._client) - @cached_property def with_raw_response(self) -> AsyncProjectResourceWithRawResponse: """ @@ -163,10 +147,6 @@ def __init__(self, project: ProjectResource) -> None: project.retrieve, ) - @cached_property - def templates(self) -> TemplatesResourceWithRawResponse: - return TemplatesResourceWithRawResponse(self._project.templates) - class AsyncProjectResourceWithRawResponse: def __init__(self, project: AsyncProjectResource) -> None: @@ -176,10 +156,6 @@ def __init__(self, project: AsyncProjectResource) -> None: project.retrieve, ) - @cached_property - def templates(self) -> AsyncTemplatesResourceWithRawResponse: - return AsyncTemplatesResourceWithRawResponse(self._project.templates) - class ProjectResourceWithStreamingResponse: def __init__(self, project: ProjectResource) -> None: @@ -189,10 +165,6 @@ def __init__(self, project: ProjectResource) -> None: project.retrieve, ) - @cached_property - def templates(self) -> TemplatesResourceWithStreamingResponse: - return TemplatesResourceWithStreamingResponse(self._project.templates) - class AsyncProjectResourceWithStreamingResponse: def __init__(self, project: AsyncProjectResource) -> None: @@ -201,7 +173,3 @@ def __init__(self, project: AsyncProjectResource) -> None: self.retrieve = async_to_streamed_response_wrapper( project.retrieve, ) - - @cached_property - def templates(self) -> AsyncTemplatesResourceWithStreamingResponse: - return AsyncTemplatesResourceWithStreamingResponse(self._project.templates) diff --git a/src/unlayer/resources/project/__init__.py b/src/unlayer/resources/project/__init__.py deleted file mode 100644 index 1df5aef..0000000 --- a/src/unlayer/resources/project/__init__.py +++ /dev/null @@ -1,33 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from .project import ( - ProjectResource, - AsyncProjectResource, - ProjectResourceWithRawResponse, - AsyncProjectResourceWithRawResponse, - ProjectResourceWithStreamingResponse, - AsyncProjectResourceWithStreamingResponse, -) -from .templates import ( - TemplatesResource, - AsyncTemplatesResource, - TemplatesResourceWithRawResponse, - AsyncTemplatesResourceWithRawResponse, - TemplatesResourceWithStreamingResponse, - AsyncTemplatesResourceWithStreamingResponse, -) - -__all__ = [ - "TemplatesResource", - "AsyncTemplatesResource", - "TemplatesResourceWithRawResponse", - "AsyncTemplatesResourceWithRawResponse", - "TemplatesResourceWithStreamingResponse", - "AsyncTemplatesResourceWithStreamingResponse", - "ProjectResource", - "AsyncProjectResource", - "ProjectResourceWithRawResponse", - "AsyncProjectResourceWithRawResponse", - "ProjectResourceWithStreamingResponse", - "AsyncProjectResourceWithStreamingResponse", -] diff --git a/src/unlayer/resources/project/templates.py b/src/unlayer/resources/templates.py similarity index 90% rename from src/unlayer/resources/project/templates.py rename to src/unlayer/resources/templates.py index e7600ac..4c0708a 100644 --- a/src/unlayer/resources/project/templates.py +++ b/src/unlayer/resources/templates.py @@ -6,21 +6,21 @@ import httpx -from ..._types import Body, Omit, Query, Headers, NotGiven, omit, not_given -from ..._utils import maybe_transform, async_maybe_transform -from ..._compat import cached_property -from ..._resource import SyncAPIResource, AsyncAPIResource -from ..._response import ( +from ..types import template_list_params, template_retrieve_params +from .._types import Body, Omit, Query, Headers, NotGiven, omit, not_given +from .._utils import maybe_transform, async_maybe_transform +from .._compat import cached_property +from .._resource import SyncAPIResource, AsyncAPIResource +from .._response import ( to_raw_response_wrapper, to_streamed_response_wrapper, async_to_raw_response_wrapper, async_to_streamed_response_wrapper, ) -from ...pagination import SyncCursorPage, AsyncCursorPage -from ..._base_client import AsyncPaginator, make_request_options -from ...types.project import template_list_params, template_retrieve_params -from ...types.project.template_list_response import TemplateListResponse -from ...types.project.template_retrieve_response import TemplateRetrieveResponse +from ..pagination import SyncCursorPage, AsyncCursorPage +from .._base_client import AsyncPaginator, make_request_options +from ..types.template_list_response import TemplateListResponse +from ..types.template_retrieve_response import TemplateRetrieveResponse __all__ = ["TemplatesResource", "AsyncTemplatesResource"] @@ -58,7 +58,7 @@ def retrieve( timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> TemplateRetrieveResponse: """ - Get project template by ID. + Get template by ID. Args: project_id: The project ID @@ -74,7 +74,7 @@ def retrieve( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return self._get( - f"/v3/project/templates/{id}", + f"/v3/templates/{id}", options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, @@ -100,10 +100,10 @@ def list( extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> SyncCursorPage[TemplateListResponse]: - """List project templates with cursor-based pagination. + """List templates with cursor-based pagination. - Returns templates in - descending order by update time. + Returns templates in descending + order by update time. Args: project_id: The project ID to list templates for @@ -125,7 +125,7 @@ def list( timeout: Override the client-level default timeout for this request, in seconds """ return self._get_api_list( - "/v3/project/templates", + "/v3/templates", page=SyncCursorPage[TemplateListResponse], options=make_request_options( extra_headers=extra_headers, @@ -180,7 +180,7 @@ async def retrieve( timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> TemplateRetrieveResponse: """ - Get project template by ID. + Get template by ID. Args: project_id: The project ID @@ -196,7 +196,7 @@ async def retrieve( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return await self._get( - f"/v3/project/templates/{id}", + f"/v3/templates/{id}", options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, @@ -224,10 +224,10 @@ def list( extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> AsyncPaginator[TemplateListResponse, AsyncCursorPage[TemplateListResponse]]: - """List project templates with cursor-based pagination. + """List templates with cursor-based pagination. - Returns templates in - descending order by update time. + Returns templates in descending + order by update time. Args: project_id: The project ID to list templates for @@ -249,7 +249,7 @@ def list( timeout: Override the client-level default timeout for this request, in seconds """ return self._get_api_list( - "/v3/project/templates", + "/v3/templates", page=AsyncCursorPage[TemplateListResponse], options=make_request_options( extra_headers=extra_headers, diff --git a/src/unlayer/types/__init__.py b/src/unlayer/types/__init__.py index 12ff9bb..33221d8 100644 --- a/src/unlayer/types/__init__.py +++ b/src/unlayer/types/__init__.py @@ -2,7 +2,11 @@ from __future__ import annotations +from .template_list_params import TemplateListParams as TemplateListParams +from .template_list_response import TemplateListResponse as TemplateListResponse from .project_retrieve_params import ProjectRetrieveParams as ProjectRetrieveParams from .workspace_list_response import WorkspaceListResponse as WorkspaceListResponse +from .template_retrieve_params import TemplateRetrieveParams as TemplateRetrieveParams from .project_retrieve_response import ProjectRetrieveResponse as ProjectRetrieveResponse +from .template_retrieve_response import TemplateRetrieveResponse as TemplateRetrieveResponse from .workspace_retrieve_response import WorkspaceRetrieveResponse as WorkspaceRetrieveResponse diff --git a/src/unlayer/types/project/__init__.py b/src/unlayer/types/project/__init__.py deleted file mode 100644 index 85258c0..0000000 --- a/src/unlayer/types/project/__init__.py +++ /dev/null @@ -1,8 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from .template_list_params import TemplateListParams as TemplateListParams -from .template_list_response import TemplateListResponse as TemplateListResponse -from .template_retrieve_params import TemplateRetrieveParams as TemplateRetrieveParams -from .template_retrieve_response import TemplateRetrieveResponse as TemplateRetrieveResponse diff --git a/src/unlayer/types/project/template_list_params.py b/src/unlayer/types/template_list_params.py similarity index 95% rename from src/unlayer/types/project/template_list_params.py rename to src/unlayer/types/template_list_params.py index ddf1d00..afb5c25 100644 --- a/src/unlayer/types/project/template_list_params.py +++ b/src/unlayer/types/template_list_params.py @@ -4,7 +4,7 @@ from typing_extensions import Literal, Required, Annotated, TypedDict -from ..._utils import PropertyInfo +from .._utils import PropertyInfo __all__ = ["TemplateListParams"] diff --git a/src/unlayer/types/project/template_list_response.py b/src/unlayer/types/template_list_response.py similarity index 95% rename from src/unlayer/types/project/template_list_response.py rename to src/unlayer/types/template_list_response.py index f306628..f4a4b74 100644 --- a/src/unlayer/types/project/template_list_response.py +++ b/src/unlayer/types/template_list_response.py @@ -6,7 +6,7 @@ from pydantic import Field as FieldInfo -from ..._models import BaseModel +from .._models import BaseModel __all__ = ["TemplateListResponse"] diff --git a/src/unlayer/types/project/template_retrieve_params.py b/src/unlayer/types/template_retrieve_params.py similarity index 91% rename from src/unlayer/types/project/template_retrieve_params.py rename to src/unlayer/types/template_retrieve_params.py index ddbe1ee..c7ca6cf 100644 --- a/src/unlayer/types/project/template_retrieve_params.py +++ b/src/unlayer/types/template_retrieve_params.py @@ -4,7 +4,7 @@ from typing_extensions import Required, Annotated, TypedDict -from ..._utils import PropertyInfo +from .._utils import PropertyInfo __all__ = ["TemplateRetrieveParams"] diff --git a/src/unlayer/types/project/template_retrieve_response.py b/src/unlayer/types/template_retrieve_response.py similarity index 96% rename from src/unlayer/types/project/template_retrieve_response.py rename to src/unlayer/types/template_retrieve_response.py index 5941d30..2475148 100644 --- a/src/unlayer/types/project/template_retrieve_response.py +++ b/src/unlayer/types/template_retrieve_response.py @@ -6,7 +6,7 @@ from pydantic import Field as FieldInfo -from ..._models import BaseModel +from .._models import BaseModel __all__ = ["TemplateRetrieveResponse", "Data"] diff --git a/tests/api_resources/project/__init__.py b/tests/api_resources/project/__init__.py deleted file mode 100644 index fd8019a..0000000 --- a/tests/api_resources/project/__init__.py +++ /dev/null @@ -1 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. diff --git a/tests/api_resources/project/test_templates.py b/tests/api_resources/test_templates.py similarity index 84% rename from tests/api_resources/project/test_templates.py rename to tests/api_resources/test_templates.py index 3354681..4a2291b 100644 --- a/tests/api_resources/project/test_templates.py +++ b/tests/api_resources/test_templates.py @@ -9,11 +9,8 @@ from unlayer import Unlayer, AsyncUnlayer from tests.utils import assert_matches_type +from unlayer.types import TemplateListResponse, TemplateRetrieveResponse from unlayer.pagination import SyncCursorPage, AsyncCursorPage -from unlayer.types.project import ( - TemplateListResponse, - TemplateRetrieveResponse, -) base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") @@ -23,7 +20,7 @@ class TestTemplates: @parametrize def test_method_retrieve(self, client: Unlayer) -> None: - template = client.project.templates.retrieve( + template = client.templates.retrieve( id="id", project_id="projectId", ) @@ -31,7 +28,7 @@ def test_method_retrieve(self, client: Unlayer) -> None: @parametrize def test_raw_response_retrieve(self, client: Unlayer) -> None: - response = client.project.templates.with_raw_response.retrieve( + response = client.templates.with_raw_response.retrieve( id="id", project_id="projectId", ) @@ -43,7 +40,7 @@ def test_raw_response_retrieve(self, client: Unlayer) -> None: @parametrize def test_streaming_response_retrieve(self, client: Unlayer) -> None: - with client.project.templates.with_streaming_response.retrieve( + with client.templates.with_streaming_response.retrieve( id="id", project_id="projectId", ) as response: @@ -58,21 +55,21 @@ def test_streaming_response_retrieve(self, client: Unlayer) -> None: @parametrize def test_path_params_retrieve(self, client: Unlayer) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): - client.project.templates.with_raw_response.retrieve( + client.templates.with_raw_response.retrieve( id="", project_id="projectId", ) @parametrize def test_method_list(self, client: Unlayer) -> None: - template = client.project.templates.list( + template = client.templates.list( project_id="projectId", ) assert_matches_type(SyncCursorPage[TemplateListResponse], template, path=["response"]) @parametrize def test_method_list_with_all_params(self, client: Unlayer) -> None: - template = client.project.templates.list( + template = client.templates.list( project_id="projectId", cursor="cursor", display_mode="email", @@ -83,7 +80,7 @@ def test_method_list_with_all_params(self, client: Unlayer) -> None: @parametrize def test_raw_response_list(self, client: Unlayer) -> None: - response = client.project.templates.with_raw_response.list( + response = client.templates.with_raw_response.list( project_id="projectId", ) @@ -94,7 +91,7 @@ def test_raw_response_list(self, client: Unlayer) -> None: @parametrize def test_streaming_response_list(self, client: Unlayer) -> None: - with client.project.templates.with_streaming_response.list( + with client.templates.with_streaming_response.list( project_id="projectId", ) as response: assert not response.is_closed @@ -113,7 +110,7 @@ class TestAsyncTemplates: @parametrize async def test_method_retrieve(self, async_client: AsyncUnlayer) -> None: - template = await async_client.project.templates.retrieve( + template = await async_client.templates.retrieve( id="id", project_id="projectId", ) @@ -121,7 +118,7 @@ async def test_method_retrieve(self, async_client: AsyncUnlayer) -> None: @parametrize async def test_raw_response_retrieve(self, async_client: AsyncUnlayer) -> None: - response = await async_client.project.templates.with_raw_response.retrieve( + response = await async_client.templates.with_raw_response.retrieve( id="id", project_id="projectId", ) @@ -133,7 +130,7 @@ async def test_raw_response_retrieve(self, async_client: AsyncUnlayer) -> None: @parametrize async def test_streaming_response_retrieve(self, async_client: AsyncUnlayer) -> None: - async with async_client.project.templates.with_streaming_response.retrieve( + async with async_client.templates.with_streaming_response.retrieve( id="id", project_id="projectId", ) as response: @@ -148,21 +145,21 @@ async def test_streaming_response_retrieve(self, async_client: AsyncUnlayer) -> @parametrize async def test_path_params_retrieve(self, async_client: AsyncUnlayer) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): - await async_client.project.templates.with_raw_response.retrieve( + await async_client.templates.with_raw_response.retrieve( id="", project_id="projectId", ) @parametrize async def test_method_list(self, async_client: AsyncUnlayer) -> None: - template = await async_client.project.templates.list( + template = await async_client.templates.list( project_id="projectId", ) assert_matches_type(AsyncCursorPage[TemplateListResponse], template, path=["response"]) @parametrize async def test_method_list_with_all_params(self, async_client: AsyncUnlayer) -> None: - template = await async_client.project.templates.list( + template = await async_client.templates.list( project_id="projectId", cursor="cursor", display_mode="email", @@ -173,7 +170,7 @@ async def test_method_list_with_all_params(self, async_client: AsyncUnlayer) -> @parametrize async def test_raw_response_list(self, async_client: AsyncUnlayer) -> None: - response = await async_client.project.templates.with_raw_response.list( + response = await async_client.templates.with_raw_response.list( project_id="projectId", ) @@ -184,7 +181,7 @@ async def test_raw_response_list(self, async_client: AsyncUnlayer) -> None: @parametrize async def test_streaming_response_list(self, async_client: AsyncUnlayer) -> None: - async with async_client.project.templates.with_streaming_response.list( + async with async_client.templates.with_streaming_response.list( project_id="projectId", ) as response: assert not response.is_closed From 43a90122527ac0011cff249e9bb284ea397abbdb Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Mon, 16 Feb 2026 14:56:58 +0000 Subject: [PATCH 32/62] chore: update SDK settings --- .github/workflows/publish-pypi.yml | 31 +++++++++ .github/workflows/release-doctor.yml | 21 ++++++ .release-please-manifest.json | 3 + .stats.yml | 2 +- CONTRIBUTING.md | 4 +- README.md | 14 ++-- bin/check-release-environment | 21 ++++++ pyproject.toml | 6 +- release-please-config.json | 66 +++++++++++++++++++ src/unlayer/_version.py | 2 +- src/unlayer/resources/convert/convert.py | 8 +-- .../resources/convert/full_to_simple.py | 8 +-- .../resources/convert/simple_to_full.py | 8 +-- src/unlayer/resources/project.py | 8 +-- src/unlayer/resources/templates.py | 8 +-- src/unlayer/resources/workspaces.py | 8 +-- 16 files changed, 180 insertions(+), 38 deletions(-) create mode 100644 .github/workflows/publish-pypi.yml create mode 100644 .github/workflows/release-doctor.yml create mode 100644 .release-please-manifest.json create mode 100644 bin/check-release-environment create mode 100644 release-please-config.json diff --git a/.github/workflows/publish-pypi.yml b/.github/workflows/publish-pypi.yml new file mode 100644 index 0000000..44450e0 --- /dev/null +++ b/.github/workflows/publish-pypi.yml @@ -0,0 +1,31 @@ +# This workflow is triggered when a GitHub release is created. +# It can also be run manually to re-publish to PyPI in case it failed for some reason. +# You can run this workflow by navigating to https://www.github.com/unlayer/unlayer-python/actions/workflows/publish-pypi.yml +name: Publish PyPI +on: + workflow_dispatch: + + release: + types: [published] + +jobs: + publish: + name: publish + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v6 + + - name: Install Rye + run: | + curl -sSf https://rye.astral.sh/get | bash + echo "$HOME/.rye/shims" >> $GITHUB_PATH + env: + RYE_VERSION: '0.44.0' + RYE_INSTALL_OPTION: '--yes' + + - name: Publish to PyPI + run: | + bash ./bin/publish-pypi + env: + PYPI_TOKEN: ${{ secrets.UNLAYER_PYPI_TOKEN || secrets.PYPI_TOKEN }} diff --git a/.github/workflows/release-doctor.yml b/.github/workflows/release-doctor.yml new file mode 100644 index 0000000..746dab3 --- /dev/null +++ b/.github/workflows/release-doctor.yml @@ -0,0 +1,21 @@ +name: Release Doctor +on: + pull_request: + branches: + - main + workflow_dispatch: + +jobs: + release_doctor: + name: release doctor + runs-on: ubuntu-latest + if: github.repository == 'unlayer/unlayer-python' && (github.event_name == 'push' || github.event_name == 'workflow_dispatch' || startsWith(github.head_ref, 'release-please') || github.head_ref == 'next') + + steps: + - uses: actions/checkout@v6 + + - name: Check release environment + run: | + bash ./bin/check-release-environment + env: + PYPI_TOKEN: ${{ secrets.UNLAYER_PYPI_TOKEN || secrets.PYPI_TOKEN }} diff --git a/.release-please-manifest.json b/.release-please-manifest.json new file mode 100644 index 0000000..1332969 --- /dev/null +++ b/.release-please-manifest.json @@ -0,0 +1,3 @@ +{ + ".": "0.0.1" +} \ No newline at end of file diff --git a/.stats.yml b/.stats.yml index c827f62..b0c7f3b 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 7 openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/unlayer%2Funlayer-e77bc881d5cb6c68be6f3d3861ed021b99f6cde45ee28d3511abe284d888262e.yml openapi_spec_hash: 7e7fae1b919c5d337e8b22be2a24d3ed -config_hash: ea554d62bfbb5e8ea43ebf0a274dec31 +config_hash: 00a7b0dff20113623ba749fe28286413 diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 8bc8eee..26c1b09 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -62,7 +62,7 @@ If you’d like to use the repository from source, you can either install from g To install via git: ```sh -$ pip install git+ssh://git@github.com/stainless-sdks/unlayer-python.git +$ pip install git+ssh://git@github.com/unlayer/unlayer-python.git ``` Alternatively, you can build from source and install the wheel file: @@ -120,7 +120,7 @@ the changes aren't made through the automated pipeline, you may want to make rel ### Publish with a GitHub workflow -You can release to package managers by using [the `Publish PyPI` GitHub action](https://www.github.com/stainless-sdks/unlayer-python/actions/workflows/publish-pypi.yml). This requires a setup organization or repository secret to be set up. +You can release to package managers by using [the `Publish PyPI` GitHub action](https://www.github.com/unlayer/unlayer-python/actions/workflows/publish-pypi.yml). This requires a setup organization or repository secret to be set up. ### Publish manually diff --git a/README.md b/README.md index 8e7620c..42a2e19 100644 --- a/README.md +++ b/README.md @@ -16,8 +16,8 @@ The full API of this library can be found in [api.md](api.md). ## Installation ```sh -# install from this staging repo -pip install git+ssh://git@github.com/stainless-sdks/unlayer-python.git +# install from the production repo +pip install git+ssh://git@github.com/unlayer/unlayer-python.git ``` > [!NOTE] @@ -83,8 +83,8 @@ By default, the async client uses `httpx` for HTTP requests. However, for improv You can enable this by installing `aiohttp`: ```sh -# install from this staging repo -pip install 'unlayer[aiohttp] @ git+ssh://git@github.com/stainless-sdks/unlayer-python.git' +# install from the production repo +pip install 'unlayer[aiohttp] @ git+ssh://git@github.com/unlayer/unlayer-python.git' ``` Then you can enable it by instantiating the client with `http_client=DefaultAioHttpClient()`: @@ -349,9 +349,9 @@ project = response.parse() # get the object that `project.retrieve()` would hav print(project.data) ``` -These methods return an [`APIResponse`](https://github.com/stainless-sdks/unlayer-python/tree/main/src/unlayer/_response.py) object. +These methods return an [`APIResponse`](https://github.com/unlayer/unlayer-python/tree/main/src/unlayer/_response.py) object. -The async client returns an [`AsyncAPIResponse`](https://github.com/stainless-sdks/unlayer-python/tree/main/src/unlayer/_response.py) with the same structure, the only difference being `await`able methods for reading the response content. +The async client returns an [`AsyncAPIResponse`](https://github.com/unlayer/unlayer-python/tree/main/src/unlayer/_response.py) with the same structure, the only difference being `await`able methods for reading the response content. #### `.with_streaming_response` @@ -457,7 +457,7 @@ This package generally follows [SemVer](https://semver.org/spec/v2.0.0.html) con We take backwards-compatibility seriously and work hard to ensure you can rely on a smooth upgrade experience. -We are keen for your feedback; please open an [issue](https://www.github.com/stainless-sdks/unlayer-python/issues) with questions, bugs, or suggestions. +We are keen for your feedback; please open an [issue](https://www.github.com/unlayer/unlayer-python/issues) with questions, bugs, or suggestions. ### Determining the installed version diff --git a/bin/check-release-environment b/bin/check-release-environment new file mode 100644 index 0000000..b845b0f --- /dev/null +++ b/bin/check-release-environment @@ -0,0 +1,21 @@ +#!/usr/bin/env bash + +errors=() + +if [ -z "${PYPI_TOKEN}" ]; then + errors+=("The PYPI_TOKEN secret has not been set. Please set it in either this repository's secrets or your organization secrets.") +fi + +lenErrors=${#errors[@]} + +if [[ lenErrors -gt 0 ]]; then + echo -e "Found the following errors in the release environment:\n" + + for error in "${errors[@]}"; do + echo -e "- $error\n" + done + + exit 1 +fi + +echo "The environment is ready to push releases!" diff --git a/pyproject.toml b/pyproject.toml index 2aa32b4..2bcff9e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -37,8 +37,8 @@ classifiers = [ ] [project.urls] -Homepage = "https://github.com/stainless-sdks/unlayer-python" -Repository = "https://github.com/stainless-sdks/unlayer-python" +Homepage = "https://github.com/unlayer/unlayer-python" +Repository = "https://github.com/unlayer/unlayer-python" [project.optional-dependencies] aiohttp = ["aiohttp", "httpx_aiohttp>=0.1.9"] @@ -126,7 +126,7 @@ path = "README.md" [[tool.hatch.metadata.hooks.fancy-pypi-readme.substitutions]] # replace relative links with absolute links pattern = '\[(.+?)\]\(((?!https?://)\S+?)\)' -replacement = '[\1](https://github.com/stainless-sdks/unlayer-python/tree/main/\g<2>)' +replacement = '[\1](https://github.com/unlayer/unlayer-python/tree/main/\g<2>)' [tool.pytest.ini_options] testpaths = ["tests"] diff --git a/release-please-config.json b/release-please-config.json new file mode 100644 index 0000000..d1ec05c --- /dev/null +++ b/release-please-config.json @@ -0,0 +1,66 @@ +{ + "packages": { + ".": {} + }, + "$schema": "https://raw.githubusercontent.com/stainless-api/release-please/main/schemas/config.json", + "include-v-in-tag": true, + "include-component-in-tag": false, + "versioning": "prerelease", + "prerelease": true, + "bump-minor-pre-major": true, + "bump-patch-for-minor-pre-major": false, + "pull-request-header": "Automated Release PR", + "pull-request-title-pattern": "release: ${version}", + "changelog-sections": [ + { + "type": "feat", + "section": "Features" + }, + { + "type": "fix", + "section": "Bug Fixes" + }, + { + "type": "perf", + "section": "Performance Improvements" + }, + { + "type": "revert", + "section": "Reverts" + }, + { + "type": "chore", + "section": "Chores" + }, + { + "type": "docs", + "section": "Documentation" + }, + { + "type": "style", + "section": "Styles" + }, + { + "type": "refactor", + "section": "Refactors" + }, + { + "type": "test", + "section": "Tests", + "hidden": true + }, + { + "type": "build", + "section": "Build System" + }, + { + "type": "ci", + "section": "Continuous Integration", + "hidden": true + } + ], + "release-type": "python", + "extra-files": [ + "src/unlayer/_version.py" + ] +} \ No newline at end of file diff --git a/src/unlayer/_version.py b/src/unlayer/_version.py index c9e1c88..b9adb5b 100644 --- a/src/unlayer/_version.py +++ b/src/unlayer/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "unlayer" -__version__ = "0.0.1" +__version__ = "0.0.1" # x-release-please-version diff --git a/src/unlayer/resources/convert/convert.py b/src/unlayer/resources/convert/convert.py index c303200..08eee7c 100644 --- a/src/unlayer/resources/convert/convert.py +++ b/src/unlayer/resources/convert/convert.py @@ -39,7 +39,7 @@ def with_raw_response(self) -> ConvertResourceWithRawResponse: This property can be used as a prefix for any HTTP method call to return the raw response object instead of the parsed content. - For more information, see https://www.github.com/stainless-sdks/unlayer-python#accessing-raw-response-data-eg-headers + For more information, see https://www.github.com/unlayer/unlayer-python#accessing-raw-response-data-eg-headers """ return ConvertResourceWithRawResponse(self) @@ -48,7 +48,7 @@ def with_streaming_response(self) -> ConvertResourceWithStreamingResponse: """ An alternative to `.with_raw_response` that doesn't eagerly read the response body. - For more information, see https://www.github.com/stainless-sdks/unlayer-python#with_streaming_response + For more information, see https://www.github.com/unlayer/unlayer-python#with_streaming_response """ return ConvertResourceWithStreamingResponse(self) @@ -68,7 +68,7 @@ def with_raw_response(self) -> AsyncConvertResourceWithRawResponse: This property can be used as a prefix for any HTTP method call to return the raw response object instead of the parsed content. - For more information, see https://www.github.com/stainless-sdks/unlayer-python#accessing-raw-response-data-eg-headers + For more information, see https://www.github.com/unlayer/unlayer-python#accessing-raw-response-data-eg-headers """ return AsyncConvertResourceWithRawResponse(self) @@ -77,7 +77,7 @@ def with_streaming_response(self) -> AsyncConvertResourceWithStreamingResponse: """ An alternative to `.with_raw_response` that doesn't eagerly read the response body. - For more information, see https://www.github.com/stainless-sdks/unlayer-python#with_streaming_response + For more information, see https://www.github.com/unlayer/unlayer-python#with_streaming_response """ return AsyncConvertResourceWithStreamingResponse(self) diff --git a/src/unlayer/resources/convert/full_to_simple.py b/src/unlayer/resources/convert/full_to_simple.py index 0d793c7..d5901f4 100644 --- a/src/unlayer/resources/convert/full_to_simple.py +++ b/src/unlayer/resources/convert/full_to_simple.py @@ -30,7 +30,7 @@ def with_raw_response(self) -> FullToSimpleResourceWithRawResponse: This property can be used as a prefix for any HTTP method call to return the raw response object instead of the parsed content. - For more information, see https://www.github.com/stainless-sdks/unlayer-python#accessing-raw-response-data-eg-headers + For more information, see https://www.github.com/unlayer/unlayer-python#accessing-raw-response-data-eg-headers """ return FullToSimpleResourceWithRawResponse(self) @@ -39,7 +39,7 @@ def with_streaming_response(self) -> FullToSimpleResourceWithStreamingResponse: """ An alternative to `.with_raw_response` that doesn't eagerly read the response body. - For more information, see https://www.github.com/stainless-sdks/unlayer-python#with_streaming_response + For more information, see https://www.github.com/unlayer/unlayer-python#with_streaming_response """ return FullToSimpleResourceWithStreamingResponse(self) @@ -97,7 +97,7 @@ def with_raw_response(self) -> AsyncFullToSimpleResourceWithRawResponse: This property can be used as a prefix for any HTTP method call to return the raw response object instead of the parsed content. - For more information, see https://www.github.com/stainless-sdks/unlayer-python#accessing-raw-response-data-eg-headers + For more information, see https://www.github.com/unlayer/unlayer-python#accessing-raw-response-data-eg-headers """ return AsyncFullToSimpleResourceWithRawResponse(self) @@ -106,7 +106,7 @@ def with_streaming_response(self) -> AsyncFullToSimpleResourceWithStreamingRespo """ An alternative to `.with_raw_response` that doesn't eagerly read the response body. - For more information, see https://www.github.com/stainless-sdks/unlayer-python#with_streaming_response + For more information, see https://www.github.com/unlayer/unlayer-python#with_streaming_response """ return AsyncFullToSimpleResourceWithStreamingResponse(self) diff --git a/src/unlayer/resources/convert/simple_to_full.py b/src/unlayer/resources/convert/simple_to_full.py index 29db149..d9b634f 100644 --- a/src/unlayer/resources/convert/simple_to_full.py +++ b/src/unlayer/resources/convert/simple_to_full.py @@ -30,7 +30,7 @@ def with_raw_response(self) -> SimpleToFullResourceWithRawResponse: This property can be used as a prefix for any HTTP method call to return the raw response object instead of the parsed content. - For more information, see https://www.github.com/stainless-sdks/unlayer-python#accessing-raw-response-data-eg-headers + For more information, see https://www.github.com/unlayer/unlayer-python#accessing-raw-response-data-eg-headers """ return SimpleToFullResourceWithRawResponse(self) @@ -39,7 +39,7 @@ def with_streaming_response(self) -> SimpleToFullResourceWithStreamingResponse: """ An alternative to `.with_raw_response` that doesn't eagerly read the response body. - For more information, see https://www.github.com/stainless-sdks/unlayer-python#with_streaming_response + For more information, see https://www.github.com/unlayer/unlayer-python#with_streaming_response """ return SimpleToFullResourceWithStreamingResponse(self) @@ -92,7 +92,7 @@ def with_raw_response(self) -> AsyncSimpleToFullResourceWithRawResponse: This property can be used as a prefix for any HTTP method call to return the raw response object instead of the parsed content. - For more information, see https://www.github.com/stainless-sdks/unlayer-python#accessing-raw-response-data-eg-headers + For more information, see https://www.github.com/unlayer/unlayer-python#accessing-raw-response-data-eg-headers """ return AsyncSimpleToFullResourceWithRawResponse(self) @@ -101,7 +101,7 @@ def with_streaming_response(self) -> AsyncSimpleToFullResourceWithStreamingRespo """ An alternative to `.with_raw_response` that doesn't eagerly read the response body. - For more information, see https://www.github.com/stainless-sdks/unlayer-python#with_streaming_response + For more information, see https://www.github.com/unlayer/unlayer-python#with_streaming_response """ return AsyncSimpleToFullResourceWithStreamingResponse(self) diff --git a/src/unlayer/resources/project.py b/src/unlayer/resources/project.py index 6ff7918..6de8cf8 100644 --- a/src/unlayer/resources/project.py +++ b/src/unlayer/resources/project.py @@ -28,7 +28,7 @@ def with_raw_response(self) -> ProjectResourceWithRawResponse: This property can be used as a prefix for any HTTP method call to return the raw response object instead of the parsed content. - For more information, see https://www.github.com/stainless-sdks/unlayer-python#accessing-raw-response-data-eg-headers + For more information, see https://www.github.com/unlayer/unlayer-python#accessing-raw-response-data-eg-headers """ return ProjectResourceWithRawResponse(self) @@ -37,7 +37,7 @@ def with_streaming_response(self) -> ProjectResourceWithStreamingResponse: """ An alternative to `.with_raw_response` that doesn't eagerly read the response body. - For more information, see https://www.github.com/stainless-sdks/unlayer-python#with_streaming_response + For more information, see https://www.github.com/unlayer/unlayer-python#with_streaming_response """ return ProjectResourceWithStreamingResponse(self) @@ -86,7 +86,7 @@ def with_raw_response(self) -> AsyncProjectResourceWithRawResponse: This property can be used as a prefix for any HTTP method call to return the raw response object instead of the parsed content. - For more information, see https://www.github.com/stainless-sdks/unlayer-python#accessing-raw-response-data-eg-headers + For more information, see https://www.github.com/unlayer/unlayer-python#accessing-raw-response-data-eg-headers """ return AsyncProjectResourceWithRawResponse(self) @@ -95,7 +95,7 @@ def with_streaming_response(self) -> AsyncProjectResourceWithStreamingResponse: """ An alternative to `.with_raw_response` that doesn't eagerly read the response body. - For more information, see https://www.github.com/stainless-sdks/unlayer-python#with_streaming_response + For more information, see https://www.github.com/unlayer/unlayer-python#with_streaming_response """ return AsyncProjectResourceWithStreamingResponse(self) diff --git a/src/unlayer/resources/templates.py b/src/unlayer/resources/templates.py index 4c0708a..68389c9 100644 --- a/src/unlayer/resources/templates.py +++ b/src/unlayer/resources/templates.py @@ -32,7 +32,7 @@ def with_raw_response(self) -> TemplatesResourceWithRawResponse: This property can be used as a prefix for any HTTP method call to return the raw response object instead of the parsed content. - For more information, see https://www.github.com/stainless-sdks/unlayer-python#accessing-raw-response-data-eg-headers + For more information, see https://www.github.com/unlayer/unlayer-python#accessing-raw-response-data-eg-headers """ return TemplatesResourceWithRawResponse(self) @@ -41,7 +41,7 @@ def with_streaming_response(self) -> TemplatesResourceWithStreamingResponse: """ An alternative to `.with_raw_response` that doesn't eagerly read the response body. - For more information, see https://www.github.com/stainless-sdks/unlayer-python#with_streaming_response + For more information, see https://www.github.com/unlayer/unlayer-python#with_streaming_response """ return TemplatesResourceWithStreamingResponse(self) @@ -154,7 +154,7 @@ def with_raw_response(self) -> AsyncTemplatesResourceWithRawResponse: This property can be used as a prefix for any HTTP method call to return the raw response object instead of the parsed content. - For more information, see https://www.github.com/stainless-sdks/unlayer-python#accessing-raw-response-data-eg-headers + For more information, see https://www.github.com/unlayer/unlayer-python#accessing-raw-response-data-eg-headers """ return AsyncTemplatesResourceWithRawResponse(self) @@ -163,7 +163,7 @@ def with_streaming_response(self) -> AsyncTemplatesResourceWithStreamingResponse """ An alternative to `.with_raw_response` that doesn't eagerly read the response body. - For more information, see https://www.github.com/stainless-sdks/unlayer-python#with_streaming_response + For more information, see https://www.github.com/unlayer/unlayer-python#with_streaming_response """ return AsyncTemplatesResourceWithStreamingResponse(self) diff --git a/src/unlayer/resources/workspaces.py b/src/unlayer/resources/workspaces.py index 494ccea..4f4f5cd 100644 --- a/src/unlayer/resources/workspaces.py +++ b/src/unlayer/resources/workspaces.py @@ -27,7 +27,7 @@ def with_raw_response(self) -> WorkspacesResourceWithRawResponse: This property can be used as a prefix for any HTTP method call to return the raw response object instead of the parsed content. - For more information, see https://www.github.com/stainless-sdks/unlayer-python#accessing-raw-response-data-eg-headers + For more information, see https://www.github.com/unlayer/unlayer-python#accessing-raw-response-data-eg-headers """ return WorkspacesResourceWithRawResponse(self) @@ -36,7 +36,7 @@ def with_streaming_response(self) -> WorkspacesResourceWithStreamingResponse: """ An alternative to `.with_raw_response` that doesn't eagerly read the response body. - For more information, see https://www.github.com/stainless-sdks/unlayer-python#with_streaming_response + For more information, see https://www.github.com/unlayer/unlayer-python#with_streaming_response """ return WorkspacesResourceWithStreamingResponse(self) @@ -100,7 +100,7 @@ def with_raw_response(self) -> AsyncWorkspacesResourceWithRawResponse: This property can be used as a prefix for any HTTP method call to return the raw response object instead of the parsed content. - For more information, see https://www.github.com/stainless-sdks/unlayer-python#accessing-raw-response-data-eg-headers + For more information, see https://www.github.com/unlayer/unlayer-python#accessing-raw-response-data-eg-headers """ return AsyncWorkspacesResourceWithRawResponse(self) @@ -109,7 +109,7 @@ def with_streaming_response(self) -> AsyncWorkspacesResourceWithStreamingRespons """ An alternative to `.with_raw_response` that doesn't eagerly read the response body. - For more information, see https://www.github.com/stainless-sdks/unlayer-python#with_streaming_response + For more information, see https://www.github.com/unlayer/unlayer-python#with_streaming_response """ return AsyncWorkspacesResourceWithStreamingResponse(self) From 4f9c5afeec6b68b6a2b0165b0ac2e3a4ac1eced3 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Mon, 16 Feb 2026 14:57:16 +0000 Subject: [PATCH 33/62] codegen metadata --- .stats.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.stats.yml b/.stats.yml index b0c7f3b..3a44165 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 7 openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/unlayer%2Funlayer-e77bc881d5cb6c68be6f3d3861ed021b99f6cde45ee28d3511abe284d888262e.yml openapi_spec_hash: 7e7fae1b919c5d337e8b22be2a24d3ed -config_hash: 00a7b0dff20113623ba749fe28286413 +config_hash: 344aa63165862b95d788ad377dd902e3 From f3ac140c81ec59dc66d01be6d267b69f25d3ad8d Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Mon, 16 Feb 2026 14:57:36 +0000 Subject: [PATCH 34/62] chore: update SDK settings --- .stats.yml | 2 +- README.md | 11 ++++------- 2 files changed, 5 insertions(+), 8 deletions(-) diff --git a/.stats.yml b/.stats.yml index 3a44165..c325690 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 7 openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/unlayer%2Funlayer-e77bc881d5cb6c68be6f3d3861ed021b99f6cde45ee28d3511abe284d888262e.yml openapi_spec_hash: 7e7fae1b919c5d337e8b22be2a24d3ed -config_hash: 344aa63165862b95d788ad377dd902e3 +config_hash: 68f088239d13d9983e4913c6b8396c0d diff --git a/README.md b/README.md index 42a2e19..ddb73df 100644 --- a/README.md +++ b/README.md @@ -16,13 +16,10 @@ The full API of this library can be found in [api.md](api.md). ## Installation ```sh -# install from the production repo -pip install git+ssh://git@github.com/unlayer/unlayer-python.git +# install from PyPI +pip install unlayer ``` -> [!NOTE] -> Once this package is [published to PyPI](https://www.stainless.com/docs/guides/publish), this will become: `pip install unlayer` - ## Usage The full API of this library can be found in [api.md](api.md). @@ -83,8 +80,8 @@ By default, the async client uses `httpx` for HTTP requests. However, for improv You can enable this by installing `aiohttp`: ```sh -# install from the production repo -pip install 'unlayer[aiohttp] @ git+ssh://git@github.com/unlayer/unlayer-python.git' +# install from PyPI +pip install unlayer[aiohttp] ``` Then you can enable it by instantiating the client with `http_client=DefaultAioHttpClient()`: From 0550d01b212ffc03f59cad2ab211af7c62a6634d Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Mon, 16 Feb 2026 14:57:53 +0000 Subject: [PATCH 35/62] codegen metadata --- .stats.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.stats.yml b/.stats.yml index c325690..5702403 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 7 openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/unlayer%2Funlayer-e77bc881d5cb6c68be6f3d3861ed021b99f6cde45ee28d3511abe284d888262e.yml openapi_spec_hash: 7e7fae1b919c5d337e8b22be2a24d3ed -config_hash: 68f088239d13d9983e4913c6b8396c0d +config_hash: 87d560e6d481bc04dc9310719a5eb946 From 23a1989ef61d0dc26ea6e8307324d005a5999187 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Mon, 16 Feb 2026 18:57:30 +0000 Subject: [PATCH 36/62] codegen metadata --- .stats.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.stats.yml b/.stats.yml index 5702403..5a7e305 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 7 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/unlayer%2Funlayer-e77bc881d5cb6c68be6f3d3861ed021b99f6cde45ee28d3511abe284d888262e.yml -openapi_spec_hash: 7e7fae1b919c5d337e8b22be2a24d3ed +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/unlayer%2Funlayer-05f06124acf955282470ca7d863e8d10c5dd36cbde746d154482e9972277cd03.yml +openapi_spec_hash: 21532f2d9416a6e55bf9107df0948cd8 config_hash: 87d560e6d481bc04dc9310719a5eb946 From 77fdbeec33af5979acae89a527d80dc7f4ddc35b Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Fri, 20 Feb 2026 04:06:17 +0000 Subject: [PATCH 37/62] chore: update mock server docs --- CONTRIBUTING.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 26c1b09..8f8a253 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -88,8 +88,7 @@ $ pip install ./path-to-wheel-file.whl Most tests require you to [set up a mock server](https://github.com/stoplightio/prism) against the OpenAPI spec to run the tests. ```sh -# you will need npm installed -$ npx prism mock path/to/your/openapi.yml +$ ./scripts/mock ``` ```sh From 8fe338e699d5563a5c10c4ad95da31a1da6c097e Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Fri, 20 Feb 2026 13:56:45 +0000 Subject: [PATCH 38/62] feat(api): api update --- .stats.yml | 6 +- README.md | 26 +- src/unlayer/__init__.py | 14 +- src/unlayer/_client.py | 234 ++++++++-------- src/unlayer/resources/project.py | 10 +- src/unlayer/resources/templates.py | 24 +- src/unlayer/resources/workspaces.py | 24 +- src/unlayer/types/project_retrieve_params.py | 6 +- src/unlayer/types/template_list_params.py | 8 +- src/unlayer/types/template_retrieve_params.py | 6 +- tests/api_resources/test_project.py | 26 +- tests/api_resources/test_templates.py | 48 ++-- tests/conftest.py | 6 +- tests/test_client.py | 256 +++++++----------- 14 files changed, 315 insertions(+), 379 deletions(-) diff --git a/.stats.yml b/.stats.yml index 5a7e305..2fb1d6b 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 7 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/unlayer%2Funlayer-05f06124acf955282470ca7d863e8d10c5dd36cbde746d154482e9972277cd03.yml -openapi_spec_hash: 21532f2d9416a6e55bf9107df0948cd8 -config_hash: 87d560e6d481bc04dc9310719a5eb946 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/unlayer%2Funlayer-14707e371ca64ee082f425e10f8bd4b7d9e2eeb28d6e69daad66902abb1b8b6b.yml +openapi_spec_hash: 8198d0442f4736109bf6c49a5a8697ab +config_hash: 144077318a24cfbe9ce6da795a628a80 diff --git a/README.md b/README.md index ddb73df..2974eba 100644 --- a/README.md +++ b/README.md @@ -29,9 +29,7 @@ import os from unlayer import Unlayer client = Unlayer( - access_token=os.environ.get("UNLAYER_ACCESS_TOKEN"), # This is the default and can be omitted - # or 'production' | 'qa' | 'dev'; defaults to "production". - environment="stage", + api_key=os.environ.get("UNLAYER_API_KEY"), # This is the default and can be omitted ) project = client.project.retrieve( @@ -40,10 +38,10 @@ project = client.project.retrieve( print(project.data) ``` -While you can provide a `access_token` keyword argument, +While you can provide an `api_key` keyword argument, we recommend using [python-dotenv](https://pypi.org/project/python-dotenv/) -to add `UNLAYER_ACCESS_TOKEN="My Access Token"` to your `.env` file -so that your Access Token is not stored in source control. +to add `UNLAYER_API_KEY="My API Key"` to your `.env` file +so that your API Key is not stored in source control. ## Async usage @@ -55,9 +53,7 @@ import asyncio from unlayer import AsyncUnlayer client = AsyncUnlayer( - access_token=os.environ.get("UNLAYER_ACCESS_TOKEN"), # This is the default and can be omitted - # or 'production' | 'qa' | 'dev'; defaults to "production". - environment="stage", + api_key=os.environ.get("UNLAYER_API_KEY"), # This is the default and can be omitted ) @@ -95,9 +91,7 @@ from unlayer import AsyncUnlayer async def main() -> None: async with AsyncUnlayer( - access_token=os.environ.get( - "UNLAYER_ACCESS_TOKEN" - ), # This is the default and can be omitted + api_key=os.environ.get("UNLAYER_API_KEY"), # This is the default and can be omitted http_client=DefaultAioHttpClient(), ) as client: project = await client.project.retrieve( @@ -132,8 +126,8 @@ client = Unlayer() all_templates = [] # Automatically fetches more pages as needed. for template in client.templates.list( - project_id="your-project-id", limit=10, + project_id="your-project-id", ): # Do something with template here all_templates.append(template) @@ -153,8 +147,8 @@ async def main() -> None: all_templates = [] # Iterate through items across all pages, issuing requests as needed. async for template in client.templates.list( - project_id="your-project-id", limit=10, + project_id="your-project-id", ): all_templates.append(template) print(all_templates) @@ -167,8 +161,8 @@ Alternatively, you can use the `.has_next_page()`, `.next_page_info()`, or `.get ```python first_page = await client.templates.list( - project_id="your-project-id", limit=10, + project_id="your-project-id", ) if first_page.has_next_page(): print(f"will fetch next page using these details: {first_page.next_page_info()}") @@ -182,8 +176,8 @@ Or just work directly with the returned data: ```python first_page = await client.templates.list( - project_id="your-project-id", limit=10, + project_id="your-project-id", ) print(f"next page cursor: {first_page.next_cursor}") # => "next page cursor: ..." diff --git a/src/unlayer/__init__.py b/src/unlayer/__init__.py index 196b04d..37d9b74 100644 --- a/src/unlayer/__init__.py +++ b/src/unlayer/__init__.py @@ -5,18 +5,7 @@ from . import types from ._types import NOT_GIVEN, Omit, NoneType, NotGiven, Transport, ProxiesTypes, omit, not_given from ._utils import file_from_path -from ._client import ( - ENVIRONMENTS, - Client, - Stream, - Timeout, - Unlayer, - Transport, - AsyncClient, - AsyncStream, - AsyncUnlayer, - RequestOptions, -) +from ._client import Client, Stream, Timeout, Unlayer, Transport, AsyncClient, AsyncStream, AsyncUnlayer, RequestOptions from ._models import BaseModel from ._version import __title__, __version__ from ._response import APIResponse as APIResponse, AsyncAPIResponse as AsyncAPIResponse @@ -74,7 +63,6 @@ "AsyncStream", "Unlayer", "AsyncUnlayer", - "ENVIRONMENTS", "file_from_path", "BaseModel", "DEFAULT_TIMEOUT", diff --git a/src/unlayer/_client.py b/src/unlayer/_client.py index 01b60d9..4c34931 100644 --- a/src/unlayer/_client.py +++ b/src/unlayer/_client.py @@ -3,8 +3,8 @@ from __future__ import annotations import os -from typing import TYPE_CHECKING, Any, Dict, Mapping, cast -from typing_extensions import Self, Literal, override +from typing import TYPE_CHECKING, Any, Mapping +from typing_extensions import Self, override import httpx @@ -12,6 +12,7 @@ from ._qs import Querystring from ._types import ( Omit, + Headers, Timeout, NotGiven, Transport, @@ -23,7 +24,7 @@ from ._compat import cached_property from ._version import __version__ from ._streaming import Stream as Stream, AsyncStream as AsyncStream -from ._exceptions import UnlayerError, APIStatusError +from ._exceptions import APIStatusError from ._base_client import ( DEFAULT_MAX_RETRIES, SyncAPIClient, @@ -37,38 +38,22 @@ from .resources.workspaces import WorkspacesResource, AsyncWorkspacesResource from .resources.convert.convert import ConvertResource, AsyncConvertResource -__all__ = [ - "ENVIRONMENTS", - "Timeout", - "Transport", - "ProxiesTypes", - "RequestOptions", - "Unlayer", - "AsyncUnlayer", - "Client", - "AsyncClient", -] - -ENVIRONMENTS: Dict[str, str] = { - "production": "https://api.unlayer.com", - "stage": "https://api.stage.unlayer.com", - "qa": "https://api.qa.unlayer.com", - "dev": "https://api.dev.unlayer.com", -} +__all__ = ["Timeout", "Transport", "ProxiesTypes", "RequestOptions", "Unlayer", "AsyncUnlayer", "Client", "AsyncClient"] class Unlayer(SyncAPIClient): # client options - access_token: str - - _environment: Literal["production", "stage", "qa", "dev"] | NotGiven + api_key: str | None + personal_access_token: str | None + project_id: str | None def __init__( self, *, - access_token: str | None = None, - environment: Literal["production", "stage", "qa", "dev"] | NotGiven = not_given, - base_url: str | httpx.URL | None | NotGiven = not_given, + api_key: str | None = None, + personal_access_token: str | None = None, + project_id: str | None = None, + base_url: str | httpx.URL | None = None, timeout: float | Timeout | None | NotGiven = not_given, max_retries: int = DEFAULT_MAX_RETRIES, default_headers: Mapping[str, str] | None = None, @@ -89,41 +74,27 @@ def __init__( ) -> None: """Construct a new synchronous Unlayer client instance. - This automatically infers the `access_token` argument from the `UNLAYER_ACCESS_TOKEN` environment variable if it is not provided. + This automatically infers the following arguments from their corresponding environment variables if they are not provided: + - `api_key` from `UNLAYER_API_KEY` + - `personal_access_token` from `UNLAYER_PERSONAL_ACCESS_TOKEN` + - `project_id` from `UNLAYER_PROJECT_ID` """ - if access_token is None: - access_token = os.environ.get("UNLAYER_ACCESS_TOKEN") - if access_token is None: - raise UnlayerError( - "The access_token client option must be set either by passing access_token to the client or by setting the UNLAYER_ACCESS_TOKEN environment variable" - ) - self.access_token = access_token - - self._environment = environment - - base_url_env = os.environ.get("UNLAYER_BASE_URL") - if is_given(base_url) and base_url is not None: - # cast required because mypy doesn't understand the type narrowing - base_url = cast("str | httpx.URL", base_url) # pyright: ignore[reportUnnecessaryCast] - elif is_given(environment): - if base_url_env and base_url is not None: - raise ValueError( - "Ambiguous URL; The `UNLAYER_BASE_URL` env var and the `environment` argument are given. If you want to use the environment, you must pass base_url=None", - ) - - try: - base_url = ENVIRONMENTS[environment] - except KeyError as exc: - raise ValueError(f"Unknown environment: {environment}") from exc - elif base_url_env is not None: - base_url = base_url_env - else: - self._environment = environment = "production" - - try: - base_url = ENVIRONMENTS[environment] - except KeyError as exc: - raise ValueError(f"Unknown environment: {environment}") from exc + if api_key is None: + api_key = os.environ.get("UNLAYER_API_KEY") + self.api_key = api_key + + if personal_access_token is None: + personal_access_token = os.environ.get("UNLAYER_PERSONAL_ACCESS_TOKEN") + self.personal_access_token = personal_access_token + + if project_id is None: + project_id = os.environ.get("UNLAYER_PROJECT_ID") + self.project_id = project_id + + if base_url is None: + base_url = os.environ.get("UNLAYER_BASE_URL") + if base_url is None: + base_url = f"https://api.unlayer.com" super().__init__( version=__version__, @@ -176,8 +147,21 @@ def qs(self) -> Querystring: @property @override def auth_headers(self) -> dict[str, str]: - access_token = self.access_token - return {"Authorization": f"Bearer {access_token}"} + return {**self._api_key_auth, **self._personal_access_token_auth} + + @property + def _api_key_auth(self) -> dict[str, str]: + api_key = self.api_key + if api_key is None: + return {} + return {"Authorization": f"Bearer {api_key}"} + + @property + def _personal_access_token_auth(self) -> dict[str, str]: + personal_access_token = self.personal_access_token + if personal_access_token is None: + return {} + return {"Authorization": f"Bearer {personal_access_token}"} @property @override @@ -185,14 +169,25 @@ def default_headers(self) -> dict[str, str | Omit]: return { **super().default_headers, "X-Stainless-Async": "false", + "X-Project-ID": self.project_id if self.project_id is not None else Omit(), **self._custom_headers, } + @override + def _validate_headers(self, headers: Headers, custom_headers: Headers) -> None: + if headers.get("Authorization") or isinstance(custom_headers.get("Authorization"), Omit): + return + + raise TypeError( + '"Could not resolve authentication method. Expected either api_key or personal_access_token to be set. Or for one of the `Authorization` or `Authorization` headers to be explicitly omitted"' + ) + def copy( self, *, - access_token: str | None = None, - environment: Literal["production", "stage", "qa", "dev"] | None = None, + api_key: str | None = None, + personal_access_token: str | None = None, + project_id: str | None = None, base_url: str | httpx.URL | None = None, timeout: float | Timeout | None | NotGiven = not_given, http_client: httpx.Client | None = None, @@ -226,9 +221,10 @@ def copy( http_client = http_client or self._client return self.__class__( - access_token=access_token or self.access_token, + api_key=api_key or self.api_key, + personal_access_token=personal_access_token or self.personal_access_token, + project_id=project_id or self.project_id, base_url=base_url or self.base_url, - environment=environment or self._environment, timeout=self.timeout if isinstance(timeout, NotGiven) else timeout, http_client=http_client, max_retries=max_retries if is_given(max_retries) else self.max_retries, @@ -277,16 +273,17 @@ def _make_status_error( class AsyncUnlayer(AsyncAPIClient): # client options - access_token: str - - _environment: Literal["production", "stage", "qa", "dev"] | NotGiven + api_key: str | None + personal_access_token: str | None + project_id: str | None def __init__( self, *, - access_token: str | None = None, - environment: Literal["production", "stage", "qa", "dev"] | NotGiven = not_given, - base_url: str | httpx.URL | None | NotGiven = not_given, + api_key: str | None = None, + personal_access_token: str | None = None, + project_id: str | None = None, + base_url: str | httpx.URL | None = None, timeout: float | Timeout | None | NotGiven = not_given, max_retries: int = DEFAULT_MAX_RETRIES, default_headers: Mapping[str, str] | None = None, @@ -307,41 +304,27 @@ def __init__( ) -> None: """Construct a new async AsyncUnlayer client instance. - This automatically infers the `access_token` argument from the `UNLAYER_ACCESS_TOKEN` environment variable if it is not provided. + This automatically infers the following arguments from their corresponding environment variables if they are not provided: + - `api_key` from `UNLAYER_API_KEY` + - `personal_access_token` from `UNLAYER_PERSONAL_ACCESS_TOKEN` + - `project_id` from `UNLAYER_PROJECT_ID` """ - if access_token is None: - access_token = os.environ.get("UNLAYER_ACCESS_TOKEN") - if access_token is None: - raise UnlayerError( - "The access_token client option must be set either by passing access_token to the client or by setting the UNLAYER_ACCESS_TOKEN environment variable" - ) - self.access_token = access_token - - self._environment = environment - - base_url_env = os.environ.get("UNLAYER_BASE_URL") - if is_given(base_url) and base_url is not None: - # cast required because mypy doesn't understand the type narrowing - base_url = cast("str | httpx.URL", base_url) # pyright: ignore[reportUnnecessaryCast] - elif is_given(environment): - if base_url_env and base_url is not None: - raise ValueError( - "Ambiguous URL; The `UNLAYER_BASE_URL` env var and the `environment` argument are given. If you want to use the environment, you must pass base_url=None", - ) - - try: - base_url = ENVIRONMENTS[environment] - except KeyError as exc: - raise ValueError(f"Unknown environment: {environment}") from exc - elif base_url_env is not None: - base_url = base_url_env - else: - self._environment = environment = "production" - - try: - base_url = ENVIRONMENTS[environment] - except KeyError as exc: - raise ValueError(f"Unknown environment: {environment}") from exc + if api_key is None: + api_key = os.environ.get("UNLAYER_API_KEY") + self.api_key = api_key + + if personal_access_token is None: + personal_access_token = os.environ.get("UNLAYER_PERSONAL_ACCESS_TOKEN") + self.personal_access_token = personal_access_token + + if project_id is None: + project_id = os.environ.get("UNLAYER_PROJECT_ID") + self.project_id = project_id + + if base_url is None: + base_url = os.environ.get("UNLAYER_BASE_URL") + if base_url is None: + base_url = f"https://api.unlayer.com" super().__init__( version=__version__, @@ -394,8 +377,21 @@ def qs(self) -> Querystring: @property @override def auth_headers(self) -> dict[str, str]: - access_token = self.access_token - return {"Authorization": f"Bearer {access_token}"} + return {**self._api_key_auth, **self._personal_access_token_auth} + + @property + def _api_key_auth(self) -> dict[str, str]: + api_key = self.api_key + if api_key is None: + return {} + return {"Authorization": f"Bearer {api_key}"} + + @property + def _personal_access_token_auth(self) -> dict[str, str]: + personal_access_token = self.personal_access_token + if personal_access_token is None: + return {} + return {"Authorization": f"Bearer {personal_access_token}"} @property @override @@ -403,14 +399,25 @@ def default_headers(self) -> dict[str, str | Omit]: return { **super().default_headers, "X-Stainless-Async": f"async:{get_async_library()}", + "X-Project-ID": self.project_id if self.project_id is not None else Omit(), **self._custom_headers, } + @override + def _validate_headers(self, headers: Headers, custom_headers: Headers) -> None: + if headers.get("Authorization") or isinstance(custom_headers.get("Authorization"), Omit): + return + + raise TypeError( + '"Could not resolve authentication method. Expected either api_key or personal_access_token to be set. Or for one of the `Authorization` or `Authorization` headers to be explicitly omitted"' + ) + def copy( self, *, - access_token: str | None = None, - environment: Literal["production", "stage", "qa", "dev"] | None = None, + api_key: str | None = None, + personal_access_token: str | None = None, + project_id: str | None = None, base_url: str | httpx.URL | None = None, timeout: float | Timeout | None | NotGiven = not_given, http_client: httpx.AsyncClient | None = None, @@ -444,9 +451,10 @@ def copy( http_client = http_client or self._client return self.__class__( - access_token=access_token or self.access_token, + api_key=api_key or self.api_key, + personal_access_token=personal_access_token or self.personal_access_token, + project_id=project_id or self.project_id, base_url=base_url or self.base_url, - environment=environment or self._environment, timeout=self.timeout if isinstance(timeout, NotGiven) else timeout, http_client=http_client, max_retries=max_retries if is_given(max_retries) else self.max_retries, diff --git a/src/unlayer/resources/project.py b/src/unlayer/resources/project.py index 6de8cf8..af286c2 100644 --- a/src/unlayer/resources/project.py +++ b/src/unlayer/resources/project.py @@ -5,7 +5,7 @@ import httpx from ..types import project_retrieve_params -from .._types import Body, Query, Headers, NotGiven, not_given +from .._types import Body, Omit, Query, Headers, NotGiven, omit, not_given from .._utils import maybe_transform, async_maybe_transform from .._compat import cached_property from .._resource import SyncAPIResource, AsyncAPIResource @@ -44,7 +44,7 @@ def with_streaming_response(self) -> ProjectResourceWithStreamingResponse: def retrieve( self, *, - project_id: str, + project_id: str | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, @@ -56,7 +56,7 @@ def retrieve( Get project details for the specified project. Args: - project_id: The project ID + project_id: The project ID (required for PAT auth, auto-resolved for API key auth) extra_headers: Send extra headers @@ -102,7 +102,7 @@ def with_streaming_response(self) -> AsyncProjectResourceWithStreamingResponse: async def retrieve( self, *, - project_id: str, + project_id: str | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, @@ -114,7 +114,7 @@ async def retrieve( Get project details for the specified project. Args: - project_id: The project ID + project_id: The project ID (required for PAT auth, auto-resolved for API key auth) extra_headers: Send extra headers diff --git a/src/unlayer/resources/templates.py b/src/unlayer/resources/templates.py index 68389c9..e41c931 100644 --- a/src/unlayer/resources/templates.py +++ b/src/unlayer/resources/templates.py @@ -49,7 +49,7 @@ def retrieve( self, id: str, *, - project_id: str, + project_id: str | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, @@ -61,7 +61,7 @@ def retrieve( Get template by ID. Args: - project_id: The project ID + project_id: The project ID (required for PAT auth, auto-resolved for API key auth) extra_headers: Send extra headers @@ -88,11 +88,11 @@ def retrieve( def list( self, *, - project_id: str, cursor: str | Omit = omit, display_mode: Literal["email", "web", "document"] | Omit = omit, limit: int | Omit = omit, name: str | Omit = omit, + project_id: str | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, @@ -106,8 +106,6 @@ def list( order by update time. Args: - project_id: The project ID to list templates for - cursor: Pagination cursor from previous response display_mode: Filter by template type @@ -116,6 +114,8 @@ def list( name: Filter by name (case-insensitive search) + project_id: The project ID to list templates for + extra_headers: Send extra headers extra_query: Add additional query parameters to the request @@ -134,11 +134,11 @@ def list( timeout=timeout, query=maybe_transform( { - "project_id": project_id, "cursor": cursor, "display_mode": display_mode, "limit": limit, "name": name, + "project_id": project_id, }, template_list_params.TemplateListParams, ), @@ -171,7 +171,7 @@ async def retrieve( self, id: str, *, - project_id: str, + project_id: str | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, @@ -183,7 +183,7 @@ async def retrieve( Get template by ID. Args: - project_id: The project ID + project_id: The project ID (required for PAT auth, auto-resolved for API key auth) extra_headers: Send extra headers @@ -212,11 +212,11 @@ async def retrieve( def list( self, *, - project_id: str, cursor: str | Omit = omit, display_mode: Literal["email", "web", "document"] | Omit = omit, limit: int | Omit = omit, name: str | Omit = omit, + project_id: str | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, @@ -230,8 +230,6 @@ def list( order by update time. Args: - project_id: The project ID to list templates for - cursor: Pagination cursor from previous response display_mode: Filter by template type @@ -240,6 +238,8 @@ def list( name: Filter by name (case-insensitive search) + project_id: The project ID to list templates for + extra_headers: Send extra headers extra_query: Add additional query parameters to the request @@ -258,11 +258,11 @@ def list( timeout=timeout, query=maybe_transform( { - "project_id": project_id, "cursor": cursor, "display_mode": display_mode, "limit": limit, "name": name, + "project_id": project_id, }, template_list_params.TemplateListParams, ), diff --git a/src/unlayer/resources/workspaces.py b/src/unlayer/resources/workspaces.py index 4f4f5cd..3bce476 100644 --- a/src/unlayer/resources/workspaces.py +++ b/src/unlayer/resources/workspaces.py @@ -51,8 +51,10 @@ def retrieve( extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> WorkspaceRetrieveResponse: - """ - Get a specific workspace by ID with its projects. + """Get a specific workspace by ID with its projects. + + Requires a Personal Access + Token (PAT). Args: extra_headers: Send extra headers @@ -83,7 +85,11 @@ def list( extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> WorkspaceListResponse: - """Get all workspaces accessible by the current token.""" + """Get all workspaces accessible by the current token. + + Requires a Personal Access + Token (PAT). + """ return self._get( "/v3/workspaces", options=make_request_options( @@ -124,8 +130,10 @@ async def retrieve( extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> WorkspaceRetrieveResponse: - """ - Get a specific workspace by ID with its projects. + """Get a specific workspace by ID with its projects. + + Requires a Personal Access + Token (PAT). Args: extra_headers: Send extra headers @@ -156,7 +164,11 @@ async def list( extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> WorkspaceListResponse: - """Get all workspaces accessible by the current token.""" + """Get all workspaces accessible by the current token. + + Requires a Personal Access + Token (PAT). + """ return await self._get( "/v3/workspaces", options=make_request_options( diff --git a/src/unlayer/types/project_retrieve_params.py b/src/unlayer/types/project_retrieve_params.py index d2299de..f18748a 100644 --- a/src/unlayer/types/project_retrieve_params.py +++ b/src/unlayer/types/project_retrieve_params.py @@ -2,7 +2,7 @@ from __future__ import annotations -from typing_extensions import Required, Annotated, TypedDict +from typing_extensions import Annotated, TypedDict from .._utils import PropertyInfo @@ -10,5 +10,5 @@ class ProjectRetrieveParams(TypedDict, total=False): - project_id: Required[Annotated[str, PropertyInfo(alias="projectId")]] - """The project ID""" + project_id: Annotated[str, PropertyInfo(alias="projectId")] + """The project ID (required for PAT auth, auto-resolved for API key auth)""" diff --git a/src/unlayer/types/template_list_params.py b/src/unlayer/types/template_list_params.py index afb5c25..79a4a48 100644 --- a/src/unlayer/types/template_list_params.py +++ b/src/unlayer/types/template_list_params.py @@ -2,7 +2,7 @@ from __future__ import annotations -from typing_extensions import Literal, Required, Annotated, TypedDict +from typing_extensions import Literal, Annotated, TypedDict from .._utils import PropertyInfo @@ -10,9 +10,6 @@ class TemplateListParams(TypedDict, total=False): - project_id: Required[Annotated[str, PropertyInfo(alias="projectId")]] - """The project ID to list templates for""" - cursor: str """Pagination cursor from previous response""" @@ -24,3 +21,6 @@ class TemplateListParams(TypedDict, total=False): name: str """Filter by name (case-insensitive search)""" + + project_id: Annotated[str, PropertyInfo(alias="projectId")] + """The project ID to list templates for""" diff --git a/src/unlayer/types/template_retrieve_params.py b/src/unlayer/types/template_retrieve_params.py index c7ca6cf..d5af5fa 100644 --- a/src/unlayer/types/template_retrieve_params.py +++ b/src/unlayer/types/template_retrieve_params.py @@ -2,7 +2,7 @@ from __future__ import annotations -from typing_extensions import Required, Annotated, TypedDict +from typing_extensions import Annotated, TypedDict from .._utils import PropertyInfo @@ -10,5 +10,5 @@ class TemplateRetrieveParams(TypedDict, total=False): - project_id: Required[Annotated[str, PropertyInfo(alias="projectId")]] - """The project ID""" + project_id: Annotated[str, PropertyInfo(alias="projectId")] + """The project ID (required for PAT auth, auto-resolved for API key auth)""" diff --git a/tests/api_resources/test_project.py b/tests/api_resources/test_project.py index 5719de6..58151bf 100644 --- a/tests/api_resources/test_project.py +++ b/tests/api_resources/test_project.py @@ -19,6 +19,11 @@ class TestProject: @parametrize def test_method_retrieve(self, client: Unlayer) -> None: + project = client.project.retrieve() + assert_matches_type(ProjectRetrieveResponse, project, path=["response"]) + + @parametrize + def test_method_retrieve_with_all_params(self, client: Unlayer) -> None: project = client.project.retrieve( project_id="projectId", ) @@ -26,9 +31,7 @@ def test_method_retrieve(self, client: Unlayer) -> None: @parametrize def test_raw_response_retrieve(self, client: Unlayer) -> None: - response = client.project.with_raw_response.retrieve( - project_id="projectId", - ) + response = client.project.with_raw_response.retrieve() assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" @@ -37,9 +40,7 @@ def test_raw_response_retrieve(self, client: Unlayer) -> None: @parametrize def test_streaming_response_retrieve(self, client: Unlayer) -> None: - with client.project.with_streaming_response.retrieve( - project_id="projectId", - ) as response: + with client.project.with_streaming_response.retrieve() as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" @@ -56,6 +57,11 @@ class TestAsyncProject: @parametrize async def test_method_retrieve(self, async_client: AsyncUnlayer) -> None: + project = await async_client.project.retrieve() + assert_matches_type(ProjectRetrieveResponse, project, path=["response"]) + + @parametrize + async def test_method_retrieve_with_all_params(self, async_client: AsyncUnlayer) -> None: project = await async_client.project.retrieve( project_id="projectId", ) @@ -63,9 +69,7 @@ async def test_method_retrieve(self, async_client: AsyncUnlayer) -> None: @parametrize async def test_raw_response_retrieve(self, async_client: AsyncUnlayer) -> None: - response = await async_client.project.with_raw_response.retrieve( - project_id="projectId", - ) + response = await async_client.project.with_raw_response.retrieve() assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" @@ -74,9 +78,7 @@ async def test_raw_response_retrieve(self, async_client: AsyncUnlayer) -> None: @parametrize async def test_streaming_response_retrieve(self, async_client: AsyncUnlayer) -> None: - async with async_client.project.with_streaming_response.retrieve( - project_id="projectId", - ) as response: + async with async_client.project.with_streaming_response.retrieve() as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" diff --git a/tests/api_resources/test_templates.py b/tests/api_resources/test_templates.py index 4a2291b..bea7f32 100644 --- a/tests/api_resources/test_templates.py +++ b/tests/api_resources/test_templates.py @@ -20,6 +20,13 @@ class TestTemplates: @parametrize def test_method_retrieve(self, client: Unlayer) -> None: + template = client.templates.retrieve( + id="id", + ) + assert_matches_type(TemplateRetrieveResponse, template, path=["response"]) + + @parametrize + def test_method_retrieve_with_all_params(self, client: Unlayer) -> None: template = client.templates.retrieve( id="id", project_id="projectId", @@ -30,7 +37,6 @@ def test_method_retrieve(self, client: Unlayer) -> None: def test_raw_response_retrieve(self, client: Unlayer) -> None: response = client.templates.with_raw_response.retrieve( id="id", - project_id="projectId", ) assert response.is_closed is True @@ -42,7 +48,6 @@ def test_raw_response_retrieve(self, client: Unlayer) -> None: def test_streaming_response_retrieve(self, client: Unlayer) -> None: with client.templates.with_streaming_response.retrieve( id="id", - project_id="projectId", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" @@ -57,32 +62,27 @@ def test_path_params_retrieve(self, client: Unlayer) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): client.templates.with_raw_response.retrieve( id="", - project_id="projectId", ) @parametrize def test_method_list(self, client: Unlayer) -> None: - template = client.templates.list( - project_id="projectId", - ) + template = client.templates.list() assert_matches_type(SyncCursorPage[TemplateListResponse], template, path=["response"]) @parametrize def test_method_list_with_all_params(self, client: Unlayer) -> None: template = client.templates.list( - project_id="projectId", cursor="cursor", display_mode="email", limit=1, name="name", + project_id="projectId", ) assert_matches_type(SyncCursorPage[TemplateListResponse], template, path=["response"]) @parametrize def test_raw_response_list(self, client: Unlayer) -> None: - response = client.templates.with_raw_response.list( - project_id="projectId", - ) + response = client.templates.with_raw_response.list() assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" @@ -91,9 +91,7 @@ def test_raw_response_list(self, client: Unlayer) -> None: @parametrize def test_streaming_response_list(self, client: Unlayer) -> None: - with client.templates.with_streaming_response.list( - project_id="projectId", - ) as response: + with client.templates.with_streaming_response.list() as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" @@ -110,6 +108,13 @@ class TestAsyncTemplates: @parametrize async def test_method_retrieve(self, async_client: AsyncUnlayer) -> None: + template = await async_client.templates.retrieve( + id="id", + ) + assert_matches_type(TemplateRetrieveResponse, template, path=["response"]) + + @parametrize + async def test_method_retrieve_with_all_params(self, async_client: AsyncUnlayer) -> None: template = await async_client.templates.retrieve( id="id", project_id="projectId", @@ -120,7 +125,6 @@ async def test_method_retrieve(self, async_client: AsyncUnlayer) -> None: async def test_raw_response_retrieve(self, async_client: AsyncUnlayer) -> None: response = await async_client.templates.with_raw_response.retrieve( id="id", - project_id="projectId", ) assert response.is_closed is True @@ -132,7 +136,6 @@ async def test_raw_response_retrieve(self, async_client: AsyncUnlayer) -> None: async def test_streaming_response_retrieve(self, async_client: AsyncUnlayer) -> None: async with async_client.templates.with_streaming_response.retrieve( id="id", - project_id="projectId", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" @@ -147,32 +150,27 @@ async def test_path_params_retrieve(self, async_client: AsyncUnlayer) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): await async_client.templates.with_raw_response.retrieve( id="", - project_id="projectId", ) @parametrize async def test_method_list(self, async_client: AsyncUnlayer) -> None: - template = await async_client.templates.list( - project_id="projectId", - ) + template = await async_client.templates.list() assert_matches_type(AsyncCursorPage[TemplateListResponse], template, path=["response"]) @parametrize async def test_method_list_with_all_params(self, async_client: AsyncUnlayer) -> None: template = await async_client.templates.list( - project_id="projectId", cursor="cursor", display_mode="email", limit=1, name="name", + project_id="projectId", ) assert_matches_type(AsyncCursorPage[TemplateListResponse], template, path=["response"]) @parametrize async def test_raw_response_list(self, async_client: AsyncUnlayer) -> None: - response = await async_client.templates.with_raw_response.list( - project_id="projectId", - ) + response = await async_client.templates.with_raw_response.list() assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" @@ -181,9 +179,7 @@ async def test_raw_response_list(self, async_client: AsyncUnlayer) -> None: @parametrize async def test_streaming_response_list(self, async_client: AsyncUnlayer) -> None: - async with async_client.templates.with_streaming_response.list( - project_id="projectId", - ) as response: + async with async_client.templates.with_streaming_response.list() as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" diff --git a/tests/conftest.py b/tests/conftest.py index 0fed985..fc6ba41 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -45,7 +45,7 @@ def pytest_collection_modifyitems(items: list[pytest.Function]) -> None: base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") -access_token = "My Access Token" +api_key = "My API Key" @pytest.fixture(scope="session") @@ -54,7 +54,7 @@ def client(request: FixtureRequest) -> Iterator[Unlayer]: if not isinstance(strict, bool): raise TypeError(f"Unexpected fixture parameter type {type(strict)}, expected {bool}") - with Unlayer(base_url=base_url, access_token=access_token, _strict_response_validation=strict) as client: + with Unlayer(base_url=base_url, api_key=api_key, _strict_response_validation=strict) as client: yield client @@ -79,6 +79,6 @@ async def async_client(request: FixtureRequest) -> AsyncIterator[AsyncUnlayer]: raise TypeError(f"Unexpected fixture parameter type {type(param)}, expected bool or dict") async with AsyncUnlayer( - base_url=base_url, access_token=access_token, _strict_response_validation=strict, http_client=http_client + base_url=base_url, api_key=api_key, _strict_response_validation=strict, http_client=http_client ) as client: yield client diff --git a/tests/test_client.py b/tests/test_client.py index 97ed357..93072a2 100644 --- a/tests/test_client.py +++ b/tests/test_client.py @@ -23,7 +23,7 @@ from unlayer._types import Omit from unlayer._utils import asyncify from unlayer._models import BaseModel, FinalRequestOptions -from unlayer._exceptions import UnlayerError, APIStatusError, APITimeoutError, APIResponseValidationError +from unlayer._exceptions import APIStatusError, APITimeoutError, APIResponseValidationError from unlayer._base_client import ( DEFAULT_TIMEOUT, HTTPX_DEFAULT_TIMEOUT, @@ -39,7 +39,7 @@ T = TypeVar("T") base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") -access_token = "My Access Token" +api_key = "My API Key" def _get_params(client: BaseClient[Any, Any]) -> dict[str, str]: @@ -136,9 +136,9 @@ def test_copy(self, client: Unlayer) -> None: copied = client.copy() assert id(copied) != id(client) - copied = client.copy(access_token="another My Access Token") - assert copied.access_token == "another My Access Token" - assert client.access_token == "My Access Token" + copied = client.copy(api_key="another My API Key") + assert copied.api_key == "another My API Key" + assert client.api_key == "My API Key" def test_copy_default_options(self, client: Unlayer) -> None: # options that have a default are overridden correctly @@ -158,10 +158,7 @@ def test_copy_default_options(self, client: Unlayer) -> None: def test_copy_default_headers(self) -> None: client = Unlayer( - base_url=base_url, - access_token=access_token, - _strict_response_validation=True, - default_headers={"X-Foo": "bar"}, + base_url=base_url, api_key=api_key, _strict_response_validation=True, default_headers={"X-Foo": "bar"} ) assert client.default_headers["X-Foo"] == "bar" @@ -196,7 +193,7 @@ def test_copy_default_headers(self) -> None: def test_copy_default_query(self) -> None: client = Unlayer( - base_url=base_url, access_token=access_token, _strict_response_validation=True, default_query={"foo": "bar"} + base_url=base_url, api_key=api_key, _strict_response_validation=True, default_query={"foo": "bar"} ) assert _get_params(client)["foo"] == "bar" @@ -321,9 +318,7 @@ def test_request_timeout(self, client: Unlayer) -> None: assert timeout == httpx.Timeout(100.0) def test_client_timeout_option(self) -> None: - client = Unlayer( - base_url=base_url, access_token=access_token, _strict_response_validation=True, timeout=httpx.Timeout(0) - ) + client = Unlayer(base_url=base_url, api_key=api_key, _strict_response_validation=True, timeout=httpx.Timeout(0)) request = client._build_request(FinalRequestOptions(method="get", url="/foo")) timeout = httpx.Timeout(**request.extensions["timeout"]) # type: ignore @@ -335,7 +330,7 @@ def test_http_client_timeout_option(self) -> None: # custom timeout given to the httpx client should be used with httpx.Client(timeout=None) as http_client: client = Unlayer( - base_url=base_url, access_token=access_token, _strict_response_validation=True, http_client=http_client + base_url=base_url, api_key=api_key, _strict_response_validation=True, http_client=http_client ) request = client._build_request(FinalRequestOptions(method="get", url="/foo")) @@ -347,7 +342,7 @@ def test_http_client_timeout_option(self) -> None: # no timeout given to the httpx client should not use the httpx default with httpx.Client() as http_client: client = Unlayer( - base_url=base_url, access_token=access_token, _strict_response_validation=True, http_client=http_client + base_url=base_url, api_key=api_key, _strict_response_validation=True, http_client=http_client ) request = client._build_request(FinalRequestOptions(method="get", url="/foo")) @@ -359,7 +354,7 @@ def test_http_client_timeout_option(self) -> None: # explicitly passing the default timeout currently results in it being ignored with httpx.Client(timeout=HTTPX_DEFAULT_TIMEOUT) as http_client: client = Unlayer( - base_url=base_url, access_token=access_token, _strict_response_validation=True, http_client=http_client + base_url=base_url, api_key=api_key, _strict_response_validation=True, http_client=http_client ) request = client._build_request(FinalRequestOptions(method="get", url="/foo")) @@ -373,17 +368,14 @@ async def test_invalid_http_client(self) -> None: async with httpx.AsyncClient() as http_client: Unlayer( base_url=base_url, - access_token=access_token, + api_key=api_key, _strict_response_validation=True, http_client=cast(Any, http_client), ) def test_default_headers_option(self) -> None: test_client = Unlayer( - base_url=base_url, - access_token=access_token, - _strict_response_validation=True, - default_headers={"X-Foo": "bar"}, + base_url=base_url, api_key=api_key, _strict_response_validation=True, default_headers={"X-Foo": "bar"} ) request = test_client._build_request(FinalRequestOptions(method="get", url="/foo")) assert request.headers.get("x-foo") == "bar" @@ -391,7 +383,7 @@ def test_default_headers_option(self) -> None: test_client2 = Unlayer( base_url=base_url, - access_token=access_token, + api_key=api_key, _strict_response_validation=True, default_headers={ "X-Foo": "stainless", @@ -406,21 +398,27 @@ def test_default_headers_option(self) -> None: test_client2.close() def test_validate_headers(self) -> None: - client = Unlayer(base_url=base_url, access_token=access_token, _strict_response_validation=True) + client = Unlayer(base_url=base_url, api_key=api_key, _strict_response_validation=True) request = client._build_request(FinalRequestOptions(method="get", url="/foo")) - assert request.headers.get("Authorization") == f"Bearer {access_token}" + assert request.headers.get("Authorization") == f"Bearer {api_key}" + + with update_env(**{"UNLAYER_API_KEY": Omit()}): + client2 = Unlayer(base_url=base_url, api_key=None, _strict_response_validation=True) - with pytest.raises(UnlayerError): - with update_env(**{"UNLAYER_ACCESS_TOKEN": Omit()}): - client2 = Unlayer(base_url=base_url, access_token=None, _strict_response_validation=True) - _ = client2 + with pytest.raises( + TypeError, + match="Could not resolve authentication method. Expected either api_key or personal_access_token to be set. Or for one of the `Authorization` or `Authorization` headers to be explicitly omitted", + ): + client2._build_request(FinalRequestOptions(method="get", url="/foo")) + + request2 = client2._build_request( + FinalRequestOptions(method="get", url="/foo", headers={"Authorization": Omit()}) + ) + assert request2.headers.get("Authorization") is None def test_default_query_option(self) -> None: client = Unlayer( - base_url=base_url, - access_token=access_token, - _strict_response_validation=True, - default_query={"query_param": "bar"}, + base_url=base_url, api_key=api_key, _strict_response_validation=True, default_query={"query_param": "bar"} ) request = client._build_request(FinalRequestOptions(method="get", url="/foo")) url = httpx.URL(request.url) @@ -592,7 +590,7 @@ def mock_handler(request: httpx.Request) -> httpx.Response: with Unlayer( base_url=base_url, - access_token=access_token, + api_key=api_key, _strict_response_validation=True, http_client=httpx.Client(transport=MockTransport(handler=mock_handler)), ) as client: @@ -686,9 +684,7 @@ class Model(BaseModel): assert response.foo == 2 def test_base_url_setter(self) -> None: - client = Unlayer( - base_url="https://example.com/from_init", access_token=access_token, _strict_response_validation=True - ) + client = Unlayer(base_url="https://example.com/from_init", api_key=api_key, _strict_response_validation=True) assert client.base_url == "https://example.com/from_init/" client.base_url = "https://example.com/from_setter" # type: ignore[assignment] @@ -699,32 +695,16 @@ def test_base_url_setter(self) -> None: def test_base_url_env(self) -> None: with update_env(UNLAYER_BASE_URL="http://localhost:5000/from/env"): - client = Unlayer(access_token=access_token, _strict_response_validation=True) + client = Unlayer(api_key=api_key, _strict_response_validation=True) assert client.base_url == "http://localhost:5000/from/env/" - # explicit environment arg requires explicitness - with update_env(UNLAYER_BASE_URL="http://localhost:5000/from/env"): - with pytest.raises(ValueError, match=r"you must pass base_url=None"): - Unlayer(access_token=access_token, _strict_response_validation=True, environment="production") - - client = Unlayer( - base_url=None, access_token=access_token, _strict_response_validation=True, environment="production" - ) - assert str(client.base_url).startswith("https://api.unlayer.com") - - client.close() - @pytest.mark.parametrize( "client", [ + Unlayer(base_url="http://localhost:5000/custom/path/", api_key=api_key, _strict_response_validation=True), Unlayer( base_url="http://localhost:5000/custom/path/", - access_token=access_token, - _strict_response_validation=True, - ), - Unlayer( - base_url="http://localhost:5000/custom/path/", - access_token=access_token, + api_key=api_key, _strict_response_validation=True, http_client=httpx.Client(), ), @@ -745,14 +725,10 @@ def test_base_url_trailing_slash(self, client: Unlayer) -> None: @pytest.mark.parametrize( "client", [ + Unlayer(base_url="http://localhost:5000/custom/path/", api_key=api_key, _strict_response_validation=True), Unlayer( base_url="http://localhost:5000/custom/path/", - access_token=access_token, - _strict_response_validation=True, - ), - Unlayer( - base_url="http://localhost:5000/custom/path/", - access_token=access_token, + api_key=api_key, _strict_response_validation=True, http_client=httpx.Client(), ), @@ -773,14 +749,10 @@ def test_base_url_no_trailing_slash(self, client: Unlayer) -> None: @pytest.mark.parametrize( "client", [ + Unlayer(base_url="http://localhost:5000/custom/path/", api_key=api_key, _strict_response_validation=True), Unlayer( base_url="http://localhost:5000/custom/path/", - access_token=access_token, - _strict_response_validation=True, - ), - Unlayer( - base_url="http://localhost:5000/custom/path/", - access_token=access_token, + api_key=api_key, _strict_response_validation=True, http_client=httpx.Client(), ), @@ -799,7 +771,7 @@ def test_absolute_request_url(self, client: Unlayer) -> None: client.close() def test_copied_client_does_not_close_http(self) -> None: - test_client = Unlayer(base_url=base_url, access_token=access_token, _strict_response_validation=True) + test_client = Unlayer(base_url=base_url, api_key=api_key, _strict_response_validation=True) assert not test_client.is_closed() copied = test_client.copy() @@ -810,7 +782,7 @@ def test_copied_client_does_not_close_http(self) -> None: assert not test_client.is_closed() def test_client_context_manager(self) -> None: - test_client = Unlayer(base_url=base_url, access_token=access_token, _strict_response_validation=True) + test_client = Unlayer(base_url=base_url, api_key=api_key, _strict_response_validation=True) with test_client as c2: assert c2 is test_client assert not c2.is_closed() @@ -831,12 +803,7 @@ class Model(BaseModel): def test_client_max_retries_validation(self) -> None: with pytest.raises(TypeError, match=r"max_retries cannot be None"): - Unlayer( - base_url=base_url, - access_token=access_token, - _strict_response_validation=True, - max_retries=cast(Any, None), - ) + Unlayer(base_url=base_url, api_key=api_key, _strict_response_validation=True, max_retries=cast(Any, None)) @pytest.mark.respx(base_url=base_url) def test_received_text_for_expected_json(self, respx_mock: MockRouter) -> None: @@ -845,12 +812,12 @@ class Model(BaseModel): respx_mock.get("/foo").mock(return_value=httpx.Response(200, text="my-custom-format")) - strict_client = Unlayer(base_url=base_url, access_token=access_token, _strict_response_validation=True) + strict_client = Unlayer(base_url=base_url, api_key=api_key, _strict_response_validation=True) with pytest.raises(APIResponseValidationError): strict_client.get("/foo", cast_to=Model) - non_strict_client = Unlayer(base_url=base_url, access_token=access_token, _strict_response_validation=False) + non_strict_client = Unlayer(base_url=base_url, api_key=api_key, _strict_response_validation=False) response = non_strict_client.get("/foo", cast_to=Model) assert isinstance(response, str) # type: ignore[unreachable] @@ -894,7 +861,7 @@ def test_retrying_timeout_errors_doesnt_leak(self, respx_mock: MockRouter, clien respx_mock.get("/v3/project").mock(side_effect=httpx.TimeoutException("Test timeout error")) with pytest.raises(APITimeoutError): - client.project.with_streaming_response.retrieve(project_id="projectId").__enter__() + client.project.with_streaming_response.retrieve().__enter__() assert _get_open_connections(client) == 0 @@ -904,7 +871,7 @@ def test_retrying_status_errors_doesnt_leak(self, respx_mock: MockRouter, client respx_mock.get("/v3/project").mock(return_value=httpx.Response(500)) with pytest.raises(APIStatusError): - client.project.with_streaming_response.retrieve(project_id="projectId").__enter__() + client.project.with_streaming_response.retrieve().__enter__() assert _get_open_connections(client) == 0 @pytest.mark.parametrize("failures_before_success", [0, 2, 4]) @@ -933,7 +900,7 @@ def retry_handler(_request: httpx.Request) -> httpx.Response: respx_mock.get("/v3/project").mock(side_effect=retry_handler) - response = client.project.with_raw_response.retrieve(project_id="projectId") + response = client.project.with_raw_response.retrieve() assert response.retries_taken == failures_before_success assert int(response.http_request.headers.get("x-stainless-retry-count")) == failures_before_success @@ -957,9 +924,7 @@ def retry_handler(_request: httpx.Request) -> httpx.Response: respx_mock.get("/v3/project").mock(side_effect=retry_handler) - response = client.project.with_raw_response.retrieve( - project_id="projectId", extra_headers={"x-stainless-retry-count": Omit()} - ) + response = client.project.with_raw_response.retrieve(extra_headers={"x-stainless-retry-count": Omit()}) assert len(response.http_request.headers.get_list("x-stainless-retry-count")) == 0 @@ -982,9 +947,7 @@ def retry_handler(_request: httpx.Request) -> httpx.Response: respx_mock.get("/v3/project").mock(side_effect=retry_handler) - response = client.project.with_raw_response.retrieve( - project_id="projectId", extra_headers={"x-stainless-retry-count": "42"} - ) + response = client.project.with_raw_response.retrieve(extra_headers={"x-stainless-retry-count": "42"}) assert response.http_request.headers.get("x-stainless-retry-count") == "42" @@ -1061,9 +1024,9 @@ def test_copy(self, async_client: AsyncUnlayer) -> None: copied = async_client.copy() assert id(copied) != id(async_client) - copied = async_client.copy(access_token="another My Access Token") - assert copied.access_token == "another My Access Token" - assert async_client.access_token == "My Access Token" + copied = async_client.copy(api_key="another My API Key") + assert copied.api_key == "another My API Key" + assert async_client.api_key == "My API Key" def test_copy_default_options(self, async_client: AsyncUnlayer) -> None: # options that have a default are overridden correctly @@ -1083,10 +1046,7 @@ def test_copy_default_options(self, async_client: AsyncUnlayer) -> None: async def test_copy_default_headers(self) -> None: client = AsyncUnlayer( - base_url=base_url, - access_token=access_token, - _strict_response_validation=True, - default_headers={"X-Foo": "bar"}, + base_url=base_url, api_key=api_key, _strict_response_validation=True, default_headers={"X-Foo": "bar"} ) assert client.default_headers["X-Foo"] == "bar" @@ -1121,7 +1081,7 @@ async def test_copy_default_headers(self) -> None: async def test_copy_default_query(self) -> None: client = AsyncUnlayer( - base_url=base_url, access_token=access_token, _strict_response_validation=True, default_query={"foo": "bar"} + base_url=base_url, api_key=api_key, _strict_response_validation=True, default_query={"foo": "bar"} ) assert _get_params(client)["foo"] == "bar" @@ -1249,7 +1209,7 @@ async def test_request_timeout(self, async_client: AsyncUnlayer) -> None: async def test_client_timeout_option(self) -> None: client = AsyncUnlayer( - base_url=base_url, access_token=access_token, _strict_response_validation=True, timeout=httpx.Timeout(0) + base_url=base_url, api_key=api_key, _strict_response_validation=True, timeout=httpx.Timeout(0) ) request = client._build_request(FinalRequestOptions(method="get", url="/foo")) @@ -1262,7 +1222,7 @@ async def test_http_client_timeout_option(self) -> None: # custom timeout given to the httpx client should be used async with httpx.AsyncClient(timeout=None) as http_client: client = AsyncUnlayer( - base_url=base_url, access_token=access_token, _strict_response_validation=True, http_client=http_client + base_url=base_url, api_key=api_key, _strict_response_validation=True, http_client=http_client ) request = client._build_request(FinalRequestOptions(method="get", url="/foo")) @@ -1274,7 +1234,7 @@ async def test_http_client_timeout_option(self) -> None: # no timeout given to the httpx client should not use the httpx default async with httpx.AsyncClient() as http_client: client = AsyncUnlayer( - base_url=base_url, access_token=access_token, _strict_response_validation=True, http_client=http_client + base_url=base_url, api_key=api_key, _strict_response_validation=True, http_client=http_client ) request = client._build_request(FinalRequestOptions(method="get", url="/foo")) @@ -1286,7 +1246,7 @@ async def test_http_client_timeout_option(self) -> None: # explicitly passing the default timeout currently results in it being ignored async with httpx.AsyncClient(timeout=HTTPX_DEFAULT_TIMEOUT) as http_client: client = AsyncUnlayer( - base_url=base_url, access_token=access_token, _strict_response_validation=True, http_client=http_client + base_url=base_url, api_key=api_key, _strict_response_validation=True, http_client=http_client ) request = client._build_request(FinalRequestOptions(method="get", url="/foo")) @@ -1300,17 +1260,14 @@ def test_invalid_http_client(self) -> None: with httpx.Client() as http_client: AsyncUnlayer( base_url=base_url, - access_token=access_token, + api_key=api_key, _strict_response_validation=True, http_client=cast(Any, http_client), ) async def test_default_headers_option(self) -> None: test_client = AsyncUnlayer( - base_url=base_url, - access_token=access_token, - _strict_response_validation=True, - default_headers={"X-Foo": "bar"}, + base_url=base_url, api_key=api_key, _strict_response_validation=True, default_headers={"X-Foo": "bar"} ) request = test_client._build_request(FinalRequestOptions(method="get", url="/foo")) assert request.headers.get("x-foo") == "bar" @@ -1318,7 +1275,7 @@ async def test_default_headers_option(self) -> None: test_client2 = AsyncUnlayer( base_url=base_url, - access_token=access_token, + api_key=api_key, _strict_response_validation=True, default_headers={ "X-Foo": "stainless", @@ -1333,21 +1290,27 @@ async def test_default_headers_option(self) -> None: await test_client2.close() def test_validate_headers(self) -> None: - client = AsyncUnlayer(base_url=base_url, access_token=access_token, _strict_response_validation=True) + client = AsyncUnlayer(base_url=base_url, api_key=api_key, _strict_response_validation=True) request = client._build_request(FinalRequestOptions(method="get", url="/foo")) - assert request.headers.get("Authorization") == f"Bearer {access_token}" + assert request.headers.get("Authorization") == f"Bearer {api_key}" - with pytest.raises(UnlayerError): - with update_env(**{"UNLAYER_ACCESS_TOKEN": Omit()}): - client2 = AsyncUnlayer(base_url=base_url, access_token=None, _strict_response_validation=True) - _ = client2 + with update_env(**{"UNLAYER_API_KEY": Omit()}): + client2 = AsyncUnlayer(base_url=base_url, api_key=None, _strict_response_validation=True) + + with pytest.raises( + TypeError, + match="Could not resolve authentication method. Expected either api_key or personal_access_token to be set. Or for one of the `Authorization` or `Authorization` headers to be explicitly omitted", + ): + client2._build_request(FinalRequestOptions(method="get", url="/foo")) + + request2 = client2._build_request( + FinalRequestOptions(method="get", url="/foo", headers={"Authorization": Omit()}) + ) + assert request2.headers.get("Authorization") is None async def test_default_query_option(self) -> None: client = AsyncUnlayer( - base_url=base_url, - access_token=access_token, - _strict_response_validation=True, - default_query={"query_param": "bar"}, + base_url=base_url, api_key=api_key, _strict_response_validation=True, default_query={"query_param": "bar"} ) request = client._build_request(FinalRequestOptions(method="get", url="/foo")) url = httpx.URL(request.url) @@ -1519,7 +1482,7 @@ async def mock_handler(request: httpx.Request) -> httpx.Response: async with AsyncUnlayer( base_url=base_url, - access_token=access_token, + api_key=api_key, _strict_response_validation=True, http_client=httpx.AsyncClient(transport=MockTransport(handler=mock_handler)), ) as client: @@ -1618,7 +1581,7 @@ class Model(BaseModel): async def test_base_url_setter(self) -> None: client = AsyncUnlayer( - base_url="https://example.com/from_init", access_token=access_token, _strict_response_validation=True + base_url="https://example.com/from_init", api_key=api_key, _strict_response_validation=True ) assert client.base_url == "https://example.com/from_init/" @@ -1630,32 +1593,18 @@ async def test_base_url_setter(self) -> None: async def test_base_url_env(self) -> None: with update_env(UNLAYER_BASE_URL="http://localhost:5000/from/env"): - client = AsyncUnlayer(access_token=access_token, _strict_response_validation=True) + client = AsyncUnlayer(api_key=api_key, _strict_response_validation=True) assert client.base_url == "http://localhost:5000/from/env/" - # explicit environment arg requires explicitness - with update_env(UNLAYER_BASE_URL="http://localhost:5000/from/env"): - with pytest.raises(ValueError, match=r"you must pass base_url=None"): - AsyncUnlayer(access_token=access_token, _strict_response_validation=True, environment="production") - - client = AsyncUnlayer( - base_url=None, access_token=access_token, _strict_response_validation=True, environment="production" - ) - assert str(client.base_url).startswith("https://api.unlayer.com") - - await client.close() - @pytest.mark.parametrize( "client", [ AsyncUnlayer( - base_url="http://localhost:5000/custom/path/", - access_token=access_token, - _strict_response_validation=True, + base_url="http://localhost:5000/custom/path/", api_key=api_key, _strict_response_validation=True ), AsyncUnlayer( base_url="http://localhost:5000/custom/path/", - access_token=access_token, + api_key=api_key, _strict_response_validation=True, http_client=httpx.AsyncClient(), ), @@ -1677,13 +1626,11 @@ async def test_base_url_trailing_slash(self, client: AsyncUnlayer) -> None: "client", [ AsyncUnlayer( - base_url="http://localhost:5000/custom/path/", - access_token=access_token, - _strict_response_validation=True, + base_url="http://localhost:5000/custom/path/", api_key=api_key, _strict_response_validation=True ), AsyncUnlayer( base_url="http://localhost:5000/custom/path/", - access_token=access_token, + api_key=api_key, _strict_response_validation=True, http_client=httpx.AsyncClient(), ), @@ -1705,13 +1652,11 @@ async def test_base_url_no_trailing_slash(self, client: AsyncUnlayer) -> None: "client", [ AsyncUnlayer( - base_url="http://localhost:5000/custom/path/", - access_token=access_token, - _strict_response_validation=True, + base_url="http://localhost:5000/custom/path/", api_key=api_key, _strict_response_validation=True ), AsyncUnlayer( base_url="http://localhost:5000/custom/path/", - access_token=access_token, + api_key=api_key, _strict_response_validation=True, http_client=httpx.AsyncClient(), ), @@ -1730,7 +1675,7 @@ async def test_absolute_request_url(self, client: AsyncUnlayer) -> None: await client.close() async def test_copied_client_does_not_close_http(self) -> None: - test_client = AsyncUnlayer(base_url=base_url, access_token=access_token, _strict_response_validation=True) + test_client = AsyncUnlayer(base_url=base_url, api_key=api_key, _strict_response_validation=True) assert not test_client.is_closed() copied = test_client.copy() @@ -1742,7 +1687,7 @@ async def test_copied_client_does_not_close_http(self) -> None: assert not test_client.is_closed() async def test_client_context_manager(self) -> None: - test_client = AsyncUnlayer(base_url=base_url, access_token=access_token, _strict_response_validation=True) + test_client = AsyncUnlayer(base_url=base_url, api_key=api_key, _strict_response_validation=True) async with test_client as c2: assert c2 is test_client assert not c2.is_closed() @@ -1764,10 +1709,7 @@ class Model(BaseModel): async def test_client_max_retries_validation(self) -> None: with pytest.raises(TypeError, match=r"max_retries cannot be None"): AsyncUnlayer( - base_url=base_url, - access_token=access_token, - _strict_response_validation=True, - max_retries=cast(Any, None), + base_url=base_url, api_key=api_key, _strict_response_validation=True, max_retries=cast(Any, None) ) @pytest.mark.respx(base_url=base_url) @@ -1777,14 +1719,12 @@ class Model(BaseModel): respx_mock.get("/foo").mock(return_value=httpx.Response(200, text="my-custom-format")) - strict_client = AsyncUnlayer(base_url=base_url, access_token=access_token, _strict_response_validation=True) + strict_client = AsyncUnlayer(base_url=base_url, api_key=api_key, _strict_response_validation=True) with pytest.raises(APIResponseValidationError): await strict_client.get("/foo", cast_to=Model) - non_strict_client = AsyncUnlayer( - base_url=base_url, access_token=access_token, _strict_response_validation=False - ) + non_strict_client = AsyncUnlayer(base_url=base_url, api_key=api_key, _strict_response_validation=False) response = await non_strict_client.get("/foo", cast_to=Model) assert isinstance(response, str) # type: ignore[unreachable] @@ -1830,7 +1770,7 @@ async def test_retrying_timeout_errors_doesnt_leak( respx_mock.get("/v3/project").mock(side_effect=httpx.TimeoutException("Test timeout error")) with pytest.raises(APITimeoutError): - await async_client.project.with_streaming_response.retrieve(project_id="projectId").__aenter__() + await async_client.project.with_streaming_response.retrieve().__aenter__() assert _get_open_connections(async_client) == 0 @@ -1840,7 +1780,7 @@ async def test_retrying_status_errors_doesnt_leak(self, respx_mock: MockRouter, respx_mock.get("/v3/project").mock(return_value=httpx.Response(500)) with pytest.raises(APIStatusError): - await async_client.project.with_streaming_response.retrieve(project_id="projectId").__aenter__() + await async_client.project.with_streaming_response.retrieve().__aenter__() assert _get_open_connections(async_client) == 0 @pytest.mark.parametrize("failures_before_success", [0, 2, 4]) @@ -1869,7 +1809,7 @@ def retry_handler(_request: httpx.Request) -> httpx.Response: respx_mock.get("/v3/project").mock(side_effect=retry_handler) - response = await client.project.with_raw_response.retrieve(project_id="projectId") + response = await client.project.with_raw_response.retrieve() assert response.retries_taken == failures_before_success assert int(response.http_request.headers.get("x-stainless-retry-count")) == failures_before_success @@ -1893,9 +1833,7 @@ def retry_handler(_request: httpx.Request) -> httpx.Response: respx_mock.get("/v3/project").mock(side_effect=retry_handler) - response = await client.project.with_raw_response.retrieve( - project_id="projectId", extra_headers={"x-stainless-retry-count": Omit()} - ) + response = await client.project.with_raw_response.retrieve(extra_headers={"x-stainless-retry-count": Omit()}) assert len(response.http_request.headers.get_list("x-stainless-retry-count")) == 0 @@ -1918,9 +1856,7 @@ def retry_handler(_request: httpx.Request) -> httpx.Response: respx_mock.get("/v3/project").mock(side_effect=retry_handler) - response = await client.project.with_raw_response.retrieve( - project_id="projectId", extra_headers={"x-stainless-retry-count": "42"} - ) + response = await client.project.with_raw_response.retrieve(extra_headers={"x-stainless-retry-count": "42"}) assert response.http_request.headers.get("x-stainless-retry-count") == "42" From 018d56516811978b88fc73ffade3a85c042404be Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Fri, 20 Feb 2026 19:01:43 +0000 Subject: [PATCH 39/62] feat(api): api update --- .stats.yml | 6 +- README.md | 34 ++++--- api.md | 4 +- src/unlayer/_client.py | 40 ++++---- src/unlayer/resources/__init__.py | 26 +++--- .../resources/{project.py => projects.py} | 92 ++++++++----------- src/unlayer/types/__init__.py | 1 - src/unlayer/types/project_retrieve_params.py | 14 --- .../{test_project.py => test_projects.py} | 52 +++++++---- tests/test_client.py | 74 +++------------ 10 files changed, 144 insertions(+), 199 deletions(-) rename src/unlayer/resources/{project.py => projects.py} (60%) delete mode 100644 src/unlayer/types/project_retrieve_params.py rename tests/api_resources/{test_project.py => test_projects.py} (69%) diff --git a/.stats.yml b/.stats.yml index 2fb1d6b..3004020 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 7 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/unlayer%2Funlayer-14707e371ca64ee082f425e10f8bd4b7d9e2eeb28d6e69daad66902abb1b8b6b.yml -openapi_spec_hash: 8198d0442f4736109bf6c49a5a8697ab -config_hash: 144077318a24cfbe9ce6da795a628a80 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/unlayer%2Funlayer-48f00d1c04c23fb4d1cb7cf4af4f56b0c920d758c1f06e06e5373e5b15e9c27d.yml +openapi_spec_hash: 6ee2a94bb9840aceb4a6161c724ce46c +config_hash: c8d97d58d67dad9eeb65eb58fc781724 diff --git a/README.md b/README.md index 2974eba..f4c8537 100644 --- a/README.md +++ b/README.md @@ -32,10 +32,11 @@ client = Unlayer( api_key=os.environ.get("UNLAYER_API_KEY"), # This is the default and can be omitted ) -project = client.project.retrieve( +page = client.templates.list( + limit=10, project_id="your-project-id", ) -print(project.data) +print(page.data) ``` While you can provide an `api_key` keyword argument, @@ -58,10 +59,11 @@ client = AsyncUnlayer( async def main() -> None: - project = await client.project.retrieve( + page = await client.templates.list( + limit=10, project_id="your-project-id", ) - print(project.data) + print(page.data) asyncio.run(main()) @@ -94,10 +96,11 @@ async def main() -> None: api_key=os.environ.get("UNLAYER_API_KEY"), # This is the default and can be omitted http_client=DefaultAioHttpClient(), ) as client: - project = await client.project.retrieve( + page = await client.templates.list( + limit=10, project_id="your-project-id", ) - print(project.data) + print(page.data) asyncio.run(main()) @@ -218,7 +221,8 @@ from unlayer import Unlayer client = Unlayer() try: - client.project.retrieve( + client.templates.list( + limit=10, project_id="your-project-id", ) except unlayer.APIConnectionError as e: @@ -263,7 +267,8 @@ client = Unlayer( ) # Or, configure per-request: -client.with_options(max_retries=5).project.retrieve( +client.with_options(max_retries=5).templates.list( + limit=10, project_id="your-project-id", ) ``` @@ -288,7 +293,8 @@ client = Unlayer( ) # Override per-request: -client.with_options(timeout=5.0).project.retrieve( +client.with_options(timeout=5.0).templates.list( + limit=10, project_id="your-project-id", ) ``` @@ -331,13 +337,14 @@ The "raw" Response object can be accessed by prefixing `.with_raw_response.` to from unlayer import Unlayer client = Unlayer() -response = client.project.with_raw_response.retrieve( +response = client.templates.with_raw_response.list( + limit=10, project_id="your-project-id", ) print(response.headers.get('X-My-Header')) -project = response.parse() # get the object that `project.retrieve()` would have returned -print(project.data) +template = response.parse() # get the object that `templates.list()` would have returned +print(template.id) ``` These methods return an [`APIResponse`](https://github.com/unlayer/unlayer-python/tree/main/src/unlayer/_response.py) object. @@ -351,7 +358,8 @@ The above interface eagerly reads the full response body when you make the reque To stream the response body, use `.with_streaming_response` instead, which requires a context manager and only reads the response body once you call `.read()`, `.text()`, `.json()`, `.iter_bytes()`, `.iter_text()`, `.iter_lines()` or `.parse()`. In the async client, these are async methods. ```python -with client.project.with_streaming_response.retrieve( +with client.templates.with_streaming_response.list( + limit=10, project_id="your-project-id", ) as response: print(response.headers.get("X-My-Header")) diff --git a/api.md b/api.md index 54f0d00..4166417 100644 --- a/api.md +++ b/api.md @@ -24,7 +24,7 @@ Methods: - client.convert.simple_to_full.create(\*\*params) -> SimpleToFullCreateResponse -# Project +# Projects Types: @@ -34,7 +34,7 @@ from unlayer.types import ProjectRetrieveResponse Methods: -- client.project.retrieve(\*\*params) -> ProjectRetrieveResponse +- client.projects.retrieve(id) -> ProjectRetrieveResponse # Templates diff --git a/src/unlayer/_client.py b/src/unlayer/_client.py index 4c34931..08f7fef 100644 --- a/src/unlayer/_client.py +++ b/src/unlayer/_client.py @@ -32,8 +32,8 @@ ) if TYPE_CHECKING: - from .resources import convert, project, templates, workspaces - from .resources.project import ProjectResource, AsyncProjectResource + from .resources import convert, projects, templates, workspaces + from .resources.projects import ProjectsResource, AsyncProjectsResource from .resources.templates import TemplatesResource, AsyncTemplatesResource from .resources.workspaces import WorkspacesResource, AsyncWorkspacesResource from .resources.convert.convert import ConvertResource, AsyncConvertResource @@ -114,10 +114,10 @@ def convert(self) -> ConvertResource: return ConvertResource(self) @cached_property - def project(self) -> ProjectResource: - from .resources.project import ProjectResource + def projects(self) -> ProjectsResource: + from .resources.projects import ProjectsResource - return ProjectResource(self) + return ProjectsResource(self) @cached_property def templates(self) -> TemplatesResource: @@ -344,10 +344,10 @@ def convert(self) -> AsyncConvertResource: return AsyncConvertResource(self) @cached_property - def project(self) -> AsyncProjectResource: - from .resources.project import AsyncProjectResource + def projects(self) -> AsyncProjectsResource: + from .resources.projects import AsyncProjectsResource - return AsyncProjectResource(self) + return AsyncProjectsResource(self) @cached_property def templates(self) -> AsyncTemplatesResource: @@ -514,10 +514,10 @@ def convert(self) -> convert.ConvertResourceWithRawResponse: return ConvertResourceWithRawResponse(self._client.convert) @cached_property - def project(self) -> project.ProjectResourceWithRawResponse: - from .resources.project import ProjectResourceWithRawResponse + def projects(self) -> projects.ProjectsResourceWithRawResponse: + from .resources.projects import ProjectsResourceWithRawResponse - return ProjectResourceWithRawResponse(self._client.project) + return ProjectsResourceWithRawResponse(self._client.projects) @cached_property def templates(self) -> templates.TemplatesResourceWithRawResponse: @@ -545,10 +545,10 @@ def convert(self) -> convert.AsyncConvertResourceWithRawResponse: return AsyncConvertResourceWithRawResponse(self._client.convert) @cached_property - def project(self) -> project.AsyncProjectResourceWithRawResponse: - from .resources.project import AsyncProjectResourceWithRawResponse + def projects(self) -> projects.AsyncProjectsResourceWithRawResponse: + from .resources.projects import AsyncProjectsResourceWithRawResponse - return AsyncProjectResourceWithRawResponse(self._client.project) + return AsyncProjectsResourceWithRawResponse(self._client.projects) @cached_property def templates(self) -> templates.AsyncTemplatesResourceWithRawResponse: @@ -576,10 +576,10 @@ def convert(self) -> convert.ConvertResourceWithStreamingResponse: return ConvertResourceWithStreamingResponse(self._client.convert) @cached_property - def project(self) -> project.ProjectResourceWithStreamingResponse: - from .resources.project import ProjectResourceWithStreamingResponse + def projects(self) -> projects.ProjectsResourceWithStreamingResponse: + from .resources.projects import ProjectsResourceWithStreamingResponse - return ProjectResourceWithStreamingResponse(self._client.project) + return ProjectsResourceWithStreamingResponse(self._client.projects) @cached_property def templates(self) -> templates.TemplatesResourceWithStreamingResponse: @@ -607,10 +607,10 @@ def convert(self) -> convert.AsyncConvertResourceWithStreamingResponse: return AsyncConvertResourceWithStreamingResponse(self._client.convert) @cached_property - def project(self) -> project.AsyncProjectResourceWithStreamingResponse: - from .resources.project import AsyncProjectResourceWithStreamingResponse + def projects(self) -> projects.AsyncProjectsResourceWithStreamingResponse: + from .resources.projects import AsyncProjectsResourceWithStreamingResponse - return AsyncProjectResourceWithStreamingResponse(self._client.project) + return AsyncProjectsResourceWithStreamingResponse(self._client.projects) @cached_property def templates(self) -> templates.AsyncTemplatesResourceWithStreamingResponse: diff --git a/src/unlayer/resources/__init__.py b/src/unlayer/resources/__init__.py index 347e4de..1de5370 100644 --- a/src/unlayer/resources/__init__.py +++ b/src/unlayer/resources/__init__.py @@ -8,13 +8,13 @@ ConvertResourceWithStreamingResponse, AsyncConvertResourceWithStreamingResponse, ) -from .project import ( - ProjectResource, - AsyncProjectResource, - ProjectResourceWithRawResponse, - AsyncProjectResourceWithRawResponse, - ProjectResourceWithStreamingResponse, - AsyncProjectResourceWithStreamingResponse, +from .projects import ( + ProjectsResource, + AsyncProjectsResource, + ProjectsResourceWithRawResponse, + AsyncProjectsResourceWithRawResponse, + ProjectsResourceWithStreamingResponse, + AsyncProjectsResourceWithStreamingResponse, ) from .templates import ( TemplatesResource, @@ -40,12 +40,12 @@ "AsyncConvertResourceWithRawResponse", "ConvertResourceWithStreamingResponse", "AsyncConvertResourceWithStreamingResponse", - "ProjectResource", - "AsyncProjectResource", - "ProjectResourceWithRawResponse", - "AsyncProjectResourceWithRawResponse", - "ProjectResourceWithStreamingResponse", - "AsyncProjectResourceWithStreamingResponse", + "ProjectsResource", + "AsyncProjectsResource", + "ProjectsResourceWithRawResponse", + "AsyncProjectsResourceWithRawResponse", + "ProjectsResourceWithStreamingResponse", + "AsyncProjectsResourceWithStreamingResponse", "TemplatesResource", "AsyncTemplatesResource", "TemplatesResourceWithRawResponse", diff --git a/src/unlayer/resources/project.py b/src/unlayer/resources/projects.py similarity index 60% rename from src/unlayer/resources/project.py rename to src/unlayer/resources/projects.py index af286c2..59073ac 100644 --- a/src/unlayer/resources/project.py +++ b/src/unlayer/resources/projects.py @@ -4,9 +4,7 @@ import httpx -from ..types import project_retrieve_params -from .._types import Body, Omit, Query, Headers, NotGiven, omit, not_given -from .._utils import maybe_transform, async_maybe_transform +from .._types import Body, Query, Headers, NotGiven, not_given from .._compat import cached_property from .._resource import SyncAPIResource, AsyncAPIResource from .._response import ( @@ -18,33 +16,33 @@ from .._base_client import make_request_options from ..types.project_retrieve_response import ProjectRetrieveResponse -__all__ = ["ProjectResource", "AsyncProjectResource"] +__all__ = ["ProjectsResource", "AsyncProjectsResource"] -class ProjectResource(SyncAPIResource): +class ProjectsResource(SyncAPIResource): @cached_property - def with_raw_response(self) -> ProjectResourceWithRawResponse: + def with_raw_response(self) -> ProjectsResourceWithRawResponse: """ This property can be used as a prefix for any HTTP method call to return the raw response object instead of the parsed content. For more information, see https://www.github.com/unlayer/unlayer-python#accessing-raw-response-data-eg-headers """ - return ProjectResourceWithRawResponse(self) + return ProjectsResourceWithRawResponse(self) @cached_property - def with_streaming_response(self) -> ProjectResourceWithStreamingResponse: + def with_streaming_response(self) -> ProjectsResourceWithStreamingResponse: """ An alternative to `.with_raw_response` that doesn't eagerly read the response body. For more information, see https://www.github.com/unlayer/unlayer-python#with_streaming_response """ - return ProjectResourceWithStreamingResponse(self) + return ProjectsResourceWithStreamingResponse(self) def retrieve( self, + id: str, *, - project_id: str | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, @@ -53,11 +51,9 @@ def retrieve( timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> ProjectRetrieveResponse: """ - Get project details for the specified project. + Get project details by ID. Args: - project_id: The project ID (required for PAT auth, auto-resolved for API key auth) - extra_headers: Send extra headers extra_query: Add additional query parameters to the request @@ -66,43 +62,41 @@ def retrieve( timeout: Override the client-level default timeout for this request, in seconds """ + if not id: + raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return self._get( - "/v3/project", + f"/v3/projects/{id}", options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query=maybe_transform({"project_id": project_id}, project_retrieve_params.ProjectRetrieveParams), + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=ProjectRetrieveResponse, ) -class AsyncProjectResource(AsyncAPIResource): +class AsyncProjectsResource(AsyncAPIResource): @cached_property - def with_raw_response(self) -> AsyncProjectResourceWithRawResponse: + def with_raw_response(self) -> AsyncProjectsResourceWithRawResponse: """ This property can be used as a prefix for any HTTP method call to return the raw response object instead of the parsed content. For more information, see https://www.github.com/unlayer/unlayer-python#accessing-raw-response-data-eg-headers """ - return AsyncProjectResourceWithRawResponse(self) + return AsyncProjectsResourceWithRawResponse(self) @cached_property - def with_streaming_response(self) -> AsyncProjectResourceWithStreamingResponse: + def with_streaming_response(self) -> AsyncProjectsResourceWithStreamingResponse: """ An alternative to `.with_raw_response` that doesn't eagerly read the response body. For more information, see https://www.github.com/unlayer/unlayer-python#with_streaming_response """ - return AsyncProjectResourceWithStreamingResponse(self) + return AsyncProjectsResourceWithStreamingResponse(self) async def retrieve( self, + id: str, *, - project_id: str | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, @@ -111,11 +105,9 @@ async def retrieve( timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> ProjectRetrieveResponse: """ - Get project details for the specified project. + Get project details by ID. Args: - project_id: The project ID (required for PAT auth, auto-resolved for API key auth) - extra_headers: Send extra headers extra_query: Add additional query parameters to the request @@ -124,52 +116,48 @@ async def retrieve( timeout: Override the client-level default timeout for this request, in seconds """ + if not id: + raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return await self._get( - "/v3/project", + f"/v3/projects/{id}", options=make_request_options( - extra_headers=extra_headers, - extra_query=extra_query, - extra_body=extra_body, - timeout=timeout, - query=await async_maybe_transform( - {"project_id": project_id}, project_retrieve_params.ProjectRetrieveParams - ), + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=ProjectRetrieveResponse, ) -class ProjectResourceWithRawResponse: - def __init__(self, project: ProjectResource) -> None: - self._project = project +class ProjectsResourceWithRawResponse: + def __init__(self, projects: ProjectsResource) -> None: + self._projects = projects self.retrieve = to_raw_response_wrapper( - project.retrieve, + projects.retrieve, ) -class AsyncProjectResourceWithRawResponse: - def __init__(self, project: AsyncProjectResource) -> None: - self._project = project +class AsyncProjectsResourceWithRawResponse: + def __init__(self, projects: AsyncProjectsResource) -> None: + self._projects = projects self.retrieve = async_to_raw_response_wrapper( - project.retrieve, + projects.retrieve, ) -class ProjectResourceWithStreamingResponse: - def __init__(self, project: ProjectResource) -> None: - self._project = project +class ProjectsResourceWithStreamingResponse: + def __init__(self, projects: ProjectsResource) -> None: + self._projects = projects self.retrieve = to_streamed_response_wrapper( - project.retrieve, + projects.retrieve, ) -class AsyncProjectResourceWithStreamingResponse: - def __init__(self, project: AsyncProjectResource) -> None: - self._project = project +class AsyncProjectsResourceWithStreamingResponse: + def __init__(self, projects: AsyncProjectsResource) -> None: + self._projects = projects self.retrieve = async_to_streamed_response_wrapper( - project.retrieve, + projects.retrieve, ) diff --git a/src/unlayer/types/__init__.py b/src/unlayer/types/__init__.py index 33221d8..3aa8608 100644 --- a/src/unlayer/types/__init__.py +++ b/src/unlayer/types/__init__.py @@ -4,7 +4,6 @@ from .template_list_params import TemplateListParams as TemplateListParams from .template_list_response import TemplateListResponse as TemplateListResponse -from .project_retrieve_params import ProjectRetrieveParams as ProjectRetrieveParams from .workspace_list_response import WorkspaceListResponse as WorkspaceListResponse from .template_retrieve_params import TemplateRetrieveParams as TemplateRetrieveParams from .project_retrieve_response import ProjectRetrieveResponse as ProjectRetrieveResponse diff --git a/src/unlayer/types/project_retrieve_params.py b/src/unlayer/types/project_retrieve_params.py deleted file mode 100644 index f18748a..0000000 --- a/src/unlayer/types/project_retrieve_params.py +++ /dev/null @@ -1,14 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing_extensions import Annotated, TypedDict - -from .._utils import PropertyInfo - -__all__ = ["ProjectRetrieveParams"] - - -class ProjectRetrieveParams(TypedDict, total=False): - project_id: Annotated[str, PropertyInfo(alias="projectId")] - """The project ID (required for PAT auth, auto-resolved for API key auth)""" diff --git a/tests/api_resources/test_project.py b/tests/api_resources/test_projects.py similarity index 69% rename from tests/api_resources/test_project.py rename to tests/api_resources/test_projects.py index 58151bf..3b4276d 100644 --- a/tests/api_resources/test_project.py +++ b/tests/api_resources/test_projects.py @@ -14,24 +14,21 @@ base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") -class TestProject: +class TestProjects: parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) @parametrize def test_method_retrieve(self, client: Unlayer) -> None: - project = client.project.retrieve() - assert_matches_type(ProjectRetrieveResponse, project, path=["response"]) - - @parametrize - def test_method_retrieve_with_all_params(self, client: Unlayer) -> None: - project = client.project.retrieve( - project_id="projectId", + project = client.projects.retrieve( + "id", ) assert_matches_type(ProjectRetrieveResponse, project, path=["response"]) @parametrize def test_raw_response_retrieve(self, client: Unlayer) -> None: - response = client.project.with_raw_response.retrieve() + response = client.projects.with_raw_response.retrieve( + "id", + ) assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" @@ -40,7 +37,9 @@ def test_raw_response_retrieve(self, client: Unlayer) -> None: @parametrize def test_streaming_response_retrieve(self, client: Unlayer) -> None: - with client.project.with_streaming_response.retrieve() as response: + with client.projects.with_streaming_response.retrieve( + "id", + ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" @@ -49,27 +48,31 @@ def test_streaming_response_retrieve(self, client: Unlayer) -> None: assert cast(Any, response.is_closed) is True + @parametrize + def test_path_params_retrieve(self, client: Unlayer) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): + client.projects.with_raw_response.retrieve( + "", + ) + -class TestAsyncProject: +class TestAsyncProjects: parametrize = pytest.mark.parametrize( "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] ) @parametrize async def test_method_retrieve(self, async_client: AsyncUnlayer) -> None: - project = await async_client.project.retrieve() - assert_matches_type(ProjectRetrieveResponse, project, path=["response"]) - - @parametrize - async def test_method_retrieve_with_all_params(self, async_client: AsyncUnlayer) -> None: - project = await async_client.project.retrieve( - project_id="projectId", + project = await async_client.projects.retrieve( + "id", ) assert_matches_type(ProjectRetrieveResponse, project, path=["response"]) @parametrize async def test_raw_response_retrieve(self, async_client: AsyncUnlayer) -> None: - response = await async_client.project.with_raw_response.retrieve() + response = await async_client.projects.with_raw_response.retrieve( + "id", + ) assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" @@ -78,7 +81,9 @@ async def test_raw_response_retrieve(self, async_client: AsyncUnlayer) -> None: @parametrize async def test_streaming_response_retrieve(self, async_client: AsyncUnlayer) -> None: - async with async_client.project.with_streaming_response.retrieve() as response: + async with async_client.projects.with_streaming_response.retrieve( + "id", + ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" @@ -86,3 +91,10 @@ async def test_streaming_response_retrieve(self, async_client: AsyncUnlayer) -> assert_matches_type(ProjectRetrieveResponse, project, path=["response"]) assert cast(Any, response.is_closed) is True + + @parametrize + async def test_path_params_retrieve(self, async_client: AsyncUnlayer) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): + await async_client.projects.with_raw_response.retrieve( + "", + ) diff --git a/tests/test_client.py b/tests/test_client.py index 93072a2..a045578 100644 --- a/tests/test_client.py +++ b/tests/test_client.py @@ -23,7 +23,7 @@ from unlayer._types import Omit from unlayer._utils import asyncify from unlayer._models import BaseModel, FinalRequestOptions -from unlayer._exceptions import APIStatusError, APITimeoutError, APIResponseValidationError +from unlayer._exceptions import APIStatusError, APIResponseValidationError from unlayer._base_client import ( DEFAULT_TIMEOUT, HTTPX_DEFAULT_TIMEOUT, @@ -103,14 +103,6 @@ async def _make_async_iterator(iterable: Iterable[T], counter: Optional[Counter] yield item -def _get_open_connections(client: Unlayer | AsyncUnlayer) -> int: - transport = client._client._transport - assert isinstance(transport, httpx.HTTPTransport) or isinstance(transport, httpx.AsyncHTTPTransport) - - pool = transport._pool - return len(pool._requests) - - class TestUnlayer: @pytest.mark.respx(base_url=base_url) def test_raw_response(self, respx_mock: MockRouter, client: Unlayer) -> None: @@ -855,25 +847,6 @@ def test_parse_retry_after_header( calculated = client._calculate_retry_timeout(remaining_retries, options, headers) assert calculated == pytest.approx(timeout, 0.5 * 0.875) # pyright: ignore[reportUnknownMemberType] - @mock.patch("unlayer._base_client.BaseClient._calculate_retry_timeout", _low_retry_timeout) - @pytest.mark.respx(base_url=base_url) - def test_retrying_timeout_errors_doesnt_leak(self, respx_mock: MockRouter, client: Unlayer) -> None: - respx_mock.get("/v3/project").mock(side_effect=httpx.TimeoutException("Test timeout error")) - - with pytest.raises(APITimeoutError): - client.project.with_streaming_response.retrieve().__enter__() - - assert _get_open_connections(client) == 0 - - @mock.patch("unlayer._base_client.BaseClient._calculate_retry_timeout", _low_retry_timeout) - @pytest.mark.respx(base_url=base_url) - def test_retrying_status_errors_doesnt_leak(self, respx_mock: MockRouter, client: Unlayer) -> None: - respx_mock.get("/v3/project").mock(return_value=httpx.Response(500)) - - with pytest.raises(APIStatusError): - client.project.with_streaming_response.retrieve().__enter__() - assert _get_open_connections(client) == 0 - @pytest.mark.parametrize("failures_before_success", [0, 2, 4]) @mock.patch("unlayer._base_client.BaseClient._calculate_retry_timeout", _low_retry_timeout) @pytest.mark.respx(base_url=base_url) @@ -898,9 +871,9 @@ def retry_handler(_request: httpx.Request) -> httpx.Response: return httpx.Response(500) return httpx.Response(200) - respx_mock.get("/v3/project").mock(side_effect=retry_handler) + respx_mock.get("/v3/templates").mock(side_effect=retry_handler) - response = client.project.with_raw_response.retrieve() + response = client.templates.with_raw_response.list() assert response.retries_taken == failures_before_success assert int(response.http_request.headers.get("x-stainless-retry-count")) == failures_before_success @@ -922,9 +895,9 @@ def retry_handler(_request: httpx.Request) -> httpx.Response: return httpx.Response(500) return httpx.Response(200) - respx_mock.get("/v3/project").mock(side_effect=retry_handler) + respx_mock.get("/v3/templates").mock(side_effect=retry_handler) - response = client.project.with_raw_response.retrieve(extra_headers={"x-stainless-retry-count": Omit()}) + response = client.templates.with_raw_response.list(extra_headers={"x-stainless-retry-count": Omit()}) assert len(response.http_request.headers.get_list("x-stainless-retry-count")) == 0 @@ -945,9 +918,9 @@ def retry_handler(_request: httpx.Request) -> httpx.Response: return httpx.Response(500) return httpx.Response(200) - respx_mock.get("/v3/project").mock(side_effect=retry_handler) + respx_mock.get("/v3/templates").mock(side_effect=retry_handler) - response = client.project.with_raw_response.retrieve(extra_headers={"x-stainless-retry-count": "42"}) + response = client.templates.with_raw_response.list(extra_headers={"x-stainless-retry-count": "42"}) assert response.http_request.headers.get("x-stainless-retry-count") == "42" @@ -1762,27 +1735,6 @@ async def test_parse_retry_after_header( calculated = async_client._calculate_retry_timeout(remaining_retries, options, headers) assert calculated == pytest.approx(timeout, 0.5 * 0.875) # pyright: ignore[reportUnknownMemberType] - @mock.patch("unlayer._base_client.BaseClient._calculate_retry_timeout", _low_retry_timeout) - @pytest.mark.respx(base_url=base_url) - async def test_retrying_timeout_errors_doesnt_leak( - self, respx_mock: MockRouter, async_client: AsyncUnlayer - ) -> None: - respx_mock.get("/v3/project").mock(side_effect=httpx.TimeoutException("Test timeout error")) - - with pytest.raises(APITimeoutError): - await async_client.project.with_streaming_response.retrieve().__aenter__() - - assert _get_open_connections(async_client) == 0 - - @mock.patch("unlayer._base_client.BaseClient._calculate_retry_timeout", _low_retry_timeout) - @pytest.mark.respx(base_url=base_url) - async def test_retrying_status_errors_doesnt_leak(self, respx_mock: MockRouter, async_client: AsyncUnlayer) -> None: - respx_mock.get("/v3/project").mock(return_value=httpx.Response(500)) - - with pytest.raises(APIStatusError): - await async_client.project.with_streaming_response.retrieve().__aenter__() - assert _get_open_connections(async_client) == 0 - @pytest.mark.parametrize("failures_before_success", [0, 2, 4]) @mock.patch("unlayer._base_client.BaseClient._calculate_retry_timeout", _low_retry_timeout) @pytest.mark.respx(base_url=base_url) @@ -1807,9 +1759,9 @@ def retry_handler(_request: httpx.Request) -> httpx.Response: return httpx.Response(500) return httpx.Response(200) - respx_mock.get("/v3/project").mock(side_effect=retry_handler) + respx_mock.get("/v3/templates").mock(side_effect=retry_handler) - response = await client.project.with_raw_response.retrieve() + response = await client.templates.with_raw_response.list() assert response.retries_taken == failures_before_success assert int(response.http_request.headers.get("x-stainless-retry-count")) == failures_before_success @@ -1831,9 +1783,9 @@ def retry_handler(_request: httpx.Request) -> httpx.Response: return httpx.Response(500) return httpx.Response(200) - respx_mock.get("/v3/project").mock(side_effect=retry_handler) + respx_mock.get("/v3/templates").mock(side_effect=retry_handler) - response = await client.project.with_raw_response.retrieve(extra_headers={"x-stainless-retry-count": Omit()}) + response = await client.templates.with_raw_response.list(extra_headers={"x-stainless-retry-count": Omit()}) assert len(response.http_request.headers.get_list("x-stainless-retry-count")) == 0 @@ -1854,9 +1806,9 @@ def retry_handler(_request: httpx.Request) -> httpx.Response: return httpx.Response(500) return httpx.Response(200) - respx_mock.get("/v3/project").mock(side_effect=retry_handler) + respx_mock.get("/v3/templates").mock(side_effect=retry_handler) - response = await client.project.with_raw_response.retrieve(extra_headers={"x-stainless-retry-count": "42"}) + response = await client.templates.with_raw_response.list(extra_headers={"x-stainless-retry-count": "42"}) assert response.http_request.headers.get("x-stainless-retry-count") == "42" From 89c49b79d3604b313eb52e2d34e60d4f3e438a8e Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 24 Feb 2026 04:16:56 +0000 Subject: [PATCH 40/62] chore(internal): add request options to SSE classes --- src/unlayer/_response.py | 3 +++ src/unlayer/_streaming.py | 11 ++++++++--- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/src/unlayer/_response.py b/src/unlayer/_response.py index 9a45370..c26d661 100644 --- a/src/unlayer/_response.py +++ b/src/unlayer/_response.py @@ -152,6 +152,7 @@ def _parse(self, *, to: type[_T] | None = None) -> R | _T: ), response=self.http_response, client=cast(Any, self._client), + options=self._options, ), ) @@ -162,6 +163,7 @@ def _parse(self, *, to: type[_T] | None = None) -> R | _T: cast_to=extract_stream_chunk_type(self._stream_cls), response=self.http_response, client=cast(Any, self._client), + options=self._options, ), ) @@ -175,6 +177,7 @@ def _parse(self, *, to: type[_T] | None = None) -> R | _T: cast_to=cast_to, response=self.http_response, client=cast(Any, self._client), + options=self._options, ), ) diff --git a/src/unlayer/_streaming.py b/src/unlayer/_streaming.py index d6a6f4d..097da18 100644 --- a/src/unlayer/_streaming.py +++ b/src/unlayer/_streaming.py @@ -4,7 +4,7 @@ import json import inspect from types import TracebackType -from typing import TYPE_CHECKING, Any, Generic, TypeVar, Iterator, AsyncIterator, cast +from typing import TYPE_CHECKING, Any, Generic, TypeVar, Iterator, Optional, AsyncIterator, cast from typing_extensions import Self, Protocol, TypeGuard, override, get_origin, runtime_checkable import httpx @@ -13,6 +13,7 @@ if TYPE_CHECKING: from ._client import Unlayer, AsyncUnlayer + from ._models import FinalRequestOptions _T = TypeVar("_T") @@ -22,7 +23,7 @@ class Stream(Generic[_T]): """Provides the core interface to iterate over a synchronous stream response.""" response: httpx.Response - + _options: Optional[FinalRequestOptions] = None _decoder: SSEBytesDecoder def __init__( @@ -31,10 +32,12 @@ def __init__( cast_to: type[_T], response: httpx.Response, client: Unlayer, + options: Optional[FinalRequestOptions] = None, ) -> None: self.response = response self._cast_to = cast_to self._client = client + self._options = options self._decoder = client._make_sse_decoder() self._iterator = self.__stream__() @@ -85,7 +88,7 @@ class AsyncStream(Generic[_T]): """Provides the core interface to iterate over an asynchronous stream response.""" response: httpx.Response - + _options: Optional[FinalRequestOptions] = None _decoder: SSEDecoder | SSEBytesDecoder def __init__( @@ -94,10 +97,12 @@ def __init__( cast_to: type[_T], response: httpx.Response, client: AsyncUnlayer, + options: Optional[FinalRequestOptions] = None, ) -> None: self.response = response self._cast_to = cast_to self._client = client + self._options = options self._decoder = client._make_sse_decoder() self._iterator = self.__stream__() From a36e016b2fc450950f964db3b4604cb620ff0e87 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 24 Feb 2026 04:30:30 +0000 Subject: [PATCH 41/62] chore(internal): make `test_proxy_environment_variables` more resilient --- tests/test_client.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tests/test_client.py b/tests/test_client.py index a045578..071a7bc 100644 --- a/tests/test_client.py +++ b/tests/test_client.py @@ -927,6 +927,8 @@ def retry_handler(_request: httpx.Request) -> httpx.Response: def test_proxy_environment_variables(self, monkeypatch: pytest.MonkeyPatch) -> None: # Test that the proxy environment variables are set correctly monkeypatch.setenv("HTTPS_PROXY", "https://example.org") + # Delete in case our environment has this set + monkeypatch.delenv("HTTP_PROXY", raising=False) client = DefaultHttpxClient() @@ -1819,6 +1821,8 @@ async def test_get_platform(self) -> None: async def test_proxy_environment_variables(self, monkeypatch: pytest.MonkeyPatch) -> None: # Test that the proxy environment variables are set correctly monkeypatch.setenv("HTTPS_PROXY", "https://example.org") + # Delete in case our environment has this set + monkeypatch.delenv("HTTP_PROXY", raising=False) client = DefaultAsyncHttpxClient() From e241cedba20b40c20322feda698c8f4a6bdf2a96 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 24 Feb 2026 08:55:41 +0000 Subject: [PATCH 42/62] codegen metadata --- .stats.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.stats.yml b/.stats.yml index 3004020..3b0ed67 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 7 openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/unlayer%2Funlayer-48f00d1c04c23fb4d1cb7cf4af4f56b0c920d758c1f06e06e5373e5b15e9c27d.yml openapi_spec_hash: 6ee2a94bb9840aceb4a6161c724ce46c -config_hash: c8d97d58d67dad9eeb65eb58fc781724 +config_hash: e1b17e2707760d0c014601073f354d8b From 0d9be446d5bc843d9035c76e6fceb948cc6d706e Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 24 Feb 2026 08:56:16 +0000 Subject: [PATCH 43/62] codegen metadata --- .stats.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.stats.yml b/.stats.yml index 3b0ed67..2702d73 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 7 openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/unlayer%2Funlayer-48f00d1c04c23fb4d1cb7cf4af4f56b0c920d758c1f06e06e5373e5b15e9c27d.yml openapi_spec_hash: 6ee2a94bb9840aceb4a6161c724ce46c -config_hash: e1b17e2707760d0c014601073f354d8b +config_hash: 249869757b6eb98ae3d58f2a47ce21e2 From 225b2fa2bcaa84bccc928cd5702cc6a8435a9cad Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 24 Feb 2026 08:58:56 +0000 Subject: [PATCH 44/62] chore(internal): version bump --- .release-please-manifest.json | 2 +- pyproject.toml | 2 +- src/unlayer/_version.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 1332969..3d2ac0b 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "0.0.1" + ".": "0.1.0" } \ No newline at end of file diff --git a/pyproject.toml b/pyproject.toml index 2bcff9e..cbb8b33 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "unlayer" -version = "0.0.1" +version = "0.1.0" description = "The official Python library for the unlayer API" dynamic = ["readme"] license = "Apache-2.0" diff --git a/src/unlayer/_version.py b/src/unlayer/_version.py index b9adb5b..47398ca 100644 --- a/src/unlayer/_version.py +++ b/src/unlayer/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "unlayer" -__version__ = "0.0.1" # x-release-please-version +__version__ = "0.1.0" # x-release-please-version From a38c6e9cb2f0eeee36798e9d4d486f1f9078e485 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Wed, 25 Feb 2026 03:57:32 +0000 Subject: [PATCH 45/62] chore(internal): make `test_proxy_environment_variables` more resilient to env --- tests/test_client.py | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/tests/test_client.py b/tests/test_client.py index 071a7bc..61e5adb 100644 --- a/tests/test_client.py +++ b/tests/test_client.py @@ -927,8 +927,14 @@ def retry_handler(_request: httpx.Request) -> httpx.Response: def test_proxy_environment_variables(self, monkeypatch: pytest.MonkeyPatch) -> None: # Test that the proxy environment variables are set correctly monkeypatch.setenv("HTTPS_PROXY", "https://example.org") - # Delete in case our environment has this set + # Delete in case our environment has any proxy env vars set monkeypatch.delenv("HTTP_PROXY", raising=False) + monkeypatch.delenv("ALL_PROXY", raising=False) + monkeypatch.delenv("NO_PROXY", raising=False) + monkeypatch.delenv("http_proxy", raising=False) + monkeypatch.delenv("https_proxy", raising=False) + monkeypatch.delenv("all_proxy", raising=False) + monkeypatch.delenv("no_proxy", raising=False) client = DefaultHttpxClient() @@ -1821,8 +1827,14 @@ async def test_get_platform(self) -> None: async def test_proxy_environment_variables(self, monkeypatch: pytest.MonkeyPatch) -> None: # Test that the proxy environment variables are set correctly monkeypatch.setenv("HTTPS_PROXY", "https://example.org") - # Delete in case our environment has this set + # Delete in case our environment has any proxy env vars set monkeypatch.delenv("HTTP_PROXY", raising=False) + monkeypatch.delenv("ALL_PROXY", raising=False) + monkeypatch.delenv("NO_PROXY", raising=False) + monkeypatch.delenv("http_proxy", raising=False) + monkeypatch.delenv("https_proxy", raising=False) + monkeypatch.delenv("all_proxy", raising=False) + monkeypatch.delenv("no_proxy", raising=False) client = DefaultAsyncHttpxClient() From 6ad6dc67c6c85e2f660ad54d546696f016d88c3c Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Wed, 25 Feb 2026 21:27:10 +0000 Subject: [PATCH 46/62] codegen metadata --- .stats.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.stats.yml b/.stats.yml index 2702d73..3004020 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 7 openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/unlayer%2Funlayer-48f00d1c04c23fb4d1cb7cf4af4f56b0c920d758c1f06e06e5373e5b15e9c27d.yml openapi_spec_hash: 6ee2a94bb9840aceb4a6161c724ce46c -config_hash: 249869757b6eb98ae3d58f2a47ce21e2 +config_hash: c8d97d58d67dad9eeb65eb58fc781724 From d64e16c43f4365f81d570bcd30d6ac2c883a438a Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 3 Mar 2026 05:10:26 +0000 Subject: [PATCH 47/62] chore(internal): codegen related update --- src/unlayer/_client.py | 18 ++++++++++++++++++ src/unlayer/resources/convert/convert.py | 12 ++++++++++++ .../resources/convert/full_to_simple.py | 4 ++++ .../resources/convert/simple_to_full.py | 4 ++++ src/unlayer/resources/projects.py | 4 ++++ src/unlayer/resources/templates.py | 4 ++++ src/unlayer/resources/workspaces.py | 4 ++++ 7 files changed, 50 insertions(+) diff --git a/src/unlayer/_client.py b/src/unlayer/_client.py index 08f7fef..2c20382 100644 --- a/src/unlayer/_client.py +++ b/src/unlayer/_client.py @@ -115,18 +115,21 @@ def convert(self) -> ConvertResource: @cached_property def projects(self) -> ProjectsResource: + """Project details and configuration.""" from .resources.projects import ProjectsResource return ProjectsResource(self) @cached_property def templates(self) -> TemplatesResource: + """Template management and retrieval.""" from .resources.templates import TemplatesResource return TemplatesResource(self) @cached_property def workspaces(self) -> WorkspacesResource: + """Workspace access and management.""" from .resources.workspaces import WorkspacesResource return WorkspacesResource(self) @@ -345,18 +348,21 @@ def convert(self) -> AsyncConvertResource: @cached_property def projects(self) -> AsyncProjectsResource: + """Project details and configuration.""" from .resources.projects import AsyncProjectsResource return AsyncProjectsResource(self) @cached_property def templates(self) -> AsyncTemplatesResource: + """Template management and retrieval.""" from .resources.templates import AsyncTemplatesResource return AsyncTemplatesResource(self) @cached_property def workspaces(self) -> AsyncWorkspacesResource: + """Workspace access and management.""" from .resources.workspaces import AsyncWorkspacesResource return AsyncWorkspacesResource(self) @@ -515,18 +521,21 @@ def convert(self) -> convert.ConvertResourceWithRawResponse: @cached_property def projects(self) -> projects.ProjectsResourceWithRawResponse: + """Project details and configuration.""" from .resources.projects import ProjectsResourceWithRawResponse return ProjectsResourceWithRawResponse(self._client.projects) @cached_property def templates(self) -> templates.TemplatesResourceWithRawResponse: + """Template management and retrieval.""" from .resources.templates import TemplatesResourceWithRawResponse return TemplatesResourceWithRawResponse(self._client.templates) @cached_property def workspaces(self) -> workspaces.WorkspacesResourceWithRawResponse: + """Workspace access and management.""" from .resources.workspaces import WorkspacesResourceWithRawResponse return WorkspacesResourceWithRawResponse(self._client.workspaces) @@ -546,18 +555,21 @@ def convert(self) -> convert.AsyncConvertResourceWithRawResponse: @cached_property def projects(self) -> projects.AsyncProjectsResourceWithRawResponse: + """Project details and configuration.""" from .resources.projects import AsyncProjectsResourceWithRawResponse return AsyncProjectsResourceWithRawResponse(self._client.projects) @cached_property def templates(self) -> templates.AsyncTemplatesResourceWithRawResponse: + """Template management and retrieval.""" from .resources.templates import AsyncTemplatesResourceWithRawResponse return AsyncTemplatesResourceWithRawResponse(self._client.templates) @cached_property def workspaces(self) -> workspaces.AsyncWorkspacesResourceWithRawResponse: + """Workspace access and management.""" from .resources.workspaces import AsyncWorkspacesResourceWithRawResponse return AsyncWorkspacesResourceWithRawResponse(self._client.workspaces) @@ -577,18 +589,21 @@ def convert(self) -> convert.ConvertResourceWithStreamingResponse: @cached_property def projects(self) -> projects.ProjectsResourceWithStreamingResponse: + """Project details and configuration.""" from .resources.projects import ProjectsResourceWithStreamingResponse return ProjectsResourceWithStreamingResponse(self._client.projects) @cached_property def templates(self) -> templates.TemplatesResourceWithStreamingResponse: + """Template management and retrieval.""" from .resources.templates import TemplatesResourceWithStreamingResponse return TemplatesResourceWithStreamingResponse(self._client.templates) @cached_property def workspaces(self) -> workspaces.WorkspacesResourceWithStreamingResponse: + """Workspace access and management.""" from .resources.workspaces import WorkspacesResourceWithStreamingResponse return WorkspacesResourceWithStreamingResponse(self._client.workspaces) @@ -608,18 +623,21 @@ def convert(self) -> convert.AsyncConvertResourceWithStreamingResponse: @cached_property def projects(self) -> projects.AsyncProjectsResourceWithStreamingResponse: + """Project details and configuration.""" from .resources.projects import AsyncProjectsResourceWithStreamingResponse return AsyncProjectsResourceWithStreamingResponse(self._client.projects) @cached_property def templates(self) -> templates.AsyncTemplatesResourceWithStreamingResponse: + """Template management and retrieval.""" from .resources.templates import AsyncTemplatesResourceWithStreamingResponse return AsyncTemplatesResourceWithStreamingResponse(self._client.templates) @cached_property def workspaces(self) -> workspaces.AsyncWorkspacesResourceWithStreamingResponse: + """Workspace access and management.""" from .resources.workspaces import AsyncWorkspacesResourceWithStreamingResponse return AsyncWorkspacesResourceWithStreamingResponse(self._client.workspaces) diff --git a/src/unlayer/resources/convert/convert.py b/src/unlayer/resources/convert/convert.py index 08eee7c..e4fdfc7 100644 --- a/src/unlayer/resources/convert/convert.py +++ b/src/unlayer/resources/convert/convert.py @@ -27,10 +27,12 @@ class ConvertResource(SyncAPIResource): @cached_property def full_to_simple(self) -> FullToSimpleResource: + """Design schema conversion between Full and Simple formats.""" return FullToSimpleResource(self._client) @cached_property def simple_to_full(self) -> SimpleToFullResource: + """Design schema conversion between Full and Simple formats.""" return SimpleToFullResource(self._client) @cached_property @@ -56,10 +58,12 @@ def with_streaming_response(self) -> ConvertResourceWithStreamingResponse: class AsyncConvertResource(AsyncAPIResource): @cached_property def full_to_simple(self) -> AsyncFullToSimpleResource: + """Design schema conversion between Full and Simple formats.""" return AsyncFullToSimpleResource(self._client) @cached_property def simple_to_full(self) -> AsyncSimpleToFullResource: + """Design schema conversion between Full and Simple formats.""" return AsyncSimpleToFullResource(self._client) @cached_property @@ -88,10 +92,12 @@ def __init__(self, convert: ConvertResource) -> None: @cached_property def full_to_simple(self) -> FullToSimpleResourceWithRawResponse: + """Design schema conversion between Full and Simple formats.""" return FullToSimpleResourceWithRawResponse(self._convert.full_to_simple) @cached_property def simple_to_full(self) -> SimpleToFullResourceWithRawResponse: + """Design schema conversion between Full and Simple formats.""" return SimpleToFullResourceWithRawResponse(self._convert.simple_to_full) @@ -101,10 +107,12 @@ def __init__(self, convert: AsyncConvertResource) -> None: @cached_property def full_to_simple(self) -> AsyncFullToSimpleResourceWithRawResponse: + """Design schema conversion between Full and Simple formats.""" return AsyncFullToSimpleResourceWithRawResponse(self._convert.full_to_simple) @cached_property def simple_to_full(self) -> AsyncSimpleToFullResourceWithRawResponse: + """Design schema conversion between Full and Simple formats.""" return AsyncSimpleToFullResourceWithRawResponse(self._convert.simple_to_full) @@ -114,10 +122,12 @@ def __init__(self, convert: ConvertResource) -> None: @cached_property def full_to_simple(self) -> FullToSimpleResourceWithStreamingResponse: + """Design schema conversion between Full and Simple formats.""" return FullToSimpleResourceWithStreamingResponse(self._convert.full_to_simple) @cached_property def simple_to_full(self) -> SimpleToFullResourceWithStreamingResponse: + """Design schema conversion between Full and Simple formats.""" return SimpleToFullResourceWithStreamingResponse(self._convert.simple_to_full) @@ -127,8 +137,10 @@ def __init__(self, convert: AsyncConvertResource) -> None: @cached_property def full_to_simple(self) -> AsyncFullToSimpleResourceWithStreamingResponse: + """Design schema conversion between Full and Simple formats.""" return AsyncFullToSimpleResourceWithStreamingResponse(self._convert.full_to_simple) @cached_property def simple_to_full(self) -> AsyncSimpleToFullResourceWithStreamingResponse: + """Design schema conversion between Full and Simple formats.""" return AsyncSimpleToFullResourceWithStreamingResponse(self._convert.simple_to_full) diff --git a/src/unlayer/resources/convert/full_to_simple.py b/src/unlayer/resources/convert/full_to_simple.py index d5901f4..35ea241 100644 --- a/src/unlayer/resources/convert/full_to_simple.py +++ b/src/unlayer/resources/convert/full_to_simple.py @@ -24,6 +24,8 @@ class FullToSimpleResource(SyncAPIResource): + """Design schema conversion between Full and Simple formats.""" + @cached_property def with_raw_response(self) -> FullToSimpleResourceWithRawResponse: """ @@ -91,6 +93,8 @@ def create( class AsyncFullToSimpleResource(AsyncAPIResource): + """Design schema conversion between Full and Simple formats.""" + @cached_property def with_raw_response(self) -> AsyncFullToSimpleResourceWithRawResponse: """ diff --git a/src/unlayer/resources/convert/simple_to_full.py b/src/unlayer/resources/convert/simple_to_full.py index d9b634f..5f22bb0 100644 --- a/src/unlayer/resources/convert/simple_to_full.py +++ b/src/unlayer/resources/convert/simple_to_full.py @@ -24,6 +24,8 @@ class SimpleToFullResource(SyncAPIResource): + """Design schema conversion between Full and Simple formats.""" + @cached_property def with_raw_response(self) -> SimpleToFullResourceWithRawResponse: """ @@ -86,6 +88,8 @@ def create( class AsyncSimpleToFullResource(AsyncAPIResource): + """Design schema conversion between Full and Simple formats.""" + @cached_property def with_raw_response(self) -> AsyncSimpleToFullResourceWithRawResponse: """ diff --git a/src/unlayer/resources/projects.py b/src/unlayer/resources/projects.py index 59073ac..4025c8c 100644 --- a/src/unlayer/resources/projects.py +++ b/src/unlayer/resources/projects.py @@ -20,6 +20,8 @@ class ProjectsResource(SyncAPIResource): + """Project details and configuration.""" + @cached_property def with_raw_response(self) -> ProjectsResourceWithRawResponse: """ @@ -74,6 +76,8 @@ def retrieve( class AsyncProjectsResource(AsyncAPIResource): + """Project details and configuration.""" + @cached_property def with_raw_response(self) -> AsyncProjectsResourceWithRawResponse: """ diff --git a/src/unlayer/resources/templates.py b/src/unlayer/resources/templates.py index e41c931..ebfb4a0 100644 --- a/src/unlayer/resources/templates.py +++ b/src/unlayer/resources/templates.py @@ -26,6 +26,8 @@ class TemplatesResource(SyncAPIResource): + """Template management and retrieval.""" + @cached_property def with_raw_response(self) -> TemplatesResourceWithRawResponse: """ @@ -148,6 +150,8 @@ def list( class AsyncTemplatesResource(AsyncAPIResource): + """Template management and retrieval.""" + @cached_property def with_raw_response(self) -> AsyncTemplatesResourceWithRawResponse: """ diff --git a/src/unlayer/resources/workspaces.py b/src/unlayer/resources/workspaces.py index 3bce476..be859fc 100644 --- a/src/unlayer/resources/workspaces.py +++ b/src/unlayer/resources/workspaces.py @@ -21,6 +21,8 @@ class WorkspacesResource(SyncAPIResource): + """Workspace access and management.""" + @cached_property def with_raw_response(self) -> WorkspacesResourceWithRawResponse: """ @@ -100,6 +102,8 @@ def list( class AsyncWorkspacesResource(AsyncAPIResource): + """Workspace access and management.""" + @cached_property def with_raw_response(self) -> AsyncWorkspacesResourceWithRawResponse: """ From 1d8e767c94155a0e0cf65f515a56cfcc9795412d Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Fri, 6 Mar 2026 05:12:47 +0000 Subject: [PATCH 48/62] refactor(types): use `extra_items` from PEP 728 --- .../types/convert/full_to_simple_create_params.py | 9 +++------ .../types/convert/simple_to_full_create_params.py | 9 +++------ 2 files changed, 6 insertions(+), 12 deletions(-) diff --git a/src/unlayer/types/convert/full_to_simple_create_params.py b/src/unlayer/types/convert/full_to_simple_create_params.py index 7038dcf..5d70ae7 100644 --- a/src/unlayer/types/convert/full_to_simple_create_params.py +++ b/src/unlayer/types/convert/full_to_simple_create_params.py @@ -2,8 +2,8 @@ from __future__ import annotations -from typing import Dict, Union -from typing_extensions import Literal, Required, Annotated, TypeAlias, TypedDict +from typing import Dict +from typing_extensions import Literal, Required, Annotated, TypedDict from ..._utils import PropertyInfo @@ -25,12 +25,9 @@ class FullToSimpleCreateParams(TypedDict, total=False): include_default_values: Annotated[bool, PropertyInfo(alias="includeDefaultValues")] -class DesignTyped(TypedDict, total=False): +class Design(TypedDict, total=False, extra_items=object): # type: ignore[call-arg] body: Required[Dict[str, object]] counters: Dict[str, object] schema_version: Annotated[float, PropertyInfo(alias="schemaVersion")] - - -Design: TypeAlias = Union[DesignTyped, Dict[str, object]] diff --git a/src/unlayer/types/convert/simple_to_full_create_params.py b/src/unlayer/types/convert/simple_to_full_create_params.py index 247905e..b973376 100644 --- a/src/unlayer/types/convert/simple_to_full_create_params.py +++ b/src/unlayer/types/convert/simple_to_full_create_params.py @@ -2,8 +2,8 @@ from __future__ import annotations -from typing import Dict, Union -from typing_extensions import Literal, Required, Annotated, TypeAlias, TypedDict +from typing import Dict +from typing_extensions import Literal, Required, Annotated, TypedDict from ..._utils import PropertyInfo @@ -24,7 +24,7 @@ class Design_Conversion(TypedDict, total=False): version: float -class DesignTyped(TypedDict, total=False): +class Design(TypedDict, total=False, extra_items=object): # type: ignore[call-arg] body: Required[Dict[str, object]] _conversion: Design_Conversion @@ -32,6 +32,3 @@ class DesignTyped(TypedDict, total=False): counters: Dict[str, object] schema_version: Annotated[float, PropertyInfo(alias="schemaVersion")] - - -Design: TypeAlias = Union[DesignTyped, Dict[str, object]] From 914e194eb6d4e80c9905ff147db9895777887c02 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Fri, 6 Mar 2026 05:30:01 +0000 Subject: [PATCH 49/62] chore(test): do not count install time for mock server timeout --- scripts/mock | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/scripts/mock b/scripts/mock index 0b28f6e..bcf3b39 100755 --- a/scripts/mock +++ b/scripts/mock @@ -21,11 +21,22 @@ echo "==> Starting mock server with URL ${URL}" # Run prism mock on the given spec if [ "$1" == "--daemon" ]; then + # Pre-install the package so the download doesn't eat into the startup timeout + npm exec --package=@stainless-api/prism-cli@5.15.0 -- prism --version + npm exec --package=@stainless-api/prism-cli@5.15.0 -- prism mock "$URL" &> .prism.log & - # Wait for server to come online + # Wait for server to come online (max 30s) echo -n "Waiting for server" + attempts=0 while ! grep -q "✖ fatal\|Prism is listening" ".prism.log" ; do + attempts=$((attempts + 1)) + if [ "$attempts" -ge 300 ]; then + echo + echo "Timed out waiting for Prism server to start" + cat .prism.log + exit 1 + fi echo -n "." sleep 0.1 done From 813ebd64472c578828063029f54000fa68a339be Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Sat, 7 Mar 2026 16:13:42 +0000 Subject: [PATCH 50/62] chore(ci): skip uploading artifacts on stainless-internal branches --- .github/workflows/ci.yml | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index e5caf73..55b6ef5 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -61,14 +61,18 @@ jobs: run: rye build - name: Get GitHub OIDC Token - if: github.repository == 'stainless-sdks/unlayer-python' + if: |- + github.repository == 'stainless-sdks/unlayer-python' && + !startsWith(github.ref, 'refs/heads/stl/') id: github-oidc uses: actions/github-script@v8 with: script: core.setOutput('github_token', await core.getIDToken()); - name: Upload tarball - if: github.repository == 'stainless-sdks/unlayer-python' + if: |- + github.repository == 'stainless-sdks/unlayer-python' && + !startsWith(github.ref, 'refs/heads/stl/') env: URL: https://pkg.stainless.com/s AUTH: ${{ steps.github-oidc.outputs.github_token }} From 3efb2dbc2cdfc4fa98de949829a66c1229720f3d Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 17 Mar 2026 10:10:56 +0000 Subject: [PATCH 51/62] fix(pydantic): do not pass `by_alias` unless set --- src/unlayer/_compat.py | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/unlayer/_compat.py b/src/unlayer/_compat.py index 786ff42..e6690a4 100644 --- a/src/unlayer/_compat.py +++ b/src/unlayer/_compat.py @@ -2,7 +2,7 @@ from typing import TYPE_CHECKING, Any, Union, Generic, TypeVar, Callable, cast, overload from datetime import date, datetime -from typing_extensions import Self, Literal +from typing_extensions import Self, Literal, TypedDict import pydantic from pydantic.fields import FieldInfo @@ -131,6 +131,10 @@ def model_json(model: pydantic.BaseModel, *, indent: int | None = None) -> str: return model.model_dump_json(indent=indent) +class _ModelDumpKwargs(TypedDict, total=False): + by_alias: bool + + def model_dump( model: pydantic.BaseModel, *, @@ -142,6 +146,9 @@ def model_dump( by_alias: bool | None = None, ) -> dict[str, Any]: if (not PYDANTIC_V1) or hasattr(model, "model_dump"): + kwargs: _ModelDumpKwargs = {} + if by_alias is not None: + kwargs["by_alias"] = by_alias return model.model_dump( mode=mode, exclude=exclude, @@ -149,7 +156,7 @@ def model_dump( exclude_defaults=exclude_defaults, # warnings are not supported in Pydantic v1 warnings=True if PYDANTIC_V1 else warnings, - by_alias=by_alias, + **kwargs, ) return cast( "dict[str, Any]", From 4c58ef0beea4d0878085e97390c59e0f09b05ed5 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 17 Mar 2026 10:16:28 +0000 Subject: [PATCH 52/62] fix(deps): bump minimum typing-extensions version --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index cbb8b33..967cabc 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -11,7 +11,7 @@ authors = [ dependencies = [ "httpx>=0.23.0, <1", "pydantic>=1.9.0, <3", - "typing-extensions>=4.10, <5", + "typing-extensions>=4.14, <5", "anyio>=3.5.0, <5", "distro>=1.7.0, <2", "sniffio", From 6c57ee5e647ebd70177c1f5e082251d8508c5fb3 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 17 Mar 2026 10:20:31 +0000 Subject: [PATCH 53/62] chore(internal): tweak CI branches --- .github/workflows/ci.yml | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 55b6ef5..581125c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,12 +1,14 @@ name: CI on: push: - branches-ignore: - - 'generated' - - 'codegen/**' - - 'integrated/**' - - 'stl-preview-head/**' - - 'stl-preview-base/**' + branches: + - '**' + - '!integrated/**' + - '!stl-preview-head/**' + - '!stl-preview-base/**' + - '!generated' + - '!codegen/**' + - 'codegen/stl/**' pull_request: branches-ignore: - 'stl-preview-head/**' From b5fb72bcdbde5f250af5904de29cd62368b08346 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Fri, 20 Mar 2026 03:23:13 +0000 Subject: [PATCH 54/62] fix: sanitize endpoint path params --- src/unlayer/_utils/__init__.py | 1 + src/unlayer/_utils/_path.py | 127 ++++++++++++++++++++++++++++ src/unlayer/resources/projects.py | 5 +- src/unlayer/resources/templates.py | 6 +- src/unlayer/resources/workspaces.py | 5 +- tests/test_utils/test_path.py | 89 +++++++++++++++++++ 6 files changed, 226 insertions(+), 7 deletions(-) create mode 100644 src/unlayer/_utils/_path.py create mode 100644 tests/test_utils/test_path.py diff --git a/src/unlayer/_utils/__init__.py b/src/unlayer/_utils/__init__.py index dc64e29..10cb66d 100644 --- a/src/unlayer/_utils/__init__.py +++ b/src/unlayer/_utils/__init__.py @@ -1,3 +1,4 @@ +from ._path import path_template as path_template from ._sync import asyncify as asyncify from ._proxy import LazyProxy as LazyProxy from ._utils import ( diff --git a/src/unlayer/_utils/_path.py b/src/unlayer/_utils/_path.py new file mode 100644 index 0000000..4d6e1e4 --- /dev/null +++ b/src/unlayer/_utils/_path.py @@ -0,0 +1,127 @@ +from __future__ import annotations + +import re +from typing import ( + Any, + Mapping, + Callable, +) +from urllib.parse import quote + +# Matches '.' or '..' where each dot is either literal or percent-encoded (%2e / %2E). +_DOT_SEGMENT_RE = re.compile(r"^(?:\.|%2[eE]){1,2}$") + +_PLACEHOLDER_RE = re.compile(r"\{(\w+)\}") + + +def _quote_path_segment_part(value: str) -> str: + """Percent-encode `value` for use in a URI path segment. + + Considers characters not in `pchar` set from RFC 3986 §3.3 to be unsafe. + https://datatracker.ietf.org/doc/html/rfc3986#section-3.3 + """ + # quote() already treats unreserved characters (letters, digits, and -._~) + # as safe, so we only need to add sub-delims, ':', and '@'. + # Notably, unlike the default `safe` for quote(), / is unsafe and must be quoted. + return quote(value, safe="!$&'()*+,;=:@") + + +def _quote_query_part(value: str) -> str: + """Percent-encode `value` for use in a URI query string. + + Considers &, = and characters not in `query` set from RFC 3986 §3.4 to be unsafe. + https://datatracker.ietf.org/doc/html/rfc3986#section-3.4 + """ + return quote(value, safe="!$'()*+,;:@/?") + + +def _quote_fragment_part(value: str) -> str: + """Percent-encode `value` for use in a URI fragment. + + Considers characters not in `fragment` set from RFC 3986 §3.5 to be unsafe. + https://datatracker.ietf.org/doc/html/rfc3986#section-3.5 + """ + return quote(value, safe="!$&'()*+,;=:@/?") + + +def _interpolate( + template: str, + values: Mapping[str, Any], + quoter: Callable[[str], str], +) -> str: + """Replace {name} placeholders in `template`, quoting each value with `quoter`. + + Placeholder names are looked up in `values`. + + Raises: + KeyError: If a placeholder is not found in `values`. + """ + # re.split with a capturing group returns alternating + # [text, name, text, name, ..., text] elements. + parts = _PLACEHOLDER_RE.split(template) + + for i in range(1, len(parts), 2): + name = parts[i] + if name not in values: + raise KeyError(f"a value for placeholder {{{name}}} was not provided") + val = values[name] + if val is None: + parts[i] = "null" + elif isinstance(val, bool): + parts[i] = "true" if val else "false" + else: + parts[i] = quoter(str(values[name])) + + return "".join(parts) + + +def path_template(template: str, /, **kwargs: Any) -> str: + """Interpolate {name} placeholders in `template` from keyword arguments. + + Args: + template: The template string containing {name} placeholders. + **kwargs: Keyword arguments to interpolate into the template. + + Returns: + The template with placeholders interpolated and percent-encoded. + + Safe characters for percent-encoding are dependent on the URI component. + Placeholders in path and fragment portions are percent-encoded where the `segment` + and `fragment` sets from RFC 3986 respectively are considered safe. + Placeholders in the query portion are percent-encoded where the `query` set from + RFC 3986 §3.3 is considered safe except for = and & characters. + + Raises: + KeyError: If a placeholder is not found in `kwargs`. + ValueError: If resulting path contains /./ or /../ segments (including percent-encoded dot-segments). + """ + # Split the template into path, query, and fragment portions. + fragment_template: str | None = None + query_template: str | None = None + + rest = template + if "#" in rest: + rest, fragment_template = rest.split("#", 1) + if "?" in rest: + rest, query_template = rest.split("?", 1) + path_template = rest + + # Interpolate each portion with the appropriate quoting rules. + path_result = _interpolate(path_template, kwargs, _quote_path_segment_part) + + # Reject dot-segments (. and ..) in the final assembled path. The check + # runs after interpolation so that adjacent placeholders or a mix of static + # text and placeholders that together form a dot-segment are caught. + # Also reject percent-encoded dot-segments to protect against incorrectly + # implemented normalization in servers/proxies. + for segment in path_result.split("/"): + if _DOT_SEGMENT_RE.match(segment): + raise ValueError(f"Constructed path {path_result!r} contains dot-segment {segment!r} which is not allowed") + + result = path_result + if query_template is not None: + result += "?" + _interpolate(query_template, kwargs, _quote_query_part) + if fragment_template is not None: + result += "#" + _interpolate(fragment_template, kwargs, _quote_fragment_part) + + return result diff --git a/src/unlayer/resources/projects.py b/src/unlayer/resources/projects.py index 4025c8c..f842cfe 100644 --- a/src/unlayer/resources/projects.py +++ b/src/unlayer/resources/projects.py @@ -5,6 +5,7 @@ import httpx from .._types import Body, Query, Headers, NotGiven, not_given +from .._utils import path_template from .._compat import cached_property from .._resource import SyncAPIResource, AsyncAPIResource from .._response import ( @@ -67,7 +68,7 @@ def retrieve( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return self._get( - f"/v3/projects/{id}", + path_template("/v3/projects/{id}", id=id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -123,7 +124,7 @@ async def retrieve( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return await self._get( - f"/v3/projects/{id}", + path_template("/v3/projects/{id}", id=id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), diff --git a/src/unlayer/resources/templates.py b/src/unlayer/resources/templates.py index ebfb4a0..75fcee7 100644 --- a/src/unlayer/resources/templates.py +++ b/src/unlayer/resources/templates.py @@ -8,7 +8,7 @@ from ..types import template_list_params, template_retrieve_params from .._types import Body, Omit, Query, Headers, NotGiven, omit, not_given -from .._utils import maybe_transform, async_maybe_transform +from .._utils import path_template, maybe_transform, async_maybe_transform from .._compat import cached_property from .._resource import SyncAPIResource, AsyncAPIResource from .._response import ( @@ -76,7 +76,7 @@ def retrieve( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return self._get( - f"/v3/templates/{id}", + path_template("/v3/templates/{id}", id=id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, @@ -200,7 +200,7 @@ async def retrieve( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return await self._get( - f"/v3/templates/{id}", + path_template("/v3/templates/{id}", id=id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, diff --git a/src/unlayer/resources/workspaces.py b/src/unlayer/resources/workspaces.py index be859fc..3ad5619 100644 --- a/src/unlayer/resources/workspaces.py +++ b/src/unlayer/resources/workspaces.py @@ -5,6 +5,7 @@ import httpx from .._types import Body, Query, Headers, NotGiven, not_given +from .._utils import path_template from .._compat import cached_property from .._resource import SyncAPIResource, AsyncAPIResource from .._response import ( @@ -70,7 +71,7 @@ def retrieve( if not workspace_id: raise ValueError(f"Expected a non-empty value for `workspace_id` but received {workspace_id!r}") return self._get( - f"/v3/workspaces/{workspace_id}", + path_template("/v3/workspaces/{workspace_id}", workspace_id=workspace_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -151,7 +152,7 @@ async def retrieve( if not workspace_id: raise ValueError(f"Expected a non-empty value for `workspace_id` but received {workspace_id!r}") return await self._get( - f"/v3/workspaces/{workspace_id}", + path_template("/v3/workspaces/{workspace_id}", workspace_id=workspace_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), diff --git a/tests/test_utils/test_path.py b/tests/test_utils/test_path.py new file mode 100644 index 0000000..8d48732 --- /dev/null +++ b/tests/test_utils/test_path.py @@ -0,0 +1,89 @@ +from __future__ import annotations + +from typing import Any + +import pytest + +from unlayer._utils._path import path_template + + +@pytest.mark.parametrize( + "template, kwargs, expected", + [ + ("/v1/{id}", dict(id="abc"), "/v1/abc"), + ("/v1/{a}/{b}", dict(a="x", b="y"), "/v1/x/y"), + ("/v1/{a}{b}/path/{c}?val={d}#{e}", dict(a="x", b="y", c="z", d="u", e="v"), "/v1/xy/path/z?val=u#v"), + ("/{w}/{w}", dict(w="echo"), "/echo/echo"), + ("/v1/static", {}, "/v1/static"), + ("", {}, ""), + ("/v1/?q={n}&count=10", dict(n=42), "/v1/?q=42&count=10"), + ("/v1/{v}", dict(v=None), "/v1/null"), + ("/v1/{v}", dict(v=True), "/v1/true"), + ("/v1/{v}", dict(v=False), "/v1/false"), + ("/v1/{v}", dict(v=".hidden"), "/v1/.hidden"), # dot prefix ok + ("/v1/{v}", dict(v="file.txt"), "/v1/file.txt"), # dot in middle ok + ("/v1/{v}", dict(v="..."), "/v1/..."), # triple dot ok + ("/v1/{a}{b}", dict(a=".", b="txt"), "/v1/.txt"), # dot var combining with adjacent to be ok + ("/items?q={v}#{f}", dict(v=".", f=".."), "/items?q=.#.."), # dots in query/fragment are fine + ( + "/v1/{a}?query={b}", + dict(a="../../other/endpoint", b="a&bad=true"), + "/v1/..%2F..%2Fother%2Fendpoint?query=a%26bad%3Dtrue", + ), + ("/v1/{val}", dict(val="a/b/c"), "/v1/a%2Fb%2Fc"), + ("/v1/{val}", dict(val="a/b/c?query=value"), "/v1/a%2Fb%2Fc%3Fquery=value"), + ("/v1/{val}", dict(val="a/b/c?query=value&bad=true"), "/v1/a%2Fb%2Fc%3Fquery=value&bad=true"), + ("/v1/{val}", dict(val="%20"), "/v1/%2520"), # escapes escape sequences in input + # Query: slash and ? are safe, # is not + ("/items?q={v}", dict(v="a/b"), "/items?q=a/b"), + ("/items?q={v}", dict(v="a?b"), "/items?q=a?b"), + ("/items?q={v}", dict(v="a#b"), "/items?q=a%23b"), + ("/items?q={v}", dict(v="a b"), "/items?q=a%20b"), + # Fragment: slash and ? are safe + ("/docs#{v}", dict(v="a/b"), "/docs#a/b"), + ("/docs#{v}", dict(v="a?b"), "/docs#a?b"), + # Path: slash, ? and # are all encoded + ("/v1/{v}", dict(v="a/b"), "/v1/a%2Fb"), + ("/v1/{v}", dict(v="a?b"), "/v1/a%3Fb"), + ("/v1/{v}", dict(v="a#b"), "/v1/a%23b"), + # same var encoded differently by component + ( + "/v1/{v}?q={v}#{v}", + dict(v="a/b?c#d"), + "/v1/a%2Fb%3Fc%23d?q=a/b?c%23d#a/b?c%23d", + ), + ("/v1/{val}", dict(val="x?admin=true"), "/v1/x%3Fadmin=true"), # query injection + ("/v1/{val}", dict(val="x#admin"), "/v1/x%23admin"), # fragment injection + ], +) +def test_interpolation(template: str, kwargs: dict[str, Any], expected: str) -> None: + assert path_template(template, **kwargs) == expected + + +def test_missing_kwarg_raises_key_error() -> None: + with pytest.raises(KeyError, match="org_id"): + path_template("/v1/{org_id}") + + +@pytest.mark.parametrize( + "template, kwargs", + [ + ("{a}/path", dict(a=".")), + ("{a}/path", dict(a="..")), + ("/v1/{a}", dict(a=".")), + ("/v1/{a}", dict(a="..")), + ("/v1/{a}/path", dict(a=".")), + ("/v1/{a}/path", dict(a="..")), + ("/v1/{a}{b}", dict(a=".", b=".")), # adjacent vars → ".." + ("/v1/{a}.", dict(a=".")), # var + static → ".." + ("/v1/{a}{b}", dict(a="", b=".")), # empty + dot → "." + ("/v1/%2e/{x}", dict(x="ok")), # encoded dot in static text + ("/v1/%2e./{x}", dict(x="ok")), # mixed encoded ".." in static + ("/v1/.%2E/{x}", dict(x="ok")), # mixed encoded ".." in static + ("/v1/{v}?q=1", dict(v="..")), + ("/v1/{v}#frag", dict(v="..")), + ], +) +def test_dot_segment_rejected(template: str, kwargs: dict[str, Any]) -> None: + with pytest.raises(ValueError, match="dot-segment"): + path_template(template, **kwargs) From a460056565b87193e01099eb3168f84e701b66af Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Fri, 20 Mar 2026 03:24:04 +0000 Subject: [PATCH 55/62] refactor(tests): switch from prism to steady --- CONTRIBUTING.md | 2 +- scripts/mock | 26 +++++++++++++------------- scripts/test | 16 ++++++++-------- 3 files changed, 22 insertions(+), 22 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 8f8a253..38563d9 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -85,7 +85,7 @@ $ pip install ./path-to-wheel-file.whl ## Running tests -Most tests require you to [set up a mock server](https://github.com/stoplightio/prism) against the OpenAPI spec to run the tests. +Most tests require you to [set up a mock server](https://github.com/dgellow/steady) against the OpenAPI spec to run the tests. ```sh $ ./scripts/mock diff --git a/scripts/mock b/scripts/mock index bcf3b39..38201de 100755 --- a/scripts/mock +++ b/scripts/mock @@ -19,34 +19,34 @@ fi echo "==> Starting mock server with URL ${URL}" -# Run prism mock on the given spec +# Run steady mock on the given spec if [ "$1" == "--daemon" ]; then # Pre-install the package so the download doesn't eat into the startup timeout - npm exec --package=@stainless-api/prism-cli@5.15.0 -- prism --version + npm exec --package=@stdy/cli@0.19.3 -- steady --version - npm exec --package=@stainless-api/prism-cli@5.15.0 -- prism mock "$URL" &> .prism.log & + npm exec --package=@stdy/cli@0.19.3 -- steady --host 127.0.0.1 -p 4010 --validator-query-array-format=comma --validator-query-object-format=brackets "$URL" &> .stdy.log & - # Wait for server to come online (max 30s) + # Wait for server to come online via health endpoint (max 30s) echo -n "Waiting for server" attempts=0 - while ! grep -q "✖ fatal\|Prism is listening" ".prism.log" ; do + while ! curl --silent --fail "http://127.0.0.1:4010/_x-steady/health" >/dev/null 2>&1; do + if ! kill -0 $! 2>/dev/null; then + echo + cat .stdy.log + exit 1 + fi attempts=$((attempts + 1)) if [ "$attempts" -ge 300 ]; then echo - echo "Timed out waiting for Prism server to start" - cat .prism.log + echo "Timed out waiting for Steady server to start" + cat .stdy.log exit 1 fi echo -n "." sleep 0.1 done - if grep -q "✖ fatal" ".prism.log"; then - cat .prism.log - exit 1 - fi - echo else - npm exec --package=@stainless-api/prism-cli@5.15.0 -- prism mock "$URL" + npm exec --package=@stdy/cli@0.19.3 -- steady --host 127.0.0.1 -p 4010 --validator-query-array-format=comma --validator-query-object-format=brackets "$URL" fi diff --git a/scripts/test b/scripts/test index dbeda2d..2dfdc40 100755 --- a/scripts/test +++ b/scripts/test @@ -9,8 +9,8 @@ GREEN='\033[0;32m' YELLOW='\033[0;33m' NC='\033[0m' # No Color -function prism_is_running() { - curl --silent "http://localhost:4010" >/dev/null 2>&1 +function steady_is_running() { + curl --silent "http://127.0.0.1:4010/_x-steady/health" >/dev/null 2>&1 } kill_server_on_port() { @@ -25,7 +25,7 @@ function is_overriding_api_base_url() { [ -n "$TEST_API_BASE_URL" ] } -if ! is_overriding_api_base_url && ! prism_is_running ; then +if ! is_overriding_api_base_url && ! steady_is_running ; then # When we exit this script, make sure to kill the background mock server process trap 'kill_server_on_port 4010' EXIT @@ -36,19 +36,19 @@ fi if is_overriding_api_base_url ; then echo -e "${GREEN}✔ Running tests against ${TEST_API_BASE_URL}${NC}" echo -elif ! prism_is_running ; then - echo -e "${RED}ERROR:${NC} The test suite will not run without a mock Prism server" +elif ! steady_is_running ; then + echo -e "${RED}ERROR:${NC} The test suite will not run without a mock Steady server" echo -e "running against your OpenAPI spec." echo echo -e "To run the server, pass in the path or url of your OpenAPI" - echo -e "spec to the prism command:" + echo -e "spec to the steady command:" echo - echo -e " \$ ${YELLOW}npm exec --package=@stainless-api/prism-cli@5.15.0 -- prism mock path/to/your.openapi.yml${NC}" + echo -e " \$ ${YELLOW}npm exec --package=@stdy/cli@0.19.3 -- steady path/to/your.openapi.yml --host 127.0.0.1 -p 4010 --validator-query-array-format=comma --validator-query-object-format=brackets${NC}" echo exit 1 else - echo -e "${GREEN}✔ Mock prism server is running with your OpenAPI spec${NC}" + echo -e "${GREEN}✔ Mock steady server is running with your OpenAPI spec${NC}" echo fi From fb8d51fe87ef290c4bf160386c5275428b0e7eb3 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Sat, 21 Mar 2026 04:17:12 +0000 Subject: [PATCH 56/62] chore(tests): bump steady to v0.19.4 --- scripts/mock | 6 +++--- scripts/test | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/scripts/mock b/scripts/mock index 38201de..e1c19e8 100755 --- a/scripts/mock +++ b/scripts/mock @@ -22,9 +22,9 @@ echo "==> Starting mock server with URL ${URL}" # Run steady mock on the given spec if [ "$1" == "--daemon" ]; then # Pre-install the package so the download doesn't eat into the startup timeout - npm exec --package=@stdy/cli@0.19.3 -- steady --version + npm exec --package=@stdy/cli@0.19.4 -- steady --version - npm exec --package=@stdy/cli@0.19.3 -- steady --host 127.0.0.1 -p 4010 --validator-query-array-format=comma --validator-query-object-format=brackets "$URL" &> .stdy.log & + npm exec --package=@stdy/cli@0.19.4 -- steady --host 127.0.0.1 -p 4010 --validator-form-array-format=comma --validator-query-array-format=comma --validator-form-object-format=brackets --validator-query-object-format=brackets "$URL" &> .stdy.log & # Wait for server to come online via health endpoint (max 30s) echo -n "Waiting for server" @@ -48,5 +48,5 @@ if [ "$1" == "--daemon" ]; then echo else - npm exec --package=@stdy/cli@0.19.3 -- steady --host 127.0.0.1 -p 4010 --validator-query-array-format=comma --validator-query-object-format=brackets "$URL" + npm exec --package=@stdy/cli@0.19.4 -- steady --host 127.0.0.1 -p 4010 --validator-form-array-format=comma --validator-query-array-format=comma --validator-form-object-format=brackets --validator-query-object-format=brackets "$URL" fi diff --git a/scripts/test b/scripts/test index 2dfdc40..36fab0a 100755 --- a/scripts/test +++ b/scripts/test @@ -43,7 +43,7 @@ elif ! steady_is_running ; then echo -e "To run the server, pass in the path or url of your OpenAPI" echo -e "spec to the steady command:" echo - echo -e " \$ ${YELLOW}npm exec --package=@stdy/cli@0.19.3 -- steady path/to/your.openapi.yml --host 127.0.0.1 -p 4010 --validator-query-array-format=comma --validator-query-object-format=brackets${NC}" + echo -e " \$ ${YELLOW}npm exec --package=@stdy/cli@0.19.4 -- steady path/to/your.openapi.yml --host 127.0.0.1 -p 4010 --validator-form-array-format=comma --validator-query-array-format=comma --validator-form-object-format=brackets --validator-query-object-format=brackets${NC}" echo exit 1 From 201041595b0b9547b9949ea5ca8f7e30ece56fd7 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Sat, 21 Mar 2026 04:21:49 +0000 Subject: [PATCH 57/62] chore(tests): bump steady to v0.19.5 --- scripts/mock | 6 +++--- scripts/test | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/scripts/mock b/scripts/mock index e1c19e8..ab814d3 100755 --- a/scripts/mock +++ b/scripts/mock @@ -22,9 +22,9 @@ echo "==> Starting mock server with URL ${URL}" # Run steady mock on the given spec if [ "$1" == "--daemon" ]; then # Pre-install the package so the download doesn't eat into the startup timeout - npm exec --package=@stdy/cli@0.19.4 -- steady --version + npm exec --package=@stdy/cli@0.19.5 -- steady --version - npm exec --package=@stdy/cli@0.19.4 -- steady --host 127.0.0.1 -p 4010 --validator-form-array-format=comma --validator-query-array-format=comma --validator-form-object-format=brackets --validator-query-object-format=brackets "$URL" &> .stdy.log & + npm exec --package=@stdy/cli@0.19.5 -- steady --host 127.0.0.1 -p 4010 --validator-form-array-format=comma --validator-query-array-format=comma --validator-form-object-format=brackets --validator-query-object-format=brackets "$URL" &> .stdy.log & # Wait for server to come online via health endpoint (max 30s) echo -n "Waiting for server" @@ -48,5 +48,5 @@ if [ "$1" == "--daemon" ]; then echo else - npm exec --package=@stdy/cli@0.19.4 -- steady --host 127.0.0.1 -p 4010 --validator-form-array-format=comma --validator-query-array-format=comma --validator-form-object-format=brackets --validator-query-object-format=brackets "$URL" + npm exec --package=@stdy/cli@0.19.5 -- steady --host 127.0.0.1 -p 4010 --validator-form-array-format=comma --validator-query-array-format=comma --validator-form-object-format=brackets --validator-query-object-format=brackets "$URL" fi diff --git a/scripts/test b/scripts/test index 36fab0a..d1c8e1a 100755 --- a/scripts/test +++ b/scripts/test @@ -43,7 +43,7 @@ elif ! steady_is_running ; then echo -e "To run the server, pass in the path or url of your OpenAPI" echo -e "spec to the steady command:" echo - echo -e " \$ ${YELLOW}npm exec --package=@stdy/cli@0.19.4 -- steady path/to/your.openapi.yml --host 127.0.0.1 -p 4010 --validator-form-array-format=comma --validator-query-array-format=comma --validator-form-object-format=brackets --validator-query-object-format=brackets${NC}" + echo -e " \$ ${YELLOW}npm exec --package=@stdy/cli@0.19.5 -- steady path/to/your.openapi.yml --host 127.0.0.1 -p 4010 --validator-form-array-format=comma --validator-query-array-format=comma --validator-form-object-format=brackets --validator-query-object-format=brackets${NC}" echo exit 1 From 9afbed5da82b84d67439833c0afd4c0cb8f3c848 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 24 Mar 2026 04:19:54 +0000 Subject: [PATCH 58/62] chore(internal): update gitignore --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 95ceb18..3824f4c 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ .prism.log +.stdy.log _dev __pycache__ From d59860cf9d43af501b8d749a7a14d1cadc068dbe Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 24 Mar 2026 04:25:10 +0000 Subject: [PATCH 59/62] chore(tests): bump steady to v0.19.6 --- scripts/mock | 6 +++--- scripts/test | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/scripts/mock b/scripts/mock index ab814d3..b319bdf 100755 --- a/scripts/mock +++ b/scripts/mock @@ -22,9 +22,9 @@ echo "==> Starting mock server with URL ${URL}" # Run steady mock on the given spec if [ "$1" == "--daemon" ]; then # Pre-install the package so the download doesn't eat into the startup timeout - npm exec --package=@stdy/cli@0.19.5 -- steady --version + npm exec --package=@stdy/cli@0.19.6 -- steady --version - npm exec --package=@stdy/cli@0.19.5 -- steady --host 127.0.0.1 -p 4010 --validator-form-array-format=comma --validator-query-array-format=comma --validator-form-object-format=brackets --validator-query-object-format=brackets "$URL" &> .stdy.log & + npm exec --package=@stdy/cli@0.19.6 -- steady --host 127.0.0.1 -p 4010 --validator-form-array-format=comma --validator-query-array-format=comma --validator-form-object-format=brackets --validator-query-object-format=brackets "$URL" &> .stdy.log & # Wait for server to come online via health endpoint (max 30s) echo -n "Waiting for server" @@ -48,5 +48,5 @@ if [ "$1" == "--daemon" ]; then echo else - npm exec --package=@stdy/cli@0.19.5 -- steady --host 127.0.0.1 -p 4010 --validator-form-array-format=comma --validator-query-array-format=comma --validator-form-object-format=brackets --validator-query-object-format=brackets "$URL" + npm exec --package=@stdy/cli@0.19.6 -- steady --host 127.0.0.1 -p 4010 --validator-form-array-format=comma --validator-query-array-format=comma --validator-form-object-format=brackets --validator-query-object-format=brackets "$URL" fi diff --git a/scripts/test b/scripts/test index d1c8e1a..ab01948 100755 --- a/scripts/test +++ b/scripts/test @@ -43,7 +43,7 @@ elif ! steady_is_running ; then echo -e "To run the server, pass in the path or url of your OpenAPI" echo -e "spec to the steady command:" echo - echo -e " \$ ${YELLOW}npm exec --package=@stdy/cli@0.19.5 -- steady path/to/your.openapi.yml --host 127.0.0.1 -p 4010 --validator-form-array-format=comma --validator-query-array-format=comma --validator-form-object-format=brackets --validator-query-object-format=brackets${NC}" + echo -e " \$ ${YELLOW}npm exec --package=@stdy/cli@0.19.6 -- steady path/to/your.openapi.yml --host 127.0.0.1 -p 4010 --validator-form-array-format=comma --validator-query-array-format=comma --validator-form-object-format=brackets --validator-query-object-format=brackets${NC}" echo exit 1 From 4f4afd4ea7956d39900052a0316687e3b8bc92f3 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Wed, 25 Mar 2026 03:05:16 +0000 Subject: [PATCH 60/62] chore(ci): skip lint on metadata-only changes Note that we still want to run tests, as these depend on the metadata. --- .github/workflows/ci.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 581125c..cb10d13 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -19,7 +19,7 @@ jobs: timeout-minutes: 10 name: lint runs-on: ${{ github.repository == 'stainless-sdks/unlayer-python' && 'depot-ubuntu-24.04' || 'ubuntu-latest' }} - if: github.event_name == 'push' || github.event.pull_request.head.repo.fork + if: (github.event_name == 'push' || github.event.pull_request.head.repo.fork) && (github.event_name != 'push' || github.event.head_commit.message != 'codegen metadata') steps: - uses: actions/checkout@v6 @@ -38,7 +38,7 @@ jobs: run: ./scripts/lint build: - if: github.event_name == 'push' || github.event.pull_request.head.repo.fork + if: (github.event_name == 'push' || github.event.pull_request.head.repo.fork) && (github.event_name != 'push' || github.event.head_commit.message != 'codegen metadata') timeout-minutes: 10 name: build permissions: From 475b68a4354de160e445d93ddb9062e809442b52 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Wed, 25 Mar 2026 03:05:49 +0000 Subject: [PATCH 61/62] chore(tests): bump steady to v0.19.7 --- scripts/mock | 6 +++--- scripts/test | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/scripts/mock b/scripts/mock index b319bdf..09eb49f 100755 --- a/scripts/mock +++ b/scripts/mock @@ -22,9 +22,9 @@ echo "==> Starting mock server with URL ${URL}" # Run steady mock on the given spec if [ "$1" == "--daemon" ]; then # Pre-install the package so the download doesn't eat into the startup timeout - npm exec --package=@stdy/cli@0.19.6 -- steady --version + npm exec --package=@stdy/cli@0.19.7 -- steady --version - npm exec --package=@stdy/cli@0.19.6 -- steady --host 127.0.0.1 -p 4010 --validator-form-array-format=comma --validator-query-array-format=comma --validator-form-object-format=brackets --validator-query-object-format=brackets "$URL" &> .stdy.log & + npm exec --package=@stdy/cli@0.19.7 -- steady --host 127.0.0.1 -p 4010 --validator-form-array-format=comma --validator-query-array-format=comma --validator-form-object-format=brackets --validator-query-object-format=brackets "$URL" &> .stdy.log & # Wait for server to come online via health endpoint (max 30s) echo -n "Waiting for server" @@ -48,5 +48,5 @@ if [ "$1" == "--daemon" ]; then echo else - npm exec --package=@stdy/cli@0.19.6 -- steady --host 127.0.0.1 -p 4010 --validator-form-array-format=comma --validator-query-array-format=comma --validator-form-object-format=brackets --validator-query-object-format=brackets "$URL" + npm exec --package=@stdy/cli@0.19.7 -- steady --host 127.0.0.1 -p 4010 --validator-form-array-format=comma --validator-query-array-format=comma --validator-form-object-format=brackets --validator-query-object-format=brackets "$URL" fi diff --git a/scripts/test b/scripts/test index ab01948..e46b9b5 100755 --- a/scripts/test +++ b/scripts/test @@ -43,7 +43,7 @@ elif ! steady_is_running ; then echo -e "To run the server, pass in the path or url of your OpenAPI" echo -e "spec to the steady command:" echo - echo -e " \$ ${YELLOW}npm exec --package=@stdy/cli@0.19.6 -- steady path/to/your.openapi.yml --host 127.0.0.1 -p 4010 --validator-form-array-format=comma --validator-query-array-format=comma --validator-form-object-format=brackets --validator-query-object-format=brackets${NC}" + echo -e " \$ ${YELLOW}npm exec --package=@stdy/cli@0.19.7 -- steady path/to/your.openapi.yml --host 127.0.0.1 -p 4010 --validator-form-array-format=comma --validator-query-array-format=comma --validator-form-object-format=brackets --validator-query-object-format=brackets${NC}" echo exit 1 From df99d903f16d75e89f47d138769089682a4307b0 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Fri, 27 Mar 2026 05:32:30 +0000 Subject: [PATCH 62/62] feat(internal): implement indices array format for query and form serialization --- scripts/mock | 4 ++-- scripts/test | 2 +- src/unlayer/_qs.py | 5 ++++- 3 files changed, 7 insertions(+), 4 deletions(-) diff --git a/scripts/mock b/scripts/mock index 09eb49f..290e21b 100755 --- a/scripts/mock +++ b/scripts/mock @@ -24,7 +24,7 @@ if [ "$1" == "--daemon" ]; then # Pre-install the package so the download doesn't eat into the startup timeout npm exec --package=@stdy/cli@0.19.7 -- steady --version - npm exec --package=@stdy/cli@0.19.7 -- steady --host 127.0.0.1 -p 4010 --validator-form-array-format=comma --validator-query-array-format=comma --validator-form-object-format=brackets --validator-query-object-format=brackets "$URL" &> .stdy.log & + npm exec --package=@stdy/cli@0.19.7 -- steady --host 127.0.0.1 -p 4010 --validator-query-array-format=comma --validator-form-array-format=comma --validator-query-object-format=brackets --validator-form-object-format=brackets "$URL" &> .stdy.log & # Wait for server to come online via health endpoint (max 30s) echo -n "Waiting for server" @@ -48,5 +48,5 @@ if [ "$1" == "--daemon" ]; then echo else - npm exec --package=@stdy/cli@0.19.7 -- steady --host 127.0.0.1 -p 4010 --validator-form-array-format=comma --validator-query-array-format=comma --validator-form-object-format=brackets --validator-query-object-format=brackets "$URL" + npm exec --package=@stdy/cli@0.19.7 -- steady --host 127.0.0.1 -p 4010 --validator-query-array-format=comma --validator-form-array-format=comma --validator-query-object-format=brackets --validator-form-object-format=brackets "$URL" fi diff --git a/scripts/test b/scripts/test index e46b9b5..661f9bf 100755 --- a/scripts/test +++ b/scripts/test @@ -43,7 +43,7 @@ elif ! steady_is_running ; then echo -e "To run the server, pass in the path or url of your OpenAPI" echo -e "spec to the steady command:" echo - echo -e " \$ ${YELLOW}npm exec --package=@stdy/cli@0.19.7 -- steady path/to/your.openapi.yml --host 127.0.0.1 -p 4010 --validator-form-array-format=comma --validator-query-array-format=comma --validator-form-object-format=brackets --validator-query-object-format=brackets${NC}" + echo -e " \$ ${YELLOW}npm exec --package=@stdy/cli@0.19.7 -- steady path/to/your.openapi.yml --host 127.0.0.1 -p 4010 --validator-query-array-format=comma --validator-form-array-format=comma --validator-query-object-format=brackets --validator-form-object-format=brackets${NC}" echo exit 1 diff --git a/src/unlayer/_qs.py b/src/unlayer/_qs.py index ada6fd3..de8c99b 100644 --- a/src/unlayer/_qs.py +++ b/src/unlayer/_qs.py @@ -101,7 +101,10 @@ def _stringify_item( items.extend(self._stringify_item(key, item, opts)) return items elif array_format == "indices": - raise NotImplementedError("The array indices format is not supported yet") + items = [] + for i, item in enumerate(value): + items.extend(self._stringify_item(f"{key}[{i}]", item, opts)) + return items elif array_format == "brackets": items = [] key = key + "[]"