You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@camel.apache.org by da...@apache.org on 2026/10/18 13:12:34 UTC
(camel-jbang-examples) branch CAMEL-25765-rest-code-first created (now 0d19f7e)
This is an automated email from the ASF dual-hosted git repository.
davsclaus pushed a change to branch CAMEL-25765-rest-code-first
in repository https://gitbox.apache.org/repos/asf/camel-jbang-examples.git
at 0d19f7e CAMEL-25765: contracts/rest-code-first - the REST DSL with JSON binding to a Java record and a generated API description
This branch includes the following new commits:
new 0d19f7e CAMEL-25765: contracts/rest-code-first - the REST DSL with JSON binding to a Java record and a generated API description
The 1 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails. The revisions
listed as "add" were already present in the repository and have only
been added to this reference.
(camel-jbang-examples) 01/01: CAMEL-25765: contracts/rest-code-first - the REST DSL with JSON binding to a Java record and a generated API description
Posted by da...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
davsclaus pushed a commit to branch CAMEL-25765-rest-code-first
in repository https://gitbox.apache.org/repos/asf/camel-jbang-examples.git
commit 0d19f7e5c9b5ef526bbf88cd7a16d36923d07fbd
Author: Claus Ibsen <cl...@gmail.com>
AuthorDate: Sun Oct 18 15:12:26 2026 +0200
CAMEL-25765: 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.
Co-Authored-By: Claude Opus 5.5 (1M context) <no...@anthropic.com>
Claude-Session: https://claude.ai/code/session_01STT6whBgK1AqsSsUKrnE8m
---
.github/workflows/build.yml | 2 +
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, 444 insertions(+), 6 deletions(-)
diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index 05cc0cd..8251be4 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -86,6 +86,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
diff --git a/README.md b/README.md
index 290139b..e13e217 100644
--- a/README.md
+++ b/README.md
@@ -114,6 +114,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 0be9e36..789b311 100644
--- a/camel-jbang-example-catalog.json
+++ b/camel-jbang-example-catalog.json
@@ -657,7 +657,7 @@
"infraServices": [
"keycloak"
],
- "order": 3
+ "order": 4
},
{
"name": "contracts/openapi-client",
@@ -705,7 +705,7 @@
"stock-api.json"
],
"needs": "`contracts/openapi-server` running first",
- "order": 2
+ "order": 3
},
{
"name": "contracts/openapi-server",
@@ -753,6 +753,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..186bbd7
--- /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()}"
+ 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"