From d659f8490ff949863b81545a4e06121de7681e9d Mon Sep 17 00:00:00 2001 From: Claus Ibsen Date: Fri, 2 Oct 2026 19:49:11 +0200 Subject: [PATCH] chore: find the SKU in stock.json with Groovy instead of a jsonpath filter stock-api, http-client, openapi-server and mcp-server looked up one SKU with a jsonpath filter returning a list, then a size check and ${body[0]}. They now unmarshal the file and run body.find { it.sku == headers.sku }, which gives the item or null in one line. READMEs, metadata and the catalog follow. Co-Authored-By: Claude Opus 5.5 (1M context) Claude-Session: https://claude.ai/code/session_01STT6whBgK1AqsSsUKrnE8m --- ai/mcp-server/README.md | 2 +- ai/mcp-server/mcp-server.camel.yaml | 13 ++++++---- ai/mcp-server/metadata.json | 2 +- camel-jbang-example-catalog.json | 9 ++++--- connect/http-client/http-client.camel.yaml | 15 ++++++----- connect/http-client/metadata.json | 2 +- connect/stock-api/README.md | 10 ++++---- connect/stock-api/metadata.json | 3 ++- connect/stock-api/stock-api.camel.yaml | 15 ++++++----- contracts/openapi-server/README.md | 4 +-- contracts/openapi-server/metadata.json | 2 +- .../openapi-server/openapi-server.camel.yaml | 25 +++++-------------- 12 files changed, 46 insertions(+), 56 deletions(-) diff --git a/ai/mcp-server/README.md b/ai/mcp-server/README.md index 2170f2f..9eb62a7 100644 --- a/ai/mcp-server/README.md +++ b/ai/mcp-server/README.md @@ -74,7 +74,7 @@ The MCP server is a preview feature since Camel 4.22. 1. One `ai-tool` route that answers a fixed text, with the four `camel.server.mcp-*` properties; run it and see the server line in the log, then `initialize` and `tools/list` with `curl`. 2. Add a parameter and use `${header.sku}` in the answer; call it with `tools/call`. -3. Answer from `stock.json` with `jsonpath` and add the unknown case. +3. Answer from `stock.json`: unmarshal it, find the SKU with Groovy and add the unknown case. 4. Add the second tool and connect a real agent. ## Try changing diff --git a/ai/mcp-server/mcp-server.camel.yaml b/ai/mcp-server/mcp-server.camel.yaml index 02834fb..55a7399 100644 --- a/ai/mcp-server/mcp-server.camel.yaml +++ b/ai/mcp-server/mcp-server.camel.yaml @@ -17,16 +17,19 @@ expression: constant: expression: resource:file:stock.json + - unmarshal: + json: + library: Jackson + # the item of that SKU as a map, or null when there is none - setBody: expression: - jsonpath: - expression: "$[?(@.sku == '${header.sku}')]" - resultType: java.util.List + groovy: + expression: "body.find { it.sku == headers.sku }" - choice: when: - expression: simple: - expression: "${body.size()} == 0" + expression: "${body} == null" steps: - setBody: expression: @@ -37,7 +40,7 @@ - setBody: expression: simple: - expression: "${body[0][qty]} ${header.sku} in stock" + expression: "${body[qty]} ${header.sku} in stock" - log: message: "Tool stock_level(${header.sku}): ${body}" diff --git a/ai/mcp-server/metadata.json b/ai/mcp-server/metadata.json index f5134f5..3ccae52 100644 --- a/ai/mcp-server/metadata.json +++ b/ai/mcp-server/metadata.json @@ -16,7 +16,7 @@ ], "languages": [ "simple", - "jsonpath", + "groovy", "constant" ], "dataformats": [ diff --git a/camel-jbang-example-catalog.json b/camel-jbang-example-catalog.json index dbabdbe..3991ceb 100644 --- a/camel-jbang-example-catalog.json +++ b/camel-jbang-example-catalog.json @@ -112,7 +112,7 @@ ], "languages": [ "simple", - "jsonpath", + "groovy", "constant" ], "dataformats": [ @@ -548,7 +548,7 @@ ], "languages": [ "simple", - "jsonpath" + "groovy" ], "dataformats": [ "json" @@ -589,11 +589,12 @@ "setBody", "setHeader", "choice", + "unmarshal", "marshal" ], "languages": [ "constant", - "jsonpath", + "groovy", "simple" ], "dataformats": [ @@ -728,7 +729,7 @@ ], "languages": [ "simple", - "jsonpath", + "groovy", "constant" ], "dataformats": [ diff --git a/connect/http-client/http-client.camel.yaml b/connect/http-client/http-client.camel.yaml index 79f40db..a73f67e 100644 --- a/connect/http-client/http-client.camel.yaml +++ b/connect/http-client/http-client.camel.yaml @@ -19,16 +19,19 @@ expression: constant: expression: resource:file:stock.json + - unmarshal: + json: + library: Jackson + # the item of that SKU as a map, or null when there is none - setBody: expression: - jsonpath: - expression: "$[?(@.sku == '${header.sku}')]" - resultType: java.util.List + groovy: + expression: "body.find { it.sku == headers.sku }" - choice: when: - expression: simple: - expression: "${body.size()} == 0" + expression: "${body} == null" steps: - setHeader: name: CamelHttpResponseCode @@ -41,10 +44,6 @@ expression: '{"error": "unknown sku ${header.sku}"}' otherwise: steps: - - setBody: - expression: - simple: - expression: "${body[0]}" - marshal: json: library: Jackson diff --git a/connect/http-client/metadata.json b/connect/http-client/metadata.json index 07a2492..2487795 100644 --- a/connect/http-client/metadata.json +++ b/connect/http-client/metadata.json @@ -22,7 +22,7 @@ ], "languages": [ "simple", - "jsonpath" + "groovy" ], "dataformats": [ "json" diff --git a/connect/stock-api/README.md b/connect/stock-api/README.md index 10cbd83..6cf1cf0 100644 --- a/connect/stock-api/README.md +++ b/connect/stock-api/README.md @@ -40,17 +40,17 @@ Then, in another terminal, call it with `curl` as above. Stop it with `ctrl` + ` item. Each operation hands over to a `direct` route. The CLI starts the HTTP server on port 8080 because the file uses the REST DSL; `application.properties` sets the port. - The list route sets the body to `resource:file:stock.json`, the file next to the route, and the content type. -- The item route loads the same file and picks the item with a `jsonpath` filter, `$[?(@.sku == '...')]`, in which - the `{sku}` path parameter arrives as the header `sku`. The result is a list: empty means 404 with a small error - body, otherwise the first element is marshalled back to JSON. +- The item route loads the same file, unmarshals it into a list of maps, and picks the item with one line of Groovy, + `body.find { it.sku == headers.sku }`, in which the `{sku}` path parameter arrives as the header `sku`. The result is + the item or null: null means 404 with a small error body, otherwise the item is marshalled back to JSON. - `CamelHttpResponseCode` is the header that sets the status code; 200 is the default. ## Build it step by step 1. A `rest` with one `get` that answers `resource:file:stock.json`; run it and `curl localhost:8080/stock`. 2. Add the `/{sku}` operation and log `${header.sku}` to see the path parameter arrive. -3. Filter the file with `jsonpath` and return the first element; `curl` a known SKU. -4. Add the `choice` for the empty result and the 404. +3. Unmarshal the file and find the item with Groovy, `body.find { it.sku == headers.sku }`; `curl` a known SKU. +4. Add the `choice` for the null result and the 404. ## Try changing diff --git a/connect/stock-api/metadata.json b/connect/stock-api/metadata.json index cf02c8c..e5ec94b 100644 --- a/connect/stock-api/metadata.json +++ b/connect/stock-api/metadata.json @@ -13,11 +13,12 @@ "setBody", "setHeader", "choice", + "unmarshal", "marshal" ], "languages": [ "constant", - "jsonpath", + "groovy", "simple" ], "dataformats": [ diff --git a/connect/stock-api/stock-api.camel.yaml b/connect/stock-api/stock-api.camel.yaml index b5464b8..94fafe5 100644 --- a/connect/stock-api/stock-api.camel.yaml +++ b/connect/stock-api/stock-api.camel.yaml @@ -37,16 +37,19 @@ expression: constant: expression: resource:file:stock.json + - unmarshal: + json: + library: Jackson + # the item of that SKU as a map, or null when there is none - setBody: expression: - jsonpath: - expression: "$[?(@.sku == '${header.sku}')]" - resultType: java.util.List + groovy: + expression: "body.find { it.sku == headers.sku }" - choice: when: - expression: simple: - expression: "${body.size()} == 0" + expression: "${body} == null" steps: - setHeader: name: CamelHttpResponseCode @@ -59,10 +62,6 @@ expression: '{"error": "unknown sku ${header.sku}"}' otherwise: steps: - - setBody: - expression: - simple: - expression: "${body[0]}" - marshal: json: library: Jackson diff --git a/contracts/openapi-server/README.md b/contracts/openapi-server/README.md index 04fbb10..746aaa9 100644 --- a/contracts/openapi-server/README.md +++ b/contracts/openapi-server/README.md @@ -48,8 +48,8 @@ Stop it with `ctrl` + `c`. `apiContextPath: openapi` serves the contract. - Each operation is a `direct` route named after its `operationId`: `listStock`, `getStock`, `reserveStock`. Path parameters arrive as headers, `sku` here; the request body is the message body. -- `lookup` is a helper route the two SKU operations share: it filters the stock file with `jsonpath` and - leaves the item, or null, as the body. +- `lookup` is a helper route the two SKU operations share: it unmarshals the stock file and finds the SKU + with Groovy, `body.find { it.sku == headers.sku }`, which leaves the item, or null, as the body. - The contract check does not look at the values, so `reserveStock` runs `validate` on them and an `onException` turns the failed predicate into a 400 with an error body, the status the contract promises. - `CamelHttpResponseCode` sets 404 and 409; the bodies are the JSON the contract's `Error` schema describes. diff --git a/contracts/openapi-server/metadata.json b/contracts/openapi-server/metadata.json index 2983f8b..07a2707 100644 --- a/contracts/openapi-server/metadata.json +++ b/contracts/openapi-server/metadata.json @@ -21,7 +21,7 @@ ], "languages": [ "simple", - "jsonpath", + "groovy", "constant" ], "dataformats": [ diff --git a/contracts/openapi-server/openapi-server.camel.yaml b/contracts/openapi-server/openapi-server.camel.yaml index 23d364e..d2f497b 100644 --- a/contracts/openapi-server/openapi-server.camel.yaml +++ b/contracts/openapi-server/openapi-server.camel.yaml @@ -139,24 +139,11 @@ expression: constant: expression: resource:file:stock.json + - unmarshal: + json: + library: Jackson + # the item of that SKU as a map, or null when there is none - setBody: expression: - jsonpath: - expression: "$[?(@.sku == '${header.sku}')]" - resultType: java.util.List - - choice: - when: - - expression: - simple: - expression: "${body.size()} == 0" - steps: - - setBody: - expression: - simple: - expression: "${null}" - otherwise: - steps: - - setBody: - expression: - simple: - expression: "${body[0]}" + groovy: + expression: "body.find { it.sku == headers.sku }"