feat(handler): per-entrypoint compute_manifest hook for dynamic apps#1629
feat(handler): per-entrypoint compute_manifest hook for dynamic apps#1629OnkarVO7 wants to merge 6 commits into
Conversation
Add a convention-based extension point so multi-entrypoint apps can
inject *dynamic* per-submission logic (placeholder fill-in, SQL gen,
full DAG rewrite) into the `/manifest` response without monkey-patching
`create_app_handler_service` or shipping their own router.
## How
The `/manifest` and `/workflows/v1/manifest` handlers now:
1. Load the static manifest (existing behaviour)
2. Substitute `{deployment_name}` and `{app_name}` (existing behaviour)
3. **NEW**: Look for `app.<entrypoint_snake>.core.compute_manifest`
where `entrypoint_snake = entrypoint.replace("-", "_")`. If found
and callable, hand it the static manifest dict + the decoded
`fe_inputs` query payload (Heracles forwards FE form data this way
per the Heracles change in atlanhq/heracles#5704). The callable's
return dict becomes the response body.
Apps that don't define the convention module get the static manifest
unchanged — fully backward-compatible.
Convention rationale: kebab-cased entrypoint names match
`atlan.yaml`'s `entrypoints[].name` (and the build pipeline regex);
Python module identifiers can't contain hyphens, hence the snake
translation. The `app.` prefix matches the established `app/<pkg>/`
layout used by teradata and the new csa-uber-app
(atlanhq/atlan-csa-uber-app#11).
## API surface
```python
# Apps that need dynamic substitution drop this in:
# app/<snake>/core.py
def compute_manifest(manifest: dict, fe_inputs: dict) -> dict:
return {**manifest, "extras": fe_inputs}
```
```http
GET /workflows/v1/manifest?entrypoint=csa-hello-a&fe_inputs=%7B%22x%22%3A1%7D
```
`fe_inputs` is JSON sent as a URL-encoded query param (no body — keeps
the route GET). Empty/missing → hook receives `{}`. Malformed JSON →
HTTP 400 (not 500).
## Tests
Added `TestComputeManifestHook` with six cases: hook invocation,
no-hook fallback, missing-fe_inputs default, malformed-fe_inputs 400,
legacy `/manifest` alias forwarding, and kebab→snake translation.
Tests inject the fake compute module via `monkeypatch.setitem(sys.modules,
...)` so they don't touch the real filesystem.
221 / 221 handler tests pass.
## Replaces
The CSA Uber App's monkey-patch in `csa_uber_app/sdk_extensions.py`
and ~150 lines of `csa_uber_app/routes.py`. The follow-up cleanup PR
in atlanhq/atlan-csa-uber-app removes both once this ships.
✅ Snyk checks have passed. No issues have been found so far.
💻 Catch issues earlier using the plugins for VS Code, JetBrains IDEs, Visual Studio, and Eclipse. |
📜 Docstring Coverage ReportRESULT: PASSED (minimum: 30.0%, actual: 79.6%) Detailed Coverage Report |
📦 Trivy Vulnerability Scan Results
Report SummaryCould not generate summary table (data length mismatch: 9 vs 8). Scan Result Detailsrequirements.txtuv.lock |
📦 Trivy Secret Scan Results
Report SummaryCould not generate summary table (data length mismatch: 9 vs 8). Scan Result Detailsrequirements.txtuv.lock |
☂️ Python Coverage
Overall Coverage
New FilesNo new covered files... Modified FilesNo covered modified files...
|
|
🛠 Full Test Coverage Report: https://k.atlan.dev/coverage/application-sdk/pr/1629 |
…/fetch_metadata Mirrors the compute_manifest discovery pattern for the three handler lifecycle endpoints. Multi-entrypoint apps drop a per-package `app.<entrypoint_snake>.handler` module exposing plain async `test_auth` / `preflight_check` / `fetch_metadata` functions; the SDK discovers them via importlib and dispatches based on the `connector` field that Heracles already sends in the body. - Add `connector: str = ""` to AuthInput, PreflightInput, MetadataInput. - Add `_strip_app_prefix(connector, app_name)` and `_discover_handler_fn` helpers in handler/service.py. - Wire dispatch into the three route handlers; fall through to the app-level `Handler` instance when no per-entrypoint module is found. - Tests: 9 new cases covering hit/miss, empty connector, malformed prefix, and entrypoint fn raising. Backward-compatible: single-entrypoint apps continue to use `Handler.<fn>` exactly as before.
|
@sdk-review |
|
🔄 SDK Review starting (review) — ~10-15 min. Watch live progress |
cmgrote
left a comment
There was a problem hiding this comment.
Few questions:
- Can we not define typed contracts everywhere? For example I see methods that return but have no defined return type, translation of JSON into arbitrary dictionaries, etc
- I'm not crazy about carrying Heracles' "connector" terminology when what we're really talking about is an (app-qualified) entrypoint. Any chance we can rename that on our end proactively (leave renaming in Heracles for another day)?
@vaibhavatlan can you look at & comment on the transformations? I'm seeing various snake/kebab translations -- thought we were centralizing those somewhere else?
PreflightInput: both branches added independent fields (connector from feature branch, metadata from main). Combined both fields.
📚 Capability manifest drift detected
To resolve, pick one:
This check is informational, not blocking. Reviewers should ensure the manifest is |
SDK Review: PR #1629 — feat(handler): per-entrypoint compute_manifest hook for dynamic appsVerdict: NEEDS FIXES
Findings by File
Strengths
|
…or absent
Heracles' configmap-based credential metadata path forwards the config
name (e.g. "atlan-connectors-asset-export-advanced") but doesn't always
populate the body's `connector` field, so the per-entrypoint dispatch
on /workflows/v1/{auth,check,metadata} silently fell through to the
app-level Handler instead of routing to `app.<snake>.handler.<fn>`.
Adds a `_resolve_entrypoint` helper that tries `connector` first and
falls back to `connectorConfigName`; both call into the existing
`_entrypoint_from_connector` which now also accepts an exact-match
(connector == entrypoint) on top of the documented hyphen-suffix.
Also retargets the three "no entrypoint module" fallback tests at a
contrived `sdk-fallback-noep-*` name so they don't collide with real
`app.csa_hello_*` modules that may be on PYTHONPATH in dev envs where
the uber-app repo is editable-installed alongside the SDK.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…lter Heracles' app client posts the widget-specific filter key as ``metadataTemplateKey`` (and a ``type`` mirror it adds) but the v3 SDK's ``MetadataInput`` only exposes ``object_filter``. Per-entrypoint hooks that branch their fetch_metadata response by widget — e.g. asset-export-advanced returning tags vs connectors vs typenames vs custommetadata — saw an empty ``object_filter`` and fell through to their default branch, so every widget rendered the same fallback list. Bridge the gap in the /workflows/v1/metadata route: when ``object_filter`` is empty after model_validate, populate it from ``metadataTemplateKey`` (preferred) or ``type`` (Heracles mirror) read from the raw request body. An explicit ``object_filter`` in the body still wins. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Summary
Add a convention-based extension point so multi-entrypoint apps can inject dynamic per-submission logic (placeholder fill-in, SQL gen, full DAG rewrite) into the `/manifest` response without monkey-patching `create_app_handler_service` or shipping their own router.
How
The `/manifest` and `/workflows/v1/manifest` handlers now:
Apps that don't define the convention module get the static manifest unchanged — fully backward-compatible.
Convention rationale
API surface
`fe_inputs` is JSON sent as a URL-encoded query param (no body — keeps the route GET). Empty/missing → hook receives `{}`. Malformed JSON → HTTP 400 (not 500).
Failure-mode matrix
Tests
Added `TestComputeManifestHook` with six cases:
Tests inject the fake module via `monkeypatch.setitem(sys.modules, ...)` so they don't touch the real filesystem.
Local: 221/221 handler tests pass (was 215; +6 new).
Test plan
Replaces
The CSA Uber App's monkey-patch in `csa_uber_app/sdk_extensions.py` (~75 lines) and `csa_uber_app/routes.py` (~125 lines). Follow-up cleanup tracked in atlanhq/atlan-csa-uber-app#11 (will rebase to drop those files once this ships).
Linear: HYP-878