DoculaDevelopersDezvoltatori
API 0.8.0 · BuildVersiune produs 0.33.0
Documentation navigationNavigare documentație

ExamplesExemple · API 0.8.0

execution-trace

Each example is a fixture directory validated at build time against the released contracts: a JSON contract, a declarative transform, a template, a curl sequence and the expected output. Guides embed these files verbatim so documentation cannot drift from validated fixtures.Fiecare exemplu este un director de fixture-uri validat la build față de contractele publicate: un contract JSON, o transformare declarativă, un șablon, o secvență curl și rezultatul așteptat. Ghidurile includ aceste fișiere textual, astfel încât documentația nu poate devia de fixture-urile validate.

Files in this fixtureFișierele acestui fixture

execution-trace/curl.sh · 3407 bytes · sha256 31ee4ce4cdea48c1…
#!/bin/sh
# Docula example: execution trace and artifacts.
# login -> submitExecution -> streamExecutionEvents (SSE) until completion -> getExecution -> downloadExecutionArtifact + hash check.
# Requires curl, jq and sha256sum. Environment: DOCULA_API, DOCULA_ORIGIN (defaults to DOCULA_API), DOCULA_USERNAME,
# DOCULA_PASSWORD, DOCULA_RELEASE_ID (a published document-generation release whose contract accepts the person input).
set -eu
API=${DOCULA_API:?set DOCULA_API to the API origin, for example http://127.0.0.1:18081}
ORIGIN=${DOCULA_ORIGIN:-$API}
HERE=$(cd "$(dirname "$0")" && pwd)
JAR=$(mktemp)
trap 'rm -f "$JAR" "$JAR.body" "$JAR.events"' EXIT

CSRF=$(jq -n --arg u "${DOCULA_USERNAME:?}" --arg p "${DOCULA_PASSWORD:?}" '{username: $u, password: $p}' |
  curl --fail --silent --show-error -c "$JAR" -H "Origin: $ORIGIN" -H 'Content-Type: application/json' \
    --data-binary @- "$API/api/v1/auth/login" | jq -r '.csrf_token')

# 1. Submit (submitExecution). Idempotency-Key is required: replaying the same key and body returns the same execution (200),
#    a different body under the same key is a 409.
jq --arg r "${DOCULA_RELEASE_ID:?}" '.release_id = $r' "$HERE/submit-execution.request.json" > "$JAR.body"
SUBMITTED=$(curl --fail --silent --show-error -b "$JAR" -X POST -H "Origin: $ORIGIN" -H "X-CSRF-Token: $CSRF" \
  -H "Idempotency-Key: docs-example-$(date +%Y%m%d%H%M%S)" -H 'Content-Type: application/json' \
  --data-binary @"$JAR.body" "$API/api/v1/executions")
EXECUTION_ID=$(printf '%s' "$SUBMITTED" | jq -r '.execution.id')

# 2. Follow the trace (streamExecutionEvents). The stream replays persisted events, then continues live and closes itself
#    after execution.completed or step.failed. Send Last-Event-ID to resume after a disconnect.
curl --fail --silent --show-error --no-buffer --max-time 120 -b "$JAR" -H 'Accept: text/event-stream' -H 'Last-Event-ID: 0' \
  "$API/api/v1/executions/$EXECUTION_ID/events" | tee "$JAR.events"
grep -q '"type":"execution.completed"' "$JAR.events" || { echo "execution did not complete" >&2; exit 1; }

# 3. Read the final state (getExecution): four steps and the retained artifacts with their SHA-256 hashes.
EXECUTION=$(curl --fail --silent --show-error -b "$JAR" "$API/api/v1/executions/$EXECUTION_ID")
printf '%s' "$EXECUTION" | jq '{status, steps: [.steps[] | {name, status, duration_ms}], artifacts}'

# 4. Download one artifact (downloadExecutionArtifact is a POST with an empty JSON body because it is audited)
#    and verify the bytes against the X-Content-SHA256 header and the recorded content_hash.
ARTIFACT_ID=$(printf '%s' "$EXECUTION" | jq -r '.artifacts[] | select(.kind == "pdf") | .id')
EXPECTED=$(printf '%s' "$EXECUTION" | jq -r '.artifacts[] | select(.kind == "pdf") | .content_hash')
curl --fail --silent --show-error -b "$JAR" -X POST -H "Origin: $ORIGIN" -H "X-CSRF-Token: $CSRF" \
  -H 'Content-Type: application/json' --data-binary @"$HERE/download-artifact.request.json" \
  -D "$JAR.headers" -o document.pdf "$API/api/v1/executions/$EXECUTION_ID/artifacts/$ARTIFACT_ID/download"
HEADER_HASH=$(tr -d '\r' < "$JAR.headers" | awk 'tolower($1) == "x-content-sha256:" { print $2 }')
ACTUAL=$(sha256sum document.pdf | cut -d ' ' -f 1)
rm -f "$JAR.headers"
[ "$ACTUAL" = "$EXPECTED" ] && [ "$ACTUAL" = "$HEADER_HASH" ] || { echo "artifact hash mismatch" >&2; exit 1; }
printf 'document.pdf sha256=%s\n' "$ACTUAL"
execution-trace/download-artifact.request.json · 3 bytes · sha256 ca3d163bab055381…
{}
execution-trace/events.sse · 2884 bytes · sha256 9a061eb11628f777…
retry: 1000

id: 1
event: execution
data: {"id":1,"execution_id":"55555555-5555-4555-8555-000000000001","type":"execution.submitted","correlation_id":"docs-example-correlation-id","payload":{"input_hash":"6444ce79c02f6b8557cd5def4969b6f49e0c371530e24b3d5b5eff8aeb99d700","raw_input_hash":"889007605af33b472a3f0b3e0c85826ca70cc3484d668e2cae165ebef41a04d8","release_hash":"a4d451ec23463726f72c43d64c710968f6b602cd653b4de8adee1b556240a829"},"occurred_at":"2026-08-30T09:15:00Z"}

id: 2
event: execution
data: {"id":2,"execution_id":"55555555-5555-4555-8555-000000000001","type":"step.succeeded","step":"validate","attempt":1,"correlation_id":"docs-example-correlation-id","payload":{"contract_hash":"cc8321d6375c494d043fdd0260f21bc0ec51dacc9f6abb7f909cdcd3041b78bf","contract_version_id":"11111111-1111-4111-8111-000000000001","input_hash":"6444ce79c02f6b8557cd5def4969b6f49e0c371530e24b3d5b5eff8aeb99d700"},"occurred_at":"2026-08-30T09:15:01Z"}

id: 3
event: execution
data: {"id":3,"execution_id":"55555555-5555-4555-8555-000000000001","type":"step.succeeded","step":"transform.declarative","attempt":1,"correlation_id":"docs-example-correlation-id","payload":{"input_hash":"6444ce79c02f6b8557cd5def4969b6f49e0c371530e24b3d5b5eff8aeb99d700","output_hash":"e0ee8bb50685e05fa0f47ed04203ae953fdfd055f5bd2892ea186504254f8c3a","transform_hash":"aa214ea38326805d95661c3ad1643cc07f88e2bae0438ac0448a66d93335ca6e","transform_version_id":"22222222-2222-4222-8222-000000000001"},"occurred_at":"2026-08-30T09:15:02Z"}

id: 4
event: execution
data: {"id":4,"execution_id":"55555555-5555-4555-8555-000000000001","type":"step.succeeded","step":"compose","attempt":1,"correlation_id":"docs-example-correlation-id","payload":{"artifact_id":"77777777-7777-4777-8777-000000000001","kind":"docx","media_type":"application/vnd.openxmlformats-officedocument.wordprocessingml.document","byte_size":18432,"content_hash":"584cb925e6ad45273e46037369c5ec3a5d7cfdd409ce13a69e7087f8accd1c79"},"occurred_at":"2026-08-30T09:15:04Z"}

id: 5
event: execution
data: {"id":5,"execution_id":"55555555-5555-4555-8555-000000000001","type":"step.succeeded","step":"render","attempt":1,"correlation_id":"docs-example-correlation-id","payload":{"artifact_id":"77777777-7777-4777-8777-000000000002","kind":"pdf","media_type":"application/pdf","byte_size":24576,"content_hash":"c35b21d6ca39aa7cc3b79a705d989f1a6e88b99ab43988d74048799e3db926a3"},"occurred_at":"2026-08-30T09:15:07Z"}

id: 6
event: execution
data: {"id":6,"execution_id":"55555555-5555-4555-8555-000000000001","type":"execution.completed","step":"render","attempt":1,"correlation_id":"docs-example-correlation-id","payload":{"artifact_id":"77777777-7777-4777-8777-000000000002","kind":"pdf","media_type":"application/pdf","byte_size":24576,"content_hash":"c35b21d6ca39aa7cc3b79a705d989f1a6e88b99ab43988d74048799e3db926a3"},"occurred_at":"2026-08-30T09:15:07Z"}

execution-trace/execution.json · 4083 bytes · sha256 979b2e1f98f1fc3c…
{
  "id": "55555555-5555-4555-8555-000000000001",
  "release_id": "66666666-6666-4666-8666-000000000001",
  "release_hash": "a4d451ec23463726f72c43d64c710968f6b602cd653b4de8adee1b556240a829",
  "processing_kind": "document_generation",
  "input": {
    "first_name": "Iulia",
    "last_name": "Popescu",
    "email": "[email protected]"
  },
  "input_hash": "6444ce79c02f6b8557cd5def4969b6f49e0c371530e24b3d5b5eff8aeb99d700",
  "raw_input": "{\"first_name\": \"Iulia\", \"last_name\": \"Popescu\", \"email\": \"[email protected]\"}",
  "raw_input_hash": "889007605af33b472a3f0b3e0c85826ca70cc3484d668e2cae165ebef41a04d8",
  "output": {
    "display_name": "Iulia Popescu",
    "first_name": "Iulia",
    "middle_name": null,
    "preferred_contact": "[email protected]",
    "source": "docs-example"
  },
  "output_hash": "e0ee8bb50685e05fa0f47ed04203ae953fdfd055f5bd2892ea186504254f8c3a",
  "status": "completed",
  "submitted_by": "88888888-8888-4888-8888-000000000001",
  "correlation_id": "docs-example-correlation-id",
  "created_at": "2026-08-30T09:15:00Z",
  "updated_at": "2026-08-30T09:15:07Z",
  "completed_at": "2026-08-30T09:15:07Z",
  "steps": [
    {
      "name": "validate",
      "position": 1,
      "status": "succeeded",
      "attempt": 1,
      "state_version": 2,
      "started_at": "2026-08-30T09:15:00Z",
      "finished_at": "2026-08-30T09:15:01Z",
      "duration_ms": 1000,
      "summary": {
        "contract_hash": "cc8321d6375c494d043fdd0260f21bc0ec51dacc9f6abb7f909cdcd3041b78bf",
        "contract_version_id": "11111111-1111-4111-8111-000000000001",
        "input_hash": "6444ce79c02f6b8557cd5def4969b6f49e0c371530e24b3d5b5eff8aeb99d700"
      }
    },
    {
      "name": "transform.declarative",
      "position": 2,
      "status": "succeeded",
      "attempt": 1,
      "state_version": 3,
      "started_at": "2026-08-30T09:15:01Z",
      "finished_at": "2026-08-30T09:15:02Z",
      "duration_ms": 1000,
      "summary": {
        "input_hash": "6444ce79c02f6b8557cd5def4969b6f49e0c371530e24b3d5b5eff8aeb99d700",
        "output_hash": "e0ee8bb50685e05fa0f47ed04203ae953fdfd055f5bd2892ea186504254f8c3a",
        "transform_hash": "aa214ea38326805d95661c3ad1643cc07f88e2bae0438ac0448a66d93335ca6e",
        "transform_version_id": "22222222-2222-4222-8222-000000000001"
      }
    },
    {
      "name": "compose",
      "position": 3,
      "status": "succeeded",
      "attempt": 1,
      "state_version": 4,
      "started_at": "2026-08-30T09:15:02Z",
      "finished_at": "2026-08-30T09:15:04Z",
      "duration_ms": 2000,
      "summary": {
        "artifact_id": "77777777-7777-4777-8777-000000000001",
        "kind": "docx",
        "media_type": "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
        "byte_size": 18432,
        "content_hash": "584cb925e6ad45273e46037369c5ec3a5d7cfdd409ce13a69e7087f8accd1c79"
      }
    },
    {
      "name": "render",
      "position": 4,
      "status": "succeeded",
      "attempt": 1,
      "state_version": 5,
      "started_at": "2026-08-30T09:15:04Z",
      "finished_at": "2026-08-30T09:15:07Z",
      "duration_ms": 3000,
      "summary": {
        "artifact_id": "77777777-7777-4777-8777-000000000002",
        "kind": "pdf",
        "media_type": "application/pdf",
        "byte_size": 24576,
        "content_hash": "c35b21d6ca39aa7cc3b79a705d989f1a6e88b99ab43988d74048799e3db926a3"
      }
    }
  ],
  "artifacts": [
    {
      "id": "77777777-7777-4777-8777-000000000001",
      "kind": "docx",
      "media_type": "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
      "byte_size": 18432,
      "content_hash": "584cb925e6ad45273e46037369c5ec3a5d7cfdd409ce13a69e7087f8accd1c79",
      "created_at": "2026-08-30T09:15:04Z"
    },
    {
      "id": "77777777-7777-4777-8777-000000000002",
      "kind": "pdf",
      "media_type": "application/pdf",
      "byte_size": 24576,
      "content_hash": "c35b21d6ca39aa7cc3b79a705d989f1a6e88b99ab43988d74048799e3db926a3",
      "created_at": "2026-08-30T09:15:07Z"
    }
  ]
}
execution-trace/fixture.json · 1795 bytes · sha256 2316e59ae8bab399…
{
  "schema": "docula.example-fixture/v1",
  "slug": "execution-trace",
  "guide": "/developers/guides/execution-trace-and-artifacts",
  "title": "Execution trace and artifacts",
  "summary": "A submitExecution body, the SSE transcript the API streams for a completed document-generation run (execution.submitted, four step.succeeded events, execution.completed), the final getExecution document, and the curl sequence that submits, follows the trace, downloads the PDF artifact and verifies its hash.",
  "operations": ["login", "submitExecution", "streamExecutionEvents", "getExecution", "downloadExecutionArtifact"],
  "related": ["release-immutability"],
  "files": [
    { "path": "submit-execution.request.json", "purpose": "submitExecution body: the release to run and the raw input; release_id is a placeholder replaced by curl.sh.", "validate": { "kind": "openapi", "schema": "#/components/schemas/SubmitExecutionRequest" } },
    { "path": "events.sse", "purpose": "text/event-stream transcript of streamExecutionEvents; every data line is an ExecutionEvent.", "validate": { "kind": "sse" } },
    { "path": "execution.json", "purpose": "getExecution response after completion: steps, hashes and retained artifacts.", "validate": { "kind": "openapi", "schema": "#/components/schemas/Execution", "expect": { "/status": "completed", "/processing_kind": "document_generation" } } },
    { "path": "download-artifact.request.json", "purpose": "downloadExecutionArtifact body: an empty JSON object (the download is a POST because it is audited).", "validate": { "kind": "openapi", "schema": "#/components/schemas/EmptyObject" } },
    { "path": "curl.sh", "purpose": "Submit, stream the trace, read the execution, download and verify the artifact.", "validate": { "kind": "shell" } }
  ]
}
execution-trace/submit-execution.request.json · 166 bytes · sha256 a5db78f3738b7977…
{
  "release_id": "66666666-6666-4666-8666-000000000001",
  "input": {
    "first_name": "Iulia",
    "last_name": "Popescu",
    "email": "[email protected]"
  }
}