This is an automated email from the ASF dual-hosted git repository.
davsclaus pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/camel-jbang-examples.git
The following commit(s) were added to refs/heads/main by this push:
new b402ca8 contracts/rest-code-first - the REST DSL with JSON binding to
a Java record and a generated API description (#118)
b402ca8 is described below
commit b402ca89148041e8849c0a8030f84d83302acaec
Author: Claus Ibsen <[email protected]>
AuthorDate: Tue Oct 20 11:21:46 2026 +0200
contracts/rest-code-first - the REST DSL with JSON binding to a Java record
and a generated API description (#118)
* contracts/rest-code-first - the REST DSL with JSON binding to a Java
record and a generated API description
GET /orders and GET /orders/{id} answer the orders as JSON through the json
binding, POST /orders binds a new order to a Java record and answers 201,
400 without lines, and the generated API description is served at
/openapi. First in the contracts group, before the contract-first
examples, with a Citrus YAML test in the CI matrix.
Closes #123
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
Claude-Session: https://claude.ai/code/session_01STT6whBgK1AqsSsUKrnE8m
* contracts/rest-code-first - the order lines predicate works on Camel
4.22, and CI tests with Camel 4.23.0
Camel 4.22 rejects a bare ${...} as the right-hand side of ||
(SimpleIllegalSyntaxException), so the predicate
compares it with true. camel@apache/camel defaults to the latest LTS
release (4.22.0), so CI now pins Camel 4.23.0
for the CLI and for the integrations the Citrus tests start.
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
Signed-off-by: Claus Ibsen <[email protected]>
---------
Signed-off-by: Claus Ibsen <[email protected]>
Co-authored-by: Claude Opus 5.5 (1M context) <[email protected]>
---
.github/workflows/build.yml | 15 ++-
README.md | 1 +
camel-jbang-example-catalog.json | 52 +++++++++-
contracts/README.md | 3 +-
contracts/keycloak-security-rest/metadata.json | 2 +-
contracts/openapi-client/metadata.json | 2 +-
contracts/openapi-server/metadata.json | 2 +-
contracts/rest-code-first/Order.java | 30 ++++++
contracts/rest-code-first/README.md | 91 ++++++++++++++++++
contracts/rest-code-first/application.properties | 20 ++++
contracts/rest-code-first/metadata.json | 38 ++++++++
contracts/rest-code-first/orders.json | 5 +
.../rest-code-first/rest-code-first.camel.yaml | 107 +++++++++++++++++++++
.../test/rest-code-first.citrus.it.yaml | 95 ++++++++++++++++++
14 files changed, 454 insertions(+), 9 deletions(-)
diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index e1fc747..02a6e47 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -22,6 +22,13 @@ on:
branches:
- main
+env:
+ # the Camel release the examples are tested with: camel@apache/camel
defaults to the latest LTS release
+ CAMEL_VERSION: 4.23.0
+ # the Camel version of the integrations the Citrus tests start
+ CITRUS_CAMEL_JBANG_VERSION: 4.23.0
+ CITRUS_CAMEL_CLI_VERSION: 4.23.0
+
jobs:
integration-tests:
runs-on: ubuntu-latest
@@ -90,6 +97,8 @@ jobs:
path: connect/file-processing
- name: MCP server
path: ai/mcp-server
+ - name: REST code first
+ path: contracts/rest-code-first
- name: OpenAPI server
path: contracts/openapi-server
- name: OpenAPI client
@@ -135,8 +144,8 @@ jobs:
jbang trust add https://github.com/apache/camel/
- jbang camel@apache/camel --version
- jbang camel@apache/camel plugin add test
+ jbang -Dcamel.jbang.version=$CAMEL_VERSION camel@apache/camel
--version
+ jbang -Dcamel.jbang.version=$CAMEL_VERSION camel@apache/camel plugin
add test
- name: Run tests
env:
CITRUS_CAMEL_JBANG_DUMP_INTEGRATION_OUTPUT: "true"
@@ -144,7 +153,7 @@ jobs:
CITRUS_TESTCONTAINERS_REGISTRY_MIRROR: "mirror.gcr.io"
run: |
source "/home/runner/.sdkman/bin/sdkman-init.sh"
- jbang camel@apache/camel test run ${{ matrix.path }}/test
+ jbang -Dcamel.jbang.version=$CAMEL_VERSION camel@apache/camel test
run ${{ matrix.path }}/test
- uses: actions/upload-artifact@v4
if: always()
with:
diff --git a/README.md b/README.md
index 278df37..192ba9e 100644
--- a/README.md
+++ b/README.md
@@ -116,6 +116,7 @@ An OpenAPI contract served and called, and an API protected
by Keycloak.
| Example | What you will see | Needs |
|---|---|---|
+| [REST code first](contracts/rest-code-first/) | The shop's order API written
with the REST DSL: GET /orders and GET /orders/{id} answer the orders as JSON
(404 for an unknown id), POST /orders binds the JSON of a new order to a Java
record and answers 201 (400 without lines), and the API description generated
from the operations is served at /openapi. | nothing |
| [OpenAPI server](contracts/openapi-server/) | The stock API contract first:
stock-api.json is the OpenAPI contract, the REST DSL serves its three
operations on port 8080 with request validation, GET /stock/{sku} answers from
a file or 404, POST /stock/{sku}/reserve answers 200, 409 when the stock is
short or 400 for a bad reservation, and /openapi serves the contract. | nothing
|
| [OpenAPI client](contracts/openapi-client/) | The picking desk reserves
stock for every order line by calling the stock API by contract: rest-openapi
turns the operationId reserveStock into the HTTP call from stock-api.json; the
log shows each reservation and one 409 for the cap that is out of stock. Needs
the openapi-server example running. | `contracts/openapi-server` running first |
| [Keycloak security](contracts/keycloak-security-rest/) | Two HTTP endpoints
on port 8081: the public one answers everyone, the protected one requires a
bearer token from a Keycloak started with camel infra whose user has the admin
role, otherwise 403; the realm, client and users are created in the Keycloak
console as the README describes. | `camel infra run keycloak` |
diff --git a/camel-jbang-example-catalog.json b/camel-jbang-example-catalog.json
index 63ecd5c..20e0ba5 100644
--- a/camel-jbang-example-catalog.json
+++ b/camel-jbang-example-catalog.json
@@ -770,7 +770,7 @@
"infraServices": [
"keycloak"
],
- "order": 3
+ "order": 4
},
{
"name": "contracts/openapi-client",
@@ -818,7 +818,7 @@
"stock-api.json"
],
"needs": "`contracts/openapi-server` running first",
- "order": 2
+ "order": 3
},
{
"name": "contracts/openapi-server",
@@ -866,6 +866,54 @@
"stock-api.json",
"stock.json"
],
+ "order": 2
+ },
+ {
+ "name": "contracts/rest-code-first",
+ "title": "REST code first",
+ "description": "The shop's order API written with the REST DSL: GET
/orders and GET /orders/{id} answer the orders as JSON (404 for an unknown id),
POST /orders binds the JSON of a new order to a Java record and answers 201
(400 without lines), and the API description generated from the operations is
served at /openapi.",
+ "level": "contracts",
+ "teaches": {
+ "components": [
+ "rest",
+ "platform-http",
+ "direct",
+ "log"
+ ],
+ "eips": [
+ "restConfiguration",
+ "rest",
+ "choice",
+ "filter",
+ "setHeader",
+ "unmarshal"
+ ],
+ "languages": [
+ "simple",
+ "groovy"
+ ],
+ "dataformats": [
+ "json"
+ ]
+ },
+ "tags": [
+ "shop",
+ "rest",
+ "code-first",
+ "binding",
+ "openapi",
+ "java-record"
+ ],
+ "bundled": true,
+ "requiresDocker": false,
+ "hasCitrusTests": true,
+ "files": [
+ "Order.java",
+ "README.md",
+ "application.properties",
+ "orders.json",
+ "rest-code-first.camel.yaml"
+ ],
"order": 1
},
{
diff --git a/contracts/README.md b/contracts/README.md
index e1b5d8b..e3803e4 100644
--- a/contracts/README.md
+++ b/contracts/README.md
@@ -5,11 +5,12 @@ An OpenAPI contract served and called, and an API protected
by Keycloak.
| Example | What you will see | Needs |
|---|---|---|
+| [REST code first](rest-code-first/) | The shop's order API written with the
REST DSL: GET /orders and GET /orders/{id} answer the orders as JSON (404 for
an unknown id), POST /orders binds the JSON of a new order to a Java record and
answers 201 (400 without lines), and the API description generated from the
operations is served at /openapi. | nothing |
| [OpenAPI server](openapi-server/) | The stock API contract first:
stock-api.json is the OpenAPI contract, the REST DSL serves its three
operations on port 8080 with request validation, GET /stock/{sku} answers from
a file or 404, POST /stock/{sku}/reserve answers 200, 409 when the stock is
short or 400 for a bad reservation, and /openapi serves the contract. | nothing
|
| [OpenAPI client](openapi-client/) | The picking desk reserves stock for
every order line by calling the stock API by contract: rest-openapi turns the
operationId reserveStock into the HTTP call from stock-api.json; the log shows
each reservation and one 409 for the cap that is out of stock. Needs the
openapi-server example running. | `contracts/openapi-server` running first |
| [Keycloak security](keycloak-security-rest/) | Two HTTP endpoints on port
8081: the public one answers everyone, the protected one requires a bearer
token from a Keycloak started with camel infra whose user has the admin role,
otherwise 403; the realm, client and users are created in the Keycloak console
as the README describes. | `camel infra run keycloak` |
-Start with [OpenAPI server](openapi-server/); the examples read best in the
order above, each one building on what the one before it set up.
+Start with [REST code first](rest-code-first/); the examples read best in the
order above, each one building on what the one before it set up.
Every example has a README that says what you will see, how it works, how to
build it step by step and what to try changing; the ones with a `test/`
directory also say how to run their test with `camel test run`.
<!-- group:end -->
diff --git a/contracts/keycloak-security-rest/metadata.json
b/contracts/keycloak-security-rest/metadata.json
index 9c54063..d0b2474 100644
--- a/contracts/keycloak-security-rest/metadata.json
+++ b/contracts/keycloak-security-rest/metadata.json
@@ -2,7 +2,7 @@
"title": "Keycloak security",
"description": "Two HTTP endpoints on port 8081: the public one answers
everyone, the protected one requires a bearer token from a Keycloak started
with camel infra whose user has the admin role, otherwise 403; the realm,
client and users are created in the Keycloak console as the README describes.",
"level": "contracts",
- "order": 3,
+ "order": 4,
"teaches": {
"components": [
"platform-http",
diff --git a/contracts/openapi-client/metadata.json
b/contracts/openapi-client/metadata.json
index 588b520..1cb29e6 100644
--- a/contracts/openapi-client/metadata.json
+++ b/contracts/openapi-client/metadata.json
@@ -2,7 +2,7 @@
"title": "OpenAPI client",
"description": "The picking desk reserves stock for every order line by
calling the stock API by contract: rest-openapi turns the operationId
reserveStock into the HTTP call from stock-api.json; the log shows each
reservation and one 409 for the cap that is out of stock. Needs the
openapi-server example running.",
"level": "contracts",
- "order": 2,
+ "order": 3,
"teaches": {
"components": [
"rest-openapi",
diff --git a/contracts/openapi-server/metadata.json
b/contracts/openapi-server/metadata.json
index 20456f6..2d9037f 100644
--- a/contracts/openapi-server/metadata.json
+++ b/contracts/openapi-server/metadata.json
@@ -2,7 +2,7 @@
"title": "OpenAPI server",
"description": "The stock API contract first: stock-api.json is the
OpenAPI contract, the REST DSL serves its three operations on port 8080 with
request validation, GET /stock/{sku} answers from a file or 404, POST
/stock/{sku}/reserve answers 200, 409 when the stock is short or 400 for a bad
reservation, and /openapi serves the contract.",
"level": "contracts",
- "order": 1,
+ "order": 2,
"teaches": {
"components": [
"rest",
diff --git a/contracts/rest-code-first/Order.java
b/contracts/rest-code-first/Order.java
new file mode 100644
index 0000000..cc39359
--- /dev/null
+++ b/contracts/rest-code-first/Order.java
@@ -0,0 +1,30 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package camel.example;
+
+import java.util.List;
+
+/**
+ * An order of the shop, as the JSON of POST /orders binds to it: the REST DSL
turns the request body into this record
+ * (bindingMode json, type camel.example.Order), and the record back into JSON
for the answer.
+ */
+public record Order(String orderId, String customer, String country, String
status, List<Line> lines) {
+
+ /** One line of an order. */
+ public record Line(String sku, int qty, double price) {
+ }
+}
diff --git a/contracts/rest-code-first/README.md
b/contracts/rest-code-first/README.md
new file mode 100644
index 0000000..fe96d6c
--- /dev/null
+++ b/contracts/rest-code-first/README.md
@@ -0,0 +1,91 @@
+# REST code first
+
+The shop's order API written with the REST DSL. The routes declare each
operation, the JSON of a new order is bound
+to a Java record, and Camel builds the API description from the operations and
serves it at `/openapi`.
+
+## What you will see
+
+```text
+INFO ... rest-code-first.camel.yaml:101 : Order ORD-1004 created with 1 line(s)
+```
+
+and the answers of the API:
+
+```shell
+$ curl http://localhost:8080/orders/ORD-1002
+{"orderId":"ORD-1002","customer":"C-207","country":"DE","status":"paid","lines":[{"sku":"CAMEL-MUG","qty":3,"price":9.5}]}
+
+$ curl -i http://localhost:8080/orders/ORD-9999
+HTTP/1.1 404 Not Found
+{"error": "unknown order ORD-9999"}
+
+$ curl -i -H 'Content-Type: application/json' -d '{"orderId": "ORD-1004",
"customer": "C-482", "country": "DK", "status": "paid", "lines": [{"sku":
"CAMEL-MUG", "qty": 1, "price": 9.5}]}' http://localhost:8080/orders
+HTTP/1.1 201 Created
+{"orderId":"ORD-1004","customer":"C-482","country":"DK","status":"paid","lines":[{"sku":"CAMEL-MUG","qty":1,"price":9.5}]}
+```
+
+## Install Camel CLI
+
+Install [JBang](https://www.jbang.dev/download/) and the Camel CLI as
described in the
+[root README](../../README.md#install-the-camel-cli); `camel --version`
confirms the install.
+
+## Run it
+
+```shell
+camel run *
+```
+
+Then call the API as shown above. `curl http://localhost:8080/openapi` shows
the API description.
+Stop the example with `ctrl` + `c`.
+
+## How it works
+
+- `rest-code-first.camel.yaml` declares the API with the REST DSL: `GET
/orders`, `GET /orders/{id}` and
+ `POST /orders`, each with an id and a description, and each sent to a
`direct` route.
+- `restConfiguration` sets `bindingMode: json`. The answers are Java objects
(a List, a Map, a record), and the
+ binding writes them as JSON. For `POST /orders`, `type: camel.example.Order`
makes the binding read the request
+ JSON into the record.
+- `Order.java` is the record, with a nested record per line. Simple reads its
fields as `${body.orderId}`.
+- An error answer, 404 or 400, is not bound (`skipBindingOnErrorCode` is on by
default), so the route writes it as
+ JSON text.
+- `apiContextPath: openapi` serves the API description that Camel generates
from the operations.
+ `application.properties` adds `camel-openapi-java` for it, which `camel run`
does not add by itself for a
+ code-first API.
+- `orders.json` holds the three orders of the shop.
+
+## Build it step by step
+
+Ask your assistant, or type it yourself, one step at a time, and run after
each:
+
+1. A REST DSL `GET /orders` that answers the orders from `orders.json`.
+2. `GET /orders/{id}`: the order with that id, or 404 with `{"error": "unknown
order <id>"}`.
+3. `POST /orders` with JSON binding to a Java record `camel.example.Order`:
400 when the order has no lines,
+ otherwise log the order and answer 201.
+4. Serve the API description at `/openapi`.
+
+## Try changing
+
+- `bindingMode: off`: the answers are no longer JSON, and the routes have to
marshal them themselves.
+- Add `PUT /orders/{id}` that changes the status of an order.
+- Compare with the [openapi-server](../openapi-server/) example, which starts
from the contract instead.
+
+## Integration testing
+
+The example comes with a test in the [Citrus](https://citrusframework.org/)
YAML DSL,
+`test/rest-code-first.citrus.it.yaml`, which the Camel CLI runs:
+
+```shell
+camel test run test/rest-code-first.citrus.it.yaml
+```
+
+The test calls each operation and checks the answers, and the API description.
+
+## Help and contributions
+
+If you hit any problem using Camel or have some feedback, then please
+[let us know](https://camel.apache.org/community/support/).
+
+We also love contributors, so
+[get involved](https://camel.apache.org/community/contributing/) :-)
+
+The Camel riders!
diff --git a/contracts/rest-code-first/application.properties
b/contracts/rest-code-first/application.properties
new file mode 100644
index 0000000..ee269d6
--- /dev/null
+++ b/contracts/rest-code-first/application.properties
@@ -0,0 +1,20 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+camel.server.port=8080
+# the API description at /openapi needs camel-openapi-java, which camel run
does not add by itself for code-first REST
+camel.jbang.dependencies=camel:openapi-java
diff --git a/contracts/rest-code-first/metadata.json
b/contracts/rest-code-first/metadata.json
new file mode 100644
index 0000000..111cc1a
--- /dev/null
+++ b/contracts/rest-code-first/metadata.json
@@ -0,0 +1,38 @@
+{
+ "title": "REST code first",
+ "description": "The shop's order API written with the REST DSL: GET
/orders and GET /orders/{id} answer the orders as JSON (404 for an unknown id),
POST /orders binds the JSON of a new order to a Java record and answers 201
(400 without lines), and the API description generated from the operations is
served at /openapi.",
+ "level": "contracts",
+ "order": 1,
+ "teaches": {
+ "components": [
+ "rest",
+ "platform-http",
+ "direct",
+ "log"
+ ],
+ "eips": [
+ "restConfiguration",
+ "rest",
+ "choice",
+ "filter",
+ "setHeader",
+ "unmarshal"
+ ],
+ "languages": [
+ "simple",
+ "groovy"
+ ],
+ "dataformats": [
+ "json"
+ ]
+ },
+ "tags": [
+ "shop",
+ "rest",
+ "code-first",
+ "binding",
+ "openapi",
+ "java-record"
+ ],
+ "bundled": true
+}
diff --git a/contracts/rest-code-first/orders.json
b/contracts/rest-code-first/orders.json
new file mode 100644
index 0000000..ceea517
--- /dev/null
+++ b/contracts/rest-code-first/orders.json
@@ -0,0 +1,5 @@
+[
+ {"orderId": "ORD-1001", "customer": "C-482", "country": "DK", "status":
"paid", "lines": [{"sku": "CAMEL-TSHIRT", "qty": 2, "price": 19.95}, {"sku":
"CAMEL-MUG", "qty": 1, "price": 9.5}]},
+ {"orderId": "ORD-1002", "customer": "C-207", "country": "DE", "status":
"paid", "lines": [{"sku": "CAMEL-MUG", "qty": 3, "price": 9.5}]},
+ {"orderId": "ORD-1003", "customer": "C-134", "country": "US", "status":
"pending", "lines": [{"sku": "CAMEL-TSHIRT", "qty": 1, "price": 19.95}, {"sku":
"CAMEL-CAP", "qty": 1, "price": 14.0}, {"sku": "CAMEL-MUG", "qty": 2, "price":
9.5}]}
+]
diff --git a/contracts/rest-code-first/rest-code-first.camel.yaml
b/contracts/rest-code-first/rest-code-first.camel.yaml
new file mode 100644
index 0000000..b5f0daa
--- /dev/null
+++ b/contracts/rest-code-first/rest-code-first.camel.yaml
@@ -0,0 +1,107 @@
+# The shop's order API, code first: the REST DSL declares each operation,
binds the JSON of a new order to a Java
+# record, and builds the API description from the operations, served at
/openapi.
+- restConfiguration:
+ bindingMode: json
+ apiContextPath: openapi
+
+- rest:
+ path: /orders
+ get:
+ - id: listOrders
+ description: All orders
+ to:
+ uri: direct:list-orders
+ - id: getOrder
+ path: "/{id}"
+ description: One order, by its id
+ to:
+ uri: direct:get-order
+ post:
+ - id: createOrder
+ description: Creates an order
+ type: camel.example.Order
+ to:
+ uri: direct:create-order
+
+# The answers are Java objects (a List, a Map, a record): the json binding
writes them as JSON
+- route:
+ id: list-orders
+ from:
+ uri: direct
+ parameters:
+ name: list-orders
+ steps:
+ - setBody:
+ expression:
+ constant:
+ expression: "resource:file:orders.json"
+ - unmarshal:
+ json:
+ library: Jackson
+
+- route:
+ id: get-order
+ from:
+ uri: direct
+ parameters:
+ name: get-order
+ steps:
+ - setBody:
+ expression:
+ constant:
+ expression: "resource:file:orders.json"
+ - unmarshal:
+ json:
+ library: Jackson
+ - setBody:
+ expression:
+ groovy:
+ expression: "body.find { it.orderId == headers.id }"
+ - filter:
+ expression:
+ simple:
+ expression: "${body} == null"
+ steps:
+ - setHeader:
+ name: CamelHttpResponseCode
+ expression:
+ constant:
+ expression: "404"
+ # an error answer is not bound to JSON (skipBindingOnErrorCode),
so it is written as JSON text
+ - setBody:
+ expression:
+ simple:
+ expression: '{"error": "unknown order ${header.id}"}'
+
+# The binding has turned the request into a camel.example.Order record
+- route:
+ id: create-order
+ from:
+ uri: direct
+ parameters:
+ name: create-order
+ steps:
+ - choice:
+ when:
+ - expression:
+ simple:
+ expression: "${body.lines} == null ||
${body.lines.isEmpty()} == true"
+ steps:
+ - setHeader:
+ name: CamelHttpResponseCode
+ expression:
+ constant:
+ expression: "400"
+ - setBody:
+ expression:
+ constant:
+ expression: '{"error": "an order needs at least one
line"}'
+ otherwise:
+ steps:
+ - log:
+ message: "Order ${body.orderId} created with
${body.lines.size()} line(s)"
+ - setHeader:
+ name: CamelHttpResponseCode
+ expression:
+ constant:
+ expression: "201"
diff --git a/contracts/rest-code-first/test/rest-code-first.citrus.it.yaml
b/contracts/rest-code-first/test/rest-code-first.citrus.it.yaml
new file mode 100644
index 0000000..b1d7cb2
--- /dev/null
+++ b/contracts/rest-code-first/test/rest-code-first.citrus.it.yaml
@@ -0,0 +1,95 @@
+name: rest-code-first-test
+description: The order API answers as the README shows
+actions:
+ - camel:
+ jbang:
+ run:
+ integration:
+ name: "rest-code-first"
+ file: "../rest-code-first.camel.yaml"
+ systemProperties:
+ file: "../application.properties"
+ resources:
+ - "Order.java"
+ - "orders.json"
+ - camel:
+ jbang:
+ verify:
+ integration: "rest-code-first"
+ logMessage: "(rest-code-first) started"
+ - http:
+ client: "http://localhost:8080"
+ sendRequest:
+ GET:
+ path: "/orders/ORD-1002"
+ # the client's default Content-Type text/plain makes the json
binding skip writing the answer
+ contentType: "application/json"
+ - http:
+ client: "http://localhost:8080"
+ receiveResponse:
+ response:
+ status: "200"
+ validate:
+ - jsonPath:
+ - expression: "$.customer"
+ value: "C-207"
+ - http:
+ client: "http://localhost:8080"
+ sendRequest:
+ GET:
+ path: "/orders/ORD-9999"
+ # the client's default Content-Type text/plain makes the json
binding skip writing the answer
+ contentType: "application/json"
+ - http:
+ client: "http://localhost:8080"
+ receiveResponse:
+ response:
+ status: "404"
+ validate:
+ - jsonPath:
+ - expression: "$.error"
+ value: "unknown order ORD-9999"
+ - http:
+ client: "http://localhost:8080"
+ sendRequest:
+ POST:
+ path: "/orders"
+ contentType: "application/json"
+ body:
+ data: '{"orderId": "ORD-1004", "customer": "C-482", "country":
"DK", "status": "paid", "lines": [{"sku": "CAMEL-MUG", "qty": 1, "price":
9.5}]}'
+ - http:
+ client: "http://localhost:8080"
+ receiveResponse:
+ response:
+ status: "201"
+ validate:
+ - jsonPath:
+ - expression: "$.orderId"
+ value: "ORD-1004"
+ - http:
+ client: "http://localhost:8080"
+ sendRequest:
+ POST:
+ path: "/orders"
+ contentType: "application/json"
+ body:
+ data: '{"orderId": "ORD-1005", "customer": "C-482", "country":
"DK", "status": "paid", "lines": []}'
+ - http:
+ client: "http://localhost:8080"
+ receiveResponse:
+ response:
+ status: "400"
+ - http:
+ client: "http://localhost:8080"
+ sendRequest:
+ GET:
+ path: "/openapi"
+ - http:
+ client: "http://localhost:8080"
+ receiveResponse:
+ response:
+ status: "200"
+ validate:
+ - jsonPath:
+ - expression: "$.paths['/orders/{id}'].get.operationId"
+ value: "getOrder"