dirigent-examples 0.15.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- dirigent_examples/__init__.py +22 -0
- dirigent_examples/py.typed +0 -0
- dirigent_examples/shelves/README.md +299 -0
- dirigent_examples/shelves/composition/README.md +18 -0
- dirigent_examples/shelves/composition/chained-instances.yaml +97 -0
- dirigent_examples/shelves/composition/composition-child.yaml +64 -0
- dirigent_examples/shelves/composition/composition-parent.yaml +89 -0
- dirigent_examples/shelves/connections.yaml +52 -0
- dirigent_examples/shelves/demo/README.md +19 -0
- dirigent_examples/shelves/demo/markdown-showcase.yaml +117 -0
- dirigent_examples/shelves/demo/params-showcase.yaml +92 -0
- dirigent_examples/shelves/demo/requires.yaml +65 -0
- dirigent_examples/shelves/demo/weekly-import-malawi.yaml +54 -0
- dirigent_examples/shelves/demo/weekly-import-nepal.yaml +75 -0
- dirigent_examples/shelves/docker/README.md +29 -0
- dirigent_examples/shelves/docker/docker-build-push.yaml +110 -0
- dirigent_examples/shelves/docker/docker-build-run.yaml +106 -0
- dirigent_examples/shelves/docker/docker-compose-database.yaml +124 -0
- dirigent_examples/shelves/docker/docker-compose-failing-up.yaml +83 -0
- dirigent_examples/shelves/docker/docker-compose-file.yaml +117 -0
- dirigent_examples/shelves/docker/docker-compose-profiles-env.yaml +133 -0
- dirigent_examples/shelves/docker/docker-compose-stack.yaml +70 -0
- dirigent_examples/shelves/docker/docker-hello.yaml +53 -0
- dirigent_examples/shelves/docker/docker-remote-daemon.yaml +92 -0
- dirigent_examples/shelves/docker/docker-run-failing-teardown.yaml +94 -0
- dirigent_examples/shelves/docker/docker-ticker.yaml +49 -0
- dirigent_examples/shelves/execute/README.md +16 -0
- dirigent_examples/shelves/execute/long-log.yaml +89 -0
- dirigent_examples/shelves/failure/README.md +20 -0
- dirigent_examples/shelves/failure/error-handler.yaml +89 -0
- dirigent_examples/shelves/failure/optional-step.yaml +82 -0
- dirigent_examples/shelves/failure/retries.yaml +75 -0
- dirigent_examples/shelves/failure/retry-budget.yaml +82 -0
- dirigent_examples/shelves/failure/step-timeout.yaml +96 -0
- dirigent_examples/shelves/git/README.md +32 -0
- dirigent_examples/shelves/git/git-checkout-build.yaml +125 -0
- dirigent_examples/shelves/git/git-checkout-compose.yaml +138 -0
- dirigent_examples/shelves/git/git-checkout-public.yaml +84 -0
- dirigent_examples/shelves/graph/README.md +22 -0
- dirigent_examples/shelves/graph/deep-chain.yaml +119 -0
- dirigent_examples/shelves/graph/fan-in.yaml +80 -0
- dirigent_examples/shelves/graph/fan-out.yaml +66 -0
- dirigent_examples/shelves/graph/linear.yaml +66 -0
- dirigent_examples/shelves/graph/parallel-branches.yaml +57 -0
- dirigent_examples/shelves/graph/parallel-sleep.yaml +55 -0
- dirigent_examples/shelves/graph/skip-diamond.yaml +105 -0
- dirigent_examples/shelves/graph/wide-fan.yaml +147 -0
- dirigent_examples/shelves/hello-world.yaml +30 -0
- dirigent_examples/shelves/open-data/README.md +67 -0
- dirigent_examples/shelves/open-data/feeds-composition.yaml +120 -0
- dirigent_examples/shelves/open-data/gdacs-disaster-updates.yaml +230 -0
- dirigent_examples/shelves/open-data/github-releases-relay.yaml +225 -0
- dirigent_examples/shelves/open-data/hdx-dataset-watch.yaml +211 -0
- dirigent_examples/shelves/open-data/kobo-submissions-to-csv.yaml +149 -0
- dirigent_examples/shelves/open-data/nominatim-geocode-facilities.yaml +169 -0
- dirigent_examples/shelves/open-data/odk-central-submissions.yaml +146 -0
- dirigent_examples/shelves/open-data/open-meteo-weekly-report.yaml +142 -0
- dirigent_examples/shelves/open-data/overpass-health-facilities.yaml +154 -0
- dirigent_examples/shelves/open-data/usgs-earthquakes-alert.yaml +208 -0
- dirigent_examples/shelves/open-data/who-gho-indicators-to-parquet.yaml +163 -0
- dirigent_examples/shelves/open-data/wikidata-country-reference.yaml +146 -0
- dirigent_examples/shelves/open-data/world-bank-population-trend.yaml +166 -0
- dirigent_examples/shelves/patterns/README.md +144 -0
- dirigent_examples/shelves/patterns/concurrency-queue.yaml +89 -0
- dirigent_examples/shelves/patterns/concurrency-replace.yaml +92 -0
- dirigent_examples/shelves/patterns/concurrency-skip.yaml +97 -0
- dirigent_examples/shelves/patterns/connections-referenced-vs-carried.yaml +140 -0
- dirigent_examples/shelves/patterns/deadline-on-a-sensor.yaml +106 -0
- dirigent_examples/shelves/patterns/fan-out-continue.yaml +88 -0
- dirigent_examples/shelves/patterns/fan-out-fail-fast.yaml +80 -0
- dirigent_examples/shelves/patterns/fan-out-from-params.yaml +84 -0
- dirigent_examples/shelves/patterns/fan-out-item-wise.yaml +111 -0
- dirigent_examples/shelves/patterns/fan-out-literal-list.yaml +81 -0
- dirigent_examples/shelves/patterns/fan-out-nested-objects.yaml +107 -0
- dirigent_examples/shelves/patterns/fan-out-then-join.yaml +86 -0
- dirigent_examples/shelves/patterns/log-levels.yaml +119 -0
- dirigent_examples/shelves/patterns/outputs-inline-vs-storage.yaml +140 -0
- dirigent_examples/shelves/patterns/params-every-type.yaml +259 -0
- dirigent_examples/shelves/patterns/params-validation-refuses.yaml +131 -0
- dirigent_examples/shelves/patterns/pipeline-run-child.yaml +96 -0
- dirigent_examples/shelves/patterns/pipeline-run-fire-and-forget.yaml +99 -0
- dirigent_examples/shelves/patterns/pipeline-run-strict.yaml +103 -0
- dirigent_examples/shelves/patterns/pipeline-run-wait.yaml +107 -0
- dirigent_examples/shelves/patterns/pipeline-run-with-params.yaml +124 -0
- dirigent_examples/shelves/patterns/poll-cadence.yaml +102 -0
- dirigent_examples/shelves/patterns/priority-layered.yaml +120 -0
- dirigent_examples/shelves/patterns/references-cheat-sheet.yaml +186 -0
- dirigent_examples/shelves/patterns/retry-budget-exhausted.yaml +92 -0
- dirigent_examples/shelves/patterns/retry-exponential-backoff.yaml +88 -0
- dirigent_examples/shelves/patterns/retry-only-transient.yaml +108 -0
- dirigent_examples/shelves/patterns/retry-with-jitter.yaml +102 -0
- dirigent_examples/shelves/patterns/rule-all-done.yaml +83 -0
- dirigent_examples/shelves/patterns/rule-all-success.yaml +79 -0
- dirigent_examples/shelves/patterns/rule-always.yaml +92 -0
- dirigent_examples/shelves/patterns/rule-one-failed.yaml +86 -0
- dirigent_examples/shelves/patterns/schedule-at-once.yaml +110 -0
- dirigent_examples/shelves/patterns/schedule-cron-timezone.yaml +121 -0
- dirigent_examples/shelves/patterns/schedule-interval.yaml +109 -0
- dirigent_examples/shelves/patterns/schedule-window-half-open.yaml +105 -0
- dirigent_examples/shelves/patterns/sensor-http-ready.yaml +121 -0
- dirigent_examples/shelves/patterns/sensor-storage-exists.yaml +134 -0
- dirigent_examples/shelves/patterns/step-names-and-keys.yaml +99 -0
- dirigent_examples/shelves/patterns/timeout-fails-the-step.yaml +94 -0
- dirigent_examples/shelves/patterns/timeout-skips-the-step.yaml +102 -0
- dirigent_examples/shelves/patterns/webhook-mapping-nested-payload.yaml +125 -0
- dirigent_examples/shelves/patterns/webhook-signed.yaml +144 -0
- dirigent_examples/shelves/preview/s3-parquet-to-ingestion.yaml +92 -0
- dirigent_examples/shelves/python/README.md +31 -0
- dirigent_examples/shelves/python/apply_and_run.py +52 -0
- dirigent_examples/shelves/python/ci_gate.py +76 -0
- dirigent_examples/shelves/python/connections.py +61 -0
- dirigent_examples/shelves/python/error_handling.py +84 -0
- dirigent_examples/shelves/python/follow_logs.py +39 -0
- dirigent_examples/shelves/python/list_and_filter.py +52 -0
- dirigent_examples/shelves/queues/README.md +59 -0
- dirigent_examples/shelves/queues/kafka-consume-then-transform.yaml +105 -0
- dirigent_examples/shelves/queues/kafka-produce-then-consume.yaml +124 -0
- dirigent_examples/shelves/queues/rabbitmq-consume-ack-on-success.yaml +102 -0
- dirigent_examples/shelves/queues/report-to-kafka.yaml +105 -0
- dirigent_examples/shelves/queues/report-to-rabbitmq.yaml +105 -0
- dirigent_examples/shelves/recipes/README.md +130 -0
- dirigent_examples/shelves/recipes/csv-header-rules.yaml +147 -0
- dirigent_examples/shelves/recipes/csv-to-ndjson.yaml +107 -0
- dirigent_examples/shelves/recipes/etl-csv-clean-validate-parquet.yaml +207 -0
- dirigent_examples/shelves/recipes/filter-by-predicate.yaml +116 -0
- dirigent_examples/shelves/recipes/filter-then-map-then-reduce.yaml +109 -0
- dirigent_examples/shelves/recipes/http-fetch-validate-post.yaml +142 -0
- dirigent_examples/shelves/recipes/http-follow-redirects.yaml +100 -0
- dirigent_examples/shelves/recipes/http-get-with-query.yaml +96 -0
- dirigent_examples/shelves/recipes/http-headers-and-auth-connection.yaml +110 -0
- dirigent_examples/shelves/recipes/http-post-file-from-storage.yaml +117 -0
- dirigent_examples/shelves/recipes/http-post-json-echo.yaml +105 -0
- dirigent_examples/shelves/recipes/http-post-report.yaml +183 -0
- dirigent_examples/shelves/recipes/http-save-body-to-storage.yaml +106 -0
- dirigent_examples/shelves/recipes/http-success-status-list.yaml +80 -0
- dirigent_examples/shelves/recipes/http-timeout-override.yaml +104 -0
- dirigent_examples/shelves/recipes/jq-dedupe-by-key.yaml +78 -0
- dirigent_examples/shelves/recipes/jq-defaults-and-nulls.yaml +91 -0
- dirigent_examples/shelves/recipes/jq-group-by-and-sum.yaml +74 -0
- dirigent_examples/shelves/recipes/jq-join-two-lists.yaml +77 -0
- dirigent_examples/shelves/recipes/jq-long-to-wide.yaml +76 -0
- dirigent_examples/shelves/recipes/jq-nested-to-flat.yaml +89 -0
- dirigent_examples/shelves/recipes/jq-pivot-wide-to-long.yaml +65 -0
- dirigent_examples/shelves/recipes/jq-running-totals.yaml +82 -0
- dirigent_examples/shelves/recipes/jq-string-cleaning.yaml +88 -0
- dirigent_examples/shelves/recipes/jq-top-n.yaml +85 -0
- dirigent_examples/shelves/recipes/jq-validate-in-jq-vs-schema.yaml +124 -0
- dirigent_examples/shelves/recipes/jq-window-dates.yaml +82 -0
- dirigent_examples/shelves/recipes/json-to-csv-flattening.yaml +141 -0
- dirigent_examples/shelves/recipes/large-output-to-storage.yaml +134 -0
- dirigent_examples/shelves/recipes/map-enrich-with-lookup.yaml +96 -0
- dirigent_examples/shelves/recipes/ndjson-to-parquet.yaml +130 -0
- dirigent_examples/shelves/recipes/pagination-by-fan-out.yaml +115 -0
- dirigent_examples/shelves/recipes/parquet-round-trip-types.yaml +163 -0
- dirigent_examples/shelves/recipes/reconcile-two-sources.yaml +159 -0
- dirigent_examples/shelves/recipes/report-built-in.yaml +72 -0
- dirigent_examples/shelves/recipes/report-daily-digest.yaml +186 -0
- dirigent_examples/shelves/recipes/report-to-file.yaml +131 -0
- dirigent_examples/shelves/recipes/report-to-webhook.yaml +136 -0
- dirigent_examples/shelves/recipes/schema-carried.yaml +112 -0
- dirigent_examples/shelves/recipes/schema-formats.yaml +107 -0
- dirigent_examples/shelves/recipes/schema-referenced.yaml +86 -0
- dirigent_examples/shelves/recipes/schema-refuses-then-rule.yaml +127 -0
- dirigent_examples/shelves/recipes/storage-copy-dated-archive.yaml +114 -0
- dirigent_examples/shelves/recipes/storage-exists-gate.yaml +127 -0
- dirigent_examples/shelves/recipes/storage-manifest-of-a-fan-out.yaml +104 -0
- dirigent_examples/shelves/recipes/storage-write-then-read.yaml +119 -0
- dirigent_examples/shelves/recipes/webhook-post-hmac.yaml +132 -0
- dirigent_examples/shelves/recipes/webhook-post-summary.yaml +142 -0
- dirigent_examples/shelves/s3/README.md +34 -0
- dirigent_examples/shelves/s3/report-to-s3.yaml +93 -0
- dirigent_examples/shelves/s3/s3-copy-and-verify.yaml +105 -0
- dirigent_examples/shelves/s3/s3-csv-report.yaml +87 -0
- dirigent_examples/shelves/s3/s3-parquet-report.yaml +77 -0
- dirigent_examples/shelves/s3/s3-round-trip.yaml +125 -0
- dirigent_examples/shelves/schemas/README.md +36 -0
- dirigent_examples/shelves/schemas/echo-reading.json +18 -0
- dirigent_examples/shelves/schemas/ou-record.json +13 -0
- dirigent_examples/shelves/schemas/station-reading.json +13 -0
- dirigent_examples/shelves/sensors/README.md +16 -0
- dirigent_examples/shelves/sensors/sensor-gate.yaml +65 -0
- dirigent_examples/shelves/sensors/time-window.yaml +61 -0
- dirigent_examples/shelves/sql/README.md +52 -0
- dirigent_examples/shelves/sql/duckdb-parquet-to-report.yaml +146 -0
- dirigent_examples/shelves/sql/sql-postgres-readonly.yaml +111 -0
- dirigent_examples/shelves/sql/sql-query-to-storage.yaml +85 -0
- dirigent_examples/shelves/sql/sql-sqlite-roundtrip.yaml +114 -0
- dirigent_examples/shelves/sql/warehouse.sql +42 -0
- dirigent_examples/shelves/transform/README.md +36 -0
- dirigent_examples/shelves/transform/csv-report.yaml +55 -0
- dirigent_examples/shelves/transform/jq-filter-and-map.yaml +70 -0
- dirigent_examples/shelves/transform/jq-group-and-aggregate.yaml +70 -0
- dirigent_examples/shelves/transform/jq-join-two-sources.yaml +98 -0
- dirigent_examples/shelves/transform/jq-reshape.yaml +91 -0
- dirigent_examples/shelves/transform/jq-stream-through-storage.yaml +112 -0
- dirigent_examples/shelves/transform/ndjson-round-trip.yaml +56 -0
- dirigent_examples/shelves/transform/parquet-round-trip.yaml +68 -0
- dirigent_examples/shelves/transform/std-convert-fan-out.yaml +142 -0
- dirigent_examples/shelves/transform/xml-feed-to-ndjson.yaml +116 -0
- dirigent_examples/shelves/transform/yaml-config-to-json.yaml +104 -0
- dirigent_examples/shelves/triggers/README.md +45 -0
- dirigent_examples/shelves/triggers/at-one-time.yaml +78 -0
- dirigent_examples/shelves/triggers/cron-nightly.yaml +79 -0
- dirigent_examples/shelves/triggers/cron-windowed.yaml +86 -0
- dirigent_examples/shelves/triggers/document-nightly.yaml +80 -0
- dirigent_examples/shelves/triggers/interval-rolling.yaml +88 -0
- dirigent_examples/shelves/triggers/managed-and-manual.yaml +109 -0
- dirigent_examples/shelves/triggers/webhook-trigger.yaml +75 -0
- dirigent_examples/shelves/validate/README.md +31 -0
- dirigent_examples/shelves/validate/expects-a-shape.yaml +56 -0
- dirigent_examples/shelves/validate/the-shape-is-wrong.yaml +46 -0
- dirigent_examples-0.15.0.dist-info/METADATA +21 -0
- dirigent_examples-0.15.0.dist-info/RECORD +216 -0
- dirigent_examples-0.15.0.dist-info/WHEEL +4 -0
- dirigent_examples-0.15.0.dist-info/entry_points.txt +3 -0
- dirigent_examples-0.15.0.dist-info/licenses/LICENSE +18 -0
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
# POST a file that lives in storage: read it, then send what the read brought back.
|
|
2
|
+
#
|
|
3
|
+
# In: a table of readings generated inline and written to the run's scratch space.
|
|
4
|
+
# Out: the echo service's view of the upload -- the size it received and the content type it
|
|
5
|
+
# was told -- beside the size the file actually is.
|
|
6
|
+
#
|
|
7
|
+
# A file is bytes at a URI, and a request sends a value, so the hop between them is a
|
|
8
|
+
# storage.read step. Four hops:
|
|
9
|
+
#
|
|
10
|
+
# build transform.jq, the payload a real pipeline would have fetched.
|
|
11
|
+
# file storage.write, the value to a URI. Output: uri, bytes_written, content_type.
|
|
12
|
+
# load storage.read, the same URI. The object is application/json, so it comes back
|
|
13
|
+
# as `value`; max_size is how much of it this step will hold, and an object
|
|
14
|
+
# past it is refused rather than truncated.
|
|
15
|
+
# upload http.request. body is that value, serialised and sent as JSON by the block, so
|
|
16
|
+
# nothing here builds a request out of strings.
|
|
17
|
+
#
|
|
18
|
+
# HOW BIG A FILE THIS SENDS. Everything above passes through the worker, so the file has to
|
|
19
|
+
# fit in it twice over: once in the read, once in the request. That is the trade for being
|
|
20
|
+
# able to look at what is sent. Bytes nobody has to look at move between URIs with
|
|
21
|
+
# storage.copy instead, bounded by the storage rather than by the worker, and a body coming
|
|
22
|
+
# back the other way is bounded by max_response.
|
|
23
|
+
#
|
|
24
|
+
# The file is addressed as ${steps.file.output.uri} rather than typed twice, so one step
|
|
25
|
+
# decides where it went and a rename is a one-line change. A missing object is a rejected
|
|
26
|
+
# read, not a retried one: calling again cannot make a file appear that was never written.
|
|
27
|
+
#
|
|
28
|
+
# To change it: point the write at s3://<bucket>/... and the read follows it, because the
|
|
29
|
+
# scheme is the only thing that changes.
|
|
30
|
+
#
|
|
31
|
+
# dg run --local examples/recipes/http-post-file-from-storage.yaml
|
|
32
|
+
# dg run --local examples/recipes/http-post-file-from-storage.yaml -p stations=500
|
|
33
|
+
|
|
34
|
+
format: dirigent/v1
|
|
35
|
+
kind: pipeline
|
|
36
|
+
code: http-post-file-from-storage
|
|
37
|
+
name: POST a file read out of storage
|
|
38
|
+
description: Write a file into the run's scratch space, read it back with storage.read, and POST what the read brought back.
|
|
39
|
+
|
|
40
|
+
tags: [recipes, http, storage, transform]
|
|
41
|
+
|
|
42
|
+
requires:
|
|
43
|
+
blocks:
|
|
44
|
+
- transform.jq
|
|
45
|
+
- storage.write
|
|
46
|
+
- storage.read
|
|
47
|
+
- http.request
|
|
48
|
+
|
|
49
|
+
params:
|
|
50
|
+
type: object
|
|
51
|
+
properties:
|
|
52
|
+
day:
|
|
53
|
+
type: string
|
|
54
|
+
format: date
|
|
55
|
+
default: "2026-01-01"
|
|
56
|
+
description: The day the uploaded file is named for.
|
|
57
|
+
stations:
|
|
58
|
+
type: integer
|
|
59
|
+
minimum: 1
|
|
60
|
+
default: 40
|
|
61
|
+
description: How many rows the file holds, which is what makes it big enough to matter.
|
|
62
|
+
|
|
63
|
+
steps:
|
|
64
|
+
build:
|
|
65
|
+
block: transform.jq
|
|
66
|
+
config:
|
|
67
|
+
input:
|
|
68
|
+
count: ${params.stations}
|
|
69
|
+
day: ${params.day}
|
|
70
|
+
program: |
|
|
71
|
+
. as {$count, $day}
|
|
72
|
+
| {day: $day,
|
|
73
|
+
readings: [range($count)
|
|
74
|
+
| {station: "st-\(.)", celsius: (. % 17) - 5}]}
|
|
75
|
+
|
|
76
|
+
file:
|
|
77
|
+
block: storage.write
|
|
78
|
+
depends_on: [build]
|
|
79
|
+
config:
|
|
80
|
+
target: ${run.scratch}/readings-${params.day}.json
|
|
81
|
+
value: ${steps.build.output.value}
|
|
82
|
+
|
|
83
|
+
load:
|
|
84
|
+
block: storage.read
|
|
85
|
+
depends_on: [file]
|
|
86
|
+
config:
|
|
87
|
+
source: ${steps.file.output.uri}
|
|
88
|
+
max_size: 8mb
|
|
89
|
+
|
|
90
|
+
upload:
|
|
91
|
+
block: http.request
|
|
92
|
+
depends_on: [load]
|
|
93
|
+
config:
|
|
94
|
+
url: https://postman-echo.com/post
|
|
95
|
+
method: POST
|
|
96
|
+
# A value, so the block serialises it and sends it as application/json. An endpoint
|
|
97
|
+
# that wants some other type is told with content_type.
|
|
98
|
+
body: ${steps.load.output.value}
|
|
99
|
+
timeout: 30s
|
|
100
|
+
|
|
101
|
+
receipt:
|
|
102
|
+
block: transform.jq
|
|
103
|
+
depends_on: [file, load, upload]
|
|
104
|
+
config:
|
|
105
|
+
input:
|
|
106
|
+
written: ${steps.file.output.bytes_written}
|
|
107
|
+
read: ${steps.load.output.bytes_read}
|
|
108
|
+
echoed: ${steps.upload.output.body}
|
|
109
|
+
program: |
|
|
110
|
+
{written, read,
|
|
111
|
+
# What the receiver was told the body was, and how much of it arrived.
|
|
112
|
+
content_type: .echoed.headers["content-type"],
|
|
113
|
+
received: (.echoed.headers["content-length"] | tonumber),
|
|
114
|
+
rows: (.echoed.json.readings | length),
|
|
115
|
+
# The file and the read agree on one number, and the receiver counted the same
|
|
116
|
+
# bytes back out of the request.
|
|
117
|
+
intact: (.written == .read and .read == (.echoed.headers["content-length"] | tonumber))}
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
# POST a JSON body built from upstream work, and read the receipt.
|
|
2
|
+
#
|
|
3
|
+
# In: a batch of readings built inline.
|
|
4
|
+
# Out: the echo service's view of the POST -- the body it parsed and the headers it saw --
|
|
5
|
+
# alongside the status and the payload's own row count.
|
|
6
|
+
#
|
|
7
|
+
# body is a value, not a string. The block serialises it, sets the content type, and sends
|
|
8
|
+
# it, so a payload assembled by a jq step upstream travels as structure the whole way and
|
|
9
|
+
# nothing hand-builds JSON with string interpolation. That is what keeps a quote in a
|
|
10
|
+
# station name from becoming a syntax error on somebody else's parser.
|
|
11
|
+
#
|
|
12
|
+
# https://postman-echo.com/post answers with what it received, so the .json field of the
|
|
13
|
+
# response is this pipeline's own body coming back -- which is why the last step can compare
|
|
14
|
+
# what was sent with what arrived and report them side by side.
|
|
15
|
+
#
|
|
16
|
+
# Two headers are worth knowing about. The block sets content-type itself, so a header map
|
|
17
|
+
# is for what the receiver wants on top: a routing key, an idempotency key, an API version.
|
|
18
|
+
# A secret does not go here -- it goes on a connection, which is the whole point of
|
|
19
|
+
# http-headers-and-auth-connection.yaml.
|
|
20
|
+
#
|
|
21
|
+
# A POST is not idempotent, and the block declares itself so: the engine will spend a retry
|
|
22
|
+
# budget on one, and it is on the author to know whether the receiver deduplicates. An
|
|
23
|
+
# idempotency key in the headers is what makes that safe, and it belongs in the payload's
|
|
24
|
+
# own identity rather than in a random value minted per attempt.
|
|
25
|
+
#
|
|
26
|
+
# To change it: -p batch=3 sends a bigger payload; the shape of the document does not move.
|
|
27
|
+
#
|
|
28
|
+
# dg run --local examples/recipes/http-post-json-echo.yaml
|
|
29
|
+
# dg run --local examples/recipes/http-post-json-echo.yaml -p batch=3
|
|
30
|
+
|
|
31
|
+
format: dirigent/v1
|
|
32
|
+
kind: pipeline
|
|
33
|
+
code: http-post-json-echo
|
|
34
|
+
name: POST a JSON body
|
|
35
|
+
description: Build a payload upstream, POST it as structure rather than as text, and read the receipt the endpoint answers with.
|
|
36
|
+
|
|
37
|
+
tags: [recipes, http, transform]
|
|
38
|
+
|
|
39
|
+
requires:
|
|
40
|
+
blocks:
|
|
41
|
+
- transform.jq
|
|
42
|
+
- http.request
|
|
43
|
+
|
|
44
|
+
params:
|
|
45
|
+
type: object
|
|
46
|
+
properties:
|
|
47
|
+
batch:
|
|
48
|
+
type: integer
|
|
49
|
+
minimum: 1
|
|
50
|
+
default: 5
|
|
51
|
+
description: How many readings the payload carries.
|
|
52
|
+
day:
|
|
53
|
+
type: string
|
|
54
|
+
format: date
|
|
55
|
+
default: "2026-01-01"
|
|
56
|
+
description: The day the payload reports.
|
|
57
|
+
|
|
58
|
+
steps:
|
|
59
|
+
payload:
|
|
60
|
+
block: transform.jq
|
|
61
|
+
config:
|
|
62
|
+
input:
|
|
63
|
+
batch: ${params.batch}
|
|
64
|
+
day: ${params.day}
|
|
65
|
+
program: |
|
|
66
|
+
. as {$batch, $day}
|
|
67
|
+
| {day: $day,
|
|
68
|
+
source: "dirigent recipes",
|
|
69
|
+
readings: [range($batch)
|
|
70
|
+
| {station: "st-\(.)", celsius: (. * 2) - 4, note: "it's fine"}]}
|
|
71
|
+
|
|
72
|
+
publish:
|
|
73
|
+
block: http.request
|
|
74
|
+
depends_on: [payload]
|
|
75
|
+
config:
|
|
76
|
+
url: https://postman-echo.com/post
|
|
77
|
+
method: POST
|
|
78
|
+
# A value, serialised by the block. Nothing here builds JSON out of strings.
|
|
79
|
+
body: ${steps.payload.output.value}
|
|
80
|
+
headers:
|
|
81
|
+
# What the receiver asked for, on top of the content type the block sets itself.
|
|
82
|
+
x-batch-day: ${params.day}
|
|
83
|
+
# Derived from what is being sent rather than minted per attempt, so a retry of the
|
|
84
|
+
# same payload presents the same key.
|
|
85
|
+
idempotency-key: readings-${params.day}
|
|
86
|
+
timeout: 30s
|
|
87
|
+
|
|
88
|
+
receipt:
|
|
89
|
+
block: transform.jq
|
|
90
|
+
depends_on: [payload, publish]
|
|
91
|
+
config:
|
|
92
|
+
input:
|
|
93
|
+
sent: ${steps.payload.output.value}
|
|
94
|
+
status: ${steps.publish.output.status}
|
|
95
|
+
duration_ms: ${steps.publish.output.duration_ms}
|
|
96
|
+
echoed: ${steps.publish.output.body}
|
|
97
|
+
program: |
|
|
98
|
+
{status, duration_ms,
|
|
99
|
+
sent_rows: (.sent.readings | length),
|
|
100
|
+
# The echo service returns the parsed body under .json, so this is the payload
|
|
101
|
+
# having made the round trip.
|
|
102
|
+
received_rows: (.echoed.json.readings | length),
|
|
103
|
+
identical: (.echoed.json == .sent),
|
|
104
|
+
content_type: .echoed.headers["content-type"],
|
|
105
|
+
batch_day_header: .echoed.headers["x-batch-day"]}
|
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
# POST a batch, summarise the receipt, and let the run write the page a person reads.
|
|
2
|
+
#
|
|
3
|
+
# In: a count of stations and a count of days, as parameters. The readings are generated,
|
|
4
|
+
# so the payload is genuinely large without forty rows being typed out.
|
|
5
|
+
# Out: forty readings POSTed, a small summary of what came back, and the run's own report
|
|
6
|
+
# document: a markdown page rendered when the run settles, stored with the run, and
|
|
7
|
+
# read back with `dg runs report ID --markdown`.
|
|
8
|
+
#
|
|
9
|
+
# The four hops:
|
|
10
|
+
#
|
|
11
|
+
# readings a jq program builds the batch. range() twice is a cross product, so
|
|
12
|
+
# stations x days rows come out of five lines rather than out of a literal, and
|
|
13
|
+
# the arithmetic is fixed rather than random: the same parameters always POST
|
|
14
|
+
# the same bytes, which is what makes a rerun comparable to the first run.
|
|
15
|
+
# publish the batch goes out as structure. body is a value the block serialises, so a
|
|
16
|
+
# quote in a station name is not somebody else's parse error. The URL is here
|
|
17
|
+
# rather than on a connection because Postman Echo is public and this recipe
|
|
18
|
+
# should run with nothing applied first.
|
|
19
|
+
# summary postman-echo answers with what it received: the parsed body under .json and
|
|
20
|
+
# the headers it saw under .headers. This hop keeps a page's worth of it -- a
|
|
21
|
+
# row count, min, max and mean, the first few rows, and the headers as a list
|
|
22
|
+
# of {name, value} pairs, because a template loops over a list without asking
|
|
23
|
+
# anything of the sandbox.
|
|
24
|
+
# report the section at the bottom. It renders no data of its own: everything in it is
|
|
25
|
+
# the run's facts and the outputs the three steps left behind.
|
|
26
|
+
#
|
|
27
|
+
# Where the template's facts come from:
|
|
28
|
+
#
|
|
29
|
+
# run.* the engine's account of the run -- id, status, trigger, the two moments and
|
|
30
|
+
# the duration between them. Half of them are legitimately null depending on
|
|
31
|
+
# how a run ended, and a null renders as nothing rather than failing the page.
|
|
32
|
+
# pipeline.* the pipeline version this run pinned, so the page names what actually ran.
|
|
33
|
+
# steps[] one entry per step, in the order a person watched them, carrying outcome,
|
|
34
|
+
# attempts, duration_ms, output and output_bytes. output is the step's *last*
|
|
35
|
+
# attempt's value, and it is there only when the step ran: a skipped or
|
|
36
|
+
# never-reached step has none, which is why the page guards on it before
|
|
37
|
+
# reading into it.
|
|
38
|
+
# rendered_at when this page was rendered, which is the moment the run settled.
|
|
39
|
+
#
|
|
40
|
+
# steps is a list, in the order a person watched the run. step is the same facts by name --
|
|
41
|
+
# `step.summary.output.value` is the summary hop's output -- which is how this page reads one
|
|
42
|
+
# hop without walking the list, and it is why a step a template reads should be named for what
|
|
43
|
+
# it holds.
|
|
44
|
+
#
|
|
45
|
+
# The three filters are the ones that make a machine's number readable: `duration` turns
|
|
46
|
+
# milliseconds into 1m30s, `iso` renders a moment, `bytes` a size. `round` is Jinja's own.
|
|
47
|
+
#
|
|
48
|
+
# To change it: -p stations=20 -p days=10 POSTs two hundred rows and the page reports two
|
|
49
|
+
# hundred; -p rows_shown=10 lengthens the table without touching the payload. Deleting the
|
|
50
|
+
# report: section stops the document being rendered at all, and `report: {}` swaps this page
|
|
51
|
+
# for the built-in one -- report-built-in.yaml is that document.
|
|
52
|
+
#
|
|
53
|
+
# dg run --local examples/recipes/http-post-report.yaml
|
|
54
|
+
# dg apply examples/recipes/http-post-report.yaml
|
|
55
|
+
# dg run http-post-report --watch
|
|
56
|
+
# dg runs report RUN_ID --markdown
|
|
57
|
+
|
|
58
|
+
format: dirigent/v1
|
|
59
|
+
kind: pipeline
|
|
60
|
+
code: http-post-report
|
|
61
|
+
name: POST a batch and report on it
|
|
62
|
+
description: POST a generated batch to an echo service, summarise the receipt, and render the run's own markdown report from the run's facts and its steps' outputs.
|
|
63
|
+
|
|
64
|
+
tags: [recipes, http, transform, report, starter]
|
|
65
|
+
|
|
66
|
+
requires:
|
|
67
|
+
blocks:
|
|
68
|
+
- transform.jq
|
|
69
|
+
- http.request
|
|
70
|
+
|
|
71
|
+
params:
|
|
72
|
+
type: object
|
|
73
|
+
properties:
|
|
74
|
+
stations:
|
|
75
|
+
type: integer
|
|
76
|
+
minimum: 1
|
|
77
|
+
default: 8
|
|
78
|
+
description: How many stations report.
|
|
79
|
+
days:
|
|
80
|
+
type: integer
|
|
81
|
+
minimum: 1
|
|
82
|
+
default: 5
|
|
83
|
+
description: How many days each station reports for.
|
|
84
|
+
rows_shown:
|
|
85
|
+
type: integer
|
|
86
|
+
minimum: 1
|
|
87
|
+
default: 5
|
|
88
|
+
description: How many rows the report's table carries.
|
|
89
|
+
|
|
90
|
+
steps:
|
|
91
|
+
readings:
|
|
92
|
+
block: transform.jq
|
|
93
|
+
config:
|
|
94
|
+
input:
|
|
95
|
+
stations: ${params.stations}
|
|
96
|
+
days: ${params.days}
|
|
97
|
+
program: |
|
|
98
|
+
. as {$stations, $days}
|
|
99
|
+
| {source: "dirigent recipes",
|
|
100
|
+
readings: [range($days) as $day
|
|
101
|
+
| range($stations) as $station
|
|
102
|
+
| {station: "st-\($station)",
|
|
103
|
+
day: ("2026-01-0" + ($day + 1 | tostring)),
|
|
104
|
+
# Fixed arithmetic, so the same parameters POST the same bytes.
|
|
105
|
+
celsius: ((($station * 7) + ($day * 3)) % 19) - 4}]}
|
|
106
|
+
|
|
107
|
+
publish:
|
|
108
|
+
block: http.request
|
|
109
|
+
depends_on: [readings]
|
|
110
|
+
config:
|
|
111
|
+
url: https://postman-echo.com/post
|
|
112
|
+
method: POST
|
|
113
|
+
# A value the block serialises. Nothing here builds JSON out of strings.
|
|
114
|
+
body: ${steps.readings.output.value}
|
|
115
|
+
headers:
|
|
116
|
+
# A header the report's own table has something to show. Headers are strings, so
|
|
117
|
+
# a count is interpolated into one rather than passed as a number.
|
|
118
|
+
x-report-batch: ${params.stations}-stations-${params.days}-days
|
|
119
|
+
timeout: 30s
|
|
120
|
+
|
|
121
|
+
summary:
|
|
122
|
+
block: transform.jq
|
|
123
|
+
depends_on: [publish]
|
|
124
|
+
config:
|
|
125
|
+
input:
|
|
126
|
+
shown: ${params.rows_shown}
|
|
127
|
+
status: ${steps.publish.output.status}
|
|
128
|
+
echoed: ${steps.publish.output.body}
|
|
129
|
+
program: |
|
|
130
|
+
. as {$shown, $status, $echoed}
|
|
131
|
+
| ($echoed.json.readings | map(.celsius)) as $values
|
|
132
|
+
| {status: $status,
|
|
133
|
+
rows: ($echoed.json.readings | length),
|
|
134
|
+
min: ($values | min),
|
|
135
|
+
max: ($values | max),
|
|
136
|
+
mean: (($values | add) / ($values | length)),
|
|
137
|
+
first_rows: ($echoed.json.readings[:$shown]),
|
|
138
|
+
# A list rather than a map: a template loops over a list with no filter at all.
|
|
139
|
+
headers: ($echoed.headers | to_entries | map({name: .key, value: (.value | tostring)}))}
|
|
140
|
+
|
|
141
|
+
report:
|
|
142
|
+
template: |
|
|
143
|
+
# {{ pipeline.name or pipeline.code }}: {{ run.status }}
|
|
144
|
+
|
|
145
|
+
- run: `{{ run.id }}`
|
|
146
|
+
- pipeline: `{{ pipeline.code }}` version {{ pipeline.version }}
|
|
147
|
+
- trigger: {{ run.trigger }}
|
|
148
|
+
- started: {{ run.started_at | iso }}
|
|
149
|
+
- finished: {{ run.finished_at | iso }}
|
|
150
|
+
- duration: {{ run.duration_ms | duration }}
|
|
151
|
+
- rendered: {{ rendered_at | iso }}
|
|
152
|
+
|
|
153
|
+
## Steps
|
|
154
|
+
|
|
155
|
+
| step | outcome | attempts | took | output |
|
|
156
|
+
| --- | --- | --- | --- | --- |
|
|
157
|
+
{% for one in steps %}
|
|
158
|
+
| {{ one.step }} | {{ one.outcome }} | {{ one.attempts }} | {{ one.duration_ms | duration }} | {{ one.output_bytes | bytes }} |
|
|
159
|
+
{% endfor %}
|
|
160
|
+
|
|
161
|
+
## What came back
|
|
162
|
+
|
|
163
|
+
{% set echoed = step.summary.output.value %}
|
|
164
|
+
{% if echoed %}
|
|
165
|
+
The echo service answered {{ echoed.status }} and parsed {{ echoed.rows }} readings back,
|
|
166
|
+
between {{ echoed.min }}C and {{ echoed.max }}C, averaging {{ echoed.mean | round(1) }}C.
|
|
167
|
+
|
|
168
|
+
| station | day | celsius |
|
|
169
|
+
| --- | --- | --- |
|
|
170
|
+
{% for row in echoed.first_rows %}
|
|
171
|
+
| {{ row.station }} | {{ row.day }} | {{ row.celsius }} |
|
|
172
|
+
{% endfor %}
|
|
173
|
+
|
|
174
|
+
### Headers it saw
|
|
175
|
+
|
|
176
|
+
| header | value |
|
|
177
|
+
| --- | --- |
|
|
178
|
+
{% for header in echoed.headers %}
|
|
179
|
+
| {{ header.name }} | {{ header.value }} |
|
|
180
|
+
{% endfor %}
|
|
181
|
+
{% else %}
|
|
182
|
+
The summary step left no output, so this run never got as far as a receipt.
|
|
183
|
+
{% endif %}
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
# Call a service, put the answer in a file, and read the file back.
|
|
2
|
+
#
|
|
3
|
+
# In: a query the echo service reflects back, named by parameters.
|
|
4
|
+
# Out: the body as a file in the run's scratch space, and a small summary read back out of
|
|
5
|
+
# that file.
|
|
6
|
+
#
|
|
7
|
+
# An http.request step hands its answer on as `body`, and a body worth keeping travels to
|
|
8
|
+
# storage the way every other value does: through a storage.write step. Four hops, and what
|
|
9
|
+
# each one hands on:
|
|
10
|
+
#
|
|
11
|
+
# download http.request. Output: status, headers, body, body_bytes, duration_ms. body is
|
|
12
|
+
# the parsed document here, because the echo service answers JSON.
|
|
13
|
+
# save storage.write, the value to a URI. Output: uri, bytes_written, content_type.
|
|
14
|
+
# The bytes are staged beside the target and renamed on close, so the object
|
|
15
|
+
# becomes visible only once it is whole -- which is what makes a storage.exists
|
|
16
|
+
# sensor on the other side a gate rather than a race.
|
|
17
|
+
# back storage.read, the same URI. It is application/json, so the object comes back
|
|
18
|
+
# as `value` rather than as `text`.
|
|
19
|
+
# summary transform.jq, which is where a pipeline decides how much of a large body it
|
|
20
|
+
# actually needs: this one keeps four fields.
|
|
21
|
+
#
|
|
22
|
+
# HOW BIG A BODY THIS HOLDS. max_response bounds what the call reads into the worker, and a
|
|
23
|
+
# body past it is refused rather than read half way; max_size bounds what the read brings
|
|
24
|
+
# back. Both are whole values a step can look at. Bytes nobody has to look at never become a
|
|
25
|
+
# step output at all: storage.copy moves them between URIs, bounded by the storage rather
|
|
26
|
+
# than by the worker's memory.
|
|
27
|
+
#
|
|
28
|
+
# To change it: point save at s3://<bucket>/incoming/... and the same three steps become a
|
|
29
|
+
# download into an object store, because the scheme is the only thing that changes.
|
|
30
|
+
#
|
|
31
|
+
# dg run --local examples/recipes/http-save-body-to-storage.yaml
|
|
32
|
+
# dg run --local examples/recipes/http-save-body-to-storage.yaml -p day=2026-02-01
|
|
33
|
+
|
|
34
|
+
format: dirigent/v1
|
|
35
|
+
kind: pipeline
|
|
36
|
+
code: http-save-body-to-storage
|
|
37
|
+
name: A response body saved to storage
|
|
38
|
+
description: Call an endpoint, write the body it answered with to a storage URI, and read the file back for a summary.
|
|
39
|
+
|
|
40
|
+
tags: [recipes, http, storage, transform, starter]
|
|
41
|
+
|
|
42
|
+
requires:
|
|
43
|
+
blocks:
|
|
44
|
+
- http.request
|
|
45
|
+
- storage.write
|
|
46
|
+
- storage.read
|
|
47
|
+
- transform.jq
|
|
48
|
+
|
|
49
|
+
params:
|
|
50
|
+
type: object
|
|
51
|
+
properties:
|
|
52
|
+
day:
|
|
53
|
+
type: string
|
|
54
|
+
format: date
|
|
55
|
+
default: "2026-01-01"
|
|
56
|
+
description: The day the downloaded file is named for.
|
|
57
|
+
station:
|
|
58
|
+
type: string
|
|
59
|
+
default: st-1
|
|
60
|
+
description: A query value, echoed back inside the downloaded body.
|
|
61
|
+
|
|
62
|
+
steps:
|
|
63
|
+
download:
|
|
64
|
+
block: http.request
|
|
65
|
+
config:
|
|
66
|
+
url: https://postman-echo.com/get
|
|
67
|
+
method: GET
|
|
68
|
+
query:
|
|
69
|
+
station: ${params.station}
|
|
70
|
+
day: ${params.day}
|
|
71
|
+
max_response: 8mb
|
|
72
|
+
timeout: 30s
|
|
73
|
+
|
|
74
|
+
save:
|
|
75
|
+
block: storage.write
|
|
76
|
+
depends_on: [download]
|
|
77
|
+
config:
|
|
78
|
+
# ${run.scratch} is the run's own prefix in whatever storage the instance is
|
|
79
|
+
# configured with: a temporary directory under --local, the artifact root on a
|
|
80
|
+
# server, a bucket where an s3 connection serves the scheme.
|
|
81
|
+
target: ${run.scratch}/incoming/${params.day}.json
|
|
82
|
+
value: ${steps.download.output.body}
|
|
83
|
+
|
|
84
|
+
back:
|
|
85
|
+
block: storage.read
|
|
86
|
+
depends_on: [save]
|
|
87
|
+
config:
|
|
88
|
+
# Where the write said the object landed, not the same path typed twice.
|
|
89
|
+
source: ${steps.save.output.uri}
|
|
90
|
+
max_size: 8mb
|
|
91
|
+
|
|
92
|
+
summary:
|
|
93
|
+
block: transform.jq
|
|
94
|
+
depends_on: [download, save, back]
|
|
95
|
+
config:
|
|
96
|
+
input:
|
|
97
|
+
status: ${steps.download.output.status}
|
|
98
|
+
uri: ${steps.save.output.uri}
|
|
99
|
+
bytes: ${steps.save.output.bytes_written}
|
|
100
|
+
body: ${steps.back.output.value}
|
|
101
|
+
program: |
|
|
102
|
+
{status, uri, file_bytes: .bytes,
|
|
103
|
+
summary: {url: .body.url,
|
|
104
|
+
station: .body.args.station,
|
|
105
|
+
day: .body.args.day,
|
|
106
|
+
host: .body.headers.host}}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
# When a non-2xx is the right answer: success_status, and what it does not mean.
|
|
2
|
+
#
|
|
3
|
+
# In: a status code, as a parameter, defaulting to 418.
|
|
4
|
+
# Out: that status treated as success, with the run green and the body available to the
|
|
5
|
+
# steps below.
|
|
6
|
+
#
|
|
7
|
+
# By default any 2xx is success and everything else fails the step. success_status replaces
|
|
8
|
+
# that list for one step: writing [200, 418] says exactly those two are success, and 200
|
|
9
|
+
# alone no longer passes unless it is in the list. It is a replacement, not an addition,
|
|
10
|
+
# which is the field's one sharp edge.
|
|
11
|
+
#
|
|
12
|
+
# It is worth using where an API genuinely answers a non-2xx for an outcome that is not an
|
|
13
|
+
# error:
|
|
14
|
+
#
|
|
15
|
+
# 404 on a delete or a lookup, where "it is not there" is the state being asked about.
|
|
16
|
+
# 409 on a create, where "it already exists" is the state being asked about.
|
|
17
|
+
# 204 for an accepted write with nothing to return.
|
|
18
|
+
#
|
|
19
|
+
# It is not a way to silence a failure. A step that lists 500 as success has not made the
|
|
20
|
+
# server healthy; it has removed the run's only way of saying otherwise. The rule of thumb
|
|
21
|
+
# is that a listed status must be one the steps below can act on, and the branch below here
|
|
22
|
+
# does exactly that: it reads the status and reports what it means.
|
|
23
|
+
#
|
|
24
|
+
# https://postman-echo.com/status/<code> answers with the code it is given, which is what
|
|
25
|
+
# makes this runnable without arranging a real 409.
|
|
26
|
+
#
|
|
27
|
+
# To change it: -p status=404 with -p expected=404 is the "not there" case; -p status=500
|
|
28
|
+
# with the default expectations fails the run, which is the check that this recipe is not
|
|
29
|
+
# just swallowing everything.
|
|
30
|
+
#
|
|
31
|
+
# dg run --local examples/recipes/http-success-status-list.yaml
|
|
32
|
+
# dg run --local examples/recipes/http-success-status-list.yaml -p status=200
|
|
33
|
+
# dg run --local examples/recipes/http-success-status-list.yaml -p status=500 # fails, on purpose
|
|
34
|
+
|
|
35
|
+
format: dirigent/v1
|
|
36
|
+
kind: pipeline
|
|
37
|
+
code: http-success-status-list
|
|
38
|
+
name: Statuses that count as success
|
|
39
|
+
description: Accept a specific non-2xx as success for one call with success_status, and branch on what the status actually meant.
|
|
40
|
+
|
|
41
|
+
tags: [recipes, http, transform]
|
|
42
|
+
|
|
43
|
+
requires:
|
|
44
|
+
blocks:
|
|
45
|
+
- http.request
|
|
46
|
+
- transform.jq
|
|
47
|
+
|
|
48
|
+
params:
|
|
49
|
+
type: object
|
|
50
|
+
properties:
|
|
51
|
+
status:
|
|
52
|
+
type: integer
|
|
53
|
+
default: 418
|
|
54
|
+
description: The status the endpoint is asked to answer with.
|
|
55
|
+
|
|
56
|
+
steps:
|
|
57
|
+
call:
|
|
58
|
+
block: http.request
|
|
59
|
+
config:
|
|
60
|
+
url: https://postman-echo.com/status/${params.status}
|
|
61
|
+
method: GET
|
|
62
|
+
# Replaces the default, so 200 is listed explicitly: leaving it out would fail a call
|
|
63
|
+
# that succeeded ordinarily.
|
|
64
|
+
success_status: [200, 404, 418]
|
|
65
|
+
timeout: 20s
|
|
66
|
+
|
|
67
|
+
interpret:
|
|
68
|
+
block: transform.jq
|
|
69
|
+
depends_on: [call]
|
|
70
|
+
config:
|
|
71
|
+
input:
|
|
72
|
+
status: ${steps.call.output.status}
|
|
73
|
+
program: |
|
|
74
|
+
# A listed status has to be one the steps below can act on; that is what makes
|
|
75
|
+
# listing it different from ignoring a failure.
|
|
76
|
+
{status,
|
|
77
|
+
outcome: (if .status == 200 then "present"
|
|
78
|
+
elif .status == 404 then "absent, which is an answer"
|
|
79
|
+
else "answered, and unusual" end),
|
|
80
|
+
acted_on: (.status != 200)}
|