Metadata-Version: 2.4
Name: keyban-api-client
Version: 0.0.1
Summary: Python client for Keyban API
Home-page: https://github.com/keyban-io/dap
Author: Keyban
Author-email: support@keyban.io
Requires-Python: >=3.8
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: requests>=2.31.0
Requires-Dist: pydantic>=2.0.0
Requires-Dist: typing-extensions>=4.0.0
Dynamic: author
Dynamic: author-email
Dynamic: home-page
Dynamic: license-file
Dynamic: requires-python

# Keyban API Client

A Python client library for interacting with the Keyban API Product Sheet management. This client provides a clean, type-safe interface for managing product sheets, including full CRUD operations.

## Features

- **List product sheets** with filtering and pagination
- **Get specific product sheets** by ID (public endpoint)
- **Create new product sheets** with validation
- **Update existing product sheets** with partial updates
- **Delete product sheets** with proper authorization
- **Type-safe models** using Pydantic for request/response validation
- **Comprehensive error handling** with proper HTTP status code handling
- **Convenience functions** for common operations

## Quick Start

### Simple Product Creation

```python
from uuid import UUID
from keyban_api_client import ProductSheetClient, ProductSheetData, CreateProductSheetRequest

# Initialize the client
client = ProductSheetClient(
    base_url="https://api.prod.keyban.io",
    api_key="your-api-key"
)

# Create a simple product sheet (requires auth)
product_data = ProductSheetData(
    name="My Test Product",
    description="A product created via Python client"
)

request = CreateProductSheetRequest(
    application=UUID("your-application-id"),
    network="StarknetSepolia",
    status="DRAFT",
    data=product_data,
    certified_paths=["name", "description"]
)

new_sheet = client.create_product_sheet(request)
print(f"Created product sheet: {new_sheet.id}")

# List product sheets
sheets = client.list_product_sheets(page_size=10)
print(f"Found {sheets.total} product sheets")

# Get a specific product sheet (public endpoint - no auth needed)
sheet = client.get_product_sheet(new_sheet.id)
print(f"Product: {sheet.data.name}")

# Close the client when done
client.close()
```

### Full Product Creation

```python
from uuid import UUID
from keyban_api_client import ProductSheetClient, ProductSheetData, CreateProductSheetRequest

# Initialize client
client = ProductSheetClient(
    base_url="https://api.prod.keyban.io",
    api_key="your-api-key"
)

# Create a comprehensive product with custom fields
product_data = ProductSheetData(
    # Standard product fields
    identifier="PROD-001",
    name="Organic Cotton T-Shirt",
    description="Sustainably produced cotton t-shirt",
    image="https://example.com/product.jpg",
    gtin="1234567890123",
    sku="ECO-TS-M-BLU",
    brand={
        "name": "EcoWear",
        "identifier": "ecowear-brand"
    },
    countryOfOrigin="TR",  # Turkey
    keywords=["organic", "cotton", "sustainable"],
    
    # Custom fields via **{} syntax
    **{
        "material": "100% Organic Cotton",
        "certification": "GOTS Certified",
        "manufacturer": {
            "name": "EcoTextiles Ltd",
            "location": "Istanbul, Turkey"
        }
    }
)

# Create with certification
request = CreateProductSheetRequest(
    application=UUID("your-application-id"),
    network="StarknetSepolia",
    status="DRAFT",
    data=product_data,
    certified_paths=[
        "name", "brand", "gtin", "sku", "countryOfOrigin"
    ]  # Only standard fields can be certified
)

sheet = client.create_product_sheet(request)
client.close()
```

## Product Sheet Certification

Product sheets are certified on the blockchain using the `certifiedPaths` field. This field points to specific fields within `productSheet.data` that will be digitally signed and recorded on the blockchain.

### How Certification Works

1. **Certified Paths**: The `certifiedPaths` field contains an array of JSON paths pointing to fields in the product data that should be certified
2. **Automatic Triggering**: Certification events are automatically triggered when:
   - `certifiedPaths` field is updated
   - Any data field pointed to by `certifiedPaths` changes (during create or update operations)
