Metadata-Version: 2.4
Name: vikdata-sdk
Version: 0.1.0
Summary: Python SDK for vikdata API - IAM, RBAC, and API management platform
Author-email: vikdata <dev@vikdata.com>
License: MIT
Classifier: Development Status :: 4 - Beta
Classifier: Framework :: FastAPI
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Typing :: Typed
Requires-Python: >=3.11
Requires-Dist: httpx>=0.28.0
Requires-Dist: pydantic-settings>=2.7.0
Requires-Dist: pydantic[email]>=2.10.0
Requires-Dist: websockets>=14.0
Provides-Extra: dev
Requires-Dist: mypy>=1.13.0; extra == 'dev'
Requires-Dist: pytest-asyncio>=0.24.0; extra == 'dev'
Requires-Dist: pytest-cov>=6.0.0; extra == 'dev'
Requires-Dist: pytest>=8.3.0; extra == 'dev'
Requires-Dist: respx>=0.21.0; extra == 'dev'
Requires-Dist: ruff>=0.8.0; extra == 'dev'
Description-Content-Type: text/markdown

# vikdata-sdk

Python SDK for the vikdata platform - service registration, health monitoring, and usage tracking for microservices.

## Features

- **Service Registration**: Auto-register microservices with vikdata on startup
- **Health Monitoring**: Health check framework with automatic reporting
- **Usage Tracking**: Automatic API usage metrics for billing and analytics
- **JWT Validation**: Validate JWT tokens from vikdata (optional, via vikdata-auth package)
- **FastAPI Integration**: Middleware and decorators for seamless integration

## Installation

```bash
pip install vikdata-sdk

# With FastAPI support
pip install vikdata-sdk[fastapi]
```

## Quick Start

### Option 1: Decorator (Easiest)

```python
from fastapi import FastAPI
from vikdata import vikdata_service

@vikdata_service(
    name="fishinfo",
    service_url="https://fishinfo.example.com",
    vikdata_url="https://api.vikdata.com",
)
def create_app() -> FastAPI:
    app = FastAPI()

    @app.get("/vessels")
    async def list_vessels():
        return {"vessels": []}

    return app

app = create_app()
```

### Option 2: Manual Integration

```python
from fastapi import FastAPI
from vikdata import VikdataClient, VikdataMiddleware, UsageReporter
from contextlib import asynccontextmanager

# Initialize client
client = VikdataClient(
    base_url="https://api.vikdata.com",
    service_key="your-service-key",  # For initial registration
)

# Create usage reporter
usage_reporter = UsageReporter(
    client=client,
    service="fishinfo",
    batch_size=100,
    flush_interval=60.0,
)

@asynccontextmanager
async def lifespan(app: FastAPI):
    # Startup
    async with client:
        registration = await client.services.register(
            name="fishinfo",
            service_url="https://fishinfo.example.com",
            health_endpoint="/health",
            description="Maritime telemetry service",
            permissions=["fishinfo:*"],
        )

        # Update client with received API key
        client.api_key = registration.api_key

        # Start usage tracking
        await usage_reporter.start()

        yield

        # Shutdown
        await usage_reporter.stop()
        await client.services.unregister(registration.id)

# Create app
app = FastAPI(lifespan=lifespan)

# Add middleware
app.add_middleware(
    VikdataMiddleware,
    client=client,
    usage_reporter=usage_reporter,
)

@app.get("/vessels")
async def list_vessels():
    return {"vessels": []}
```

## Health Monitoring

```python
from vikdata.health import HealthChecker

# Create health checker
health = HealthChecker()

# Add health checks
async def check_database():
    try:
        # Check database connection
        await db.execute("SELECT 1")
        return True
    except Exception:
        return False

async def check_redis():
    try:
        # Check redis connection
        await redis.ping()
        return True
    except Exception:
        return False

health.add_check("database", check_database)
health.add_check("redis", check_redis)

# Add health endpoint
@app.get("/health")
async def health_endpoint():
    result = await health.check()
    status_code = 200 if result["status"] == "healthy" else 503
    return JSONResponse(result, status_code=status_code)
```

### Automatic Health Reporting

With the decorator, health checks are automatically reported to vikdata:

```python
from vikdata import vikdata_service

@vikdata_service(
    name="fishinfo",
    service_url="https://fishinfo.example.com",
    vikdata_url="https://api.vikdata.com",
    enable_health_reporting=True,  # Default: True
    health_check_interval=30.0,    # Seconds between reports
)
def create_app() -> FastAPI:
    app = FastAPI()

    # Access health checker via app.state
    @app.on_event("startup")
    async def setup_health():
        health_checker = app.state.health_checker
        health_checker.add_check("database", check_database)
        health_checker.add_check("redis", check_redis)

    return app
```

## Usage Tracking

Usage tracking is automatic when using the middleware. Metrics are batched and sent to vikdata periodically:

