diff --git a/.devcontainer/devcontainer-lock.json b/.devcontainer/devcontainer-lock.json deleted file mode 100644 index 96d86886..00000000 --- a/.devcontainer/devcontainer-lock.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "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/ci.yml b/.github/workflows/ci.yml index 7bcc0a32..ed433ce0 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -18,7 +18,7 @@ jobs: lint: timeout-minutes: 10 name: lint - runs-on: ubuntu-latest + runs-on: ${{ github.repository == 'stainless-sdks/imagekit-python' && 'depot-ubuntu-24.04' || 'ubuntu-latest' }} if: (github.event_name == 'push' || github.event.pull_request.head.repo.fork) && (github.event_name != 'push' || github.event.head_commit.message != 'codegen metadata') steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 @@ -44,7 +44,7 @@ jobs: permissions: contents: read id-token: write - runs-on: ubuntu-latest + runs-on: ${{ github.repository == 'stainless-sdks/imagekit-python' && 'depot-ubuntu-24.04' || 'ubuntu-latest' }} steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 @@ -61,3 +61,44 @@ jobs: - name: Run build run: rye build + + - name: Get GitHub OIDC Token + if: |- + github.repository == 'stainless-sdks/imagekit-python' && + !startsWith(github.ref, 'refs/heads/stl/') + id: github-oidc + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0 + with: + script: core.setOutput('github_token', await core.getIDToken()); + + - name: Upload tarball + if: |- + github.repository == 'stainless-sdks/imagekit-python' && + !startsWith(github.ref, 'refs/heads/stl/') + env: + URL: https://pkg.stainless.com/s + AUTH: ${{ steps.github-oidc.outputs.github_token }} + SHA: ${{ github.sha }} + run: ./scripts/utils/upload-artifact.sh + + test: + timeout-minutes: 10 + name: test + runs-on: ${{ github.repository == 'stainless-sdks/imagekit-python' && 'depot-ubuntu-24.04' || 'ubuntu-latest' }} + if: github.event_name == 'push' || github.event.pull_request.head.repo.fork + steps: + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + + - name: Install Rye + run: | + curl -sSf https://rye.astral.sh/get | bash + echo "$HOME/.rye/shims" >> $GITHUB_PATH + env: + RYE_VERSION: '0.44.0' + RYE_INSTALL_OPTION: '--yes' + + - name: Bootstrap + run: ./scripts/bootstrap + + - name: Run tests + run: ./scripts/test diff --git a/.github/workflows/release-doctor.yml b/.github/workflows/release-doctor.yml index 0ba26a30..a52ac7fb 100644 --- a/.github/workflows/release-doctor.yml +++ b/.github/workflows/release-doctor.yml @@ -2,7 +2,7 @@ name: Release Doctor on: pull_request: branches: - - main + - master workflow_dispatch: jobs: @@ -18,5 +18,4 @@ jobs: run: | bash ./bin/check-release-environment env: - RELEASE_PLEASE_TOKEN: ${{ secrets.RELEASE_PLEASE_TOKEN }} PYPI_TOKEN: ${{ secrets.IMAGE_KIT_PYPI_TOKEN || secrets.PYPI_TOKEN }} diff --git a/.github/workflows/release-please.yml b/.github/workflows/release-please.yml deleted file mode 100644 index 07734d2e..00000000 --- a/.github/workflows/release-please.yml +++ /dev/null @@ -1,20 +0,0 @@ -name: Release Please -on: - push: - branches: - - main - -permissions: - contents: write - pull-requests: write - -jobs: - release-please: - if: github.repository == 'imagekit-developer/imagekit-python' - runs-on: ubuntu-latest - - steps: - - uses: googleapis/release-please-action@5c625bfb5d1ff62eadeeb3772007f7f66fdcf071 # v4.4.1 - id: release - with: - token: ${{ secrets.RELEASE_PLEASE_TOKEN }} diff --git a/.github/workflows/sync-release-as.yml b/.github/workflows/sync-release-as.yml deleted file mode 100644 index 6ce6a7a1..00000000 --- a/.github/workflows/sync-release-as.yml +++ /dev/null @@ -1,63 +0,0 @@ -name: Sync Release-As from release PR title - -on: - pull_request: - types: [edited] - -permissions: - contents: write - -jobs: - sync: - if: >- - 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 - steps: - - name: Extract versions from old and new title - id: parse - env: - NEW_TITLE: ${{ github.event.pull_request.title }} - OLD_TITLE: ${{ github.event.changes.title.from }} - run: | - # Anchored on pull-request-title-pattern "release: ${version}" from release-please-config.json. - extract() { - echo "$1" | grep -oE '^release:[[:space:]]+v?[0-9]+\.[0-9]+\.[0-9]+(-[0-9A-Za-z.-]+)?' \ - | sed -E 's/^release:[[:space:]]+v?//' - } - NEW_VERSION=$(extract "$NEW_TITLE") - OLD_VERSION=$(extract "$OLD_TITLE") - echo "old=$OLD_VERSION" - echo "new=$NEW_VERSION" - if [ -z "$NEW_VERSION" ]; then - echo "::notice::No semver in new title; nothing to do." - echo "skip=true" >> "$GITHUB_OUTPUT" - exit 0 - fi - if [ "$NEW_VERSION" = "$OLD_VERSION" ]; then - echo "::notice::Version unchanged ($NEW_VERSION); not pushing." - echo "skip=true" >> "$GITHUB_OUTPUT" - exit 0 - fi - echo "version=$NEW_VERSION" >> "$GITHUB_OUTPUT" - - - name: Check out main - if: steps.parse.outputs.skip != 'true' - uses: actions/checkout@v6 - with: - ref: main - token: ${{ secrets.RELEASE_PLEASE_TOKEN }} - fetch-depth: 1 - - - name: Push empty Release-As commit - if: steps.parse.outputs.skip != 'true' - env: - VERSION: ${{ steps.parse.outputs.version }} - run: | - git config user.name "release-as-bot" - git config user.email "release-as-bot@users.noreply.github.com" - git commit --allow-empty -m "chore: pin next release - - Release-As: ${VERSION}" - git push origin main diff --git a/.gitignore b/.gitignore index fe299350..3824f4c4 100644 --- a/.gitignore +++ b/.gitignore @@ -14,5 +14,3 @@ dist .envrc codegen.log Brewfile.lock.json - -examples/temp \ No newline at end of file diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 8ed0bbc0..4a0511b9 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "5.9.0" + ".": "5.7.0" } \ No newline at end of file diff --git a/.stats.yml b/.stats.yml index c71d849e..3e656416 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1 +1,4 @@ -configured_endpoints: 54 +configured_endpoints: 48 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/imagekit-inc/imagekit-5280e65473c0c42b7f41216fd22aeb469acb5f54b6ee1a3181f3ec9b7a19dc17.yml +openapi_spec_hash: 7c103e2dff0edcbeea82057e62f58d4d +config_hash: 7960882e624d385c4d9aecca2132adad diff --git a/.vscode/settings.json b/.vscode/settings.json index c3a26d68..5b010307 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,4 +1,3 @@ { "python.analysis.importFormat": "relative", - "python.analysis.typeCheckingMode": "basic" } diff --git a/CHANGELOG.md b/CHANGELOG.md deleted file mode 100644 index 19568404..00000000 --- a/CHANGELOG.md +++ /dev/null @@ -1,297 +0,0 @@ -# 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) - - -### Features - -* **origins:** add useIAMRole for IAM role authentication ([ec709a5](https://github.com/imagekit-developer/imagekit-python/commit/ec709a515b07f3d7fba758357b5dd8d6e3432bf1)) - - -### Documentation - -* update default value in description for intensity in colorize transformation ([a67b14e](https://github.com/imagekit-developer/imagekit-python/commit/a67b14ebe09b7a8f3679a7b7793321d607a25563)) - -## [5.6.0](https://github.com/imagekit-developer/imagekit-python/compare/v5.5.2...v5.6.0) (2026-06-03) - - -### Bug Fixes - -* metadata response shape ([8a8a923](https://github.com/imagekit-developer/imagekit-python/commit/8a8a9231073d8701dd6bed22a1694f13632e66f4)) - - -### Chores - -* pin next release ([c964964](https://github.com/imagekit-developer/imagekit-python/commit/c964964d9dfb86835bebbbccf1177ef24d6cfe6e)) - -## [5.5.2](https://github.com/imagekit-developer/imagekit-python/compare/v5.5.1...v5.5.2) (2026-05-25) - - -### Features - -* initial stlc build ([8274740](https://github.com/imagekit-developer/imagekit-python/commit/8274740b92f67c625694f84b50ac3f044ec45a1f)) - - -### Chores - -* pin next release ([3e3f6b0](https://github.com/imagekit-developer/imagekit-python/commit/3e3f6b03e9c91b49e07794a928e90c879adb0782)) -* trigger release-please ([0a0e2a2](https://github.com/imagekit-developer/imagekit-python/commit/0a0e2a235a4b3baa3e1202f0f9802dc3285e1eec)) - -## 5.5.1 (2026-05-17) - -Full Changelog: [v5.5.0...v5.5.1](https://github.com/imagekit-developer/imagekit-python/compare/v5.5.0...v5.5.1) - -### Chores - -* trigger build ([ae54bfc](https://github.com/imagekit-developer/imagekit-python/commit/ae54bfcda1d717f8e23b90ba4f9f3407739458e3)) -* trigger build for test ([ac69401](https://github.com/imagekit-developer/imagekit-python/commit/ac6940190c1896d465eb5c6b88e71ffdd0ec02d6)) - -## 5.5.0 (2026-05-13) - -Full Changelog: [v5.4.0...v5.5.0](https://github.com/imagekit-developer/imagekit-python/compare/v5.4.0...v5.5.0) - -### Features - -* **api:** add no-enlarge crop modes and colorize transformation ([c4ddf30](https://github.com/imagekit-developer/imagekit-python/commit/c4ddf30c84d5d10a483ab5b2563510f049e414b2)) -* **api:** manual updates ([965e263](https://github.com/imagekit-developer/imagekit-python/commit/965e26380c05a76c356fc994e0a92dc8efe3ac4e)) -* **helper:** add colorize transformation to supported transforms ([8a96c55](https://github.com/imagekit-developer/imagekit-python/commit/8a96c55d03a9f1448954dd56510781b10b858436)) -* **internal/types:** support eagerly validating pydantic iterators ([35bcec1](https://github.com/imagekit-developer/imagekit-python/commit/35bcec134edcbbf356f5f93d5a1e719f69b458a3)) -* support setting headers via env ([39cbf90](https://github.com/imagekit-developer/imagekit-python/commit/39cbf90f16813f2ee8f477554ece14f804cc765d)) -* **tests:** add colorize transformation to advanced URL generation test ([efa4d19](https://github.com/imagekit-developer/imagekit-python/commit/efa4d197ca7d8a3cba88243d8c187400f17b2dea)) - - -### Bug Fixes - -* **client:** add missing f-string prefix in file type error message ([b3926a7](https://github.com/imagekit-developer/imagekit-python/commit/b3926a7b73d379cf56ce3985bc602254fda83f40)) -* use correct field name format for multipart file arrays ([c89d2b3](https://github.com/imagekit-developer/imagekit-python/commit/c89d2b36f2049df445ac5f365ed8dac2544d116b)) - - -### Performance Improvements - -* **client:** optimize file structure copying in multipart requests ([b22ab86](https://github.com/imagekit-developer/imagekit-python/commit/b22ab86e45de2bbc479942eaa27dd0c9b89cbc91)) - - -### Chores - -* configure new SDK language ([2b40f08](https://github.com/imagekit-developer/imagekit-python/commit/2b40f08e2757811dd14e29aed78f4f928fd97111)) -* **internal:** more robust bootstrap script ([e5703df](https://github.com/imagekit-developer/imagekit-python/commit/e5703dfdb61f9842a508947555544ad9910a225d)) -* **internal:** reformat pyproject.toml ([15a2ce1](https://github.com/imagekit-developer/imagekit-python/commit/15a2ce1f0293a0ab4d96792379e127f68f5cc64d)) - -## 5.4.0 (2026-04-13) - -Full Changelog: [v5.3.0...v5.4.0](https://github.com/imagekit-developer/imagekit-python/compare/v5.3.0...v5.4.0) - -### Features - -* **api:** dam related webhook events ([8803680](https://github.com/imagekit-developer/imagekit-python/commit/8803680ae4bb3ea801d71520cc1354b7a1558bc6)) -* **api:** fix spec indentation ([1a2417d](https://github.com/imagekit-developer/imagekit-python/commit/1a2417d4336d1b9403eb1bc2b65187209fe833c7)) -* **api:** indentation fix ([6ad7341](https://github.com/imagekit-developer/imagekit-python/commit/6ad7341af30e43252519a3c44826be408323cbbe)) -* **api:** merge with main to bring back missing parameters ([a07e952](https://github.com/imagekit-developer/imagekit-python/commit/a07e95275e50dcd975f3ec816420eee7645ce223)) -* **api:** update webhook event names and remove DAM prefix ([bf9e082](https://github.com/imagekit-developer/imagekit-python/commit/bf9e082da50cea2f983b5bd88caca825e5039ec5)) - - -### Bug Fixes - -* **api:** extract shared schemas to prevent Go webhook union breaking changes ([9dcc234](https://github.com/imagekit-developer/imagekit-python/commit/9dcc234c1a5cd387a0989806819ced1b823277c0)) -* **api:** rename DamFile events to File for consistency ([16b113f](https://github.com/imagekit-developer/imagekit-python/commit/16b113f1e6f42b4ac1af43c4cf0567cae55f6ecf)) -* **client:** preserve hardcoded query params when merging with user params ([cbdc71f](https://github.com/imagekit-developer/imagekit-python/commit/cbdc71fee37ce26c0a05cabc55cb03b46c29b216)) -* ensure file data are only sent as 1 parameter ([aa0272a](https://github.com/imagekit-developer/imagekit-python/commit/aa0272a8fe212b1a841031d25fddaa49359ec9d9)) - - -### Documentation - -* improve examples ([bc9d18e](https://github.com/imagekit-developer/imagekit-python/commit/bc9d18e102e37ad28dfe7181cbc3b8323ed79cb2)) - - -### Refactors - -* AITags to singular AITag schema with array items pattern ([96ad1bb](https://github.com/imagekit-developer/imagekit-python/commit/96ad1bb10dbfdad7112d82f5b6cc7199429e0fe3)) - -## 5.3.0 (2026-04-06) - -Full Changelog: [v5.2.0...v5.3.0](https://github.com/imagekit-developer/imagekit-python/compare/v5.2.0...v5.3.0) - -### Features - -* **api:** dpr type update ([39d38db](https://github.com/imagekit-developer/imagekit-python/commit/39d38dbd0ca1e81dc84771e6a98a629f90e8dba9)) -* **api:** Introduce lxc, lyc, lap parameters in overlays. ([5c9a08b](https://github.com/imagekit-developer/imagekit-python/commit/5c9a08b40db8734d022ff4670b8cf9204b2841fd)) -* **api:** revert dpr breaking change ([7301276](https://github.com/imagekit-developer/imagekit-python/commit/73012764930ba8b461f98bbfd0349b395e46a7a4)) -* **client:** import HelperResource and AsyncHelperResource in TYPE_CHECKING block ([22fc9cb](https://github.com/imagekit-developer/imagekit-python/commit/22fc9cb33d8c5724b0a042cc014ed6bdd54f7113)) -* **internal:** implement indices array format for query and form serialization ([4533c28](https://github.com/imagekit-developer/imagekit-python/commit/4533c2831ad26cf5ef53da37c8d4fe095bb67bd8)) -* **overlay:** support camelCase and snake_case for position properties in overlays ([5dd43b9](https://github.com/imagekit-developer/imagekit-python/commit/5dd43b9d84722fa62ffff4b8985282489003aa13)) - - -### Bug Fixes - -* **deps:** bump minimum typing-extensions version ([393174d](https://github.com/imagekit-developer/imagekit-python/commit/393174d253a106393b888aed50f9ca7623c9c06e)) -* **pydantic:** do not pass `by_alias` unless set ([fda01e4](https://github.com/imagekit-developer/imagekit-python/commit/fda01e45e315c192d24a6183cd24fd43cbcb0722)) -* sanitize endpoint path params ([fa1972c](https://github.com/imagekit-developer/imagekit-python/commit/fa1972cd605c2a7a81ad069161cc687d0ec4193d)) - - -### Chores - -* **ci:** skip lint on metadata-only changes ([5e9e6f3](https://github.com/imagekit-developer/imagekit-python/commit/5e9e6f35ba227e2fda57cb343f26f0ad0bcbb584)) -* **ci:** skip uploading artifacts on stainless-internal branches ([15805f5](https://github.com/imagekit-developer/imagekit-python/commit/15805f5e6b642f0cebfbc8131f99f886e8e72e99)) -* **dependencies:** require standardwebhooks 1.0.1 ([f7c4465](https://github.com/imagekit-developer/imagekit-python/commit/f7c44652ef95cfa1aefef380d280036158519007)) -* format all `api.md` files ([09cbb17](https://github.com/imagekit-developer/imagekit-python/commit/09cbb17e722d374477b13fd4045201ab75ddcc7e)) -* **internal:** add request options to SSE classes ([c0dee43](https://github.com/imagekit-developer/imagekit-python/commit/c0dee43afe3bc1f6ea35649532594e59fb4b8953)) -* **internal:** bump dependencies ([6702b4b](https://github.com/imagekit-developer/imagekit-python/commit/6702b4bcd12af1d670a4b73a7d9bedd68ccc5560)) -* **internal:** fix lint error on Python 3.14 ([89d503b](https://github.com/imagekit-developer/imagekit-python/commit/89d503b2a885f57edbbd6ffded3d1cddac61a53e)) -* **internal:** make `test_proxy_environment_variables` more resilient ([821dd3f](https://github.com/imagekit-developer/imagekit-python/commit/821dd3f61db9b2be3297dc1b8a9e63d257df9ed1)) -* **internal:** make `test_proxy_environment_variables` more resilient to env ([487887e](https://github.com/imagekit-developer/imagekit-python/commit/487887eb0e4c405c0f8294e6a82ef6c7a2187c5c)) -* **internal:** remove mock server code ([978ed61](https://github.com/imagekit-developer/imagekit-python/commit/978ed611a909e2d616b322e23bfe3d14e8f256f4)) -* **internal:** tweak CI branches ([369ff73](https://github.com/imagekit-developer/imagekit-python/commit/369ff736880f83ac7196411241801fe9b04a7dfb)) -* **internal:** update gitignore ([ab04623](https://github.com/imagekit-developer/imagekit-python/commit/ab04623fdceb9337d9519b119ead7949f1d4ed2f)) -* **tests:** update webhook tests ([d94ada8](https://github.com/imagekit-developer/imagekit-python/commit/d94ada85d3c70d8f896fe276d73afd6c3fb17326)) -* update mock server docs ([54f47c6](https://github.com/imagekit-developer/imagekit-python/commit/54f47c663b48f2b6a88bf05ba26a0f2a139ee752)) -* update placeholder string ([d06cdca](https://github.com/imagekit-developer/imagekit-python/commit/d06cdca52df17c23df1d9cd8a468b8184bde219a)) - -## 5.2.0 (2026-02-02) - -Full Changelog: [v5.1.2...v5.2.0](https://github.com/imagekit-developer/imagekit-python/compare/v5.1.2...v5.2.0) - -### Features - -* **api:** add customMetadata property to folder schema ([9b8597b](https://github.com/imagekit-developer/imagekit-python/commit/9b8597b8d8b4f11eb4c9e93ddbd924169fe9b0ea)) -* **client:** add custom JSON encoder for extended type support ([2d7dd40](https://github.com/imagekit-developer/imagekit-python/commit/2d7dd4063992e7c49518ea8bca1bbf9dfec7aa9c)) - - -### Bug Fixes - -* **api:** add missing embeddedMetadata and video properties to FileDetails ([b1ffb23](https://github.com/imagekit-developer/imagekit-python/commit/b1ffb235b3f6dae292af80bd99d965db44db47f9)) - -## 5.1.2 (2026-01-29) - -Full Changelog: [v5.1.1...v5.1.2](https://github.com/imagekit-developer/imagekit-python/compare/v5.1.1...v5.1.2) - -### Bug Fixes - -* **docs:** fix mcp installation instructions for remote servers ([df26dbd](https://github.com/imagekit-developer/imagekit-python/commit/df26dbdccce2814bbf30ee94853883a266211586)) -* **tests:** update subtitle overlay references from "l-subtitle" to "l-subtitles" ([11fb58a](https://github.com/imagekit-developer/imagekit-python/commit/11fb58a82c0ff8eb5bdf4bf779b15ea85046604a)) - - -### Chores - -* **ci:** upgrade `actions/github-script` ([a75c01b](https://github.com/imagekit-developer/imagekit-python/commit/a75c01be5c51bdee1531f89b45519af872bb8c59)) - -## 5.1.1 (2026-01-20) - -Full Changelog: [v5.1.0...v5.1.1](https://github.com/imagekit-developer/imagekit-python/compare/v5.1.0...v5.1.1) - -### Bug Fixes - -* vocab field is required ([4ab29b2](https://github.com/imagekit-developer/imagekit-python/commit/4ab29b248b89398b4334d6e1946a35a561997b2a)) - - -### Chores - -* **internal:** update `actions/checkout` version ([7826590](https://github.com/imagekit-developer/imagekit-python/commit/782659076636d78290d488da3f834343550627c8)) - -## 5.1.0 (2026-01-16) - -Full Changelog: [v5.0.0...v5.1.0](https://github.com/imagekit-developer/imagekit-python/compare/v5.0.0...v5.1.0) - -### Features - -* add support for new transformations and layer modes in URL generation ([5fd87b1](https://github.com/imagekit-developer/imagekit-python/commit/5fd87b198090318eb19eb68c1d06ebc3636d735c)) -* **api:** Add saved extensions API and enhance transformation options ([a0781ed](https://github.com/imagekit-developer/imagekit-python/commit/a0781edc19f2cbd78a87e973e0cc2277079fb02a)) -* **client:** add support for binary request streaming ([f8580d6](https://github.com/imagekit-developer/imagekit-python/commit/f8580d644e31312e439a54704ca2e3858407ea0b)) - - -### Bug Fixes - -* add ai-tasks property to response schemas with enum values ([06de9eb](https://github.com/imagekit-developer/imagekit-python/commit/06de9ebc34e6fbf21f3863cd86d75556c429ff8f)) -* **client:** loosen auth header validation ([40ef10e](https://github.com/imagekit-developer/imagekit-python/commit/40ef10e6e81ff3727a095aead127d296486a3c09)) -* use async_to_httpx_files in patch method ([0014808](https://github.com/imagekit-developer/imagekit-python/commit/0014808307e55091a943d2f6b087fefbaee8ed0a)) - - -### Chores - -* **internal:** add `--fix` argument to lint script ([e6bf019](https://github.com/imagekit-developer/imagekit-python/commit/e6bf0196fe985302e11fb440cd3d215114a8e4c3)) -* **internal:** add missing files argument to base client ([aec7892](https://github.com/imagekit-developer/imagekit-python/commit/aec7892b063c00b730afcdc440c0fa3ebe1cdae8)) -* **internal:** codegen related update ([49635b4](https://github.com/imagekit-developer/imagekit-python/commit/49635b4dc6bd4268fc6a62f9df2a2e15c56afcee)) -* speedup initial import ([ad1da84](https://github.com/imagekit-developer/imagekit-python/commit/ad1da84adad57d0a64a8f06a04c6ddb6b8f0e96b)) - - -### Documentation - -* prominently feature MCP server setup in root SDK readmes ([51c1a9a](https://github.com/imagekit-developer/imagekit-python/commit/51c1a9ae1545a25b574195ec73b83dab64d9becb)) - -## 5.0.0 (2025-12-13) - -Full Changelog: [v0.0.1...v5.0.0](https://github.com/imagekit-developer/imagekit-python/compare/v0.0.1...v5.0.0) - -### Features - -* add bulk delete options ([c1c4d32](https://github.com/imagekit-developer/imagekit-python/commit/c1c4d3206b06594ba77a8a1c4dab7d0c5b74de9a)) -* add file related functionalities ([681677b](https://github.com/imagekit-developer/imagekit-python/commit/681677bc60a207f433b4bc242c41e37f2d4c05a1)) -* add sdk version to url ([9c3e67d](https://github.com/imagekit-developer/imagekit-python/commit/9c3e67d20f78b799e974889420ead23f457b5cfa)) -* add url class for url genration ([5e615ed](https://github.com/imagekit-developer/imagekit-python/commit/5e615ed34386e3231c5c7963ff37ceb28ab7d2f1)) -* **api:** python publish true ([8072dfd](https://github.com/imagekit-developer/imagekit-python/commit/8072dfd2eee562f98ac79fb5b11afe700e0dd6a3)) -* implement client with all func. ([67dd4b2](https://github.com/imagekit-developer/imagekit-python/commit/67dd4b28822086009278e4ab3f85d52690e6e9b7)) -* implement get_remote_url_metadata ([1272740](https://github.com/imagekit-developer/imagekit-python/commit/12727400dc5bc6678f6769c5143c11962f58eea4)) -* **webhooks:** allow key parameter to accept bytes in unwrap method ([09ae375](https://github.com/imagekit-developer/imagekit-python/commit/09ae37575b6b1eba57f67c6b1dea3d59e10d270d)) - - -### Bug Fixes - -* binary file upload ([23c9c46](https://github.com/imagekit-developer/imagekit-python/commit/23c9c46f37a5b32144f86700227254e6f05bf491)) -* change ubuntu latest to ubuntu-20.04 in test.yml ([1e4b551](https://github.com/imagekit-developer/imagekit-python/commit/1e4b55192d08ebf1aa436fa56832322477605942)) -* Changes for CI/CD ([0bd2ac3](https://github.com/imagekit-developer/imagekit-python/commit/0bd2ac3e9b11e8269a2eacb2424d49ef58e37c5f)) -* fix issue [#35](https://github.com/imagekit-developer/imagekit-python/issues/35),[#37](https://github.com/imagekit-developer/imagekit-python/issues/37),[#41](https://github.com/imagekit-developer/imagekit-python/issues/41),[#44](https://github.com/imagekit-developer/imagekit-python/issues/44) ([1f913c8](https://github.com/imagekit-developer/imagekit-python/commit/1f913c8e34a06afbffa93adbbc79e8a174a02dac)) -* fix query params implementation ([2b7e6d4](https://github.com/imagekit-developer/imagekit-python/commit/2b7e6d4a148b6d94b52532846bd950d4eeeefac4)) -* make ik-attachment option handle True boolean value ([6eb9cd0](https://github.com/imagekit-developer/imagekit-python/commit/6eb9cd099021a1fd9bcc9dfeb080ec610d4bcfbd)) -* move the workflow to correct folder ([d9f933a](https://github.com/imagekit-developer/imagekit-python/commit/d9f933a8e78c61b8a61df1d74a28859f9e889378)) -* request toolbelt to 0.10.1 in requirements/test/txt ([c22ed89](https://github.com/imagekit-developer/imagekit-python/commit/c22ed89208f69f7d8fb21cc777049d72dad40093)) -* **serialization:** adjust custom_metadata type check for serialization ([6e3f209](https://github.com/imagekit-developer/imagekit-python/commit/6e3f2092cad4b2c3ed7d1f3086c7bfb2a9a51b08)) - - -### Chores - -* add func alias ([d7ce593](https://github.com/imagekit-developer/imagekit-python/commit/d7ce593318b24f33ba828b65042e16e892690b80)) -* add init file ([0cbbd27](https://github.com/imagekit-developer/imagekit-python/commit/0cbbd27f00ac3fe36d3fbc0bf6fa2b015308576c)) -* add publish github workflow script ([a275172](https://github.com/imagekit-developer/imagekit-python/commit/a275172c3e7096b7390665102bae4d95c718db9d)) -* add required constants ([48de1c0](https://github.com/imagekit-developer/imagekit-python/commit/48de1c02295fb42d522f8ee930c16ee763d7b93d)) -* add requirements files ([e8d3d9d](https://github.com/imagekit-developer/imagekit-python/commit/e8d3d9d60e946b036b3f8e37a9dbf1e68be5482d)) -* add sample file for devs ([65d1a3f](https://github.com/imagekit-developer/imagekit-python/commit/65d1a3f77eaa5a5c9dba5202a75dee3c70aa64a0)) -* add sample of get file metadata ([6d11584](https://github.com/imagekit-developer/imagekit-python/commit/6d115841c341df0f7a9d4d9bd0c33c1cf386d9c7)) -* change pacakge name & fix import ([2c1734a](https://github.com/imagekit-developer/imagekit-python/commit/2c1734a6e12c935bc80f72ec6b8cdd5a971e5a47)) -* fix package name ([c0c939d](https://github.com/imagekit-developer/imagekit-python/commit/c0c939d86fa5738855a0d6b606e33249ecd5a47a)) -* fix package name ([4bc8041](https://github.com/imagekit-developer/imagekit-python/commit/4bc8041e22c6333710645ddc95446c9c348eea5b)) -* fix sample ([2188038](https://github.com/imagekit-developer/imagekit-python/commit/2188038436aabfce68a3c1d7bb198ffda203dc72)) -* init ([febccef](https://github.com/imagekit-developer/imagekit-python/commit/febccef19d6ca6ae2b6c4272d44ae1625c9f3391)) -* remove unecessary workflow file ([97f19eb](https://github.com/imagekit-developer/imagekit-python/commit/97f19eb8284c5edfe164f98ad296ea1e69b21bf8)) -* remove unused dummy methods from API documentation ([4727908](https://github.com/imagekit-developer/imagekit-python/commit/472790845ef7009aa3695fc084ef8c5d1d63f2ab)) -* sync repo ([c6afd44](https://github.com/imagekit-developer/imagekit-python/commit/c6afd449e74ebb20ebc8d3390355219fccaf2178)) -* unused import removed ([22774ff](https://github.com/imagekit-developer/imagekit-python/commit/22774fff1ac08c0573efc06ab10f3fe31e6d3f69)) -* update SDK settings ([81f0de9](https://github.com/imagekit-developer/imagekit-python/commit/81f0de954a0d531c6b98354386462f4186a58aba)) - - -### Build System - -* add url and requirements ([211228e](https://github.com/imagekit-developer/imagekit-python/commit/211228ef91fe29b83507c89f3bf22cfb6b1c8184)) -* add url and requirements ([683ad01](https://github.com/imagekit-developer/imagekit-python/commit/683ad016099d4e4614b6f369bff69d9a7422029e)) -* add url and requirements ([#2](https://github.com/imagekit-developer/imagekit-python/issues/2)) ([211228e](https://github.com/imagekit-developer/imagekit-python/commit/211228ef91fe29b83507c89f3bf22cfb6b1c8184)) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index b8304fb3..43b35bfe 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.git +$ pip install git+ssh://git@github.com/imagekit-developer/imagekit-python#master.git ``` Alternatively, you can build from source and install the wheel file: diff --git a/README.md b/README.md index 38e270e8..0a875350 100644 --- a/README.md +++ b/README.md @@ -1,42 +1,24 @@ -# ImageKit.io Python SDK +# Image Kit Python API library [![PyPI version](https://img.shields.io/pypi/v/imagekitio.svg?label=pypi%20(stable))](https://pypi.org/project/imagekitio/) -The ImageKit Python SDK provides convenient access to the ImageKit REST API from any Python 3.9+ application. It offers powerful tools for URL generation and transformation, signed URLs for secure content delivery, webhook verification, file uploads, and more. The library includes type definitions for all request params and response fields, and offers both synchronous and asynchronous clients powered by [httpx](https://github.com/encode/httpx). +The Image Kit Python library provides convenient access to the Image Kit REST API from any Python 3.9+ +application. The library includes type definitions for all request params and response fields, +and offers both synchronous and asynchronous clients powered by [httpx](https://github.com/encode/httpx). -The REST API documentation can be found on [imagekit.io](https://imagekit.io/docs/api-reference). The full API of this library can be found in [api.md](api.md). +## MCP Server + +Use the Image Kit MCP Server to enable AI assistants to interact with this API, allowing them to explore endpoints, make test requests, and use documentation to help integrate this SDK into your application. + +[![Add to Cursor](https://cursor.com/deeplink/mcp-install-dark.svg)](https://cursor.com/en-US/install-mcp?name=%40imagekit%2Fapi-mcp&config=eyJjb21tYW5kIjoibnB4IiwiYXJncyI6WyIteSIsIkBpbWFnZWtpdC9hcGktbWNwIl0sImVudiI6eyJJTUFHRUtJVF9QUklWQVRFX0tFWSI6Ik15IFByaXZhdGUgS2V5IiwiT1BUSU9OQUxfSU1BR0VLSVRfSUdOT1JFU19USElTIjoiTXkgUGFzc3dvcmQiLCJJTUFHRUtJVF9XRUJIT09LX1NFQ1JFVCI6Ik15IFdlYmhvb2sgU2VjcmV0In19) +[![Install in VS Code](https://img.shields.io/badge/_-Add_to_VS_Code-blue?style=for-the-badge&logo=data:image/svg%2bxml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGZpbGw9Im5vbmUiIHZpZXdCb3g9IjAgMCA0MCA0MCI+PHBhdGggZmlsbD0iI0VFRSIgZmlsbC1ydWxlPSJldmVub2RkIiBkPSJNMzAuMjM1IDM5Ljg4NGEyLjQ5MSAyLjQ5MSAwIDAgMS0xLjc4MS0uNzNMMTIuNyAyNC43OGwtMy40NiAyLjYyNC0zLjQwNiAyLjU4MmExLjY2NSAxLjY2NSAwIDAgMS0xLjA4Mi4zMzggMS42NjQgMS42NjQgMCAwIDEtMS4wNDYtLjQzMWwtMi4yLTJhMS42NjYgMS42NjYgMCAwIDEgMC0yLjQ2M0w3LjQ1OCAyMCA0LjY3IDE3LjQ1MyAxLjUwNyAxNC41N2ExLjY2NSAxLjY2NSAwIDAgMSAwLTIuNDYzbDIuMi0yYTEuNjY1IDEuNjY1IDAgMCAxIDIuMTMtLjA5N2w2Ljg2MyA1LjIwOUwyOC40NTIuODQ0YTIuNDg4IDIuNDg4IDAgMCAxIDEuODQxLS43MjljLjM1MS4wMDkuNjk5LjA5MSAxLjAxOS4yNDVsOC4yMzYgMy45NjFhMi41IDIuNSAwIDAgMSAxLjQxNSAyLjI1M3YuMDk5LS4wNDVWMzMuMzd2LS4wNDUuMDk1YTIuNTAxIDIuNTAxIDAgMCAxLTEuNDE2IDIuMjU3bC04LjIzNSAzLjk2MWEyLjQ5MiAyLjQ5MiAwIDAgMS0xLjA3Ny4yNDZabS43MTYtMjguOTQ3LTExLjk0OCA5LjA2MiAxMS45NTIgOS4wNjUtLjAwNC0xOC4xMjdaIi8+PC9zdmc+)](https://vscode.stainless.com/mcp/%7B%22name%22%3A%22%40imagekit%2Fapi-mcp%22%2C%22command%22%3A%22npx%22%2C%22args%22%3A%5B%22-y%22%2C%22%40imagekit%2Fapi-mcp%22%5D%2C%22env%22%3A%7B%22IMAGEKIT_PRIVATE_KEY%22%3A%22My%20Private%20Key%22%2C%22OPTIONAL_IMAGEKIT_IGNORES_THIS%22%3A%22My%20Password%22%2C%22IMAGEKIT_WEBHOOK_SECRET%22%3A%22My%20Webhook%20Secret%22%7D%7D) -## Table of Contents - -- [Installation](#installation) -- [Requirements](#requirements) -- [Usage](#usage) - - [Using types](#using-types) - - [Nested params](#nested-params) - - [Async usage](#async-usage) -- [URL generation](#url-generation) - - [Basic URL generation](#basic-url-generation) - - [URL generation with transformations](#url-generation-with-transformations) - - [URL generation with image overlay](#url-generation-with-image-overlay) - - [URL generation with text overlay](#url-generation-with-text-overlay) - - [URL generation with multiple overlays](#url-generation-with-multiple-overlays) - - [Signed URLs for secure delivery](#signed-urls-for-secure-delivery) - - [Using Raw transformations for undocumented features](#using-raw-transformations-for-undocumented-features) -- [Authentication parameters for client-side uploads](#authentication-parameters-for-client-side-uploads) -- [Webhook verification](#webhook-verification) -- [Advanced Usage](#advanced-usage) - - [File uploads](#file-uploads) - - [Handling errors](#handling-errors) - - [Retries](#retries) - - [Timeouts](#timeouts) - - [Logging](#logging) - - [Accessing raw response data](#accessing-raw-response-data-eg-headers) - - [Making custom/undocumented requests](#making-customundocumented-requests) - - [Configuring the HTTP client](#configuring-the-http-client) - - [Managing HTTP resources](#managing-http-resources) -- [Versioning](#versioning) -- [Contributing](#contributing) +> Note: You may need to set environment variables in your MCP client. + +## Documentation + +The REST API documentation can be found on [imagekit.io](https://imagekit.io/docs/api-reference). The full API of this library can be found in [api.md](api.md). ## Installation @@ -55,18 +37,16 @@ from imagekitio import ImageKit client = ImageKit( private_key=os.environ.get("IMAGEKIT_PRIVATE_KEY"), # This is the default and can be omitted + password=os.environ.get( + "OPTIONAL_IMAGEKIT_IGNORES_THIS" + ), # This is the default and can be omitted ) -# Upload a file -with open("/path/to/your/image.jpg", "rb") as f: - file_data = f.read() - response = client.files.upload( - file=file_data, - file_name="uploaded-image.jpg", + file=b"https://www.example.com/public-url.jpg", + file_name="file-name.jpg", ) -print(response.file_id) -print(response.url) +print(response.video_codec) ``` While you can provide a `private_key` keyword argument, @@ -74,50 +54,7 @@ we recommend using [python-dotenv](https://pypi.org/project/python-dotenv/) to add `IMAGEKIT_PRIVATE_KEY="My Private Key"` to your `.env` file so that your Private Key is not stored in source control. - -### Using types - -Nested request parameters are [TypedDicts](https://docs.python.org/3/library/typing.html#typing.TypedDict). Responses are [Pydantic models](https://docs.pydantic.dev) which also provide helper methods for things like: - -- Serializing back into JSON, `model.to_json()` -- Converting to a dictionary, `model.to_dict()` - -Typed requests and responses provide autocomplete and documentation within your editor. If you would like to see type errors in VS Code to help catch bugs earlier, set `python.analysis.typeCheckingMode` to `basic`. - -### Nested params - -Nested parameters are dictionaries, typed using `TypedDict`, for example: - -```python -from imagekitio import ImageKit - -client = ImageKit() - -# Read file into memory and upload -with open("/path/to/file.jpg", "rb") as f: - file_data = f.read() - -response = client.files.upload( - file=file_data, - file_name="fileName", - transformation={ - "post": [ - { - "type": "thumbnail", - "value": "w-150,h-150", - }, - { - "protocol": "dash", - "type": "abs", - "value": "sr-240_360_480_720_1080", - }, - ] - }, -) -print(response.file_id) -``` - -### Async usage +## Async usage Simply import `AsyncImageKit` instead of `ImageKit` and use `await` with each API call: @@ -128,20 +65,18 @@ from imagekitio import AsyncImageKit client = AsyncImageKit( private_key=os.environ.get("IMAGEKIT_PRIVATE_KEY"), # This is the default and can be omitted + password=os.environ.get( + "OPTIONAL_IMAGEKIT_IGNORES_THIS" + ), # This is the default and can be omitted ) async def main() -> None: - # Read file into memory and upload - with open("/path/to/your/image.jpg", "rb") as f: - file_data = f.read() - response = await client.files.upload( - file=file_data, + file=b"https://www.example.com/public-url.jpg", file_name="file-name.jpg", ) - print(response.file_id) - print(response.url) + print(response.video_codec) asyncio.run(main()) @@ -149,7 +84,7 @@ asyncio.run(main()) Functionality between the synchronous and asynchronous clients is otherwise identical. -#### With aiohttp +### With aiohttp By default, the async client uses `httpx` for HTTP requests. However, for improved concurrency performance you may also use `aiohttp` as the HTTP backend. @@ -174,400 +109,78 @@ async def main() -> None: private_key=os.environ.get( "IMAGEKIT_PRIVATE_KEY" ), # This is the default and can be omitted + password=os.environ.get( + "OPTIONAL_IMAGEKIT_IGNORES_THIS" + ), # This is the default and can be omitted http_client=DefaultAioHttpClient(), ) as client: - # Read file into memory and upload - with open("/path/to/your/image.jpg", "rb") as f: - file_data = f.read() - response = await client.files.upload( - file=file_data, + file=b"https://www.example.com/public-url.jpg", file_name="file-name.jpg", ) - print(response.file_id) - print(response.url) + print(response.video_codec) asyncio.run(main()) ``` -## URL generation - -The ImageKit SDK provides a powerful `helper.build_url()` method for generating optimized image and video URLs with transformations. Here are examples ranging from simple URLs to complex transformations with overlays and signed URLs. - -### Basic URL generation +## Using types -Generate a simple URL without any transformations: - -```python -import os -from imagekitio import ImageKit - -client = ImageKit( - private_key=os.environ.get("IMAGEKIT_PRIVATE_KEY"), -) - -# Basic URL without transformations -url = client.helper.build_url( - url_endpoint="https://ik.imagekit.io/your_imagekit_id", - src="/path/to/image.jpg", -) -print(url) -# Result: https://ik.imagekit.io/your_imagekit_id/path/to/image.jpg -``` - -### URL generation with transformations - -Apply common transformations like resizing, cropping, and format conversion: - -```python -import os -from imagekitio import ImageKit - -client = ImageKit( - private_key=os.environ.get("IMAGEKIT_PRIVATE_KEY"), -) - -# URL with basic transformations -url = client.helper.build_url( - url_endpoint="https://ik.imagekit.io/your_imagekit_id", - src="/path/to/image.jpg", - transformation=[ - { - "width": 400, - "height": 300, - "crop": "maintain_ratio", - "quality": 80, - "format": "webp", - } - ], -) -print(url) -# Result: https://ik.imagekit.io/your_imagekit_id/path/to/image.jpg?tr=w-400,h-300,c-maintain_ratio,q-80,f-webp -``` - -### URL generation with image overlay - -Add image overlays to your base image: - -```python -import os -from imagekitio import ImageKit - -client = ImageKit( - private_key=os.environ.get("IMAGEKIT_PRIVATE_KEY"), -) - -# URL with image overlay -url = client.helper.build_url( - url_endpoint="https://ik.imagekit.io/your_imagekit_id", - src="/path/to/base-image.jpg", - transformation=[ - { - "width": 500, - "height": 400, - "overlay": { - "type": "image", - "input": "/path/to/overlay-logo.png", - "position": { - "x": 10, - "y": 10, - }, - "transformation": [ - { - "width": 100, - "height": 50, - } - ], - }, - } - ], -) -print(url) -# Result: URL with image overlay positioned at x:10, y:10 -``` - -### URL generation with text overlay - -Add customized text overlays: - -```python -import os -from imagekitio import ImageKit +Nested request parameters are [TypedDicts](https://docs.python.org/3/library/typing.html#typing.TypedDict). Responses are [Pydantic models](https://docs.pydantic.dev) which also provide helper methods for things like: -client = ImageKit( - private_key=os.environ.get("IMAGEKIT_PRIVATE_KEY"), -) +- Serializing back into JSON, `model.to_json()` +- Converting to a dictionary, `model.to_dict()` -# URL with text overlay -url = client.helper.build_url( - url_endpoint="https://ik.imagekit.io/your_imagekit_id", - src="/path/to/base-image.jpg", - transformation=[ - { - "width": 600, - "height": 400, - "overlay": { - "type": "text", - "text": "Sample Text Overlay", - "position": { - "x": 50, - "y": 50, - "focus": "center", - }, - "transformation": [ - { - "font_size": 40, - "font_family": "Arial", - "font_color": "FFFFFF", - "typography": "b", # bold - } - ], - }, - } - ], -) -print(url) -# Result: URL with bold white Arial text overlay at center position -``` +Typed requests and responses provide autocomplete and documentation within your editor. If you would like to see type errors in VS Code to help catch bugs earlier, set `python.analysis.typeCheckingMode` to `basic`. -### URL generation with multiple overlays +## Nested params -Combine multiple overlays for complex compositions: +Nested parameters are dictionaries, typed using `TypedDict`, for example: ```python -import os from imagekitio import ImageKit -client = ImageKit( - private_key=os.environ.get("IMAGEKIT_PRIVATE_KEY"), -) +client = ImageKit() -# URL with multiple overlays (text + image) -url = client.helper.build_url( - url_endpoint="https://ik.imagekit.io/your_imagekit_id", - src="/path/to/base-image.jpg", - transformation=[ - { - "width": 800, - "height": 600, - "overlay": { - "type": "text", - "text": "Header Text", - "position": { - "x": 20, - "y": 20, - }, - "transformation": [ - { - "font_size": 30, - "font_color": "000000", - } - ], +response = client.files.upload( + file=b"Example data", + file_name="fileName", + transformation={ + "post": [ + { + "type": "thumbnail", + "value": "w-150,h-150", }, - }, - { - "overlay": { - "type": "image", - "input": "/watermark.png", - "position": { - "focus": "bottom_right", - }, - "transformation": [ - { - "width": 100, - "opacity": 70, - } - ], + { + "protocol": "dash", + "type": "abs", + "value": "sr-240_360_480_720_1080", }, - }, - ], -) -print(url) -# Result: URL with text overlay at top-left and semi-transparent watermark at bottom-right -``` - -### Signed URLs for secure delivery - -Generate signed URLs that expire after a specified time for secure content delivery: - -```python -import os -from imagekitio import ImageKit - -client = ImageKit( - private_key=os.environ.get("IMAGEKIT_PRIVATE_KEY"), -) - -# Generate a signed URL that expires in 1 hour (3600 seconds) -url = client.helper.build_url( - url_endpoint="https://ik.imagekit.io/your_imagekit_id", - src="/private/secure-image.jpg", - transformation=[ - { - "width": 400, - "height": 300, - "quality": 90, - } - ], - signed=True, - expires_in=3600, # URL expires in 1 hour -) -print(url) -# Result: URL with signature parameters (?ik-t=timestamp&ik-s=signature) - -# Generate a signed URL that doesn't expire -permanent_signed_url = client.helper.build_url( - url_endpoint="https://ik.imagekit.io/your_imagekit_id", - src="/private/secure-image.jpg", - signed=True, - # No expires_in means the URL won't expire -) -print(permanent_signed_url) -# Result: URL with signature parameter (?ik-s=signature) -``` - -### Using Raw transformations for undocumented features - -ImageKit frequently adds new transformation parameters that might not yet be documented in the SDK. You can use the `raw` parameter to access these features or create custom transformation strings: - -```python -import os -from imagekitio import ImageKit - -client = ImageKit( - private_key=os.environ.get("IMAGEKIT_PRIVATE_KEY"), -) - -# Using Raw transformation for undocumented or new parameters -url = client.helper.build_url( - url_endpoint="https://ik.imagekit.io/your_imagekit_id", - src="/path/to/image.jpg", - transformation=[ - { - # Combine documented transformations with raw parameters - "width": 400, - "height": 300, - }, - { - # Use raw for undocumented transformations or complex parameters - "raw": "something-new", - }, - ], -) -print(url) -# Result: https://ik.imagekit.io/your_imagekit_id/path/to/image.jpg?tr=w-400,h-300:something-new -``` - -## Authentication parameters for client-side uploads - -Generate authentication parameters for secure client-side file uploads: - -```python -import os -from imagekitio import ImageKit - -client = ImageKit( - private_key=os.environ.get("IMAGEKIT_PRIVATE_KEY"), -) - -# Generate authentication parameters for client-side uploads -auth_params = client.helper.get_authentication_parameters() -print(auth_params) -# Result: {'expire': , 'signature': '', 'token': ''} - -# Generate with custom token and expiry -custom_auth_params = client.helper.get_authentication_parameters( - token="my-custom-token", - expire=1800 + ] + }, ) -print(custom_auth_params) -# Result: {'expire': 1800, 'signature': '', 'token': 'my-custom-token'} +print(response.transformation) ``` -These authentication parameters can be used in client-side upload forms to securely upload files without exposing your private API key. - -## Webhook verification - -The ImageKit SDK provides utilities to verify webhook signatures for secure event handling. This ensures that webhook requests are actually coming from ImageKit and haven't been tampered with. - -For detailed information about webhook setup, signature verification, and handling different webhook events, refer to the [ImageKit webhook documentation](https://imagekit.io/docs/webhooks#verify-webhook-signature). - -## Advanced Usage - -### File uploads +## File uploads -Request parameters that correspond to file uploads can be passed as `bytes`, a [`PathLike`](https://docs.python.org/3/library/os.html#os.PathLike) instance, an `IO[bytes]` file object, or a tuple of `(filename, contents, media type)`. - -Here are common file upload patterns: +Request parameters that correspond to file uploads can be passed as `bytes`, or a [`PathLike`](https://docs.python.org/3/library/os.html#os.PathLike) instance or a tuple of `(filename, contents, media type)`. ```python from pathlib import Path from imagekitio import ImageKit -import io client = ImageKit() -# Method 1: Upload from bytes -# Read file into memory first, then upload -with open("/path/to/your/image.jpg", "rb") as f: - file_data = f.read() - -response = client.files.upload( - file=file_data, - file_name="uploaded-image.jpg", -) - -# Method 2: Upload from file stream (for large files) -# Pass file object directly - SDK reads it -with open("/path/to/your/image.jpg", "rb") as file_stream: - response = client.files.upload( - file=file_stream, - file_name="uploaded-image.jpg", - ) - -# Method 3: Upload using Path object (SDK reads automatically) -response = client.files.upload( - file=Path("/path/to/file.jpg"), - file_name="fileName.jpg", -) - -# Method 4: Upload from BytesIO (for programmatically generated content) -content = b"your binary data" -bytes_io = io.BytesIO(content) -response = client.files.upload( - file=bytes_io, - file_name="binary-upload.jpg", -) - -# Method 5: Upload with custom content type using tuple format -image_data = b"your binary data" -response = client.files.upload( - file=("custom.jpg", image_data, "image/jpeg"), - file_name="custom-upload.jpg", +client.files.upload( + file=Path("/path/to/file"), + file_name="fileName", ) ``` The async client uses the exact same interface. If you pass a [`PathLike`](https://docs.python.org/3/library/os.html#os.PathLike) instance, the file contents will be read asynchronously automatically. -**Note:** URL strings (e.g., `"https://example.com/image.jpg"`) are not supported by the Python SDK. To upload from a URL, download the content first: - -```python -import urllib.request - -# Download from URL and upload to ImageKit -url = "https://example.com/image.jpg" -with urllib.request.urlopen(url) as response: - url_content = response.read() - -# Upload the downloaded content -upload_response = client.files.upload( - file=url_content, - file_name="downloaded-image.jpg", -) -``` - -### Handling errors +## Handling errors When the library is unable to connect to the API (for example, due to network connection problems or a timeout), a subclass of `imagekitio.APIConnectionError` is raised. @@ -583,12 +196,8 @@ from imagekitio import ImageKit client = ImageKit() try: - # Read file into memory and upload - with open("/path/to/your/image.jpg", "rb") as f: - file_data = f.read() - - response = client.files.upload( - file=file_data, + client.files.upload( + file=b"https://www.example.com/public-url.jpg", file_name="file-name.jpg", ) except imagekitio.APIConnectionError as e: @@ -633,11 +242,8 @@ client = ImageKit( ) # Or, configure per-request: -with open("/path/to/your/image.jpg", "rb") as f: - file_data = f.read() - client.with_options(max_retries=5).files.upload( - file=file_data, + file=b"https://www.example.com/public-url.jpg", file_name="file-name.jpg", ) ``` @@ -662,11 +268,8 @@ client = ImageKit( ) # Override per-request: -with open("/path/to/your/image.jpg", "rb") as f: - file_data = f.read() - client.with_options(timeout=5.0).files.upload( - file=file_data, + file=b"https://www.example.com/public-url.jpg", file_name="file-name.jpg", ) ``` @@ -675,6 +278,8 @@ On timeout, an `APITimeoutError` is thrown. Note that requests that time out are [retried twice by default](#retries). +## Advanced + ### Logging We use the standard library [`logging`](https://docs.python.org/3/library/logging.html) module. @@ -707,24 +312,19 @@ The "raw" Response object can be accessed by prefixing `.with_raw_response.` to from imagekitio import ImageKit client = ImageKit() - -# Read file into memory and upload -with open("/path/to/your/image.jpg", "rb") as f: - file_data = f.read() - response = client.files.with_raw_response.upload( - file=file_data, + file=b"https://www.example.com/public-url.jpg", file_name="file-name.jpg", ) print(response.headers.get('X-My-Header')) file = response.parse() # get the object that `files.upload()` would have returned -print(file.file_id) +print(file.video_codec) ``` -These methods return an [`APIResponse`](https://github.com/imagekit-developer/imagekit-python/tree/main/src/imagekitio/_response.py) object. +These methods return an [`APIResponse`](https://github.com/imagekit-developer/imagekit-python/tree/master/src/imagekitio/_response.py) object. -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. +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. #### `.with_streaming_response` @@ -733,12 +333,8 @@ The above interface eagerly reads the full response body when you make the reque To stream the response body, use `.with_streaming_response` instead, which requires a context manager and only reads the response body once you call `.read()`, `.text()`, `.json()`, `.iter_bytes()`, `.iter_text()`, `.iter_lines()` or `.parse()`. In the async client, these are async methods. ```python -# Read file into memory and upload -with open("/path/to/your/image.jpg", "rb") as f: - file_data = f.read() - with client.files.with_streaming_response.upload( - file=file_data, + file=b"https://www.example.com/public-url.jpg", file_name="file-name.jpg", ) as response: print(response.headers.get("X-My-Header")) diff --git a/api.md b/api.md index bc986796..6b68aa25 100644 --- a/api.md +++ b/api.md @@ -7,7 +7,6 @@ from imagekitio.types import ( Extensions, GetImageAttributesOptions, ImageOverlay, - NamedTransformation, Overlay, OverlayPosition, OverlayTiming, @@ -27,6 +26,12 @@ from imagekitio.types import ( ) ``` +# Dummy + +Methods: + +- client.dummy.create(\*\*params) -> None + # CustomMetadataFields Types: @@ -132,22 +137,6 @@ 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: @@ -223,18 +212,6 @@ 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/bin/check-release-environment b/bin/check-release-environment index 98d98fbc..b845b0f4 100644 --- a/bin/check-release-environment +++ b/bin/check-release-environment @@ -2,10 +2,6 @@ errors=() -if [ -z "${RELEASE_PLEASE_TOKEN}" ]; then - errors+=("The RELEASE_PLEASE_TOKEN secret has not been set. Create a fine-grained GitHub PAT and add it as a repository secret.") -fi - if [ -z "${PYPI_TOKEN}" ]; then errors+=("The PYPI_TOKEN secret has not been set. Please set it in either this repository's secrets or your organization secrets.") fi diff --git a/pyproject.toml b/pyproject.toml index d02aac87..d21d0e6c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "imagekitio" -version = "5.9.0" +version = "5.7.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/main/\g<2>)' +replacement = '[\1](https://github.com/imagekit-developer/imagekit-python/tree/master/\g<2>)' [tool.pytest.ini_options] testpaths = ["tests"] @@ -169,16 +169,7 @@ 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. -# -# 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", -] +exclude = ["src/imagekitio/_files.py", "_dev/.*.py", "tests/.*"] strict_equality = true implicit_reexport = true @@ -219,16 +210,6 @@ 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/release-please-config.json b/release-please-config.json index d93f3fea..cd36a977 100644 --- a/release-please-config.json +++ b/release-please-config.json @@ -2,11 +2,11 @@ "packages": { ".": {} }, - "$schema": "https://raw.githubusercontent.com/googleapis/release-please/main/schemas/config.json", + "$schema": "https://raw.githubusercontent.com/stainless-api/release-please/main/schemas/config.json", "include-v-in-tag": true, "include-component-in-tag": false, "versioning": "prerelease", - "prerelease": false, + "prerelease": true, "bump-minor-pre-major": true, "bump-patch-for-minor-pre-major": false, "pull-request-header": "Automated Release PR", diff --git a/src/imagekit/lib/.keep b/src/imagekit/lib/.keep new file mode 100644 index 00000000..5e2c99fd --- /dev/null +++ b/src/imagekit/lib/.keep @@ -0,0 +1,4 @@ +File generated from our OpenAPI spec by Stainless. + +This directory can be used to store custom files to expand the SDK. +It is ignored by Stainless code generation and its content (other than this keep file) won't be touched. \ No newline at end of file diff --git a/src/imagekitio/_client.py b/src/imagekitio/_client.py index 7198d7ca..fdc5dfa4 100644 --- a/src/imagekitio/_client.py +++ b/src/imagekitio/_client.py @@ -46,10 +46,8 @@ folders, accounts, saved_extensions, - named_transformations, custom_metadata_fields, ) - from .lib.helper import HelperResource, AsyncHelperResource from .resources.dummy import DummyResource, AsyncDummyResource from .resources.assets import AssetsResource, AsyncAssetsResource from .resources.webhooks import WebhooksResource, AsyncWebhooksResource @@ -59,7 +57,6 @@ 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__ = [ @@ -178,12 +175,6 @@ 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 @@ -220,12 +211,6 @@ def webhooks(self) -> WebhooksResource: return WebhooksResource(self) - @cached_property - def helper(self) -> HelperResource: - from .lib.helper import HelperResource - - return HelperResource(self) - @cached_property def with_raw_response(self) -> ImageKitWithRawResponse: return ImageKitWithRawResponse(self) @@ -461,12 +446,6 @@ 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 @@ -503,12 +482,6 @@ def webhooks(self) -> AsyncWebhooksResource: return AsyncWebhooksResource(self) - @cached_property - def helper(self) -> AsyncHelperResource: - from .lib.helper import AsyncHelperResource - - return AsyncHelperResource(self) - @cached_property def with_raw_response(self) -> AsyncImageKitWithRawResponse: return AsyncImageKitWithRawResponse(self) @@ -670,12 +643,6 @@ 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 @@ -737,12 +704,6 @@ 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 @@ -804,12 +765,6 @@ 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 @@ -871,12 +826,6 @@ 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 843f87de..36332505 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/main#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/master#file-uploads" ) from None diff --git a/src/imagekitio/_version.py b/src/imagekitio/_version.py index 9d4c9bca..f86b8e12 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.9.0" # x-release-please-version +__version__ = "5.7.0" # x-release-please-version diff --git a/src/imagekitio/lib/__init__.py b/src/imagekitio/lib/__init__.py deleted file mode 100644 index 5ba9d0db..00000000 --- a/src/imagekitio/lib/__init__.py +++ /dev/null @@ -1,11 +0,0 @@ -# Custom helper functions - not generated from OpenAPI spec - -from .helper import ( - HelperResource, - AsyncHelperResource, -) - -__all__ = [ - "HelperResource", - "AsyncHelperResource", -] diff --git a/src/imagekitio/lib/helper.py b/src/imagekitio/lib/helper.py deleted file mode 100644 index 7ca9660b..00000000 --- a/src/imagekitio/lib/helper.py +++ /dev/null @@ -1,828 +0,0 @@ -# File manually created for helper functions - not generated from OpenAPI spec - -from __future__ import annotations - -import re -import hmac -import time -import uuid -import base64 -import hashlib -from typing import Any, Dict, List, Union, Iterable, Optional, Sequence, cast -from urllib.parse import quote, parse_qs, urlparse, urlunparse -from typing_extensions import Unpack - -from .._resource import SyncAPIResource, AsyncAPIResource -from ..types.shared_params.overlay import Overlay -from ..types.shared_params.src_options import SrcOptions -from ..types.shared_params.transformation import Transformation -from ..types.shared_params.text_overlay_transformation import TextOverlayTransformation -from ..types.shared_params.subtitle_overlay_transformation import SubtitleOverlayTransformation -from ..types.shared_params.solid_color_overlay_transformation import SolidColorOverlayTransformation - -# Type alias for any transformation type (main or overlay-specific) -AnyTransformation = Union[ - Transformation, TextOverlayTransformation, SubtitleOverlayTransformation, SolidColorOverlayTransformation -] - -__all__ = ["HelperResource", "AsyncHelperResource"] - -# Constants -TRANSFORMATION_PARAMETER = "tr" -SIGNATURE_PARAMETER = "ik-s" -TIMESTAMP_PARAMETER = "ik-t" -DEFAULT_TIMESTAMP = 9999999999 -SIMPLE_OVERLAY_PATH_REGEX = re.compile(r"^[a-zA-Z0-9-._/ ]*$") -SIMPLE_OVERLAY_TEXT_REGEX = re.compile(r"^[a-zA-Z0-9-._ ]*$") - -# Transformation key mapping -SUPPORTED_TRANSFORMS = { - # Basic sizing & layout - "width": "w", - "height": "h", - "aspect_ratio": "ar", - "background": "bg", - "border": "b", - "crop": "c", - "crop_mode": "cm", - "dpr": "dpr", - "focus": "fo", - "quality": "q", - "x": "x", - "x_center": "xc", - "y": "y", - "y_center": "yc", - "format": "f", - "video_codec": "vc", - "audio_codec": "ac", - "radius": "r", - "rotation": "rt", - "blur": "bl", - "named": "n", - "default_image": "di", - "flip": "fl", - "original": "orig", - "start_offset": "so", - "end_offset": "eo", - "duration": "du", - "streaming_resolutions": "sr", - # AI & advanced effects - "grayscale": "e-grayscale", - "ai_upscale": "e-upscale", - "ai_retouch": "e-retouch", - "ai_variation": "e-genvar", - "ai_drop_shadow": "e-dropshadow", - "ai_change_background": "e-changebg", - "ai_remove_background": "e-bgremove", - "ai_remove_background_external": "e-removedotbg", - "ai_edit": "e-edit", - "contrast_stretch": "e-contrast", - "shadow": "e-shadow", - "sharpen": "e-sharpen", - "unsharp_mask": "e-usm", - "gradient": "e-gradient", - "color_replace": "cr", - "colorize": "e-colorize", - "distort": "e-distort", - # Other flags & finishing - "progressive": "pr", - "lossless": "lo", - "color_profile": "cp", - "metadata": "md", - "opacity": "o", - "trim": "t", - "zoom": "z", - "page": "pg", - # Text overlay transformations - "font_size": "fs", - "font_family": "ff", - "font_color": "co", - "inner_alignment": "ia", - "padding": "pa", - "alpha": "al", - "typography": "tg", - "line_height": "lh", - # Subtitles transformations - "font_outline": "fol", - "font_shadow": "fsh", - "color": "co", - # Raw pass-through - "raw": "raw", -} - -CHAIN_TRANSFORM_DELIMITER = ":" -TRANSFORM_DELIMITER = "," -TRANSFORM_KEY_VALUE_DELIMITER = "-" - -# RFC 3986 section 3.3 defines 'pchar' (path characters) that are safe to use unencoded: -# pchar = unreserved / pct-encoded / sub-delims / ":" / "@" -# unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~" -# sub-delims = "!" / "$" / "&" / "'" / "(" / ")" / "*" / "+" / "," / ";" / "=" -# This matches what Node.js URL.pathname uses and ensures compatibility across SDKs -RFC3986_PATH_SAFE_CHARS = "/:@!$&'()*+,;=-._~" - - -def _get_transform_key(transform: str) -> str: - """Get the short transformation key from the long form.""" - if not transform: - return "" - return SUPPORTED_TRANSFORMS.get(transform, transform) - - -def _add_trailing_slash(s: str) -> str: - """Add trailing slash if not present.""" - if s and not s.endswith("/"): - return s + "/" - return s - - -def _remove_trailing_slash(s: str) -> str: - """Remove trailing slash if present.""" - if s and s.endswith("/"): - return s[:-1] - return s - - -def _remove_leading_slash(s: str) -> str: - """Remove leading slash if present.""" - if s and s.startswith("/"): - return s[1:] - return s - - -def _format_number(value: Any) -> str: - """ - Format a numeric value as a string, removing unnecessary decimal points. - - Examples: - 5.0 -> "5" - 5.5 -> "5.5" - 5 -> "5" - "5" -> "5" - """ - if isinstance(value, (int, float)): - # Check if it's a whole number - if isinstance(value, float) and value.is_integer(): - return str(int(value)) - return str(value) - return str(value) - - -def _path_join(parts: List[str], sep: str = "/") -> str: - """Join path parts, handling slashes correctly.""" - cleaned_parts: List[str] = [] - for part in parts: - if part: - # Remove leading and trailing slashes from parts - cleaned_part = part.strip("/") - if cleaned_part: - cleaned_parts.append(cleaned_part) - return sep + sep.join(cleaned_parts) if cleaned_parts else "" - - -def _safe_btoa(s: str) -> str: - """ - Base64 encode a string and then URL-encode it. - This matches Node.js behavior: safeBtoa() + encodeURIComponent(). - - In Node.js: - - encodeURIComponent() encodes: / as %2F, + as %2B, = as %3D - - Python's quote() with default safe='/' doesn't encode / - - So we need to explicitly set safe='' to encode everything - """ - encoded = base64.b64encode(s.encode("utf-8")).decode("utf-8") - # URL encode the entire base64 string (/, +, =, etc.) - # quote() with safe='' will encode all special characters to match encodeURIComponent - return quote(encoded, safe="") - - -def _process_input_path(s: str, encoding: str) -> str: - """ - Process input path for overlays. - Returns the full parameter string including the i- or ie- prefix. - """ - if not s: - return "" - - # Remove leading and trailing slashes - s = _remove_trailing_slash(_remove_leading_slash(s)) - - if encoding == "plain": - return f"i-{s.replace('/', '@@')}" - - if encoding == "base64": - # safeBtoa already encodes = as %3D, no need for further encoding - return f"ie-{_safe_btoa(s)}" - - # Auto encoding: use plain for simple paths, base64 for special characters - if SIMPLE_OVERLAY_PATH_REGEX.match(s): - return f"i-{s.replace('/', '@@')}" - else: - # safeBtoa already encodes = as %3D, no need for further encoding - return f"ie-{_safe_btoa(s)}" - - -def _process_text(s: str, encoding: str) -> str: - """ - Process text for overlays. - Returns the full parameter string including the i- or ie- prefix. - """ - if not s: - return "" - - if encoding == "plain": - return f"i-{quote(s, safe='')}" - - if encoding == "base64": - # safeBtoa already encodes = as %3D, no need for further encoding - return f"ie-{_safe_btoa(s)}" - - # Auto encoding: use plain for simple text, base64 for special characters - if SIMPLE_OVERLAY_TEXT_REGEX.match(s): - return f"i-{quote(s, safe='')}" - - # safeBtoa already encodes = as %3D, no need for further encoding - return f"ie-{_safe_btoa(s)}" - - -def _process_overlay(overlay: Overlay) -> str: - """Process overlay transformations.""" - # 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", {})) - timing: Dict[str, Any] = cast(Dict[str, Any], overlay.get("timing", {})) - transformation: List[Any] = cast(List[Any], overlay.get("transformation", [])) - - if not overlay_type: - return "" - - parsed_overlay: List[str] = [] - - if overlay_type == "text": - text: str = cast(str, overlay.get("text", "")) - if not text: - return "" - - encoding: str = cast(str, overlay.get("encoding", "auto")) - parsed_overlay.append("l-text") - - # Process the text - returns full string with i- or ie- prefix - parsed_overlay.append(_process_text(text, encoding)) - - elif overlay_type == "image": - parsed_overlay.append("l-image") - - input_val: str = cast(str, overlay.get("input", "")) - if not input_val: - return "" - - img_encoding = cast(str, overlay.get("encoding", "auto")) - - # Process the input path - returns full string with i- or ie- prefix - parsed_overlay.append(_process_input_path(input_val, img_encoding)) - - elif overlay_type == "video": - parsed_overlay.append("l-video") - - video_input = cast(str, overlay.get("input", "")) - if not video_input: - return "" - - video_encoding = cast(str, overlay.get("encoding", "auto")) - - # Process the input path - returns full string with i- or ie- prefix - parsed_overlay.append(_process_input_path(video_input, video_encoding)) - - elif overlay_type == "subtitle": - parsed_overlay.append("l-subtitles") - - subtitle_input = cast(str, overlay.get("input", "")) - if not subtitle_input: - return "" - - subtitle_encoding = cast(str, overlay.get("encoding", "auto")) - - # Process the input path - returns full string with i- or ie- prefix - parsed_overlay.append(_process_input_path(subtitle_input, subtitle_encoding)) - - elif overlay_type == "solidColor": - parsed_overlay.append("l-image") - parsed_overlay.append("i-ik_canvas") - - color: str = cast(str, overlay.get("color", "")) - if not color: - return "" - - parsed_overlay.append(f"bg-{color}") - - # Handle layerMode and layer_mode (both camelCase and snake_case) - layer_mode = overlay.get("layerMode") or overlay.get("layer_mode") - if layer_mode: - parsed_overlay.append(f"lm-{layer_mode}") - - # Handle position properties (x, y, focus, x_center, y_center, anchor_point) - # Node.js uses if (x) which skips falsy values like 0, '', false, null, undefined - x = position.get("x") - if x: - parsed_overlay.append(f"lx-{x}") - - y = position.get("y") - if y: - parsed_overlay.append(f"ly-{y}") - - # Handle xCenter and x_center (both snake_case and camelCase) - x_center = position.get("xCenter") or position.get("x_center") - if x_center: - parsed_overlay.append(f"lxc-{x_center}") - - # Handle yCenter and y_center (both snake_case and camelCase) - y_center = position.get("yCenter") or position.get("y_center") - if y_center: - parsed_overlay.append(f"lyc-{y_center}") - - # Handle anchorPoint and anchor_point (both snake_case and camelCase) - anchor_point = position.get("anchorPoint") or position.get("anchor_point") - if anchor_point: - parsed_overlay.append(f"lap-{anchor_point}") - - focus = position.get("focus") - if focus: - parsed_overlay.append(f"lfo-{focus}") - - # Handle timing properties (start, end, duration) - # Node.js uses if (start) which skips falsy values - start = timing.get("start") - if start: - parsed_overlay.append(f"lso-{_format_number(start)}") - - end = timing.get("end") - if end: - parsed_overlay.append(f"leo-{_format_number(end)}") - - duration = timing.get("duration") - if duration: - parsed_overlay.append(f"ldu-{duration}") - - # Handle nested transformations for image/video overlays - if transformation: - transformation_string: str = _build_transformation_string(transformation) - if transformation_string and transformation_string.strip(): - parsed_overlay.append(transformation_string) - - # Close overlay - parsed_overlay.append("l-end") - - return TRANSFORM_DELIMITER.join(parsed_overlay) - - -def _build_transformation_string(transformation: Optional[Sequence[AnyTransformation]]) -> str: - """Build transformation string from transformation objects.""" - if not transformation: - return "" - - parsed_transforms: List[str] = [] - - for current_transform in transformation: - if not current_transform: - continue - - parsed_transform_step: List[str] = [] - - for key, value in current_transform.items(): - if value is None: - continue - - # Handle overlay separately - if key == "overlay" and isinstance(value, dict): - raw_string: str = _process_overlay(cast(Overlay, value)) - if raw_string and raw_string.strip(): - parsed_transform_step.append(raw_string) - continue - - # Get the transformation key - transform_key: str = _get_transform_key(key) - if not transform_key: - transform_key = key - - if not transform_key: - continue - - # Handle boolean transformations that should only output key - if transform_key in [ - "e-grayscale", - "e-contrast", - "e-removedotbg", - "e-bgremove", - "e-upscale", - "e-retouch", - "e-genvar", - ]: - if value is True or value == "-" or value == "true": - parsed_transform_step.append(transform_key) - # Any other value means that the effect should not be applied - continue - - # Handle transformations that can be true or have values - if transform_key in ["e-sharpen", "e-shadow", "e-gradient", "e-usm", "e-dropshadow"] and ( - str(value).strip() == "" or value is True or value == "true" - ): - parsed_transform_step.append(transform_key) - continue - - # Handle raw transformation - if key == "raw": - if isinstance(value, str) and value.strip(): - parsed_transform_step.append(value) - continue - - # Handle default_image and font_family - replace slashes - if transform_key in ["di", "ff"]: - value = _remove_trailing_slash(_remove_leading_slash(str(value) if value else "")) - value = value.replace("/", "@@") - - # Handle streaming_resolutions array - if transform_key == "sr" and isinstance(value, list): - value = "_".join(str(v) for v in cast(List[Any], value)) - - # Special case for trim with empty string - if transform_key == "t" and str(value).strip() == "": - value = "true" - - # Skip false values - if value is False: - continue - - # Skip empty strings (except for special keys that allow empty values) - if isinstance(value, str) and value.strip() == "": - continue - - # Convert boolean True to lowercase "true" - if value is True: - value = "true" - - # Format numeric values to avoid unnecessary .0 for integers - if isinstance(value, (int, float)): - value = _format_number(value) - - # Add the transformation - parsed_transform_step.append(f"{transform_key}{TRANSFORM_KEY_VALUE_DELIMITER}{value}") - - if parsed_transform_step: - parsed_transforms.append(TRANSFORM_DELIMITER.join(parsed_transform_step)) - - return CHAIN_TRANSFORM_DELIMITER.join(parsed_transforms) - - -def _get_signature_timestamp(seconds: Optional[float]) -> int: - """Calculate expiry timestamp for URL signing.""" - if not seconds or seconds <= 0: - return DEFAULT_TIMESTAMP - - # Try to parse as int, return DEFAULT_TIMESTAMP if invalid - try: - sec = int(seconds) - if sec <= 0: - return DEFAULT_TIMESTAMP - except (ValueError, TypeError): - return DEFAULT_TIMESTAMP - - return int(time.time()) + sec - - -def _get_signature(private_key: str, url: str, url_endpoint: str, expiry_timestamp: int) -> str: - """Generate HMAC-SHA1 signature for URL signing.""" - if not private_key or not url or not url_endpoint: - return "" - - # Create the string to sign: relative path + expiry timestamp - # This matches Node.js: url.replace(addTrailingSlash(urlEndpoint), '') + String(expiryTimestamp) - url_endpoint_with_slash = _add_trailing_slash(url_endpoint) - string_to_sign = url.replace(url_endpoint_with_slash, "") + str(expiry_timestamp) - - # Generate HMAC-SHA1 signature - signature = hmac.new(private_key.encode("utf-8"), string_to_sign.encode("utf-8"), hashlib.sha1).hexdigest() - - return signature - - -def _get_authentication_parameters(token: str, expire: int, private_key: str) -> Dict[str, Any]: - """Generate authentication parameters for uploads.""" - auth_parameters = { - "token": token, - "expire": expire, - "signature": "", - } - - signature = hmac.new(private_key.encode("utf-8"), f"{token}{expire}".encode("utf-8"), hashlib.sha1).hexdigest() - - auth_parameters["signature"] = signature - return auth_parameters - - -def _build_url( - src: str, - url_endpoint: str, - transformation_position: str, - transformation: Any, - query_parameters: Dict[str, Any], - signed: bool, - expires_in: Optional[float], - private_key: str, -) -> str: - """ - Internal implementation of build_url. - - Args: - src: Accepts a relative or absolute path of the resource. - url_endpoint: Get your urlEndpoint from the ImageKit dashboard. - transformation_position: By default, the transformation string is added as a query parameter. - transformation: An array of objects specifying the transformations to be applied in the URL. - query_parameters: Additional query parameters to add to the final URL. - signed: Whether to sign the URL or not. - expires_in: When you want the signed URL to expire, specified in seconds. - private_key: Private key for signing URLs. - - Returns: - The constructed source URL. - """ - if not src: - return "" - - # Check if src is absolute URL - is_absolute_url = src.startswith("http://") or src.startswith("https://") - - # Track if src parameter is used for URL (matches Node.js isSrcParameterUsedForURL) - is_src_parameter_used_for_url = False - - # Parse URL - try: - if not is_absolute_url: - parsed_url = urlparse(url_endpoint) - else: - parsed_url = urlparse(src) - is_src_parameter_used_for_url = True - except Exception: - return "" - - # Build query parameters - query_dict_raw = dict(parse_qs(parsed_url.query)) - # Flatten lists from parse_qs - query_dict: Dict[str, str] = {k: v[0] if len(v) == 1 else ",".join(v) for k, v in query_dict_raw.items()} - - # Add additional query parameters - convert values to strings like Node.js does - if query_parameters: - for k, v in query_parameters.items(): - query_dict[k] = str(v) - - # Build transformation string - transformation_string = _build_transformation_string(transformation) - - # Determine if transformation should be in query or path - # Matches Node.js: addAsQuery = transformationUtils.addAsQueryParameter(opts) || isSrcParameterUsedForURL - add_as_query = transformation_position == "query" or is_src_parameter_used_for_url - - # Placeholder for transformation to avoid URL encoding issues - TRANSFORMATION_PLACEHOLDER = "PLEASEREPLACEJUSTBEFORESIGN" - - # Build the path - if not is_absolute_url: - # For relative URLs - endpoint_path = urlparse(url_endpoint).path - path_parts = [endpoint_path] if endpoint_path else [] - - # Add transformation in path if needed - if transformation_string and not add_as_query: - path_parts.append(f"{TRANSFORMATION_PARAMETER}{CHAIN_TRANSFORM_DELIMITER}{TRANSFORMATION_PLACEHOLDER}") - - # Add src path with RFC 3986 compliant encoding - # Python's urlunparse() doesn't auto-encode Unicode like Node.js URL does, - # so we must manually encode the path while preserving RFC 3986 safe chars - encoded_src = quote(src, safe=RFC3986_PATH_SAFE_CHARS) - path_parts.append(encoded_src) - - path = _path_join(path_parts) - else: - path = parsed_url.path - - # Add transformation to query if needed - if transformation_string and add_as_query: - query_dict[TRANSFORMATION_PARAMETER] = TRANSFORMATION_PLACEHOLDER - - # Build the URL - scheme = parsed_url.scheme or "https" - netloc = parsed_url.netloc if is_absolute_url else urlparse(url_endpoint).netloc - - # Build query string manually to avoid encoding transformation string - query_string = "" - if query_dict: - query_parts: List[str] = [] - for k, v in query_dict.items(): - query_parts.append(f"{k}={v}") - query_string = "&".join(query_parts) - - final_url = urlunparse((scheme, netloc, path, "", query_string, "")) - - # Replace placeholder with actual transformation string - if transformation_string: - final_url = final_url.replace(TRANSFORMATION_PLACEHOLDER, transformation_string) - - # Sign URL if needed - if signed or (expires_in and expires_in > 0): - expiry_timestamp = _get_signature_timestamp(expires_in) - - url_signature = _get_signature( - private_key=private_key, url=final_url, url_endpoint=url_endpoint, expiry_timestamp=expiry_timestamp - ) - - # Add signature parameters - parsed_final = urlparse(final_url) - has_existing_params = bool(parsed_final.query) - separator = "&" if has_existing_params else "?" - - if expiry_timestamp and expiry_timestamp != DEFAULT_TIMESTAMP: - final_url += f"{separator}{TIMESTAMP_PARAMETER}={expiry_timestamp}" - final_url += f"&{SIGNATURE_PARAMETER}={url_signature}" - else: - final_url += f"{separator}{SIGNATURE_PARAMETER}={url_signature}" - - return final_url - - -def _get_authentication_parameters_with_defaults( - token: Optional[str], expire: Optional[int], private_key: str -) -> Dict[str, Any]: - """ - Internal implementation of get_authentication_parameters with default value handling. - - Args: - token: Custom token for the upload session. If not provided, a UUID v4 will be generated automatically. - expire: Expiration time in seconds from now. If not provided, defaults to 1800 seconds (30 minutes). - private_key: Private key for generating authentication parameters. - - Returns: - Authentication parameters object containing token, expire, and signature. - """ - if not private_key: - raise ValueError("Private key is required for generating authentication parameters") - - # Generate token if not provided - if not token: - token = str(uuid.uuid4()) - - # Set default expiry if not provided - if expire is None: - expire = int(time.time()) + 1800 # 30 minutes default - - return _get_authentication_parameters(token, expire, private_key) - - -class HelperResource(SyncAPIResource): - """ - Helper resource for additional utility functions like URL building and authentication. - """ - - def build_url(self, **options: Unpack[SrcOptions]) -> str: - """ - Builds a source URL with the given options. - - Args: - src: Accepts a relative or absolute path of the resource. If a relative path is provided, - it is appended to the `url_endpoint`. If an absolute path is provided, `url_endpoint` is ignored. - url_endpoint: Get your urlEndpoint from the ImageKit dashboard. - transformation: An array of objects specifying the transformations to be applied in the URL. - transformation_position: By default, the transformation string is added as a query parameter. - Set to `path` to add it in the URL path instead. - signed: Whether to sign the URL or not. Set to `true` to generate a signed URL. - expires_in: When you want the signed URL to expire, specified in seconds. - query_parameters: Additional query parameters to add to the final URL. - - Returns: - The constructed source URL. - """ - return _build_url( - src=options.get("src", ""), - url_endpoint=options.get("url_endpoint", ""), - transformation_position=options.get("transformation_position", "query"), - transformation=options.get("transformation"), - query_parameters=options.get("query_parameters", {}), - signed=options.get("signed", False), - expires_in=options.get("expires_in"), - private_key=self._client.private_key, - ) - - def get_authentication_parameters( - self, - token: Optional[str] = None, - expire: Optional[int] = None, - ) -> Dict[str, Any]: - """ - Generates authentication parameters for client-side file uploads using ImageKit's Upload API. - - Args: - token: Custom token for the upload session. If not provided, a UUID v4 will be generated automatically. - expire: Expiration time in seconds from now. If not provided, defaults to 1800 seconds (30 minutes). - - Returns: - Authentication parameters object containing: - - token: Unique identifier for this upload session - - expire: Unix timestamp when these parameters expire - - signature: HMAC-SHA1 signature for authenticating the upload - """ - return _get_authentication_parameters_with_defaults( - token=token, expire=expire, private_key=self._client.private_key - ) - - def build_transformation_string(self, transformation: Optional[Iterable[Transformation]] = None) -> str: - """ - Builds a transformation string from an array of transformation objects. - - Args: - transformation: List of transformation dictionaries. - - Returns: - The transformation string in ImageKit format. - """ - if transformation is None: - return "" - - # Convert to list if it's an iterable - if not isinstance(transformation, list): - transformation = list(transformation) - - return _build_transformation_string(transformation) - - -class AsyncHelperResource(AsyncAPIResource): - """ - Async version of helper resource for additional utility functions. - """ - - async def build_url(self, **options: Unpack[SrcOptions]) -> str: - """ - Async version of build_url. - - Args: - src: Accepts a relative or absolute path of the resource. If a relative path is provided, - it is appended to the `url_endpoint`. If an absolute path is provided, `url_endpoint` is ignored. - url_endpoint: Get your urlEndpoint from the ImageKit dashboard. - transformation: An array of objects specifying the transformations to be applied in the URL. - transformation_position: By default, the transformation string is added as a query parameter. - Set to `path` to add it in the URL path instead. - signed: Whether to sign the URL or not. Set to `true` to generate a signed URL. - expires_in: When you want the signed URL to expire, specified in seconds. - query_parameters: Additional query parameters to add to the final URL. - - Returns: - The constructed source URL. - """ - return _build_url( - src=options.get("src", ""), - url_endpoint=options.get("url_endpoint", ""), - transformation_position=options.get("transformation_position", "query"), - transformation=options.get("transformation"), - query_parameters=options.get("query_parameters", {}), - signed=options.get("signed", False), - expires_in=options.get("expires_in"), - private_key=self._client.private_key, - ) - - async def get_authentication_parameters( - self, - token: Optional[str] = None, - expire: Optional[int] = None, - ) -> Dict[str, Any]: - """ - Async version of get_authentication_parameters. - - Args: - token: Custom token for the upload session. If not provided, a UUID v4 will be generated automatically. - expire: Expiration time in seconds from now. If not provided, defaults to 1800 seconds (30 minutes). - - Returns: - Authentication parameters object containing: - - token: Unique identifier for this upload session - - expire: Unix timestamp when these parameters expire - - signature: HMAC-SHA1 signature for authenticating the upload - """ - return _get_authentication_parameters_with_defaults( - token=token, expire=expire, private_key=self._client.private_key - ) - - async def build_transformation_string(self, transformation: Optional[Iterable[Transformation]] = None) -> str: - """ - Async version of build_transformation_string. - - Args: - transformation: List of transformation dictionaries. - - Returns: - The transformation string in ImageKit format. - """ - if transformation is None: - return "" - - # Convert to list if it's an iterable - if not isinstance(transformation, list): - transformation = list(transformation) - - return _build_transformation_string(transformation) diff --git a/src/imagekitio/lib/serialization_utils.py b/src/imagekitio/lib/serialization_utils.py deleted file mode 100644 index 4fe5a473..00000000 --- a/src/imagekitio/lib/serialization_utils.py +++ /dev/null @@ -1,47 +0,0 @@ -# Serialization utilities for upload options -# This file handles serialization of upload parameters before sending to ImageKit API - -import json -from typing import Any, Dict, Sequence, cast - - -def serialize_upload_options(upload_options: Dict[str, Any]) -> Dict[str, Any]: - """ - Serialize upload options to handle proper formatting for ImageKit backend API. - - Special cases handled: - - tags: converted to comma-separated string - - response_fields: converted to comma-separated string - - extensions: JSON stringified - - custom_metadata: JSON stringified - - transformation: JSON stringified - - Args: - upload_options: Dictionary containing upload parameters - - Returns: - Dictionary with serialized values - """ - serialized: Dict[str, Any] = {**upload_options} - - for key in list(serialized.keys()): - if key and serialized[key] is not None: - value = serialized[key] - - if key == "tags" and isinstance(value, (list, tuple)): - # Tags should be comma-separated string - serialized[key] = ",".join(cast(Sequence[str], value)) - elif key == "response_fields" and isinstance(value, (list, tuple)): - # Response fields should be comma-separated string - serialized[key] = ",".join(cast(Sequence[str], value)) - elif key == "extensions" and isinstance(value, list): - # Extensions should be JSON stringified - serialized[key] = json.dumps(value) - elif key == "custom_metadata" and isinstance(value, dict): - # Custom metadata should be JSON stringified - serialized[key] = json.dumps(value) - elif key == "transformation" and isinstance(value, dict): - # Transformation should be JSON stringified - serialized[key] = json.dumps(value) - - return serialized diff --git a/src/imagekitio/resources/__init__.py b/src/imagekitio/resources/__init__.py index cc245c5e..48c48648 100644 --- a/src/imagekitio/resources/__init__.py +++ b/src/imagekitio/resources/__init__.py @@ -57,10 +57,6 @@ AsyncAccountsResourceWithStreamingResponse, ) from .webhooks import WebhooksResource, AsyncWebhooksResource -from ..lib.helper import ( - HelperResource, - AsyncHelperResource, -) from .saved_extensions import ( SavedExtensionsResource, AsyncSavedExtensionsResource, @@ -69,14 +65,6 @@ SavedExtensionsResourceWithStreamingResponse, AsyncSavedExtensionsResourceWithStreamingResponse, ) -from .named_transformations import ( - NamedTransformationsResource, - AsyncNamedTransformationsResource, - NamedTransformationsResourceWithRawResponse, - AsyncNamedTransformationsResourceWithRawResponse, - NamedTransformationsResourceWithStreamingResponse, - AsyncNamedTransformationsResourceWithStreamingResponse, -) from .custom_metadata_fields import ( CustomMetadataFieldsResource, AsyncCustomMetadataFieldsResource, @@ -111,12 +99,6 @@ "AsyncSavedExtensionsResourceWithRawResponse", "SavedExtensionsResourceWithStreamingResponse", "AsyncSavedExtensionsResourceWithStreamingResponse", - "NamedTransformationsResource", - "AsyncNamedTransformationsResource", - "NamedTransformationsResourceWithRawResponse", - "AsyncNamedTransformationsResourceWithRawResponse", - "NamedTransformationsResourceWithStreamingResponse", - "AsyncNamedTransformationsResourceWithStreamingResponse", "AssetsResource", "AsyncAssetsResource", "AssetsResourceWithRawResponse", @@ -149,6 +131,4 @@ "AsyncBetaResourceWithStreamingResponse", "WebhooksResource", "AsyncWebhooksResource", - "HelperResource", - "AsyncHelperResource", ] diff --git a/src/imagekitio/resources/accounts/__init__.py b/src/imagekitio/resources/accounts/__init__.py index 1e10f739..fc56413d 100644 --- a/src/imagekitio/resources/accounts/__init__.py +++ b/src/imagekitio/resources/accounts/__init__.py @@ -32,14 +32,6 @@ URLEndpointsResourceWithStreamingResponse, AsyncURLEndpointsResourceWithStreamingResponse, ) -from .usage_analytics import ( - UsageAnalyticsResource, - AsyncUsageAnalyticsResource, - UsageAnalyticsResourceWithRawResponse, - AsyncUsageAnalyticsResourceWithRawResponse, - UsageAnalyticsResourceWithStreamingResponse, - AsyncUsageAnalyticsResourceWithStreamingResponse, -) __all__ = [ "UsageResource", @@ -48,12 +40,6 @@ "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 a15e3434..461e8cff 100644 --- a/src/imagekitio/resources/accounts/accounts.py +++ b/src/imagekitio/resources/accounts/accounts.py @@ -28,14 +28,6 @@ URLEndpointsResourceWithStreamingResponse, AsyncURLEndpointsResourceWithStreamingResponse, ) -from .usage_analytics import ( - UsageAnalyticsResource, - AsyncUsageAnalyticsResource, - UsageAnalyticsResourceWithRawResponse, - AsyncUsageAnalyticsResourceWithRawResponse, - UsageAnalyticsResourceWithStreamingResponse, - AsyncUsageAnalyticsResourceWithStreamingResponse, -) __all__ = ["AccountsResource", "AsyncAccountsResource"] @@ -45,10 +37,6 @@ 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) @@ -82,10 +70,6 @@ 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) @@ -122,10 +106,6 @@ 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) @@ -143,10 +123,6 @@ 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) @@ -164,10 +140,6 @@ 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) @@ -185,10 +157,6 @@ 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/origins.py b/src/imagekitio/resources/accounts/origins.py index c1fa446b..1bd4b50f 100644 --- a/src/imagekitio/resources/accounts/origins.py +++ b/src/imagekitio/resources/accounts/origins.py @@ -57,7 +57,6 @@ def create( base_url_for_canonical_header: str | Omit = omit, include_canonical_header: bool | Omit = omit, prefix: str | Omit = omit, - use_iam_role: 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, @@ -71,13 +70,13 @@ def create( Creates a new origin and returns the origin object. Args: - access_key: Access key for the bucket. When `useIAMRole` is `true`, send an empty string. + access_key: Access key for the bucket. bucket: S3 bucket name. name: Display name of the origin. - secret_key: Secret key for the bucket. When `useIAMRole` is `true`, send an empty string. + secret_key: Secret key for the bucket. base_url_for_canonical_header: URL used in the Canonical header (if enabled). @@ -85,9 +84,6 @@ def create( prefix: Path prefix inside the bucket. - use_iam_role: Use IAM role for authentication instead of access/secret keys. When set to - `true`, send an empty string for both `accessKey` and `secretKey`. - extra_headers: Send extra headers extra_query: Add additional query parameters to the request @@ -165,7 +161,6 @@ def create( base_url_for_canonical_header: str | Omit = omit, include_canonical_header: bool | Omit = omit, prefix: str | Omit = omit, - use_iam_role: 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, @@ -179,13 +174,13 @@ def create( Creates a new origin and returns the origin object. Args: - access_key: Access key for the bucket. When `useIAMRole` is `true`, send an empty string. + access_key: Access key for the bucket. bucket: S3 bucket name. name: Display name of the origin. - secret_key: Secret key for the bucket. When `useIAMRole` is `true`, send an empty string. + secret_key: Secret key for the bucket. base_url_for_canonical_header: URL used in the Canonical header (if enabled). @@ -193,9 +188,6 @@ def create( prefix: Path prefix inside the bucket. - use_iam_role: Use IAM role for authentication instead of access/secret keys. When set to - `true`, send an empty string for both `accessKey` and `secretKey`. - extra_headers: Send extra headers extra_query: Add additional query parameters to the request @@ -447,7 +439,6 @@ def create( base_url_for_canonical_header: str | Omit = omit, include_canonical_header: bool | Omit = omit, prefix: str | Omit = omit, - use_iam_role: bool | Omit = omit, endpoint: str | Omit = omit, s3_force_path_style: bool | Omit = omit, base_url: str | Omit = omit, @@ -482,7 +473,6 @@ def create( "base_url_for_canonical_header": base_url_for_canonical_header, "include_canonical_header": include_canonical_header, "prefix": prefix, - "use_iam_role": use_iam_role, "endpoint": endpoint, "s3_force_path_style": s3_force_path_style, "base_url": base_url, @@ -519,7 +509,6 @@ def update( base_url_for_canonical_header: str | Omit = omit, include_canonical_header: bool | Omit = omit, prefix: str | Omit = omit, - use_iam_role: 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, @@ -536,13 +525,13 @@ def update( id: Unique identifier for the origin. This is generated by ImageKit when you create a new origin. - access_key: Access key for the bucket. When `useIAMRole` is `true`, send an empty string. + access_key: Access key for the bucket. bucket: S3 bucket name. name: Display name of the origin. - secret_key: Secret key for the bucket. When `useIAMRole` is `true`, send an empty string. + secret_key: Secret key for the bucket. base_url_for_canonical_header: URL used in the Canonical header (if enabled). @@ -550,9 +539,6 @@ def update( prefix: Path prefix inside the bucket. - use_iam_role: Use IAM role for authentication instead of access/secret keys. When set to - `true`, send an empty string for both `accessKey` and `secretKey`. - extra_headers: Send extra headers extra_query: Add additional query parameters to the request @@ -635,7 +621,6 @@ def update( base_url_for_canonical_header: str | Omit = omit, include_canonical_header: bool | Omit = omit, prefix: str | Omit = omit, - use_iam_role: 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, @@ -652,13 +637,13 @@ def update( id: Unique identifier for the origin. This is generated by ImageKit when you create a new origin. - access_key: Access key for the bucket. When `useIAMRole` is `true`, send an empty string. + access_key: Access key for the bucket. bucket: S3 bucket name. name: Display name of the origin. - secret_key: Secret key for the bucket. When `useIAMRole` is `true`, send an empty string. + secret_key: Secret key for the bucket. base_url_for_canonical_header: URL used in the Canonical header (if enabled). @@ -666,9 +651,6 @@ def update( prefix: Path prefix inside the bucket. - use_iam_role: Use IAM role for authentication instead of access/secret keys. When set to - `true`, send an empty string for both `accessKey` and `secretKey`. - extra_headers: Send extra headers extra_query: Add additional query parameters to the request @@ -941,7 +923,6 @@ def update( base_url_for_canonical_header: str | Omit = omit, include_canonical_header: bool | Omit = omit, prefix: str | Omit = omit, - use_iam_role: bool | Omit = omit, endpoint: str | Omit = omit, s3_force_path_style: bool | Omit = omit, base_url: str | Omit = omit, @@ -978,7 +959,6 @@ def update( "base_url_for_canonical_header": base_url_for_canonical_header, "include_canonical_header": include_canonical_header, "prefix": prefix, - "use_iam_role": use_iam_role, "endpoint": endpoint, "s3_force_path_style": s3_force_path_style, "base_url": base_url, @@ -1139,7 +1119,6 @@ async def create( base_url_for_canonical_header: str | Omit = omit, include_canonical_header: bool | Omit = omit, prefix: str | Omit = omit, - use_iam_role: 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, @@ -1153,13 +1132,13 @@ async def create( Creates a new origin and returns the origin object. Args: - access_key: Access key for the bucket. When `useIAMRole` is `true`, send an empty string. + access_key: Access key for the bucket. bucket: S3 bucket name. name: Display name of the origin. - secret_key: Secret key for the bucket. When `useIAMRole` is `true`, send an empty string. + secret_key: Secret key for the bucket. base_url_for_canonical_header: URL used in the Canonical header (if enabled). @@ -1167,9 +1146,6 @@ async def create( prefix: Path prefix inside the bucket. - use_iam_role: Use IAM role for authentication instead of access/secret keys. When set to - `true`, send an empty string for both `accessKey` and `secretKey`. - extra_headers: Send extra headers extra_query: Add additional query parameters to the request @@ -1247,7 +1223,6 @@ async def create( base_url_for_canonical_header: str | Omit = omit, include_canonical_header: bool | Omit = omit, prefix: str | Omit = omit, - use_iam_role: 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, @@ -1261,13 +1236,13 @@ async def create( Creates a new origin and returns the origin object. Args: - access_key: Access key for the bucket. When `useIAMRole` is `true`, send an empty string. + access_key: Access key for the bucket. bucket: S3 bucket name. name: Display name of the origin. - secret_key: Secret key for the bucket. When `useIAMRole` is `true`, send an empty string. + secret_key: Secret key for the bucket. base_url_for_canonical_header: URL used in the Canonical header (if enabled). @@ -1275,9 +1250,6 @@ async def create( prefix: Path prefix inside the bucket. - use_iam_role: Use IAM role for authentication instead of access/secret keys. When set to - `true`, send an empty string for both `accessKey` and `secretKey`. - extra_headers: Send extra headers extra_query: Add additional query parameters to the request @@ -1529,7 +1501,6 @@ async def create( base_url_for_canonical_header: str | Omit = omit, include_canonical_header: bool | Omit = omit, prefix: str | Omit = omit, - use_iam_role: bool | Omit = omit, endpoint: str | Omit = omit, s3_force_path_style: bool | Omit = omit, base_url: str | Omit = omit, @@ -1564,7 +1535,6 @@ async def create( "base_url_for_canonical_header": base_url_for_canonical_header, "include_canonical_header": include_canonical_header, "prefix": prefix, - "use_iam_role": use_iam_role, "endpoint": endpoint, "s3_force_path_style": s3_force_path_style, "base_url": base_url, @@ -1601,7 +1571,6 @@ async def update( base_url_for_canonical_header: str | Omit = omit, include_canonical_header: bool | Omit = omit, prefix: str | Omit = omit, - use_iam_role: 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, @@ -1618,13 +1587,13 @@ async def update( id: Unique identifier for the origin. This is generated by ImageKit when you create a new origin. - access_key: Access key for the bucket. When `useIAMRole` is `true`, send an empty string. + access_key: Access key for the bucket. bucket: S3 bucket name. name: Display name of the origin. - secret_key: Secret key for the bucket. When `useIAMRole` is `true`, send an empty string. + secret_key: Secret key for the bucket. base_url_for_canonical_header: URL used in the Canonical header (if enabled). @@ -1632,9 +1601,6 @@ async def update( prefix: Path prefix inside the bucket. - use_iam_role: Use IAM role for authentication instead of access/secret keys. When set to - `true`, send an empty string for both `accessKey` and `secretKey`. - extra_headers: Send extra headers extra_query: Add additional query parameters to the request @@ -1717,7 +1683,6 @@ async def update( base_url_for_canonical_header: str | Omit = omit, include_canonical_header: bool | Omit = omit, prefix: str | Omit = omit, - use_iam_role: 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, @@ -1734,13 +1699,13 @@ async def update( id: Unique identifier for the origin. This is generated by ImageKit when you create a new origin. - access_key: Access key for the bucket. When `useIAMRole` is `true`, send an empty string. + access_key: Access key for the bucket. bucket: S3 bucket name. name: Display name of the origin. - secret_key: Secret key for the bucket. When `useIAMRole` is `true`, send an empty string. + secret_key: Secret key for the bucket. base_url_for_canonical_header: URL used in the Canonical header (if enabled). @@ -1748,9 +1713,6 @@ async def update( prefix: Path prefix inside the bucket. - use_iam_role: Use IAM role for authentication instead of access/secret keys. When set to - `true`, send an empty string for both `accessKey` and `secretKey`. - extra_headers: Send extra headers extra_query: Add additional query parameters to the request @@ -2023,7 +1985,6 @@ async def update( base_url_for_canonical_header: str | Omit = omit, include_canonical_header: bool | Omit = omit, prefix: str | Omit = omit, - use_iam_role: bool | Omit = omit, endpoint: str | Omit = omit, s3_force_path_style: bool | Omit = omit, base_url: str | Omit = omit, @@ -2060,7 +2021,6 @@ async def update( "base_url_for_canonical_header": base_url_for_canonical_header, "include_canonical_header": include_canonical_header, "prefix": prefix, - "use_iam_role": use_iam_role, "endpoint": endpoint, "s3_force_path_style": s3_force_path_style, "base_url": base_url, diff --git a/src/imagekitio/resources/accounts/usage.py b/src/imagekitio/resources/accounts/usage.py index 3501e726..b35d3c9b 100644 --- a/src/imagekitio/resources/accounts/usage.py +++ b/src/imagekitio/resources/accounts/usage.py @@ -63,12 +63,6 @@ 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. @@ -142,12 +136,6 @@ 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 deleted file mode 100644 index e2880fea..00000000 --- a/src/imagekitio/resources/accounts/usage_analytics.py +++ /dev/null @@ -1,224 +0,0 @@ -# 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/beta/v2/files.py b/src/imagekitio/resources/beta/v2/files.py index 15ba40fb..491c2115 100644 --- a/src/imagekitio/resources/beta/v2/files.py +++ b/src/imagekitio/resources/beta/v2/files.py @@ -30,7 +30,6 @@ ) from ...._base_client import make_request_options from ....types.beta.v2 import file_upload_params -from ....lib.serialization_utils import serialize_upload_options from ....types.shared_params.extensions import Extensions from ....types.beta.v2.file_upload_response import FileUploadResponse @@ -274,7 +273,6 @@ def upload( }, [["file"]], ) - body = serialize_upload_options(body) files = extract_files(cast(Mapping[str, object], body), paths=[["file"]]) # It should be noted that the actual Content-Type header that will be # sent to the server will contain a `boundary` parameter, e.g. @@ -530,7 +528,6 @@ async def upload( }, [["file"]], ) - body = serialize_upload_options(body) files = extract_files(cast(Mapping[str, object], body), paths=[["file"]]) # It should be noted that the actual Content-Type header that will be # sent to the server will contain a `boundary` parameter, e.g. diff --git a/src/imagekitio/resources/custom_metadata_fields.py b/src/imagekitio/resources/custom_metadata_fields.py index 6b2b084c..74553166 100644 --- a/src/imagekitio/resources/custom_metadata_fields.py +++ b/src/imagekitio/resources/custom_metadata_fields.py @@ -53,7 +53,6 @@ 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, @@ -77,10 +76,6 @@ 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 @@ -96,7 +91,6 @@ def create( "label": label, "name": name, "schema": schema, - "description": description, }, custom_metadata_field_create_params.CustomMetadataFieldCreateParams, ), @@ -110,7 +104,6 @@ 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. @@ -121,15 +114,9 @@ def update( timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> CustomMetadataField: """ - This API updates the label, description, or schema of an existing custom - metadata field. + This API updates the label 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 @@ -154,7 +141,6 @@ def update( path_template("/v1/customMetadataFields/{id}", id=id), body=maybe_transform( { - "description": description, "label": label, "schema": schema, }, @@ -285,7 +271,6 @@ 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, @@ -309,10 +294,6 @@ 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 @@ -328,7 +309,6 @@ async def create( "label": label, "name": name, "schema": schema, - "description": description, }, custom_metadata_field_create_params.CustomMetadataFieldCreateParams, ), @@ -342,7 +322,6 @@ 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. @@ -353,15 +332,9 @@ async def update( timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> CustomMetadataField: """ - This API updates the label, description, or schema of an existing custom - metadata field. + This API updates the label 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 @@ -386,7 +359,6 @@ 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/files/files.py b/src/imagekitio/resources/files/files.py index faaa418f..68f2f055 100644 --- a/src/imagekitio/resources/files/files.py +++ b/src/imagekitio/resources/files/files.py @@ -62,7 +62,6 @@ ) from ...types.file import File from ..._base_client import make_request_options -from ...lib.serialization_utils import serialize_upload_options from ...types.file_copy_response import FileCopyResponse from ...types.file_move_response import FileMoveResponse from ...types.file_rename_response import FileRenameResponse @@ -729,7 +728,6 @@ def upload( }, [["file"]], ) - body = serialize_upload_options(body) files = extract_files(cast(Mapping[str, object], body), paths=[["file"]]) # It should be noted that the actual Content-Type header that will be # sent to the server will contain a `boundary` parameter, e.g. @@ -1404,7 +1402,6 @@ async def upload( }, [["file"]], ) - body = serialize_upload_options(body) files = extract_files(cast(Mapping[str, object], body), paths=[["file"]]) # It should be noted that the actual Content-Type header that will be # sent to the server will contain a `boundary` parameter, e.g. diff --git a/src/imagekitio/resources/named_transformations.py b/src/imagekitio/resources/named_transformations.py deleted file mode 100644 index fbeb9a5d..00000000 --- a/src/imagekitio/resources/named_transformations.py +++ /dev/null @@ -1,580 +0,0 @@ -# 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/resources/webhooks.py b/src/imagekitio/resources/webhooks.py index 0ca75b5c..a561adee 100644 --- a/src/imagekitio/resources/webhooks.py +++ b/src/imagekitio/resources/webhooks.py @@ -3,7 +3,6 @@ from __future__ import annotations import json -import base64 from typing import Mapping, cast from .._models import construct_type @@ -41,13 +40,7 @@ def unwrap(self, payload: str, *, headers: Mapping[str, str], key: str | bytes | if not isinstance(headers, dict): headers = dict(headers) - if isinstance(key, str): - key_bytes = key.encode("utf-8") - else: - key_bytes = key - encoded_key = base64.b64encode(key_bytes).decode("ascii") - - Webhook(encoded_key).verify(payload, headers) + Webhook(key).verify(payload, headers) return cast( UnwrapWebhookEvent, @@ -84,13 +77,7 @@ def unwrap(self, payload: str, *, headers: Mapping[str, str], key: str | bytes | if not isinstance(headers, dict): headers = dict(headers) - if isinstance(key, str): - key_bytes = key.encode("utf-8") - else: - key_bytes = key - encoded_key = base64.b64encode(key_bytes).decode("ascii") - - Webhook(encoded_key).verify(payload, headers) + Webhook(key).verify(payload, headers) return cast( UnwrapWebhookEvent, diff --git a/src/imagekitio/types/__init__.py b/src/imagekitio/types/__init__.py index e81abbf8..c5d85205 100644 --- a/src/imagekitio/types/__init__.py +++ b/src/imagekitio/types/__init__.py @@ -21,7 +21,6 @@ OverlayPosition as OverlayPosition, SubtitleOverlay as SubtitleOverlay, SolidColorOverlay as SolidColorOverlay, - NamedTransformation as NamedTransformation, StreamingResolution as StreamingResolution, TransformationPosition as TransformationPosition, GetImageAttributesOptions as GetImageAttributesOptions, @@ -72,9 +71,6 @@ 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 60a394c1..3d713dbe 100644 --- a/src/imagekitio/types/accounts/__init__.py +++ b/src/imagekitio/types/accounts/__init__.py @@ -10,9 +10,6 @@ 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/origin_create_params.py b/src/imagekitio/types/accounts/origin_create_params.py index cc1c9c5b..7489a1d9 100644 --- a/src/imagekitio/types/accounts/origin_create_params.py +++ b/src/imagekitio/types/accounts/origin_create_params.py @@ -22,7 +22,7 @@ class S3(TypedDict, total=False): access_key: Required[Annotated[str, PropertyInfo(alias="accessKey")]] - """Access key for the bucket. When `useIAMRole` is `true`, send an empty string.""" + """Access key for the bucket.""" bucket: Required[str] """S3 bucket name.""" @@ -31,7 +31,7 @@ class S3(TypedDict, total=False): """Display name of the origin.""" secret_key: Required[Annotated[str, PropertyInfo(alias="secretKey")]] - """Secret key for the bucket. When `useIAMRole` is `true`, send an empty string.""" + """Secret key for the bucket.""" type: Required[Literal["S3"]] @@ -44,12 +44,6 @@ class S3(TypedDict, total=False): prefix: str """Path prefix inside the bucket.""" - use_iam_role: Annotated[bool, PropertyInfo(alias="useIAMRole")] - """Use IAM role for authentication instead of access/secret keys. - - When set to `true`, send an empty string for both `accessKey` and `secretKey`. - """ - class S3Compatible(TypedDict, total=False): access_key: Required[Annotated[str, PropertyInfo(alias="accessKey")]] @@ -84,7 +78,7 @@ class S3Compatible(TypedDict, total=False): class CloudinaryBackup(TypedDict, total=False): access_key: Required[Annotated[str, PropertyInfo(alias="accessKey")]] - """Access key for the bucket. When `useIAMRole` is `true`, send an empty string.""" + """Access key for the bucket.""" bucket: Required[str] """S3 bucket name.""" @@ -93,7 +87,7 @@ class CloudinaryBackup(TypedDict, total=False): """Display name of the origin.""" secret_key: Required[Annotated[str, PropertyInfo(alias="secretKey")]] - """Secret key for the bucket. When `useIAMRole` is `true`, send an empty string.""" + """Secret key for the bucket.""" type: Required[Literal["CLOUDINARY_BACKUP"]] @@ -106,12 +100,6 @@ class CloudinaryBackup(TypedDict, total=False): prefix: str """Path prefix inside the bucket.""" - use_iam_role: Annotated[bool, PropertyInfo(alias="useIAMRole")] - """Use IAM role for authentication instead of access/secret keys. - - When set to `true`, send an empty string for both `accessKey` and `secretKey`. - """ - class WebFolder(TypedDict, total=False): base_url: Required[Annotated[str, PropertyInfo(alias="baseUrl")]] diff --git a/src/imagekitio/types/accounts/origin_request_param.py b/src/imagekitio/types/accounts/origin_request_param.py index 4289b649..a2864ad4 100644 --- a/src/imagekitio/types/accounts/origin_request_param.py +++ b/src/imagekitio/types/accounts/origin_request_param.py @@ -22,7 +22,7 @@ class S3(TypedDict, total=False): access_key: Required[Annotated[str, PropertyInfo(alias="accessKey")]] - """Access key for the bucket. When `useIAMRole` is `true`, send an empty string.""" + """Access key for the bucket.""" bucket: Required[str] """S3 bucket name.""" @@ -31,7 +31,7 @@ class S3(TypedDict, total=False): """Display name of the origin.""" secret_key: Required[Annotated[str, PropertyInfo(alias="secretKey")]] - """Secret key for the bucket. When `useIAMRole` is `true`, send an empty string.""" + """Secret key for the bucket.""" type: Required[Literal["S3"]] @@ -44,12 +44,6 @@ class S3(TypedDict, total=False): prefix: str """Path prefix inside the bucket.""" - use_iam_role: Annotated[bool, PropertyInfo(alias="useIAMRole")] - """Use IAM role for authentication instead of access/secret keys. - - When set to `true`, send an empty string for both `accessKey` and `secretKey`. - """ - class S3Compatible(TypedDict, total=False): access_key: Required[Annotated[str, PropertyInfo(alias="accessKey")]] @@ -84,7 +78,7 @@ class S3Compatible(TypedDict, total=False): class CloudinaryBackup(TypedDict, total=False): access_key: Required[Annotated[str, PropertyInfo(alias="accessKey")]] - """Access key for the bucket. When `useIAMRole` is `true`, send an empty string.""" + """Access key for the bucket.""" bucket: Required[str] """S3 bucket name.""" @@ -93,7 +87,7 @@ class CloudinaryBackup(TypedDict, total=False): """Display name of the origin.""" secret_key: Required[Annotated[str, PropertyInfo(alias="secretKey")]] - """Secret key for the bucket. When `useIAMRole` is `true`, send an empty string.""" + """Secret key for the bucket.""" type: Required[Literal["CLOUDINARY_BACKUP"]] @@ -106,12 +100,6 @@ class CloudinaryBackup(TypedDict, total=False): prefix: str """Path prefix inside the bucket.""" - use_iam_role: Annotated[bool, PropertyInfo(alias="useIAMRole")] - """Use IAM role for authentication instead of access/secret keys. - - When set to `true`, send an empty string for both `accessKey` and `secretKey`. - """ - class WebFolder(TypedDict, total=False): base_url: Required[Annotated[str, PropertyInfo(alias="baseUrl")]] diff --git a/src/imagekitio/types/accounts/origin_response.py b/src/imagekitio/types/accounts/origin_response.py index cf7c6340..d4374470 100644 --- a/src/imagekitio/types/accounts/origin_response.py +++ b/src/imagekitio/types/accounts/origin_response.py @@ -45,12 +45,6 @@ class S3(BaseModel): base_url_for_canonical_header: Optional[str] = FieldInfo(alias="baseUrlForCanonicalHeader", default=None) """URL used in the Canonical header (if enabled).""" - use_iam_role: Optional[bool] = FieldInfo(alias="useIAMRole", default=None) - """ - Whether the origin authenticates using an IAM role instead of access/secret - keys. - """ - class S3Compatible(BaseModel): id: str @@ -107,12 +101,6 @@ class CloudinaryBackup(BaseModel): base_url_for_canonical_header: Optional[str] = FieldInfo(alias="baseUrlForCanonicalHeader", default=None) """URL used in the Canonical header (if enabled).""" - use_iam_role: Optional[bool] = FieldInfo(alias="useIAMRole", default=None) - """ - Whether the origin authenticates using an IAM role instead of access/secret - keys. - """ - class WebFolder(BaseModel): id: str diff --git a/src/imagekitio/types/accounts/origin_update_params.py b/src/imagekitio/types/accounts/origin_update_params.py index abb9cf9a..a7b39fba 100644 --- a/src/imagekitio/types/accounts/origin_update_params.py +++ b/src/imagekitio/types/accounts/origin_update_params.py @@ -22,7 +22,7 @@ class S3(TypedDict, total=False): access_key: Required[Annotated[str, PropertyInfo(alias="accessKey")]] - """Access key for the bucket. When `useIAMRole` is `true`, send an empty string.""" + """Access key for the bucket.""" bucket: Required[str] """S3 bucket name.""" @@ -31,7 +31,7 @@ class S3(TypedDict, total=False): """Display name of the origin.""" secret_key: Required[Annotated[str, PropertyInfo(alias="secretKey")]] - """Secret key for the bucket. When `useIAMRole` is `true`, send an empty string.""" + """Secret key for the bucket.""" type: Required[Literal["S3"]] @@ -44,12 +44,6 @@ class S3(TypedDict, total=False): prefix: str """Path prefix inside the bucket.""" - use_iam_role: Annotated[bool, PropertyInfo(alias="useIAMRole")] - """Use IAM role for authentication instead of access/secret keys. - - When set to `true`, send an empty string for both `accessKey` and `secretKey`. - """ - class S3Compatible(TypedDict, total=False): access_key: Required[Annotated[str, PropertyInfo(alias="accessKey")]] @@ -84,7 +78,7 @@ class S3Compatible(TypedDict, total=False): class CloudinaryBackup(TypedDict, total=False): access_key: Required[Annotated[str, PropertyInfo(alias="accessKey")]] - """Access key for the bucket. When `useIAMRole` is `true`, send an empty string.""" + """Access key for the bucket.""" bucket: Required[str] """S3 bucket name.""" @@ -93,7 +87,7 @@ class CloudinaryBackup(TypedDict, total=False): """Display name of the origin.""" secret_key: Required[Annotated[str, PropertyInfo(alias="secretKey")]] - """Secret key for the bucket. When `useIAMRole` is `true`, send an empty string.""" + """Secret key for the bucket.""" type: Required[Literal["CLOUDINARY_BACKUP"]] @@ -106,12 +100,6 @@ class CloudinaryBackup(TypedDict, total=False): prefix: str """Path prefix inside the bucket.""" - use_iam_role: Annotated[bool, PropertyInfo(alias="useIAMRole")] - """Use IAM role for authentication instead of access/secret keys. - - When set to `true`, send an empty string for both `accessKey` and `secretKey`. - """ - class WebFolder(TypedDict, total=False): base_url: Required[Annotated[str, PropertyInfo(alias="baseUrl")]] diff --git a/src/imagekitio/types/accounts/request_bandwidth_entry.py b/src/imagekitio/types/accounts/request_bandwidth_entry.py deleted file mode 100644 index ea245b64..00000000 --- a/src/imagekitio/types/accounts/request_bandwidth_entry.py +++ /dev/null @@ -1,15 +0,0 @@ -# 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 deleted file mode 100644 index 7d882ad1..00000000 --- a/src/imagekitio/types/accounts/usage_analytics_get_params.py +++ /dev/null @@ -1,27 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing import 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 deleted file mode 100644 index 131cf712..00000000 --- a/src/imagekitio/types/accounts/usage_analytics_response.py +++ /dev/null @@ -1,488 +0,0 @@ -# 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 d725ad3c..1ebde723 100644 --- a/src/imagekitio/types/custom_metadata_field.py +++ b/src/imagekitio/types/custom_metadata_field.py @@ -75,10 +75,3 @@ 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 63fd634b..0e265b09 100644 --- a/src/imagekitio/types/custom_metadata_field_create_params.py +++ b/src/imagekitio/types/custom_metadata_field_create_params.py @@ -28,13 +28,6 @@ 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 2a3e446e..fbb9effc 100644 --- a/src/imagekitio/types/custom_metadata_field_update_params.py +++ b/src/imagekitio/types/custom_metadata_field_update_params.py @@ -12,14 +12,6 @@ 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/metadata.py b/src/imagekitio/types/metadata.py index 9e47cc53..87ac3341 100644 --- a/src/imagekitio/types/metadata.py +++ b/src/imagekitio/types/metadata.py @@ -1,6 +1,6 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. -from typing import TYPE_CHECKING, Dict, List, Optional +from typing import Dict, List, Optional from pydantic import Field as FieldInfo @@ -14,8 +14,6 @@ class ExifExif(BaseModel): aperture_value: Optional[float] = FieldInfo(alias="ApertureValue", default=None) - brightness_value: Optional[float] = FieldInfo(alias="BrightnessValue", default=None) - color_space: Optional[int] = FieldInfo(alias="ColorSpace", default=None) create_date: Optional[str] = FieldInfo(alias="CreateDate", default=None) @@ -44,9 +42,7 @@ class ExifExif(BaseModel): f_number: Optional[float] = FieldInfo(alias="FNumber", default=None) - focal_length: Optional[float] = FieldInfo(alias="FocalLength", default=None) - - focal_length_in35mm_format: Optional[int] = FieldInfo(alias="FocalLengthIn35mmFormat", default=None) + focal_length: Optional[int] = FieldInfo(alias="FocalLength", default=None) focal_plane_resolution_unit: Optional[int] = FieldInfo(alias="FocalPlaneResolutionUnit", default=None) @@ -58,92 +54,30 @@ class ExifExif(BaseModel): iso: Optional[int] = FieldInfo(alias="ISO", default=None) - lens_model: Optional[str] = FieldInfo(alias="LensModel", default=None) - - light_source: Optional[int] = FieldInfo(alias="LightSource", default=None) - - max_aperture_value: Optional[float] = FieldInfo(alias="MaxApertureValue", default=None) - metering_mode: Optional[int] = FieldInfo(alias="MeteringMode", default=None) scene_capture_type: Optional[int] = FieldInfo(alias="SceneCaptureType", default=None) - scene_type: Optional[str] = FieldInfo(alias="SceneType", default=None) - - sensing_method: Optional[int] = FieldInfo(alias="SensingMethod", default=None) - shutter_speed_value: Optional[float] = FieldInfo(alias="ShutterSpeedValue", default=None) sub_sec_time: Optional[str] = FieldInfo(alias="SubSecTime", default=None) - user_comment: Optional[str] = FieldInfo(alias="UserComment", default=None) - white_balance: Optional[int] = FieldInfo(alias="WhiteBalance", default=None) - 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] - class ExifGps(BaseModel): """Object containing GPS information.""" - gps_altitude: Optional[float] = FieldInfo(alias="GPSAltitude", default=None) - - gps_altitude_ref: Optional[int] = FieldInfo(alias="GPSAltitudeRef", default=None) - - gps_date_stamp: Optional[str] = FieldInfo(alias="GPSDateStamp", default=None) - - gps_img_direction: Optional[float] = FieldInfo(alias="GPSImgDirection", default=None) - - gps_img_direction_ref: Optional[str] = FieldInfo(alias="GPSImgDirectionRef", default=None) - - gps_latitude: Optional[List[float]] = FieldInfo(alias="GPSLatitude", default=None) - - gps_latitude_ref: Optional[str] = FieldInfo(alias="GPSLatitudeRef", default=None) - - gps_longitude: Optional[List[float]] = FieldInfo(alias="GPSLongitude", default=None) - - gps_longitude_ref: Optional[str] = FieldInfo(alias="GPSLongitudeRef", default=None) - - gps_time_stamp: Optional[List[float]] = FieldInfo(alias="GPSTimeStamp", default=None) - gps_version_id: Optional[List[int]] = FieldInfo(alias="GPSVersionID", default=None) - 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] - class ExifImage(BaseModel): """Object containing EXIF image information.""" - artist: Optional[str] = FieldInfo(alias="Artist", default=None) - - copyright: Optional[str] = FieldInfo(alias="Copyright", default=None) - exif_offset: Optional[int] = FieldInfo(alias="ExifOffset", default=None) gps_info: Optional[int] = FieldInfo(alias="GPSInfo", default=None) - image_description: Optional[str] = FieldInfo(alias="ImageDescription", default=None) - make: Optional[str] = FieldInfo(alias="Make", default=None) model: Optional[str] = FieldInfo(alias="Model", default=None) @@ -156,23 +90,11 @@ class ExifImage(BaseModel): software: Optional[str] = FieldInfo(alias="Software", default=None) - x_resolution: Optional[float] = FieldInfo(alias="XResolution", default=None) + x_resolution: Optional[int] = FieldInfo(alias="XResolution", default=None) y_cb_cr_positioning: Optional[int] = FieldInfo(alias="YCbCrPositioning", default=None) - y_resolution: Optional[float] = FieldInfo(alias="YResolution", default=None) - - 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] + y_resolution: Optional[int] = FieldInfo(alias="YResolution", default=None) class ExifInteroperability(BaseModel): @@ -182,18 +104,6 @@ class ExifInteroperability(BaseModel): interop_version: Optional[str] = FieldInfo(alias="InteropVersion", default=None) - 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] - class ExifThumbnail(BaseModel): """Object containing Thumbnail information.""" @@ -206,21 +116,9 @@ class ExifThumbnail(BaseModel): thumbnail_offset: Optional[int] = FieldInfo(alias="ThumbnailOffset", default=None) - x_resolution: Optional[float] = FieldInfo(alias="XResolution", default=None) - - y_resolution: Optional[float] = FieldInfo(alias="YResolution", default=None) + x_resolution: Optional[int] = FieldInfo(alias="XResolution", default=None) - 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] + y_resolution: Optional[int] = FieldInfo(alias="YResolution", default=None) class Exif(BaseModel): @@ -241,18 +139,6 @@ class Exif(BaseModel): thumbnail: Optional[ExifThumbnail] = None """Object containing Thumbnail information.""" - 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] - class Metadata(BaseModel): """JSON object containing metadata.""" @@ -297,15 +183,3 @@ class Metadata(BaseModel): width: Optional[int] = None """The width of the image or video in pixels.""" - - 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/named_transformation_create_params.py b/src/imagekitio/types/named_transformation_create_params.py deleted file mode 100644 index d7d91558..00000000 --- a/src/imagekitio/types/named_transformation_create_params.py +++ /dev/null @@ -1,30 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing_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 deleted file mode 100644 index 29bd9d2d..00000000 --- a/src/imagekitio/types/named_transformation_list_response.py +++ /dev/null @@ -1,10 +0,0 @@ -# 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 deleted file mode 100644 index 23401f43..00000000 --- a/src/imagekitio/types/named_transformation_update_params.py +++ /dev/null @@ -1,29 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -from typing_extensions import TypedDict - -__all__ = ["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 c5c9043a..cae1a71e 100644 --- a/src/imagekitio/types/shared/__init__.py +++ b/src/imagekitio/types/shared/__init__.py @@ -14,7 +14,6 @@ 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 deleted file mode 100644 index dc82b342..00000000 --- a/src/imagekitio/types/shared/named_transformation.py +++ /dev/null @@ -1,42 +0,0 @@ -# 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/src/imagekitio/types/shared/transformation.py b/src/imagekitio/types/shared/transformation.py index b8edb6d8..c1927c81 100644 --- a/src/imagekitio/types/shared/transformation.py +++ b/src/imagekitio/types/shared/transformation.py @@ -144,7 +144,7 @@ class Transformation(BaseModel): - `co-color` - Color to apply (e.g., `red`, `blue`, `FF0022`). Default is gray color. - - `in-intensity` - Intensity of the color (0-100). Default is 100. See + - `in-intensity` - Intensity of the color (0-100). Default is 35. See [Colorize](https://imagekit.io/docs/effects-and-enhancements#colorize---e-colorize). """ diff --git a/src/imagekitio/types/shared_params/transformation.py b/src/imagekitio/types/shared_params/transformation.py index a5015a58..e1bd3e23 100644 --- a/src/imagekitio/types/shared_params/transformation.py +++ b/src/imagekitio/types/shared_params/transformation.py @@ -142,7 +142,7 @@ class Transformation(TypedDict, total=False): - `co-color` - Color to apply (e.g., `red`, `blue`, `FF0022`). Default is gray color. - - `in-intensity` - Intensity of the color (0-100). Default is 100. See + - `in-intensity` - Intensity of the color (0-100). Default is 35. See [Colorize](https://imagekit.io/docs/effects-and-enhancements#colorize---e-colorize). """ diff --git a/tests/api_resources/accounts/test_origins.py b/tests/api_resources/accounts/test_origins.py index cee184a5..a4f60701 100644 --- a/tests/api_resources/accounts/test_origins.py +++ b/tests/api_resources/accounts/test_origins.py @@ -41,7 +41,6 @@ def test_method_create_with_all_params_overload_1(self, client: ImageKit) -> Non base_url_for_canonical_header="https://cdn.example.com", include_canonical_header=False, prefix="raw-assets", - use_iam_role=True, ) assert_matches_type(OriginResponse, origin, path=["response"]) @@ -169,7 +168,6 @@ def test_method_create_with_all_params_overload_3(self, client: ImageKit) -> Non base_url_for_canonical_header="https://cdn.example.com", include_canonical_header=False, prefix="raw-assets", - use_iam_role=True, ) assert_matches_type(OriginResponse, origin, path=["response"]) @@ -524,7 +522,6 @@ def test_method_update_with_all_params_overload_1(self, client: ImageKit) -> Non base_url_for_canonical_header="https://cdn.example.com", include_canonical_header=False, prefix="raw-assets", - use_iam_role=True, ) assert_matches_type(OriginResponse, origin, path=["response"]) @@ -687,7 +684,6 @@ def test_method_update_with_all_params_overload_3(self, client: ImageKit) -> Non base_url_for_canonical_header="https://cdn.example.com", include_canonical_header=False, prefix="raw-assets", - use_iam_role=True, ) assert_matches_type(OriginResponse, origin, path=["response"]) @@ -1255,7 +1251,6 @@ async def test_method_create_with_all_params_overload_1(self, async_client: Asyn base_url_for_canonical_header="https://cdn.example.com", include_canonical_header=False, prefix="raw-assets", - use_iam_role=True, ) assert_matches_type(OriginResponse, origin, path=["response"]) @@ -1383,7 +1378,6 @@ async def test_method_create_with_all_params_overload_3(self, async_client: Asyn base_url_for_canonical_header="https://cdn.example.com", include_canonical_header=False, prefix="raw-assets", - use_iam_role=True, ) assert_matches_type(OriginResponse, origin, path=["response"]) @@ -1738,7 +1732,6 @@ async def test_method_update_with_all_params_overload_1(self, async_client: Asyn base_url_for_canonical_header="https://cdn.example.com", include_canonical_header=False, prefix="raw-assets", - use_iam_role=True, ) assert_matches_type(OriginResponse, origin, path=["response"]) @@ -1901,7 +1894,6 @@ async def test_method_update_with_all_params_overload_3(self, async_client: Asyn base_url_for_canonical_header="https://cdn.example.com", include_canonical_header=False, prefix="raw-assets", - use_iam_role=True, ) assert_matches_type(OriginResponse, origin, path=["response"]) diff --git a/tests/api_resources/accounts/test_usage_analytics.py b/tests/api_resources/accounts/test_usage_analytics.py deleted file mode 100644 index 3881e5ad..00000000 --- a/tests/api_resources/accounts/test_usage_analytics.py +++ /dev/null @@ -1,99 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -import os -from typing import Any, cast - -import pytest - -from 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 4cfb101d..ec73bab1 100644 --- a/tests/api_resources/test_custom_metadata_fields.py +++ b/tests/api_resources/test_custom_metadata_fields.py @@ -47,7 +47,6 @@ 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"]) @@ -94,7 +93,6 @@ 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"], @@ -253,7 +251,6 @@ 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"]) @@ -300,7 +297,6 @@ 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 deleted file mode 100644 index d59095c8..00000000 --- a/tests/api_resources/test_named_transformations.py +++ /dev/null @@ -1,451 +0,0 @@ -# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -from __future__ import annotations - -import os -from typing import Any, cast - -import pytest - -from 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( - "", - ) diff --git a/tests/custom/__init__.py b/tests/custom/__init__.py deleted file mode 100644 index dad8a0a3..00000000 --- a/tests/custom/__init__.py +++ /dev/null @@ -1,2 +0,0 @@ -# Custom tests for manually created helper functions -# These tests are separate from auto-generated API tests diff --git a/tests/custom/test_helper_authentication.py b/tests/custom/test_helper_authentication.py deleted file mode 100644 index a0a08efa..00000000 --- a/tests/custom/test_helper_authentication.py +++ /dev/null @@ -1,114 +0,0 @@ -"""Helper authentication tests - converted from Ruby SDK.""" - -import re - -import pytest - -from imagekitio import ImageKit, ImageKitError - - -class TestHelperAuthentication: - """Test helper authentication parameter generation.""" - - def test_should_return_correct_authentication_parameters_with_provided_token_and_expire(self) -> None: - """Should return correct authentication parameters with provided token and expire.""" - private_key = "private_key_test" - client = ImageKit(private_key=private_key) - - token = "your_token" - expire = 1582269249 - - params = client.helper.get_authentication_parameters(token=token, expire=expire) - - # Expected exact match with Node.js output - expected_signature = "e71bcd6031016b060d349d212e23e85c791decdd" - - assert params["token"] == token - assert params["expire"] == expire - assert params["signature"] == expected_signature - - def test_should_return_authentication_parameters_with_required_properties_when_no_params_provided(self) -> None: - """Should return authentication parameters with required properties when no params provided.""" - private_key = "private_key_test" - client = ImageKit(private_key=private_key) - - params = client.helper.get_authentication_parameters() - - # Check that all required properties exist - assert "token" in params, "Expected token parameter" - assert "expire" in params, "Expected expire parameter" - assert "signature" in params, "Expected signature parameter" - - # Token should be a UUID v4 format (36 characters with dashes) - token = params["token"] - assert isinstance(token, str) - assert re.match( - r"^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$", token, re.IGNORECASE - ), "Expected token to be UUID v4 format" - - # Expire should be a number greater than current time - expire = params["expire"] - assert isinstance(expire, int) - import time - - current_time = int(time.time()) - assert expire > current_time, f"Expected expire {expire} to be greater than current time {current_time}" - - # Signature should be a hex string (40 characters for HMAC-SHA1) - signature = params["signature"] - assert isinstance(signature, str) - assert re.match(r"^[a-f0-9]{40}$", signature), "Expected signature to be 40 character hex string" - - def test_should_handle_edge_case_with_expire_time_0(self) -> None: - """Should handle edge case with expire time 0.""" - private_key = "private_key_test" - client = ImageKit(private_key=private_key) - - token = "test_token" - expire = 0 - - params = client.helper.get_authentication_parameters(token=token, expire=expire) - - assert params["token"] == token - assert params["expire"] == expire - assert "signature" in params - # Signature should still be generated even with expire = 0 - assert isinstance(params["signature"], str) - assert len(params["signature"]) == 40 - - def test_should_handle_empty_string_token(self) -> None: - """Should handle empty string token.""" - private_key = "private_key_test" - client = ImageKit(private_key=private_key) - - token = "" # Empty string is falsy - expire = 1582269249 - - params = client.helper.get_authentication_parameters(token=token, expire=expire) - - # Since empty string is falsy, it should generate a token - token_result = params["token"] - assert isinstance(token_result, str) - assert len(token_result) > 0, "Expected token to be generated when empty string is provided" - assert re.match( - r"^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$", token_result, re.IGNORECASE - ), "Expected generated token to be UUID v4 format" - - assert params["expire"] == expire - - # Signature should be a hex string (40 characters for HMAC-SHA1) - signature = params["signature"] - assert isinstance(signature, str) - assert re.match(r"^[a-f0-9]{40}$", signature), "Expected signature to be 40 character hex string" - - def test_should_raise_error_when_private_key_is_not_provided(self) -> None: - """Should raise error when private key is empty.""" - with pytest.raises(ValueError, match="Private key is required"): - client = ImageKit(private_key="") - client.helper.get_authentication_parameters(token="test", expire=123) - - def test_should_raise_error_when_private_key_is_nil(self) -> None: - """Should raise error when private key is None.""" - with pytest.raises(ImageKitError, match="private_key client option must be set"): - client = ImageKit(private_key=None) # type: ignore - client.helper.get_authentication_parameters(token="test", expire=123) diff --git a/tests/custom/test_serialization_utils.py b/tests/custom/test_serialization_utils.py deleted file mode 100644 index b523aeb4..00000000 --- a/tests/custom/test_serialization_utils.py +++ /dev/null @@ -1,228 +0,0 @@ -"""Unit tests for serialization_utils module.""" - -import json -from typing import Any, Dict, List - -from imagekitio.lib.serialization_utils import serialize_upload_options - - -class TestSerializeUploadOptions: - """Test cases for serialize_upload_options function.""" - - def test_should_convert_tags_array_to_comma_separated_string(self): - """Test that tags array is converted to comma-separated string.""" - body = {"tags": ["tag1", "tag2", "tag3"]} - result = serialize_upload_options(body) - assert result["tags"] == "tag1,tag2,tag3" - - def test_should_convert_tags_tuple_to_comma_separated_string(self): - """Test that tags tuple is converted to comma-separated string.""" - body = {"tags": ("tag1", "tag2", "tag3")} - result = serialize_upload_options(body) - assert result["tags"] == "tag1,tag2,tag3" - - def test_should_convert_response_fields_array_to_comma_separated_string(self): - """Test that response_fields array is converted to comma-separated string.""" - body = {"response_fields": ["tags", "customCoordinates", "metadata"]} - result = serialize_upload_options(body) - assert result["response_fields"] == "tags,customCoordinates,metadata" - - def test_should_convert_response_fields_tuple_to_comma_separated_string(self): - """Test that response_fields tuple is converted to comma-separated string.""" - body = {"response_fields": ("tags", "customCoordinates")} - result = serialize_upload_options(body) - assert result["response_fields"] == "tags,customCoordinates" - - def test_should_json_stringify_extensions_array(self): - """Test that extensions array is JSON stringified.""" - body = {"extensions": [{"name": "remove-bg"}, {"name": "google-auto-tagging", "minConfidence": 80}]} - result = serialize_upload_options(body) - expected = json.dumps(body["extensions"]) - assert result["extensions"] == expected - # Verify it's valid JSON - assert json.loads(result["extensions"]) == body["extensions"] - - def test_should_json_stringify_custom_metadata_object(self): - """Test that custom_metadata object is JSON stringified.""" - body = {"custom_metadata": {"key1": "value1", "key2": 123, "key3": True}} - result = serialize_upload_options(body) - expected = json.dumps(body["custom_metadata"]) - assert result["custom_metadata"] == expected - # Verify it's valid JSON - assert json.loads(result["custom_metadata"]) == body["custom_metadata"] - - def test_should_json_stringify_transformation_object(self): - """Test that transformation object is JSON stringified.""" - body = { - "transformation": { - "pre": "l-image,i-logo.png,w-100,h-100", - "post": [{"type": "thumbnail", "value": "h-300"}], - } - } - result = serialize_upload_options(body) - expected = json.dumps(body["transformation"]) - assert result["transformation"] == expected - # Verify it's valid JSON - assert json.loads(result["transformation"]) == body["transformation"] - - def test_should_handle_all_serializable_fields_together(self): - """Test that all serializable fields are processed correctly together.""" - body = { - "file": "test.jpg", - "file_name": "test.jpg", - "tags": ["tag1", "tag2"], - "response_fields": ["tags", "metadata"], - "extensions": [{"name": "remove-bg"}], - "custom_metadata": {"key": "value"}, - "transformation": {"pre": "w-100"}, - "folder": "/images", - } - result = serialize_upload_options(body) - - assert result["tags"] == "tag1,tag2" - assert result["response_fields"] == "tags,metadata" - assert result["extensions"] == json.dumps([{"name": "remove-bg"}]) - assert result["custom_metadata"] == json.dumps({"key": "value"}) - assert result["transformation"] == json.dumps({"pre": "w-100"}) - # Non-serializable fields should remain unchanged - assert result["file"] == "test.jpg" - assert result["file_name"] == "test.jpg" - assert result["folder"] == "/images" - - def test_should_not_modify_original_body(self): - """Test that the original body is not modified.""" - body = { - "tags": ["tag1", "tag2"], - "response_fields": ["tags"], - "extensions": [{"name": "ext1"}], - } - original_tags = body["tags"].copy() - original_response_fields = body["response_fields"].copy() - original_extensions = body["extensions"].copy() - - serialize_upload_options(body) - - # Original should remain unchanged - assert body["tags"] == original_tags - assert body["response_fields"] == original_response_fields - assert body["extensions"] == original_extensions - - def test_should_handle_empty_arrays(self): - """Test that empty arrays are converted to empty strings.""" - body: Dict[str, List[str]] = {"tags": [], "response_fields": []} - result = serialize_upload_options(body) - assert result["tags"] == "" - assert result["response_fields"] == "" - - def test_should_handle_empty_extensions_array(self): - """Test that empty extensions array is JSON stringified.""" - body: Dict[str, List[Any]] = {"extensions": []} - result = serialize_upload_options(body) - assert result["extensions"] == "[]" - - def test_should_handle_none_values(self): - """Test that None values are not processed.""" - body = { - "tags": None, - "response_fields": None, - "extensions": None, - "custom_metadata": None, - "transformation": None, - } - result = serialize_upload_options(body) - # None values should remain None - assert result["tags"] is None - assert result["response_fields"] is None - assert result["extensions"] is None - assert result["custom_metadata"] is None - assert result["transformation"] is None - - def test_should_handle_empty_object(self): - """Test that an empty object is returned as is.""" - body: Dict[str, Any] = {} - result = serialize_upload_options(body) - assert result == {} - - def test_should_skip_non_matching_fields(self): - """Test that fields not in the serialization list are left unchanged.""" - body = { - "file_name": "test.jpg", - "folder": "/images", - "is_private_file": True, - "use_unique_file_name": False, - } - result = serialize_upload_options(body) - assert result == body - - def test_should_handle_single_tag(self): - """Test that a single tag array is handled correctly.""" - body = {"tags": ["single-tag"]} - result = serialize_upload_options(body) - assert result["tags"] == "single-tag" - - def test_should_handle_tags_with_empty_strings(self): - """Test that tags with empty strings are still joined.""" - body = {"tags": ["tag1", "", "tag2"]} - result = serialize_upload_options(body) - assert result["tags"] == "tag1,,tag2" - - def test_should_handle_complex_nested_extensions(self): - """Test that complex nested extensions are properly JSON stringified.""" - body = { - "extensions": [ - { - "name": "aws-auto-tagging", - "options": {"maxTags": 10, "minConfidence": 75}, - }, - { - "name": "remove-bg", - "options": {"add_shadow": True, "bg_color": "white"}, - }, - ] - } - result = serialize_upload_options(body) - expected = json.dumps(body["extensions"]) - assert result["extensions"] == expected - assert json.loads(result["extensions"]) == body["extensions"] - - def test_should_handle_nested_custom_metadata(self): - """Test that nested custom metadata is properly JSON stringified.""" - body = { - "custom_metadata": { - "product": {"name": "Test Product", "price": 99.99, "inStock": True}, - "category": "electronics", - } - } - result = serialize_upload_options(body) - expected = json.dumps(body["custom_metadata"]) - assert result["custom_metadata"] == expected - assert json.loads(result["custom_metadata"]) == body["custom_metadata"] - - def test_should_handle_transformation_with_both_pre_and_post(self): - """Test that transformation with both pre and post is properly handled.""" - body = { - "transformation": { - "pre": "w-200,h-200", - "post": [{"type": "transformation", "value": "w-100,h-100"}], - } - } - result = serialize_upload_options(body) - expected = json.dumps(body["transformation"]) - assert result["transformation"] == expected - assert json.loads(result["transformation"]) == body["transformation"] - - def test_should_not_modify_non_dict_custom_metadata(self): - """Test that custom_metadata is only serialized when it's a dict.""" - # This shouldn't happen in practice but testing edge case - body = {"custom_metadata": "string_value"} - result = serialize_upload_options(body) - # String value should remain unchanged - assert result["custom_metadata"] == "string_value" - - def test_should_not_modify_non_list_extensions(self): - """Test that extensions is only serialized when it's a list.""" - # This shouldn't happen in practice but testing edge case - body = {"extensions": "string_value"} - result = serialize_upload_options(body) - # String value should remain unchanged - assert result["extensions"] == "string_value" diff --git a/tests/custom/url_generation/__init__.py b/tests/custom/url_generation/__init__.py deleted file mode 100644 index e0c071a8..00000000 --- a/tests/custom/url_generation/__init__.py +++ /dev/null @@ -1 +0,0 @@ -# URL generation test module diff --git a/tests/custom/url_generation/test_advanced_url_generation.py b/tests/custom/url_generation/test_advanced_url_generation.py deleted file mode 100644 index 67d04b36..00000000 --- a/tests/custom/url_generation/test_advanced_url_generation.py +++ /dev/null @@ -1,295 +0,0 @@ -"""Advanced URL generation tests imported from Ruby SDK.""" - -import pytest - -from imagekitio import ImageKit - - -class TestAdvancedURLGeneration: - """Test advanced URL generation matching Ruby SDK advanced_url_generation_test.rb.""" - - @pytest.fixture(autouse=True) - def setup(self): - """Setup client for each test.""" - self.client = ImageKit(private_key="My Private API Key") - - # AI Transformation Tests - def test_should_generate_the_correct_url_for_ai_background_removal_when_set_to_true(self): - """Test AI background removal transformation.""" - url = self.client.helper.build_url( - src="/test_path1.jpg", - url_endpoint="https://ik.imagekit.io/test_url_endpoint", - transformation_position="query", - transformation=[{"ai_remove_background": True}], - ) - expected = "https://ik.imagekit.io/test_url_endpoint/test_path1.jpg?tr=e-bgremove" - assert url == expected - - def test_should_generate_the_correct_url_for_external_ai_background_removal_when_set_to_true(self): - """Test external AI background removal transformation.""" - url = self.client.helper.build_url( - src="/test_path1.jpg", - url_endpoint="https://ik.imagekit.io/test_url_endpoint", - transformation_position="query", - transformation=[{"ai_remove_background_external": True}], - ) - expected = "https://ik.imagekit.io/test_url_endpoint/test_path1.jpg?tr=e-removedotbg" - assert url == expected - - def test_should_generate_the_correct_url_when_ai_drop_shadow_transformation_is_set_to_true(self): - """Test AI drop shadow transformation.""" - url = self.client.helper.build_url( - src="/test_path1.jpg", - url_endpoint="https://ik.imagekit.io/test_url_endpoint", - transformation_position="query", - transformation=[{"ai_drop_shadow": True}], - ) - expected = "https://ik.imagekit.io/test_url_endpoint/test_path1.jpg?tr=e-dropshadow" - assert url == expected - - def test_should_generate_the_correct_url_when_gradient_transformation_is_set_to_true(self): - """Test gradient transformation.""" - url = self.client.helper.build_url( - src="/test_path1.jpg", - url_endpoint="https://ik.imagekit.io/test_url_endpoint", - transformation_position="query", - transformation=[{"gradient": True}], - ) - expected = "https://ik.imagekit.io/test_url_endpoint/test_path1.jpg?tr=e-gradient" - assert url == expected - - def test_should_not_apply_ai_background_removal_when_value_is_not_true(self): - """Test that AI background removal is not applied when not true.""" - url = self.client.helper.build_url( - src="/test_path1.jpg", - url_endpoint="https://ik.imagekit.io/test_url_endpoint", - transformation_position="query", - transformation=[{}], - ) - expected = "https://ik.imagekit.io/test_url_endpoint/test_path1.jpg" - assert url == expected - - def test_should_not_apply_external_ai_background_removal_when_value_is_not_true(self): - """Test that external AI background removal is not applied when not true.""" - url = self.client.helper.build_url( - src="/test_path1.jpg", - url_endpoint="https://ik.imagekit.io/test_url_endpoint", - transformation_position="query", - transformation=[{}], - ) - expected = "https://ik.imagekit.io/test_url_endpoint/test_path1.jpg" - assert url == expected - - def test_should_handle_ai_transformations_with_parameters(self): - """Test AI transformations with custom parameters.""" - url = self.client.helper.build_url( - src="/test_path1.jpg", - url_endpoint="https://ik.imagekit.io/test_url_endpoint", - transformation_position="query", - transformation=[{"ai_drop_shadow": "custom-shadow-params"}], - ) - expected = "https://ik.imagekit.io/test_url_endpoint/test_path1.jpg?tr=e-dropshadow-custom-shadow-params" - assert url == expected - - def test_should_handle_gradient_with_parameters(self): - """Test gradient with custom parameters.""" - url = self.client.helper.build_url( - src="/test_path1.jpg", - url_endpoint="https://ik.imagekit.io/test_url_endpoint", - transformation_position="query", - transformation=[{"gradient": "ld-top_from-green_to-00FF0010_sp-1"}], - ) - expected = ( - "https://ik.imagekit.io/test_url_endpoint/test_path1.jpg?tr=e-gradient-ld-top_from-green_to-00FF0010_sp-1" - ) - assert url == expected - - def test_should_combine_ai_transformations_with_regular_transformations(self): - """Test combining AI and regular transformations.""" - url = self.client.helper.build_url( - src="/test_path1.jpg", - url_endpoint="https://ik.imagekit.io/test_url_endpoint", - transformation_position="query", - transformation=[{"width": 300, "height": 200, "ai_remove_background": True}], - ) - expected = "https://ik.imagekit.io/test_url_endpoint/test_path1.jpg?tr=w-300,h-200,e-bgremove" - assert url == expected - - def test_should_handle_multiple_ai_transformations(self): - """Test multiple AI transformations.""" - url = self.client.helper.build_url( - src="/test_path1.jpg", - url_endpoint="https://ik.imagekit.io/test_url_endpoint", - transformation_position="query", - transformation=[{"ai_remove_background": True, "ai_drop_shadow": True}], - ) - expected = "https://ik.imagekit.io/test_url_endpoint/test_path1.jpg?tr=e-bgremove,e-dropshadow" - assert url == expected - - # Parameter-specific tests - def test_should_generate_the_correct_url_for_width_transformation_when_provided_with_a_number_value(self): - """Test width transformation with number value.""" - url = self.client.helper.build_url( - src="/test_path1.jpg", - url_endpoint="https://ik.imagekit.io/test_url_endpoint", - transformation_position="query", - transformation=[{"width": 400}], - ) - expected = "https://ik.imagekit.io/test_url_endpoint/test_path1.jpg?tr=w-400" - assert url == expected - - def test_should_generate_the_correct_url_for_height_transformation_when_provided_with_a_string_value(self): - """Test height transformation with string value.""" - url = self.client.helper.build_url( - src="/test_path1.jpg", - url_endpoint="https://ik.imagekit.io/test_url_endpoint", - transformation_position="query", - transformation=[{"height": "300"}], - ) - expected = "https://ik.imagekit.io/test_url_endpoint/test_path1.jpg?tr=h-300" - assert url == expected - - def test_should_generate_the_correct_url_for_aspect_ratio_transformation_when_provided_with_colon_format(self): - """Test aspect ratio transformation with colon format.""" - url = self.client.helper.build_url( - src="/test_path1.jpg", - url_endpoint="https://ik.imagekit.io/test_url_endpoint", - transformation_position="query", - transformation=[{"aspect_ratio": "4:3"}], - ) - expected = "https://ik.imagekit.io/test_url_endpoint/test_path1.jpg?tr=ar-4:3" - assert url == expected - - def test_should_generate_the_correct_url_for_quality_transformation_when_provided_with_a_number_value(self): - """Test quality transformation with number value.""" - url = self.client.helper.build_url( - src="/test_path1.jpg", - url_endpoint="https://ik.imagekit.io/test_url_endpoint", - transformation_position="query", - transformation=[{"quality": 80}], - ) - expected = "https://ik.imagekit.io/test_url_endpoint/test_path1.jpg?tr=q-80" - assert url == expected - - # Additional parameter validation tests - def test_should_skip_transformation_parameters_that_are_undefined_or_empty(self): - """Test that undefined/empty parameters are skipped.""" - url = self.client.helper.build_url( - src="/test_path1.jpg", - url_endpoint="https://ik.imagekit.io/test_url_endpoint", - transformation_position="query", - transformation=[{"width": 300}], - ) - expected = "https://ik.imagekit.io/test_url_endpoint/test_path1.jpg?tr=w-300" - assert url == expected - - def test_should_handle_boolean_transformation_values(self): - """Test boolean transformation values.""" - url = self.client.helper.build_url( - src="/test_path1.jpg", - url_endpoint="https://ik.imagekit.io/test_url_endpoint", - transformation_position="query", - transformation=[{"trim": True}], - ) - expected = "https://ik.imagekit.io/test_url_endpoint/test_path1.jpg?tr=t-true" - assert url == expected - - def test_should_handle_transformation_parameter_with_empty_string_value(self): - """Test transformation with empty string value.""" - url = self.client.helper.build_url( - src="/test_path1.jpg", - url_endpoint="https://ik.imagekit.io/test_url_endpoint", - transformation_position="query", - transformation=[{"default_image": ""}], - ) - expected = "https://ik.imagekit.io/test_url_endpoint/test_path1.jpg" - assert url == expected - - def test_should_handle_complex_transformation_combinations(self): - """Test complex transformation combinations.""" - url = self.client.helper.build_url( - src="/test_path1.jpg", - url_endpoint="https://ik.imagekit.io/test_url_endpoint", - transformation_position="query", - transformation=[{"width": 300, "height": 200, "quality": 85, "border": "5_FF0000"}], - ) - expected = "https://ik.imagekit.io/test_url_endpoint/test_path1.jpg?tr=w-300,h-200,q-85,b-5_FF0000" - assert url == expected - - def test_should_handle_radius_with_complex_corner_values(self): - """Test radius transformation with complex corner-specific values.""" - url = self.client.helper.build_url( - src="/test_path1.jpg", - url_endpoint="https://ik.imagekit.io/test_url_endpoint", - transformation_position="query", - transformation=[{"radius": "10_20_20_max"}], - ) - expected = "https://ik.imagekit.io/test_url_endpoint/test_path1.jpg?tr=r-10_20_20_max" - assert url == expected - - def test_should_generate_the_correct_url_with_many_transformations_including_video_and_ai_transforms(self): - """Test many transformations including video and AI.""" - url = self.client.helper.build_url( - src="/test_path.jpg", - url_endpoint="https://ik.imagekit.io/test_url_endpoint", - transformation_position="query", - transformation=[ - { - "height": 300, - "width": 400, - "aspect_ratio": "4-3", - "quality": 40, - "crop": "force", - "crop_mode": "extract", - "focus": "left", - "format": "jpeg", - "radius": 50, - "background": "A94D34", - "border": "5-A94D34", - "rotation": 90, - "blur": 10, - "named": "some_name", - "progressive": True, - "lossless": True, - "trim": 5, - "metadata": True, - "color_profile": True, - "default_image": "/folder/file.jpg/", - "dpr": 3, - "x": 10, - "y": 20, - "x_center": 30, - "y_center": 40, - "flip": "h", - "opacity": 0.8, - "zoom": 2, - "video_codec": "h264", - "audio_codec": "aac", - "start_offset": 5, - "end_offset": 15, - "duration": 10, - "streaming_resolutions": ["1440", "1080"], - "grayscale": True, - "ai_upscale": True, - "ai_retouch": True, - "ai_variation": True, - "ai_drop_shadow": True, - "ai_change_background": "prompt-car", - "ai_edit": "prompt-make it vintage", - "ai_remove_background": True, - "contrast_stretch": True, - "shadow": "bl-15_st-40_x-10_y-N5", - "sharpen": 10, - "unsharp_mask": "2-2-0.8-0.024", - "gradient": "from-red_to-white", - "color_replace": "FF0000_10_0000FF", - "colorize": "co-red_in-50", - "distort": "p-10_20_100_20_100_200_10_200", - "original": True, - "page": "2_4", - "raw": "h-200,w-300,l-image,i-logo.png,l-end", - } - ], - ) - expected = "https://ik.imagekit.io/test_url_endpoint/test_path.jpg?tr=h-300,w-400,ar-4-3,q-40,c-force,cm-extract,fo-left,f-jpeg,r-50,bg-A94D34,b-5-A94D34,rt-90,bl-10,n-some_name,pr-true,lo-true,t-5,md-true,cp-true,di-folder@@file.jpg,dpr-3,x-10,y-20,xc-30,yc-40,fl-h,o-0.8,z-2,vc-h264,ac-aac,so-5,eo-15,du-10,sr-1440_1080,e-grayscale,e-upscale,e-retouch,e-genvar,e-dropshadow,e-changebg-prompt-car,e-edit-prompt-make it vintage,e-bgremove,e-contrast,e-shadow-bl-15_st-40_x-10_y-N5,e-sharpen-10,e-usm-2-2-0.8-0.024,e-gradient-from-red_to-white,cr-FF0000_10_0000FF,e-colorize-co-red_in-50,e-distort-p-10_20_100_20_100_200_10_200,orig-true,pg-2_4,h-200,w-300,l-image,i-logo.png,l-end" - assert url == expected diff --git a/tests/custom/url_generation/test_basic_url_generation.py b/tests/custom/url_generation/test_basic_url_generation.py deleted file mode 100644 index d91614b1..00000000 --- a/tests/custom/url_generation/test_basic_url_generation.py +++ /dev/null @@ -1,261 +0,0 @@ -"""Basic URL generation tests - converted from Ruby SDK.""" - -from typing import TYPE_CHECKING - -import pytest - -from imagekitio import ImageKit - -if TYPE_CHECKING: - from imagekitio._client import ImageKit as ImageKitType - - -class TestBasicURLGeneration: - """Test basic URL generation functionality.""" - - client: "ImageKitType" - - @pytest.fixture(autouse=True) - def setup(self) -> None: - """Set up test client.""" - self.client = ImageKit(private_key="My Private API Key") - - def test_should_return_an_empty_string_when_src_is_not_provided(self) -> None: - """Should return an empty string when src is not provided.""" - url = self.client.helper.build_url( - src="", url_endpoint="https://ik.imagekit.io/test_url_endpoint", transformation_position="query" - ) - - assert url == "" - - def test_should_generate_a_valid_url_when_src_is_slash(self) -> None: - """Should generate a valid URL when src is slash.""" - url = self.client.helper.build_url( - src="/", url_endpoint="https://ik.imagekit.io/test_url_endpoint", transformation_position="query" - ) - - expected = "https://ik.imagekit.io/test_url_endpoint" - assert url == expected - - def test_should_generate_a_valid_url_when_src_is_provided_with_transformation(self) -> None: - """Should generate a valid URL when src is provided with transformation.""" - url = self.client.helper.build_url( - src="/test_path.jpg", - url_endpoint="https://ik.imagekit.io/test_url_endpoint", - transformation_position="query", - ) - - expected = "https://ik.imagekit.io/test_url_endpoint/test_path.jpg" - assert url == expected - - def test_should_generate_a_valid_url_when_a_src_is_provided_without_transformation(self) -> None: - """Should generate a valid URL when a src is provided without transformation.""" - url = self.client.helper.build_url( - src="https://ik.imagekit.io/test_url_endpoint/test_path_alt.jpg", - url_endpoint="https://ik.imagekit.io/test_url_endpoint", - transformation_position="query", - ) - - expected = "https://ik.imagekit.io/test_url_endpoint/test_path_alt.jpg" - assert url == expected - - def test_should_generate_a_valid_url_when_undefined_transformation_parameters_are_provided_with_path(self) -> None: - """Should generate a valid URL when undefined transformation parameters are provided with path.""" - url = self.client.helper.build_url( - src="/test_path_alt.jpg", - url_endpoint="https://ik.imagekit.io/test_url_endpoint", - transformation_position="query", - ) - - expected = "https://ik.imagekit.io/test_url_endpoint/test_path_alt.jpg" - assert url == expected - - def test_by_default_transformation_position_should_be_query(self) -> None: - """By default transformation position should be query.""" - url = self.client.helper.build_url( - src="/test_path.jpg", - url_endpoint="https://ik.imagekit.io/test_url_endpoint", - transformation=[{"height": 300, "width": 400}, {"rotation": 90}], - ) - - expected = "https://ik.imagekit.io/test_url_endpoint/test_path.jpg?tr=h-300,w-400:rt-90" - assert url == expected - - def test_should_generate_the_url_without_sdk_version(self) -> None: - """Should generate the URL without SDK version.""" - url = self.client.helper.build_url( - src="/test_path.jpg", - url_endpoint="https://ik.imagekit.io/test_url_endpoint", - transformation=[{"height": 300, "width": 400}], - transformation_position="path", - ) - - expected = "https://ik.imagekit.io/test_url_endpoint/tr:h-300,w-400/test_path.jpg" - assert url == expected - - def test_should_generate_the_correct_url_with_a_valid_src_and_transformation(self) -> None: - """Should generate the correct URL with a valid src and transformation.""" - url = self.client.helper.build_url( - src="/test_path.jpg", - url_endpoint="https://ik.imagekit.io/test_url_endpoint", - transformation_position="query", - transformation=[{"height": 300, "width": 400}], - ) - - expected = "https://ik.imagekit.io/test_url_endpoint/test_path.jpg?tr=h-300,w-400" - assert url == expected - - def test_should_add_transformation_as_query_when_src_has_absolute_url_even_if_transformation_position_is_path( - self, - ) -> None: - """Should add transformation as query when src has absolute URL even if transformation position is path.""" - url = self.client.helper.build_url( - src="https://my.custom.domain.com/test_path.jpg", - url_endpoint="https://ik.imagekit.io/test_url_endpoint", - transformation_position="path", - transformation=[{"height": 300, "width": 400}], - ) - - expected = "https://my.custom.domain.com/test_path.jpg?tr=h-300,w-400" - assert url == expected - - def test_should_generate_correct_url_when_src_has_query_params(self) -> None: - """Should generate correct URL when src has query params.""" - url = self.client.helper.build_url( - src="https://ik.imagekit.io/imagekit_id/new-endpoint/test_path.jpg?t1=v1", - url_endpoint="https://ik.imagekit.io/imagekit_id/new-endpoint", - transformation_position="query", - transformation=[{"height": 300, "width": 400}], - ) - - expected = "https://ik.imagekit.io/imagekit_id/new-endpoint/test_path.jpg?t1=v1&tr=h-300,w-400" - assert url == expected - - def test_should_generate_the_correct_url_when_the_provided_path_contains_multiple_leading_slashes(self) -> None: - """Should generate the correct URL when the provided path contains multiple leading slashes.""" - url = self.client.helper.build_url( - src="///test_path.jpg", - url_endpoint="https://ik.imagekit.io/test_url_endpoint", - transformation_position="query", - transformation=[{"height": 300, "width": 400}], - ) - - expected = "https://ik.imagekit.io/test_url_endpoint/test_path.jpg?tr=h-300,w-400" - assert url == expected - - def test_should_generate_the_correct_url_when_the_url_endpoint_is_overridden(self) -> None: - """Should generate the correct URL when the URL endpoint is overridden.""" - url = self.client.helper.build_url( - src="/test_path.jpg", - url_endpoint="https://ik.imagekit.io/test_url_endpoint_alt", - transformation_position="query", - transformation=[{"height": 300, "width": 400}], - ) - - expected = "https://ik.imagekit.io/test_url_endpoint_alt/test_path.jpg?tr=h-300,w-400" - assert url == expected - - def test_should_generate_the_correct_url_with_transformation_position_as_query_parameter_when_src_is_provided( - self, - ) -> None: - """Should generate the correct URL with transformation position as query parameter when src is provided.""" - url = self.client.helper.build_url( - src="/test_path.jpg", - url_endpoint="https://ik.imagekit.io/test_url_endpoint", - transformation_position="query", - transformation=[{"height": 300, "width": 400}], - ) - - expected = "https://ik.imagekit.io/test_url_endpoint/test_path.jpg?tr=h-300,w-400" - assert url == expected - - def test_should_generate_the_correct_url_with_a_valid_src_parameter_and_transformation(self) -> None: - """Should generate the correct URL with a valid src parameter and transformation.""" - url = self.client.helper.build_url( - src="https://ik.imagekit.io/test_url_endpoint/test_path_alt.jpg", - url_endpoint="https://ik.imagekit.io/test_url_endpoint", - transformation_position="query", - transformation=[{"height": 300, "width": 400}], - ) - - expected = "https://ik.imagekit.io/test_url_endpoint/test_path_alt.jpg?tr=h-300,w-400" - assert url == expected - - def test_should_merge_query_parameters_correctly_in_the_generated_url(self) -> None: - """Should merge query parameters correctly in the generated URL.""" - url = self.client.helper.build_url( - src="https://ik.imagekit.io/test_url_endpoint/test_path_alt.jpg?t1=v1", - url_endpoint="https://ik.imagekit.io/test_url_endpoint", - transformation_position="query", - query_parameters={"t2": "v2", "t3": "v3"}, - transformation=[{"height": 300, "width": 400}], - ) - - expected = "https://ik.imagekit.io/test_url_endpoint/test_path_alt.jpg?t1=v1&t2=v2&t3=v3&tr=h-300,w-400" - assert url == expected - - def test_should_generate_the_correct_url_with_chained_transformations(self) -> None: - """Should generate the correct URL with chained transformations.""" - url = self.client.helper.build_url( - src="/test_path.jpg", - url_endpoint="https://ik.imagekit.io/test_url_endpoint", - transformation_position="query", - transformation=[{"height": 300, "width": 400}, {"rotation": 90}], - ) - - expected = "https://ik.imagekit.io/test_url_endpoint/test_path.jpg?tr=h-300,w-400:rt-90" - assert url == expected - - def test_should_generate_the_correct_url_with_chained_transformations_including_raw_transformation(self) -> None: - """Should generate the correct URL with chained transformations including raw transformation.""" - url = self.client.helper.build_url( - src="/test_path.jpg", - url_endpoint="https://ik.imagekit.io/test_url_endpoint", - transformation_position="query", - transformation=[{"height": 300, "width": 400}, {"raw": "rndm_trnsf-abcd"}], - ) - - expected = "https://ik.imagekit.io/test_url_endpoint/test_path.jpg?tr=h-300,w-400:rndm_trnsf-abcd" - assert url == expected - - def test_should_generate_the_correct_url_when_border_transformation_is_applied(self) -> None: - """Should generate the correct URL when border transformation is applied.""" - url = self.client.helper.build_url( - src="/test_path.jpg", - url_endpoint="https://ik.imagekit.io/test_url_endpoint", - transformation_position="query", - transformation=[{"height": 300, "width": 400, "border": "20_FF0000"}], - ) - - expected = "https://ik.imagekit.io/test_url_endpoint/test_path.jpg?tr=h-300,w-400,b-20_FF0000" - assert url == expected - - def test_should_generate_the_correct_url_when_transformation_has_empty_key_and_value(self) -> None: - """Should generate the correct URL when transformation has empty key and value.""" - url = self.client.helper.build_url( - src="/test_path.jpg", - url_endpoint="https://ik.imagekit.io/test_url_endpoint", - transformation_position="query", - transformation=[{"raw": ""}], - ) - - expected = "https://ik.imagekit.io/test_url_endpoint/test_path.jpg" - assert url == expected - - def test_should_generate_a_valid_url_when_cname_is_used(self) -> None: - """Should generate a valid URL when CNAME is used.""" - url = self.client.helper.build_url( - src="/test_path.jpg", url_endpoint="https://custom.domain.com", transformation_position="query" - ) - - expected = "https://custom.domain.com/test_path.jpg" - assert url == expected - - def test_should_generate_a_valid_url_when_cname_with_path_is_used(self) -> None: - """Should generate a valid URL when CNAME with path is used.""" - url = self.client.helper.build_url( - src="/test_path.jpg", url_endpoint="https://custom.domain.com/url-pattern", transformation_position="query" - ) - - expected = "https://custom.domain.com/url-pattern/test_path.jpg" - assert url == expected diff --git a/tests/custom/url_generation/test_build_transformation_string.py b/tests/custom/url_generation/test_build_transformation_string.py deleted file mode 100644 index 447bd94c..00000000 --- a/tests/custom/url_generation/test_build_transformation_string.py +++ /dev/null @@ -1,76 +0,0 @@ -"""Build transformation string tests imported from Ruby SDK.""" - -import pytest - -from imagekitio import ImageKit - - -class TestBuildTransformationString: - """Test build_transformation_string matching Ruby SDK build_transformation_string_test.rb.""" - - @pytest.fixture(autouse=True) - def setup(self): - """Setup client for each test.""" - self.client = ImageKit(private_key="test-key") - - def test_should_return_empty_string_for_empty_transformation_array(self): - """Test empty transformation array returns empty string.""" - result = self.client.helper.build_transformation_string(None) - assert result == "" - - result = self.client.helper.build_transformation_string([]) - assert result == "" - - def test_should_generate_transformation_string_for_width_only(self): - """Test transformation string for width only.""" - result = self.client.helper.build_transformation_string([{"width": 300}]) - expected = "w-300" - assert result == expected - - def test_should_generate_transformation_string_for_multiple_parameters(self): - """Test transformation string for multiple parameters.""" - result = self.client.helper.build_transformation_string([{"width": 300, "height": 200}]) - expected = "w-300,h-200" - assert result == expected - - def test_should_generate_transformation_string_for_chained_transformations(self): - """Test transformation string for chained transformations.""" - result = self.client.helper.build_transformation_string([{"width": 300}, {"height": 200}]) - expected = "w-300:h-200" - assert result == expected - - def test_should_handle_empty_transformation_object(self): - """Test empty transformation object.""" - result = self.client.helper.build_transformation_string([{}]) - expected = "" - assert result == expected - - def test_should_handle_transformation_with_overlay(self): - """Test transformation with overlay.""" - result = self.client.helper.build_transformation_string([{"overlay": {"type": "text", "text": "Hello"}}]) - expected = "l-text,i-Hello,l-end" - assert result == expected - - def test_should_handle_raw_transformation_parameter(self): - """Test raw transformation parameter.""" - result = self.client.helper.build_transformation_string([{"raw": "custom-transform-123"}]) - expected = "custom-transform-123" - assert result == expected - - def test_should_handle_mixed_parameters_with_raw(self): - """Test mixed parameters with raw.""" - result = self.client.helper.build_transformation_string([{"width": 300, "raw": "custom-param-123"}]) - expected = "w-300,custom-param-123" - assert result == expected - - def test_should_handle_quality_parameter(self): - """Test quality parameter.""" - result = self.client.helper.build_transformation_string([{"quality": 80}]) - expected = "q-80" - assert result == expected - - def test_should_handle_aspect_ratio_parameter(self): - """Test aspect ratio parameter.""" - result = self.client.helper.build_transformation_string([{"aspect_ratio": "4:3"}]) - expected = "ar-4:3" - assert result == expected diff --git a/tests/custom/url_generation/test_overlay.py b/tests/custom/url_generation/test_overlay.py deleted file mode 100644 index 518ed59e..00000000 --- a/tests/custom/url_generation/test_overlay.py +++ /dev/null @@ -1,505 +0,0 @@ -"""Overlay transformation tests imported from Ruby SDK.""" - -import pytest - -from imagekitio import ImageKit - - -class TestOverlay: - """Test overlay functionality matching Ruby SDK overlay_test.rb.""" - - @pytest.fixture(autouse=True) - def setup(self): - """Setup client for each test.""" - self.client = ImageKit(private_key="My Private API Key") - - # Basic overlay tests - def test_should_ignore_overlay_when_type_property_is_missing(self): - """Test that overlay is ignored when type is missing.""" - url = self.client.helper.build_url( - src="/base-image.jpg", - url_endpoint="https://ik.imagekit.io/test_url_endpoint", - transformation_position="path", - transformation=[{"width": 300}], - ) - expected = "https://ik.imagekit.io/test_url_endpoint/tr:w-300/base-image.jpg" - assert url == expected - - def test_should_ignore_text_overlay_when_text_property_is_missing(self): - """Test that text overlay is ignored when text is empty.""" - url = self.client.helper.build_url( - src="/base-image.jpg", - url_endpoint="https://ik.imagekit.io/test_url_endpoint", - transformation_position="path", - transformation=[{"overlay": {"type": "text", "text": ""}}], - ) - expected = "https://ik.imagekit.io/test_url_endpoint/base-image.jpg" - assert url == expected - - def test_should_ignore_image_overlay_when_input_property_is_missing(self): - """Test that image overlay is ignored when input is empty.""" - url = self.client.helper.build_url( - src="/base-image.jpg", - url_endpoint="https://ik.imagekit.io/test_url_endpoint", - transformation_position="path", - transformation=[{"overlay": {"type": "image", "input": ""}}], - ) - expected = "https://ik.imagekit.io/test_url_endpoint/base-image.jpg" - assert url == expected - - def test_should_ignore_video_overlay_when_input_property_is_missing(self): - """Test that video overlay is ignored when input is empty.""" - url = self.client.helper.build_url( - src="/base-image.jpg", - url_endpoint="https://ik.imagekit.io/test_url_endpoint", - transformation_position="path", - transformation=[{"overlay": {"type": "video", "input": ""}}], - ) - expected = "https://ik.imagekit.io/test_url_endpoint/base-image.jpg" - assert url == expected - - def test_should_ignore_subtitle_overlay_when_input_property_is_missing(self): - """Test that subtitle overlay is ignored when input is empty.""" - url = self.client.helper.build_url( - src="/base-image.jpg", - url_endpoint="https://ik.imagekit.io/test_url_endpoint", - transformation_position="path", - transformation=[{"overlay": {"type": "subtitle", "input": ""}}], - ) - expected = "https://ik.imagekit.io/test_url_endpoint/base-image.jpg" - assert url == expected - - def test_should_ignore_solid_color_overlay_when_color_property_is_missing(self): - """Test that solid color overlay is ignored when color is empty.""" - url = self.client.helper.build_url( - src="/base-image.jpg", - url_endpoint="https://ik.imagekit.io/test_url_endpoint", - transformation_position="path", - transformation=[{"overlay": {"type": "solidColor", "color": ""}}], - ) - expected = "https://ik.imagekit.io/test_url_endpoint/base-image.jpg" - assert url == expected - - # Basic overlay functionality tests - def test_should_generate_url_with_text_overlay_using_url_encoding(self): - """Test text overlay with URL encoding.""" - url = self.client.helper.build_url( - src="/base-image.jpg", - url_endpoint="https://ik.imagekit.io/test_url_endpoint", - transformation_position="path", - transformation=[{"overlay": {"type": "text", "text": "Minimal Text"}}], - ) - expected = "https://ik.imagekit.io/test_url_endpoint/tr:l-text,i-Minimal%20Text,l-end/base-image.jpg" - assert url == expected - - def test_should_generate_url_with_image_overlay_from_input_file(self): - """Test image overlay from input file.""" - url = self.client.helper.build_url( - src="/base-image.jpg", - url_endpoint="https://ik.imagekit.io/test_url_endpoint", - transformation_position="path", - transformation=[{"overlay": {"type": "image", "input": "logo.png"}}], - ) - expected = "https://ik.imagekit.io/test_url_endpoint/tr:l-image,i-logo.png,l-end/base-image.jpg" - assert url == expected - - def test_should_generate_url_with_video_overlay_from_input_file(self): - """Test video overlay from input file.""" - url = self.client.helper.build_url( - src="/base-video.mp4", - url_endpoint="https://ik.imagekit.io/test_url_endpoint", - transformation_position="path", - transformation=[{"overlay": {"type": "video", "input": "play-pause-loop.mp4"}}], - ) - expected = "https://ik.imagekit.io/test_url_endpoint/tr:l-video,i-play-pause-loop.mp4,l-end/base-video.mp4" - assert url == expected - - def test_should_generate_url_with_subtitle_overlay_from_input_file(self): - """Test subtitle overlay from input file.""" - url = self.client.helper.build_url( - src="/base-video.mp4", - url_endpoint="https://ik.imagekit.io/test_url_endpoint", - transformation_position="path", - transformation=[{"overlay": {"type": "subtitle", "input": "subtitle.srt"}}], - ) - expected = "https://ik.imagekit.io/test_url_endpoint/tr:l-subtitles,i-subtitle.srt,l-end/base-video.mp4" - assert url == expected - - def test_should_generate_url_with_solid_color_overlay_using_background_color(self): - """Test solid color overlay.""" - url = self.client.helper.build_url( - src="/base-image.jpg", - url_endpoint="https://ik.imagekit.io/test_url_endpoint", - transformation_position="path", - transformation=[{"overlay": {"type": "solidColor", "color": "FF0000"}}], - ) - expected = "https://ik.imagekit.io/test_url_endpoint/tr:l-image,i-ik_canvas,bg-FF0000,l-end/base-image.jpg" - assert url == expected - - def test_should_generate_url_with_multiple_complex_overlays_including_nested_transformations(self): - """Test complex overlays with nested transformations.""" - url = self.client.helper.build_url( - src="/base-image.jpg", - url_endpoint="https://ik.imagekit.io/test_url_endpoint", - transformation_position="path", - transformation=[ - # Text overlay - { - "overlay": { - "type": "text", - "text": "Every thing", - "position": {"x": "10", "y": "20", "focus": "center"}, - "timing": {"start": 5.0, "duration": "10", "end": 15.0}, - "transformation": [ - { - "width": "bw_mul_0.5", - "font_size": 20.0, - "font_family": "Arial", - "font_color": "0000ff", - "inner_alignment": "left", - "padding": 5.0, - "alpha": 7.0, - "typography": "b", - "background": "red", - "radius": 10.0, - "rotation": "N45", - "flip": "h", - "line_height": 20.0, - } - ], - } - }, - # Image overlay - { - "overlay": { - "type": "image", - "input": "logo.png", - "position": {"x": "10", "y": "20", "focus": "center"}, - "timing": {"start": 5.0, "duration": "10", "end": 15.0}, - "transformation": [ - { - "width": "bw_mul_0.5", - "height": "bh_mul_0.5", - "rotation": "N45", - "flip": "h", - "overlay": {"type": "text", "text": "Nested text overlay"}, - } - ], - } - }, - # Video overlay - { - "overlay": { - "type": "video", - "input": "play-pause-loop.mp4", - "position": {"x": "10", "y": "20", "focus": "center"}, - "timing": {"start": 5.0, "duration": "10", "end": 15.0}, - "transformation": [ - {"width": "bw_mul_0.5", "height": "bh_mul_0.5", "rotation": "N45", "flip": "h"} - ], - } - }, - # Subtitle overlay - { - "overlay": { - "type": "subtitle", - "input": "subtitle.srt", - "position": {"x": "10", "y": "20", "focus": "center"}, - "timing": {"start": 5.0, "duration": "10", "end": 15.0}, - "transformation": [ - { - "background": "red", - "color": "0000ff", - "font_family": "Arial", - "font_outline": "2_A1CCDD50", - "font_shadow": "A1CCDD_3", - } - ], - } - }, - # Solid color overlay - { - "overlay": { - "type": "solidColor", - "color": "FF0000", - "position": {"x": "10", "y": "20", "focus": "center"}, - "timing": {"start": 5.0, "duration": "10", "end": 15.0}, - "transformation": [ - { - "width": "bw_mul_0.5", - "height": "bh_mul_0.5", - "alpha": 0.5, - "background": "red", - "gradient": True, - "radius": "max", - } - ], - } - }, - ], - ) - expected = "https://ik.imagekit.io/test_url_endpoint/tr:l-text,i-Every%20thing,lx-10,ly-20,lfo-center,lso-5,leo-15,ldu-10,w-bw_mul_0.5,fs-20,ff-Arial,co-0000ff,ia-left,pa-5,al-7,tg-b,bg-red,r-10,rt-N45,fl-h,lh-20,l-end:l-image,i-logo.png,lx-10,ly-20,lfo-center,lso-5,leo-15,ldu-10,w-bw_mul_0.5,h-bh_mul_0.5,rt-N45,fl-h,l-text,i-Nested%20text%20overlay,l-end,l-end:l-video,i-play-pause-loop.mp4,lx-10,ly-20,lfo-center,lso-5,leo-15,ldu-10,w-bw_mul_0.5,h-bh_mul_0.5,rt-N45,fl-h,l-end:l-subtitles,i-subtitle.srt,lx-10,ly-20,lfo-center,lso-5,leo-15,ldu-10,bg-red,co-0000ff,ff-Arial,fol-2_A1CCDD50,fsh-A1CCDD_3,l-end:l-image,i-ik_canvas,bg-FF0000,lx-10,ly-20,lfo-center,lso-5,leo-15,ldu-10,w-bw_mul_0.5,h-bh_mul_0.5,al-0.5,bg-red,e-gradient,r-max,l-end/base-image.jpg" - assert url == expected - - # Overlay encoding tests - def test_should_use_plain_encoding_for_simple_image_paths_with_slashes_converted_to_double_at(self): - """Test plain encoding for simple image paths.""" - url = self.client.helper.build_url( - src="/medium_cafe_B1iTdD0C.jpg", - url_endpoint="https://ik.imagekit.io/demo", - transformation_position="path", - transformation=[{"overlay": {"type": "image", "input": "/customer_logo/nykaa.png"}}], - ) - expected = "https://ik.imagekit.io/demo/tr:l-image,i-customer_logo@@nykaa.png,l-end/medium_cafe_B1iTdD0C.jpg" - assert url == expected - - def test_should_use_base64_encoding_for_image_paths_containing_special_characters(self): - """Test base64 encoding for image paths with special characters.""" - url = self.client.helper.build_url( - src="/medium_cafe_B1iTdD0C.jpg", - url_endpoint="https://ik.imagekit.io/demo", - transformation_position="path", - transformation=[{"overlay": {"type": "image", "input": "/customer_logo/Ñykaa.png"}}], - ) - expected = "https://ik.imagekit.io/demo/tr:l-image,ie-Y3VzdG9tZXJfbG9nby%2FDkXlrYWEucG5n,l-end/medium_cafe_B1iTdD0C.jpg" - assert url == expected - - def test_should_use_plain_encoding_for_simple_text_overlays(self): - """Test plain encoding for simple text.""" - url = self.client.helper.build_url( - src="/sample.jpg", - url_endpoint="https://ik.imagekit.io/demo", - transformation_position="path", - transformation=[{"overlay": {"type": "text", "text": "HelloWorld"}}], - ) - expected = "https://ik.imagekit.io/demo/tr:l-text,i-HelloWorld,l-end/sample.jpg" - assert url == expected - - def test_should_convert_slashes_to_double_at_in_font_family_paths_for_custom_fonts(self): - """Test font family path conversion.""" - url = self.client.helper.build_url( - src="/medium_cafe_B1iTdD0C.jpg", - url_endpoint="https://ik.imagekit.io/demo", - transformation_position="path", - transformation=[ - { - "overlay": { - "type": "text", - "text": "Manu", - "transformation": [{"font_family": "nested-path/Poppins-Regular_Q15GrYWmL.ttf"}], - } - } - ], - ) - expected = "https://ik.imagekit.io/demo/tr:l-text,i-Manu,ff-nested-path@@Poppins-Regular_Q15GrYWmL.ttf,l-end/medium_cafe_B1iTdD0C.jpg" - assert url == expected - - def test_should_use_url_encoding_for_text_overlays_with_spaces_and_safe_characters(self): - """Test URL encoding for text with spaces.""" - url = self.client.helper.build_url( - src="/sample.jpg", - url_endpoint="https://ik.imagekit.io/demo", - transformation_position="path", - transformation=[{"overlay": {"type": "text", "text": "Hello World"}}], - ) - expected = "https://ik.imagekit.io/demo/tr:l-text,i-Hello%20World,l-end/sample.jpg" - assert url == expected - - def test_should_use_base64_encoding_for_text_overlays_with_special_unicode_characters(self): - """Test base64 encoding for Unicode text.""" - url = self.client.helper.build_url( - src="/sample.jpg", - url_endpoint="https://ik.imagekit.io/demo", - transformation_position="path", - transformation=[{"overlay": {"type": "text", "text": "हिन्दी"}}], - ) - expected = "https://ik.imagekit.io/demo/tr:l-text,ie-4KS54KS%2F4KSo4KWN4KSm4KWA,l-end/sample.jpg" - assert url == expected - - def test_should_use_plain_encoding_when_explicitly_specified_for_text_overlay(self): - """Test explicit plain encoding for text.""" - url = self.client.helper.build_url( - src="/sample.jpg", - url_endpoint="https://ik.imagekit.io/demo", - transformation_position="path", - transformation=[{"overlay": {"type": "text", "text": "HelloWorld", "encoding": "plain"}}], - ) - expected = "https://ik.imagekit.io/demo/tr:l-text,i-HelloWorld,l-end/sample.jpg" - assert url == expected - - def test_should_use_base64_encoding_when_explicitly_specified_for_text_overlay(self): - """Test explicit base64 encoding for text.""" - url = self.client.helper.build_url( - src="/sample.jpg", - url_endpoint="https://ik.imagekit.io/demo", - transformation_position="path", - transformation=[{"overlay": {"type": "text", "text": "HelloWorld", "encoding": "base64"}}], - ) - expected = "https://ik.imagekit.io/demo/tr:l-text,ie-SGVsbG9Xb3JsZA%3D%3D,l-end/sample.jpg" - assert url == expected - - def test_should_use_plain_encoding_when_explicitly_specified_for_image_overlay(self): - """Test explicit plain encoding for image.""" - url = self.client.helper.build_url( - src="/sample.jpg", - url_endpoint="https://ik.imagekit.io/demo", - transformation_position="path", - transformation=[{"overlay": {"type": "image", "input": "/customer/logo.png", "encoding": "plain"}}], - ) - expected = "https://ik.imagekit.io/demo/tr:l-image,i-customer@@logo.png,l-end/sample.jpg" - assert url == expected - - def test_should_use_base64_encoding_when_explicitly_specified_for_image_overlay(self): - """Test explicit base64 encoding for image.""" - url = self.client.helper.build_url( - src="/sample.jpg", - url_endpoint="https://ik.imagekit.io/demo", - transformation_position="path", - transformation=[{"overlay": {"type": "image", "input": "/customer/logo.png", "encoding": "base64"}}], - ) - expected = "https://ik.imagekit.io/demo/tr:l-image,ie-Y3VzdG9tZXIvbG9nby5wbmc%3D,l-end/sample.jpg" - assert url == expected - - def test_should_use_base64_encoding_when_explicitly_specified_for_video_overlay(self): - """Test explicit base64 encoding for video.""" - url = self.client.helper.build_url( - src="/sample.mp4", - url_endpoint="https://ik.imagekit.io/demo", - transformation_position="path", - transformation=[{"overlay": {"type": "video", "input": "/path/to/video.mp4", "encoding": "base64"}}], - ) - expected = "https://ik.imagekit.io/demo/tr:l-video,ie-cGF0aC90by92aWRlby5tcDQ%3D,l-end/sample.mp4" - assert url == expected - - def test_should_use_base64_encoding_when_explicitly_specified_for_subtitle_overlay(self): - """Test explicit base64 encoding for subtitle.""" - url = self.client.helper.build_url( - src="/sample.mp4", - url_endpoint="https://ik.imagekit.io/demo", - transformation_position="path", - transformation=[{"overlay": {"type": "subtitle", "input": "sub.srt", "encoding": "base64"}}], - ) - expected = "https://ik.imagekit.io/demo/tr:l-subtitles,ie-c3ViLnNydA%3D%3D,l-end/sample.mp4" - assert url == expected - - def test_should_use_plain_encoding_when_explicitly_specified_for_subtitle_overlay(self): - """Test explicit plain encoding for subtitle overlay.""" - url = self.client.helper.build_url( - src="/sample.mp4", - url_endpoint="https://ik.imagekit.io/demo", - transformation_position="path", - transformation=[{"overlay": {"type": "subtitle", "input": "/sub.srt", "encoding": "plain"}}], - ) - expected = "https://ik.imagekit.io/demo/tr:l-subtitles,i-sub.srt,l-end/sample.mp4" - assert url == expected - - def test_should_properly_encode_overlay_text_when_transformations_are_in_query_parameters(self): - """Test text overlay encoding with query position.""" - url = self.client.helper.build_url( - src="/sample.jpg", - url_endpoint="https://ik.imagekit.io/demo", - transformation_position="query", - transformation=[{"overlay": {"type": "text", "text": "Minimal Text"}}], - ) - expected = "https://ik.imagekit.io/demo/sample.jpg?tr=l-text,i-Minimal%20Text,l-end" - assert url == expected - - # Layer mode tests - def test_should_handle_layer_mode_multiply_in_overlay(self): - """Test layer mode multiply in image overlay.""" - url = self.client.helper.build_url( - src="/base-image.jpg", - url_endpoint="https://ik.imagekit.io/test_url_endpoint", - transformation_position="path", - transformation=[ - { - "overlay": { - "type": "image", - "input": "overlay.png", - "layer_mode": "multiply", - } - } - ], - ) - expected = "https://ik.imagekit.io/test_url_endpoint/tr:l-image,i-overlay.png,lm-multiply,l-end/base-image.jpg" - assert url == expected - - def test_should_handle_layer_mode_cutter_in_overlay(self): - """Test layer mode cutter in image overlay.""" - url = self.client.helper.build_url( - src="/base-image.jpg", - url_endpoint="https://ik.imagekit.io/test_url_endpoint", - transformation_position="path", - transformation=[ - { - "overlay": { - "type": "image", - "input": "overlay.png", - "layer_mode": "cutter", - } - } - ], - ) - expected = "https://ik.imagekit.io/test_url_endpoint/tr:l-image,i-overlay.png,lm-cutter,l-end/base-image.jpg" - assert url == expected - - def test_should_handle_layer_mode_cutout_in_overlay(self): - """Test layer mode cutout in image overlay.""" - url = self.client.helper.build_url( - src="/base-image.jpg", - url_endpoint="https://ik.imagekit.io/test_url_endpoint", - transformation_position="path", - transformation=[ - { - "overlay": { - "type": "image", - "input": "overlay.png", - "layer_mode": "cutout", - } - } - ], - ) - expected = "https://ik.imagekit.io/test_url_endpoint/tr:l-image,i-overlay.png,lm-cutout,l-end/base-image.jpg" - assert url == expected - - def test_should_handle_layer_mode_displace_in_overlay(self): - """Test layer mode displace in image overlay.""" - url = self.client.helper.build_url( - src="/base-image.jpg", - url_endpoint="https://ik.imagekit.io/test_url_endpoint", - transformation_position="path", - transformation=[ - { - "overlay": { - "type": "image", - "input": "overlay.png", - "layer_mode": "displace", - } - } - ], - ) - expected = "https://ik.imagekit.io/test_url_endpoint/tr:l-image,i-overlay.png,lm-displace,l-end/base-image.jpg" - assert url == expected - - def test_should_handle_x_center_y_center_and_anchor_point_in_overlay_position(self): - """Test xCenter, yCenter and anchorPoint in overlay position.""" - url = self.client.helper.build_url( - src="/base-image.jpg", - url_endpoint="https://ik.imagekit.io/test_url_endpoint", - transformation_position="path", - transformation=[ - { - "overlay": { - "type": "text", - "text": "Centered Text", - "position": { - "x_center": "bw_mul_0.5", - "y_center": "bh_mul_0.5", - "anchor_point": "top_left", - }, - } - } - ], - ) - expected = "https://ik.imagekit.io/test_url_endpoint/tr:l-text,i-Centered%20Text,lxc-bw_mul_0.5,lyc-bh_mul_0.5,lap-top_left,l-end/base-image.jpg" - assert url == expected diff --git a/tests/custom/url_generation/test_signing.py b/tests/custom/url_generation/test_signing.py deleted file mode 100644 index 8df8b6d3..00000000 --- a/tests/custom/url_generation/test_signing.py +++ /dev/null @@ -1,168 +0,0 @@ -"""Signing URL tests - converted from Ruby SDK.""" - -from typing import TYPE_CHECKING - -import pytest - -from imagekitio import ImageKit - -if TYPE_CHECKING: - from imagekitio._client import ImageKit as ImageKitType - - -class TestSigning: - """Test URL signing functionality.""" - - client: "ImageKitType" - - @pytest.fixture(autouse=True) - def setup(self) -> None: - """Set up test client.""" - self.client = ImageKit(private_key="dummy-key") - - def test_should_generate_a_signed_url_when_signed_is_true_without_expires_in(self) -> None: - """Should generate a signed URL when signed is true without expires_in.""" - url = self.client.helper.build_url( - src="sdk-testing-files/future-search.png", url_endpoint="https://ik.imagekit.io/demo/", signed=True - ) - - expected = "https://ik.imagekit.io/demo/sdk-testing-files/future-search.png?ik-s=32dbbbfc5f945c0403c71b54c38e76896ef2d6b0" - assert url == expected - - def test_should_generate_a_signed_url_when_signed_is_true_with_expires_in(self) -> None: - """Should generate a signed URL when signed is true with expires_in.""" - url = self.client.helper.build_url( - src="sdk-testing-files/future-search.png", - url_endpoint="https://ik.imagekit.io/demo/", - signed=True, - expires_in=3600, - ) - - # Expect ik-t exist in the URL. We don't assert signature because it will keep changing. - assert "ik-t" in url - - def test_should_generate_a_signed_url_when_expires_in_is_above_0_and_even_if_signed_is_false(self) -> None: - """Should generate a signed URL when expires_in is above 0 and even if signed is false.""" - url = self.client.helper.build_url( - src="sdk-testing-files/future-search.png", - url_endpoint="https://ik.imagekit.io/demo/", - signed=False, - expires_in=3600, - ) - - # Expect ik-t exist in the URL. We don't assert signature because it will keep changing. - assert "ik-t" in url - - def test_should_generate_signed_url_with_special_characters_in_filename(self) -> None: - """Should generate signed URL with special characters in filename.""" - url = self.client.helper.build_url( - src="sdk-testing-files/हिन्दी.png", url_endpoint="https://ik.imagekit.io/demo/", signed=True - ) - - expected = "https://ik.imagekit.io/demo/sdk-testing-files/%E0%A4%B9%E0%A4%BF%E0%A4%A8%E0%A5%8D%E0%A4%A6%E0%A5%80.png?ik-s=3fff2f31da1f45e007adcdbe95f88c8c330e743c" - assert url == expected - - def test_should_generate_signed_url_with_text_overlay_containing_special_characters(self) -> None: - """Should generate signed URL with text overlay containing special characters.""" - url = self.client.helper.build_url( - src="sdk-testing-files/हिन्दी.png", - url_endpoint="https://ik.imagekit.io/demo/", - transformation=[ - { - "overlay": { - "type": "text", - "text": "हिन्दी", - "transformation": [ - { - "font_color": "red", - "font_size": "32", - "font_family": "sdk-testing-files/Poppins-Regular_Q15GrYWmL.ttf", - } - ], - } - } - ], - signed=True, - ) - - expected = "https://ik.imagekit.io/demo/sdk-testing-files/%E0%A4%B9%E0%A4%BF%E0%A4%A8%E0%A5%8D%E0%A4%A6%E0%A5%80.png?tr=l-text,ie-4KS54KS%2F4KSo4KWN4KSm4KWA,co-red,fs-32,ff-sdk-testing-files@@Poppins-Regular_Q15GrYWmL.ttf,l-end&ik-s=ac9f24a03080102555e492185533c1ae6bd93fa7" - assert url == expected - - def test_should_generate_signed_url_with_text_overlay_and_special_characters_using_path_transformation_position( - self, - ) -> None: - """Should generate signed URL with text overlay and special characters using path transformation position.""" - url = self.client.helper.build_url( - src="sdk-testing-files/हिन्दी.png", - url_endpoint="https://ik.imagekit.io/demo/", - transformation_position="path", - transformation=[ - { - "overlay": { - "type": "text", - "text": "हिन्दी", - "transformation": [ - { - "font_color": "red", - "font_size": "32", - "font_family": "sdk-testing-files/Poppins-Regular_Q15GrYWmL.ttf", - } - ], - } - } - ], - signed=True, - ) - - expected = "https://ik.imagekit.io/demo/tr:l-text,ie-4KS54KS%2F4KSo4KWN4KSm4KWA,co-red,fs-32,ff-sdk-testing-files@@Poppins-Regular_Q15GrYWmL.ttf,l-end/sdk-testing-files/%E0%A4%B9%E0%A4%BF%E0%A4%A8%E0%A5%8D%E0%A4%A6%E0%A5%80.png?ik-s=69f2ecbb7364bbbad24616e1f7f1bac5a560fc71" - assert url == expected - - def test_should_generate_signed_url_with_query_parameters(self) -> None: - """Should generate signed URL with query parameters.""" - url = self.client.helper.build_url( - src="sdk-testing-files/future-search.png", - url_endpoint="https://ik.imagekit.io/demo/", - query_parameters={"version": "1.0", "cache": "false"}, - signed=True, - ) - - expected = "https://ik.imagekit.io/demo/sdk-testing-files/future-search.png?version=1.0&cache=false&ik-s=f2e5a1b8b6a0b03fd63789dfc6413a94acef9fd8" - assert url == expected - - def test_should_generate_signed_url_with_transformations_and_query_parameters(self) -> None: - """Should generate signed URL with transformations and query parameters.""" - url = self.client.helper.build_url( - src="sdk-testing-files/future-search.png", - url_endpoint="https://ik.imagekit.io/demo/", - transformation=[{"width": 300, "height": 200}], - query_parameters={"version": "2.0"}, - signed=True, - ) - - expected = "https://ik.imagekit.io/demo/sdk-testing-files/future-search.png?version=2.0&tr=w-300,h-200&ik-s=601d97a7834b7554f4dabf0d3fc3a219ceeb6b31" - assert url == expected - - def test_should_not_sign_url_when_signed_is_false(self) -> None: - """Should not sign URL when signed is false.""" - url = self.client.helper.build_url( - src="sdk-testing-files/future-search.png", url_endpoint="https://ik.imagekit.io/demo/", signed=False - ) - - expected = "https://ik.imagekit.io/demo/sdk-testing-files/future-search.png" - assert url == expected - assert "ik-s=" not in url - assert "ik-t=" not in url - - def test_should_generate_signed_url_with_transformations_in_path_position_and_query_parameters(self) -> None: - """Should generate signed URL with transformations in path position and query parameters.""" - url = self.client.helper.build_url( - src="sdk-testing-files/future-search.png", - url_endpoint="https://ik.imagekit.io/demo/", - transformation=[{"width": 300, "height": 200}], - transformation_position="path", - query_parameters={"version": "2.0"}, - signed=True, - ) - - expected = "https://ik.imagekit.io/demo/tr:w-300,h-200/sdk-testing-files/future-search.png?version=2.0&ik-s=dd1ee8f83d019bc59fd57a5fc4674a11eb8a3496" - assert url == expected