feat: client.requests + client.inbox — approve/reject both request systems programmatically (BLDX-1611) - #1003
Open
Aryamanz29 wants to merge 12 commits into
Open
feat: client.requests + client.inbox — approve/reject both request systems programmatically (BLDX-1611)#1003Aryamanz29 wants to merge 12 commits into
Aryamanz29 wants to merge 12 commits into
Conversation
…ox requests (BLDX-1611)
Ports the atlan-java RequestsEndpoint surface to pyatlan. Customers could
not approve Metadata Inbox requests programmatically: the Heracles REST
endpoints exist but pyatlan had no client for them (hasattr(client,
'requests') was False on every release).
Adds:
- model/atlan_request.py: AtlanRequest, AttributeRequest (creator),
AtlanRequestAction, AtlanRequestResponse
- client.requests (sync) and AsyncAtlanClient.requests (async) with
list / list_actionable / get / create / approve / reject
- Heracles API constants for /requests, /requests/actionable,
/requests/{id}, /requests/{id}/action
Notes:
- AttributeRequest.creator() sets requestType/approvalType/sourceType
explicitly: Heracles requires them on the wire and pyatlan's
exclude_unset serialization strips declared defaults (the BLDX-1589
trap) — test-covered.
- GET /requests/{id} can return a single-element list; the client
unwraps it (observed live).
- Visibility caveat documented on both clients: requests are scoped to
the token's identity — the API key's service account must be an admin
(or designated approver) to see and action requests raised for humans.
Validated end-to-end against a live tenant with an admin service-account
key: create -> list (filtered) -> get -> list_actionable -> approve ->
attribute change APPLIED on the asset; reject -> change NOT applied.
Unit: 8 new tests, full suite 6,867 green, qa-checks clean.
…ct) alongside classic client.requests (BLDX-1611) Tenants can run two coexisting request systems: the classic Requests module (client.requests, Heracles /requests) and the newer Governance Workflows Inbox (Labs flag GOVERNANCE_WORKFLOWS_INBOX; Heracles /approval-workflow-requests). Customers straddle both, so the SDK now speaks both: - client.inbox.get(guid) — one approval-workflow request - client.inbox.approve_all(group_key, sub_type?, comment?) — native server-side bulk approval (PUT /actions/bulk); group_key is the task GUID or related asset GUID - client.inbox.reject_all(...) — bulk rejection - Inbox tasks are Task assets — list them with FluentSearch on Task Notes: - this API family is snake_case on the wire (unlike classic camelCase); models disable the camelCase alias generator and a test guards it - sync + async parity; 5 new unit tests
…Is (BLDX-1611) - client.requests.list()/list_actionable() take typed, discoverable filters (status, request_type, destination_guid/qualified_name, entity_type, created_by) instead of requiring callers to know the Heracles JSON filter grammar; raw post_filter stays as an escape hatch and refuses to be silently combined with typed filters - new enums: AtlanRequestStatus, AtlanRequestType (all wire values from the Heracles contract), ApprovalWorkflowRequestType (Inbox task sub-types) — used across both clients - AtlanRequestResponse / AsyncAtlanRequestResponse now paginate lazily (iterate to walk ALL matches), mirroring UserResponse/GroupResponse - 5 new unit tests: typed-filter wire shape, AND-combining, raw+typed rejection, lazy pagination call pattern, enum sub_type coercion
…ulk-action (BLDX-1611) Classic flow is fully self-contained: creates its own AttributeRequest against a disposable term, finds it via the typed filter, approves and verifies the change LANDED on the backend entity, rejects a second and verifies it did not. Passes live (3 passed) against a test tenant. The inbox bulk-action test is gated on ATLAN_TEST_WORKFLOW_GROUP_KEY: creating workflow tasks programmatically requires an existing governance workflow (workflow_guids is required on POST /approval-workflow-requests), which only tenants with the Governance Workflows Labs feature have.
…cases (BLDX-1611) atlan_requests_test.py now covers only the classic Requests module (self-contained). inbox_test.py covers client.inbox with the two cases that pin the bulk semantics: - group_key = task guid -> exactly ONE task actioned (group of one) - group_key = asset guid -> EVERY pending task on the asset actioned (requires 2+ seeded tasks; asserts total_tasks >= 2) Both env-gated (ATLAN_TEST_INBOX_TASK_GUID / ATLAN_TEST_INBOX_ASSET_GUID) because workflow tasks cannot be self-created — raising one requires a configured governance workflow on the tenant.
…ate exclusion (BLDX-1611)
Live testing on a workflows-enabled tenant showed plain equality filters
({"status": "active"}) are silently ignored by the endpoint — a list
filtered to active returned already-approved and rejected requests too.
The Atlan UI's own grammar works: status through the $in operator, AND'd
with an isDuplicate:false clause. build_requests_filter now emits exactly
that shape (request_type gets $in as well; plain equality kept for guid/
qualified-name/entity-type/created-by, which match verified live).
…uests (BLDX-1611) Live kill-argo testing: listing a page containing already-actioned requests crashed with a pydantic ValidationError — the server returns rejectedBy/approvedBy as arrays (multi-approver), while the model (copied from atlan-java, which is stale here) typed them str. Now Union[str, List[str]] with a regression test parsing an actioned record.
…latform version (BLDX-1611) Second live failure on the same fields: rejectedBy is a list of approver OBJECTS on kill-argo (first fix assumed list of strings — typed from inference, not evidence). These fields now parse as Any with the variants documented; regression test parses string, string-list, and object-list shapes. Follow-up: capture the real payload and introduce a typed Approver model from evidence.
…ire, not a list (BLDX-1611)
Live get() on a workflows-enabled tenant returns approval_details as an
OBJECT ({is_auto_approved, manual_approver_details:{approvers, strategy}}),
not a list. Kept untyped like the other version-variant fields.
Live validation captured: get() requires the WORKFLOW-REQUEST guid (found
in the Task asset's task_actions fulfillment URL — the task guid itself
500s), and bulk actions are strictly recipient-scoped: neither task guid,
asset guid, nor request guid actions a task whose recipient is another
user, even for a gov-admin caller.
…xplanation (BLDX-1611) Validated live with a controlled experiment (same caller, two tasks): - task addressed to the caller -> bulk reject queued, total_tasks=1 - task addressed to another user -> 1003 'No pending tasks found for the specified group', even though the group visibly has a pending task Bulk approvals are strictly recipient-scoped and an admin role does not override it. The server message reads like a wrong group key; the SDK now raises an actionable message explaining the scoping and the automation path (workflow approver config currently supports only human users and groups, so automation may require a user token).
…foreign recipient (BLDX-1611) Live testing surfaced the second cause: a group whose tasks were all already approved returns the same 1003, and the previous message wrongly asserted recipient mismatch. The message now names both causes and how to distinguish them (task_execution_action via Task search).
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
Customers cannot manage Atlan's request/approval systems programmatically (Zendesk 127299 / BLDX-1611). Atlan has two coexisting systems and pyatlan covered neither:
/requests/approval-workflow-requestsGOVERNANCE_WORKFLOWS_INBOX, off by default)RequestsEndpoint)What this adds
client.requests(classic, java parity + improvements):client.inbox(governance workflows, first SDK anywhere):Sync + async parity throughout; typed filters (
$ingrammar mirrored from the UI — plain equality is silently ignored by the endpoint); enums (AtlanRequestStatus,AtlanRequestType,ApprovalWorkflowRequestType); lazy pagination matchingUserResponse/GroupResponse.Live validation (all cells verified on real tenants)
Classic (self-contained integration test, 3/3 passing live): create → typed-filter list → get → approve → change lands on the backend entity → reject → change NOT applied.
Inbox (validated on a workflows-enabled tenant):
get()with the workflow-request guid (found in the task'stask_actionsfulfillment URL; the task guid itself 500s) ✅reject_all(task_guid)→total_tasks: 1✅ ·approve_all(task_guid)→total_tasks: 1, task flips to APPROVED ✅approve_all(asset_guid)→total_tasks: 2, both tasks flip to APPROVED ✅ (true bulk)The identity rule (the actual customer blocker)
Approval rights follow the token's identity, in both systems:
The server's misleading
1003: No pending tasks foundis translated by the SDK into an actionable message naming both real causes (already-actioned vs foreign recipient) — each observed live.Wire-truth fixes found during live testing
statusrequires$in+isDuplicateclause (plain equality silently ignored)approvedBy/rejectedBy: string OR list-of-objects depending on version → lenient + regression-testedapproval_details: object, not list → lenientTesting
atlan_requests_test.py(self-contained, passing live) +inbox_test.py(env-gated: workflow tasks cannot be self-created —workflow_guidsrequired — so gated on pre-seeded task/asset guids)./qa-checksclean (1,013 files), full unit suite 6,877 greenCloses BLDX-1611.
🤖 Generated with Claude Code