3. **Blockchain Event**: A new blockchain event is emitted containing:
   - IPFS CID pointing to the certified data
   - Certifier signature
   - Product ID

### Example: Creating a Product with Certification

```python
from keyban_api_client import ProductSheetData, CreateProductSheetRequest

# Create product with standard structure + custom fields
product_data = ProductSheetData(
    # Standard base fields
    identifier="PROD-001",
    name="Organic Cotton T-Shirt",
    description="Sustainably produced cotton t-shirt",
    image="https://example.com/product.jpg",
    
    # Standard product extension fields  
    gtin="1234567890123",
    sku="ECO-TS-M-BLU",
    brand={
        "name": "EcoWear",
        "identifier": "ecowear-brand",
        "description": "Sustainable fashion brand"
    },
    countryOfOrigin="TR",  # Turkey - converts to {"identifier": "TR"}
    keywords=["organic", "cotton", "sustainable", "GOTS"],
    
    # Custom domain-specific fields using **{} syntax
    **{
        "material": "100% Organic Cotton",
        "certification": "GOTS Certified",
        "manufacturer": {
            "name": "EcoTextiles Ltd",
            "location": "Istanbul, Turkey",
            "certifications": ["GOTS", "OEKO-TEX"]
        },
        "sustainability": {
            "carbonFootprint": "2.1 kg CO2",
            "waterUsage": "2700L", 
            "recyclable": True
        }
    }
)

# Specify which fields should be certified on blockchain
# Standard product fields can be certified, custom fields are stored but not certified
request = CreateProductSheetRequest(
    application=your_app_id,
    network="StarknetSepolia",
    status="draft",
    data=product_data,
    certified_paths=[
        # Standard product fields (can be certified)
        "identifier",
        "name", 
        "description",
        "image",
        "gtin",
        "sku", 
        "brand.name",
        "brand.identifier",
        "countryOfOrigin",
        # Custom fields like "material", "certification" are stored via **{}
        # Nested data like "manufacturer.*" is stored but cannot be certified yet (work in progress)
    ]  # Only standard fields are blockchain-certified
)

# Create the product sheet - certification job will be automatically triggered
sheet = client.create_product_sheet(request)

# Later updates to certified fields will trigger new certification events
update_data = UpdateProductSheetRequest(
    data=ProductSheetData(
        name="Updated Standard Product",
        description="Updated product description",
        keywords=["updated", "product", "standard"]
    ),
    certified_paths=["name", "description", "keywords"]  # Re-certify updated fields
)
client.update_product_sheet(sheet.id, update_data)
```

### Tracking Certifications

You can track product certifications using the Keyban indexer API:

```bash
curl 'https://subql-starknet-sepolia.prod.keyban.io/' \
  -H 'accept: application/json' \
  -H 'content-type: application/json' \
  --data-raw '{
    "query": "query CertificationEvents { productCertifications( filter: {productId: {equalTo: \"<productSheetId>\"}} ) { edges { node { transactionId ipfsCid certifierPubkey certifierSignature } } } }",
    "operationName": "CertificationEvents"
  }'
```

Replace `<productSheetId>` with your actual product sheet ID.

## Advanced Usage

### Advanced Filtering with FilterOperator

```python
from keyban_api_client import FilterOperator

# Filter by application ID
app_filter = FilterOperator(field="application.id", operator="eq", value="your-app-id")

# Filter by product name (contains)
name_filter = FilterOperator(field="data.name", operator="contains", value="Cotton")

# Filter by status
status_filter = FilterOperator(field="status", operator="eq", value="ACTIVE")

# Use multiple filters
sheets = client.list_product_sheets(
    filters=[app_filter, name_filter, status_filter],
    page_size=20
)

print(f"Found {sheets.total} matching products")
for sheet in sheets.data:
    print(f"- {sheet.data.name} ({sheet.status})")
```

### Error Handling

