Routing a shipment according to its status
This workflow routes each shipment status update: it confirms each package at delivery, publishes the completion event and alerts logistics in case of delay.
Loading the diagram…
The need
Each shipment status update must be routed to the customer notification or the logistics alert depending on the delivery status. The workflow confirms each package at delivery, publishes the completion event and alerts logistics in case of delay.
What the workflow does
- Shipment status — the Trigger node captures the
delivery.statusevent. The tracking number, the status and the list of packages arrive indata. - Normalize the payload — the Code node prepares
context.variables.statusandcontext.variables.packagesfor the following nodes. - Delivery status — the Switch node routes
{{ context.variables.status }}to the corresponding branch. - Alert on delay — if the status is
delayed, an email warns the logistics team. - Confirm each package — if the status is
delivered, the ForEach node iterates over{{ data.packages }}(current element{{ colis }}). - Update the package tracking — at each iteration, the PostgreSQL node confirms the package (animated "loop" edge).
- Shipment completed event — after the loop, the Send Google Pub/Sub node publishes
delivery_completedon the CRM's Pub/Sub topic.
What you need
- A Trigger node that captures the
delivery.statusevent. - The Code node, the Switch node, the Send an email node, the ForEach node, the PostgreSQL node and the Send Google Pub/Sub node.
Construction
Copy the workflow definition and paste it into the editor (the Trigger node is recreated automatically, it is not in the snippet):
[
{
"id": "code-108",
"name": "Normalize the payload",
"config": {
"type": "CODE",
"code": "context.variables.status = data.status; context.variables.packages = data.packages;"
},
"parents": [
"trigger"
],
"position": {
"x": 0,
"y": 200
},
"next": "switch-108"
},
{
"id": "switch-108",
"name": "Delivery status",
"config": {
"type": "SWITCH",
"variable": "{{ context.variables.status }}",
"cases": [
{
"value": "delivered",
"next": "foreach-108"
},
{
"value": "delayed",
"next": "email-108"
}
],
"noMatchBehaviour": "error"
},
"parents": [
"code-108"
],
"position": {
"x": 0,
"y": 400
}
},
{
"id": "foreach-108",
"name": "Confirm each package",
"config": {
"type": "FOREACH",
"foreachCode": "data.packages",
"itemKey": "colis"
},
"parents": [
"switch-108"
],
"position": {
"x": 800,
"y": 600
},
"next": "event-108",
"loopStartNodeId": "db-108"
},
{
"id": "db-108",
"name": "Update the package tracking",
"config": {
"type": "DATABASE_POSTGRESQL",
"query": "UPDATE shipments SET status = 'confirmed' WHERE tracking = $1",
"binds": [
"$.colis.tracking"
],
"outputKey": "trackingUpdate"
},
"parents": [
"foreach-108"
],
"position": {
"x": 1600,
"y": 800
}
},
{
"id": "event-108",
"name": "Shipment completed event",
"config": {
"type": "SEND_EVENT",
"provider": {
"type": "GCP_PUBSUB",
"projectId": "mon-projet-logistique",
"topicId": "livraisons"
},
"body": "{\"event\": \"delivery_completed\", \"trackingNumber\": \"{{ data.trackingNumber }}\"}"
},
"parents": [
"foreach-108"
],
"position": {
"x": 800,
"y": 1000
}
},
{
"id": "email-108",
"name": "Alert on delay",
"config": {
"type": "SEND_EMAIL",
"to": [
"logistique@transporteur.fr"
],
"subject": "Delayed delivery: {{ data.trackingNumber }}",
"content": "Delivery {{ data.trackingNumber }} is delayed.",
"sendAsHtml": true
},
"parents": [
"switch-108"
],
"position": {
"x": 800,
"y": 800
}
}
]
Check that it works
- Expected values: the
deliveredanddelayedcases must match the statuses published by the carrier exactly (case, spelling). - Deploy the workflow (Deploy a workflow), then trigger the real scenario: every execution generates a trace you can view (View traces).
Variations
- Unhandled cases:
noMatchBehaviour: "error"makes the workflow fail if the status is unknown; add an "other" branch to track unknown statuses instead of crashing. - Loop: the animated "loop" edge illustrates the iteration of the ForEach node; the loop body (Update the package tracking) runs once per package.
Going further
See also: Examples and integrations