Metadata-Version: 2.4
Name: nexgensis-connect
Version: 1.0.0
Summary: Controlled, auditable API payload mapping and orchestration for Django
Author: Nexgensis
Project-URL: Repository, https://github.com/Nexgensis/connect
Requires-Python: >=3.11
Description-Content-Type: text/markdown
Requires-Dist: Django<6.0,>=5.2
Requires-Dist: djangorestframework<4,>=3.15
Requires-Dist: django-filter<26,>=24
Requires-Dist: django-api-utility<4,>=3.0
Requires-Dist: cryptography<46,>=43
Provides-Extra: celery
Requires-Dist: celery<6,>=5.5; extra == "celery"
Requires-Dist: django-celery-beat<3,>=2.8; extra == "celery"
Requires-Dist: redis<7,>=6; extra == "celery"
Provides-Extra: postgres
Requires-Dist: psycopg[binary]<4,>=3.2; extra == "postgres"
Requires-Dist: dj-database-url<4,>=3; extra == "postgres"
Requires-Dist: gunicorn<24,>=23; extra == "postgres"
Provides-Extra: test
Requires-Dist: pytest<9,>=8; extra == "test"
Requires-Dist: pytest-django<5,>=4.11; extra == "test"
Requires-Dist: pytest-cov<7,>=6; extra == "test"
Requires-Dist: httpx<1,>=0.28; extra == "test"
Requires-Dist: fastapi<1,>=0.116; extra == "test"
Requires-Dist: uvicorn<1,>=0.35; extra == "test"
Requires-Dist: python-multipart<1,>=0.0.20; extra == "test"
Requires-Dist: build<2,>=1.2; extra == "test"

# Nexgensis Connect

Nexgensis Connect is a controlled API payload-mapping and orchestration package for Django. It adds reusable field and value mappings, ordered/parallel/for-each chains, manual/event/scheduled triggers, and immutable execution snapshots on top of `django-api-utility`.

## Responsibility boundary

```text
Trigger → Chain → Request mapping → API Utility definition key
        → authentication and HTTPX → Response mapping → execution audit
```

Nexgensis Connect owns mappings, chains, triggers, run state, and audit snapshots. API Utility owns services, endpoints, authentication, secrets, token caching, retries, schemas, HTTP transport, and process-wide mTLS. The framework never evaluates arbitrary Python, JavaScript, or expression code.

## Integrations are configuration, not code

Every business-system integration (BMR/eBMR, ERP, vendor APIs, our own internal sync APIs) goes through Connect. A chain's last step calls the receiving system's API, which does its own syncing - there is no separate "write to our database" step. Only calls to our own EdgeX infrastructure stay as direct `request_by_key` code.

What used to need a developer, and is now configured per endpoint / rule / chain:

| Need | How |
| --- | --- |
| Paged list APIs | Endpoint **Pagination**: `PAGE`, `OFFSET`, `CURSOR`, `NEXT_LINK` (see `services/pagination.py` for `pagination_config` keys). Pages are joined before response mapping; exceeding `max_pages` fails the call rather than silently truncating. |
| Combining / reshaping fields | Transforms `template`, `concat`, `coalesce`, `split`, `join`, `filter`, `flatten_tree` (plus the original single-value set). `GET field-mappings/transform-choices/` returns each one's description and example params. |
| Our code → our id | Transform `lookup` against a source registered once in `NEXGENSIS_CONNECT_LOOKUPS` (only registered sources are queryable). |
| Retrying a failed run safely | `POST runs/<id>/resume/` (Execution history → Resume): steps/items that already succeeded are logged `reused` and **not** called again. Set an endpoint's **Idempotency header** for upstreams that de-duplicate; the key is stable across resumes. |
| Change history / rollback | Every config save is a `ConfigRevision` (who, when, full snapshot) - `config-revisions/`, restore with `POST config-revisions/<id>/restore/`. |
| Promoting dev → prod | `GET config/export/[?chains=a,b]` and `POST config/import/` (`dry_run: true` shows create/update/unchanged first). Keys, not ids; transport (services, URLs, auth) stays per environment. |
| Approval for config changes | Set `NEXGENSIS_CONNECT_CHANGE_GATE` to a host function (see `services/change_gate.py`). edgenexus routes changes into its single-approver + e-signature change control (module `/integrations`), on by default when `DEBUG` is off (`CONNECT_CHANGE_CONTROL_ENABLED`). |

### Triggers - how an integration starts

Code never names a chain or an endpoint, only a trigger:

```python
from nexgensis_connect import trigger

result = trigger("mes.batch.completed", {"batch_no": "B-24-0117", "quantity": 500.2})
# {"trigger": ..., "status": "success" | "failed", "results": [{"chain", "status", "run_id", "message", "steps": [...]}]}
```

Other systems call `POST <connect mount>/triggers/<name>/fire/` with the payload as the body, using the host's normal API auth. Both are synchronous and return the same result once every connected chain has finished.

- **Trigger definitions** (`trigger-definitions/`, Integrations → Triggers) - `<system>.<entity>.<event>` name, payload fields (checked on every fire), sample, optional dedupe field. Created by developers; access through the host's access control.
- **Connections** (`triggers/`) - which chain(s) run for a name, each with its own payload mapping (same rules/transforms as field mapping). One name can feed several chains.
- **Schedules** - a connection started by Celery beat: cron + timezone, payload with `{now}`, `{today}`, `{last_success_at}`. Saving syncs beat automatically; a schedule never overlaps its own running run; `last_success_at` only advances on success.

The remaining sanctioned reason to write code is a genuinely new transform (`register_transform`) or auth type - written once, then reusable from config by every integration.

## Local development

Python 3.11 or newer is required. On this Intel macOS workspace, Python 3.12 is available at `/usr/local/bin/python3.12`.

```bash
cd /Users/Nexgensis-L010/Documents/IOT/nexgensis-connect
sh scripts/setup_local_env.sh
.venv/bin/python backend/manage.py migrate
.venv/bin/python backend/manage.py seed_demo
.venv/bin/pytest

cd frontend
npm install
npm run dev
```

The editable API Utility installation points to `../api-utility`. No API Utility source is copied into this repository.

## Docker end-to-end environment

```bash
sh scripts/generate_test_certs.sh certs
docker compose up --build --abort-on-container-exit --exit-code-from integration-tests
docker compose down --volumes
```

The environment starts PostgreSQL, Redis, Django/Gunicorn, Celery Worker, Celery Beat, the React/Vite console, a dummy HTTP API, and a client-certificate-required mTLS API. The one-shot test container runs the unit/integration suite and then exercises mapping, API Utility dispatch, Basic authentication, a real positive and negative mTLS handshake, Celery event delivery, Beat schedule synchronization, and execution-audit retrieval.

The recorded validation matrix is available in [TEST_RESULTS.md](TEST_RESULTS.md).

Endpoints:

- UI: `http://localhost:3001`
- API: `http://localhost:8000/api/connect/`
- Admin: `http://localhost:8000/admin/`

## Embedding in another Django service

Install `django-api-utility` and `nexgensis-connect`, then add both apps:

```python
INSTALLED_APPS = [
    # ...
    "django_api_utility",
    "nexgensis_connect",
]
```

Include `nexgensis_connect.urls` under an authenticated host route. The host application must apply its own DRF authentication, authorization, tenant isolation, payload-retention policy, Celery configuration, and API Utility secret provider.