```python
import requests
from keyban_api_client import ProductSheetClient

client = ProductSheetClient(base_url="...", api_key="...")

try:
    sheets = client.list_product_sheets()
except requests.HTTPError as e:
    if e.response.status_code == 401:
        print("Authentication failed")
    elif e.response.status_code == 404:
        print("Resource not found")
    else:
        print(f"HTTP error: {e}")
except Exception as e:
    print(f"Unexpected error: {e}")
```

### Pagination

```python
# Get all product sheets across multiple pages
all_sheets = []
page = 1
page_size = 50

while True:
    response = client.list_product_sheets(
        current_page=page, 
        page_size=page_size
    )
    
    all_sheets.extend(response.data)
    
    # Check if we've got all sheets
    if len(response.data) < page_size:
        break
    
    page += 1

print(f"Retrieved {len(all_sheets)} total product sheets")
```

### Context Manager Usage

```python
# Automatically close the session
with ProductSheetClient(base_url="...", api_key="...") as client:
    sheets = client.list_product_sheets()
    # Client automatically closed when exiting the context
```

## API Reference

### ProductSheetClient

Main client class for interacting with the API.

#### Constructor

```python
ProductSheetClient(
    base_url: str,
    api_version: str = "v1", 
    api_key: Optional[str] = None,
    timeout: int = 30
)
```

#### Methods

##### `list_product_sheets(application_id=None, name_filter=None, current_page=1, page_size=10)`

List product sheets with optional filtering.

- **application_id** (UUID, optional): Filter by application ID
- **name_filter** (str, optional): Filter by product name (case-insensitive substring)
- **current_page** (int): Page number (1-based, default: 1)
- **page_size** (int): Items per page (default: 10, max: 100)

Returns: `ProductSheetListResponse` with `data` (list of sheets) and `total` count.

##### `get_product_sheet(product_sheet_id: UUID)`

Get a specific product sheet by ID. This is a public endpoint.

Returns: `ProductSheet` object.

##### `create_product_sheet(product_sheet_data: CreateProductSheetRequest)`

Create a new product sheet. Requires authentication.

Returns: Created `ProductSheet` object.

##### `update_product_sheet(product_sheet_id: UUID, update_data: UpdateProductSheetRequest)`

Update an existing product sheet. Requires authentication and organization access.

Returns: Updated `ProductSheet` object.

##### `delete_product_sheet(product_sheet_id: UUID)`

Delete an existing product sheet. Requires authentication and organization-level access. Only product sheets belonging to the authenticated organization can be deleted.

Returns: `bool` - True if deletion was successful.

### Data Models

#### ProductSheet

Main product sheet model with fields:

- `id`: UUID
- `application`: UUID  
- `network`: str (network enum value)
- `status`: str (status enum value)
- `data`: ProductSheetData (product information)
- `certified_paths`: Optional[List[str]] (blockchain-certified fields)
- `created_at`: datetime
- `updated_at`: datetime

#### ProductSheetData

Product data following standard product format:

- `name`: str (required)
- `description`: Optional[str]
- `brand`: Optional[str]
- `category`: Optional[str]
- Additional fields allowed (extra="allow")

#### CreateProductSheetRequest

For creating new product sheets:

- `application`: UUID (required)
- `network`: str (required)
- `status`: str (required)  
- `data`: ProductSheetData (required)
- `certified_paths`: Optional[List[str]]

#### UpdateProductSheetRequest  

For updating existing sheets (all fields optional):

- `network`: Optional[str]
- `status`: Optional[str]
- `data`: Optional[ProductSheetData]
- `certified_paths`: Optional[List[str]]

## API Endpoints Covered

This client covers the following endpoints from the Keyban API Product Sheet controller:

- `GET /v1/dpp/product-sheets` - List product sheets
- `GET /v1/dpp/product-sheets/:id` - Get product sheet by ID
- `POST /v1/dpp/product-sheets` - Create product sheet  
- `PATCH /v1/dpp/product-sheets/:id` - Update product sheet
- `DELETE /v1/dpp/product-sheets/:id` - Delete product sheet

## License

This client is part of the DAP (Digital Asset Platform) by Keyban project.
