diff --git a/.devcontainer/devcontainer-lock.json b/.devcontainer/devcontainer-lock.json new file mode 100644 index 00000000..96d86886 --- /dev/null +++ b/.devcontainer/devcontainer-lock.json @@ -0,0 +1,9 @@ +{ + "features": { + "ghcr.io/devcontainers/features/node:1": { + "version": "1.7.1", + "resolved": "ghcr.io/devcontainers/features/node@sha256:8c0de46939b61958041700ee89e3493f3b2e4131a06dc46b4d9423427d06e5f6", + "integrity": "sha256:8c0de46939b61958041700ee89e3493f3b2e4131a06dc46b4d9423427d06e5f6" + } + } +} diff --git a/.github/workflows/release-doctor.yml b/.github/workflows/release-doctor.yml index a6374d9d..0ba26a30 100644 --- a/.github/workflows/release-doctor.yml +++ b/.github/workflows/release-doctor.yml @@ -2,7 +2,7 @@ name: Release Doctor on: pull_request: branches: - - master + - main workflow_dispatch: jobs: diff --git a/.github/workflows/release-please.yml b/.github/workflows/release-please.yml index a922d704..07734d2e 100644 --- a/.github/workflows/release-please.yml +++ b/.github/workflows/release-please.yml @@ -2,7 +2,7 @@ name: Release Please on: push: branches: - - master + - main permissions: contents: write diff --git a/.github/workflows/stlc-promote.yml b/.github/workflows/stlc-promote.yml deleted file mode 100644 index 69e80f0a..00000000 --- a/.github/workflows/stlc-promote.yml +++ /dev/null @@ -1,65 +0,0 @@ -name: Promote SDKs - -on: - push: - branches: [main] - -permissions: - contents: read - -jobs: - promote: - runs-on: ubuntu-latest - env: - PRODUCTION_REPO: imagekit-developer/imagekit-python - PRODUCTION_BRANCH: master - GH_TOKEN: ${{ secrets.SDK_WRITE_TOKEN }} - steps: - - name: Check out staging - uses: actions/checkout@v4 - with: - fetch-depth: 0 - persist-credentials: false - - - name: Fetch production main - run: | - git remote add production \ - "https://x-access-token:${GH_TOKEN}@github.com/${PRODUCTION_REPO}.git" - git fetch production "${PRODUCTION_BRANCH}" - - - name: Check if production is already in sync - id: diff - run: | - STAGING_SHA=$(git rev-parse origin/main) - PRODUCTION_SHA=$(git rev-parse production/${PRODUCTION_BRANCH}) - if [ "$STAGING_SHA" = "$PRODUCTION_SHA" ]; then - echo "Production is already at $STAGING_SHA. Nothing to release." - echo "synced=true" >> "$GITHUB_OUTPUT" - else - echo "synced=false" >> "$GITHUB_OUTPUT" - fi - - - name: Push staging main to the release branch on production - if: steps.diff.outputs.synced == 'false' - run: | - git push production origin/main:refs/heads/stainless/release --force - - - name: Open or update the release PR on production - if: steps.diff.outputs.synced == 'false' - run: | - EXISTING_PR=$(gh pr list \ - --repo "${PRODUCTION_REPO}" \ - --head stainless/release \ - --state open \ - --json number \ - --jq '.[0].number') - if [ -z "${EXISTING_PR}" ]; then - gh pr create \ - --repo "${PRODUCTION_REPO}" \ - --base "${PRODUCTION_BRANCH}" \ - --head stainless/release \ - --title "Release SDK updates" \ - --body "$(git log --oneline production/${PRODUCTION_BRANCH}..origin/main)" - else - echo "Release PR #${EXISTING_PR} already exists. Force-push has updated it." - fi diff --git a/.github/workflows/sync-release-as.yml b/.github/workflows/sync-release-as.yml index 99150044..6ce6a7a1 100644 --- a/.github/workflows/sync-release-as.yml +++ b/.github/workflows/sync-release-as.yml @@ -10,7 +10,7 @@ permissions: jobs: sync: if: >- - github.event.pull_request.base.ref == 'master' && + github.event.pull_request.base.ref == 'main' && startsWith(github.event.pull_request.head.ref, 'release-please--') && github.event.changes.title != null runs-on: ubuntu-latest @@ -42,11 +42,11 @@ jobs: fi echo "version=$NEW_VERSION" >> "$GITHUB_OUTPUT" - - name: Check out master + - name: Check out main if: steps.parse.outputs.skip != 'true' uses: actions/checkout@v6 with: - ref: master + ref: main token: ${{ secrets.RELEASE_PLEASE_TOKEN }} fetch-depth: 1 @@ -60,4 +60,4 @@ jobs: git commit --allow-empty -m "chore: pin next release Release-As: ${VERSION}" - git push origin master + git push origin main diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 4a0511b9..8ed0bbc0 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "5.7.0" + ".": "5.9.0" } \ No newline at end of file diff --git a/.stats.yml b/.stats.yml index 58526a77..c71d849e 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1 +1 @@ -configured_endpoints: 48 +configured_endpoints: 54 diff --git a/CHANGELOG.md b/CHANGELOG.md index 0b07a519..19568404 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,28 @@ # Changelog +## [5.9.0](https://github.com/imagekit-developer/imagekit-python/compare/v5.8.0...v5.9.0) (2026-08-15) + + +### Features + +* add description field in custom metadata field api ([70bca6b](https://github.com/imagekit-developer/imagekit-python/commit/70bca6b7d48109cfa538f45db839cefc776630c7)) +* **api:** Named Transformations ([960bcdb](https://github.com/imagekit-developer/imagekit-python/commit/960bcdbdd9488aac929f519cb520897bf35bbf48)) + + +### Chores + +* pin next release ([1d08373](https://github.com/imagekit-developer/imagekit-python/commit/1d08373869924039a64c698e93082973110481e6)) +* update branch names in workflow and delete promote workflow ([1753b17](https://github.com/imagekit-developer/imagekit-python/commit/1753b17e626783768c8fd05dd3e4bae58c7eb3a1)) +* update default branch name to main ([85d2edf](https://github.com/imagekit-developer/imagekit-python/commit/85d2edfcef1e66229f509e356ee459646cab3074)) +* update mypy configuration to exclude additional files and prevent crashes from cyclic TypedDicts ([7f3623f](https://github.com/imagekit-developer/imagekit-python/commit/7f3623f39401f0fb61fd00c6cccc8fdea0840cd7)) + +## [5.8.0](https://github.com/imagekit-developer/imagekit-python/compare/v5.7.0...v5.8.0) (2026-07-14) + + +### Features + +* **api:** add usage analytics breakdown endpoint ([d072e97](https://github.com/imagekit-developer/imagekit-python/commit/d072e970b046f3792a760a413f84599c8f3d68fa)) + ## [5.7.0](https://github.com/imagekit-developer/imagekit-python/compare/v5.6.0...v5.7.0) (2026-06-18) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 43b35bfe..b8304fb3 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/imagekit-developer/imagekit-python#master.git +$ pip install git+ssh://git@github.com/imagekit-developer/imagekit-python.git ``` Alternatively, you can build from source and install the wheel file: diff --git a/README.md b/README.md index 6ea03a48..38e270e8 100644 --- a/README.md +++ b/README.md @@ -722,9 +722,9 @@ file = response.parse() # get the object that `files.upload()` would have retur print(file.file_id) ``` -These methods return an [`APIResponse`](https://github.com/imagekit-developer/imagekit-python/tree/master/src/imagekitio/_response.py) object. +These methods return an [`APIResponse`](https://github.com/imagekit-developer/imagekit-python/tree/main/src/imagekitio/_response.py) object. -The async client returns an [`AsyncAPIResponse`](https://github.com/imagekit-developer/imagekit-python/tree/master/src/imagekitio/_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/imagekit-developer/imagekit-python/tree/main/src/imagekitio/_response.py) with the same structure, the only difference being `await`able methods for reading the response content. #### `.with_streaming_response` diff --git a/api.md b/api.md index a331b681..bc986796 100644 --- a/api.md +++ b/api.md @@ -7,6 +7,7 @@ from imagekitio.types import ( Extensions, GetImageAttributesOptions, ImageOverlay, + NamedTransformation, Overlay, OverlayPosition, OverlayTiming, @@ -131,6 +132,22 @@ Methods: - client.saved_extensions.delete(id) -> None - client.saved_extensions.get(id) -> SavedExtension +# NamedTransformations + +Types: + +```python +from imagekitio.types import NamedTransformationListResponse +``` + +Methods: + +- client.named_transformations.create(\*\*params) -> NamedTransformation +- client.named_transformations.update(id, \*\*params) -> NamedTransformation +- client.named_transformations.list() -> NamedTransformationListResponse +- client.named_transformations.delete(id) -> None +- client.named_transformations.get(id) -> NamedTransformation + # Assets Types: @@ -206,6 +223,18 @@ Methods: - client.accounts.usage.get(\*\*params) -> UsageGetResponse +## UsageAnalytics + +Types: + +```python +from imagekitio.types.accounts import RequestBandwidthEntry, UsageAnalyticsResponse +``` + +Methods: + +- client.accounts.usage_analytics.get(\*\*params) -> UsageAnalyticsResponse + ## Origins Types: diff --git a/pyproject.toml b/pyproject.toml index d21d0e6c..d02aac87 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "imagekitio" -version = "5.7.0" +version = "5.9.0" description = "The official Python library for the ImageKit API" dynamic = ["readme"] license = "Apache-2.0" @@ -127,7 +127,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/imagekit-developer/imagekit-python/tree/master/\g<2>)' +replacement = '[\1](https://github.com/imagekit-developer/imagekit-python/tree/main/\g<2>)' [tool.pytest.ini_options] testpaths = ["tests"] @@ -169,7 +169,16 @@ show_error_codes = true # # We also exclude our `tests` as mypy doesn't always infer # types correctly and Pyright will still catch any type errors. -exclude = ["src/imagekitio/_files.py", "_dev/.*.py", "tests/.*"] +# +# Dummy resource exists only to force shared models into the SDK; its +# TypedDict aggregates mutually recursive overlay types and crashes mypy. +exclude = [ + "src/imagekitio/_files.py", + "_dev/.*.py", + "tests/.*", + "src/imagekitio/types/dummy_create_params.py", + "src/imagekitio/resources/dummy.py", +] strict_equality = true implicit_reexport = true @@ -210,6 +219,16 @@ module = "black.files.*" ignore_errors = true ignore_missing_imports = true +# Client imports dummy (for shared-model codegen). exclude alone is not enough — +# mypy still follows that import and crashes on the cyclic TypedDict. +[[tool.mypy.overrides]] +module = [ + "imagekitio.types.dummy_create_params", + "imagekitio.resources.dummy", +] +ignore_errors = true +follow_imports = "skip" + [tool.ruff] line-length = 120 diff --git a/scripts/utils/upload-artifact.sh b/scripts/utils/upload-artifact.sh index f6c63024..ace7ffbf 100755 --- a/scripts/utils/upload-artifact.sh +++ b/scripts/utils/upload-artifact.sh @@ -20,7 +20,7 @@ UPLOAD_RESPONSE=$(curl -v -X PUT \ 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/imagekit-python-staging/$SHA/$FILENAME'\033[0m" + echo -e "\033[32mInstallation: pip install 'https://pkg.stainless.com/s/imagekit-python/$SHA/$FILENAME'\033[0m" else echo -e "\033[31mFailed to upload artifact.\033[0m" exit 1 diff --git a/src/imagekitio/_client.py b/src/imagekitio/_client.py index 506c1dc4..7198d7ca 100644 --- a/src/imagekitio/_client.py +++ b/src/imagekitio/_client.py @@ -46,6 +46,7 @@ folders, accounts, saved_extensions, + named_transformations, custom_metadata_fields, ) from .lib.helper import HelperResource, AsyncHelperResource @@ -58,6 +59,7 @@ from .resources.folders.folders import FoldersResource, AsyncFoldersResource from .resources.saved_extensions import SavedExtensionsResource, AsyncSavedExtensionsResource from .resources.accounts.accounts import AccountsResource, AsyncAccountsResource + from .resources.named_transformations import NamedTransformationsResource, AsyncNamedTransformationsResource from .resources.custom_metadata_fields import CustomMetadataFieldsResource, AsyncCustomMetadataFieldsResource __all__ = [ @@ -176,6 +178,12 @@ def saved_extensions(self) -> SavedExtensionsResource: return SavedExtensionsResource(self) + @cached_property + def named_transformations(self) -> NamedTransformationsResource: + from .resources.named_transformations import NamedTransformationsResource + + return NamedTransformationsResource(self) + @cached_property def assets(self) -> AssetsResource: from .resources.assets import AssetsResource @@ -453,6 +461,12 @@ def saved_extensions(self) -> AsyncSavedExtensionsResource: return AsyncSavedExtensionsResource(self) + @cached_property + def named_transformations(self) -> AsyncNamedTransformationsResource: + from .resources.named_transformations import AsyncNamedTransformationsResource + + return AsyncNamedTransformationsResource(self) + @cached_property def assets(self) -> AsyncAssetsResource: from .resources.assets import AsyncAssetsResource @@ -656,6 +670,12 @@ def saved_extensions(self) -> saved_extensions.SavedExtensionsResourceWithRawRes return SavedExtensionsResourceWithRawResponse(self._client.saved_extensions) + @cached_property + def named_transformations(self) -> named_transformations.NamedTransformationsResourceWithRawResponse: + from .resources.named_transformations import NamedTransformationsResourceWithRawResponse + + return NamedTransformationsResourceWithRawResponse(self._client.named_transformations) + @cached_property def assets(self) -> assets.AssetsResourceWithRawResponse: from .resources.assets import AssetsResourceWithRawResponse @@ -717,6 +737,12 @@ def saved_extensions(self) -> saved_extensions.AsyncSavedExtensionsResourceWithR return AsyncSavedExtensionsResourceWithRawResponse(self._client.saved_extensions) + @cached_property + def named_transformations(self) -> named_transformations.AsyncNamedTransformationsResourceWithRawResponse: + from .resources.named_transformations import AsyncNamedTransformationsResourceWithRawResponse + + return AsyncNamedTransformationsResourceWithRawResponse(self._client.named_transformations) + @cached_property def assets(self) -> assets.AsyncAssetsResourceWithRawResponse: from .resources.assets import AsyncAssetsResourceWithRawResponse @@ -778,6 +804,12 @@ def saved_extensions(self) -> saved_extensions.SavedExtensionsResourceWithStream return SavedExtensionsResourceWithStreamingResponse(self._client.saved_extensions) + @cached_property + def named_transformations(self) -> named_transformations.NamedTransformationsResourceWithStreamingResponse: + from .resources.named_transformations import NamedTransformationsResourceWithStreamingResponse + + return NamedTransformationsResourceWithStreamingResponse(self._client.named_transformations) + @cached_property def assets(self) -> assets.AssetsResourceWithStreamingResponse: from .resources.assets import AssetsResourceWithStreamingResponse @@ -839,6 +871,12 @@ def saved_extensions(self) -> saved_extensions.AsyncSavedExtensionsResourceWithS return AsyncSavedExtensionsResourceWithStreamingResponse(self._client.saved_extensions) + @cached_property + def named_transformations(self) -> named_transformations.AsyncNamedTransformationsResourceWithStreamingResponse: + from .resources.named_transformations import AsyncNamedTransformationsResourceWithStreamingResponse + + return AsyncNamedTransformationsResourceWithStreamingResponse(self._client.named_transformations) + @cached_property def assets(self) -> assets.AsyncAssetsResourceWithStreamingResponse: from .resources.assets import AsyncAssetsResourceWithStreamingResponse diff --git a/src/imagekitio/_files.py b/src/imagekitio/_files.py index 36332505..843f87de 100644 --- a/src/imagekitio/_files.py +++ b/src/imagekitio/_files.py @@ -36,7 +36,7 @@ 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. See https://github.com/imagekit-developer/imagekit-python/tree/master#file-uploads" + f"{prefix} to be bytes, an io.IOBase instance, PathLike or a tuple but received {type(obj)} instead. See https://github.com/imagekit-developer/imagekit-python/tree/main#file-uploads" ) from None diff --git a/src/imagekitio/_version.py b/src/imagekitio/_version.py index f86b8e12..9d4c9bca 100644 --- a/src/imagekitio/_version.py +++ b/src/imagekitio/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "imagekitio" -__version__ = "5.7.0" # x-release-please-version +__version__ = "5.9.0" # x-release-please-version diff --git a/src/imagekitio/lib/helper.py b/src/imagekitio/lib/helper.py index 19320c39..7ca9660b 100644 --- a/src/imagekitio/lib/helper.py +++ b/src/imagekitio/lib/helper.py @@ -247,9 +247,6 @@ def _process_text(s: str, encoding: str) -> str: def _process_overlay(overlay: Overlay) -> str: """Process overlay transformations.""" - if not overlay: - return "" - # Extract type, position, timing, and transformation from overlay overlay_type: str = cast(str, overlay.get("type", "")) position: Dict[str, Any] = cast(Dict[str, Any], overlay.get("position", {})) diff --git a/src/imagekitio/resources/__init__.py b/src/imagekitio/resources/__init__.py index cbd36eeb..cc245c5e 100644 --- a/src/imagekitio/resources/__init__.py +++ b/src/imagekitio/resources/__init__.py @@ -69,6 +69,14 @@ SavedExtensionsResourceWithStreamingResponse, AsyncSavedExtensionsResourceWithStreamingResponse, ) +from .named_transformations import ( + NamedTransformationsResource, + AsyncNamedTransformationsResource, + NamedTransformationsResourceWithRawResponse, + AsyncNamedTransformationsResourceWithRawResponse, + NamedTransformationsResourceWithStreamingResponse, + AsyncNamedTransformationsResourceWithStreamingResponse, +) from .custom_metadata_fields import ( CustomMetadataFieldsResource, AsyncCustomMetadataFieldsResource, @@ -103,6 +111,12 @@ "AsyncSavedExtensionsResourceWithRawResponse", "SavedExtensionsResourceWithStreamingResponse", "AsyncSavedExtensionsResourceWithStreamingResponse", + "NamedTransformationsResource", + "AsyncNamedTransformationsResource", + "NamedTransformationsResourceWithRawResponse", + "AsyncNamedTransformationsResourceWithRawResponse", + "NamedTransformationsResourceWithStreamingResponse", + "AsyncNamedTransformationsResourceWithStreamingResponse", "AssetsResource", "AsyncAssetsResource", "AssetsResourceWithRawResponse", diff --git a/src/imagekitio/resources/accounts/__init__.py b/src/imagekitio/resources/accounts/__init__.py index fc56413d..1e10f739 100644 --- a/src/imagekitio/resources/accounts/__init__.py +++ b/src/imagekitio/resources/accounts/__init__.py @@ -32,6 +32,14 @@ URLEndpointsResourceWithStreamingResponse, AsyncURLEndpointsResourceWithStreamingResponse, ) +from .usage_analytics import ( + UsageAnalyticsResource, + AsyncUsageAnalyticsResource, + UsageAnalyticsResourceWithRawResponse, + AsyncUsageAnalyticsResourceWithRawResponse, + UsageAnalyticsResourceWithStreamingResponse, + AsyncUsageAnalyticsResourceWithStreamingResponse, +) __all__ = [ "UsageResource", @@ -40,6 +48,12 @@ "AsyncUsageResourceWithRawResponse", "UsageResourceWithStreamingResponse", "AsyncUsageResourceWithStreamingResponse", + "UsageAnalyticsResource", + "AsyncUsageAnalyticsResource", + "UsageAnalyticsResourceWithRawResponse", + "AsyncUsageAnalyticsResourceWithRawResponse", + "UsageAnalyticsResourceWithStreamingResponse", + "AsyncUsageAnalyticsResourceWithStreamingResponse", "OriginsResource", "AsyncOriginsResource", "OriginsResourceWithRawResponse", diff --git a/src/imagekitio/resources/accounts/accounts.py b/src/imagekitio/resources/accounts/accounts.py index 461e8cff..a15e3434 100644 --- a/src/imagekitio/resources/accounts/accounts.py +++ b/src/imagekitio/resources/accounts/accounts.py @@ -28,6 +28,14 @@ URLEndpointsResourceWithStreamingResponse, AsyncURLEndpointsResourceWithStreamingResponse, ) +from .usage_analytics import ( + UsageAnalyticsResource, + AsyncUsageAnalyticsResource, + UsageAnalyticsResourceWithRawResponse, + AsyncUsageAnalyticsResourceWithRawResponse, + UsageAnalyticsResourceWithStreamingResponse, + AsyncUsageAnalyticsResourceWithStreamingResponse, +) __all__ = ["AccountsResource", "AsyncAccountsResource"] @@ -37,6 +45,10 @@ class AccountsResource(SyncAPIResource): def usage(self) -> UsageResource: return UsageResource(self._client) + @cached_property + def usage_analytics(self) -> UsageAnalyticsResource: + return UsageAnalyticsResource(self._client) + @cached_property def origins(self) -> OriginsResource: return OriginsResource(self._client) @@ -70,6 +82,10 @@ class AsyncAccountsResource(AsyncAPIResource): def usage(self) -> AsyncUsageResource: return AsyncUsageResource(self._client) + @cached_property + def usage_analytics(self) -> AsyncUsageAnalyticsResource: + return AsyncUsageAnalyticsResource(self._client) + @cached_property def origins(self) -> AsyncOriginsResource: return AsyncOriginsResource(self._client) @@ -106,6 +122,10 @@ def __init__(self, accounts: AccountsResource) -> None: def usage(self) -> UsageResourceWithRawResponse: return UsageResourceWithRawResponse(self._accounts.usage) + @cached_property + def usage_analytics(self) -> UsageAnalyticsResourceWithRawResponse: + return UsageAnalyticsResourceWithRawResponse(self._accounts.usage_analytics) + @cached_property def origins(self) -> OriginsResourceWithRawResponse: return OriginsResourceWithRawResponse(self._accounts.origins) @@ -123,6 +143,10 @@ def __init__(self, accounts: AsyncAccountsResource) -> None: def usage(self) -> AsyncUsageResourceWithRawResponse: return AsyncUsageResourceWithRawResponse(self._accounts.usage) + @cached_property + def usage_analytics(self) -> AsyncUsageAnalyticsResourceWithRawResponse: + return AsyncUsageAnalyticsResourceWithRawResponse(self._accounts.usage_analytics) + @cached_property def origins(self) -> AsyncOriginsResourceWithRawResponse: return AsyncOriginsResourceWithRawResponse(self._accounts.origins) @@ -140,6 +164,10 @@ def __init__(self, accounts: AccountsResource) -> None: def usage(self) -> UsageResourceWithStreamingResponse: return UsageResourceWithStreamingResponse(self._accounts.usage) + @cached_property + def usage_analytics(self) -> UsageAnalyticsResourceWithStreamingResponse: + return UsageAnalyticsResourceWithStreamingResponse(self._accounts.usage_analytics) + @cached_property def origins(self) -> OriginsResourceWithStreamingResponse: return OriginsResourceWithStreamingResponse(self._accounts.origins) @@ -157,6 +185,10 @@ def __init__(self, accounts: AsyncAccountsResource) -> None: def usage(self) -> AsyncUsageResourceWithStreamingResponse: return AsyncUsageResourceWithStreamingResponse(self._accounts.usage) + @cached_property + def usage_analytics(self) -> AsyncUsageAnalyticsResourceWithStreamingResponse: + return AsyncUsageAnalyticsResourceWithStreamingResponse(self._accounts.usage_analytics) + @cached_property def origins(self) -> AsyncOriginsResourceWithStreamingResponse: return AsyncOriginsResourceWithStreamingResponse(self._accounts.origins) diff --git a/src/imagekitio/resources/accounts/usage.py b/src/imagekitio/resources/accounts/usage.py index b35d3c9b..3501e726 100644 --- a/src/imagekitio/resources/accounts/usage.py +++ b/src/imagekitio/resources/accounts/usage.py @@ -63,6 +63,12 @@ def get( other words, the data covers the period starting from the specified start date up to, but not including, the end date. + For an agency account, the returned usage is aggregated across the agency and + all of its child accounts that are billed to it. + + The response is cached for 6 hours per account, date range and requested + metrics. + Args: end_date: Specify a `endDate` in `YYYY-MM-DD` format. It should be after the `startDate`. The difference between `startDate` and `endDate` should be less than 90 days. @@ -136,6 +142,12 @@ async def get( other words, the data covers the period starting from the specified start date up to, but not including, the end date. + For an agency account, the returned usage is aggregated across the agency and + all of its child accounts that are billed to it. + + The response is cached for 6 hours per account, date range and requested + metrics. + Args: end_date: Specify a `endDate` in `YYYY-MM-DD` format. It should be after the `startDate`. The difference between `startDate` and `endDate` should be less than 90 days. diff --git a/src/imagekitio/resources/accounts/usage_analytics.py b/src/imagekitio/resources/accounts/usage_analytics.py new file mode 100644 index 00000000..e2880fea --- /dev/null +++ b/src/imagekitio/resources/accounts/usage_analytics.py @@ -0,0 +1,224 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import Union +from datetime import date + +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.accounts import usage_analytics_get_params +from ...types.accounts.usage_analytics_response import UsageAnalyticsResponse + +__all__ = ["UsageAnalyticsResource", "AsyncUsageAnalyticsResource"] + + +class UsageAnalyticsResource(SyncAPIResource): + @cached_property + def with_raw_response(self) -> UsageAnalyticsResourceWithRawResponse: + """ + 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/imagekit-developer/imagekit-python#accessing-raw-response-data-eg-headers + """ + return UsageAnalyticsResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> UsageAnalyticsResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/imagekit-developer/imagekit-python#with_streaming_response + """ + return UsageAnalyticsResourceWithStreamingResponse(self) + + def get( + self, + *, + end_date: Union[str, date], + start_date: Union[str, date], + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> UsageAnalyticsResponse: + """ + **Note:** This API is currently in beta. + + Get the account analytics data between two dates. The response covers the period + from the start date to the end date, both dates inclusive. Both dates are + interpreted as UTC calendar days. + + The returned data is scoped to the requesting account only. Unlike + `/v1/accounts/usage`, an agency account's analytics are not aggregated across + its child accounts. + + The response is cached for 5 minutes per account and date range. Use + `generatedAt` to check how fresh the returned data is. + + Args: + end_date: Specify an `endDate` in `YYYY-MM-DD` format, interpreted as a UTC calendar day. + It should be after the `startDate`. The difference between `startDate` and + `endDate` should be less than 90 days. + + start_date: Specify a `startDate` in `YYYY-MM-DD` format, interpreted as a UTC calendar day. + It should be before the `endDate`. The difference between `startDate` and + `endDate` should be less than 90 days. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters 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( + "/v1/accounts/usage-analytics", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=maybe_transform( + { + "end_date": end_date, + "start_date": start_date, + }, + usage_analytics_get_params.UsageAnalyticsGetParams, + ), + ), + cast_to=UsageAnalyticsResponse, + ) + + +class AsyncUsageAnalyticsResource(AsyncAPIResource): + @cached_property + def with_raw_response(self) -> AsyncUsageAnalyticsResourceWithRawResponse: + """ + 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/imagekit-developer/imagekit-python#accessing-raw-response-data-eg-headers + """ + return AsyncUsageAnalyticsResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncUsageAnalyticsResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/imagekit-developer/imagekit-python#with_streaming_response + """ + return AsyncUsageAnalyticsResourceWithStreamingResponse(self) + + async def get( + self, + *, + end_date: Union[str, date], + start_date: Union[str, date], + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + ) -> UsageAnalyticsResponse: + """ + **Note:** This API is currently in beta. + + Get the account analytics data between two dates. The response covers the period + from the start date to the end date, both dates inclusive. Both dates are + interpreted as UTC calendar days. + + The returned data is scoped to the requesting account only. Unlike + `/v1/accounts/usage`, an agency account's analytics are not aggregated across + its child accounts. + + The response is cached for 5 minutes per account and date range. Use + `generatedAt` to check how fresh the returned data is. + + Args: + end_date: Specify an `endDate` in `YYYY-MM-DD` format, interpreted as a UTC calendar day. + It should be after the `startDate`. The difference between `startDate` and + `endDate` should be less than 90 days. + + start_date: Specify a `startDate` in `YYYY-MM-DD` format, interpreted as a UTC calendar day. + It should be before the `endDate`. The difference between `startDate` and + `endDate` should be less than 90 days. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters 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( + "/v1/accounts/usage-analytics", + options=make_request_options( + extra_headers=extra_headers, + extra_query=extra_query, + extra_body=extra_body, + timeout=timeout, + query=await async_maybe_transform( + { + "end_date": end_date, + "start_date": start_date, + }, + usage_analytics_get_params.UsageAnalyticsGetParams, + ), + ), + cast_to=UsageAnalyticsResponse, + ) + + +class UsageAnalyticsResourceWithRawResponse: + def __init__(self, usage_analytics: UsageAnalyticsResource) -> None: + self._usage_analytics = usage_analytics + + self.get = to_raw_response_wrapper( + usage_analytics.get, + ) + + +class AsyncUsageAnalyticsResourceWithRawResponse: + def __init__(self, usage_analytics: AsyncUsageAnalyticsResource) -> None: + self._usage_analytics = usage_analytics + + self.get = async_to_raw_response_wrapper( + usage_analytics.get, + ) + + +class UsageAnalyticsResourceWithStreamingResponse: + def __init__(self, usage_analytics: UsageAnalyticsResource) -> None: + self._usage_analytics = usage_analytics + + self.get = to_streamed_response_wrapper( + usage_analytics.get, + ) + + +class AsyncUsageAnalyticsResourceWithStreamingResponse: + def __init__(self, usage_analytics: AsyncUsageAnalyticsResource) -> None: + self._usage_analytics = usage_analytics + + self.get = async_to_streamed_response_wrapper( + usage_analytics.get, + ) diff --git a/src/imagekitio/resources/custom_metadata_fields.py b/src/imagekitio/resources/custom_metadata_fields.py index 74553166..6b2b084c 100644 --- a/src/imagekitio/resources/custom_metadata_fields.py +++ b/src/imagekitio/resources/custom_metadata_fields.py @@ -53,6 +53,7 @@ def create( label: str, name: str, schema: custom_metadata_field_create_params.Schema, + description: 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, @@ -76,6 +77,10 @@ def create( name: API name of the custom metadata field. This should be unique across all (including deleted) custom metadata fields. + description: Optional description for the custom metadata field. Can be up to 500 characters. + This is shown as a hint to the users while setting the field's value on an asset + in the media library UI. + extra_headers: Send extra headers extra_query: Add additional query parameters to the request @@ -91,6 +96,7 @@ def create( "label": label, "name": name, "schema": schema, + "description": description, }, custom_metadata_field_create_params.CustomMetadataFieldCreateParams, ), @@ -104,6 +110,7 @@ def update( self, id: str, *, + description: str | Omit = omit, label: str | Omit = omit, schema: custom_metadata_field_update_params.Schema | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. @@ -114,9 +121,15 @@ def update( timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> CustomMetadataField: """ - This API updates the label or schema of an existing custom metadata field. + This API updates the label, description, or schema of an existing custom + metadata field. Args: + description: Optional description for the custom metadata field. Can be up to 500 characters. + Send an empty string to clear an existing description. This is shown as a hint + to the users while setting the field's value on an asset in the media library + UI. + label: Human readable name of the custom metadata field. This should be unique across all non deleted custom metadata fields. This name is displayed as form field label to the users while setting field value on an asset in the media library @@ -141,6 +154,7 @@ def update( path_template("/v1/customMetadataFields/{id}", id=id), body=maybe_transform( { + "description": description, "label": label, "schema": schema, }, @@ -271,6 +285,7 @@ async def create( label: str, name: str, schema: custom_metadata_field_create_params.Schema, + description: 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, @@ -294,6 +309,10 @@ async def create( name: API name of the custom metadata field. This should be unique across all (including deleted) custom metadata fields. + description: Optional description for the custom metadata field. Can be up to 500 characters. + This is shown as a hint to the users while setting the field's value on an asset + in the media library UI. + extra_headers: Send extra headers extra_query: Add additional query parameters to the request @@ -309,6 +328,7 @@ async def create( "label": label, "name": name, "schema": schema, + "description": description, }, custom_metadata_field_create_params.CustomMetadataFieldCreateParams, ), @@ -322,6 +342,7 @@ async def update( self, id: str, *, + description: str | Omit = omit, label: str | Omit = omit, schema: custom_metadata_field_update_params.Schema | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. @@ -332,9 +353,15 @@ async def update( timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> CustomMetadataField: """ - This API updates the label or schema of an existing custom metadata field. + This API updates the label, description, or schema of an existing custom + metadata field. Args: + description: Optional description for the custom metadata field. Can be up to 500 characters. + Send an empty string to clear an existing description. This is shown as a hint + to the users while setting the field's value on an asset in the media library + UI. + label: Human readable name of the custom metadata field. This should be unique across all non deleted custom metadata fields. This name is displayed as form field label to the users while setting field value on an asset in the media library @@ -359,6 +386,7 @@ async def update( path_template("/v1/customMetadataFields/{id}", id=id), body=await async_maybe_transform( { + "description": description, "label": label, "schema": schema, }, diff --git a/src/imagekitio/resources/named_transformations.py b/src/imagekitio/resources/named_transformations.py new file mode 100644 index 00000000..fbeb9a5d --- /dev/null +++ b/src/imagekitio/resources/named_transformations.py @@ -0,0 +1,580 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +import httpx + +from ..types import named_transformation_create_params, named_transformation_update_params +from .._types import Body, Omit, Query, Headers, NoneType, NotGiven, omit, not_given +from .._utils import path_template, 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.shared.named_transformation import NamedTransformation +from ..types.named_transformation_list_response import NamedTransformationListResponse + +__all__ = ["NamedTransformationsResource", "AsyncNamedTransformationsResource"] + + +class NamedTransformationsResource(SyncAPIResource): + @cached_property + def with_raw_response(self) -> NamedTransformationsResourceWithRawResponse: + """ + 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/imagekit-developer/imagekit-python#accessing-raw-response-data-eg-headers + """ + return NamedTransformationsResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> NamedTransformationsResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/imagekit-developer/imagekit-python#with_streaming_response + """ + return NamedTransformationsResourceWithStreamingResponse(self) + + def create( + self, + *, + name: str, + transformation: str, + enabled: 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, + ) -> NamedTransformation: + """ + Creates a new named transformation and returns the created object. + + A named transformation is a short, reusable name for a transformation string. + Use it in image and video URLs as `tr:n-`, and update the underlying + transformation later without changing existing URLs. Learn more about + [named transformations](https://imagekit.io/docs/transformations#named-transformations). + + You can create up to 250 named transformations per account. + + Args: + name: Alias for the transformation string, used in URLs as `tr:n-`. This is + case-sensitive, contains only alphanumeric characters or `_` (underscore), and + is unique across all named transformations for your account. + + transformation: The transformation string this named transformation refers to. Learn more about + the [transformation string syntax](https://imagekit.io/docs/transformations). + + enabled: Whether the named transformation is currently enabled. When set to `false`, + requests using this named transformation fail at delivery time. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters 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( + "/v1/named-transformations", + body=maybe_transform( + { + "name": name, + "transformation": transformation, + "enabled": enabled, + }, + named_transformation_create_params.NamedTransformationCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=NamedTransformation, + ) + + def update( + self, + id: str, + *, + enabled: bool | Omit = omit, + name: str | Omit = omit, + transformation: 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, + ) -> NamedTransformation: + """ + Updates the named transformation identified by `id` and returns the updated + object. Only the fields present in the request body are updated; other fields + stay unchanged. + + Renaming or disabling a named transformation fails with a `409` error if it is + still referenced (via the `n-` token) by an upload pre-transformation or + post-transformation setting. This check is best-effort and can't detect + references in your own application code or in previously generated URLs. + + Args: + id: Unique identifier for a named transformation. + + enabled: Whether the named transformation is enabled. Omit to leave the current value + unchanged. + + name: Alias for the transformation string, used in URLs as `tr:n-`. This is + case-sensitive, contains only alphanumeric characters or `_` (underscore), and + is unique across all named transformations for your account. + + transformation: The transformation string this named transformation refers to. Learn more about + the [transformation string syntax](https://imagekit.io/docs/transformations). + + extra_headers: Send extra headers + + extra_query: Add additional query parameters 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._patch( + path_template("/v1/named-transformations/{id}", id=id), + body=maybe_transform( + { + "enabled": enabled, + "name": name, + "transformation": transformation, + }, + named_transformation_update_params.NamedTransformationUpdateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=NamedTransformation, + ) + + 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, + ) -> NamedTransformationListResponse: + """Returns an array of all named transformations configured for your account.""" + return self._get( + "/v1/named-transformations", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=NamedTransformationListResponse, + ) + + 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: + """ + Permanently deletes the named transformation identified by `id`. + + Deletion fails with a `409` error if the named transformation is still + referenced (via the `n-` token) by an upload pre-transformation or + post-transformation setting. This check is best-effort and can't detect + references in your own application code or in previously generated URLs. + + Args: + id: Unique identifier for a named transformation. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters 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( + path_template("/v1/named-transformations/{id}", id=id), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=NoneType, + ) + + def get( + 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, + ) -> NamedTransformation: + """ + Retrieves the named transformation identified by `id`. + + Args: + id: Unique identifier for a named transformation. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters 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( + path_template("/v1/named-transformations/{id}", id=id), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=NamedTransformation, + ) + + +class AsyncNamedTransformationsResource(AsyncAPIResource): + @cached_property + def with_raw_response(self) -> AsyncNamedTransformationsResourceWithRawResponse: + """ + 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/imagekit-developer/imagekit-python#accessing-raw-response-data-eg-headers + """ + return AsyncNamedTransformationsResourceWithRawResponse(self) + + @cached_property + def with_streaming_response(self) -> AsyncNamedTransformationsResourceWithStreamingResponse: + """ + An alternative to `.with_raw_response` that doesn't eagerly read the response body. + + For more information, see https://www.github.com/imagekit-developer/imagekit-python#with_streaming_response + """ + return AsyncNamedTransformationsResourceWithStreamingResponse(self) + + async def create( + self, + *, + name: str, + transformation: str, + enabled: 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, + ) -> NamedTransformation: + """ + Creates a new named transformation and returns the created object. + + A named transformation is a short, reusable name for a transformation string. + Use it in image and video URLs as `tr:n-`, and update the underlying + transformation later without changing existing URLs. Learn more about + [named transformations](https://imagekit.io/docs/transformations#named-transformations). + + You can create up to 250 named transformations per account. + + Args: + name: Alias for the transformation string, used in URLs as `tr:n-`. This is + case-sensitive, contains only alphanumeric characters or `_` (underscore), and + is unique across all named transformations for your account. + + transformation: The transformation string this named transformation refers to. Learn more about + the [transformation string syntax](https://imagekit.io/docs/transformations). + + enabled: Whether the named transformation is currently enabled. When set to `false`, + requests using this named transformation fail at delivery time. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters 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( + "/v1/named-transformations", + body=await async_maybe_transform( + { + "name": name, + "transformation": transformation, + "enabled": enabled, + }, + named_transformation_create_params.NamedTransformationCreateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=NamedTransformation, + ) + + async def update( + self, + id: str, + *, + enabled: bool | Omit = omit, + name: str | Omit = omit, + transformation: 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, + ) -> NamedTransformation: + """ + Updates the named transformation identified by `id` and returns the updated + object. Only the fields present in the request body are updated; other fields + stay unchanged. + + Renaming or disabling a named transformation fails with a `409` error if it is + still referenced (via the `n-` token) by an upload pre-transformation or + post-transformation setting. This check is best-effort and can't detect + references in your own application code or in previously generated URLs. + + Args: + id: Unique identifier for a named transformation. + + enabled: Whether the named transformation is enabled. Omit to leave the current value + unchanged. + + name: Alias for the transformation string, used in URLs as `tr:n-`. This is + case-sensitive, contains only alphanumeric characters or `_` (underscore), and + is unique across all named transformations for your account. + + transformation: The transformation string this named transformation refers to. Learn more about + the [transformation string syntax](https://imagekit.io/docs/transformations). + + extra_headers: Send extra headers + + extra_query: Add additional query parameters 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._patch( + path_template("/v1/named-transformations/{id}", id=id), + body=await async_maybe_transform( + { + "enabled": enabled, + "name": name, + "transformation": transformation, + }, + named_transformation_update_params.NamedTransformationUpdateParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=NamedTransformation, + ) + + 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, + ) -> NamedTransformationListResponse: + """Returns an array of all named transformations configured for your account.""" + return await self._get( + "/v1/named-transformations", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=NamedTransformationListResponse, + ) + + 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: + """ + Permanently deletes the named transformation identified by `id`. + + Deletion fails with a `409` error if the named transformation is still + referenced (via the `n-` token) by an upload pre-transformation or + post-transformation setting. This check is best-effort and can't detect + references in your own application code or in previously generated URLs. + + Args: + id: Unique identifier for a named transformation. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters 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( + path_template("/v1/named-transformations/{id}", id=id), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=NoneType, + ) + + async def get( + 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, + ) -> NamedTransformation: + """ + Retrieves the named transformation identified by `id`. + + Args: + id: Unique identifier for a named transformation. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters 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( + path_template("/v1/named-transformations/{id}", id=id), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=NamedTransformation, + ) + + +class NamedTransformationsResourceWithRawResponse: + def __init__(self, named_transformations: NamedTransformationsResource) -> None: + self._named_transformations = named_transformations + + self.create = to_raw_response_wrapper( + named_transformations.create, + ) + self.update = to_raw_response_wrapper( + named_transformations.update, + ) + self.list = to_raw_response_wrapper( + named_transformations.list, + ) + self.delete = to_raw_response_wrapper( + named_transformations.delete, + ) + self.get = to_raw_response_wrapper( + named_transformations.get, + ) + + +class AsyncNamedTransformationsResourceWithRawResponse: + def __init__(self, named_transformations: AsyncNamedTransformationsResource) -> None: + self._named_transformations = named_transformations + + self.create = async_to_raw_response_wrapper( + named_transformations.create, + ) + self.update = async_to_raw_response_wrapper( + named_transformations.update, + ) + self.list = async_to_raw_response_wrapper( + named_transformations.list, + ) + self.delete = async_to_raw_response_wrapper( + named_transformations.delete, + ) + self.get = async_to_raw_response_wrapper( + named_transformations.get, + ) + + +class NamedTransformationsResourceWithStreamingResponse: + def __init__(self, named_transformations: NamedTransformationsResource) -> None: + self._named_transformations = named_transformations + + self.create = to_streamed_response_wrapper( + named_transformations.create, + ) + self.update = to_streamed_response_wrapper( + named_transformations.update, + ) + self.list = to_streamed_response_wrapper( + named_transformations.list, + ) + self.delete = to_streamed_response_wrapper( + named_transformations.delete, + ) + self.get = to_streamed_response_wrapper( + named_transformations.get, + ) + + +class AsyncNamedTransformationsResourceWithStreamingResponse: + def __init__(self, named_transformations: AsyncNamedTransformationsResource) -> None: + self._named_transformations = named_transformations + + self.create = async_to_streamed_response_wrapper( + named_transformations.create, + ) + self.update = async_to_streamed_response_wrapper( + named_transformations.update, + ) + self.list = async_to_streamed_response_wrapper( + named_transformations.list, + ) + self.delete = async_to_streamed_response_wrapper( + named_transformations.delete, + ) + self.get = async_to_streamed_response_wrapper( + named_transformations.get, + ) diff --git a/src/imagekitio/types/__init__.py b/src/imagekitio/types/__init__.py index c5d85205..e81abbf8 100644 --- a/src/imagekitio/types/__init__.py +++ b/src/imagekitio/types/__init__.py @@ -21,6 +21,7 @@ OverlayPosition as OverlayPosition, SubtitleOverlay as SubtitleOverlay, SolidColorOverlay as SolidColorOverlay, + NamedTransformation as NamedTransformation, StreamingResolution as StreamingResolution, TransformationPosition as TransformationPosition, GetImageAttributesOptions as GetImageAttributesOptions, @@ -71,6 +72,9 @@ from .video_transformation_ready_event import VideoTransformationReadyEvent as VideoTransformationReadyEvent from .custom_metadata_field_list_params import CustomMetadataFieldListParams as CustomMetadataFieldListParams from .upload_post_transform_error_event import UploadPostTransformErrorEvent as UploadPostTransformErrorEvent +from .named_transformation_create_params import NamedTransformationCreateParams as NamedTransformationCreateParams +from .named_transformation_list_response import NamedTransformationListResponse as NamedTransformationListResponse +from .named_transformation_update_params import NamedTransformationUpdateParams as NamedTransformationUpdateParams from .upload_pre_transform_success_event import UploadPreTransformSuccessEvent as UploadPreTransformSuccessEvent from .custom_metadata_field_create_params import CustomMetadataFieldCreateParams as CustomMetadataFieldCreateParams from .custom_metadata_field_list_response import CustomMetadataFieldListResponse as CustomMetadataFieldListResponse diff --git a/src/imagekitio/types/accounts/__init__.py b/src/imagekitio/types/accounts/__init__.py index 3d713dbe..60a394c1 100644 --- a/src/imagekitio/types/accounts/__init__.py +++ b/src/imagekitio/types/accounts/__init__.py @@ -10,6 +10,9 @@ from .origin_request_param import OriginRequestParam as OriginRequestParam from .origin_update_params import OriginUpdateParams as OriginUpdateParams from .url_endpoint_response import URLEndpointResponse as URLEndpointResponse +from .request_bandwidth_entry import RequestBandwidthEntry as RequestBandwidthEntry +from .usage_analytics_response import UsageAnalyticsResponse as UsageAnalyticsResponse from .url_endpoint_create_params import URLEndpointCreateParams as URLEndpointCreateParams from .url_endpoint_list_response import URLEndpointListResponse as URLEndpointListResponse from .url_endpoint_update_params import URLEndpointUpdateParams as URLEndpointUpdateParams +from .usage_analytics_get_params import UsageAnalyticsGetParams as UsageAnalyticsGetParams diff --git a/src/imagekitio/types/accounts/request_bandwidth_entry.py b/src/imagekitio/types/accounts/request_bandwidth_entry.py new file mode 100644 index 00000000..ea245b64 --- /dev/null +++ b/src/imagekitio/types/accounts/request_bandwidth_entry.py @@ -0,0 +1,15 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from pydantic import Field as FieldInfo + +from ..._models import BaseModel + +__all__ = ["RequestBandwidthEntry"] + + +class RequestBandwidthEntry(BaseModel): + bandwidth_bytes: float = FieldInfo(alias="bandwidthBytes") + """Total bandwidth used in bytes.""" + + request_count: float = FieldInfo(alias="requestCount") + """Number of requests.""" diff --git a/src/imagekitio/types/accounts/usage_analytics_get_params.py b/src/imagekitio/types/accounts/usage_analytics_get_params.py new file mode 100644 index 00000000..7d882ad1 --- /dev/null +++ b/src/imagekitio/types/accounts/usage_analytics_get_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 Union +from datetime import date +from typing_extensions import Required, Annotated, TypedDict + +from ..._utils import PropertyInfo + +__all__ = ["UsageAnalyticsGetParams"] + + +class UsageAnalyticsGetParams(TypedDict, total=False): + end_date: Required[Annotated[Union[str, date], PropertyInfo(alias="endDate", format="iso8601")]] + """Specify an `endDate` in `YYYY-MM-DD` format, interpreted as a UTC calendar day. + + It should be after the `startDate`. The difference between `startDate` and + `endDate` should be less than 90 days. + """ + + start_date: Required[Annotated[Union[str, date], PropertyInfo(alias="startDate", format="iso8601")]] + """Specify a `startDate` in `YYYY-MM-DD` format, interpreted as a UTC calendar day. + + It should be before the `endDate`. The difference between `startDate` and + `endDate` should be less than 90 days. + """ diff --git a/src/imagekitio/types/accounts/usage_analytics_response.py b/src/imagekitio/types/accounts/usage_analytics_response.py new file mode 100644 index 00000000..131cf712 --- /dev/null +++ b/src/imagekitio/types/accounts/usage_analytics_response.py @@ -0,0 +1,488 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import TYPE_CHECKING, Dict, List +from datetime import date, datetime + +from pydantic import Field as FieldInfo + +from ..._models import BaseModel +from .request_bandwidth_entry import RequestBandwidthEntry + +__all__ = [ + "UsageAnalyticsResponse", + "Browser", + "BrowserByBandwidth", + "BrowserByRequest", + "Cache", + "Country", + "CountryByBandwidth", + "CountryByRequest", + "Device", + "DeviceByBandwidth", + "DeviceByRequest", + "ErrorReason", + "Extension", + "Format", + "FormatByBandwidth", + "FormatByRequest", + "StatusCode", + "Top404Asset", + "TopImages", + "TopImagesByBandwidth", + "TopImagesByRequest", + "TopImageTransforms", + "TopImageTransformsByBandwidth", + "TopImageTransformsByRequest", + "TopOtherAssets", + "TopOtherAssetsByBandwidth", + "TopOtherAssetsByRequest", + "TopReferrers", + "TopReferrersByBandwidth", + "TopReferrersByRequest", + "TopUserAgents", + "TopUserAgentsByBandwidth", + "TopUserAgentsByRequest", + "TopVideos", + "TopVideosByBandwidth", + "TopVideosByRequest", + "TopVideoTransforms", + "TopVideoTransformsByBandwidth", + "TopVideoTransformsByRequest", + "URLEndpoints", + "URLEndpointsByBandwidth", + "URLEndpointsByRequest", + "VideoProcessing", +] + + +class BrowserByBandwidth(RequestBandwidthEntry): + name: str + """Browser name (e.g. `Chrome`).""" + + +class BrowserByRequest(RequestBandwidthEntry): + name: str + """Browser name (e.g. `Chrome`).""" + + +class Browser(BaseModel): + """CDN traffic grouped by browser.""" + + by_bandwidth: List[BrowserByBandwidth] = FieldInfo(alias="byBandwidth") + """Top browsers sorted by bandwidth utilized.""" + + by_requests: List[BrowserByRequest] = FieldInfo(alias="byRequests") + """Top browsers sorted by request count.""" + + +class Cache(BaseModel): + """CDN cache hit, miss and error counts for the date range.""" + + error_count: float = FieldInfo(alias="errorCount") + """ + Number of requests where the CDN encountered a cache error or exceeded capacity + while serving the response. + """ + + hit_count: float = FieldInfo(alias="hitCount") + """Number of requests served from cache, including full hits and revalidated hits.""" + + miss_count: float = FieldInfo(alias="missCount") + """ + Number of requests that were not found in cache and had to be fetched from + origin. + """ + + +class CountryByBandwidth(RequestBandwidthEntry): + code: str + """ISO country code.""" + + name: str + """Country name.""" + + +class CountryByRequest(RequestBandwidthEntry): + code: str + """ISO country code.""" + + name: str + """Country name.""" + + +class Country(BaseModel): + """CDN traffic grouped by country.""" + + by_bandwidth: List[CountryByBandwidth] = FieldInfo(alias="byBandwidth") + """Top requesting countries sorted by total bandwidth utilized.""" + + by_requests: List[CountryByRequest] = FieldInfo(alias="byRequests") + """Top requesting countries sorted by request count.""" + + +class DeviceByBandwidth(RequestBandwidthEntry): + name: str + """Device category combined with operating system or vendor (e.g. + + `Desktop - Windows PC`). + """ + + +class DeviceByRequest(RequestBandwidthEntry): + name: str + """Device category combined with operating system or vendor (e.g. + + `Desktop - Windows PC`). + """ + + +class Device(BaseModel): + """CDN traffic grouped by device and operating system (e.g. + + `Desktop - Apple Mac`, `Smartphone - Apple iPhone`). + """ + + by_bandwidth: List[DeviceByBandwidth] = FieldInfo(alias="byBandwidth") + """Top device/OS combinations sorted by bandwidth utilized.""" + + by_requests: List[DeviceByRequest] = FieldInfo(alias="byRequests") + """Top device/OS combinations sorted by request count.""" + + +class ErrorReason(BaseModel): + name: str + """Description of the error reason.""" + + request_count: float = FieldInfo(alias="requestCount") + """Number of requests that failed with this error reason.""" + + +class Extension(BaseModel): + name: str + """Extension identifier.""" + + operation_count: float = FieldInfo(alias="operationCount") + """Number of times this extension ran during the date range.""" + + +class FormatByBandwidth(RequestBandwidthEntry): + name: str + """MIME type (e.g. `image/webp`).""" + + +class FormatByRequest(RequestBandwidthEntry): + name: str + """MIME type (e.g. `image/webp`).""" + + +class Format(BaseModel): + """CDN traffic grouped by response `Content-Type`.""" + + by_bandwidth: List[FormatByBandwidth] = FieldInfo(alias="byBandwidth") + """Top content types sorted by bandwidth utilized.""" + + by_requests: List[FormatByRequest] = FieldInfo(alias="byRequests") + """Top content types sorted by request count.""" + + +class StatusCode(BaseModel): + name: str + """HTTP status code.""" + + request_count: float = FieldInfo(alias="requestCount") + """Number of requests that received this status code.""" + + +class Top404Asset(BaseModel): + name: str + """URL that returned a 404 response.""" + + request_count: float = FieldInfo(alias="requestCount") + """Number of requests to this URL that returned a 404 response.""" + + +class TopImagesByBandwidth(RequestBandwidthEntry): + name: str + """URL of the image asset.""" + + +class TopImagesByRequest(RequestBandwidthEntry): + name: str + """URL of the image asset.""" + + +class TopImages(BaseModel): + """Top image assets by traffic.""" + + by_bandwidth: List[TopImagesByBandwidth] = FieldInfo(alias="byBandwidth") + """Top image assets sorted by bandwidth utilized.""" + + by_requests: List[TopImagesByRequest] = FieldInfo(alias="byRequests") + """Top image assets sorted by request count.""" + + +class TopImageTransformsByBandwidth(RequestBandwidthEntry): + name: str + """Image transformation string (e.g. `tr:w-400,h-400`).""" + + +class TopImageTransformsByRequest(RequestBandwidthEntry): + name: str + """Image transformation string (e.g. `tr:w-400,h-400`).""" + + +class TopImageTransforms(BaseModel): + """Top image transformation strings by traffic.""" + + by_bandwidth: List[TopImageTransformsByBandwidth] = FieldInfo(alias="byBandwidth") + """Top image transformation strings sorted by bandwidth utilized.""" + + by_requests: List[TopImageTransformsByRequest] = FieldInfo(alias="byRequests") + """Top image transformation strings sorted by request count.""" + + +class TopOtherAssetsByBandwidth(RequestBandwidthEntry): + name: str + """URL of the non-image, non-video asset.""" + + +class TopOtherAssetsByRequest(RequestBandwidthEntry): + name: str + """URL of the non-image, non-video asset.""" + + +class TopOtherAssets(BaseModel): + """Top non-image, non-video assets by traffic.""" + + by_bandwidth: List[TopOtherAssetsByBandwidth] = FieldInfo(alias="byBandwidth") + """Top non-image, non-video assets sorted by bandwidth utilized.""" + + by_requests: List[TopOtherAssetsByRequest] = FieldInfo(alias="byRequests") + """Top non-image, non-video assets sorted by request count.""" + + +class TopReferrersByBandwidth(RequestBandwidthEntry): + name: str + """Referrer URL.""" + + +class TopReferrersByRequest(RequestBandwidthEntry): + name: str + """Referrer URL.""" + + +class TopReferrers(BaseModel): + """Top HTTP referrers by traffic.""" + + by_bandwidth: List[TopReferrersByBandwidth] = FieldInfo(alias="byBandwidth") + """Top HTTP referrers sorted by bandwidth utilized.""" + + by_requests: List[TopReferrersByRequest] = FieldInfo(alias="byRequests") + """Top HTTP referrers sorted by request count.""" + + +class TopUserAgentsByBandwidth(RequestBandwidthEntry): + name: str + """User agent string.""" + + +class TopUserAgentsByRequest(RequestBandwidthEntry): + name: str + """User agent string.""" + + +class TopUserAgents(BaseModel): + """Top user agents by traffic.""" + + by_bandwidth: List[TopUserAgentsByBandwidth] = FieldInfo(alias="byBandwidth") + """Top user agents sorted by bandwidth utilized.""" + + by_requests: List[TopUserAgentsByRequest] = FieldInfo(alias="byRequests") + """Top user agents sorted by request count.""" + + +class TopVideosByBandwidth(RequestBandwidthEntry): + name: str + """URL of the video asset.""" + + +class TopVideosByRequest(RequestBandwidthEntry): + name: str + """Full URL of the video asset (e.g. `https://ik.imagekit.io/demo/clip.mp4`).""" + + +class TopVideos(BaseModel): + """Top video assets by traffic.""" + + by_bandwidth: List[TopVideosByBandwidth] = FieldInfo(alias="byBandwidth") + """Top video assets sorted by bandwidth utilized.""" + + by_requests: List[TopVideosByRequest] = FieldInfo(alias="byRequests") + """Top video assets sorted by request count.""" + + +class TopVideoTransformsByBandwidth(RequestBandwidthEntry): + name: str + """Video transformation string (e.g. `tr:h-720,f-mp4`).""" + + +class TopVideoTransformsByRequest(RequestBandwidthEntry): + name: str + """Video transformation string (e.g. `tr:h-720,f-mp4`).""" + + +class TopVideoTransforms(BaseModel): + """Top video transformation strings by traffic.""" + + by_bandwidth: List[TopVideoTransformsByBandwidth] = FieldInfo(alias="byBandwidth") + """Top video transformation strings sorted by bandwidth utilized.""" + + by_requests: List[TopVideoTransformsByRequest] = FieldInfo(alias="byRequests") + """Top video transformation strings sorted by request count.""" + + +class URLEndpointsByBandwidth(RequestBandwidthEntry): + name: str + """ + URL endpoint name, or `Default` for traffic that does not match a named + endpoint. + """ + + +class URLEndpointsByRequest(RequestBandwidthEntry): + name: str + """ + URL endpoint name, or `Default` for traffic that does not match a named + endpoint. + """ + + +class URLEndpoints(BaseModel): + """CDN traffic grouped by configured URL endpoint. + + Traffic that does not match any named URL endpoint pattern is grouped under `Default`. + """ + + by_bandwidth: List[URLEndpointsByBandwidth] = FieldInfo(alias="byBandwidth") + """Top URL endpoints sorted by bandwidth utilized.""" + + by_requests: List[URLEndpointsByRequest] = FieldInfo(alias="byRequests") + """Top URL endpoints sorted by request count.""" + + +class VideoProcessing(BaseModel): + codec: str + """Video codec used for the output (e.g. `h264`, `av1`).""" + + duration_seconds: float = FieldInfo(alias="durationSeconds") + """Total output duration, in seconds, for this resolution and codec combination.""" + + resolution: str + """Output resolution tier (e.g. `SD`, `HD`, `4K`).""" + + +class UsageAnalyticsResponse(BaseModel): + bandwidth_bytes: float = FieldInfo(alias="bandwidthBytes") + """Total bandwidth, in bytes, utilized during the specified date range.""" + + browser: Browser + """CDN traffic grouped by browser.""" + + cache: Cache + """CDN cache hit, miss and error counts for the date range.""" + + country: Country + """CDN traffic grouped by country.""" + + device: Device + """CDN traffic grouped by device and operating system (e.g. + + `Desktop - Apple Mac`, `Smartphone - Apple iPhone`). + """ + + end_date: date = FieldInfo(alias="endDate") + """End date of the computed analytics data.""" + + error_reasons: List[ErrorReason] = FieldInfo(alias="errorReasons") + """Request count grouped by origin error reason. + + This covers failed origin fetches, such as an asset not found at origin or an + origin timeout. It is not the HTTP status code returned to the client, see + `statusCodes` for that. + """ + + extensions: List[Extension] + """Raw per-extension operation counts for the date range. + + These are raw operation counts, not billable extension units. For billable + usage, use the `/v1/accounts/usage` endpoint. + """ + + format: Format + """CDN traffic grouped by response `Content-Type`.""" + + generated_at: datetime = FieldInfo(alias="generatedAt") + """Date and time when the analytics data was computed. + + Use this to gauge how fresh the returned data is. The date and time is in + ISO8601 format. + """ + + request_count: float = FieldInfo(alias="requestCount") + """Total number of requests made during the specified date range.""" + + start_date: date = FieldInfo(alias="startDate") + """Start date of the computed analytics data.""" + + status_codes: List[StatusCode] = FieldInfo(alias="statusCodes") + """Request count grouped by HTTP status code.""" + + top404_assets: List[Top404Asset] = FieldInfo(alias="top404Assets") + """Top URLs that returned a 404 response.""" + + top_images: TopImages = FieldInfo(alias="topImages") + """Top image assets by traffic.""" + + top_image_transforms: TopImageTransforms = FieldInfo(alias="topImageTransforms") + """Top image transformation strings by traffic.""" + + top_other_assets: TopOtherAssets = FieldInfo(alias="topOtherAssets") + """Top non-image, non-video assets by traffic.""" + + top_referrers: TopReferrers = FieldInfo(alias="topReferrers") + """Top HTTP referrers by traffic.""" + + top_user_agents: TopUserAgents = FieldInfo(alias="topUserAgents") + """Top user agents by traffic.""" + + top_videos: TopVideos = FieldInfo(alias="topVideos") + """Top video assets by traffic.""" + + top_video_transforms: TopVideoTransforms = FieldInfo(alias="topVideoTransforms") + """Top video transformation strings by traffic.""" + + url_endpoints: URLEndpoints = FieldInfo(alias="urlEndpoints") + """CDN traffic grouped by configured URL endpoint. + + Traffic that does not match any named URL endpoint pattern is grouped under + `Default`. + """ + + video_processing: List[VideoProcessing] = FieldInfo(alias="videoProcessing") + """ + Raw observed video transcode output duration, in seconds, grouped by resolution + and codec. These are raw seconds, not billable Video Processing Units (VPU). For + billable VPU totals, use the `/v1/accounts/usage` endpoint. + """ + + if TYPE_CHECKING: + # Some versions of Pydantic <2.8.0 have a bug and don’t allow assigning a + # value to this field, so for compatibility we avoid doing it at runtime. + __pydantic_extra__: Dict[str, object] = FieldInfo(init=False) # pyright: ignore[reportIncompatibleVariableOverride] + + # Stub to indicate that arbitrary properties are accepted. + # To access properties that are not valid identifiers you can use `getattr`, e.g. + # `getattr(obj, '$type')` + def __getattr__(self, attr: str) -> object: ... + else: + __pydantic_extra__: Dict[str, object] diff --git a/src/imagekitio/types/custom_metadata_field.py b/src/imagekitio/types/custom_metadata_field.py index 1ebde723..d725ad3c 100644 --- a/src/imagekitio/types/custom_metadata_field.py +++ b/src/imagekitio/types/custom_metadata_field.py @@ -75,3 +75,10 @@ class CustomMetadataField(BaseModel): schema_: Schema = FieldInfo(alias="schema") """An object that describes the rules for the custom metadata field value.""" + + description: Optional[str] = None + """Optional description of the custom metadata field. + + Only present when a description has been set. Shown as a hint to the users while + setting the field's value on an asset in the media library UI. + """ diff --git a/src/imagekitio/types/custom_metadata_field_create_params.py b/src/imagekitio/types/custom_metadata_field_create_params.py index 0e265b09..63fd634b 100644 --- a/src/imagekitio/types/custom_metadata_field_create_params.py +++ b/src/imagekitio/types/custom_metadata_field_create_params.py @@ -28,6 +28,13 @@ class CustomMetadataFieldCreateParams(TypedDict, total=False): schema: Required[Schema] + description: str + """Optional description for the custom metadata field. + + Can be up to 500 characters. This is shown as a hint to the users while setting + the field's value on an asset in the media library UI. + """ + class Schema(TypedDict, total=False): type: Required[Literal["Text", "Textarea", "Number", "Date", "Boolean", "SingleSelect", "MultiSelect"]] diff --git a/src/imagekitio/types/custom_metadata_field_update_params.py b/src/imagekitio/types/custom_metadata_field_update_params.py index fbb9effc..2a3e446e 100644 --- a/src/imagekitio/types/custom_metadata_field_update_params.py +++ b/src/imagekitio/types/custom_metadata_field_update_params.py @@ -12,6 +12,14 @@ class CustomMetadataFieldUpdateParams(TypedDict, total=False): + description: str + """Optional description for the custom metadata field. + + Can be up to 500 characters. Send an empty string to clear an existing + description. This is shown as a hint to the users while setting the field's + value on an asset in the media library UI. + """ + label: str """Human readable name of the custom metadata field. diff --git a/src/imagekitio/types/named_transformation_create_params.py b/src/imagekitio/types/named_transformation_create_params.py new file mode 100644 index 00000000..d7d91558 --- /dev/null +++ b/src/imagekitio/types/named_transformation_create_params.py @@ -0,0 +1,30 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Required, TypedDict + +__all__ = ["NamedTransformationCreateParams"] + + +class NamedTransformationCreateParams(TypedDict, total=False): + name: Required[str] + """Alias for the transformation string, used in URLs as `tr:n-`. + + This is case-sensitive, contains only alphanumeric characters or `_` + (underscore), and is unique across all named transformations for your account. + """ + + transformation: Required[str] + """The transformation string this named transformation refers to. + + Learn more about the + [transformation string syntax](https://imagekit.io/docs/transformations). + """ + + enabled: bool + """Whether the named transformation is currently enabled. + + When set to `false`, requests using this named transformation fail at delivery + time. + """ diff --git a/src/imagekitio/types/named_transformation_list_response.py b/src/imagekitio/types/named_transformation_list_response.py new file mode 100644 index 00000000..29bd9d2d --- /dev/null +++ b/src/imagekitio/types/named_transformation_list_response.py @@ -0,0 +1,10 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List +from typing_extensions import TypeAlias + +from .shared.named_transformation import NamedTransformation + +__all__ = ["NamedTransformationListResponse"] + +NamedTransformationListResponse: TypeAlias = List[NamedTransformation] diff --git a/src/imagekitio/types/named_transformation_update_params.py b/src/imagekitio/types/named_transformation_update_params.py new file mode 100644 index 00000000..23401f43 --- /dev/null +++ b/src/imagekitio/types/named_transformation_update_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_extensions import TypedDict + +__all__ = ["NamedTransformationUpdateParams"] + + +class NamedTransformationUpdateParams(TypedDict, total=False): + enabled: bool + """Whether the named transformation is enabled. + + Omit to leave the current value unchanged. + """ + + name: str + """Alias for the transformation string, used in URLs as `tr:n-`. + + This is case-sensitive, contains only alphanumeric characters or `_` + (underscore), and is unique across all named transformations for your account. + """ + + transformation: str + """The transformation string this named transformation refers to. + + Learn more about the + [transformation string syntax](https://imagekit.io/docs/transformations). + """ diff --git a/src/imagekitio/types/shared/__init__.py b/src/imagekitio/types/shared/__init__.py index cae1a71e..c5c9043a 100644 --- a/src/imagekitio/types/shared/__init__.py +++ b/src/imagekitio/types/shared/__init__.py @@ -14,6 +14,7 @@ from .overlay_position import OverlayPosition as OverlayPosition from .subtitle_overlay import SubtitleOverlay as SubtitleOverlay from .solid_color_overlay import SolidColorOverlay as SolidColorOverlay +from .named_transformation import NamedTransformation as NamedTransformation from .streaming_resolution import StreamingResolution as StreamingResolution from .transformation_position import TransformationPosition as TransformationPosition from .responsive_image_attributes import ResponsiveImageAttributes as ResponsiveImageAttributes diff --git a/src/imagekitio/types/shared/named_transformation.py b/src/imagekitio/types/shared/named_transformation.py new file mode 100644 index 00000000..dc82b342 --- /dev/null +++ b/src/imagekitio/types/shared/named_transformation.py @@ -0,0 +1,42 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from datetime import datetime + +from pydantic import Field as FieldInfo + +from ..._models import BaseModel + +__all__ = ["NamedTransformation"] + + +class NamedTransformation(BaseModel): + """ + A named transformation is an alias for a transformation string, letting you apply and later update complex transformations without changing your image or video URLs. Learn more about [named transformations](https://imagekit.io/docs/transformations#named-transformations). + """ + + id: str + """Unique identifier for a named transformation.""" + + created_at: datetime = FieldInfo(alias="createdAt") + """ISO 8601 timestamp of when the named transformation was created.""" + + enabled: bool + """Whether the named transformation is currently enabled. + + When set to `false`, requests using this named transformation fail at delivery + time. + """ + + name: str + """Alias for the transformation string, used in URLs as `tr:n-`. + + This is case-sensitive, contains only alphanumeric characters or `_` + (underscore), and is unique across all named transformations for your account. + """ + + transformation: str + """The transformation string this named transformation refers to. + + Learn more about the + [transformation string syntax](https://imagekit.io/docs/transformations). + """ diff --git a/tests/api_resources/accounts/test_usage_analytics.py b/tests/api_resources/accounts/test_usage_analytics.py new file mode 100644 index 00000000..3881e5ad --- /dev/null +++ b/tests/api_resources/accounts/test_usage_analytics.py @@ -0,0 +1,99 @@ +# 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 imagekitio import ImageKit, AsyncImageKit +from tests.utils import assert_matches_type +from imagekitio._utils import parse_date +from imagekitio.types.accounts import UsageAnalyticsResponse + +base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") + + +class TestUsageAnalytics: + parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + def test_method_get(self, client: ImageKit) -> None: + usage_analytics = client.accounts.usage_analytics.get( + end_date=parse_date("2019-12-27"), + start_date=parse_date("2019-12-27"), + ) + assert_matches_type(UsageAnalyticsResponse, usage_analytics, path=["response"]) + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + def test_raw_response_get(self, client: ImageKit) -> None: + response = client.accounts.usage_analytics.with_raw_response.get( + end_date=parse_date("2019-12-27"), + start_date=parse_date("2019-12-27"), + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + usage_analytics = response.parse() + assert_matches_type(UsageAnalyticsResponse, usage_analytics, path=["response"]) + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + def test_streaming_response_get(self, client: ImageKit) -> None: + with client.accounts.usage_analytics.with_streaming_response.get( + end_date=parse_date("2019-12-27"), + start_date=parse_date("2019-12-27"), + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + usage_analytics = response.parse() + assert_matches_type(UsageAnalyticsResponse, usage_analytics, path=["response"]) + + assert cast(Any, response.is_closed) is True + + +class TestAsyncUsageAnalytics: + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + async def test_method_get(self, async_client: AsyncImageKit) -> None: + usage_analytics = await async_client.accounts.usage_analytics.get( + end_date=parse_date("2019-12-27"), + start_date=parse_date("2019-12-27"), + ) + assert_matches_type(UsageAnalyticsResponse, usage_analytics, path=["response"]) + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + async def test_raw_response_get(self, async_client: AsyncImageKit) -> None: + response = await async_client.accounts.usage_analytics.with_raw_response.get( + end_date=parse_date("2019-12-27"), + start_date=parse_date("2019-12-27"), + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + usage_analytics = await response.parse() + assert_matches_type(UsageAnalyticsResponse, usage_analytics, path=["response"]) + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + async def test_streaming_response_get(self, async_client: AsyncImageKit) -> None: + async with async_client.accounts.usage_analytics.with_streaming_response.get( + end_date=parse_date("2019-12-27"), + start_date=parse_date("2019-12-27"), + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + usage_analytics = await response.parse() + assert_matches_type(UsageAnalyticsResponse, usage_analytics, path=["response"]) + + assert cast(Any, response.is_closed) is True diff --git a/tests/api_resources/test_custom_metadata_fields.py b/tests/api_resources/test_custom_metadata_fields.py index ec73bab1..4cfb101d 100644 --- a/tests/api_resources/test_custom_metadata_fields.py +++ b/tests/api_resources/test_custom_metadata_fields.py @@ -47,6 +47,7 @@ def test_method_create_with_all_params(self, client: ImageKit) -> None: "min_value": 1000, "select_options": ["small", "medium", "large", 30, 40, True], }, + description="description", ) assert_matches_type(CustomMetadataField, custom_metadata_field, path=["response"]) @@ -93,6 +94,7 @@ def test_method_update(self, client: ImageKit) -> None: def test_method_update_with_all_params(self, client: ImageKit) -> None: custom_metadata_field = client.custom_metadata_fields.update( id="id", + description="description", label="price", schema={ "default_value": [True, 10, "Hello"], @@ -251,6 +253,7 @@ async def test_method_create_with_all_params(self, async_client: AsyncImageKit) "min_value": 1000, "select_options": ["small", "medium", "large", 30, 40, True], }, + description="description", ) assert_matches_type(CustomMetadataField, custom_metadata_field, path=["response"]) @@ -297,6 +300,7 @@ async def test_method_update(self, async_client: AsyncImageKit) -> None: async def test_method_update_with_all_params(self, async_client: AsyncImageKit) -> None: custom_metadata_field = await async_client.custom_metadata_fields.update( id="id", + description="description", label="price", schema={ "default_value": [True, 10, "Hello"], diff --git a/tests/api_resources/test_named_transformations.py b/tests/api_resources/test_named_transformations.py new file mode 100644 index 00000000..d59095c8 --- /dev/null +++ b/tests/api_resources/test_named_transformations.py @@ -0,0 +1,451 @@ +# 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 imagekitio import ImageKit, AsyncImageKit +from tests.utils import assert_matches_type +from imagekitio.types import ( + NamedTransformationListResponse, +) +from imagekitio.types.shared import NamedTransformation + +base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") + + +class TestNamedTransformations: + parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + def test_method_create(self, client: ImageKit) -> None: + named_transformation = client.named_transformations.create( + name="small_thumbnail", + transformation="w-150,h-150,fo-center,cm-pad_resize", + ) + assert_matches_type(NamedTransformation, named_transformation, path=["response"]) + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + def test_method_create_with_all_params(self, client: ImageKit) -> None: + named_transformation = client.named_transformations.create( + name="small_thumbnail", + transformation="w-150,h-150,fo-center,cm-pad_resize", + enabled=True, + ) + assert_matches_type(NamedTransformation, named_transformation, path=["response"]) + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + def test_raw_response_create(self, client: ImageKit) -> None: + response = client.named_transformations.with_raw_response.create( + name="small_thumbnail", + transformation="w-150,h-150,fo-center,cm-pad_resize", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + named_transformation = response.parse() + assert_matches_type(NamedTransformation, named_transformation, path=["response"]) + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + def test_streaming_response_create(self, client: ImageKit) -> None: + with client.named_transformations.with_streaming_response.create( + name="small_thumbnail", + transformation="w-150,h-150,fo-center,cm-pad_resize", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + named_transformation = response.parse() + assert_matches_type(NamedTransformation, named_transformation, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + def test_method_update(self, client: ImageKit) -> None: + named_transformation = client.named_transformations.update( + id="6bZ9x2ZUx", + ) + assert_matches_type(NamedTransformation, named_transformation, path=["response"]) + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + def test_method_update_with_all_params(self, client: ImageKit) -> None: + named_transformation = client.named_transformations.update( + id="6bZ9x2ZUx", + enabled=False, + name="small_thumbnail", + transformation="w-200,h-200,fo-center,cm-pad_resize", + ) + assert_matches_type(NamedTransformation, named_transformation, path=["response"]) + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + def test_raw_response_update(self, client: ImageKit) -> None: + response = client.named_transformations.with_raw_response.update( + id="6bZ9x2ZUx", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + named_transformation = response.parse() + assert_matches_type(NamedTransformation, named_transformation, path=["response"]) + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + def test_streaming_response_update(self, client: ImageKit) -> None: + with client.named_transformations.with_streaming_response.update( + id="6bZ9x2ZUx", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + named_transformation = response.parse() + assert_matches_type(NamedTransformation, named_transformation, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + def test_path_params_update(self, client: ImageKit) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): + client.named_transformations.with_raw_response.update( + id="", + ) + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + def test_method_list(self, client: ImageKit) -> None: + named_transformation = client.named_transformations.list() + assert_matches_type(NamedTransformationListResponse, named_transformation, path=["response"]) + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + def test_raw_response_list(self, client: ImageKit) -> None: + response = client.named_transformations.with_raw_response.list() + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + named_transformation = response.parse() + assert_matches_type(NamedTransformationListResponse, named_transformation, path=["response"]) + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + def test_streaming_response_list(self, client: ImageKit) -> None: + with client.named_transformations.with_streaming_response.list() as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + named_transformation = response.parse() + assert_matches_type(NamedTransformationListResponse, named_transformation, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + def test_method_delete(self, client: ImageKit) -> None: + named_transformation = client.named_transformations.delete( + "6bZ9x2ZUx", + ) + assert named_transformation is None + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + def test_raw_response_delete(self, client: ImageKit) -> None: + response = client.named_transformations.with_raw_response.delete( + "6bZ9x2ZUx", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + named_transformation = response.parse() + assert named_transformation is None + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + def test_streaming_response_delete(self, client: ImageKit) -> None: + with client.named_transformations.with_streaming_response.delete( + "6bZ9x2ZUx", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + named_transformation = response.parse() + assert named_transformation is None + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + def test_path_params_delete(self, client: ImageKit) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): + client.named_transformations.with_raw_response.delete( + "", + ) + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + def test_method_get(self, client: ImageKit) -> None: + named_transformation = client.named_transformations.get( + "6bZ9x2ZUx", + ) + assert_matches_type(NamedTransformation, named_transformation, path=["response"]) + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + def test_raw_response_get(self, client: ImageKit) -> None: + response = client.named_transformations.with_raw_response.get( + "6bZ9x2ZUx", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + named_transformation = response.parse() + assert_matches_type(NamedTransformation, named_transformation, path=["response"]) + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + def test_streaming_response_get(self, client: ImageKit) -> None: + with client.named_transformations.with_streaming_response.get( + "6bZ9x2ZUx", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + named_transformation = response.parse() + assert_matches_type(NamedTransformation, named_transformation, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + def test_path_params_get(self, client: ImageKit) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): + client.named_transformations.with_raw_response.get( + "", + ) + + +class TestAsyncNamedTransformations: + parametrize = pytest.mark.parametrize( + "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] + ) + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + async def test_method_create(self, async_client: AsyncImageKit) -> None: + named_transformation = await async_client.named_transformations.create( + name="small_thumbnail", + transformation="w-150,h-150,fo-center,cm-pad_resize", + ) + assert_matches_type(NamedTransformation, named_transformation, path=["response"]) + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + async def test_method_create_with_all_params(self, async_client: AsyncImageKit) -> None: + named_transformation = await async_client.named_transformations.create( + name="small_thumbnail", + transformation="w-150,h-150,fo-center,cm-pad_resize", + enabled=True, + ) + assert_matches_type(NamedTransformation, named_transformation, path=["response"]) + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + async def test_raw_response_create(self, async_client: AsyncImageKit) -> None: + response = await async_client.named_transformations.with_raw_response.create( + name="small_thumbnail", + transformation="w-150,h-150,fo-center,cm-pad_resize", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + named_transformation = await response.parse() + assert_matches_type(NamedTransformation, named_transformation, path=["response"]) + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + async def test_streaming_response_create(self, async_client: AsyncImageKit) -> None: + async with async_client.named_transformations.with_streaming_response.create( + name="small_thumbnail", + transformation="w-150,h-150,fo-center,cm-pad_resize", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + named_transformation = await response.parse() + assert_matches_type(NamedTransformation, named_transformation, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + async def test_method_update(self, async_client: AsyncImageKit) -> None: + named_transformation = await async_client.named_transformations.update( + id="6bZ9x2ZUx", + ) + assert_matches_type(NamedTransformation, named_transformation, path=["response"]) + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + async def test_method_update_with_all_params(self, async_client: AsyncImageKit) -> None: + named_transformation = await async_client.named_transformations.update( + id="6bZ9x2ZUx", + enabled=False, + name="small_thumbnail", + transformation="w-200,h-200,fo-center,cm-pad_resize", + ) + assert_matches_type(NamedTransformation, named_transformation, path=["response"]) + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + async def test_raw_response_update(self, async_client: AsyncImageKit) -> None: + response = await async_client.named_transformations.with_raw_response.update( + id="6bZ9x2ZUx", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + named_transformation = await response.parse() + assert_matches_type(NamedTransformation, named_transformation, path=["response"]) + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + async def test_streaming_response_update(self, async_client: AsyncImageKit) -> None: + async with async_client.named_transformations.with_streaming_response.update( + id="6bZ9x2ZUx", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + named_transformation = await response.parse() + assert_matches_type(NamedTransformation, named_transformation, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + async def test_path_params_update(self, async_client: AsyncImageKit) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): + await async_client.named_transformations.with_raw_response.update( + id="", + ) + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + async def test_method_list(self, async_client: AsyncImageKit) -> None: + named_transformation = await async_client.named_transformations.list() + assert_matches_type(NamedTransformationListResponse, named_transformation, path=["response"]) + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + async def test_raw_response_list(self, async_client: AsyncImageKit) -> None: + response = await async_client.named_transformations.with_raw_response.list() + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + named_transformation = await response.parse() + assert_matches_type(NamedTransformationListResponse, named_transformation, path=["response"]) + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + async def test_streaming_response_list(self, async_client: AsyncImageKit) -> None: + async with async_client.named_transformations.with_streaming_response.list() as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + named_transformation = await response.parse() + assert_matches_type(NamedTransformationListResponse, named_transformation, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + async def test_method_delete(self, async_client: AsyncImageKit) -> None: + named_transformation = await async_client.named_transformations.delete( + "6bZ9x2ZUx", + ) + assert named_transformation is None + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + async def test_raw_response_delete(self, async_client: AsyncImageKit) -> None: + response = await async_client.named_transformations.with_raw_response.delete( + "6bZ9x2ZUx", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + named_transformation = await response.parse() + assert named_transformation is None + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + async def test_streaming_response_delete(self, async_client: AsyncImageKit) -> None: + async with async_client.named_transformations.with_streaming_response.delete( + "6bZ9x2ZUx", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + named_transformation = await response.parse() + assert named_transformation is None + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + async def test_path_params_delete(self, async_client: AsyncImageKit) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): + await async_client.named_transformations.with_raw_response.delete( + "", + ) + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + async def test_method_get(self, async_client: AsyncImageKit) -> None: + named_transformation = await async_client.named_transformations.get( + "6bZ9x2ZUx", + ) + assert_matches_type(NamedTransformation, named_transformation, path=["response"]) + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + async def test_raw_response_get(self, async_client: AsyncImageKit) -> None: + response = await async_client.named_transformations.with_raw_response.get( + "6bZ9x2ZUx", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + named_transformation = await response.parse() + assert_matches_type(NamedTransformation, named_transformation, path=["response"]) + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + async def test_streaming_response_get(self, async_client: AsyncImageKit) -> None: + async with async_client.named_transformations.with_streaming_response.get( + "6bZ9x2ZUx", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + named_transformation = await response.parse() + assert_matches_type(NamedTransformation, named_transformation, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @pytest.mark.skip(reason="Mock server tests are disabled") + @parametrize + async def test_path_params_get(self, async_client: AsyncImageKit) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `id` but received ''"): + await async_client.named_transformations.with_raw_response.get( + "", + )