Metadata-Version: 2.4
Name: django-api-utility
Version: 2.0.0
Summary: Reusable API integration utility for Django services
Author: Nexgensis
License: MIT
Project-URL: Repository, https://example.com/django-api-utility
Requires-Python: >=3.11
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: Django>=5.2
Requires-Dist: requests>=2.32
Requires-Dist: cryptography>=43.0
Requires-Dist: jsonschema>=4.23
Requires-Dist: tenacity>=8.2
Requires-Dist: PyJWT>=2.8
Provides-Extra: test
Requires-Dist: pytest>=7.0; extra == "test"
Requires-Dist: pytest-django>=4.5; extra == "test"
Dynamic: license-file

# django-api-utility

Reusable Django app for calling external/partner APIs from a config-driven registry, with automatic OAuth token management and user session tracking.

## Features
- **Endpoint registry** — services and endpoints defined as DB models (`ExternalServiceConfig`, `ExternalServiceEndpoint`), resolved at call time by a `definition_key`.
- **Definition-key aliasing** — callers use a stable alias (e.g. `"users_get"`) mapped via JSON config to the underlying `definition_key`, decoupling call sites from registry changes.
- **Machine-token delegation** — fetches and caches per-service access tokens from an internal auth proxy (resolved through the same definition-key registry as any other endpoint); this package no longer performs the OAuth exchange or holds client secrets/signing keys itself.
- **Payload key remapping & content-type control** — per-endpoint `payload_key_map` and `content_type` on `ExternalServiceEndpoint`.
- **Request/response schema validation** — optional JSON Schema validation per endpoint via `ExternalEndpointSchema`.
- **Resilient HTTP execution** — connection-level retries (urllib3) plus status-based retries with exponential backoff/jitter (tenacity) for idempotent methods, transparent 401 → token-refresh → retry-once, and upstream error-message extraction (including non-standard batch/207 responses).
- **Structured exception hierarchy** — typed errors carrying `http_status`, `is_retryable`, and `error_id` for observability/middleware mapping.

## Install
```bash
pip install -e .
```

## Django setup
```python
INSTALLED_APPS = [
    # ...
    "django_api_utility",
]
```
Run migrations to create the registry, token config, schema, and session tables.

## Definition key mapping
Provide a JSON mapping file via `EXTERNAL_DEFINITION_KEYS_FILE`, or use the packaged default at `config/external_definition_keys.json`.

```json
{
  "users_get": "service_users_get"
}
```

## Usage

### Calling an endpoint
```python
from django_api_utility import get_by_key, post_by_key

response = get_by_key("users_get", params={"page": 1})
create_response = post_by_key("users_create", payload={"name": "Alice"})
```
`request_by_key` (and its `get/post/put/patch/delete_by_key` shortcuts) resolves the endpoint, attaches a valid bearer token when the service requires auth, validates payload/response against any configured schema, retries on transient failures, and refreshes+retries once on a `401`.

## Configuration (Django settings)
| Setting | Purpose | Default |
|---|---|---|
| `EXTERNAL_DEFINITION_KEYS_FILE` | Path to definition-key JSON mapping | packaged `config/external_definition_keys.json` |
| `API_UTILITY_FERNET_KEY` | Encryption key for stored secrets/tokens | derived from `SECRET_KEY` |
| `API_UTILITY_VALIDATE_SCHEMAS` | Enable request/response schema validation | `True` |
| `API_UTILITY_MACHINE_TOKEN_DEFINITION_KEY` | Definition key for the auth proxy's machine-token endpoint | `AUTH_PROXY_MACHINE_TOKEN` |
| `API_UTILITY_TOKEN_EXPIRY_BUFFER_SECONDS` | Refresh-ahead buffer before expiry | `60` |
| `API_UTILITY_DEFAULT_TOKEN_EXPIRES_IN` | Fallback token TTL if the proxy omits `expires_in` | `3600` |

## Internal layout
- `django_api_utility/models.py`: service/endpoint registry, token config, and schema models
- `django_api_utility/transport/`: shared `requests` session (connection-level retries), retry policy, outbound execution primitive
- `django_api_utility/domain/`: definition-key resolution, endpoint resolution, token lifecycle, schema validation
- `django_api_utility/orchestration/`: `request_by_key` and friends — retries, auth refresh, schema hooks, error normalization

## Tests
```bash
pip install -e ".[test]"
pytest -q
```

## Changelog

### 0.2.0
- **Token issuance moved behind an auth proxy.** The package no longer performs the OAuth exchange (`client_credentials`, IDP `jwt_bearer`/RS256) or stores client secrets/signing keys itself — it now fetches a ready-to-use, cached machine token from an internal auth proxy endpoint, resolved through the normal definition-key registry.
- **Session tracking removed.** The `Session` model and `session_service` (SSO login-session tracking, revoke-on-logout/new-login) were dropped; that responsibility now lives in the auth proxy.
- **Structured exceptions.** `HttpClientError`, `TokenRequestError`, and `EndpointRequestError` now carry `http_status`, `is_retryable`, and `error_id`, so callers can map failures to typed responses without parsing message strings.
- **Resilient HTTP execution.** `transport/http_client.py` now reuses a pooled `requests.Session` with urllib3 connection-level retries (DNS/reset/429), and `orchestration/api_service.py` adds status-based retries with exponential backoff/jitter via `tenacity` for idempotent methods, plus better non-JSON/error-body handling.
- **`form_data` support** end-to-end through `request_by_key`/`_send_request`, alongside existing JSON payloads.
- **Per-endpoint payload key remapping and `content_type`** on `ExternalServiceEndpoint`.
- **Raised floors:** Python 3.11+, Django 5.2+, requests 2.32+, cryptography 43+, jsonschema 4.23+; added `tenacity` and `PyJWT` as dependencies.

### 0.1.0
- Initial release: config-driven endpoint registry (`ExternalServiceConfig`/`ExternalServiceEndpoint`), definition-key aliasing, `client_credentials`/IDP `jwt_bearer` OAuth token lifecycle with Fernet-encrypted secret storage, `Session` model + `session_service` for SSO login tracking, JSON Schema validation, basic retry/backoff on outbound calls.
