#!/bin/bash set -euo pipefail # Seed the Flow Debugger OUTBOUND-CALLS demo on the docker rig (plan # docs/plans/2026-09-06-flow-debugger-outbound-calls.md, Task 10): one site Call script that makes # all three outbound call kinds, plus an inbound method routing to it, so `flow-debug run-method` / # the /deployment/flow-debugger page show ApiCall + DbCall + Notify nodes on the External lane. # # What it creates: # - External system RigRest -> the infra REST API (scadabridge-restapi:5200, X-API-Key # scadabridge-test-key-1) with one method Echo = POST /api/Echo. # - DB connection RigDb -> the rig's ScadaBridgeMachineData database on scadabridge-mssql. # - Notification list FlowDemo (email; delivery needs an SMTP configuration row — create one on # /notifications/smtp pointing at scadabridge-smtp:1025, auth None — # otherwise the notification node ends Parked, which is itself a # valid terminal lifecycle for the demo). # - Script OutboundDemo (Call) on template SimMachine (docker/seed-delmia-sim.sh): # ExternalSystem.Call("RigRest","Echo") -> sync ApiCall, in-chain # Database.CachedWrite("RigDb", …) -> DbCall, async (creates + inserts dbo.FlowDemo) # Notify.To("FlowDemo").Send(…) -> Notify, async # - Inbound method FlowOutboundDemo (Route.To(MachineCode).Call("OutboundDemo")) + an API key # scoped to it (token printed once as FLOW_DEMO_API_KEY=…). # - Artifacts pushed to site 1 and DelmiaSim01 redeployed so the site has the new script. # # Not idempotent (names must be unique). Prerequisites: docker/deploy.sh, seed-sites.sh, # seed-delmia-sim.sh, and `infra/` up (restapi + mssql). SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" PROJECT_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)" CLI="$PROJECT_ROOT/src/ZB.MOM.WW.ScadaBridge.CLI/bin/Debug/net10.0/scadabridge" A=(--url "${SCADABRIDGE_URL:-http://localhost:9000}" --format=json --username "${SCADABRIDGE_USERNAME:-multi-role}" --password "${SCADABRIDGE_PASSWORD:-password}") SITE_ID="${SITE_ID:-1}" INSTANCE_NAME="${INSTANCE_NAME:-DelmiaSim01}" TEMPLATE_NAME="${TEMPLATE_NAME:-SimMachine}" KEY_NAME="${KEY_NAME:-flow-outbound-demo}" RIG_DB_CS="${RIG_DB_CS:-Server=scadabridge-mssql,1433;Database=ScadaBridgeMachineData;User Id=scadabridge_app;Password=ScadaBridge_Dev1#;TrustServerCertificate=true}" json_id() { python3 -c 'import json,sys; print(json.load(sys.stdin)["id"])'; } find_id() { # $1 = json array on stdin key name, $2 = value python3 -c 'import json,sys; k,v=sys.argv[1],sys.argv[2]; print(next(x["id"] for x in json.load(sys.stdin) if x.get(k)==v))' "$1" "$2"; } echo "1/7 External system RigRest -> scadabridge-restapi (method Echo = POST /api/Echo)..." ES_ID=$("$CLI" external-system create "${A[@]}" --name RigRest --endpoint-url http://scadabridge-restapi:5200 \ --auth-type ApiKey --auth-config '{"key":"scadabridge-test-key-1"}' --timeout-seconds 10 | json_id) "$CLI" external-system method create "${A[@]}" --external-system-id "$ES_ID" --name Echo --http-method POST --path /api/Echo > /dev/null echo " id=$ES_ID" echo "2/7 DB connection RigDb -> scadabridge-mssql/ScadaBridgeMachineData..." DB_ID=$("$CLI" db-connection create "${A[@]}" --name RigDb --connection-string "$RIG_DB_CS" | json_id) echo " id=$DB_ID" echo "3/7 Notification list FlowDemo..." LIST_ID=$("$CLI" notification create "${A[@]}" --name FlowDemo --emails flow-demo@rig.local | json_id) echo " id=$LIST_ID" echo "4/7 Script OutboundDemo (Call) on template $TEMPLATE_NAME..." TEMPLATE_ID=$("$CLI" template list "${A[@]}" | find_id name "$TEMPLATE_NAME") read -r -d '' DEMO_CODE <<'CS' || true // Flow Debugger outbound-calls demo: one sync API call (in-chain), one cached DB write and one // notification (both async branches). Each is an ordinary audited call — nothing here is // debugger-specific; the Flow Debugger just draws the rows they leave. System.Func P = k => Parameters.ContainsKey(k) ? Parameters[k] : null; var wo = P("WorkOrder")?.ToString() ?? "WO-0"; var echo = await ExternalSystem.Call("RigRest", "Echo", new System.Collections.Generic.Dictionary { ["workOrder"] = wo, ["source"] = "OutboundDemo" }); var db = await Database.CachedWrite("RigDb", "IF OBJECT_ID('dbo.FlowDemo','U') IS NULL CREATE TABLE dbo.FlowDemo (Id INT IDENTITY(1,1) PRIMARY KEY, WorkOrder NVARCHAR(64) NOT NULL, At DATETIME2 NOT NULL DEFAULT SYSUTCDATETIME()); INSERT INTO dbo.FlowDemo (WorkOrder) VALUES (@wo);", new System.Collections.Generic.Dictionary { ["wo"] = wo }); var notification = await Notify.To("FlowDemo").Send("Flow demo " + wo, "Echo ok=" + echo.Success + "; db op=" + db.Value); return new { Result = echo.Success, ResultText = "echo=" + echo.Success + " db=" + db.Value + " notify=" + notification }; CS "$CLI" template script add "${A[@]}" --template-id "$TEMPLATE_ID" --name OutboundDemo --trigger-type Call --code "$DEMO_CODE" > /dev/null echo " template id=$TEMPLATE_ID" echo "5/7 Inbound method FlowOutboundDemo + API key '$KEY_NAME'..." read -r -d '' METHOD_CODE <<'CS' || true System.Func P = k => Parameters.ContainsKey(k) ? Parameters[k] : null; var code = (P("MachineCode") as string ?? "").Trim(); if (string.IsNullOrEmpty(code)) return new { Result = false, ResultText = "MachineCode is required" }; return await Route.To(code).Call("OutboundDemo", new { WorkOrder = P("WorkOrder") }); CS METHOD_ID=$("$CLI" api-method create "${A[@]}" --name FlowOutboundDemo --timeout 45 \ --parameters '{"type":"object","properties":{"MachineCode":{"type":"string"},"WorkOrder":{"type":"string"}},"required":["MachineCode"]}' \ --return-def '{"type":"object","properties":{"Result":{"type":"boolean"},"ResultText":{"type":"string"}},"required":["Result","ResultText"]}' \ --script "$METHOD_CODE" | json_id) KEY_JSON=$("$CLI" security api-key create "${A[@]}" --name "$KEY_NAME" --methods FlowOutboundDemo) TOKEN=$(echo "$KEY_JSON" | python3 -c 'import json,sys; print(json.load(sys.stdin)["token"])' 2>/dev/null || echo "$KEY_JSON" | grep -o 'sbk_[A-Za-z0-9_-]*' | head -1) echo " method id=$METHOD_ID" echo "6/7 Pushing artifacts (external system + DB connection) to site $SITE_ID..." "$CLI" site deploy-artifacts "${A[@]}" --site-id "$SITE_ID" > /dev/null echo "7/7 Redeploying $INSTANCE_NAME so the site has OutboundDemo..." INSTANCE_ID=$("$CLI" instance list "${A[@]}" | find_id uniqueName "$INSTANCE_NAME") "$CLI" instance deploy "${A[@]}" --id "$INSTANCE_ID" > /dev/null echo " instance id=$INSTANCE_ID" echo echo "FLOW_DEMO_API_KEY=$TOKEN" echo "Try: scadabridge --url http://localhost:9000 flow-debug run-method --method-id $METHOD_ID --params '{\"MachineCode\":\"$INSTANCE_NAME\",\"WorkOrder\":\"WO-1\"}'" echo " then open /deployment/flow-debugger?executionId="