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

ExamplesExemple · API 0.8.0

operational-tasks-api

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

operational-tasks-api/admin.curl.sh · 1247 bytes · sha256 ea49b5fbe0a41085…
#!/bin/sh
# Docula example: readiness through the API (the /app/admin page is API-only).
# getReadiness is public and coarse; getDetailedReadiness needs an administrator session and names each dependency.
# Requires curl and jq. Environment: DOCULA_API, DOCULA_ORIGIN (defaults to DOCULA_API), DOCULA_USERNAME, DOCULA_PASSWORD.
set -eu
API=${DOCULA_API:?set DOCULA_API to the API origin, for example http://127.0.0.1:18081}
ORIGIN=${DOCULA_ORIGIN:-$API}
JAR=$(mktemp)
trap 'rm -f "$JAR"' EXIT

# 1. Public readiness (getReadiness): 200 when every dependency is ready, 503 with a Problem body otherwise.
curl --silent --show-error -o /dev/null -w 'health/ready %{http_code}\n' "$API/health/ready"

# 2. Detailed readiness (getDetailedReadiness) after login: PostgreSQL, NATS, S3 and the outbox backlog with durations.
curl --fail --silent --show-error -c "$JAR" -H "Origin: $ORIGIN" -H 'Content-Type: application/json' \
  --data-binary "$(jq -n --arg u "${DOCULA_USERNAME:?}" --arg p "${DOCULA_PASSWORD:?}" '{username: $u, password: $p}')" \
  "$API/api/v1/auth/login" > /dev/null
curl --fail --silent --show-error -b "$JAR" "$API/api/v1/admin/readiness" |
  jq '{status, dependencies: [.dependencies[] | {name, operation, status, duration_ms}]}'
operational-tasks-api/cases.curl.sh · 2103 bytes · sha256 45ea5b8910593c91…
#!/bin/sh
# Docula example: cases through the API (the /app/cases workbench is API-only).
# login -> listCases -> generateCase (deterministic, seeded) -> getCase -> importFixtureCase.
# Requires curl and jq. Environment: DOCULA_API, DOCULA_ORIGIN (defaults to DOCULA_API), DOCULA_USERNAME, DOCULA_PASSWORD,
# DOCULA_CONTRACT_VERSION_ID (a published contract version).
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"' 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')

api() {
  if [ "$#" -ge 3 ]; then
    curl --fail --silent --show-error -b "$JAR" -X "$1" -H "Origin: $ORIGIN" -H "X-CSRF-Token: $CSRF" \
      -H 'Content-Type: application/json' --data-binary @"$3" "$API$2"
  else
    curl --fail --silent --show-error -b "$JAR" -X "$1" -H "Origin: $ORIGIN" -H "X-CSRF-Token: $CSRF" "$API$2"
  fi
}

# 1. List existing cases (listCases).
api GET /api/v1/cases | jq '.cases | length'

# 2. Generate a synthetic valid case for the contract version (generateCase). Same seed + same contract hash = same case.
jq --arg c "${DOCULA_CONTRACT_VERSION_ID:?}" '.contract_version_id = $c' "$HERE/generate-case.request.json" > "$JAR.body"
CASE=$(api POST /api/v1/cases/generate "$JAR.body")
CASE_ID=$(printf '%s' "$CASE" | jq -r '.id')
printf '%s' "$CASE" | jq '{id, kind, seed, provider, content_hash}'

# 3. Read it back (getCase).
api GET "/api/v1/cases/$CASE_ID" | jq '.payload'

# 4. Import a hand-written payload as a fixture case (importFixtureCase). Only valid and boundary kinds can be imported.
jq --arg c "${DOCULA_CONTRACT_VERSION_ID:?}" '.contract_version_id = $c' "$HERE/import-case.request.json" > "$JAR.body"
api POST /api/v1/cases/import "$JAR.body" | jq '{id, kind, provider, generator_version}'
operational-tasks-api/create-template.request.json · 182 bytes · sha256 d018e6aa021e701b…
{
  "slug": "person-welcome-letter",
  "name": "Person welcome letter",
  "description": "Docs example: canonical template family created through the API-only template workbench."
}
operational-tasks-api/fixture.json · 4171 bytes · sha256 e6e966f2efd0e636…
{
  "schema": "docula.example-fixture/v1",
  "slug": "operational-tasks-api",
  "guide": "/developers/guides/operational-tasks-via-api",
  "title": "Operational tasks via the API",
  "summary": "Request bodies and curl sequences for every workbench that stays API-only in docs/OPERATIONAL-UI-SCOPE.md: cases (generate, read, import), transforms (validate, preview, update draft), templates (upload, version, preview), the intake test lab (catalog, run, history, statistics) and administrator readiness. Contract operations are covered by the contract-versioning example.",
  "operations": ["login", "listCases", "generateCase", "getCase", "importFixtureCase", "listTransforms", "getTransform", "validateTransformDraft", "previewTransform", "updateTransformDraft", "listTemplates", "createTemplate", "getTemplate", "createTemplateVersion", "previewTemplateVersion", "getIntakeLabCatalog", "runIntakeLabScenario", "getIntakeLabHistory", "getIntakeLabStatistics", "getReadiness", "getDetailedReadiness"],
  "related": ["contract-versioning", "declarative-transform", "docx-template"],
  "files": [
    { "path": "generate-case.request.json", "purpose": "generateCase body: contract version, kind and deterministic seed.", "validate": { "kind": "openapi", "schema": "#/components/schemas/GenerateCaseRequest" } },
    { "path": "import-case.request.json", "purpose": "importFixtureCase body: a hand-written valid payload for the contract version.", "validate": { "kind": "openapi", "schema": "#/components/schemas/ImportCaseRequest" } },
    { "path": "transform.json", "purpose": "declarative-v1 program embedded by the transform request bodies (same program as the declarative-transform example).", "validate": { "kind": "declarative-v1" } },
    { "path": "validate-transform.request.json", "purpose": "validateTransformDraft body; source embeds transform.json.", "validate": { "kind": "openapi", "schema": "#/components/schemas/ValidateTransformRequest" }, "embeds": [{ "field": "source", "file": "transform.json" }] },
    { "path": "preview-transform.request.json", "purpose": "previewTransform body; source embeds transform.json and names the case to run it against.", "validate": { "kind": "openapi", "schema": "#/components/schemas/PreviewTransformRequest" }, "embeds": [{ "field": "source", "file": "transform.json" }] },
    { "path": "update-transform-draft.request.json", "purpose": "updateTransformDraft body with the expected draft revision; source embeds transform.json.", "validate": { "kind": "openapi", "schema": "#/components/schemas/UpdateTransformRequest" }, "embeds": [{ "field": "source", "file": "transform.json" }] },
    { "path": "create-template.request.json", "purpose": "createTemplate JSON body that creates a canonical template family (the multipart form in templates.curl.sh is the legacy immediate upload).", "validate": { "kind": "openapi", "schema": "#/components/schemas/CanonicalTemplateCreateRequest" } },
    { "path": "preview-template.request.json", "purpose": "previewTemplateVersion body: transform version and case used to resolve every binding.", "validate": { "kind": "openapi", "schema": "#/components/schemas/TemplatePreviewRequest" } },
    { "path": "run-intake-lab.request.json", "purpose": "runIntakeLabScenario body: an empty JSON object.", "validate": { "kind": "openapi", "schema": "#/components/schemas/EmptyObject" } },
    { "path": "cases.curl.sh", "purpose": "listCases, generateCase, getCase, importFixtureCase.", "validate": { "kind": "shell" } },
    { "path": "transforms.curl.sh", "purpose": "listTransforms, validateTransformDraft, previewTransform, updateTransformDraft, getTransform.", "validate": { "kind": "shell" } },
    { "path": "templates.curl.sh", "purpose": "listTemplates, createTemplate (multipart), getTemplate, createTemplateVersion, previewTemplateVersion.", "validate": { "kind": "shell" } },
    { "path": "intake-lab.curl.sh", "purpose": "getIntakeLabCatalog, runIntakeLabScenario, getIntakeLabHistory, getIntakeLabStatistics.", "validate": { "kind": "shell" } },
    { "path": "admin.curl.sh", "purpose": "getReadiness and getDetailedReadiness.", "validate": { "kind": "shell" } }
  ]
}
operational-tasks-api/generate-case.request.json · 101 bytes · sha256 098985f64b9beca6…
{
  "contract_version_id": "11111111-1111-4111-8111-000000000001",
  "kind": "valid",
  "seed": 42
}
operational-tasks-api/import-case.request.json · 196 bytes · sha256 9afb693665cb072b…
{
  "contract_version_id": "11111111-1111-4111-8111-000000000001",
  "kind": "valid",
  "payload": {
    "first_name": "Iulia",
    "last_name": "Popescu",
    "email": "[email protected]"
  }
}
operational-tasks-api/intake-lab.curl.sh · 2166 bytes · sha256 4c3c5cbced5a20a5…
#!/bin/sh
# Docula example: intake test lab through the API (the /app/intake-test-lab page is API-only; /app/demo covers the demo journey).
# login -> getIntakeLabCatalog -> runIntakeLabScenario -> getIntakeLabHistory -> getIntakeLabStatistics.
# Requires curl and jq. Environment: DOCULA_API, DOCULA_ORIGIN (defaults to DOCULA_API), DOCULA_USERNAME, DOCULA_PASSWORD.
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"' 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')

api() {
  if [ "$#" -ge 3 ]; then
    curl --fail --silent --show-error -b "$JAR" -X "$1" -H "Origin: $ORIGIN" -H "X-CSRF-Token: $CSRF" \
      -H 'Content-Type: application/json' --data-binary @"$3" "$API$2"
  else
    curl --fail --silent --show-error -b "$JAR" -X "$1" -H "Origin: $ORIGIN" -H "X-CSRF-Token: $CSRF" "$API$2"
  fi
}

# 1. The fixed scenario catalog (getIntakeLabCatalog): monthly, semiannual, annual and invalid.
api GET /api/v1/intake-test-lab/scenarios | jq '.scenarios[] | {id, expected_outcome, schedule_interval_months}'

# 2. Run one scenario through the shared release (runIntakeLabScenario). The body is an empty JSON object; Idempotency-Key is required.
RUN=$(curl --fail --silent --show-error -b "$JAR" -X POST -H "Origin: $ORIGIN" -H "X-CSRF-Token: $CSRF" \
  -H "Idempotency-Key: docs-intake-lab-$(date +%Y%m%d%H%M%S)" -H 'Content-Type: application/json' \
  --data-binary @"$HERE/run-intake-lab.request.json" "$API/api/v1/intake-test-lab/runs/monthly")
printf '%s' "$RUN" | jq '{execution_id: .execution.id, status: .execution.status, idempotent_replay}'

# 3. Recent runs and aggregate statistics (getIntakeLabHistory, getIntakeLabStatistics).
api GET '/api/v1/intake-test-lab/history?limit=10' | jq '.runs | length'
api GET /api/v1/intake-test-lab/statistics | jq '.'
operational-tasks-api/preview-template.request.json · 122 bytes · sha256 020b4863e18b7086…
{
  "transform_version_id": "22222222-2222-4222-8222-000000000001",
  "case_id": "44444444-4444-4444-8444-000000000001"
}
operational-tasks-api/preview-transform.request.json · 701 bytes · sha256 831cc52d1af8ead6…
{
  "source": "{\n  \"language\": \"declarative-v1\",\n  \"output\": {\n    \"first_name\": { \"$copy\": \"/first_name\" },\n    \"middle_name\": { \"$copy_optional\": \"/middle_name\" },\n    \"display_name\": {\n      \"$concat\": [\n        { \"$copy\": \"/first_name\" },\n        { \"$literal\": \" \" },\n        { \"$copy\": \"/last_name\" }\n      ]\n    },\n    \"preferred_contact\": {\n      \"$coalesce\": [\n        { \"$copy_optional\": \"/phone\" },\n        { \"$copy_optional\": \"/email\" },\n        { \"$literal\": \"none\" }\n      ]\n    },\n    \"source\": { \"$literal\": \"docs-example\" }\n  }\n}\n",
  "format": "json",
  "case_id": "44444444-4444-4444-8444-000000000001"
}
operational-tasks-api/run-intake-lab.request.json · 3 bytes · sha256 ca3d163bab055381…
{}
operational-tasks-api/templates.curl.sh · 3438 bytes · sha256 bf74d43a8153371c…
#!/bin/sh
# Docula example: templates through the API (the /app/templates workbench is API-only).
# login -> listTemplates -> createTemplate (multipart upload of a .docx) -> getTemplate -> createTemplateVersion -> previewTemplateVersion.
# Requires curl and jq. Environment: DOCULA_API, DOCULA_ORIGIN (defaults to DOCULA_API), DOCULA_USERNAME, DOCULA_PASSWORD,
# DOCULA_TRANSFORM_VERSION_ID (published) and DOCULA_CASE_ID (valid or boundary case) for the preview.
# The .docx comes from the docx-template example next to this directory.
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"' 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')

api() {
  if [ "$#" -ge 3 ]; then
    curl --fail --silent --show-error -b "$JAR" -X "$1" -H "Origin: $ORIGIN" -H "X-CSRF-Token: $CSRF" \
      -H 'Content-Type: application/json' --data-binary @"$3" "$API$2"
  else
    curl --fail --silent --show-error -b "$JAR" -X "$1" -H "Origin: $ORIGIN" -H "X-CSRF-Token: $CSRF" "$API$2"
  fi
}
DOCX=${DOCULA_TEMPLATE_DOCX:-$(dirname "$HERE")/docx-template/template.docx}

# 1. Page through the registry (listTemplates).
api GET '/api/v1/templates?limit=20' | jq '.templates[] | {id, slug, name}'

# 2. Upload a template (createTemplate, multipart/form-data). The API validates the archive, extracts {{/pointer}} bindings
#    and stores version 1 immediately. Alternatively POST create-template.request.json as JSON to create an empty canonical family.
TEMPLATE=$(curl --fail --silent --show-error -b "$JAR" -X POST -H "Origin: $ORIGIN" -H "X-CSRF-Token: $CSRF" \
  -F 'name=Person welcome letter' -F 'description=Docs example: uploaded through the API-only template workbench.' \
  -F "file=@$DOCX;type=application/vnd.openxmlformats-officedocument.wordprocessingml.document" "$API/api/v1/templates")
TEMPLATE_ID=$(printf '%s' "$TEMPLATE" | jq -r '.template.id')
printf '%s' "$TEMPLATE" | jq '.template.versions[] | {number, byte_size, content_hash, bindings}'

# 3. Read the family (getTemplate).
api GET "/api/v1/templates/$TEMPLATE_ID" | jq '{id, name, versions: (.versions | length)}'

# 4. Add a version from a revised file (createTemplateVersion, multipart/form-data). Identical bytes are rejected as a duplicate.
VERSION=$(curl --fail --silent --show-error -b "$JAR" -X POST -H "Origin: $ORIGIN" -H "X-CSRF-Token: $CSRF" \
  -F "file=@${DOCULA_TEMPLATE_DOCX_V2:-$DOCX};type=application/vnd.openxmlformats-officedocument.wordprocessingml.document" \
  "$API/api/v1/templates/$TEMPLATE_ID/versions" || true)
VERSION_NUMBER=$(printf '%s' "$VERSION" | jq -r '.version.number // 1')

# 5. Resolve every binding against a transform version and a case (previewTemplateVersion). Missing or null bindings fail here,
#    before any release is published.
jq --arg t "${DOCULA_TRANSFORM_VERSION_ID:?}" --arg k "${DOCULA_CASE_ID:?}" '.transform_version_id = $t | .case_id = $k' \
  "$HERE/preview-template.request.json" > "$JAR.body"
api POST "/api/v1/templates/$TEMPLATE_ID/versions/$VERSION_NUMBER/preview" "$JAR.body" | jq '{bindings, bindings_hash, preview_hash}'
operational-tasks-api/transform.json · 531 bytes · sha256 aa969a3ce067413d…
{
  "language": "declarative-v1",
  "output": {
    "first_name": { "$copy": "/first_name" },
    "middle_name": { "$copy_optional": "/middle_name" },
    "display_name": {
      "$concat": [
        { "$copy": "/first_name" },
        { "$literal": " " },
        { "$copy": "/last_name" }
      ]
    },
    "preferred_contact": {
      "$coalesce": [
        { "$copy_optional": "/phone" },
        { "$copy_optional": "/email" },
        { "$literal": "none" }
      ]
    },
    "source": { "$literal": "docs-example" }
  }
}
operational-tasks-api/transforms.curl.sh · 2644 bytes · sha256 6970c8a13d41b53e…
#!/bin/sh
# Docula example: transforms through the API (the /app/transforms workbench is API-only).
# login -> listTransforms -> validateTransformDraft -> previewTransform against a case -> updateTransformDraft -> getTransform.
# Requires curl and jq. Environment: DOCULA_API, DOCULA_ORIGIN (defaults to DOCULA_API), DOCULA_USERNAME, DOCULA_PASSWORD,
# DOCULA_TRANSFORM_ID (an existing transform) and DOCULA_CASE_ID (a valid or boundary case to preview against).
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"' 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')

api() {
  if [ "$#" -ge 3 ]; then
    curl --fail --silent --show-error -b "$JAR" -X "$1" -H "Origin: $ORIGIN" -H "X-CSRF-Token: $CSRF" \
      -H 'Content-Type: application/json' --data-binary @"$3" "$API$2"
  else
    curl --fail --silent --show-error -b "$JAR" -X "$1" -H "Origin: $ORIGIN" -H "X-CSRF-Token: $CSRF" "$API$2"
  fi
}

# 1. Page through the registry (listTransforms).
api GET '/api/v1/transforms?limit=20' | jq '.transforms[] | {id, name, draft_revision}'

# 2. Validate a program without saving it (validateTransformDraft): syntax, operators and limits.
api POST "/api/v1/transforms/${DOCULA_TRANSFORM_ID:?}/validate" "$HERE/validate-transform.request.json" | jq '.valid'

# 3. Preview the program against a stored case (previewTransform): returns the canonical output and its hash.
jq --arg k "${DOCULA_CASE_ID:?}" '.case_id = $k' "$HERE/preview-transform.request.json" > "$JAR.body"
api POST "/api/v1/transforms/$DOCULA_TRANSFORM_ID/preview" "$JAR.body" | jq '{output, output_hash}'

# 4. Save the draft (updateTransformDraft) with the live draft revision; a stale revision is rejected with 412.
REVISION=$(api GET "/api/v1/transforms/$DOCULA_TRANSFORM_ID" | jq -r '.draft_revision')
jq --argjson rev "$REVISION" '.expected_revision = $rev' "$HERE/update-transform-draft.request.json" > "$JAR.body"
api PUT "/api/v1/transforms/$DOCULA_TRANSFORM_ID" "$JAR.body" | jq '{id: .transform.id, draft_revision: .transform.draft_revision}'

# 5. Freeze and publish as in the declarative-transform example (createTransformVersion, publishTransformVersion).
api GET "/api/v1/transforms/$DOCULA_TRANSFORM_ID" | jq '{draft_revision, versions: [.versions[] | {number, status}]}'
operational-tasks-api/update-transform-draft.request.json · 803 bytes · sha256 69e0c65162db1efe…
{
  "name": "Person welcome projection",
  "description": "Docs example: draft revised through the API-only transform workbench.",
  "source": "{\n  \"language\": \"declarative-v1\",\n  \"output\": {\n    \"first_name\": { \"$copy\": \"/first_name\" },\n    \"middle_name\": { \"$copy_optional\": \"/middle_name\" },\n    \"display_name\": {\n      \"$concat\": [\n        { \"$copy\": \"/first_name\" },\n        { \"$literal\": \" \" },\n        { \"$copy\": \"/last_name\" }\n      ]\n    },\n    \"preferred_contact\": {\n      \"$coalesce\": [\n        { \"$copy_optional\": \"/phone\" },\n        { \"$copy_optional\": \"/email\" },\n        { \"$literal\": \"none\" }\n      ]\n    },\n    \"source\": { \"$literal\": \"docs-example\" }\n  }\n}\n",
  "format": "json",
  "expected_revision": 1
}
operational-tasks-api/validate-transform.request.json · 648 bytes · sha256 a4d85129344da55d…
{
  "source": "{\n  \"language\": \"declarative-v1\",\n  \"output\": {\n    \"first_name\": { \"$copy\": \"/first_name\" },\n    \"middle_name\": { \"$copy_optional\": \"/middle_name\" },\n    \"display_name\": {\n      \"$concat\": [\n        { \"$copy\": \"/first_name\" },\n        { \"$literal\": \" \" },\n        { \"$copy\": \"/last_name\" }\n      ]\n    },\n    \"preferred_contact\": {\n      \"$coalesce\": [\n        { \"$copy_optional\": \"/phone\" },\n        { \"$copy_optional\": \"/email\" },\n        { \"$literal\": \"none\" }\n      ]\n    },\n    \"source\": { \"$literal\": \"docs-example\" }\n  }\n}\n",
  "format": "json"
}