davsclaus opened a new pull request, #28059: URL: https://github.com/apache/camel/pull/28059
## What [CAMEL-25749](https://issues.apache.org/jira/browse/CAMEL-25749), step 1 of the plan on [CAMEL-24844](https://issues.apache.org/jira/browse/CAMEL-24844), as agreed on the issue. A new helper in camel-support, `org.apache.camel.support.BodyFacts`. It says what a message body is, which step made it so, and what to write so that a step that failed on it gets the body it needs: the facts a person, or a model in a dev loop, otherwise gets from a debugger. ```java public static String describe(Exchange exchange); // what the body is, and the step that set it public static String hint(Exchange exchange, Need need); // what to write: DATA, A_BODY public static String hint(Exchange exchange, Need need, String target); // BYTES: the Content-Type or the file name written public static String dataFormatOf(String contentType); // json, jacksonXml, csv, yaml or null public enum Need { DATA, BYTES, A_BODY } ``` - **`describe`** names the body in plain words: text, bytes, a stream, the file as read, a Map, a List, an iterator, an object. From the message history it adds the step that set it. That is the last node reached with a body of another type, so "set by unmarshal at orders.yaml:9". When every node saw the same type: "unchanged since from: file://orders". With the message history off, the step is left out. URIs are sanitized. - **`hint`** says what to write for what the failing step needed. Each `Need` constant, and the class Javadoc's table of hints by need and by body, document it: - `DATA`: the step reads fields. Unmarshal, in the data format the Content-Type, the file name or the text names (with xpath for XML, rows for CSV). For a Map, its keys. For an iterator, split it. - `BYTES`: the step writes or sends the payload. Marshal, in the data format the target's Content-Type or file name names, or set the body to the text. - `A_BODY`: the step needs a body at all. Read the data first: setBody with constant: resource:file:..., poll:, pollEnrich or a from: consumer. - When nothing names a data format, it lists the usual ones. A hint is null when the body is not why the step failed. - **No camel-api change.** Callers pass the sentence to the hint argument `InvalidPayloadException` already has, and to the message of `NoTypeConversionAvailableException`. ## The sentences that existed move onto it | where | JIRA | the new sentence (example) | |---|---|---| | http producer request | CAMEL-25309 | `the body is a Map (LinkedHashMap), which is not an HTTP request body: marshal it first (marshal: jacksonXml, for the Content-Type application/xml), or set the body to the text` | | platform-http reply | CAMEL-25751 | `... which is not an HTTP response body: marshal it first (...), or set the body to the text, as the last step of the route; or let the REST binding write it (bindingMode: json)` | | file write | CAMEL-25751 | `... which is not file content: marshal it first (marshal: csv, for the file name lines.csv), or set the body to the text` | | Groovy on text | CAMEL-25330 | `the body is bytes (a byte[]), not parsed data: unmarshal it first (unmarshal: json) to read its fields` | | unmarshal on a null body | CAMEL-25285 | `Cannot unmarshal using jackson: the body is null, so there is nothing to unmarshal: read the data first - setBody with constant: ...; or set allowNullBody=true to skip a null body.` | | jsonpath, jq on a null body | CAMEL-24838 | `The jsonpath expression got no message body to evaluate: the body is null: read the data first - ...` | | a missing key of the body Map | CAMEL-25322 | `(the Map has no key status; the body is a Map (LinkedHashMap), set by split (...): its keys are: sku, qty)` | With the message history on (the dev profile), each sentence also names the step that set the body. That is what a missing key inside a split over an order's lines needs: the body there is a line, not the order. The three copies of the Content-Type to data format mapping that #28048 added are gone. Behaviour changes worth noting: - A writing failure on an object, or on an iterator, now gets a hint too, not only a Map or a List. - "unmarshal on the variable X" keeps its own sentence: it is not about the message body. ## Tests - **`BodyFactsTest` (camel-core):** the plain words, each hint per need with the data format signals, `dataFormatOf`, and the message history in a route ("set by setBody", "unchanged since from: direct://unchanged"). - **The tests of the moved sentences**, updated to the one wording: `GroovyMissingPropertyHintTest`, `JsonPathNullBodyHintTest`, `JqNullBodyHintTest`, `HttpProducerMapBodyTest`, `VertxPlatformHttpMapResponseTest`, `FileProducerMapBodyTest`, `UnmarshalProcessorTest`, `SimpleSyntaxHintsTest`. - **Local runs**, against camel-support and camel-bean from this branch through a scratch local repository, 0 failures: | module | tests | |---|---| | camel-support | 139 | | camel-bean | 9 | | camel-groovy | 119 | | camel-jsonpath | 133 | | camel-jq | 45 | | camel-file | 28 | | camel-platform-http-vertx | 160 | | camel-http | 297 | | camel-core (targeted classes) | 41 | ## Next The second PR gives a sentence to the failures that have none today: `${body.qty}` on the stream cache of an HTTP request, and `${body.size()}` on sql's StreamList iterator. _Claude Code on behalf of davsclaus_ 🤖 Generated with [Claude Code](https://claude.com/claude-code) -- This is an automated message from the Apache Git Service. To respond to the message, please log on to GitHub and use the URL above to go to the specific comment. To unsubscribe, e-mail: [email protected] For queries about this service, please contact Infrastructure at: [email protected]
