initial commit after automated creating
This commit is contained in:
commit
1cf124a5a3
48 changed files with 12041 additions and 0 deletions
14
.env.example
Normal file
14
.env.example
Normal file
|
|
@ -0,0 +1,14 @@
|
||||||
|
# Copy this file to .env and fill in your values.
|
||||||
|
# All variables are prefixed with AGRAVITY_.
|
||||||
|
|
||||||
|
# Required: your x-functions-key API key
|
||||||
|
AGRAVITY_API_KEY=your-api-key-here
|
||||||
|
|
||||||
|
# Optional: override the base API URL
|
||||||
|
# AGRAVITY_BASE_URL=https://devagravitypublic.azurewebsites.net/api
|
||||||
|
|
||||||
|
# Optional: HTTP request timeout in seconds
|
||||||
|
# AGRAVITY_TIMEOUT=60.0
|
||||||
|
|
||||||
|
# Optional: set to false to disable SSL certificate verification (not recommended)
|
||||||
|
# AGRAVITY_VERIFY_SSL=true
|
||||||
37
.gitignore
vendored
Normal file
37
.gitignore
vendored
Normal file
|
|
@ -0,0 +1,37 @@
|
||||||
|
# Python
|
||||||
|
__pycache__/
|
||||||
|
*.py[cod]
|
||||||
|
*.pyo
|
||||||
|
*.pyd
|
||||||
|
*.egg
|
||||||
|
*.egg-info/
|
||||||
|
dist/
|
||||||
|
build/
|
||||||
|
.eggs/
|
||||||
|
|
||||||
|
# Virtual environments
|
||||||
|
.venv/
|
||||||
|
venv/
|
||||||
|
env/
|
||||||
|
|
||||||
|
# Type checking
|
||||||
|
.mypy_cache/
|
||||||
|
.dmypy.json
|
||||||
|
dmypy.json
|
||||||
|
|
||||||
|
# Linting
|
||||||
|
.ruff_cache/
|
||||||
|
|
||||||
|
# Environment files
|
||||||
|
.env
|
||||||
|
|
||||||
|
# pytest
|
||||||
|
.pytest_cache/
|
||||||
|
.coverage
|
||||||
|
htmlcov/
|
||||||
|
|
||||||
|
# VS Code
|
||||||
|
.vscode/
|
||||||
|
|
||||||
|
# macOS
|
||||||
|
.DS_Store
|
||||||
141
README.md
Normal file
141
README.md
Normal file
|
|
@ -0,0 +1,141 @@
|
||||||
|
# agravity-client
|
||||||
|
|
||||||
|
A fully **Pythonic, Pydantic v2–driven, async** REST client for the
|
||||||
|
[Agravity DAM API](https://agravity.io) (v10.3.0).
|
||||||
|
|
||||||
|
Built on [`httpx`](https://www.python-httpx.org/) and
|
||||||
|
[Pydantic](https://docs.pydantic.dev/), this library gives you:
|
||||||
|
|
||||||
|
- **Typed return values** – every endpoint returns a validated Pydantic model.
|
||||||
|
- **Async-first** – all network calls are `async def` using `httpx.AsyncClient`.
|
||||||
|
- **Context-manager support** – clean connection lifecycle.
|
||||||
|
- **Auto-configuration** – reads `AGRAVITY_*` env vars or a `.env` file via
|
||||||
|
`pydantic-settings`.
|
||||||
|
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
```bash
|
||||||
|
pip install agravity-client
|
||||||
|
```
|
||||||
|
|
||||||
|
_Or in development mode:_
|
||||||
|
|
||||||
|
```bash
|
||||||
|
pip install -e ".[dev]"
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
## Quick start
|
||||||
|
|
||||||
|
```python
|
||||||
|
import asyncio
|
||||||
|
from agravity_client import AgravityClient, AgravityConfig
|
||||||
|
|
||||||
|
async def main():
|
||||||
|
config = AgravityConfig(api_key="YOUR_API_KEY") # or set AGRAVITY_API_KEY env var
|
||||||
|
|
||||||
|
async with AgravityClient(config) as client:
|
||||||
|
# ------------------------------------------------------------------
|
||||||
|
# Version & capabilities
|
||||||
|
# ------------------------------------------------------------------
|
||||||
|
version = await client.general.get_version()
|
||||||
|
print(version.version)
|
||||||
|
|
||||||
|
# ------------------------------------------------------------------
|
||||||
|
# List assets in a collection
|
||||||
|
# ------------------------------------------------------------------
|
||||||
|
page = await client.assets.list_assets(
|
||||||
|
collection_id="your-collection-id",
|
||||||
|
limit=25,
|
||||||
|
)
|
||||||
|
for asset in page.assets or []:
|
||||||
|
print(asset.id, asset.name)
|
||||||
|
|
||||||
|
# ------------------------------------------------------------------
|
||||||
|
# Upload an asset
|
||||||
|
# ------------------------------------------------------------------
|
||||||
|
with open("photo.jpg", "rb") as fh:
|
||||||
|
new_asset = await client.assets.upload_asset(
|
||||||
|
fh.read(),
|
||||||
|
"photo.jpg",
|
||||||
|
name="My photo",
|
||||||
|
collection_id="your-collection-id",
|
||||||
|
)
|
||||||
|
print("Created:", new_asset.id)
|
||||||
|
|
||||||
|
# ------------------------------------------------------------------
|
||||||
|
# Search
|
||||||
|
# ------------------------------------------------------------------
|
||||||
|
from agravity_client.models import AzSearchOptions
|
||||||
|
results = await client.search.search(
|
||||||
|
AzSearchOptions(searchterm="sunset", limit=10)
|
||||||
|
)
|
||||||
|
print(results.count, "results")
|
||||||
|
|
||||||
|
asyncio.run(main())
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
## Configuration
|
||||||
|
|
||||||
|
| Setting | Env variable | Default |
|
||||||
|
|---------|-------------|---------|
|
||||||
|
| `base_url` | `AGRAVITY_BASE_URL` | `https://devagravitypublic.azurewebsites.net/api` |
|
||||||
|
| `api_key` | `AGRAVITY_API_KEY` | _(empty)_ |
|
||||||
|
| `timeout` | `AGRAVITY_TIMEOUT` | `60.0` |
|
||||||
|
| `verify_ssl` | `AGRAVITY_VERIFY_SSL` | `true` |
|
||||||
|
|
||||||
|
Copy `.env.example` to `.env` and fill in your values.
|
||||||
|
|
||||||
|
|
||||||
|
## API modules
|
||||||
|
|
||||||
|
| Attribute | Endpoints covered |
|
||||||
|
|-----------|-------------------|
|
||||||
|
| `client.assets` | `/assets`, `/assetsupload`, `/assetsbulkupdate`, all sub-resources |
|
||||||
|
| `client.collections` | `/collections` CRUD, ancestors, descendants, preview, bynames |
|
||||||
|
| `client.collection_types` | `/collectiontypes` |
|
||||||
|
| `client.relations` | `/relations`, `/assetrelationtypes` |
|
||||||
|
| `client.search` | `/search`, `/search/facette`, `/search/adminstatus`, `/savedsearch` |
|
||||||
|
| `client.sharing` | `/sharing`, `/sharing/quickshares` |
|
||||||
|
| `client.portals` | `/portals` |
|
||||||
|
| `client.workspaces` | `/workspaces` |
|
||||||
|
| `client.auth` | `/auth/containerwrite`, `/auth/inbox`, `/auth/users` |
|
||||||
|
| `client.download_formats` | `/downloadformats` |
|
||||||
|
| `client.static_lists` | `/staticdefinedlists` |
|
||||||
|
| `client.translations` | `/translations` |
|
||||||
|
| `client.publishing` | `/publish` |
|
||||||
|
| `client.secure_upload` | `/secureupload` |
|
||||||
|
| `client.helper` | `/helper/*` |
|
||||||
|
| `client.webappdata` | `/webappdata` |
|
||||||
|
| `client.general` | `/version`, `/deleted`, `/durable`, `/negotiate`, `/public`, `/config` |
|
||||||
|
| `client.ai` | `/ai/reverseassetsearch` |
|
||||||
|
|
||||||
|
|
||||||
|
## Development
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Create and activate a virtual environment
|
||||||
|
python -m venv .venv
|
||||||
|
.venv\Scripts\activate # Windows
|
||||||
|
# source .venv/bin/activate # Unix
|
||||||
|
|
||||||
|
# Install in editable mode with all dev dependencies
|
||||||
|
pip install -e ".[dev]"
|
||||||
|
|
||||||
|
# Lint & format
|
||||||
|
ruff check .
|
||||||
|
ruff format .
|
||||||
|
|
||||||
|
# Type-check
|
||||||
|
mypy agravity_client
|
||||||
|
|
||||||
|
# Run tests
|
||||||
|
pytest
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
## Licence
|
||||||
|
|
||||||
|
MIT
|
||||||
120
agravity_client.code-workspace
Normal file
120
agravity_client.code-workspace
Normal file
|
|
@ -0,0 +1,120 @@
|
||||||
|
{
|
||||||
|
"folders": [
|
||||||
|
{
|
||||||
|
"path": "."
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"settings": {
|
||||||
|
// Python environment
|
||||||
|
"python.defaultInterpreterPath": "${workspaceFolder}/.venv/Scripts/python.exe",
|
||||||
|
"python.terminal.activateEnvironment": true,
|
||||||
|
|
||||||
|
// Editor
|
||||||
|
"editor.formatOnSave": true,
|
||||||
|
"editor.rulers": [100],
|
||||||
|
"editor.tabSize": 4,
|
||||||
|
|
||||||
|
// Ruff (linter + formatter)
|
||||||
|
"[python]": {
|
||||||
|
"editor.defaultFormatter": "charliermarsh.ruff",
|
||||||
|
"editor.formatOnSave": true,
|
||||||
|
"editor.codeActionsOnSave": {
|
||||||
|
"source.fixAll.ruff": "explicit",
|
||||||
|
"source.organizeImports.ruff": "explicit"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
// Pylance / Pyright
|
||||||
|
"python.analysis.typeCheckingMode": "basic",
|
||||||
|
"python.analysis.autoImportCompletions": true,
|
||||||
|
"python.analysis.indexing": true,
|
||||||
|
|
||||||
|
// Test discovery
|
||||||
|
"python.testing.pytestEnabled": true,
|
||||||
|
"python.testing.unittestEnabled": false,
|
||||||
|
"python.testing.pytestArgs": ["tests"],
|
||||||
|
|
||||||
|
// File associations
|
||||||
|
"files.associations": {
|
||||||
|
"*.env.example": "dotenv"
|
||||||
|
},
|
||||||
|
|
||||||
|
// Exclude noise from explorer
|
||||||
|
"files.exclude": {
|
||||||
|
"**/__pycache__": true,
|
||||||
|
"**/*.pyc": true,
|
||||||
|
"**/.mypy_cache": true,
|
||||||
|
"**/.ruff_cache": true,
|
||||||
|
"**/*.egg-info": true,
|
||||||
|
".venv": false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"extensions": {
|
||||||
|
"recommendations": [
|
||||||
|
"ms-python.python",
|
||||||
|
"ms-python.vscode-pylance",
|
||||||
|
"charliermarsh.ruff",
|
||||||
|
"ms-python.mypy-type-checker",
|
||||||
|
"tamasfe.even-better-toml",
|
||||||
|
"mikestead.dotenv"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"launch": {
|
||||||
|
"version": "0.2.0",
|
||||||
|
"configurations": [
|
||||||
|
{
|
||||||
|
"name": "Python: Current File",
|
||||||
|
"type": "debugpy",
|
||||||
|
"request": "launch",
|
||||||
|
"program": "${file}",
|
||||||
|
"console": "integratedTerminal",
|
||||||
|
"justMyCode": true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"tasks": {
|
||||||
|
"version": "2.0.0",
|
||||||
|
"tasks": [
|
||||||
|
{
|
||||||
|
"label": "Install (editable)",
|
||||||
|
"type": "shell",
|
||||||
|
"command": "pip install -e \".[dev]\"",
|
||||||
|
"group": "build",
|
||||||
|
"presentation": { "reveal": "always" },
|
||||||
|
"problemMatcher": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "Ruff: lint",
|
||||||
|
"type": "shell",
|
||||||
|
"command": "ruff check .",
|
||||||
|
"group": "test",
|
||||||
|
"presentation": { "reveal": "always" },
|
||||||
|
"problemMatcher": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "Ruff: format",
|
||||||
|
"type": "shell",
|
||||||
|
"command": "ruff format .",
|
||||||
|
"group": "build",
|
||||||
|
"presentation": { "reveal": "always" },
|
||||||
|
"problemMatcher": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "mypy: type-check",
|
||||||
|
"type": "shell",
|
||||||
|
"command": "mypy agravity_client",
|
||||||
|
"group": "test",
|
||||||
|
"presentation": { "reveal": "always" },
|
||||||
|
"problemMatcher": ["$mypy"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "pytest",
|
||||||
|
"type": "shell",
|
||||||
|
"command": "pytest -v",
|
||||||
|
"group": { "kind": "test", "isDefault": true },
|
||||||
|
"presentation": { "reveal": "always" },
|
||||||
|
"problemMatcher": []
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
29
agravity_client/__init__.py
Normal file
29
agravity_client/__init__.py
Normal file
|
|
@ -0,0 +1,29 @@
|
||||||
|
"""Agravity DAM Python client – public API surface."""
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from agravity_client.client import AgravityClient
|
||||||
|
from agravity_client.config import AgravityConfig
|
||||||
|
from agravity_client.exceptions import (
|
||||||
|
AgravityAPIError,
|
||||||
|
AgravityAuthenticationError,
|
||||||
|
AgravityConnectionError,
|
||||||
|
AgravityError,
|
||||||
|
AgravityNotFoundError,
|
||||||
|
AgravityTimeoutError,
|
||||||
|
AgravityValidationError,
|
||||||
|
)
|
||||||
|
from agravity_client.models import * # noqa: F403 – re-export all model symbols
|
||||||
|
|
||||||
|
__version__ = "0.1.0"
|
||||||
|
__all__ = [
|
||||||
|
"AgravityClient",
|
||||||
|
"AgravityConfig",
|
||||||
|
# Exceptions
|
||||||
|
"AgravityError",
|
||||||
|
"AgravityAPIError",
|
||||||
|
"AgravityAuthenticationError",
|
||||||
|
"AgravityConnectionError",
|
||||||
|
"AgravityNotFoundError",
|
||||||
|
"AgravityTimeoutError",
|
||||||
|
"AgravityValidationError",
|
||||||
|
]
|
||||||
42
agravity_client/api/__init__.py
Normal file
42
agravity_client/api/__init__.py
Normal file
|
|
@ -0,0 +1,42 @@
|
||||||
|
"""Agravity API client sub-modules."""
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from agravity_client.api.ai import AiApi
|
||||||
|
from agravity_client.api.assets import AssetsApi
|
||||||
|
from agravity_client.api.auth import AuthApi
|
||||||
|
from agravity_client.api.collection_types import CollectionTypesApi
|
||||||
|
from agravity_client.api.collections import CollectionsApi
|
||||||
|
from agravity_client.api.download_formats import DownloadFormatsApi
|
||||||
|
from agravity_client.api.general import GeneralApi
|
||||||
|
from agravity_client.api.helper import HelperApi
|
||||||
|
from agravity_client.api.portals import PortalsApi
|
||||||
|
from agravity_client.api.publishing import PublishingApi
|
||||||
|
from agravity_client.api.relations import RelationsApi
|
||||||
|
from agravity_client.api.search import SearchApi
|
||||||
|
from agravity_client.api.secure_upload import SecureUploadApi
|
||||||
|
from agravity_client.api.sharing import SharingApi
|
||||||
|
from agravity_client.api.static_lists import StaticListsApi
|
||||||
|
from agravity_client.api.translations import TranslationsApi
|
||||||
|
from agravity_client.api.webappdata import WebAppDataApi
|
||||||
|
from agravity_client.api.workspaces import WorkspacesApi
|
||||||
|
|
||||||
|
__all__ = [
|
||||||
|
"AiApi",
|
||||||
|
"AssetsApi",
|
||||||
|
"AuthApi",
|
||||||
|
"CollectionTypesApi",
|
||||||
|
"CollectionsApi",
|
||||||
|
"DownloadFormatsApi",
|
||||||
|
"GeneralApi",
|
||||||
|
"HelperApi",
|
||||||
|
"PortalsApi",
|
||||||
|
"PublishingApi",
|
||||||
|
"RelationsApi",
|
||||||
|
"SearchApi",
|
||||||
|
"SecureUploadApi",
|
||||||
|
"SharingApi",
|
||||||
|
"StaticListsApi",
|
||||||
|
"TranslationsApi",
|
||||||
|
"WebAppDataApi",
|
||||||
|
"WorkspacesApi",
|
||||||
|
]
|
||||||
37
agravity_client/api/ai.py
Normal file
37
agravity_client/api/ai.py
Normal file
|
|
@ -0,0 +1,37 @@
|
||||||
|
"""AI Operations API module (reverse asset search)."""
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from typing import Optional
|
||||||
|
|
||||||
|
from agravity_client.api.base import AgravityBaseApi
|
||||||
|
from agravity_client.models.asset import Asset
|
||||||
|
|
||||||
|
|
||||||
|
class AiApi(AgravityBaseApi):
|
||||||
|
"""Covers all /ai endpoints."""
|
||||||
|
|
||||||
|
async def reverse_asset_search(
|
||||||
|
self,
|
||||||
|
image_file: bytes,
|
||||||
|
filename: str,
|
||||||
|
*,
|
||||||
|
limit: Optional[int] = None,
|
||||||
|
content_type: str = "application/octet-stream",
|
||||||
|
) -> list[Asset]:
|
||||||
|
"""POST /ai/reverseassetsearch – find similar assets by image.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
image_file: Raw image bytes to use as the search query.
|
||||||
|
filename: Original filename for the image.
|
||||||
|
limit: Maximum number of results to return.
|
||||||
|
content_type: MIME type of the uploaded image.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
A list of matching :class:`~agravity_client.models.asset.Asset` objects.
|
||||||
|
"""
|
||||||
|
files = {"image_file": (filename, image_file, content_type)}
|
||||||
|
data = {}
|
||||||
|
if limit is not None:
|
||||||
|
data["limit"] = str(limit)
|
||||||
|
resp = await self._post("/ai/reverseassetsearch", data=data, files=files)
|
||||||
|
return [Asset.model_validate(item) for item in resp.json()]
|
||||||
420
agravity_client/api/assets.py
Normal file
420
agravity_client/api/assets.py
Normal file
|
|
@ -0,0 +1,420 @@
|
||||||
|
"""Asset Management and Operations API module."""
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from pathlib import Path
|
||||||
|
from typing import Any, Optional
|
||||||
|
|
||||||
|
from agravity_client.api.base import AgravityBaseApi
|
||||||
|
from agravity_client.models.asset import (
|
||||||
|
Asset,
|
||||||
|
AssetAvailability,
|
||||||
|
AssetBlob,
|
||||||
|
AssetBulkUpdate,
|
||||||
|
AssetIdFormat,
|
||||||
|
AssetPageResult,
|
||||||
|
)
|
||||||
|
from agravity_client.models.collection import Collection
|
||||||
|
from agravity_client.models.common import AgravityInfoResponse
|
||||||
|
from agravity_client.models.download import DynamicImageOperation
|
||||||
|
from agravity_client.models.publish import PublishEntity, PublishedAsset
|
||||||
|
from agravity_client.models.relation import AssetRelation
|
||||||
|
from agravity_client.models.versioning import VersionedAsset, VersionEntity
|
||||||
|
|
||||||
|
|
||||||
|
class AssetsApi(AgravityBaseApi):
|
||||||
|
"""Covers all /assets endpoints."""
|
||||||
|
|
||||||
|
# ------------------------------------------------------------------
|
||||||
|
# Asset CRUD
|
||||||
|
# ------------------------------------------------------------------
|
||||||
|
|
||||||
|
async def list_assets(
|
||||||
|
self,
|
||||||
|
*,
|
||||||
|
collection_id: Optional[str] = None,
|
||||||
|
collection_type_id: Optional[str] = None,
|
||||||
|
fields: Optional[str] = None,
|
||||||
|
expose: Optional[bool] = None,
|
||||||
|
continuation_token: Optional[str] = None,
|
||||||
|
limit: Optional[int] = None,
|
||||||
|
orderby: Optional[str] = None,
|
||||||
|
filter: Optional[str] = None,
|
||||||
|
items: Optional[bool] = None,
|
||||||
|
translations: Optional[bool] = None,
|
||||||
|
accept_language: Optional[str] = None,
|
||||||
|
) -> AssetPageResult:
|
||||||
|
"""GET /assets – list assets (optionally filtered/paginated)."""
|
||||||
|
params: dict[str, Any] = {
|
||||||
|
"collectionid": collection_id,
|
||||||
|
"collectiontypeid": collection_type_id,
|
||||||
|
"fields": fields,
|
||||||
|
"expose": expose,
|
||||||
|
"continuation_token": continuation_token,
|
||||||
|
"limit": limit,
|
||||||
|
"orderby": orderby,
|
||||||
|
"filter": filter,
|
||||||
|
"items": items,
|
||||||
|
"translations": translations,
|
||||||
|
}
|
||||||
|
headers = {"Accept-Language": accept_language} if accept_language else None
|
||||||
|
resp = await self._get("/assets", params=params, extra_headers=headers)
|
||||||
|
return AssetPageResult.model_validate(resp.json())
|
||||||
|
|
||||||
|
async def create_asset(self, payload: dict[str, Any]) -> Asset:
|
||||||
|
"""POST /assets – create a new asset."""
|
||||||
|
resp = await self._post("/assets", json=payload)
|
||||||
|
return Asset.model_validate(resp.json())
|
||||||
|
|
||||||
|
async def get_asset(
|
||||||
|
self,
|
||||||
|
asset_id: str,
|
||||||
|
*,
|
||||||
|
fields: Optional[str] = None,
|
||||||
|
items: Optional[bool] = None,
|
||||||
|
translations: Optional[bool] = None,
|
||||||
|
accept_language: Optional[str] = None,
|
||||||
|
) -> Asset:
|
||||||
|
"""GET /assets/{id}."""
|
||||||
|
params: dict[str, Any] = {
|
||||||
|
"fields": fields,
|
||||||
|
"items": items,
|
||||||
|
"translations": translations,
|
||||||
|
}
|
||||||
|
headers = {"Accept-Language": accept_language} if accept_language else None
|
||||||
|
resp = await self._get(f"/assets/{asset_id}", params=params, extra_headers=headers)
|
||||||
|
return Asset.model_validate(resp.json())
|
||||||
|
|
||||||
|
async def update_asset(self, asset_id: str, payload: dict[str, Any]) -> Asset:
|
||||||
|
"""POST /assets/{id} – update asset metadata."""
|
||||||
|
resp = await self._post(f"/assets/{asset_id}", json=payload)
|
||||||
|
return Asset.model_validate(resp.json())
|
||||||
|
|
||||||
|
async def delete_asset(self, asset_id: str) -> None:
|
||||||
|
"""DELETE /assets/{id}."""
|
||||||
|
await self._delete(f"/assets/{asset_id}")
|
||||||
|
|
||||||
|
# ------------------------------------------------------------------
|
||||||
|
# Upload
|
||||||
|
# ------------------------------------------------------------------
|
||||||
|
|
||||||
|
async def upload_asset(
|
||||||
|
self,
|
||||||
|
file: bytes,
|
||||||
|
filename: str,
|
||||||
|
*,
|
||||||
|
name: Optional[str] = None,
|
||||||
|
collection_id: Optional[str] = None,
|
||||||
|
preview_of: Optional[str] = None,
|
||||||
|
content_type: str = "application/octet-stream",
|
||||||
|
) -> Asset:
|
||||||
|
"""POST /assetsupload – upload a new asset file (multipart)."""
|
||||||
|
files: dict[str, Any] = {
|
||||||
|
"file": (filename, file, content_type),
|
||||||
|
}
|
||||||
|
data: dict[str, Any] = {}
|
||||||
|
if name:
|
||||||
|
data["name"] = name
|
||||||
|
if collection_id:
|
||||||
|
data["collectionid"] = collection_id
|
||||||
|
if filename:
|
||||||
|
data["filename"] = filename
|
||||||
|
if preview_of:
|
||||||
|
data["previewof"] = preview_of
|
||||||
|
resp = await self._post("/assetsupload", data=data, files=files)
|
||||||
|
return Asset.model_validate(resp.json())
|
||||||
|
|
||||||
|
async def upload_asset_from_path(
|
||||||
|
self,
|
||||||
|
path: Path | str,
|
||||||
|
*,
|
||||||
|
name: Optional[str] = None,
|
||||||
|
collection_id: Optional[str] = None,
|
||||||
|
preview_of: Optional[str] = None,
|
||||||
|
) -> Asset:
|
||||||
|
"""Convenience wrapper: read a local file and call :meth:`upload_asset`."""
|
||||||
|
path = Path(path)
|
||||||
|
return await self.upload_asset(
|
||||||
|
path.read_bytes(),
|
||||||
|
path.name,
|
||||||
|
name=name,
|
||||||
|
collection_id=collection_id,
|
||||||
|
preview_of=preview_of,
|
||||||
|
)
|
||||||
|
|
||||||
|
# ------------------------------------------------------------------
|
||||||
|
# Bulk update
|
||||||
|
# ------------------------------------------------------------------
|
||||||
|
|
||||||
|
async def bulk_update_assets(self, payload: AssetBulkUpdate) -> AgravityInfoResponse:
|
||||||
|
"""POST /assetsbulkupdate."""
|
||||||
|
resp = await self._post("/assetsbulkupdate", json=payload.model_dump(by_alias=True, exclude_none=True))
|
||||||
|
return AgravityInfoResponse.model_validate(resp.json())
|
||||||
|
|
||||||
|
async def bulk_upsert_assets(self, payload: AssetBulkUpdate) -> AgravityInfoResponse:
|
||||||
|
"""PUT /assetsbulkupdate."""
|
||||||
|
resp = await self._put("/assetsbulkupdate", json=payload.model_dump(by_alias=True, exclude_none=True))
|
||||||
|
return AgravityInfoResponse.model_validate(resp.json())
|
||||||
|
|
||||||
|
async def bulk_delete_assets(self, asset_ids: list[str]) -> AgravityInfoResponse:
|
||||||
|
"""DELETE /assetsbulkupdate."""
|
||||||
|
resp = await self._delete("/assetsbulkupdate", params={"ids": ",".join(asset_ids)})
|
||||||
|
return AgravityInfoResponse.model_validate(resp.json())
|
||||||
|
|
||||||
|
# ------------------------------------------------------------------
|
||||||
|
# Blobs / binary operations
|
||||||
|
# ------------------------------------------------------------------
|
||||||
|
|
||||||
|
async def get_asset_blob(
|
||||||
|
self,
|
||||||
|
asset_id: str,
|
||||||
|
c: str,
|
||||||
|
*,
|
||||||
|
portal_id: Optional[str] = None,
|
||||||
|
key: Optional[str] = None,
|
||||||
|
) -> AssetBlob:
|
||||||
|
"""GET /assets/{id}/blobs – get a specific blob by format key."""
|
||||||
|
resp = await self._get(
|
||||||
|
f"/assets/{asset_id}/blobs",
|
||||||
|
params={"c": c, "portal_id": portal_id, "key": key},
|
||||||
|
)
|
||||||
|
return AssetBlob.model_validate(resp.json())
|
||||||
|
|
||||||
|
async def get_shared_asset_blob(
|
||||||
|
self,
|
||||||
|
asset_id: str,
|
||||||
|
share_id: str,
|
||||||
|
format: str,
|
||||||
|
*,
|
||||||
|
password: Optional[str] = None,
|
||||||
|
) -> AssetBlob:
|
||||||
|
"""GET /assets/{id}/blob – retrieve a blob via share token."""
|
||||||
|
headers = {"ay-password": password} if password else None
|
||||||
|
resp = await self._get(
|
||||||
|
f"/assets/{asset_id}/blob",
|
||||||
|
params={"share_id": share_id, "format": format},
|
||||||
|
extra_headers=headers,
|
||||||
|
)
|
||||||
|
return AssetBlob.model_validate(resp.json())
|
||||||
|
|
||||||
|
async def download_asset(
|
||||||
|
self,
|
||||||
|
asset_id: str,
|
||||||
|
*,
|
||||||
|
format: Optional[str] = None,
|
||||||
|
c: Optional[str] = None,
|
||||||
|
) -> AssetBlob:
|
||||||
|
"""GET /assets/{id}/download – resolve download URL for an asset."""
|
||||||
|
resp = await self._get(
|
||||||
|
f"/assets/{asset_id}/download",
|
||||||
|
params={"format": format, "c": c},
|
||||||
|
)
|
||||||
|
return AssetBlob.model_validate(resp.json())
|
||||||
|
|
||||||
|
async def resize_asset(
|
||||||
|
self,
|
||||||
|
asset_id: str,
|
||||||
|
*,
|
||||||
|
width: Optional[int] = None,
|
||||||
|
height: Optional[int] = None,
|
||||||
|
mode: Optional[str] = None,
|
||||||
|
format: Optional[str] = None,
|
||||||
|
bgcolor: Optional[str] = None,
|
||||||
|
dpi: Optional[int] = None,
|
||||||
|
depth: Optional[int] = None,
|
||||||
|
) -> bytes:
|
||||||
|
"""GET /assets/{id}/resize – return resized image bytes."""
|
||||||
|
resp = await self._get(
|
||||||
|
f"/assets/{asset_id}/resize",
|
||||||
|
params={
|
||||||
|
"width": width,
|
||||||
|
"height": height,
|
||||||
|
"mode": mode,
|
||||||
|
"format": format,
|
||||||
|
"bgcolor": bgcolor,
|
||||||
|
"dpi": dpi,
|
||||||
|
"depth": depth,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
return resp.content
|
||||||
|
|
||||||
|
async def imageedit_asset(
|
||||||
|
self,
|
||||||
|
asset_id: str,
|
||||||
|
*,
|
||||||
|
width: Optional[int] = None,
|
||||||
|
height: Optional[int] = None,
|
||||||
|
mode: Optional[str] = None,
|
||||||
|
target: Optional[str] = None,
|
||||||
|
bgcolor: Optional[str] = None,
|
||||||
|
dpi: Optional[int] = None,
|
||||||
|
depth: Optional[int] = None,
|
||||||
|
quality: Optional[int] = None,
|
||||||
|
colorspace: Optional[str] = None,
|
||||||
|
crop_x: Optional[int] = None,
|
||||||
|
crop_y: Optional[int] = None,
|
||||||
|
crop_width: Optional[int] = None,
|
||||||
|
crop_height: Optional[int] = None,
|
||||||
|
filter: Optional[str] = None,
|
||||||
|
original: Optional[bool] = None,
|
||||||
|
) -> bytes:
|
||||||
|
"""GET /assets/{id}/imageedit – apply image transformations."""
|
||||||
|
resp = await self._get(
|
||||||
|
f"/assets/{asset_id}/imageedit",
|
||||||
|
params={
|
||||||
|
"width": width, "height": height, "mode": mode, "target": target,
|
||||||
|
"bgcolor": bgcolor, "dpi": dpi, "depth": depth, "quality": quality,
|
||||||
|
"colorspace": colorspace, "crop_x": crop_x, "crop_y": crop_y,
|
||||||
|
"crop_width": crop_width, "crop_height": crop_height,
|
||||||
|
"filter": filter, "original": original,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
return resp.content
|
||||||
|
|
||||||
|
async def imageedit_asset_operations(
|
||||||
|
self,
|
||||||
|
asset_id: str,
|
||||||
|
operations: list[DynamicImageOperation],
|
||||||
|
) -> bytes:
|
||||||
|
"""POST /assets/{id}/imageedit – apply a sequence of image operations."""
|
||||||
|
payload = [op.model_dump(by_alias=True, exclude_none=True) for op in operations]
|
||||||
|
resp = await self._post(f"/assets/{asset_id}/imageedit", json=payload)
|
||||||
|
return resp.content
|
||||||
|
|
||||||
|
async def imageedit_asset_by_format(
|
||||||
|
self,
|
||||||
|
asset_id: str,
|
||||||
|
download_format_id: str,
|
||||||
|
) -> bytes:
|
||||||
|
"""GET /assets/{id}/imageedit/{download_format_id}."""
|
||||||
|
resp = await self._get(f"/assets/{asset_id}/imageedit/{download_format_id}")
|
||||||
|
return resp.content
|
||||||
|
|
||||||
|
# ------------------------------------------------------------------
|
||||||
|
# Collections membership
|
||||||
|
# ------------------------------------------------------------------
|
||||||
|
|
||||||
|
async def get_asset_collections(self, asset_id: str) -> list[Collection]:
|
||||||
|
"""GET /assets/{id}/collections."""
|
||||||
|
resp = await self._get(f"/assets/{asset_id}/collections")
|
||||||
|
return [Collection.model_validate(item) for item in resp.json()]
|
||||||
|
|
||||||
|
async def add_asset_to_collection(
|
||||||
|
self,
|
||||||
|
asset_id: str,
|
||||||
|
*,
|
||||||
|
collection_id: Optional[str] = None,
|
||||||
|
) -> None:
|
||||||
|
"""POST /assets/{id}/tocollection."""
|
||||||
|
await self._post(
|
||||||
|
f"/assets/{asset_id}/tocollection",
|
||||||
|
params={"collectionid": collection_id},
|
||||||
|
)
|
||||||
|
|
||||||
|
# ------------------------------------------------------------------
|
||||||
|
# Availability
|
||||||
|
# ------------------------------------------------------------------
|
||||||
|
|
||||||
|
async def update_asset_availability(
|
||||||
|
self, asset_id: str, availability: AssetAvailability
|
||||||
|
) -> AssetAvailability:
|
||||||
|
"""PUT /assets/{id}/availability."""
|
||||||
|
resp = await self._put(
|
||||||
|
f"/assets/{asset_id}/availability",
|
||||||
|
json=availability.model_dump(by_alias=True, exclude_none=True),
|
||||||
|
)
|
||||||
|
return AssetAvailability.model_validate(resp.json())
|
||||||
|
|
||||||
|
# ------------------------------------------------------------------
|
||||||
|
# Relations
|
||||||
|
# ------------------------------------------------------------------
|
||||||
|
|
||||||
|
async def get_asset_relations(self, asset_id: str) -> list[AssetRelation]:
|
||||||
|
"""GET /assets/{id}/relations."""
|
||||||
|
resp = await self._get(f"/assets/{asset_id}/relations")
|
||||||
|
return [AssetRelation.model_validate(item) for item in resp.json()]
|
||||||
|
|
||||||
|
# ------------------------------------------------------------------
|
||||||
|
# Publishing (per-asset)
|
||||||
|
# ------------------------------------------------------------------
|
||||||
|
|
||||||
|
async def get_asset_publish(self, asset_id: str) -> PublishEntity:
|
||||||
|
"""GET /assets/{id}/publish."""
|
||||||
|
resp = await self._get(f"/assets/{asset_id}/publish")
|
||||||
|
return PublishEntity.model_validate(resp.json())
|
||||||
|
|
||||||
|
async def create_asset_publish(
|
||||||
|
self,
|
||||||
|
asset_id: str,
|
||||||
|
payload: Optional[dict[str, Any]] = None,
|
||||||
|
) -> PublishedAsset:
|
||||||
|
"""POST /assets/{id}/publish."""
|
||||||
|
resp = await self._post(f"/assets/{asset_id}/publish", json=payload)
|
||||||
|
return PublishedAsset.model_validate(resp.json())
|
||||||
|
|
||||||
|
async def get_asset_published_by_id(
|
||||||
|
self, asset_id: str, pub_id: str
|
||||||
|
) -> PublishedAsset:
|
||||||
|
"""GET /assets/{id}/publish/{pid}."""
|
||||||
|
resp = await self._get(f"/assets/{asset_id}/publish/{pub_id}")
|
||||||
|
return PublishedAsset.model_validate(resp.json())
|
||||||
|
|
||||||
|
# ------------------------------------------------------------------
|
||||||
|
# Versioning (per-asset)
|
||||||
|
# ------------------------------------------------------------------
|
||||||
|
|
||||||
|
async def get_asset_versions(self, asset_id: str) -> VersionEntity:
|
||||||
|
"""GET /assets/{id}/versions."""
|
||||||
|
resp = await self._get(f"/assets/{asset_id}/versions")
|
||||||
|
return VersionEntity.model_validate(resp.json())
|
||||||
|
|
||||||
|
async def create_asset_version(
|
||||||
|
self,
|
||||||
|
asset_id: str,
|
||||||
|
payload: Optional[dict[str, Any]] = None,
|
||||||
|
) -> VersionedAsset:
|
||||||
|
"""POST /assets/{id}/versions – create a new version snapshot."""
|
||||||
|
resp = await self._post(f"/assets/{asset_id}/versions", json=payload)
|
||||||
|
return VersionedAsset.model_validate(resp.json())
|
||||||
|
|
||||||
|
async def upload_asset_version(
|
||||||
|
self,
|
||||||
|
asset_id: str,
|
||||||
|
file: bytes,
|
||||||
|
filename: str,
|
||||||
|
*,
|
||||||
|
content_type: str = "application/octet-stream",
|
||||||
|
) -> VersionedAsset:
|
||||||
|
"""POST /assets/{id}/versionsupload – upload a new version file."""
|
||||||
|
files = {"file": (filename, file, content_type)}
|
||||||
|
resp = await self._post(f"/assets/{asset_id}/versionsupload", files=files)
|
||||||
|
return VersionedAsset.model_validate(resp.json())
|
||||||
|
|
||||||
|
async def restore_asset_version(
|
||||||
|
self, asset_id: str, version_nr: int
|
||||||
|
) -> dict[str, Any]:
|
||||||
|
"""POST /assets/{id}/versions/{vNr}/restore."""
|
||||||
|
resp = await self._post(f"/assets/{asset_id}/versions/{version_nr}/restore")
|
||||||
|
return resp.json()
|
||||||
|
|
||||||
|
async def delete_asset_version(self, asset_id: str, version_nr: int) -> None:
|
||||||
|
"""DELETE /assets/{id}/versions/{vNr}."""
|
||||||
|
await self._delete(f"/assets/{asset_id}/versions/{version_nr}")
|
||||||
|
|
||||||
|
async def update_asset_version(
|
||||||
|
self,
|
||||||
|
asset_id: str,
|
||||||
|
version_nr: int,
|
||||||
|
payload: Optional[dict[str, Any]] = None,
|
||||||
|
) -> VersionedAsset:
|
||||||
|
"""POST /assets/{id}/versions/{vNr} – update version metadata."""
|
||||||
|
resp = await self._post(
|
||||||
|
f"/assets/{asset_id}/versions/{version_nr}", json=payload
|
||||||
|
)
|
||||||
|
return VersionedAsset.model_validate(resp.json())
|
||||||
|
|
||||||
|
async def get_asset_version_blob(
|
||||||
|
self, asset_id: str, version_nr: int
|
||||||
|
) -> AssetBlob:
|
||||||
|
"""GET /assets/{id}/versions/{vNr}/blobs."""
|
||||||
|
resp = await self._get(f"/assets/{asset_id}/versions/{version_nr}/blobs")
|
||||||
|
return AssetBlob.model_validate(resp.json())
|
||||||
35
agravity_client/api/auth.py
Normal file
35
agravity_client/api/auth.py
Normal file
|
|
@ -0,0 +1,35 @@
|
||||||
|
"""Auth API module (SAS tokens, users)."""
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from typing import Optional
|
||||||
|
|
||||||
|
from agravity_client.api.base import AgravityBaseApi
|
||||||
|
from agravity_client.models.auth import AgravityUser, SasToken
|
||||||
|
|
||||||
|
|
||||||
|
class AuthApi(AgravityBaseApi):
|
||||||
|
"""Covers all /auth endpoints."""
|
||||||
|
|
||||||
|
async def get_container_write_sas_token(
|
||||||
|
self,
|
||||||
|
container: str,
|
||||||
|
blob: str,
|
||||||
|
*,
|
||||||
|
permission: Optional[str] = None,
|
||||||
|
) -> SasToken:
|
||||||
|
"""GET /auth/containerwrite."""
|
||||||
|
resp = await self._get(
|
||||||
|
"/auth/containerwrite",
|
||||||
|
params={"container": container, "blob": blob, "permission": permission},
|
||||||
|
)
|
||||||
|
return SasToken.model_validate(resp.json())
|
||||||
|
|
||||||
|
async def get_inbox_sas_token(self) -> SasToken:
|
||||||
|
"""GET /auth/inbox (deprecated)."""
|
||||||
|
resp = await self._get("/auth/inbox")
|
||||||
|
return SasToken.model_validate(resp.json())
|
||||||
|
|
||||||
|
async def get_users(self) -> list[AgravityUser]:
|
||||||
|
"""GET /auth/users."""
|
||||||
|
resp = await self._get("/auth/users")
|
||||||
|
return [AgravityUser.model_validate(item) for item in resp.json()]
|
||||||
160
agravity_client/api/base.py
Normal file
160
agravity_client/api/base.py
Normal file
|
|
@ -0,0 +1,160 @@
|
||||||
|
"""Base HTTP client shared by all Agravity API modules."""
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from typing import Any, Optional
|
||||||
|
|
||||||
|
import httpx
|
||||||
|
|
||||||
|
from agravity_client.config import AgravityConfig
|
||||||
|
from agravity_client.exceptions import (
|
||||||
|
AgravityAPIError,
|
||||||
|
AgravityAuthenticationError,
|
||||||
|
AgravityConnectionError,
|
||||||
|
AgravityNotFoundError,
|
||||||
|
AgravityTimeoutError,
|
||||||
|
AgravityValidationError,
|
||||||
|
)
|
||||||
|
from agravity_client.models.common import AgravityErrorResponse
|
||||||
|
|
||||||
|
|
||||||
|
class AgravityBaseApi:
|
||||||
|
"""Low-level async HTTP wrapper around ``httpx.AsyncClient``.
|
||||||
|
|
||||||
|
Every high-level API module subclasses this and calls the
|
||||||
|
``_get`` / ``_post`` / ``_put`` / ``_delete`` / ``_patch`` helpers,
|
||||||
|
which centralise auth header injection and error mapping.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, http: httpx.AsyncClient, config: AgravityConfig) -> None:
|
||||||
|
self._http = http
|
||||||
|
self._config = config
|
||||||
|
|
||||||
|
# ------------------------------------------------------------------
|
||||||
|
# Private helpers
|
||||||
|
# ------------------------------------------------------------------
|
||||||
|
|
||||||
|
def _auth_headers(self) -> dict[str, str]:
|
||||||
|
"""Return the authentication headers for every request."""
|
||||||
|
if self._config.api_key:
|
||||||
|
return {"x-functions-key": self._config.api_key}
|
||||||
|
return {}
|
||||||
|
|
||||||
|
def _full_url(self, path: str) -> str:
|
||||||
|
"""Combine base URL with a relative path."""
|
||||||
|
return f"{self._config.base_url}/{path.lstrip('/')}"
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _raise_for_status(response: httpx.Response) -> None:
|
||||||
|
"""Map HTTP error codes to typed exceptions."""
|
||||||
|
if response.is_success:
|
||||||
|
return
|
||||||
|
|
||||||
|
error_response: Optional[AgravityErrorResponse] = None
|
||||||
|
raw_body: Optional[str] = None
|
||||||
|
|
||||||
|
try:
|
||||||
|
data = response.json()
|
||||||
|
error_response = AgravityErrorResponse.model_validate(data)
|
||||||
|
except Exception:
|
||||||
|
raw_body = response.text
|
||||||
|
|
||||||
|
status = response.status_code
|
||||||
|
if status == 401:
|
||||||
|
raise AgravityAuthenticationError(status, error_response, raw_body)
|
||||||
|
if status == 404:
|
||||||
|
raise AgravityNotFoundError(status, error_response, raw_body)
|
||||||
|
if status in (400, 422):
|
||||||
|
raise AgravityValidationError(status, error_response, raw_body)
|
||||||
|
raise AgravityAPIError(status, error_response, raw_body)
|
||||||
|
|
||||||
|
# ------------------------------------------------------------------
|
||||||
|
# HTTP verbs
|
||||||
|
# ------------------------------------------------------------------
|
||||||
|
|
||||||
|
async def _request(
|
||||||
|
self,
|
||||||
|
method: str,
|
||||||
|
path: str,
|
||||||
|
*,
|
||||||
|
params: Optional[dict[str, Any]] = None,
|
||||||
|
json: Optional[Any] = None,
|
||||||
|
data: Optional[dict[str, Any]] = None,
|
||||||
|
files: Optional[dict[str, Any]] = None,
|
||||||
|
content: Optional[bytes] = None,
|
||||||
|
extra_headers: Optional[dict[str, str]] = None,
|
||||||
|
) -> httpx.Response:
|
||||||
|
"""Dispatch a request and raise on error."""
|
||||||
|
headers = {**self._auth_headers(), **(extra_headers or {})}
|
||||||
|
# Remove None-valued query params
|
||||||
|
if params:
|
||||||
|
params = {k: v for k, v in params.items() if v is not None}
|
||||||
|
|
||||||
|
try:
|
||||||
|
response = await self._http.request(
|
||||||
|
method,
|
||||||
|
self._full_url(path),
|
||||||
|
params=params or None,
|
||||||
|
json=json,
|
||||||
|
data=data,
|
||||||
|
files=files,
|
||||||
|
content=content,
|
||||||
|
headers=headers,
|
||||||
|
timeout=self._config.timeout,
|
||||||
|
)
|
||||||
|
except httpx.TimeoutException as exc:
|
||||||
|
raise AgravityTimeoutError(str(exc)) from exc
|
||||||
|
except httpx.TransportError as exc:
|
||||||
|
raise AgravityConnectionError(str(exc)) from exc
|
||||||
|
|
||||||
|
self._raise_for_status(response)
|
||||||
|
return response
|
||||||
|
|
||||||
|
async def _get(
|
||||||
|
self,
|
||||||
|
path: str,
|
||||||
|
params: Optional[dict[str, Any]] = None,
|
||||||
|
extra_headers: Optional[dict[str, str]] = None,
|
||||||
|
) -> httpx.Response:
|
||||||
|
return await self._request("GET", path, params=params, extra_headers=extra_headers)
|
||||||
|
|
||||||
|
async def _post(
|
||||||
|
self,
|
||||||
|
path: str,
|
||||||
|
json: Optional[Any] = None,
|
||||||
|
data: Optional[dict[str, Any]] = None,
|
||||||
|
files: Optional[dict[str, Any]] = None,
|
||||||
|
content: Optional[bytes] = None,
|
||||||
|
params: Optional[dict[str, Any]] = None,
|
||||||
|
extra_headers: Optional[dict[str, str]] = None,
|
||||||
|
) -> httpx.Response:
|
||||||
|
return await self._request(
|
||||||
|
"POST", path,
|
||||||
|
json=json, data=data, files=files, content=content,
|
||||||
|
params=params, extra_headers=extra_headers,
|
||||||
|
)
|
||||||
|
|
||||||
|
async def _put(
|
||||||
|
self,
|
||||||
|
path: str,
|
||||||
|
json: Optional[Any] = None,
|
||||||
|
params: Optional[dict[str, Any]] = None,
|
||||||
|
extra_headers: Optional[dict[str, str]] = None,
|
||||||
|
) -> httpx.Response:
|
||||||
|
return await self._request("PUT", path, json=json, params=params, extra_headers=extra_headers)
|
||||||
|
|
||||||
|
async def _patch(
|
||||||
|
self,
|
||||||
|
path: str,
|
||||||
|
json: Optional[Any] = None,
|
||||||
|
params: Optional[dict[str, Any]] = None,
|
||||||
|
extra_headers: Optional[dict[str, str]] = None,
|
||||||
|
) -> httpx.Response:
|
||||||
|
return await self._request("PATCH", path, json=json, params=params, extra_headers=extra_headers)
|
||||||
|
|
||||||
|
async def _delete(
|
||||||
|
self,
|
||||||
|
path: str,
|
||||||
|
params: Optional[dict[str, Any]] = None,
|
||||||
|
extra_headers: Optional[dict[str, str]] = None,
|
||||||
|
) -> httpx.Response:
|
||||||
|
return await self._request("DELETE", path, params=params, extra_headers=extra_headers)
|
||||||
24
agravity_client/api/collection_types.py
Normal file
24
agravity_client/api/collection_types.py
Normal file
|
|
@ -0,0 +1,24 @@
|
||||||
|
"""Collection Type Management API module."""
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from agravity_client.api.base import AgravityBaseApi
|
||||||
|
from agravity_client.models.collection import CollectionType, CollTypeItem
|
||||||
|
|
||||||
|
|
||||||
|
class CollectionTypesApi(AgravityBaseApi):
|
||||||
|
"""Covers all /collectiontypes endpoints."""
|
||||||
|
|
||||||
|
async def list_collection_types(self) -> list[CollectionType]:
|
||||||
|
"""GET /collectiontypes."""
|
||||||
|
resp = await self._get("/collectiontypes")
|
||||||
|
return [CollectionType.model_validate(item) for item in resp.json()]
|
||||||
|
|
||||||
|
async def get_collection_type(self, type_id: str) -> CollectionType:
|
||||||
|
"""GET /collectiontypes/{id}."""
|
||||||
|
resp = await self._get(f"/collectiontypes/{type_id}")
|
||||||
|
return CollectionType.model_validate(resp.json())
|
||||||
|
|
||||||
|
async def get_collection_type_items(self, type_id: str) -> list[CollTypeItem]:
|
||||||
|
"""GET /collectiontypes/{id}/items."""
|
||||||
|
resp = await self._get(f"/collectiontypes/{type_id}/items")
|
||||||
|
return [CollTypeItem.model_validate(item) for item in resp.json()]
|
||||||
106
agravity_client/api/collections.py
Normal file
106
agravity_client/api/collections.py
Normal file
|
|
@ -0,0 +1,106 @@
|
||||||
|
"""Collection Management API module."""
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from typing import Any, Optional
|
||||||
|
|
||||||
|
from agravity_client.api.base import AgravityBaseApi
|
||||||
|
from agravity_client.models.collection import (
|
||||||
|
Collection,
|
||||||
|
EntityListResult,
|
||||||
|
EntityNamesRequest,
|
||||||
|
MoveCollectionBody,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class CollectionsApi(AgravityBaseApi):
|
||||||
|
"""Covers all /collections endpoints."""
|
||||||
|
|
||||||
|
async def list_collections(
|
||||||
|
self,
|
||||||
|
*,
|
||||||
|
accept_language: Optional[str] = None,
|
||||||
|
) -> list[Collection]:
|
||||||
|
"""GET /collections."""
|
||||||
|
headers = {"Accept-Language": accept_language} if accept_language else None
|
||||||
|
resp = await self._get("/collections", extra_headers=headers)
|
||||||
|
return [Collection.model_validate(item) for item in resp.json()]
|
||||||
|
|
||||||
|
async def create_collection(
|
||||||
|
self, payload: dict[str, Any]
|
||||||
|
) -> Collection:
|
||||||
|
"""POST /collections."""
|
||||||
|
resp = await self._post("/collections", json=payload)
|
||||||
|
return Collection.model_validate(resp.json())
|
||||||
|
|
||||||
|
async def get_collection(
|
||||||
|
self,
|
||||||
|
collection_id: str,
|
||||||
|
*,
|
||||||
|
accept_language: Optional[str] = None,
|
||||||
|
) -> Collection:
|
||||||
|
"""GET /collections/{id}."""
|
||||||
|
headers = {"Accept-Language": accept_language} if accept_language else None
|
||||||
|
resp = await self._get(f"/collections/{collection_id}", extra_headers=headers)
|
||||||
|
return Collection.model_validate(resp.json())
|
||||||
|
|
||||||
|
async def update_collection(
|
||||||
|
self, collection_id: str, payload: dict[str, Any]
|
||||||
|
) -> Collection:
|
||||||
|
"""POST /collections/{id}."""
|
||||||
|
resp = await self._post(f"/collections/{collection_id}", json=payload)
|
||||||
|
return Collection.model_validate(resp.json())
|
||||||
|
|
||||||
|
async def delete_collection(self, collection_id: str) -> None:
|
||||||
|
"""DELETE /collections/{id}."""
|
||||||
|
await self._delete(f"/collections/{collection_id}")
|
||||||
|
|
||||||
|
async def get_collection_ancestors(
|
||||||
|
self,
|
||||||
|
collection_id: str,
|
||||||
|
*,
|
||||||
|
accept_language: Optional[str] = None,
|
||||||
|
) -> list[Collection]:
|
||||||
|
"""GET /collections/{id}/ancestors."""
|
||||||
|
headers = {"Accept-Language": accept_language} if accept_language else None
|
||||||
|
resp = await self._get(f"/collections/{collection_id}/ancestors", extra_headers=headers)
|
||||||
|
return [Collection.model_validate(item) for item in resp.json()]
|
||||||
|
|
||||||
|
async def get_collection_descendants(
|
||||||
|
self,
|
||||||
|
collection_id: str,
|
||||||
|
*,
|
||||||
|
accept_language: Optional[str] = None,
|
||||||
|
) -> list[Collection]:
|
||||||
|
"""GET /collections/{id}/descendants."""
|
||||||
|
headers = {"Accept-Language": accept_language} if accept_language else None
|
||||||
|
resp = await self._get(f"/collections/{collection_id}/descendants", extra_headers=headers)
|
||||||
|
return [Collection.model_validate(item) for item in resp.json()]
|
||||||
|
|
||||||
|
async def get_collection_preview(
|
||||||
|
self,
|
||||||
|
collection_id: str,
|
||||||
|
) -> bytes:
|
||||||
|
"""GET /collections/{id}/preview – returns image bytes."""
|
||||||
|
resp = await self._get(f"/collections/{collection_id}/preview")
|
||||||
|
return resp.content
|
||||||
|
|
||||||
|
async def get_collections_by_names(
|
||||||
|
self,
|
||||||
|
request: EntityNamesRequest,
|
||||||
|
) -> EntityListResult:
|
||||||
|
"""POST /collections/bynames."""
|
||||||
|
resp = await self._post(
|
||||||
|
"/collections/bynames",
|
||||||
|
json=request.model_dump(by_alias=True, exclude_none=True),
|
||||||
|
)
|
||||||
|
return EntityListResult.model_validate(resp.json())
|
||||||
|
|
||||||
|
async def move_collection(
|
||||||
|
self,
|
||||||
|
payload: MoveCollectionBody,
|
||||||
|
) -> None:
|
||||||
|
"""POST /movecolltocoll – move a collection to another parent."""
|
||||||
|
await self._post(
|
||||||
|
"/movecolltocoll",
|
||||||
|
json=payload.model_dump(by_alias=True, exclude_none=True),
|
||||||
|
)
|
||||||
21
agravity_client/api/download_formats.py
Normal file
21
agravity_client/api/download_formats.py
Normal file
|
|
@ -0,0 +1,21 @@
|
||||||
|
"""Download Format Management API module."""
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from agravity_client.api.base import AgravityBaseApi
|
||||||
|
from agravity_client.models.download import DownloadFormat
|
||||||
|
|
||||||
|
|
||||||
|
class DownloadFormatsApi(AgravityBaseApi):
|
||||||
|
"""Covers all /downloadformats endpoints."""
|
||||||
|
|
||||||
|
async def list_download_formats(self) -> list[DownloadFormat]:
|
||||||
|
"""GET /downloadformats."""
|
||||||
|
resp = await self._get("/downloadformats")
|
||||||
|
return [DownloadFormat.model_validate(item) for item in resp.json()]
|
||||||
|
|
||||||
|
async def list_download_formats_from_shared(
|
||||||
|
self, share_id: str
|
||||||
|
) -> list[DownloadFormat]:
|
||||||
|
"""GET /downloadformats/{shareId} – formats available for a share token."""
|
||||||
|
resp = await self._get(f"/downloadformats/{share_id}")
|
||||||
|
return [DownloadFormat.model_validate(item) for item in resp.json()]
|
||||||
50
agravity_client/api/general.py
Normal file
50
agravity_client/api/general.py
Normal file
|
|
@ -0,0 +1,50 @@
|
||||||
|
"""General / utility endpoints (version, deleted, durable, signalR, public)."""
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from typing import Any, Optional
|
||||||
|
|
||||||
|
from agravity_client.api.base import AgravityBaseApi
|
||||||
|
from agravity_client.models.auth import AgravityVersion
|
||||||
|
from agravity_client.models.common import DeletedEntities, SignalRConnectionInfo
|
||||||
|
|
||||||
|
|
||||||
|
class GeneralApi(AgravityBaseApi):
|
||||||
|
"""Covers miscellaneous top-level endpoints."""
|
||||||
|
|
||||||
|
async def get_version(self) -> AgravityVersion:
|
||||||
|
"""GET /version – API version and capabilities."""
|
||||||
|
resp = await self._get("/version")
|
||||||
|
return AgravityVersion.model_validate(resp.json())
|
||||||
|
|
||||||
|
async def get_deleted_entities(self) -> list[DeletedEntities]:
|
||||||
|
"""GET /deleted – list recently deleted entities."""
|
||||||
|
resp = await self._get("/deleted")
|
||||||
|
return [DeletedEntities.model_validate(item) for item in resp.json()]
|
||||||
|
|
||||||
|
async def get_durable_status(
|
||||||
|
self,
|
||||||
|
instance_id: Optional[str] = None,
|
||||||
|
) -> dict[str, Any]:
|
||||||
|
"""GET /durable – check status of a durable function instance."""
|
||||||
|
resp = await self._get("/durable", params={"instanceId": instance_id})
|
||||||
|
return resp.json()
|
||||||
|
|
||||||
|
async def get_signalr_connection_info(self) -> SignalRConnectionInfo:
|
||||||
|
"""GET /negotiate – retrieve SignalR connection details."""
|
||||||
|
resp = await self._get("/negotiate")
|
||||||
|
return SignalRConnectionInfo.model_validate(resp.json())
|
||||||
|
|
||||||
|
async def get_public_asset(self, asset_id: str) -> dict[str, Any]:
|
||||||
|
"""GET /public/{id} – publicly accessible asset metadata."""
|
||||||
|
resp = await self._get(f"/public/{asset_id}")
|
||||||
|
return resp.json()
|
||||||
|
|
||||||
|
async def view_public_asset(self, asset_id: str) -> bytes:
|
||||||
|
"""GET /public/{id}/view – publicly accessible asset binary."""
|
||||||
|
resp = await self._get(f"/public/{asset_id}/view")
|
||||||
|
return resp.content
|
||||||
|
|
||||||
|
async def get_frontend_config(self) -> list[dict[str, Any]]:
|
||||||
|
"""GET /config – retrieve frontend configuration entries."""
|
||||||
|
resp = await self._get("/config")
|
||||||
|
return resp.json()
|
||||||
38
agravity_client/api/helper.py
Normal file
38
agravity_client/api/helper.py
Normal file
|
|
@ -0,0 +1,38 @@
|
||||||
|
"""Helper Tools API module (searchable/filterable item metadata)."""
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from typing import Any, Optional
|
||||||
|
|
||||||
|
from agravity_client.api.base import AgravityBaseApi
|
||||||
|
from agravity_client.models.search import SearchableItem
|
||||||
|
|
||||||
|
|
||||||
|
class HelperApi(AgravityBaseApi):
|
||||||
|
"""Covers all /helper endpoints."""
|
||||||
|
|
||||||
|
async def get_user_defined_lists(
|
||||||
|
self,
|
||||||
|
*,
|
||||||
|
collection_type_id: Optional[str] = None,
|
||||||
|
) -> list[dict[str, Any]]:
|
||||||
|
"""GET /helper/userdefinedlists."""
|
||||||
|
resp = await self._get(
|
||||||
|
"/helper/userdefinedlists",
|
||||||
|
params={"collectiontypeid": collection_type_id},
|
||||||
|
)
|
||||||
|
return resp.json()
|
||||||
|
|
||||||
|
async def get_searchable_item_names(self) -> list[str]:
|
||||||
|
"""GET /helper/searchableitemnames – list names of searchable fields."""
|
||||||
|
resp = await self._get("/helper/searchableitemnames")
|
||||||
|
return resp.json()
|
||||||
|
|
||||||
|
async def get_searchable_items(self) -> list[SearchableItem]:
|
||||||
|
"""GET /helper/searchableitems – full searchable field definitions."""
|
||||||
|
resp = await self._get("/helper/searchableitems")
|
||||||
|
return [SearchableItem.model_validate(item) for item in resp.json()]
|
||||||
|
|
||||||
|
async def get_filterable_items(self) -> list[SearchableItem]:
|
||||||
|
"""GET /helper/filterableitems – fields that can be used as filters."""
|
||||||
|
resp = await self._get("/helper/filterableitems")
|
||||||
|
return [SearchableItem.model_validate(item) for item in resp.json()]
|
||||||
73
agravity_client/api/portals.py
Normal file
73
agravity_client/api/portals.py
Normal file
|
|
@ -0,0 +1,73 @@
|
||||||
|
"""Portal Management API module."""
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from typing import Any, Optional
|
||||||
|
|
||||||
|
from agravity_client.api.base import AgravityBaseApi
|
||||||
|
from agravity_client.models.download import DownloadZipRequest, DownloadZipStatus
|
||||||
|
from agravity_client.models.portal import Portal, PortalConfiguration
|
||||||
|
|
||||||
|
|
||||||
|
class PortalsApi(AgravityBaseApi):
|
||||||
|
"""Covers all /portals endpoints."""
|
||||||
|
|
||||||
|
async def list_portals(self) -> list[Portal]:
|
||||||
|
"""GET /portals."""
|
||||||
|
resp = await self._get("/portals")
|
||||||
|
return [Portal.model_validate(item) for item in resp.json()]
|
||||||
|
|
||||||
|
async def get_portal(self, portal_id: str) -> Portal:
|
||||||
|
"""GET /portals/{id}."""
|
||||||
|
resp = await self._get(f"/portals/{portal_id}")
|
||||||
|
return Portal.model_validate(resp.json())
|
||||||
|
|
||||||
|
async def get_portal_config(self, portal_id: str) -> PortalConfiguration:
|
||||||
|
"""GET /portals/{id}/config."""
|
||||||
|
resp = await self._get(f"/portals/{portal_id}/config")
|
||||||
|
return PortalConfiguration.model_validate(resp.json())
|
||||||
|
|
||||||
|
async def enhance_portal_token(
|
||||||
|
self,
|
||||||
|
portal_id: str,
|
||||||
|
payload: Optional[dict[str, Any]] = None,
|
||||||
|
) -> dict[str, Any]:
|
||||||
|
"""POST /portals/{id}/enhancetoken – augment the auth token claims."""
|
||||||
|
resp = await self._post(
|
||||||
|
f"/portals/{portal_id}/enhancetoken",
|
||||||
|
json=payload,
|
||||||
|
)
|
||||||
|
return resp.json()
|
||||||
|
|
||||||
|
async def save_portal_user_attributes(
|
||||||
|
self,
|
||||||
|
portal_id: str,
|
||||||
|
payload: dict[str, Any],
|
||||||
|
) -> dict[str, Any]:
|
||||||
|
"""POST /portals/{id}/saveuserattributes."""
|
||||||
|
resp = await self._post(
|
||||||
|
f"/portals/{portal_id}/saveuserattributes",
|
||||||
|
json=payload,
|
||||||
|
)
|
||||||
|
return resp.json()
|
||||||
|
|
||||||
|
async def get_portal_zip_status(self, portal_id: str) -> DownloadZipStatus:
|
||||||
|
"""GET /portals/{id}/zip."""
|
||||||
|
resp = await self._get(f"/portals/{portal_id}/zip")
|
||||||
|
return DownloadZipStatus.model_validate(resp.json())
|
||||||
|
|
||||||
|
async def create_portal_zip(
|
||||||
|
self,
|
||||||
|
portal_id: str,
|
||||||
|
payload: DownloadZipRequest,
|
||||||
|
) -> DownloadZipStatus:
|
||||||
|
"""POST /portals/{id}/zip."""
|
||||||
|
resp = await self._post(
|
||||||
|
f"/portals/{portal_id}/zip",
|
||||||
|
json=payload.model_dump(by_alias=True, exclude_none=True),
|
||||||
|
)
|
||||||
|
return DownloadZipStatus.model_validate(resp.json())
|
||||||
|
|
||||||
|
async def get_portal_asset_ids(self, portal_id: str) -> list[str]:
|
||||||
|
"""GET /portals/{id}/assetids."""
|
||||||
|
resp = await self._get(f"/portals/{portal_id}/assetids")
|
||||||
|
return resp.json()
|
||||||
14
agravity_client/api/publishing.py
Normal file
14
agravity_client/api/publishing.py
Normal file
|
|
@ -0,0 +1,14 @@
|
||||||
|
"""Publishing API module (global published assets list)."""
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from agravity_client.api.base import AgravityBaseApi
|
||||||
|
from agravity_client.models.publish import PublishEntity
|
||||||
|
|
||||||
|
|
||||||
|
class PublishingApi(AgravityBaseApi):
|
||||||
|
"""Covers the /publish endpoint."""
|
||||||
|
|
||||||
|
async def list_published(self) -> list[PublishEntity]:
|
||||||
|
"""GET /publish – list all published entities across assets."""
|
||||||
|
resp = await self._get("/publish")
|
||||||
|
return [PublishEntity.model_validate(item) for item in resp.json()]
|
||||||
57
agravity_client/api/relations.py
Normal file
57
agravity_client/api/relations.py
Normal file
|
|
@ -0,0 +1,57 @@
|
||||||
|
"""Asset Relation and Asset Relation Type API modules."""
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from typing import Any, Optional
|
||||||
|
|
||||||
|
from agravity_client.api.base import AgravityBaseApi
|
||||||
|
from agravity_client.models.relation import AssetRelation, AssetRelationType
|
||||||
|
|
||||||
|
|
||||||
|
class RelationsApi(AgravityBaseApi):
|
||||||
|
"""Covers /relations and /assetrelationtypes endpoints."""
|
||||||
|
|
||||||
|
# ------------------------------------------------------------------
|
||||||
|
# Asset Relations (CRUD)
|
||||||
|
# ------------------------------------------------------------------
|
||||||
|
|
||||||
|
async def list_relations(self) -> list[AssetRelation]:
|
||||||
|
"""GET /relations."""
|
||||||
|
resp = await self._get("/relations")
|
||||||
|
return [AssetRelation.model_validate(item) for item in resp.json()]
|
||||||
|
|
||||||
|
async def create_relation(self, payload: dict[str, Any]) -> AssetRelation:
|
||||||
|
"""POST /relations."""
|
||||||
|
resp = await self._post("/relations", json=payload)
|
||||||
|
return AssetRelation.model_validate(resp.json())
|
||||||
|
|
||||||
|
async def get_relation(self, relation_id: str) -> AssetRelation:
|
||||||
|
"""GET /relations/{id}."""
|
||||||
|
resp = await self._get(f"/relations/{relation_id}")
|
||||||
|
return AssetRelation.model_validate(resp.json())
|
||||||
|
|
||||||
|
async def update_relation(
|
||||||
|
self,
|
||||||
|
relation_id: str,
|
||||||
|
payload: dict[str, Any],
|
||||||
|
) -> AssetRelation:
|
||||||
|
"""POST /relations/{id}."""
|
||||||
|
resp = await self._post(f"/relations/{relation_id}", json=payload)
|
||||||
|
return AssetRelation.model_validate(resp.json())
|
||||||
|
|
||||||
|
async def delete_relation(self, relation_id: str) -> None:
|
||||||
|
"""DELETE /relations/{id}."""
|
||||||
|
await self._delete(f"/relations/{relation_id}")
|
||||||
|
|
||||||
|
# ------------------------------------------------------------------
|
||||||
|
# Asset Relation Types (read-only)
|
||||||
|
# ------------------------------------------------------------------
|
||||||
|
|
||||||
|
async def list_relation_types(self) -> list[AssetRelationType]:
|
||||||
|
"""GET /assetrelationtypes."""
|
||||||
|
resp = await self._get("/assetrelationtypes")
|
||||||
|
return [AssetRelationType.model_validate(item) for item in resp.json()]
|
||||||
|
|
||||||
|
async def get_relation_type(self, type_id: str) -> AssetRelationType:
|
||||||
|
"""GET /assetrelationtypes/{id}."""
|
||||||
|
resp = await self._get(f"/assetrelationtypes/{type_id}")
|
||||||
|
return AssetRelationType.model_validate(resp.json())
|
||||||
42
agravity_client/api/search.py
Normal file
42
agravity_client/api/search.py
Normal file
|
|
@ -0,0 +1,42 @@
|
||||||
|
"""Search Management API module."""
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from typing import Optional
|
||||||
|
|
||||||
|
from agravity_client.api.base import AgravityBaseApi
|
||||||
|
from agravity_client.models.search import (
|
||||||
|
AzSearchOptions,
|
||||||
|
SavedSearch,
|
||||||
|
SearchAdminStatus,
|
||||||
|
SearchResult,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class SearchApi(AgravityBaseApi):
|
||||||
|
"""Covers /search and /savedsearch endpoints."""
|
||||||
|
|
||||||
|
async def search(self, options: AzSearchOptions) -> SearchResult:
|
||||||
|
"""POST /search – perform a full-text/faceted search."""
|
||||||
|
resp = await self._post(
|
||||||
|
"/search",
|
||||||
|
json=options.model_dump(by_alias=True, exclude_none=True),
|
||||||
|
)
|
||||||
|
return SearchResult.model_validate(resp.json())
|
||||||
|
|
||||||
|
async def search_facette(self, options: AzSearchOptions) -> SearchResult:
|
||||||
|
"""POST /search/facette – faceted search."""
|
||||||
|
resp = await self._post(
|
||||||
|
"/search/facette",
|
||||||
|
json=options.model_dump(by_alias=True, exclude_none=True),
|
||||||
|
)
|
||||||
|
return SearchResult.model_validate(resp.json())
|
||||||
|
|
||||||
|
async def get_search_admin_status(self) -> SearchAdminStatus:
|
||||||
|
"""GET /search/adminstatus."""
|
||||||
|
resp = await self._get("/search/adminstatus")
|
||||||
|
return SearchAdminStatus.model_validate(resp.json())
|
||||||
|
|
||||||
|
async def list_saved_searches(self) -> list[SavedSearch]:
|
||||||
|
"""GET /savedsearch."""
|
||||||
|
resp = await self._get("/savedsearch")
|
||||||
|
return [SavedSearch.model_validate(item) for item in resp.json()]
|
||||||
28
agravity_client/api/secure_upload.py
Normal file
28
agravity_client/api/secure_upload.py
Normal file
|
|
@ -0,0 +1,28 @@
|
||||||
|
"""Secure Upload API module."""
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from typing import Optional
|
||||||
|
|
||||||
|
from agravity_client.api.base import AgravityBaseApi
|
||||||
|
from agravity_client.models.secure_upload import SecureUploadEntity
|
||||||
|
|
||||||
|
|
||||||
|
class SecureUploadApi(AgravityBaseApi):
|
||||||
|
"""Covers all /secureupload endpoints."""
|
||||||
|
|
||||||
|
async def check_secure_upload(
|
||||||
|
self,
|
||||||
|
*,
|
||||||
|
token: Optional[str] = None,
|
||||||
|
) -> dict:
|
||||||
|
"""GET /secureupload – check status / retrieve upload config."""
|
||||||
|
resp = await self._get("/secureupload", params={"token": token})
|
||||||
|
return resp.json()
|
||||||
|
|
||||||
|
async def create_secure_upload(
|
||||||
|
self,
|
||||||
|
payload: Optional[dict] = None,
|
||||||
|
) -> SecureUploadEntity:
|
||||||
|
"""POST /secureupload – create a secure upload entity."""
|
||||||
|
resp = await self._post("/secureupload", json=payload)
|
||||||
|
return SecureUploadEntity.model_validate(resp.json())
|
||||||
79
agravity_client/api/sharing.py
Normal file
79
agravity_client/api/sharing.py
Normal file
|
|
@ -0,0 +1,79 @@
|
||||||
|
"""Sharing Management API module."""
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from typing import Optional
|
||||||
|
|
||||||
|
from agravity_client.api.base import AgravityBaseApi
|
||||||
|
from agravity_client.models.download import DownloadZipRequest, DownloadZipStatus
|
||||||
|
from agravity_client.models.sharing import QuickShareFull, SharedCollectionFull
|
||||||
|
|
||||||
|
|
||||||
|
class SharingApi(AgravityBaseApi):
|
||||||
|
"""Covers all /sharing endpoints."""
|
||||||
|
|
||||||
|
# ------------------------------------------------------------------
|
||||||
|
# Shared collections
|
||||||
|
# ------------------------------------------------------------------
|
||||||
|
|
||||||
|
async def list_shared_collections(self) -> list[SharedCollectionFull]:
|
||||||
|
"""GET /sharing."""
|
||||||
|
resp = await self._get("/sharing")
|
||||||
|
return [SharedCollectionFull.model_validate(item) for item in resp.json()]
|
||||||
|
|
||||||
|
async def get_shared_collection(
|
||||||
|
self,
|
||||||
|
token: str,
|
||||||
|
*,
|
||||||
|
password: Optional[str] = None,
|
||||||
|
) -> SharedCollectionFull:
|
||||||
|
"""GET /sharing/{token}."""
|
||||||
|
headers = {"ay-password": password} if password else None
|
||||||
|
resp = await self._get(f"/sharing/{token}", extra_headers=headers)
|
||||||
|
return SharedCollectionFull.model_validate(resp.json())
|
||||||
|
|
||||||
|
async def get_shared_collection_zip_status(
|
||||||
|
self,
|
||||||
|
token: str,
|
||||||
|
*,
|
||||||
|
password: Optional[str] = None,
|
||||||
|
) -> DownloadZipStatus:
|
||||||
|
"""GET /sharing/{token}/zip."""
|
||||||
|
headers = {"ay-password": password} if password else None
|
||||||
|
resp = await self._get(f"/sharing/{token}/zip", extra_headers=headers)
|
||||||
|
return DownloadZipStatus.model_validate(resp.json())
|
||||||
|
|
||||||
|
async def create_shared_collection_zip(
|
||||||
|
self,
|
||||||
|
token: str,
|
||||||
|
payload: DownloadZipRequest,
|
||||||
|
*,
|
||||||
|
password: Optional[str] = None,
|
||||||
|
) -> DownloadZipStatus:
|
||||||
|
"""POST /sharing/{token}/zip."""
|
||||||
|
headers = {"ay-password": password} if password else None
|
||||||
|
resp = await self._post(
|
||||||
|
f"/sharing/{token}/zip",
|
||||||
|
json=payload.model_dump(by_alias=True, exclude_none=True),
|
||||||
|
extra_headers=headers,
|
||||||
|
)
|
||||||
|
return DownloadZipStatus.model_validate(resp.json())
|
||||||
|
|
||||||
|
# ------------------------------------------------------------------
|
||||||
|
# Quick shares
|
||||||
|
# ------------------------------------------------------------------
|
||||||
|
|
||||||
|
async def list_quickshares(self) -> list[QuickShareFull]:
|
||||||
|
"""GET /sharing/quickshares."""
|
||||||
|
resp = await self._get("/sharing/quickshares")
|
||||||
|
return [QuickShareFull.model_validate(item) for item in resp.json()]
|
||||||
|
|
||||||
|
async def get_quickshare(
|
||||||
|
self,
|
||||||
|
token: str,
|
||||||
|
*,
|
||||||
|
password: Optional[str] = None,
|
||||||
|
) -> QuickShareFull:
|
||||||
|
"""GET /sharing/quickshares/{token}."""
|
||||||
|
headers = {"ay-password": password} if password else None
|
||||||
|
resp = await self._get(f"/sharing/quickshares/{token}", extra_headers=headers)
|
||||||
|
return QuickShareFull.model_validate(resp.json())
|
||||||
30
agravity_client/api/static_lists.py
Normal file
30
agravity_client/api/static_lists.py
Normal file
|
|
@ -0,0 +1,30 @@
|
||||||
|
"""Static Defined Lists API module."""
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from typing import Any
|
||||||
|
|
||||||
|
from agravity_client.api.base import AgravityBaseApi
|
||||||
|
from agravity_client.models.static_lists import StaticDefinedList
|
||||||
|
|
||||||
|
|
||||||
|
class StaticListsApi(AgravityBaseApi):
|
||||||
|
"""Covers all /staticdefinedlists endpoints."""
|
||||||
|
|
||||||
|
async def list_static_defined_lists(self) -> list[StaticDefinedList]:
|
||||||
|
"""GET /staticdefinedlists."""
|
||||||
|
resp = await self._get("/staticdefinedlists")
|
||||||
|
return [StaticDefinedList.model_validate(item) for item in resp.json()]
|
||||||
|
|
||||||
|
async def get_static_defined_list(self, list_id: str) -> StaticDefinedList:
|
||||||
|
"""GET /staticdefinedlists/{id}."""
|
||||||
|
resp = await self._get(f"/staticdefinedlists/{list_id}")
|
||||||
|
return StaticDefinedList.model_validate(resp.json())
|
||||||
|
|
||||||
|
async def update_static_defined_list(
|
||||||
|
self,
|
||||||
|
list_id: str,
|
||||||
|
payload: dict[str, Any],
|
||||||
|
) -> StaticDefinedList:
|
||||||
|
"""PUT /staticdefinedlists/{id}."""
|
||||||
|
resp = await self._put(f"/staticdefinedlists/{list_id}", json=payload)
|
||||||
|
return StaticDefinedList.model_validate(resp.json())
|
||||||
43
agravity_client/api/translations.py
Normal file
43
agravity_client/api/translations.py
Normal file
|
|
@ -0,0 +1,43 @@
|
||||||
|
"""Translation Management API module."""
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from typing import Any
|
||||||
|
|
||||||
|
from agravity_client.api.base import AgravityBaseApi
|
||||||
|
from agravity_client.models.common import DictionaryObject
|
||||||
|
|
||||||
|
|
||||||
|
class TranslationsApi(AgravityBaseApi):
|
||||||
|
"""Covers all /translations endpoints."""
|
||||||
|
|
||||||
|
async def get_translation(self, entity_id: str) -> DictionaryObject:
|
||||||
|
"""GET /translations/{id} – retrieve all translations for an entity."""
|
||||||
|
resp = await self._get(f"/translations/{entity_id}")
|
||||||
|
return DictionaryObject.model_validate(resp.json())
|
||||||
|
|
||||||
|
async def update_translation_property(
|
||||||
|
self,
|
||||||
|
entity_id: str,
|
||||||
|
property_name: str,
|
||||||
|
payload: dict[str, Any],
|
||||||
|
) -> DictionaryObject:
|
||||||
|
"""PUT /translations/{id}/{property}."""
|
||||||
|
resp = await self._put(
|
||||||
|
f"/translations/{entity_id}/{property_name}",
|
||||||
|
json=payload,
|
||||||
|
)
|
||||||
|
return DictionaryObject.model_validate(resp.json())
|
||||||
|
|
||||||
|
async def update_translation_custom_field(
|
||||||
|
self,
|
||||||
|
entity_id: str,
|
||||||
|
property_name: str,
|
||||||
|
custom_field: str,
|
||||||
|
payload: dict[str, Any],
|
||||||
|
) -> DictionaryObject:
|
||||||
|
"""PUT /translations/{id}/{property}/{custom_field}."""
|
||||||
|
resp = await self._put(
|
||||||
|
f"/translations/{entity_id}/{property_name}/{custom_field}",
|
||||||
|
json=payload,
|
||||||
|
)
|
||||||
|
return DictionaryObject.model_validate(resp.json())
|
||||||
45
agravity_client/api/webappdata.py
Normal file
45
agravity_client/api/webappdata.py
Normal file
|
|
@ -0,0 +1,45 @@
|
||||||
|
"""Web App Data API module."""
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from typing import Optional, Union
|
||||||
|
|
||||||
|
from agravity_client.api.base import AgravityBaseApi
|
||||||
|
from agravity_client.models.search import AllWebAppData, GroupAllAppData
|
||||||
|
|
||||||
|
|
||||||
|
class WebAppDataApi(AgravityBaseApi):
|
||||||
|
"""Covers all /webappdata endpoints."""
|
||||||
|
|
||||||
|
async def get_web_app_data(
|
||||||
|
self,
|
||||||
|
*,
|
||||||
|
collection_type_id: Optional[str] = None,
|
||||||
|
accept_language: Optional[str] = None,
|
||||||
|
) -> Union[AllWebAppData, GroupAllAppData]:
|
||||||
|
"""GET /webappdata – retrieve structured data for web app initialization.
|
||||||
|
|
||||||
|
If *collection_type_id* is supplied the API may return a
|
||||||
|
:class:`GroupAllAppData` instead of :class:`AllWebAppData`.
|
||||||
|
"""
|
||||||
|
params = {"collectiontypeid": collection_type_id}
|
||||||
|
headers = {"Accept-Language": accept_language} if accept_language else None
|
||||||
|
resp = await self._get("/webappdata", params=params, extra_headers=headers)
|
||||||
|
data = resp.json()
|
||||||
|
# Heuristic: GroupAllAppData has a "collection_type" key
|
||||||
|
if "collection_type" in data or "collectionType" in data:
|
||||||
|
return GroupAllAppData.model_validate(data)
|
||||||
|
return AllWebAppData.model_validate(data)
|
||||||
|
|
||||||
|
async def get_web_app_data_by_collection_type(
|
||||||
|
self,
|
||||||
|
collection_type_id: str,
|
||||||
|
*,
|
||||||
|
accept_language: Optional[str] = None,
|
||||||
|
) -> GroupAllAppData:
|
||||||
|
"""GET /webappdata/{collectionTypeId}."""
|
||||||
|
headers = {"Accept-Language": accept_language} if accept_language else None
|
||||||
|
resp = await self._get(
|
||||||
|
f"/webappdata/{collection_type_id}",
|
||||||
|
extra_headers=headers,
|
||||||
|
)
|
||||||
|
return GroupAllAppData.model_validate(resp.json())
|
||||||
19
agravity_client/api/workspaces.py
Normal file
19
agravity_client/api/workspaces.py
Normal file
|
|
@ -0,0 +1,19 @@
|
||||||
|
"""Workspaces API module."""
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from agravity_client.api.base import AgravityBaseApi
|
||||||
|
from agravity_client.models.workspace import Workspace
|
||||||
|
|
||||||
|
|
||||||
|
class WorkspacesApi(AgravityBaseApi):
|
||||||
|
"""Covers all /workspaces endpoints."""
|
||||||
|
|
||||||
|
async def list_workspaces(self) -> list[Workspace]:
|
||||||
|
"""GET /workspaces."""
|
||||||
|
resp = await self._get("/workspaces")
|
||||||
|
return [Workspace.model_validate(item) for item in resp.json()]
|
||||||
|
|
||||||
|
async def get_workspace(self, workspace_id: str) -> Workspace:
|
||||||
|
"""GET /workspaces/{id}."""
|
||||||
|
resp = await self._get(f"/workspaces/{workspace_id}")
|
||||||
|
return Workspace.model_validate(resp.json())
|
||||||
114
agravity_client/client.py
Normal file
114
agravity_client/client.py
Normal file
|
|
@ -0,0 +1,114 @@
|
||||||
|
"""Top-level AgravityClient – the single entry point for users."""
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from types import TracebackType
|
||||||
|
from typing import Optional, Type
|
||||||
|
|
||||||
|
import httpx
|
||||||
|
|
||||||
|
from agravity_client.api.ai import AiApi
|
||||||
|
from agravity_client.api.assets import AssetsApi
|
||||||
|
from agravity_client.api.auth import AuthApi
|
||||||
|
from agravity_client.api.collection_types import CollectionTypesApi
|
||||||
|
from agravity_client.api.collections import CollectionsApi
|
||||||
|
from agravity_client.api.download_formats import DownloadFormatsApi
|
||||||
|
from agravity_client.api.general import GeneralApi
|
||||||
|
from agravity_client.api.helper import HelperApi
|
||||||
|
from agravity_client.api.portals import PortalsApi
|
||||||
|
from agravity_client.api.publishing import PublishingApi
|
||||||
|
from agravity_client.api.relations import RelationsApi
|
||||||
|
from agravity_client.api.search import SearchApi
|
||||||
|
from agravity_client.api.secure_upload import SecureUploadApi
|
||||||
|
from agravity_client.api.sharing import SharingApi
|
||||||
|
from agravity_client.api.static_lists import StaticListsApi
|
||||||
|
from agravity_client.api.translations import TranslationsApi
|
||||||
|
from agravity_client.api.webappdata import WebAppDataApi
|
||||||
|
from agravity_client.api.workspaces import WorkspacesApi
|
||||||
|
from agravity_client.config import AgravityConfig
|
||||||
|
|
||||||
|
|
||||||
|
class AgravityClient:
|
||||||
|
"""Fully async Agravity DAM API client.
|
||||||
|
|
||||||
|
Usage example::
|
||||||
|
|
||||||
|
import asyncio
|
||||||
|
from agravity_client import AgravityClient, AgravityConfig
|
||||||
|
|
||||||
|
async def main():
|
||||||
|
config = AgravityConfig(api_key="your-key")
|
||||||
|
async with AgravityClient(config) as client:
|
||||||
|
version = await client.general.get_version()
|
||||||
|
print(version)
|
||||||
|
|
||||||
|
asyncio.run(main())
|
||||||
|
|
||||||
|
The client can also be used without the context-manager idiom – just
|
||||||
|
remember to call :meth:`aclose` when you are done.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(
|
||||||
|
self,
|
||||||
|
config: Optional[AgravityConfig] = None,
|
||||||
|
*,
|
||||||
|
http_client: Optional[httpx.AsyncClient] = None,
|
||||||
|
) -> None:
|
||||||
|
self._config = config or AgravityConfig()
|
||||||
|
self._http = http_client or httpx.AsyncClient(
|
||||||
|
verify=self._config.verify_ssl,
|
||||||
|
timeout=self._config.timeout,
|
||||||
|
)
|
||||||
|
self._init_api_modules()
|
||||||
|
|
||||||
|
# ------------------------------------------------------------------
|
||||||
|
# Context-manager support
|
||||||
|
# ------------------------------------------------------------------
|
||||||
|
|
||||||
|
async def __aenter__(self) -> "AgravityClient":
|
||||||
|
return self
|
||||||
|
|
||||||
|
async def __aexit__(
|
||||||
|
self,
|
||||||
|
exc_type: Optional[Type[BaseException]],
|
||||||
|
exc_val: Optional[BaseException],
|
||||||
|
exc_tb: Optional[TracebackType],
|
||||||
|
) -> None:
|
||||||
|
await self.aclose()
|
||||||
|
|
||||||
|
async def aclose(self) -> None:
|
||||||
|
"""Close the underlying HTTP transport."""
|
||||||
|
await self._http.aclose()
|
||||||
|
|
||||||
|
# ------------------------------------------------------------------
|
||||||
|
# API module initialisation
|
||||||
|
# ------------------------------------------------------------------
|
||||||
|
|
||||||
|
def _init_api_modules(self) -> None:
|
||||||
|
args = (self._http, self._config)
|
||||||
|
self.ai = AiApi(*args)
|
||||||
|
self.assets = AssetsApi(*args)
|
||||||
|
self.auth = AuthApi(*args)
|
||||||
|
self.collection_types = CollectionTypesApi(*args)
|
||||||
|
self.collections = CollectionsApi(*args)
|
||||||
|
self.download_formats = DownloadFormatsApi(*args)
|
||||||
|
self.general = GeneralApi(*args)
|
||||||
|
self.helper = HelperApi(*args)
|
||||||
|
self.portals = PortalsApi(*args)
|
||||||
|
self.publishing = PublishingApi(*args)
|
||||||
|
self.relations = RelationsApi(*args)
|
||||||
|
self.search = SearchApi(*args)
|
||||||
|
self.secure_upload = SecureUploadApi(*args)
|
||||||
|
self.sharing = SharingApi(*args)
|
||||||
|
self.static_lists = StaticListsApi(*args)
|
||||||
|
self.translations = TranslationsApi(*args)
|
||||||
|
self.webappdata = WebAppDataApi(*args)
|
||||||
|
self.workspaces = WorkspacesApi(*args)
|
||||||
|
|
||||||
|
# ------------------------------------------------------------------
|
||||||
|
# Convenience read-only properties
|
||||||
|
# ------------------------------------------------------------------
|
||||||
|
|
||||||
|
@property
|
||||||
|
def config(self) -> AgravityConfig:
|
||||||
|
"""The current client configuration."""
|
||||||
|
return self._config
|
||||||
48
agravity_client/config.py
Normal file
48
agravity_client/config.py
Normal file
|
|
@ -0,0 +1,48 @@
|
||||||
|
"""Agravity client configuration using Pydantic Settings."""
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from pydantic import Field, field_validator
|
||||||
|
from pydantic_settings import BaseSettings, SettingsConfigDict
|
||||||
|
|
||||||
|
|
||||||
|
class AgravityConfig(BaseSettings):
|
||||||
|
"""Configuration for the Agravity API client.
|
||||||
|
|
||||||
|
Values can be provided via environment variables or directly in code.
|
||||||
|
|
||||||
|
Environment variable names:
|
||||||
|
AGRAVITY_BASE_URL
|
||||||
|
AGRAVITY_API_KEY
|
||||||
|
AGRAVITY_TIMEOUT
|
||||||
|
AGRAVITY_VERIFY_SSL
|
||||||
|
"""
|
||||||
|
|
||||||
|
model_config = SettingsConfigDict(
|
||||||
|
env_prefix="AGRAVITY_",
|
||||||
|
env_file=".env",
|
||||||
|
env_file_encoding="utf-8",
|
||||||
|
case_sensitive=False,
|
||||||
|
extra="ignore",
|
||||||
|
)
|
||||||
|
|
||||||
|
base_url: str = Field(
|
||||||
|
default="https://devagravitypublic.azurewebsites.net/api",
|
||||||
|
description="The base URL for the Agravity API (without trailing slash).",
|
||||||
|
)
|
||||||
|
api_key: str = Field(
|
||||||
|
default="",
|
||||||
|
description="The API key (x-functions-key) used for authentication.",
|
||||||
|
)
|
||||||
|
timeout: float = Field(
|
||||||
|
default=60.0,
|
||||||
|
description="Default HTTP request timeout in seconds.",
|
||||||
|
)
|
||||||
|
verify_ssl: bool = Field(
|
||||||
|
default=True,
|
||||||
|
description="Whether to verify SSL certificates.",
|
||||||
|
)
|
||||||
|
|
||||||
|
@field_validator("base_url")
|
||||||
|
@classmethod
|
||||||
|
def strip_trailing_slash(cls, v: str) -> str:
|
||||||
|
return v.rstrip("/")
|
||||||
59
agravity_client/exceptions.py
Normal file
59
agravity_client/exceptions.py
Normal file
|
|
@ -0,0 +1,59 @@
|
||||||
|
"""Custom exceptions for the Agravity API client."""
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from typing import Optional
|
||||||
|
|
||||||
|
from agravity_client.models.common import AgravityErrorResponse
|
||||||
|
|
||||||
|
|
||||||
|
class AgravityError(Exception):
|
||||||
|
"""Base exception for all Agravity client errors."""
|
||||||
|
|
||||||
|
|
||||||
|
class AgravityAPIError(AgravityError):
|
||||||
|
"""Raised when the API returns a non-2xx response.
|
||||||
|
|
||||||
|
Attributes:
|
||||||
|
status_code: The HTTP status code returned by the server.
|
||||||
|
error_response: The parsed AgravityErrorResponse body, if available.
|
||||||
|
raw_body: The raw response text, if parsing failed.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(
|
||||||
|
self,
|
||||||
|
status_code: int,
|
||||||
|
error_response: Optional[AgravityErrorResponse] = None,
|
||||||
|
raw_body: Optional[str] = None,
|
||||||
|
) -> None:
|
||||||
|
self.status_code = status_code
|
||||||
|
self.error_response = error_response
|
||||||
|
self.raw_body = raw_body
|
||||||
|
|
||||||
|
if error_response and error_response.error_message:
|
||||||
|
msg = f"HTTP {status_code}: {error_response.error_message}"
|
||||||
|
elif raw_body:
|
||||||
|
msg = f"HTTP {status_code}: {raw_body[:200]}"
|
||||||
|
else:
|
||||||
|
msg = f"HTTP {status_code}"
|
||||||
|
|
||||||
|
super().__init__(msg)
|
||||||
|
|
||||||
|
|
||||||
|
class AgravityAuthenticationError(AgravityAPIError):
|
||||||
|
"""Raised when the API returns a 401 Unauthorized response."""
|
||||||
|
|
||||||
|
|
||||||
|
class AgravityNotFoundError(AgravityAPIError):
|
||||||
|
"""Raised when the API returns a 404 Not Found response."""
|
||||||
|
|
||||||
|
|
||||||
|
class AgravityValidationError(AgravityAPIError):
|
||||||
|
"""Raised when the API returns a 400 Bad Request / 422 response."""
|
||||||
|
|
||||||
|
|
||||||
|
class AgravityTimeoutError(AgravityError):
|
||||||
|
"""Raised when an HTTP request times out."""
|
||||||
|
|
||||||
|
|
||||||
|
class AgravityConnectionError(AgravityError):
|
||||||
|
"""Raised when a network-level connection error occurs."""
|
||||||
138
agravity_client/models/__init__.py
Normal file
138
agravity_client/models/__init__.py
Normal file
|
|
@ -0,0 +1,138 @@
|
||||||
|
"""Agravity DAM – Pydantic models (re-exported from sub-modules)."""
|
||||||
|
|
||||||
|
from agravity_client.models.asset import (
|
||||||
|
Asset,
|
||||||
|
AssetAvailability,
|
||||||
|
AssetBlob,
|
||||||
|
AssetBlobOrientation,
|
||||||
|
AssetBlobType,
|
||||||
|
AssetBulkUpdate,
|
||||||
|
AssetCheckout,
|
||||||
|
AssetIconRule,
|
||||||
|
AssetIdFormat,
|
||||||
|
AssetPageResult,
|
||||||
|
AssetRole,
|
||||||
|
)
|
||||||
|
from agravity_client.models.auth import (
|
||||||
|
AgravityUser,
|
||||||
|
AgravityUserOnlineStatus,
|
||||||
|
AgravityVersion,
|
||||||
|
SasToken,
|
||||||
|
)
|
||||||
|
from agravity_client.models.collection import (
|
||||||
|
CollTypeItem,
|
||||||
|
Collection,
|
||||||
|
CollectionType,
|
||||||
|
CollectionUDL,
|
||||||
|
CollectionUDLListEntity,
|
||||||
|
CollectionUDLReference,
|
||||||
|
EntityListResult,
|
||||||
|
EntityNamesRequest,
|
||||||
|
MoveCollectionBody,
|
||||||
|
PermissionEntity,
|
||||||
|
PermissionRole,
|
||||||
|
)
|
||||||
|
from agravity_client.models.common import (
|
||||||
|
AgravityErrorResponse,
|
||||||
|
AgravityInfoResponse,
|
||||||
|
DataResult,
|
||||||
|
DeletedEntities,
|
||||||
|
DictionaryObject,
|
||||||
|
EntityId,
|
||||||
|
EntityIdName,
|
||||||
|
FrontendAppConfig,
|
||||||
|
SignalRConnectionInfo,
|
||||||
|
WhereParam,
|
||||||
|
WhereParamOperator,
|
||||||
|
WhereParamValueType,
|
||||||
|
)
|
||||||
|
from agravity_client.models.download import (
|
||||||
|
DistZipResponse,
|
||||||
|
DownloadFormat,
|
||||||
|
DownloadZipRequest,
|
||||||
|
DownloadZipStatus,
|
||||||
|
DynamicImageOperation,
|
||||||
|
SharedAllowedFormat,
|
||||||
|
ZipType,
|
||||||
|
)
|
||||||
|
from agravity_client.models.portal import (
|
||||||
|
Portal,
|
||||||
|
PortalAuthentication,
|
||||||
|
PortalAuthMethod,
|
||||||
|
PortalConfiguration,
|
||||||
|
PortalFields,
|
||||||
|
PortalLinks,
|
||||||
|
PortalTheme,
|
||||||
|
PortalUserContext,
|
||||||
|
)
|
||||||
|
from agravity_client.models.publish import PublishedAsset, PublishEntity
|
||||||
|
from agravity_client.models.relation import AssetRelation, AssetRelationType, RelatedAsset
|
||||||
|
from agravity_client.models.search import (
|
||||||
|
AllWebAppData,
|
||||||
|
AzSearchOptions,
|
||||||
|
GroupAllAppData,
|
||||||
|
SavedSearch,
|
||||||
|
SearchableItem,
|
||||||
|
SearchAdminDataSourceStatus,
|
||||||
|
SearchAdminIndexerLastRun,
|
||||||
|
SearchAdminIndexerStatus,
|
||||||
|
SearchAdminIndexStatus,
|
||||||
|
SearchAdminSkillStatus,
|
||||||
|
SearchAdminStatistics,
|
||||||
|
SearchAdminStatus,
|
||||||
|
SearchFacet,
|
||||||
|
SearchFacetEntity,
|
||||||
|
SearchResult,
|
||||||
|
)
|
||||||
|
from agravity_client.models.secure_upload import SecureUploadEntity
|
||||||
|
from agravity_client.models.sharing import (
|
||||||
|
QuickShareFull,
|
||||||
|
SharedAsset,
|
||||||
|
SharedCollectionFull,
|
||||||
|
)
|
||||||
|
from agravity_client.models.static_lists import StaticDefinedList
|
||||||
|
from agravity_client.models.versioning import VersionedAsset, VersionEntity
|
||||||
|
from agravity_client.models.workspace import Workspace
|
||||||
|
|
||||||
|
__all__ = [
|
||||||
|
# asset
|
||||||
|
"Asset", "AssetAvailability", "AssetBlob", "AssetBlobOrientation",
|
||||||
|
"AssetBlobType", "AssetBulkUpdate", "AssetCheckout", "AssetIconRule",
|
||||||
|
"AssetIdFormat", "AssetPageResult", "AssetRole",
|
||||||
|
# auth
|
||||||
|
"AgravityUser", "AgravityUserOnlineStatus", "AgravityVersion", "SasToken",
|
||||||
|
# collection
|
||||||
|
"CollTypeItem", "Collection", "CollectionType", "CollectionUDL",
|
||||||
|
"CollectionUDLListEntity", "CollectionUDLReference", "EntityListResult",
|
||||||
|
"EntityNamesRequest", "MoveCollectionBody", "PermissionEntity", "PermissionRole",
|
||||||
|
# common
|
||||||
|
"AgravityErrorResponse", "AgravityInfoResponse", "DataResult", "DeletedEntities",
|
||||||
|
"DictionaryObject", "EntityId", "EntityIdName", "FrontendAppConfig",
|
||||||
|
"SignalRConnectionInfo", "WhereParam", "WhereParamOperator", "WhereParamValueType",
|
||||||
|
# download
|
||||||
|
"DistZipResponse", "DownloadFormat", "DownloadZipRequest", "DownloadZipStatus",
|
||||||
|
"DynamicImageOperation", "SharedAllowedFormat", "ZipType",
|
||||||
|
# portal
|
||||||
|
"Portal", "PortalAuthentication", "PortalAuthMethod", "PortalConfiguration",
|
||||||
|
"PortalFields", "PortalLinks", "PortalTheme", "PortalUserContext",
|
||||||
|
# publish
|
||||||
|
"PublishedAsset", "PublishEntity",
|
||||||
|
# relation
|
||||||
|
"AssetRelation", "AssetRelationType", "RelatedAsset",
|
||||||
|
# search
|
||||||
|
"AllWebAppData", "AzSearchOptions", "GroupAllAppData", "SavedSearch",
|
||||||
|
"SearchableItem", "SearchAdminDataSourceStatus", "SearchAdminIndexerLastRun",
|
||||||
|
"SearchAdminIndexerStatus", "SearchAdminIndexStatus", "SearchAdminSkillStatus",
|
||||||
|
"SearchAdminStatistics", "SearchAdminStatus", "SearchFacet", "SearchFacetEntity",
|
||||||
|
"SearchResult",
|
||||||
|
# secure upload
|
||||||
|
"SecureUploadEntity",
|
||||||
|
# sharing
|
||||||
|
"QuickShareFull", "SharedAsset", "SharedCollectionFull",
|
||||||
|
# static lists
|
||||||
|
"StaticDefinedList",
|
||||||
|
# versioning
|
||||||
|
"VersionedAsset", "VersionEntity",
|
||||||
|
# workspace
|
||||||
|
"Workspace",
|
||||||
|
]
|
||||||
208
agravity_client/models/asset.py
Normal file
208
agravity_client/models/asset.py
Normal file
|
|
@ -0,0 +1,208 @@
|
||||||
|
"""Asset-related Pydantic models."""
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from enum import Enum
|
||||||
|
from typing import Any, Optional
|
||||||
|
|
||||||
|
from pydantic import ConfigDict, Field
|
||||||
|
|
||||||
|
from agravity_client.models.common import DictionaryObject, WhereParam, _Base
|
||||||
|
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# Enums
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
class AssetBlobType(str, Enum):
|
||||||
|
unknown = "UNKNOWN"
|
||||||
|
image = "IMAGE"
|
||||||
|
video = "VIDEO"
|
||||||
|
audio = "AUDIO"
|
||||||
|
document = "DOCUMENT"
|
||||||
|
text = "TEXT"
|
||||||
|
other = "OTHER"
|
||||||
|
|
||||||
|
|
||||||
|
class AssetBlobOrientation(str, Enum):
|
||||||
|
portrait = "PORTRAIT"
|
||||||
|
landscape = "LANDSCAPE"
|
||||||
|
square = "SQUARE"
|
||||||
|
|
||||||
|
|
||||||
|
class AssetRole(str, Enum):
|
||||||
|
none = "NONE"
|
||||||
|
viewer = "VIEWER"
|
||||||
|
editor = "EDITOR"
|
||||||
|
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# AssetBlob
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
class AssetBlob(_Base):
|
||||||
|
blob_type: Optional[AssetBlobType] = AssetBlobType.unknown
|
||||||
|
name: Optional[str] = None
|
||||||
|
container: Optional[str] = None
|
||||||
|
size: Optional[int] = None
|
||||||
|
extension: Optional[str] = None
|
||||||
|
content_type: Optional[str] = None
|
||||||
|
md5: Optional[str] = None
|
||||||
|
add_data: Optional[dict[str, Any]] = None
|
||||||
|
|
||||||
|
# image metadata
|
||||||
|
width: Optional[int] = None
|
||||||
|
height: Optional[int] = None
|
||||||
|
maxwidthheight: Optional[int] = None
|
||||||
|
quality: Optional[float] = None
|
||||||
|
orientation: Optional[AssetBlobOrientation] = AssetBlobOrientation.portrait
|
||||||
|
colorspace: Optional[str] = None
|
||||||
|
profile: Optional[str] = None
|
||||||
|
transparency: Optional[bool] = None
|
||||||
|
mode: Optional[str] = None
|
||||||
|
target: Optional[str] = None
|
||||||
|
filter: Optional[str] = None
|
||||||
|
dpi_x: Optional[float] = None
|
||||||
|
dpi_y: Optional[float] = None
|
||||||
|
perhash: Optional[str] = None
|
||||||
|
dominantcolor: Optional[str] = None
|
||||||
|
depth: Optional[int] = None
|
||||||
|
animated: Optional[bool] = None
|
||||||
|
|
||||||
|
# video metadata
|
||||||
|
duration: Optional[int] = None
|
||||||
|
videocodec: Optional[str] = None
|
||||||
|
videobitrate: Optional[int] = None
|
||||||
|
fps: Optional[float] = None
|
||||||
|
colormode: Optional[str] = None
|
||||||
|
|
||||||
|
# audio metadata
|
||||||
|
audiocodec: Optional[str] = None
|
||||||
|
audiosamplerate: Optional[str] = None
|
||||||
|
audiochanneloutput: Optional[str] = None
|
||||||
|
audiobitrate: Optional[int] = None
|
||||||
|
|
||||||
|
# document metadata
|
||||||
|
author: Optional[str] = None
|
||||||
|
title: Optional[str] = None
|
||||||
|
language: Optional[str] = None
|
||||||
|
wordcount: Optional[int] = None
|
||||||
|
pages: Optional[int] = None
|
||||||
|
encoding_name: Optional[str] = None
|
||||||
|
encoding_code: Optional[str] = None
|
||||||
|
|
||||||
|
# URL / download info
|
||||||
|
url: Optional[str] = None
|
||||||
|
size_readable: Optional[str] = None
|
||||||
|
downloadable: Optional[bool] = None
|
||||||
|
expires: Optional[str] = Field(None, description="ISO 8601 date-time string")
|
||||||
|
uploaded_date: Optional[str] = Field(None, description="ISO 8601 date-time string")
|
||||||
|
uploaded_by: Optional[str] = None
|
||||||
|
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# AssetCheckout
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
class AssetCheckout(_Base):
|
||||||
|
user_id: Optional[str] = None
|
||||||
|
checkout_date: Optional[str] = Field(None, description="ISO 8601 date-time string")
|
||||||
|
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# AssetIconRule
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
class AssetIconRule(_Base):
|
||||||
|
id: Optional[str] = None
|
||||||
|
entity_type: Optional[str] = None
|
||||||
|
type: Optional[str] = None
|
||||||
|
path: Optional[str] = None
|
||||||
|
color: Optional[str] = None
|
||||||
|
value: Optional[str] = None
|
||||||
|
icon: Optional[str] = None
|
||||||
|
operator: Optional[str] = None
|
||||||
|
translations: Optional[dict[str, DictionaryObject]] = None
|
||||||
|
name: Optional[str] = None
|
||||||
|
description: Optional[str] = None
|
||||||
|
add_properties: Optional[dict[str, Any]] = None
|
||||||
|
status: Optional[str] = None
|
||||||
|
created_date: Optional[str] = None
|
||||||
|
created_by: Optional[str] = None
|
||||||
|
modified_date: Optional[str] = None
|
||||||
|
modified_by: Optional[str] = None
|
||||||
|
pk: Optional[str] = None
|
||||||
|
etag: Optional[str] = Field(None, alias="_etag")
|
||||||
|
|
||||||
|
model_config = ConfigDict(extra="allow", populate_by_name=True)
|
||||||
|
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# Asset (core entity)
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
class Asset(_Base):
|
||||||
|
id: Optional[str] = None
|
||||||
|
entity_type: Optional[str] = None
|
||||||
|
name: Optional[str] = None
|
||||||
|
asset_type: Optional[str] = None
|
||||||
|
duplicates: Optional[list[str]] = None
|
||||||
|
keywords: Optional[list[str]] = None
|
||||||
|
orig_blob: Optional[AssetBlob] = None
|
||||||
|
blobs: Optional[list[AssetBlob]] = None
|
||||||
|
collections: Optional[list[str]] = None
|
||||||
|
failed_reason: Optional[str] = None
|
||||||
|
quality_gate: Optional[list[str]] = None
|
||||||
|
region_of_origin: Optional[str] = None
|
||||||
|
availability: Optional[str] = None
|
||||||
|
available_from: Optional[str] = Field(None, description="ISO 8601 date-time string")
|
||||||
|
available_to: Optional[str] = Field(None, description="ISO 8601 date-time string")
|
||||||
|
checkout: Optional[AssetCheckout] = None
|
||||||
|
fs_synced: Optional[bool] = None
|
||||||
|
custom: Optional[dict[str, Any]] = None
|
||||||
|
items: Optional[list[Any]] = None # List[CollTypeItem] – to avoid circular import
|
||||||
|
translations: Optional[dict[str, DictionaryObject]] = None
|
||||||
|
role: Optional[AssetRole] = AssetRole.none
|
||||||
|
description: Optional[str] = None
|
||||||
|
add_properties: Optional[dict[str, Any]] = None
|
||||||
|
status: Optional[str] = None
|
||||||
|
created_date: Optional[str] = None
|
||||||
|
created_by: Optional[str] = None
|
||||||
|
modified_date: Optional[str] = None
|
||||||
|
modified_by: Optional[str] = None
|
||||||
|
pk: Optional[str] = None
|
||||||
|
etag: Optional[str] = Field(None, alias="_etag")
|
||||||
|
|
||||||
|
model_config = ConfigDict(extra="allow", populate_by_name=True)
|
||||||
|
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# AssetAvailability
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
class AssetAvailability(_Base):
|
||||||
|
availability: Optional[str] = None
|
||||||
|
available_from: Optional[str] = Field(None, description="ISO 8601 date-time string")
|
||||||
|
available_to: Optional[str] = Field(None, description="ISO 8601 date-time string")
|
||||||
|
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# AssetBulkUpdate
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
class AssetBulkUpdate(_Base):
|
||||||
|
collection_id: Optional[str] = None
|
||||||
|
ref_asset: Optional[Asset] = None
|
||||||
|
asset_ids: Optional[list[str]] = None
|
||||||
|
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# AssetPageResult
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
class AssetPageResult(_Base):
|
||||||
|
page: Optional[list[Asset]] = None
|
||||||
|
page_size: Optional[int] = None
|
||||||
|
size: Optional[int] = None
|
||||||
|
continuation_token: Optional[str] = None
|
||||||
|
filter: Optional[list[WhereParam]] = None
|
||||||
|
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# AssetIdFormat (used in QuickShareFull)
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
class AssetIdFormat(_Base):
|
||||||
|
id: Optional[str] = None
|
||||||
|
name: Optional[str] = None
|
||||||
|
format: Optional[str] = None
|
||||||
58
agravity_client/models/auth.py
Normal file
58
agravity_client/models/auth.py
Normal file
|
|
@ -0,0 +1,58 @@
|
||||||
|
"""Auth-related Pydantic models."""
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from typing import Optional
|
||||||
|
|
||||||
|
from pydantic import ConfigDict, Field
|
||||||
|
|
||||||
|
from agravity_client.models.common import _Base
|
||||||
|
|
||||||
|
|
||||||
|
class AgravityUserOnlineStatus(_Base):
|
||||||
|
status: Optional[str] = None
|
||||||
|
last_connection: Optional[str] = Field(None, description="ISO 8601 date-time string")
|
||||||
|
|
||||||
|
|
||||||
|
class AgravityUser(_Base):
|
||||||
|
id: Optional[str] = None
|
||||||
|
entity_type: Optional[str] = None
|
||||||
|
name: Optional[str] = None
|
||||||
|
email: Optional[str] = None
|
||||||
|
impersonation: Optional[str] = None
|
||||||
|
apikey: Optional[str] = None
|
||||||
|
online_status: Optional[AgravityUserOnlineStatus] = None
|
||||||
|
roles: Optional[list[str]] = None
|
||||||
|
groups: Optional[list[str]] = None
|
||||||
|
status: Optional[str] = None
|
||||||
|
created_date: Optional[str] = None
|
||||||
|
created_by: Optional[str] = None
|
||||||
|
modified_date: Optional[str] = None
|
||||||
|
modified_by: Optional[str] = None
|
||||||
|
pk: Optional[str] = None
|
||||||
|
etag: Optional[str] = Field(None, alias="_etag")
|
||||||
|
|
||||||
|
model_config = ConfigDict(extra="allow", populate_by_name=True)
|
||||||
|
|
||||||
|
|
||||||
|
class AgravityVersion(_Base):
|
||||||
|
name: Optional[str] = None
|
||||||
|
company: Optional[str] = None
|
||||||
|
customer: Optional[str] = None
|
||||||
|
contact: Optional[str] = None
|
||||||
|
updated: Optional[str] = Field(None, description="ISO 8601 date-time string")
|
||||||
|
client_id: Optional[str] = None
|
||||||
|
tenant_id: Optional[str] = None
|
||||||
|
subscription_id: Optional[str] = None
|
||||||
|
base: Optional[str] = None
|
||||||
|
version: Optional[str] = None
|
||||||
|
enabled_features: Optional[list[str]] = None
|
||||||
|
region: Optional[str] = None
|
||||||
|
|
||||||
|
|
||||||
|
class SasToken(_Base):
|
||||||
|
token: Optional[str] = None
|
||||||
|
container: Optional[str] = None
|
||||||
|
blob: Optional[str] = None
|
||||||
|
url: Optional[str] = None
|
||||||
|
fulltoken: Optional[str] = None
|
||||||
|
expires: Optional[str] = Field(None, description="ISO 8601 date-time string")
|
||||||
166
agravity_client/models/collection.py
Normal file
166
agravity_client/models/collection.py
Normal file
|
|
@ -0,0 +1,166 @@
|
||||||
|
"""Collection-related Pydantic models."""
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from enum import Enum
|
||||||
|
from typing import Any, Optional
|
||||||
|
|
||||||
|
from pydantic import ConfigDict, Field
|
||||||
|
|
||||||
|
from agravity_client.models.common import DictionaryObject, _Base
|
||||||
|
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# Enums
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
class PermissionRole(str, Enum):
|
||||||
|
none = "NONE"
|
||||||
|
viewer = "VIEWER"
|
||||||
|
editor = "EDITOR"
|
||||||
|
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# PermissionEntity
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
class PermissionEntity(_Base):
|
||||||
|
id: Optional[str] = None
|
||||||
|
role: Optional[PermissionRole] = PermissionRole.none
|
||||||
|
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# CollTypeItem
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
class CollTypeItem(_Base):
|
||||||
|
id: Optional[str] = None
|
||||||
|
entity_type: Optional[str] = None
|
||||||
|
name: Optional[str] = None
|
||||||
|
item_type: Optional[str] = None
|
||||||
|
format: Optional[str] = None
|
||||||
|
label: Optional[str] = None
|
||||||
|
default_value: Optional[Any] = None
|
||||||
|
mandatory: Optional[bool] = None
|
||||||
|
searchable: Optional[bool] = None
|
||||||
|
onlyasset: Optional[bool] = None
|
||||||
|
multi: Optional[bool] = None
|
||||||
|
md5: Optional[str] = None
|
||||||
|
group: Optional[str] = None
|
||||||
|
order: Optional[int] = None
|
||||||
|
translations: Optional[dict[str, DictionaryObject]] = None
|
||||||
|
status: Optional[str] = None
|
||||||
|
created_date: Optional[str] = None
|
||||||
|
created_by: Optional[str] = None
|
||||||
|
modified_date: Optional[str] = None
|
||||||
|
modified_by: Optional[str] = None
|
||||||
|
pk: Optional[str] = None
|
||||||
|
etag: Optional[str] = Field(None, alias="_etag")
|
||||||
|
|
||||||
|
model_config = ConfigDict(extra="allow", populate_by_name=True)
|
||||||
|
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# Collection
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
class Collection(_Base):
|
||||||
|
id: Optional[str] = None
|
||||||
|
entity_type: Optional[str] = None
|
||||||
|
parent: Optional[str] = None
|
||||||
|
path: Optional[str] = None
|
||||||
|
level: Optional[int] = None
|
||||||
|
custom: Optional[dict[str, Any]] = None
|
||||||
|
items: Optional[list[CollTypeItem]] = None
|
||||||
|
translations: Optional[dict[str, DictionaryObject]] = None
|
||||||
|
role: Optional[PermissionRole] = PermissionRole.none
|
||||||
|
name: Optional[str] = None
|
||||||
|
description: Optional[str] = None
|
||||||
|
add_properties: Optional[dict[str, Any]] = None
|
||||||
|
status: Optional[str] = None
|
||||||
|
created_date: Optional[str] = None
|
||||||
|
created_by: Optional[str] = None
|
||||||
|
modified_date: Optional[str] = None
|
||||||
|
modified_by: Optional[str] = None
|
||||||
|
pk: Optional[str] = None
|
||||||
|
etag: Optional[str] = Field(None, alias="_etag")
|
||||||
|
|
||||||
|
model_config = ConfigDict(extra="allow", populate_by_name=True)
|
||||||
|
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# CollectionType
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
class CollectionType(_Base):
|
||||||
|
id: Optional[str] = None
|
||||||
|
entity_type: Optional[str] = None
|
||||||
|
name: Optional[str] = None
|
||||||
|
items: Optional[list[CollTypeItem]] = None
|
||||||
|
translations: Optional[dict[str, DictionaryObject]] = None
|
||||||
|
order: Optional[int] = None
|
||||||
|
permissions: Optional[list[PermissionEntity]] = None
|
||||||
|
permissionless: Optional[bool] = None
|
||||||
|
role: Optional[PermissionRole] = PermissionRole.none
|
||||||
|
description: Optional[str] = None
|
||||||
|
add_properties: Optional[dict[str, Any]] = None
|
||||||
|
status: Optional[str] = None
|
||||||
|
created_date: Optional[str] = None
|
||||||
|
created_by: Optional[str] = None
|
||||||
|
modified_date: Optional[str] = None
|
||||||
|
modified_by: Optional[str] = None
|
||||||
|
pk: Optional[str] = None
|
||||||
|
etag: Optional[str] = Field(None, alias="_etag")
|
||||||
|
|
||||||
|
model_config = ConfigDict(extra="allow", populate_by_name=True)
|
||||||
|
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# CollectionUDL (user-defined list)
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
class CollectionUDL(_Base):
|
||||||
|
id: Optional[str] = None
|
||||||
|
pk: Optional[str] = None
|
||||||
|
name: Optional[str] = None
|
||||||
|
entity_type: Optional[str] = None
|
||||||
|
status: Optional[str] = None
|
||||||
|
translations: Optional[dict[str, DictionaryObject]] = None
|
||||||
|
children: Optional[list[Any]] = None # List[EntityIdName]
|
||||||
|
|
||||||
|
|
||||||
|
class CollectionUDLReference(_Base):
|
||||||
|
parent: Optional[str] = None
|
||||||
|
coll_types: Optional[list[str]] = None
|
||||||
|
permissions: Optional[list[PermissionEntity]] = None
|
||||||
|
|
||||||
|
|
||||||
|
class CollectionUDLListEntity(_Base):
|
||||||
|
id: Optional[str] = None
|
||||||
|
entity_type: Optional[str] = None
|
||||||
|
udl_refs: Optional[list[CollectionUDLReference]] = None
|
||||||
|
udl_entries: Optional[list[CollectionUDL]] = None
|
||||||
|
status: Optional[str] = None
|
||||||
|
created_date: Optional[str] = None
|
||||||
|
created_by: Optional[str] = None
|
||||||
|
modified_date: Optional[str] = None
|
||||||
|
modified_by: Optional[str] = None
|
||||||
|
pk: Optional[str] = None
|
||||||
|
etag: Optional[str] = Field(None, alias="_etag")
|
||||||
|
|
||||||
|
model_config = ConfigDict(extra="allow", populate_by_name=True)
|
||||||
|
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# EntityNamesRequest / EntityListResult
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
class EntityNamesRequest(_Base):
|
||||||
|
names: Optional[list[str]] = None
|
||||||
|
filter: Optional[str] = None
|
||||||
|
|
||||||
|
|
||||||
|
class EntityListResult(_Base):
|
||||||
|
entities: Optional[list[Any]] = None # List[EntityIdName]
|
||||||
|
notfounds: Optional[list[str]] = None
|
||||||
|
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# MoveCollectionBody
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
class MoveCollectionBody(_Base):
|
||||||
|
from_collection_id: Optional[str] = None
|
||||||
|
to_collection_id: Optional[str] = None
|
||||||
|
operation: Optional[str] = None
|
||||||
131
agravity_client/models/common.py
Normal file
131
agravity_client/models/common.py
Normal file
|
|
@ -0,0 +1,131 @@
|
||||||
|
"""Common / shared Pydantic models for the Agravity API."""
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from enum import Enum
|
||||||
|
from typing import Any, Optional
|
||||||
|
|
||||||
|
from pydantic import BaseModel, ConfigDict, Field
|
||||||
|
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# Shared Config mixin: allow extra fields (the API may return undocumented ones)
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
class _Base(BaseModel):
|
||||||
|
model_config = ConfigDict(extra="allow", populate_by_name=True)
|
||||||
|
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# Error / Info responses
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
class AgravityErrorResponse(_Base):
|
||||||
|
error_id: Optional[str] = None
|
||||||
|
error_message: Optional[str] = None
|
||||||
|
exception: Optional[str] = None
|
||||||
|
|
||||||
|
|
||||||
|
class AgravityInfoResponse(_Base):
|
||||||
|
info_id: Optional[str] = None
|
||||||
|
info_message: Optional[str] = None
|
||||||
|
info_object: Optional[Any] = None
|
||||||
|
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# Dictionary helper
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
class DictionaryObject(_Base):
|
||||||
|
"""A freeform dictionary of key -> any value (used for translations etc.)"""
|
||||||
|
|
||||||
|
model_config = ConfigDict(extra="allow")
|
||||||
|
|
||||||
|
def to_dict(self) -> dict[str, Any]:
|
||||||
|
return dict(self.model_extra or {})
|
||||||
|
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# EntityId / EntityIdName
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
class EntityId(_Base):
|
||||||
|
id: Optional[str] = None
|
||||||
|
pk: Optional[str] = None
|
||||||
|
|
||||||
|
|
||||||
|
class EntityIdName(_Base):
|
||||||
|
id: Optional[str] = None
|
||||||
|
pk: Optional[str] = None
|
||||||
|
name: Optional[str] = None
|
||||||
|
entity_type: Optional[str] = None
|
||||||
|
status: Optional[str] = None
|
||||||
|
translations: Optional[dict[str, DictionaryObject]] = None
|
||||||
|
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# Frontend config
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
class FrontendAppConfig(_Base):
|
||||||
|
key: Optional[str] = None
|
||||||
|
value: Optional[str] = None
|
||||||
|
description: Optional[str] = None
|
||||||
|
contentType: Optional[str] = None
|
||||||
|
sinceApiVersion: Optional[str] = None
|
||||||
|
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# SignalR
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
class SignalRConnectionInfo(_Base):
|
||||||
|
url: Optional[str] = None
|
||||||
|
accessToken: Optional[str] = None
|
||||||
|
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# Deleted Entities
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
class DeletedEntities(_Base):
|
||||||
|
id: Optional[str] = None
|
||||||
|
name: Optional[str] = None
|
||||||
|
deleted: Optional[str] = Field(None, description="ISO 8601 date-time string")
|
||||||
|
entity_type: Optional[str] = None
|
||||||
|
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# DataResult (search sub-model)
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
class DataResult(_Base):
|
||||||
|
asset: Optional[list[Any]] = None # List[Asset] – forward ref resolved later
|
||||||
|
sum_asset_results: Optional[int] = None
|
||||||
|
collection: Optional[list[Any]] = None # List[Collection]
|
||||||
|
sum_collection_results: Optional[int] = None
|
||||||
|
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# WhereParam (filter parameter in AssetPageResult)
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
class WhereParamOperator(str, Enum):
|
||||||
|
equals = "Equals"
|
||||||
|
not_equals = "NotEquals"
|
||||||
|
greater_than = "GreaterThan"
|
||||||
|
less_than = "LessThan"
|
||||||
|
greater_than_or_equal = "GreaterThanOrEqual"
|
||||||
|
less_than_or_equal = "LessThanOrEqual"
|
||||||
|
contains = "Contains"
|
||||||
|
starts_with = "StartsWith"
|
||||||
|
array_contains = "ArrayContains"
|
||||||
|
array_contains_partial = "ArrayContainsPartial"
|
||||||
|
is_defined = "IsDefined"
|
||||||
|
is_not_defined = "IsNotDefined"
|
||||||
|
is_empty = "IsEmpty"
|
||||||
|
is_not_empty = "IsNotEmpty"
|
||||||
|
|
||||||
|
|
||||||
|
class WhereParamValueType(str, Enum):
|
||||||
|
string = "String"
|
||||||
|
bool = "Bool"
|
||||||
|
number = "Number"
|
||||||
|
|
||||||
|
|
||||||
|
class WhereParam(_Base):
|
||||||
|
operator: Optional[WhereParamOperator] = WhereParamOperator.equals
|
||||||
|
field: Optional[str] = None
|
||||||
|
value: Optional[Any] = None
|
||||||
|
notPrefix: Optional[bool] = None
|
||||||
|
valueType: Optional[WhereParamValueType] = WhereParamValueType.string
|
||||||
104
agravity_client/models/download.py
Normal file
104
agravity_client/models/download.py
Normal file
|
|
@ -0,0 +1,104 @@
|
||||||
|
"""Download-format and ZIP-related Pydantic models."""
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from enum import Enum
|
||||||
|
from typing import Any, Optional
|
||||||
|
|
||||||
|
from pydantic import ConfigDict, Field
|
||||||
|
|
||||||
|
from agravity_client.models.common import DictionaryObject, _Base
|
||||||
|
from agravity_client.models.collection import PermissionEntity
|
||||||
|
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# Enums
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
class ZipType(str, Enum):
|
||||||
|
download = "DOWNLOAD"
|
||||||
|
shared = "SHARED"
|
||||||
|
quickshare = "QUICKSHARE"
|
||||||
|
portal = "PORTAL"
|
||||||
|
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# DynamicImageOperation
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
class DynamicImageOperation(_Base):
|
||||||
|
operation: Optional[str] = None
|
||||||
|
params: Optional[list[Any]] = None
|
||||||
|
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# SharedAllowedFormat
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
class SharedAllowedFormat(_Base):
|
||||||
|
asset_type: Optional[str] = None
|
||||||
|
format: Optional[str] = None
|
||||||
|
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# DownloadFormat
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
class DownloadFormat(_Base):
|
||||||
|
id: Optional[str] = None
|
||||||
|
entity_type: Optional[str] = None
|
||||||
|
operations: Optional[list[DynamicImageOperation]] = None
|
||||||
|
extension: Optional[str] = None
|
||||||
|
asset_type: Optional[str] = None
|
||||||
|
origin: Optional[str] = None
|
||||||
|
fallback_thumb: Optional[bool] = None
|
||||||
|
target_filename: Optional[str] = None
|
||||||
|
translations: Optional[dict[str, DictionaryObject]] = None
|
||||||
|
permissions: Optional[list[PermissionEntity]] = None
|
||||||
|
name: Optional[str] = None
|
||||||
|
description: Optional[str] = None
|
||||||
|
add_properties: Optional[dict[str, Any]] = None
|
||||||
|
status: Optional[str] = None
|
||||||
|
created_date: Optional[str] = None
|
||||||
|
created_by: Optional[str] = None
|
||||||
|
modified_date: Optional[str] = None
|
||||||
|
modified_by: Optional[str] = None
|
||||||
|
pk: Optional[str] = None
|
||||||
|
etag: Optional[str] = Field(None, alias="_etag")
|
||||||
|
|
||||||
|
model_config = ConfigDict(extra="allow", populate_by_name=True)
|
||||||
|
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# DownloadZipRequest
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
class DownloadZipRequest(_Base):
|
||||||
|
id: Optional[str] = None
|
||||||
|
zip_type: Optional[ZipType] = ZipType.download
|
||||||
|
asset_ids: Optional[list[str]] = None
|
||||||
|
allowed_formats: Optional[list[SharedAllowedFormat]] = None
|
||||||
|
zipname: Optional[str] = None
|
||||||
|
email_to: Optional[list[str]] = None
|
||||||
|
message: Optional[str] = None
|
||||||
|
valid_until: Optional[str] = Field(None, description="ISO 8601 date-time string")
|
||||||
|
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# DownloadZipStatus
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
class DownloadZipStatus(_Base):
|
||||||
|
id: Optional[str] = None
|
||||||
|
user: Optional[str] = None
|
||||||
|
percent: Optional[float] = None
|
||||||
|
part: Optional[float] = None
|
||||||
|
count: Optional[int] = None
|
||||||
|
message: Optional[str] = None
|
||||||
|
status: Optional[str] = None
|
||||||
|
zip_type: Optional[ZipType] = ZipType.download
|
||||||
|
zipname: Optional[str] = None
|
||||||
|
size: Optional[str] = None
|
||||||
|
url: Optional[str] = None
|
||||||
|
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# DistZipResponse (used in GroupAllAppData)
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
class DistZipResponse(_Base):
|
||||||
|
url: Optional[str] = None
|
||||||
|
modified_date: Optional[str] = Field(None, description="ISO 8601 date-time string")
|
||||||
|
size: Optional[int] = None
|
||||||
175
agravity_client/models/portal.py
Normal file
175
agravity_client/models/portal.py
Normal file
|
|
@ -0,0 +1,175 @@
|
||||||
|
"""Portal-related Pydantic models."""
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from enum import Enum
|
||||||
|
from typing import Any, Optional
|
||||||
|
|
||||||
|
from pydantic import ConfigDict, Field
|
||||||
|
|
||||||
|
from agravity_client.models.collection import CollTypeItem, PermissionEntity
|
||||||
|
from agravity_client.models.common import DictionaryObject, FrontendAppConfig, _Base
|
||||||
|
from agravity_client.models.download import SharedAllowedFormat
|
||||||
|
from agravity_client.models.asset import AssetIconRule
|
||||||
|
from agravity_client.models.static_lists import StaticDefinedList
|
||||||
|
from agravity_client.models.collection import CollectionUDL
|
||||||
|
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# Enums
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
class PortalAuthMethod(str, Enum):
|
||||||
|
undefined = "UNDEFINED"
|
||||||
|
none = "NONE"
|
||||||
|
password = "PASSWORD"
|
||||||
|
eeid = "EEID"
|
||||||
|
auth0 = "AUTH0"
|
||||||
|
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# PortalAuthentication
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
class PortalAuthentication(_Base):
|
||||||
|
method: Optional[PortalAuthMethod] = PortalAuthMethod.undefined
|
||||||
|
issuer: Optional[str] = None
|
||||||
|
client_id: Optional[str] = None
|
||||||
|
tenant_id: Optional[str] = None
|
||||||
|
password: Optional[str] = None
|
||||||
|
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# PortalUserContext
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
class PortalUserContext(_Base):
|
||||||
|
key: Optional[str] = None
|
||||||
|
mandatory: Optional[bool] = None
|
||||||
|
mapping: Optional[dict[str, str]] = None
|
||||||
|
options: Optional[list[str]] = None
|
||||||
|
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# PortalFields
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
class PortalFields(_Base):
|
||||||
|
name: Optional[str] = None
|
||||||
|
detail_order: Optional[int] = None
|
||||||
|
facet_order: Optional[int] = None
|
||||||
|
labels: Optional[dict[str, str]] = None
|
||||||
|
user_context: Optional[PortalUserContext] = None
|
||||||
|
format: Optional[str] = None
|
||||||
|
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# PortalLinks
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
class PortalLinks(_Base):
|
||||||
|
conditions: Optional[str] = None
|
||||||
|
privacy: Optional[str] = None
|
||||||
|
impressum: Optional[str] = None
|
||||||
|
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# PortalTheme
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
class PortalTheme(_Base):
|
||||||
|
logo_url: Optional[str] = None
|
||||||
|
topbar_color: Optional[str] = None
|
||||||
|
background_url: Optional[str] = None
|
||||||
|
fav_icon: Optional[str] = None
|
||||||
|
icon_empty: Optional[str] = None
|
||||||
|
icon_active: Optional[str] = None
|
||||||
|
colors: Optional[dict[str, Any]] = None
|
||||||
|
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# Portal (base entity)
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
class _PortalBase(_Base):
|
||||||
|
"""Shared fields between Portal and PortalConfiguration."""
|
||||||
|
|
||||||
|
id: Optional[str] = None
|
||||||
|
entity_type: Optional[str] = None
|
||||||
|
authentication: Optional[PortalAuthentication] = None
|
||||||
|
languages: Optional[str] = None
|
||||||
|
fields: Optional[list[PortalFields]] = None
|
||||||
|
filter: Optional[str] = None
|
||||||
|
limit_ids: Optional[list[str]] = None
|
||||||
|
allowed_formats: Optional[list[SharedAllowedFormat]] = None
|
||||||
|
asset_icon_rules: Optional[list[AssetIconRule]] = None
|
||||||
|
allowed_origins: Optional[list[str]] = None
|
||||||
|
links: Optional[PortalLinks] = None
|
||||||
|
theme: Optional[PortalTheme] = None
|
||||||
|
name: Optional[str] = None
|
||||||
|
description: Optional[str] = None
|
||||||
|
add_properties: Optional[dict[str, Any]] = None
|
||||||
|
status: Optional[str] = None
|
||||||
|
created_date: Optional[str] = None
|
||||||
|
created_by: Optional[str] = None
|
||||||
|
modified_date: Optional[str] = None
|
||||||
|
modified_by: Optional[str] = None
|
||||||
|
pk: Optional[str] = None
|
||||||
|
etag: Optional[str] = Field(None, alias="_etag")
|
||||||
|
|
||||||
|
model_config = ConfigDict(extra="allow", populate_by_name=True)
|
||||||
|
|
||||||
|
|
||||||
|
class Portal(_PortalBase):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# PortalConfiguration (extends Portal with full config data)
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
class PortalConfiguration(_PortalBase):
|
||||||
|
download_formats: Optional[list[Any]] = None # List[DownloadFormat]
|
||||||
|
sdls: Optional[list[StaticDefinedList]] = None
|
||||||
|
udls: Optional[list[CollectionUDL]] = None
|
||||||
|
items: Optional[list[CollTypeItem]] = None
|
||||||
|
configs: Optional[list[FrontendAppConfig]] = None
|
||||||
|
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# Custom claims provider response models (token issuance / attribute collection)
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
class CustomClaimsProviderClaims(_Base):
|
||||||
|
userContext: Optional[list[str]] = None
|
||||||
|
role: Optional[str] = None
|
||||||
|
|
||||||
|
|
||||||
|
class CustomClaimsProviderActionTokenIssuanceStart(_Base):
|
||||||
|
claims: Optional[CustomClaimsProviderClaims] = None
|
||||||
|
odata_type: Optional[str] = Field(None, alias="@odata.type")
|
||||||
|
model_config = ConfigDict(extra="allow", populate_by_name=True)
|
||||||
|
|
||||||
|
|
||||||
|
class CustomClaimsProviderActionAttributeCollectionSubmit(_Base):
|
||||||
|
odata_type: Optional[str] = Field(None, alias="@odata.type")
|
||||||
|
model_config = ConfigDict(extra="allow", populate_by_name=True)
|
||||||
|
|
||||||
|
|
||||||
|
class CustomClaimsProviderDataTokenIssuanceStart(_Base):
|
||||||
|
actions: Optional[list[CustomClaimsProviderActionTokenIssuanceStart]] = None
|
||||||
|
odata_type: Optional[str] = Field(None, alias="@odata.type")
|
||||||
|
model_config = ConfigDict(extra="allow", populate_by_name=True)
|
||||||
|
|
||||||
|
|
||||||
|
class CustomClaimsProviderDataAttributeCollectionSubmit(_Base):
|
||||||
|
actions: Optional[list[CustomClaimsProviderActionAttributeCollectionSubmit]] = None
|
||||||
|
odata_type: Optional[str] = Field(None, alias="@odata.type")
|
||||||
|
model_config = ConfigDict(extra="allow", populate_by_name=True)
|
||||||
|
|
||||||
|
|
||||||
|
class CustomClaimsProviderResponseContentTokenIssuanceStart(_Base):
|
||||||
|
data: Optional[CustomClaimsProviderDataTokenIssuanceStart] = None
|
||||||
|
|
||||||
|
|
||||||
|
class CustomClaimsProviderResponseContentAttributeCollectionSubmit(_Base):
|
||||||
|
data: Optional[CustomClaimsProviderDataAttributeCollectionSubmit] = None
|
||||||
|
|
||||||
|
|
||||||
|
# Convenience alias used by the API client
|
||||||
|
from typing import Union as _Union # noqa: E402
|
||||||
|
|
||||||
|
CustomClaimsProviderResponse = _Union[
|
||||||
|
CustomClaimsProviderResponseContentTokenIssuanceStart,
|
||||||
|
CustomClaimsProviderResponseContentAttributeCollectionSubmit,
|
||||||
|
]
|
||||||
39
agravity_client/models/publish.py
Normal file
39
agravity_client/models/publish.py
Normal file
|
|
@ -0,0 +1,39 @@
|
||||||
|
"""Publish-related Pydantic models."""
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from typing import Any, Optional
|
||||||
|
|
||||||
|
from pydantic import ConfigDict, Field
|
||||||
|
|
||||||
|
from agravity_client.models.common import _Base
|
||||||
|
|
||||||
|
|
||||||
|
class PublishedAsset(_Base):
|
||||||
|
id: Optional[str] = None
|
||||||
|
name: Optional[str] = None
|
||||||
|
target: Optional[str] = None
|
||||||
|
description: Optional[str] = None
|
||||||
|
usecases: Optional[list[str]] = None
|
||||||
|
created_date: Optional[str] = None
|
||||||
|
created_by: Optional[str] = None
|
||||||
|
url: Optional[str] = None
|
||||||
|
cdn: Optional[str] = None
|
||||||
|
status_table_id: Optional[str] = None
|
||||||
|
format: Optional[str] = None
|
||||||
|
properties: Optional[dict[str, Any]] = None
|
||||||
|
|
||||||
|
|
||||||
|
class PublishEntity(_Base):
|
||||||
|
id: Optional[str] = None
|
||||||
|
entity_type: Optional[str] = None
|
||||||
|
published: Optional[list[PublishedAsset]] = None
|
||||||
|
region_of_origin: Optional[str] = None
|
||||||
|
status: Optional[str] = None
|
||||||
|
created_date: Optional[str] = None
|
||||||
|
created_by: Optional[str] = None
|
||||||
|
modified_date: Optional[str] = None
|
||||||
|
modified_by: Optional[str] = None
|
||||||
|
pk: Optional[str] = None
|
||||||
|
etag: Optional[str] = Field(None, alias="_etag")
|
||||||
|
|
||||||
|
model_config = ConfigDict(extra="allow", populate_by_name=True)
|
||||||
51
agravity_client/models/relation.py
Normal file
51
agravity_client/models/relation.py
Normal file
|
|
@ -0,0 +1,51 @@
|
||||||
|
"""Asset relation Pydantic models."""
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from typing import Any, Optional
|
||||||
|
|
||||||
|
from pydantic import ConfigDict, Field
|
||||||
|
|
||||||
|
from agravity_client.models.collection import PermissionEntity
|
||||||
|
from agravity_client.models.common import DictionaryObject, _Base
|
||||||
|
|
||||||
|
|
||||||
|
class RelatedAsset(_Base):
|
||||||
|
id: Optional[str] = None
|
||||||
|
parent: Optional[bool] = None
|
||||||
|
|
||||||
|
|
||||||
|
class AssetRelation(_Base):
|
||||||
|
id: Optional[str] = None
|
||||||
|
entity_type: Optional[str] = None
|
||||||
|
assets: Optional[list[RelatedAsset]] = None
|
||||||
|
status: Optional[str] = None
|
||||||
|
created_date: Optional[str] = None
|
||||||
|
created_by: Optional[str] = None
|
||||||
|
modified_date: Optional[str] = None
|
||||||
|
modified_by: Optional[str] = None
|
||||||
|
pk: Optional[str] = None
|
||||||
|
etag: Optional[str] = Field(None, alias="_etag")
|
||||||
|
|
||||||
|
model_config = ConfigDict(extra="allow", populate_by_name=True)
|
||||||
|
|
||||||
|
|
||||||
|
class AssetRelationType(_Base):
|
||||||
|
id: Optional[str] = None
|
||||||
|
entity_type: Optional[str] = None
|
||||||
|
hierarchical: Optional[bool] = None
|
||||||
|
sequential: Optional[bool] = None
|
||||||
|
unique_per_asset: Optional[bool] = None
|
||||||
|
translations: Optional[dict[str, DictionaryObject]] = None
|
||||||
|
permissions: Optional[list[PermissionEntity]] = None
|
||||||
|
name: Optional[str] = None
|
||||||
|
description: Optional[str] = None
|
||||||
|
add_properties: Optional[dict[str, Any]] = None
|
||||||
|
status: Optional[str] = None
|
||||||
|
created_date: Optional[str] = None
|
||||||
|
created_by: Optional[str] = None
|
||||||
|
modified_date: Optional[str] = None
|
||||||
|
modified_by: Optional[str] = None
|
||||||
|
pk: Optional[str] = None
|
||||||
|
etag: Optional[str] = Field(None, alias="_etag")
|
||||||
|
|
||||||
|
model_config = ConfigDict(extra="allow", populate_by_name=True)
|
||||||
172
agravity_client/models/search.py
Normal file
172
agravity_client/models/search.py
Normal file
|
|
@ -0,0 +1,172 @@
|
||||||
|
"""Search-related Pydantic models (includes web-app data and saved searches)."""
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from typing import Any, Optional
|
||||||
|
|
||||||
|
from pydantic import ConfigDict, Field
|
||||||
|
|
||||||
|
from agravity_client.models.asset import Asset
|
||||||
|
from agravity_client.models.collection import Collection, CollectionType
|
||||||
|
from agravity_client.models.common import DictionaryObject, _Base
|
||||||
|
from agravity_client.models.download import DistZipResponse
|
||||||
|
from agravity_client.models.publish import PublishedAsset
|
||||||
|
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# AzSearchOptions
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
class AzSearchOptions(_Base):
|
||||||
|
searchString: Optional[str] = None
|
||||||
|
limit: Optional[int] = None
|
||||||
|
skip: Optional[int] = None
|
||||||
|
collectiontypeid: Optional[str] = None
|
||||||
|
collectionid: Optional[str] = None
|
||||||
|
filter: Optional[str] = None
|
||||||
|
orderby: Optional[str] = None
|
||||||
|
mode: Optional[str] = None
|
||||||
|
broadness: Optional[int] = None
|
||||||
|
rel_id: Optional[str] = None
|
||||||
|
ids: Optional[str] = None
|
||||||
|
portal_id: Optional[str] = None
|
||||||
|
scopefilter: Optional[str] = None
|
||||||
|
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# SearchFacet
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
class SearchFacetEntity(_Base):
|
||||||
|
count: Optional[int] = None
|
||||||
|
value: Optional[str] = None
|
||||||
|
name: Optional[str] = None
|
||||||
|
|
||||||
|
|
||||||
|
class SearchFacet(_Base):
|
||||||
|
name: Optional[str] = None
|
||||||
|
entities: Optional[list[SearchFacetEntity]] = None
|
||||||
|
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# DataResult → SearchResult
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
class _DataResult(_Base):
|
||||||
|
asset: Optional[list[Asset]] = None
|
||||||
|
sum_asset_results: Optional[int] = None
|
||||||
|
collection: Optional[list[Collection]] = None
|
||||||
|
sum_collection_results: Optional[int] = None
|
||||||
|
|
||||||
|
|
||||||
|
class SearchResult(_Base):
|
||||||
|
data_result: Optional[_DataResult] = None
|
||||||
|
options: Optional[AzSearchOptions] = None
|
||||||
|
facets: Optional[list[SearchFacet]] = None
|
||||||
|
count: Optional[int] = None
|
||||||
|
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# SearchableItem
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
class SearchableItem(_Base):
|
||||||
|
name: Optional[str] = None
|
||||||
|
is_key: Optional[bool] = None
|
||||||
|
filterable: Optional[bool] = None
|
||||||
|
hidden: Optional[bool] = None
|
||||||
|
searchable: Optional[bool] = None
|
||||||
|
facetable: Optional[bool] = None
|
||||||
|
sortable: Optional[bool] = None
|
||||||
|
is_collection: Optional[bool] = None
|
||||||
|
searchtype: Optional[str] = None
|
||||||
|
fields: Optional[list["SearchableItem"]] = None
|
||||||
|
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# SearchAdmin* models
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
class SearchAdminStatistics(_Base):
|
||||||
|
documentcount: Optional[int] = None
|
||||||
|
storagesizebytes: Optional[int] = None
|
||||||
|
|
||||||
|
|
||||||
|
class SearchAdminIndexStatus(_Base):
|
||||||
|
name: Optional[str] = None
|
||||||
|
status: Optional[str] = None
|
||||||
|
statistics: Optional[SearchAdminStatistics] = None
|
||||||
|
|
||||||
|
|
||||||
|
class SearchAdminIndexerLastRun(_Base):
|
||||||
|
status: Optional[str] = None
|
||||||
|
starttime: Optional[str] = None
|
||||||
|
endtime: Optional[str] = None
|
||||||
|
itemcount: Optional[int] = None
|
||||||
|
faileditemcount: Optional[int] = None
|
||||||
|
|
||||||
|
|
||||||
|
class SearchAdminIndexerStatus(_Base):
|
||||||
|
name: Optional[str] = None
|
||||||
|
status: Optional[str] = None
|
||||||
|
error: Optional[str] = None
|
||||||
|
lastrun: Optional[SearchAdminIndexerLastRun] = None
|
||||||
|
history: Optional[list[SearchAdminIndexerLastRun]] = None
|
||||||
|
|
||||||
|
|
||||||
|
class SearchAdminDataSourceStatus(_Base):
|
||||||
|
name: Optional[str] = None
|
||||||
|
status: Optional[str] = None
|
||||||
|
|
||||||
|
|
||||||
|
class SearchAdminSkillStatus(_Base):
|
||||||
|
name: Optional[str] = None
|
||||||
|
skills: Optional[list[str]] = None
|
||||||
|
|
||||||
|
|
||||||
|
class SearchAdminStatus(_Base):
|
||||||
|
index: Optional[SearchAdminIndexStatus] = None
|
||||||
|
indexer: Optional[SearchAdminIndexerStatus] = None
|
||||||
|
datasource: Optional[SearchAdminDataSourceStatus] = None
|
||||||
|
skillsets: Optional[list[SearchAdminSkillStatus]] = None
|
||||||
|
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# SavedSearch
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
class SavedSearch(_Base):
|
||||||
|
id: Optional[str] = None
|
||||||
|
entity_type: Optional[str] = None
|
||||||
|
searchstring: Optional[str] = None
|
||||||
|
external: Optional[bool] = None
|
||||||
|
translations: Optional[dict[str, DictionaryObject]] = None
|
||||||
|
name: Optional[str] = None
|
||||||
|
description: Optional[str] = None
|
||||||
|
add_properties: Optional[dict[str, Any]] = None
|
||||||
|
status: Optional[str] = None
|
||||||
|
created_date: Optional[str] = None
|
||||||
|
created_by: Optional[str] = None
|
||||||
|
modified_date: Optional[str] = None
|
||||||
|
modified_by: Optional[str] = None
|
||||||
|
pk: Optional[str] = None
|
||||||
|
etag: Optional[str] = Field(None, alias="_etag")
|
||||||
|
|
||||||
|
model_config = ConfigDict(extra="allow", populate_by_name=True)
|
||||||
|
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# Web App Data
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
class AllWebAppData(_Base):
|
||||||
|
root_collection: Optional[Collection] = None
|
||||||
|
subcollections: Optional[list[Collection]] = None
|
||||||
|
assets: Optional[list[Asset]] = None
|
||||||
|
pub_assets: Optional[list[PublishedAsset]] = None
|
||||||
|
created_date: Optional[str] = None
|
||||||
|
|
||||||
|
|
||||||
|
class KeyValuePairObject(_Base):
|
||||||
|
model_config = ConfigDict(extra="allow")
|
||||||
|
|
||||||
|
|
||||||
|
class GroupAllAppData(_Base):
|
||||||
|
collection_type: Optional[CollectionType] = None
|
||||||
|
collections: Optional[list[Collection]] = None
|
||||||
|
assets: Optional[list[Asset]] = None
|
||||||
|
created_date: Optional[str] = None
|
||||||
|
add_info: Optional[list[KeyValuePairObject]] = None
|
||||||
|
dist: Optional[DistZipResponse] = None
|
||||||
35
agravity_client/models/secure_upload.py
Normal file
35
agravity_client/models/secure_upload.py
Normal file
|
|
@ -0,0 +1,35 @@
|
||||||
|
"""Secure Upload Pydantic model."""
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from typing import Optional
|
||||||
|
|
||||||
|
from pydantic import ConfigDict, Field
|
||||||
|
|
||||||
|
from agravity_client.models.common import _Base
|
||||||
|
|
||||||
|
|
||||||
|
class CreateSftpUserResult(_Base):
|
||||||
|
url: Optional[str] = None
|
||||||
|
password: Optional[str] = None
|
||||||
|
|
||||||
|
|
||||||
|
class SecureUploadEntity(_Base):
|
||||||
|
id: Optional[str] = None
|
||||||
|
entity_type: Optional[str] = None
|
||||||
|
collection_id: Optional[str] = None
|
||||||
|
url: Optional[str] = None
|
||||||
|
valid_until: Optional[str] = Field(None, description="ISO 8601 date-time string")
|
||||||
|
password: Optional[str] = None
|
||||||
|
asset_tags: Optional[list[str]] = None
|
||||||
|
message: Optional[str] = None
|
||||||
|
sftp_connection: Optional[CreateSftpUserResult] = None
|
||||||
|
check_name_for_version: Optional[bool] = None
|
||||||
|
status: Optional[str] = None
|
||||||
|
created_date: Optional[str] = None
|
||||||
|
created_by: Optional[str] = None
|
||||||
|
modified_date: Optional[str] = None
|
||||||
|
modified_by: Optional[str] = None
|
||||||
|
pk: Optional[str] = None
|
||||||
|
etag: Optional[str] = Field(None, alias="_etag")
|
||||||
|
|
||||||
|
model_config = ConfigDict(extra="allow", populate_by_name=True)
|
||||||
71
agravity_client/models/sharing.py
Normal file
71
agravity_client/models/sharing.py
Normal file
|
|
@ -0,0 +1,71 @@
|
||||||
|
"""Sharing-related Pydantic models (shared collections and quick shares)."""
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from typing import Any, Optional
|
||||||
|
|
||||||
|
from pydantic import ConfigDict, Field
|
||||||
|
|
||||||
|
from agravity_client.models.asset import AssetBlob, AssetIdFormat
|
||||||
|
from agravity_client.models.common import EntityId, _Base
|
||||||
|
from agravity_client.models.download import SharedAllowedFormat
|
||||||
|
|
||||||
|
|
||||||
|
class SharedAsset(_Base):
|
||||||
|
id: Optional[str] = None
|
||||||
|
name: Optional[str] = None
|
||||||
|
description: Optional[str] = None
|
||||||
|
created_date: Optional[str] = None
|
||||||
|
modified_date: Optional[str] = None
|
||||||
|
asset_type: Optional[str] = None
|
||||||
|
orig_blob: Optional[AssetBlob] = None
|
||||||
|
blobs: Optional[list[AssetBlob]] = None
|
||||||
|
|
||||||
|
|
||||||
|
class SharedCollectionFull(_Base):
|
||||||
|
page: Optional[list[SharedAsset]] = None
|
||||||
|
page_size: Optional[int] = None
|
||||||
|
size: Optional[int] = None
|
||||||
|
continuation_token: Optional[str] = None
|
||||||
|
id: Optional[str] = None
|
||||||
|
entity_type: Optional[str] = None
|
||||||
|
collection_id: Optional[str] = None
|
||||||
|
url: Optional[str] = None
|
||||||
|
valid_until: Optional[str] = Field(None, description="ISO 8601 date-time string")
|
||||||
|
valid_for: Optional[str] = None
|
||||||
|
message: Optional[str] = None
|
||||||
|
global_: Optional[bool] = Field(None, alias="global")
|
||||||
|
allowed_formats: Optional[list[SharedAllowedFormat]] = None
|
||||||
|
password: Optional[str] = None
|
||||||
|
status: Optional[str] = None
|
||||||
|
created_date: Optional[str] = None
|
||||||
|
created_by: Optional[str] = None
|
||||||
|
modified_date: Optional[str] = None
|
||||||
|
modified_by: Optional[str] = None
|
||||||
|
pk: Optional[str] = None
|
||||||
|
etag: Optional[str] = Field(None, alias="_etag")
|
||||||
|
|
||||||
|
model_config = ConfigDict(extra="allow", populate_by_name=True)
|
||||||
|
|
||||||
|
|
||||||
|
class QuickShareFull(_Base):
|
||||||
|
page: Optional[list[SharedAsset]] = None
|
||||||
|
page_size: Optional[int] = None
|
||||||
|
size: Optional[int] = None
|
||||||
|
continuation_token: Optional[str] = None
|
||||||
|
id: Optional[str] = None
|
||||||
|
entity_type: Optional[str] = None
|
||||||
|
name: Optional[str] = None
|
||||||
|
assets: Optional[list[AssetIdFormat]] = None
|
||||||
|
users: Optional[list[EntityId]] = None
|
||||||
|
expires: Optional[str] = Field(None, description="ISO 8601 date-time string")
|
||||||
|
url: Optional[str] = None
|
||||||
|
zip_url: Optional[str] = None
|
||||||
|
status: Optional[str] = None
|
||||||
|
created_date: Optional[str] = None
|
||||||
|
created_by: Optional[str] = None
|
||||||
|
modified_date: Optional[str] = None
|
||||||
|
modified_by: Optional[str] = None
|
||||||
|
pk: Optional[str] = None
|
||||||
|
etag: Optional[str] = Field(None, alias="_etag")
|
||||||
|
|
||||||
|
model_config = ConfigDict(extra="allow", populate_by_name=True)
|
||||||
27
agravity_client/models/static_lists.py
Normal file
27
agravity_client/models/static_lists.py
Normal file
|
|
@ -0,0 +1,27 @@
|
||||||
|
"""Static Defined List Pydantic model."""
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from typing import Any, Optional
|
||||||
|
|
||||||
|
from pydantic import ConfigDict, Field
|
||||||
|
|
||||||
|
from agravity_client.models.common import DictionaryObject, _Base
|
||||||
|
|
||||||
|
|
||||||
|
class StaticDefinedList(_Base):
|
||||||
|
id: Optional[str] = None
|
||||||
|
entity_type: Optional[str] = None
|
||||||
|
translations: Optional[dict[str, DictionaryObject]] = None
|
||||||
|
values: Optional[list[str]] = None
|
||||||
|
name: Optional[str] = None
|
||||||
|
description: Optional[str] = None
|
||||||
|
add_properties: Optional[dict[str, Any]] = None
|
||||||
|
status: Optional[str] = None
|
||||||
|
created_date: Optional[str] = None
|
||||||
|
created_by: Optional[str] = None
|
||||||
|
modified_date: Optional[str] = None
|
||||||
|
modified_by: Optional[str] = None
|
||||||
|
pk: Optional[str] = None
|
||||||
|
etag: Optional[str] = Field(None, alias="_etag")
|
||||||
|
|
||||||
|
model_config = ConfigDict(extra="allow", populate_by_name=True)
|
||||||
36
agravity_client/models/versioning.py
Normal file
36
agravity_client/models/versioning.py
Normal file
|
|
@ -0,0 +1,36 @@
|
||||||
|
"""Asset versioning Pydantic models."""
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from typing import Optional
|
||||||
|
|
||||||
|
from pydantic import ConfigDict, Field
|
||||||
|
|
||||||
|
from agravity_client.models.asset import AssetBlob
|
||||||
|
from agravity_client.models.common import _Base
|
||||||
|
|
||||||
|
|
||||||
|
class VersionedAsset(_Base):
|
||||||
|
version_nr: Optional[int] = None
|
||||||
|
until_date: Optional[str] = Field(None, description="ISO 8601 date-time string")
|
||||||
|
version_info: Optional[str] = None
|
||||||
|
created_date: Optional[str] = None
|
||||||
|
created_by: Optional[str] = None
|
||||||
|
blob_data: Optional[AssetBlob] = None
|
||||||
|
blob_uploaded: Optional[str] = Field(None, description="ISO 8601 date-time string")
|
||||||
|
mime_type: Optional[str] = None
|
||||||
|
|
||||||
|
|
||||||
|
class VersionEntity(_Base):
|
||||||
|
id: Optional[str] = None
|
||||||
|
entity_type: Optional[str] = None
|
||||||
|
versions: Optional[list[VersionedAsset]] = None
|
||||||
|
region_of_origin: Optional[str] = None
|
||||||
|
status: Optional[str] = None
|
||||||
|
created_date: Optional[str] = None
|
||||||
|
created_by: Optional[str] = None
|
||||||
|
modified_date: Optional[str] = None
|
||||||
|
modified_by: Optional[str] = None
|
||||||
|
pk: Optional[str] = None
|
||||||
|
etag: Optional[str] = Field(None, alias="_etag")
|
||||||
|
|
||||||
|
model_config = ConfigDict(extra="allow", populate_by_name=True)
|
||||||
30
agravity_client/models/workspace.py
Normal file
30
agravity_client/models/workspace.py
Normal file
|
|
@ -0,0 +1,30 @@
|
||||||
|
"""Workspace Pydantic model."""
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from typing import Any, Optional
|
||||||
|
|
||||||
|
from pydantic import ConfigDict, Field
|
||||||
|
|
||||||
|
from agravity_client.models.collection import CollectionType, PermissionEntity
|
||||||
|
from agravity_client.models.common import DictionaryObject, _Base
|
||||||
|
|
||||||
|
|
||||||
|
class Workspace(_Base):
|
||||||
|
id: Optional[str] = None
|
||||||
|
entity_type: Optional[str] = None
|
||||||
|
name: Optional[str] = None
|
||||||
|
collection_types: Optional[list[CollectionType]] = None
|
||||||
|
translations: Optional[dict[str, DictionaryObject]] = None
|
||||||
|
order: Optional[int] = None
|
||||||
|
permissions: Optional[list[PermissionEntity]] = None
|
||||||
|
description: Optional[str] = None
|
||||||
|
add_properties: Optional[dict[str, Any]] = None
|
||||||
|
status: Optional[str] = None
|
||||||
|
created_date: Optional[str] = None
|
||||||
|
created_by: Optional[str] = None
|
||||||
|
modified_date: Optional[str] = None
|
||||||
|
modified_by: Optional[str] = None
|
||||||
|
pk: Optional[str] = None
|
||||||
|
etag: Optional[str] = Field(None, alias="_etag")
|
||||||
|
|
||||||
|
model_config = ConfigDict(extra="allow", populate_by_name=True)
|
||||||
70
pyproject.toml
Normal file
70
pyproject.toml
Normal file
|
|
@ -0,0 +1,70 @@
|
||||||
|
[build-system]
|
||||||
|
requires = ["hatchling"]
|
||||||
|
build-backend = "hatchling.build"
|
||||||
|
|
||||||
|
[project]
|
||||||
|
name = "agravity-client"
|
||||||
|
version = "0.1.0"
|
||||||
|
description = "Pythonic, Pydantic-driven async REST client for the Agravity DAM API (v10.3.0)."
|
||||||
|
readme = "README.md"
|
||||||
|
requires-python = ">=3.10"
|
||||||
|
license = { text = "MIT" }
|
||||||
|
keywords = ["agravity", "dam", "rest", "api", "client", "pydantic", "httpx"]
|
||||||
|
classifiers = [
|
||||||
|
"Development Status :: 3 - Alpha",
|
||||||
|
"Intended Audience :: Developers",
|
||||||
|
"License :: OSI Approved :: MIT License",
|
||||||
|
"Programming Language :: Python :: 3",
|
||||||
|
"Programming Language :: Python :: 3.10",
|
||||||
|
"Programming Language :: Python :: 3.11",
|
||||||
|
"Programming Language :: Python :: 3.12",
|
||||||
|
"Topic :: Software Development :: Libraries :: Python Modules",
|
||||||
|
"Typing :: Typed",
|
||||||
|
]
|
||||||
|
dependencies = [
|
||||||
|
"httpx>=0.27",
|
||||||
|
"pydantic>=2.6",
|
||||||
|
"pydantic-settings>=2.2",
|
||||||
|
]
|
||||||
|
|
||||||
|
[project.optional-dependencies]
|
||||||
|
dev = [
|
||||||
|
"pytest>=8.1",
|
||||||
|
"pytest-asyncio>=0.23",
|
||||||
|
"pytest-httpx>=0.30",
|
||||||
|
"ruff>=0.4",
|
||||||
|
"mypy>=1.10",
|
||||||
|
]
|
||||||
|
|
||||||
|
[tool.hatch.build.targets.wheel]
|
||||||
|
packages = ["agravity_client"]
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# Ruff
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
[tool.ruff]
|
||||||
|
target-version = "py310"
|
||||||
|
line-length = 100
|
||||||
|
|
||||||
|
[tool.ruff.lint]
|
||||||
|
select = ["E", "F", "I", "N", "UP", "ANN", "B", "C4", "T20"]
|
||||||
|
ignore = [
|
||||||
|
"ANN101", # missing type annotation for self
|
||||||
|
"ANN102", # missing type annotation for cls
|
||||||
|
"ANN401", # Dynamically typed expressions (Any) are disallowed
|
||||||
|
]
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# mypy
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
[tool.mypy]
|
||||||
|
python_version = "3.10"
|
||||||
|
strict = true
|
||||||
|
ignore_missing_imports = true
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# pytest
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
[tool.pytest.ini_options]
|
||||||
|
asyncio_mode = "auto"
|
||||||
|
testpaths = ["tests"]
|
||||||
8501
swagger.json
Normal file
8501
swagger.json
Normal file
File diff suppressed because one or more lines are too long
1
tests/__init__.py
Normal file
1
tests/__init__.py
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
# tests package
|
||||||
31
tests/conftest.py
Normal file
31
tests/conftest.py
Normal file
|
|
@ -0,0 +1,31 @@
|
||||||
|
"""Shared pytest fixtures for the agravity-client test suite."""
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
import pytest_asyncio
|
||||||
|
import httpx
|
||||||
|
|
||||||
|
from agravity_client.client import AgravityClient
|
||||||
|
from agravity_client.config import AgravityConfig
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def config() -> AgravityConfig:
|
||||||
|
"""Return a test AgravityConfig with a dummy API key."""
|
||||||
|
return AgravityConfig(
|
||||||
|
api_key="test-api-key",
|
||||||
|
base_url="https://api.example.local/api",
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest_asyncio.fixture
|
||||||
|
async def client(config: AgravityConfig) -> AgravityClient:
|
||||||
|
"""Return an AgravityClient backed by a real (but not-yet-connected) transport.
|
||||||
|
|
||||||
|
Tests that need to mock HTTP responses should use ``pytest-httpx``'s
|
||||||
|
``httpx_mock`` fixture and pass a transport via::
|
||||||
|
|
||||||
|
client._http = httpx.AsyncClient(transport=httpx_mock.get_async_handler())
|
||||||
|
"""
|
||||||
|
async with AgravityClient(config) as c:
|
||||||
|
yield c
|
||||||
72
tests/test_models.py
Normal file
72
tests/test_models.py
Normal file
|
|
@ -0,0 +1,72 @@
|
||||||
|
"""Basic smoke tests for Pydantic model validation."""
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
from agravity_client.models import (
|
||||||
|
Asset,
|
||||||
|
AssetBlob,
|
||||||
|
AssetBlobType,
|
||||||
|
AssetPageResult,
|
||||||
|
Collection,
|
||||||
|
AgravityVersion,
|
||||||
|
SearchResult,
|
||||||
|
AzSearchOptions,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class TestAssetModel:
|
||||||
|
def test_minimal_asset(self):
|
||||||
|
asset = Asset.model_validate({"id": "abc123"})
|
||||||
|
assert asset.id == "abc123"
|
||||||
|
|
||||||
|
def test_asset_with_blob_type_enum(self):
|
||||||
|
asset = Asset.model_validate({
|
||||||
|
"id": "abc",
|
||||||
|
"asset_type": "IMAGE",
|
||||||
|
})
|
||||||
|
assert asset.asset_type == "IMAGE"
|
||||||
|
|
||||||
|
def test_asset_page_result(self):
|
||||||
|
result = AssetPageResult.model_validate({
|
||||||
|
"assets": [{"id": "a1"}, {"id": "a2"}],
|
||||||
|
"size": 2,
|
||||||
|
})
|
||||||
|
assert len(result.assets) == 2
|
||||||
|
assert result.assets[0].id == "a1"
|
||||||
|
|
||||||
|
def test_extra_fields_allowed(self):
|
||||||
|
"""Unknown API fields must not raise ValidationError."""
|
||||||
|
asset = Asset.model_validate({"id": "x", "future_field": "value"})
|
||||||
|
assert asset.id == "x"
|
||||||
|
|
||||||
|
|
||||||
|
class TestCollectionModel:
|
||||||
|
def test_minimal_collection(self):
|
||||||
|
coll = Collection.model_validate({"id": "c1", "name": "Root"})
|
||||||
|
assert coll.id == "c1"
|
||||||
|
assert coll.name == "Root"
|
||||||
|
|
||||||
|
|
||||||
|
class TestVersionModel:
|
||||||
|
def test_agravity_version(self):
|
||||||
|
v = AgravityVersion.model_validate({
|
||||||
|
"name": "Agravity",
|
||||||
|
"version": "10.3.0",
|
||||||
|
})
|
||||||
|
assert v.version == "10.3.0"
|
||||||
|
|
||||||
|
|
||||||
|
class TestSearchModels:
|
||||||
|
def test_search_options_defaults(self):
|
||||||
|
opts = AzSearchOptions()
|
||||||
|
assert opts.limit is None
|
||||||
|
|
||||||
|
def test_search_options_with_values(self):
|
||||||
|
opts = AzSearchOptions(searchterm="hello", limit=5)
|
||||||
|
dumped = opts.model_dump(exclude_none=True)
|
||||||
|
assert dumped["searchterm"] == "hello"
|
||||||
|
assert dumped["limit"] == 5
|
||||||
|
|
||||||
|
def test_search_result_minimal(self):
|
||||||
|
result = SearchResult.model_validate({})
|
||||||
|
assert result.count is None
|
||||||
Loading…
Add table
Add a link
Reference in a new issue