```python
from vikdata import UsageReporter

reporter = UsageReporter(
    client=client,
    service="fishinfo",
    batch_size=100,        # Flush after 100 metrics
    flush_interval=60.0,   # Or flush every 60 seconds
)

await reporter.start()

# Metrics are automatically tracked by middleware
# Manual recording is also possible:
await reporter.record(
    tenant_id="tenant-123",
    endpoint="/api/v1/vessels",
    method="GET",
    latency_ms=45.2,
    status_code=200,
)

await reporter.stop()  # Flushes remaining metrics
```

## Service Management

```python
from vikdata import VikdataClient

async with VikdataClient(base_url="...", api_key="...") as client:
    # Register service
    registration = await client.services.register(
        name="fishinfo",
        service_url="https://fishinfo.example.com",
        health_endpoint="/health",
        description="Maritime telemetry service",
        permissions=["fishinfo:read", "fishinfo:write"],
    )
    print(f"Service registered: {registration.id}")
    print(f"API Key: {registration.api_key}")

    # List all services
    services = await client.services.list()
    for service in services:
        print(f"{service.name}: {service.status}")

    # Get specific service
    service = await client.services.get(registration.id)

    # Check health status
    health = await client.services.health_status(registration.id)
    print(f"Health: {health.status}")

    # Report health
    await client.services.report_health(
        service_id=registration.id,
        status="healthy",
        checks={
            "database": {"healthy": True, "latency_ms": 5.2},
            "redis": {"healthy": True, "latency_ms": 1.1},
        },
    )

    # Unregister service
    await client.services.unregister(registration.id)
```

## Configuration

Configuration can be set via environment variables with `VIKDATA_` prefix:

```bash
export VIKDATA_BASE_URL=https://api.vikdata.com
export VIKDATA_API_KEY=vk_your_api_key
export VIKDATA_SERVICE_KEY=sk_your_service_key
export VIKDATA_TIMEOUT=30.0
export VIKDATA_MAX_RETRIES=3
export VIKDATA_USAGE_BATCH_SIZE=100
export VIKDATA_USAGE_FLUSH_INTERVAL=60.0
export VIKDATA_HEALTH_CHECK_INTERVAL=30.0
```

Or via code:

```python
from vikdata import VikdataClient, VikdataConfig

config = VikdataConfig(
    base_url="https://api.vikdata.com",
    api_key="vk_your_api_key",
    service_key="sk_your_service_key",
    timeout=30.0,
    max_retries=3,
    usage_batch_size=100,
    usage_flush_interval=60.0,
    health_check_interval=30.0,
)

client = VikdataClient.from_config(config)
```

## Middleware

### JWT Validation Only

```python
from vikdata.middleware import JWTValidationMiddleware

app.add_middleware(
    JWTValidationMiddleware,
    client=client,
    exclude_paths=["/health", "/metrics", "/docs"],
)
```

### Usage Tracking Only

```python
from vikdata.middleware import UsageMiddleware

app.add_middleware(
    UsageMiddleware,
    usage_reporter=usage_reporter,
)
```

### Combined (JWT + Usage)

```python
from vikdata.middleware import VikdataMiddleware

app.add_middleware(
    VikdataMiddleware,
    client=client,
    usage_reporter=usage_reporter,
    exclude_paths=["/health", "/metrics"],
)
```

## Advanced: ServiceManager

For more control over the service lifecycle:

```python
from vikdata import ServiceManager, VikdataClient
from contextlib import asynccontextmanager

client = VikdataClient(...)
manager = ServiceManager(
    name="fishinfo",
    service_url="https://fishinfo.example.com",
    client=client,
    health_endpoint="/health",
    description="Maritime telemetry service",
    permissions=["fishinfo:*"],
)

@asynccontextmanager
async def app_lifespan(app: FastAPI):
    # Register and start
    await manager.register()

    # Enable features
    health_checker, health_reporter = manager.enable_health_reporting(interval=30.0)
    health_checker.add_check("database", check_database)

    usage_reporter = manager.enable_usage_tracking(batch_size=100, flush_interval=60.0)

    await manager.start()

    yield

    # Cleanup
    await manager.stop()
    await manager.unregister()

app = FastAPI(lifespan=app_lifespan)
```

## Error Handling

The SDK provides specific exceptions for different error types:

```python
from vikdata.exceptions import (
    VikdataError,              # Base exception
    AuthenticationError,       # Invalid credentials/token
    AuthorizationError,        # Missing permissions
    NetworkError,              # Network connectivity issues
    RateLimitError,           # Rate limit exceeded
    ServiceRegistrationError,  # Service registration failed
    HealthCheckError,         # Health check failed
    ResourceNotFoundError,    # 404 errors
    ConflictError,            # 409 errors
    ServerError,              # 5xx errors
    TimeoutError,             # Request timeout
    APIError,                 # Generic API error
)

try:
    await client.services.register(...)
except ServiceRegistrationError as e:
    print(f"Registration failed: {e}")
except RateLimitError as e:
    print(f"Rate limited, retry after {e.retry_after} seconds")
except VikdataError as e:
    print(f"Vikdata error: {e}")
```

## Development

```bash
# Install development dependencies
pip install -e ".[dev,fastapi]"

# Run tests
pytest

# Run tests with coverage
pytest --cov=vikdata --cov-report=html

# Lint code
ruff check src/

# Type check
mypy src/
```

## License

MIT
