367 lines
10 KiB
Markdown
367 lines
10 KiB
Markdown
# Elytra PIM Client
|
|
|
|
A fully Pythonic and **Pydantic-driven** client for the Elytra PIM (Product Information Management) API.
|
|
|
|
## Features
|
|
|
|
- 🐍 Fully Pythonic with Pydantic v2 data validation
|
|
- 📦 Auto-generated Pydantic models from OpenAPI specification
|
|
- 🔐 Bearer token authentication
|
|
- ✅ Request/Response validation with Pydantic
|
|
- 🌍 Multi-language support
|
|
- 📄 Full type hints throughout the codebase
|
|
- 🧪 Comprehensive error handling
|
|
- 🔄 Context manager support
|
|
- 🔄 Automatic serialization/deserialization
|
|
|
|
## Installation
|
|
|
|
### From Forgejo PyPI
|
|
|
|
Install directly from the HIM-public package registry:
|
|
|
|
```bash
|
|
pip install --index-url https://git.him-tools.de/api/packages/HIM-public/pypi/simple/ elytra-pim-client
|
|
```
|
|
|
|
### From Source
|
|
|
|
Clone the repository:
|
|
|
|
```bash
|
|
git clone https://git.him-tools.de/HIM-public/elytra_client.git
|
|
cd elytra_client
|
|
pip install -e .
|
|
```
|
|
|
|
### With Development Dependencies
|
|
|
|
```bash
|
|
pip install -e ".[dev]"
|
|
```
|
|
|
|
## Quick Start
|
|
|
|
### Basic Usage
|
|
|
|
```python
|
|
from elytra_client import ElytraClient, SingleProductResponse
|
|
|
|
# Initialize the client
|
|
client = ElytraClient(
|
|
base_url="https://example.com/api/v1",
|
|
api_key="your-api-key"
|
|
)
|
|
|
|
# Get all products - returns dict with Pydantic validated items
|
|
products_response = client.get_products(lang="en", page=1, limit=10)
|
|
for product in products_response["items"]:
|
|
print(f"Product: {product.productName}, ID: {product.id}")
|
|
|
|
# Get a specific product - returns Pydantic model directly
|
|
product: SingleProductResponse = client.get_product(product_id=123, lang="en")
|
|
print(f"Name: {product.productName}")
|
|
print(f"Status: {product.objectStatus}")
|
|
|
|
# Close the client
|
|
client.close()
|
|
```
|
|
|
|
### Creating Products with Validation
|
|
|
|
```python
|
|
from elytra_client import (
|
|
ElytraClient,
|
|
SingleNewProductRequestBody,
|
|
AttributeRequestBody,
|
|
)
|
|
|
|
with ElytraClient(base_url="https://example.com/api/v1", api_key="your-api-key") as client:
|
|
# Create a new product with validated Pydantic model
|
|
new_product = SingleNewProductRequestBody(
|
|
productName="NEW-PRODUCT-001",
|
|
parentId=1,
|
|
attributeGroupId=10,
|
|
attributes=[
|
|
AttributeRequestBody(
|
|
attributeId=1,
|
|
value="Sample Value",
|
|
languageCode="en"
|
|
)
|
|
]
|
|
)
|
|
|
|
# Validation happens automatically
|
|
created_product = client.create_product(new_product)
|
|
print(f"Created product ID: {created_product.id}")
|
|
```
|
|
|
|
### Environment Variable Configuration
|
|
|
|
Set your environment variables:
|
|
|
|
```bash
|
|
export ELYTRA_BASE_URL="https://example.com/api/v1"
|
|
export ELYTRA_API_KEY="your-api-key"
|
|
export ELYTRA_TIMEOUT="30"
|
|
export ELYTRA_VERIFY_SSL="true"
|
|
```
|
|
|
|
Then load from environment:
|
|
|
|
```python
|
|
from elytra_client import ElytraClient
|
|
from elytra_client.config import ElytraConfig
|
|
|
|
config = ElytraConfig.from_env()
|
|
client = ElytraClient(base_url=config.base_url, api_key=config.api_key)
|
|
```
|
|
|
|
## Legacy REST API (Lobster PIM)
|
|
|
|
The package also includes a subpackage for the older Lobster PIM REST API, which provides access to scheduled jobs and protocol logs that are not yet available in the newer API.
|
|
|
|
### Quick Start
|
|
|
|
```python
|
|
from elytra_client.rest_api import LobsterRestApiClient, RestApiAuth
|
|
|
|
# Create authentication
|
|
auth = RestApiAuth.from_username_password("username", "password")
|
|
|
|
# Create client for the REST API
|
|
client = LobsterRestApiClient("http://lobster-server:8080", auth=auth)
|
|
|
|
# Get all active jobs
|
|
jobs = client.get_all_active_jobs()
|
|
for job in jobs.jobInfoObjects:
|
|
print(f"Job: {job.name} - Status: {job.status}")
|
|
|
|
# Execute a job
|
|
result = client.execute_job(job_id=123)
|
|
print(f"Execution started with runtime ID: {result.runtimeId}")
|
|
|
|
# Get protocol/logs
|
|
protocols = client.get_protocols()
|
|
```
|
|
|
|
### Features
|
|
|
|
- 📋 **Job Management**: Access, monitor, and execute scheduled jobs
|
|
- 📜 **Protocol/Logs**: Retrieve execution logs and protocol information
|
|
- 🔐 **Flexible Authentication**: Username/password or API token authentication
|
|
- ⏳ **Job Control**: Execute jobs with parameter overrides and queue management
|
|
- 🎯 **Type Safety**: Full Pydantic validation for all responses
|
|
|
|
### Authentication Methods
|
|
|
|
```python
|
|
# Username/Password
|
|
auth = RestApiAuth.from_username_password("admin", "password")
|
|
|
|
# API Token (domain-specific)
|
|
auth = RestApiAuth.from_api_token("admin", "token-id", domain="Jobs")
|
|
```
|
|
|
|
### Documentation
|
|
|
|
See [elytra_client/rest_api/README.md](elytra_client/rest_api/README.md) for comprehensive REST API documentation, including:
|
|
- Complete API reference
|
|
- Authentication details
|
|
- Job management examples
|
|
- Protocol/log access
|
|
- Error handling
|
|
- Common usage scenarios
|
|
|
|
## Webhooks (Elytra Event API)
|
|
|
|
The package includes a subpackage for handling CloudEvents-based webhooks from the Elytra PIM Event API.
|
|
|
|
### Quick Start
|
|
|
|
```python
|
|
from elytra_client.webhooks import CloudEvent, parse_webhook_payload
|
|
|
|
# Parse an incoming webhook payload
|
|
event = parse_webhook_payload(request.json())
|
|
print(f"Event type: {event.eventtype}")
|
|
print(f"Object ID: {event.get_object_id()}")
|
|
```
|
|
|
|
### With Authentication Validation
|
|
|
|
```python
|
|
from elytra_client.webhooks import WebhookValidator, BasicAuth
|
|
|
|
auth = BasicAuth(username="user", password="pass")
|
|
validator = WebhookValidator(auth_type="basic", auth=auth)
|
|
|
|
event = validator.validate_and_parse(
|
|
payload=request.json(),
|
|
headers=dict(request.headers)
|
|
)
|
|
```
|
|
|
|
### Features
|
|
|
|
- 📨 **CloudEvents Support**: Strongly-typed Pydantic models following the CNCF CloudEvents v1.0 spec
|
|
- 🔐 **Flexible Authentication**: Basic, Bearer token, and API Key authentication
|
|
- 🔀 **Event Routing**: `SimpleWebhookEventDispatcher` to route events by type and operation to dedicated handlers
|
|
- 🔁 **Retry Logic**: Exponential backoff retry policies for reliable webhook delivery
|
|
- 🌐 **Framework Integration**: Ready-to-use examples for Flask and FastAPI
|
|
|
|
### Authentication Methods
|
|
|
|
```python
|
|
# Basic authentication
|
|
auth = BasicAuth(username="admin", password="secret")
|
|
|
|
# Bearer token
|
|
auth = BearerAuth(token="eyJhbGciOiJIUzI1NiIs...")
|
|
|
|
# API Key (custom header)
|
|
auth = APIKeyAuth(api_key="sk_live_secret123", header_name="X-API-Key")
|
|
```
|
|
|
|
### Event Types and Operations
|
|
|
|
Events carry an `EventType` (e.g., `PRODUCT`, `PRODUCT_GROUP`, `PRODUCT_ATTRIBUTE_VALUE`) and an `Operation` (`ADD`, `MODIFY`, `REMOVE`, `LINK`, `UNLINK`).
|
|
|
|
### Documentation
|
|
|
|
See [elytra_client/webhooks/README.md](elytra_client/webhooks/README.md) for comprehensive webhook documentation, including:
|
|
- Complete API reference
|
|
- Authentication details
|
|
- Event routing and dispatching
|
|
- Retry logic configuration
|
|
- Flask and FastAPI integration examples
|
|
|
|
## API Methods
|
|
|
|
All methods return Pydantic models with full type validation and IDE autocompletion support.
|
|
|
|
### Products
|
|
|
|
- `get_products(...) -> Dict` - Get all products (items are `SingleProductResponse` Pydantic models)
|
|
- `get_product(id, lang) -> SingleProductResponse` - Get single product
|
|
- `create_product(data) -> SingleProductResponse` - Create new product with validation
|
|
- `update_product(data) -> SingleProductResponse` - Update product with validation
|
|
- `delete_product(id) -> Dict` - Delete product
|
|
|
|
### Product Groups
|
|
|
|
- `get_product_groups(...) -> Dict` - Get all product groups (items are `SingleProductGroupResponse` models)
|
|
- `get_product_group(id, lang) -> SingleProductGroupResponse` - Get single product group
|
|
- `create_product_group(data) -> SingleProductGroupResponse` - Create new product group
|
|
- `update_product_group(data) -> SingleProductGroupResponse` - Update product group
|
|
- `delete_product_group(id) -> Dict` - Delete product group
|
|
|
|
### Attributes
|
|
|
|
- `get_attributes(...) -> Dict` - Get all attributes (items are `SingleAttributeResponse` models)
|
|
- `get_attribute(id, lang) -> SingleAttributeResponse` - Get single attribute
|
|
|
|
### Health Check
|
|
|
|
- `health_check() -> Dict` - Check API health status
|
|
|
|
## Error Handling
|
|
|
|
The client provides specific exception classes for different error types:
|
|
|
|
```python
|
|
from elytra_client import ElytraClient
|
|
from elytra_client.exceptions import (
|
|
ElytraAuthenticationError,
|
|
ElytraNotFoundError,
|
|
ElytraValidationError,
|
|
ElytraAPIError,
|
|
)
|
|
from pydantic import ValidationError
|
|
|
|
try:
|
|
client = ElytraClient(base_url="https://example.com/api/v1", api_key="invalid-key")
|
|
product = client.get_product(123)
|
|
except ElytraAuthenticationError:
|
|
print("Authentication failed")
|
|
except ElytraNotFoundError:
|
|
print("Product not found")
|
|
except ElytraValidationError as e:
|
|
print(f"API response validation failed: {e}")
|
|
except ValidationError as e:
|
|
print(f"Request model validation failed: {e}")
|
|
except ElytraAPIError as e:
|
|
print(f"API error: {e}")
|
|
```
|
|
|
|
### Validation
|
|
|
|
- **Request validation**: Pydantic models validate all input before sending to API
|
|
- **Response validation**: Pydantic models validate API responses for data integrity
|
|
- **Automatic deserialization**: Responses are automatically converted to Pydantic models
|
|
|
|
## Pydantic Models
|
|
|
|
All request and response models are automatically generated from the OpenAPI specification using [datamodel-code-generator](https://github.com/koxudaxi/datamodel-code-generator).
|
|
|
|
### Available Models
|
|
|
|
- **Response Models**: `SingleProductResponse`, `SingleProductGroupResponse`, `SingleAttributeResponse`, etc.
|
|
- **Request Models**: `SingleNewProductRequestBody`, `SingleUpdateProductRequestBody`, `SingleNewProductGroupRequestBody`, etc.
|
|
- **Attribute Models**: `ProductAttributeResponse`, `AttributeRequestBody`
|
|
|
|
All models include:
|
|
- ✅ Full type hints and validation
|
|
- ✅ Documentation from OpenAPI spec
|
|
- ✅ IDE autocompletion support
|
|
- ✅ Automatic serialization/deserialization
|
|
|
|
### Regenerating Models
|
|
|
|
To regenerate models from the OpenAPI spec:
|
|
|
|
```bash
|
|
python -m datamodel_code_generator --input openapi.yaml --input-file-type openapi --output elytra_client/models.py --target-python-version 3.10
|
|
```
|
|
|
|
## Development
|
|
|
|
### Running Tests
|
|
|
|
```bash
|
|
pytest
|
|
```
|
|
|
|
### Code Quality
|
|
|
|
Format code with Black:
|
|
|
|
```bash
|
|
black elytra_client tests
|
|
```
|
|
|
|
Check with flake8:
|
|
|
|
```bash
|
|
flake8 elytra_client tests
|
|
```
|
|
|
|
Type checking with mypy:
|
|
|
|
```bash
|
|
mypy elytra_client
|
|
```
|
|
|
|
## API Documentation
|
|
|
|
For complete API documentation, refer to the OpenAPI specification in `openapi.yaml`
|
|
see here: https://w4services.atlassian.net/wiki/spaces/pimdocext/pages/426803205/Web+API
|
|
or visit the Elytra website: https://www.elytra.ch/
|
|
|
|
## Contact
|
|
|
|
For support on this client, please email: info@hoerl-im.de
|
|
|
|
## License
|
|
|
|
MIT License - see LICENSE file for details
|