Repository navigation
Conversation
…ils handling
Implement inbound event-driven status notification endpoints under
/unit_jobs/{job_id}/ to receive CloudEvents from execution runners,
map stages to APEx processing statuses, update the database, and
return RFC 7807 problem details.
- Add dependencies eoap-problems-registry[fastapi] and stage-events-client
to requirements.txt.
- Add DISPATCH_API_PUBLIC_URL setting in app/config/settings.py and env.example.
- Register problem_registry_exception_handler in app/middleware/error_handling.py
to produce application/problem+json responses for RFC 7807 errors.
- Add CloudEvent schemas, response models, and STAGE_TO_APEX_STATUS mapping
in app/schemas/callbacks.py.
- Implement get_job_by_platform_or_id and update_job_status_from_callback in
app/services/processing.py without altering database models.
- Implement POST /unit_jobs/{job_id}/success, /inProgress, and /failed endpoints
in app/routers/unit_jobs.py returning 202 Accepted.
- Surgically integrate callback entry points and component schemas into openapi.json.
- Add unit tests covering 202 status transitions and 404 RFC 7807 responses
in tests/routers/test_unit_jobs_callbacks.py.
Enhance the POST /processes/{processID}/execution request payload in
OGCAPIProcessPlatform.execute_job() with the 'subscriber' object
(successUri, inProgressUri, failedUri) to register APEx callback URIs
with the runner according to the OGC API - Processes specification.
- Inject subscriber dictionary into execute_job payload in
app/platforms/implementations/ogc_api_process.py.
- Construct callback URIs dynamically using DISPATCH_API_PUBLIC_URL.
- Keep openEO platform execution completely untouched.
- Update test assertions in tests/platforms/test_ogc_api_process_platform.py
to verify subscriber block presence.
…ror details - Update eoap-problems-registry to 1.7.0 and stage-events-client to 1.1.0 in requirements.txt. - Remove custom problem_registry_exception_handler in app/middleware/error_handling.py and import it from eoap_problems_registry.fastapi. - Enrich NotFound and ServerError ProblemRegistryException instances with fine-grained ErrorDetail objects in unit jobs callback endpoints. - Update unit tests in test_unit_jobs_callbacks.py to assert the returned errors detail structure.
…duplicate logic - Refactor on_success_job, on_progress_job, and on_failed_job in app/routers/unit_jobs.py from async def to synchronous def to allow FastAPI to offload synchronous database operations to the threadpool. - Change HTTP response status from 202 Accepted to 200 OK across app/routers/unit_jobs.py and openapi.json to accurately reflect immediate, synchronous database state updates. - Introduce _handle_callback_status_update helper function to eliminate repetitive try/except blocks, logging, ErrorDetail, and ProblemRegistryException boilerplate across all callback endpoints. - Update unit tests in tests/routers/test_unit_jobs_callbacks.py to assert HTTP_200_OK.
- Update event status mapping: submitted -> QUEUED, prepared -> RUNNING, piped/completed -> FINISHED. - Update corresponding unit test assertions.
- Allow looking up jobs using <namespace>:<uid>:<workflow_name> format. - Clean up noisy debug logs in lookup functions.
- Switch endpoints to /unit_jobs/success, /inProgress, and /failed. - Read job identity directly from CloudEvent body. - Update subscriber URLs and openapi.json.
This branch has not been deployed
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.
APEx and the target OGC API Processes platform maintain separate records of a job’s status, once APEx submits a job, the target platform executes it independently and updates its own status as processing progresses and these changes are not automatically reflected in the status stored by APEx.
Currently, rebuilding the full API call stack, as shown in the sequence diagram below, APEx refreshes its stored job status only when a client invokes
GET /jobs_status. This means that synchronization depends on a client requesting an update, rather than on the job actually changing state. Between requests—or if no further requests are made—the status recorded by APEx can become outdated.For example, a job may have already completed, failed, or been canceled on the target platform while APEx still records it as queued or running. This discrepancy does not necessarily indicate an execution problem: it means that APEx has not yet learned about the change. As a result, users and services relying on APEx’s stored status may receive an inaccurate picture of ongoing processing, and APEx cannot provide timely notifications of job status changes.
As a result:
RUNNINGuntil a client queries its status);This PR addresses the gap by allowing the target platform to notify APEx when a job progresses or reaches a final state. APEx updates its stored status when these notifications arrive, reducing the delay between execution changes and their visibility in APEx, without requiring a client to request a status refresh.
Solution description
Without drastically redesigning the APEx Dispatcher API, our solution builds upon the existing OGC API - Processes execute API by extending its execution request payload with the
subscriberelement:This PR enables the Dispatch API to receive job lifecycle notifications from execution runners and update the stored APEx job status. OGC execution requests now include subscriber callback URLs, allowing runners to report progress, completion, and failure directly to the dispatcher.
The callback payloads use a common envelope based on the CloudEvents core specification and its Partitioning extension. Building on this envelope, the Stage Events event models define the event types and stage-specific data, giving runners and the dispatcher a shared, explicitly defined contract for each execution stage. Reusing their schema-generated Pydantic models provides validation of required fields and event types at the API boundary, reducing duplicated schema maintenance and the risk of producers and consumers interpreting the same notification differently. The common CloudEvent envelope carries the event type, source, and workflow subject, providing consistent context for identifying the originating execution and correlating its notifications. Stage-specific payloads retain execution detail, such as inputs, outputs, usage, and optional failure information, while the dispatcher maps supported events to its existing APEx status model. This keeps runner event semantics separate from the dispatcher's status representation and provides a foundation for future monitoring consumers to reuse the same payload contract. Together, the OGC subscriber mechanism and the Stage Events payloads address complementary concerns: where notifications are delivered and how their content is interpreted. Delivered callbacks can therefore update stored job status without waiting for a client status query, using an existing event model without requiring a database schema change.
The implementation adds three endpoints accepting structured
CloudEvents:POST /unit_jobs/inProgresssubmittedQUEUEDPOST /unit_jobs/inProgressprepared,stagedRUNNINGPOST /unit_jobs/inProgressdismissedCANCELEDPOST /unit_jobs/successpiped,completedFINISHEDPOST /unit_jobs/failedfailedFAILEDCallbacks identify the job through the CloudEvent
subject, without requiring a job ID in the callback URL. Three-part subjects (<namespace>:<workflow_uid>:<workflow_name>) are normalized to<namespace>:<workflow_name>. The lookup service supports internal numeric IDs and platform job identifiers, including workflow-name matching.Successful callbacks return
200 OK. Progress notifications received before the job is registered are also acknowledged with200 OK, without updating a record, to accommodate notifications arriving during job creation. Success and failure callbacks for unknown jobs return404, while unexpected errors during callback lookup or status updates return500.These callback errors are returned as
application/problem+json, the JSON representation defined by RFC 9457 — Problem Details for HTTP APIs, which supersedes RFC 7807. The standard complements HTTP status codes with a machine-readable problem description:typeidentifies the problem category,titleprovides a short summary,statusrecords the HTTP status,detailprovides explanatory text, andinstanceidentifies the problem occurrence. This gives callback senders a predictable response structure and allows them to classify errors by problem type without parsing human-readable messages.The implementation uses the EOAP Problems Registry to supply shared problem definitions and their documented type URIs. Its
NotFoundandServerErrormodels, serialized through the registry's FastAPI exception handler, keep callback responses aligned with the same error vocabulary used by other APIs adopting the registry. The dispatcher setsinstanceto the callback request path and adds job-specific information through the registry'serrorsextension, withparameter="job_id"and a descriptivedetail. Here,job_idlabels the affected job identifier extracted from the CloudEvent subject; it is not a callback URL parameter. Reusing these models centralizes the response definitions, reduces duplicated error-handling code, and gives clients and monitoring tools a consistent basis for reporting and diagnosing callback failures.Solution implementation details
Additional changes include:
successUri,inProgressUri, andfailedUrito the subscriber payload sent with OGC execution requests.DISPATCH_API_PUBLIC_URLto construct callback URLs.CREATEDtoQUEUED, and mapping OGC accepted status toQUEUED.Tests added or updated cover event-to-status mappings, subject normalization, job lookup and status updates, unknown-job problem responses, early progress acknowledgements, and subscriber payload generation.
For deployment, set
DISPATCH_API_PUBLIC_URLto the externally reachable Dispatch API base URL, including any deployment path prefix. It defaults tohttp://localhost:8000.No database schema migration is required.
Callback invocation examples
Set the Dispatch API base URL, including any deployment path prefix:
DISPATCH_API_BASE_URL="http://localhost:8000"The examples below use the workflow subject
default:test-uid:job-123, which the dispatcher resolves to the platform job identifierdefault:job-123. Replace these values with an existing test job's namespace, workflow UID, and workflow name. Keeppartitionkeyaligned withsubjectand update the identifiers indataaccordingly. Each example includes a distinct sample eventid; use a new ID for each distinct event you produce.These are individual examples, not a script to run sequentially: success, failure, and dismissal represent different outcomes. Each invocation updates the matching job's stored status. Empty objects illustrate the payload shapes used in the callback tests; real runners can supply the corresponding inputs, outputs, entries, and usage information.
submitted→QUEUEDprepared→RUNNINGstaged→RUNNINGpiped→FINISHEDcompleted→FINISHEDfailed→FAILEDdismissed→CANCELEDFor
dismissed, the payload'sdata.statusis the runner status (Pendingin this example); the event type determines the APEx mapping toCANCELED.For an existing job, each successful invocation returns
200 OK. AninProgresscallback for an unknown job also returns200 OK, but no record is updated. Unknown jobs on thesuccessandfailedendpoints return404with anapplication/problem+jsonresponse; unexpected lookup or update errors return500in the same format.The sequence diagram below illustrates the complete flow, from registering callback URLs during job submission to updating the stored APEx job status when notifications arrive:
Co-authored-by: maxmiraftab max.miraftab@terradue.com
Appendix: Reusing job status notifications across target platforms
Integrators of other APEx target platforms are encouraged to adopt the same notification endpoints and payload format introduced by this PR. Although the integration described here uses OGC API Processes to register callback URLs, the notification mechanism provides a common way for target platforms to report job progress and outcomes to APEx.
By sending notifications when a job changes state, a platform can help APEx maintain an up-to-date view of its execution without waiting for a client to request a status refresh. Extending this approach across integrations would provide users with more consistent status reporting, regardless of which platform executes their jobs.
Integrations should reuse the existing endpoints:
POST /unit_jobs/inProgressfor submission, progress, and cancellation notifications.POST /unit_jobs/successfor successful execution.POST /unit_jobs/failedfor failed execution.Notifications should follow the same CloudEvents envelope and supported Stage Events payload schemas, including the job reference used by APEx to associate each notification with the correct execution. Platforms with different internal lifecycle models should map their states to the supported events, preserving the shared meaning of each event and its corresponding APEx status.
Reusing this contract avoids introducing a separate notification format or additional endpoints for every platform. It also allows validation, status mapping, and error handling to remain consistent across integrations, while providing a shared foundation for future monitoring capabilities.
Integrators are therefore invited to use the payload definitions and invocation examples in this PR as the starting point for their implementation. Where a platform’s lifecycle cannot be represented by the existing events, we encourage discussing extensions to the shared contract so that improvements can benefit all participating platforms.