Recipes
Append to a List
Submit-and-review flows are everywhere: place an order, see it in the history; send an event, see it in the feed. This recipe accepts items with POST and returns everything submitted so far with GET, using flags as an append-only store.
The recipe
mock "POST /purchases" {
format = "json"
body = <<-EOF
{{= $id (uuid)}}
{{setFlag (append 'purchases:' $id) request.body}}
{{= $ids (getFlag 'purchases:ids')}}
{{#if $ids}}
{{setFlag 'purchases:ids' (append $ids ',' $id)}}
{{else}}
{{setFlag 'purchases:ids' $id}}
{{/if}}
{
"id": "{{$id}}",
"status": "created"
}
EOF
}
mock "GET /purchases" {
format = "json"
body = <<-EOF
{{= $ids (getFlag 'purchases:ids')}}
[
{{#if $ids}}
{{#forEach (split $ids ',')}}
{{json (getFlag (append 'purchases:' item))}}{{^isLast}},{{/isLast}}
{{/forEach}}
{{/if}}
]
EOF
}Try it
$curl -X POST http://localhost:8080/purchases -H 'Content-Type: application/json' -d '{"product": "Widget", "qty": 2}'
{
"id": "7d4c2f2e-9b1a-4f7e-8a3d-5e6f7a8b9c0d",
"status": "created"
}
$curl http://localhost:8080/purchases
[
{ "product": "Widget", "qty": 2 }
]
How it works
- Every submission gets a fresh id from the
uuidhelper, and the whole request body is stored as an object underpurchases:<id>. purchases:idsaccumulates the ids as a comma-separated string. The first submission is the special case: appending to a missing flag would start the list with a comma, so theifbranch sets it directly.- The
GETsplits the id list back into an array, looks each record up, and renders it with thejsonhelper. The surroundingifkeeps the response a valid empty array before anything is submitted. POSTmocks default to status201, so nostatusfield is needed.
Since nothing is ever removed,
{{^isLast}},{{/isLast}} is safe here. If you add deletion, switch the list to the accumulator pattern from Stateful CRUD, which tolerates missing records.Variations
- Add a reset mock for test isolation:
DELETE /purchaseswith{{delFlag 'purchases:ids'}}in the body. - Give entries a TTL by passing a third argument to both
setFlagcalls, and the feed cleans itself up after a while (combine with the accumulator pattern so expired records do not break commas). - Echo the stored item back with an id in each list entry by storing a composed object instead of the raw body; see the id note in Stateful CRUD.