Tutorial

7.5 Ask it

dotnet run --urls http://localhost:5080

The catalogue needs nobody signed in:

curl -s http://localhost:5080/trax/graphql -H 'Content-Type: application/json' \
  -d '{"query":"{ discover { products(order: { sku: ASC }) { nodes { sku name price stock } } } }"}'
{"data":{"discover":{"products":{"nodes":[{"sku":"CAP","name":"Cap","price":18.50,"stock":0},{"sku":"MUG","name":"Mug","price":12.00,"stock":10},{"sku":"TEE","name":"T-shirt","price":25.00,"stock":3}]}}}}

Your stock is lower, after the orders of the earlier chapters. The row version is not a field: the schema serves IProductListing, not Product.

Place an order as ada. The answer carries the run's externalId, which is how a run is named to everything that can follow it:

ADA=$(curl -s http://localhost:5080/dev/token/ada)
curl -s http://localhost:5080/trax/graphql -H "Authorization: Bearer $ADA" \
  -H 'Content-Type: application/json' -d '{
  "query": "mutation Place($input: PlaceOrderInput!) { dispatch { placeOrder(input: $input) { externalId output { orderId total } } } }",
  "variables": { "input": { "paymentToken": "tok_visa", "lines": [{ "sku": "MUG", "quantity": 1 }] } }
}'
{"data":{"dispatch":{"placeOrder":{"externalId":"f3e0f595ed6a472b926c46dbaf671e8d","output":{"orderId":"c5625240-1274-4e08-b2ff-2115a19fc9aa","total":12.00}}}}}

Read the run back, step by step:

curl -s http://localhost:5080/trax/graphql -H "Authorization: Bearer $ADA" \
  -H 'Content-Type: application/json' -d '{
  "query": "query Progress($runId: String!) { runProgress(runId: $runId) { state steps { position name state } problem { code } } }",
  "variables": { "runId": "f3e0f595ed6a472b926c46dbaf671e8d" }
}'
{"data":{"runProgress":{"state":"COMPLETED","steps":[{"position":0,"name":"IdentifyCustomerJunction","state":"COMPLETED"},{"position":1,"name":"PriceCartJunction","state":"COMPLETED"},{"position":2,"name":"AssessRiskJunction","state":"COMPLETED"},{"position":3,"name":"LooksFraudulent","state":"COMPLETED"},{"position":4,"name":"LooksFraudulent","state":"COMPLETED"},{"position":5,"name":"(withheld)","state":"COMPLETED"},{"position":6,"name":"(withheld)","state":"COMPLETED"},{"position":7,"name":"(withheld)","state":"COMPLETED"}],"problem":null}}}

Use your own externalId. The first three steps are the junctions before the fraud check. Then the question is asked and routed on, as two steps. After that the names are withheld, and that is deliberate: which junctions ran after a decision says what was decided, and a customer is not told how the fraud check judged them. [TraxRunProgress(Answers = true)] would show them, and the answer with them, for a train whose decisions its caller may see.

A run is followed by the caller who started it and by nobody else. Ask the same as grace and the answer is NOT_FOUND, the same answer as for a run that does not exist, so it tells her nothing about ada's orders:

{"data":{"runProgress":{"state":null,"steps":[],"problem":{"code":"NOT_FOUND"}}}}

A run started over a mutation returns once it has finished, so here the steps are all done. A subscription to the same runProgress streams each step as it happens, which matters once runs are queued and take longer than a request.

Errors come back as GraphQL errors. A plain TrainException keeps its message for the customer, under the code TRAX_TRAIN_ERROR:

{"errors":[{"message":"The card was declined.","path":["dispatch","placeOrder"],"extensions":{"code":"TRAX_TRAIN_ERROR"}}],"data":{"dispatch":{"placeOrder":null}}}

Anything else is masked, so the payment provider's timeout, with our provider's name in its message, reaches the customer as:

{"errors":[{"message":"Unexpected Execution Error","path":["dispatch","placeOrder"]}],"data":{"dispatch":{"placeOrder":null}}}

This is why chapter 1 threw TrainException for every message meant for the customer and nothing else.

SDK Reference

AddTraxGraphQL | TraxMutation | Query models | Run progress | AddJunctionEvents | dispatch mutations


Next: 7.6 Test it