gnodet-bot commented on code in PR #27960:
URL: https://github.com/apache/camel/pull/27960#discussion_r4300265300
##########
dsl/camel-jbang/camel-jbang-mcp/src/main/java/org/apache/camel/dsl/jbang/core/commands/mcp/OpenApiTools.java:
##########
@@ -407,6 +309,110 @@ private List<OperationStub> collectOperationStubs(OpenAPI
openAPI) {
return stubs;
}
+ /**
+ * One mock file per path of the spec. For an operation without a route,
camel-rest-openapi serves the first
+ * resource of mockIncludePattern (default classpath:camel-mock/**) whose
location without extension ends with the
+ * request path, whatever the method: a path parameter is matched by its
value, so {@code /pets/{petId}} takes one
+ * file per value, and the listed file carries the sample value {@code 1},
such as {@code camel-mock/pets/1.json}.
+ */
+ private List<MockFileInfo> collectMockFiles(OpenAPI openAPI) {
+ List<MockFileInfo> files = new ArrayList<>();
+ if (openAPI.getPaths() == null) {
+ return files;
+ }
+ for (Map.Entry<String, PathItem> pathEntry :
openAPI.getPaths().entrySet()) {
+ String path = pathEntry.getKey();
+ Map<PathItem.HttpMethod, Operation> operations =
pathEntry.getValue().readOperationsMap();
+ if (operations == null || operations.isEmpty()) {
+ continue;
+ }
+
+ List<String> operationIds = new ArrayList<>();
+ Map<String, String> withoutFile = new LinkedHashMap<>();
+ String responseType = null;
+ String example = null;
+ for (Map.Entry<PathItem.HttpMethod, Operation> opEntry :
operations.entrySet()) {
+ String method = opEntry.getKey().name();
+ Operation op = opEntry.getValue();
+ operationIds.add(operationId(path, opEntry.getKey(), op));
+
+ if (op.getResponses() != null) {
+ Map.Entry<String, ApiResponse> success =
findFirstSuccessResponse(op.getResponses());
+ if (responseType == null && success != null &&
success.getValue().getContent() != null
+ && !success.getValue().getContent().isEmpty()) {
+ responseType =
success.getValue().getContent().keySet().iterator().next();
+ }
+ }
+ if (BODY_VERBS.contains(method)) {
+ withoutFile.put(method, "echoes the request body with HTTP
200");
+ continue;
+ }
+ ApiResponse ok = op.getResponses() != null ?
op.getResponses().get("200") : null;
+ String opExample = firstExample(ok);
+ if (opExample != null) {
+ withoutFile.put(method, "answers the example of its '200'
response with HTTP 200 when the request's"
+ + " Content-Type or Accept header
names a media type of that response"
+ + " (Accept */* takes
application/json, then application/xml), otherwise"
+ + " HTTP 204 with an empty body");
+ if (example == null) {
+ example = opExample;
+ }
+ } else {
+ // no example in the '200' response, or no '200' response
at all (CAMEL-25663): an empty answer
+ withoutFile.put(method, "answers HTTP 204 with an empty
body");
+ }
+ }
+
+ String ext = responseType != null && responseType.contains("xml")
? ".xml" : ".json";
+ StringBuilder note = new StringBuilder("Served with HTTP 200 for
any method of ").append(path)
+ .append(" without a route.");
+ // the component matches the request path, not the template: a
path parameter takes a sample value
+ String sample = path.replaceAll("\\{[^}]+}",
SAMPLE_PATH_PARAMETER_VALUE);
+ if (path.contains("{")) {
+ note.append(" Camel matches the request path, so the file name
carries the value the request uses:"
+ + " camel-mock")
+ .append(sample).append(ext).append(" answers
").append(sample)
+ .append(", add one file per value.");
+ }
+ note.append(" Without the file, ");
+ withoutFile.forEach((method, answer) ->
note.append(method).append(' ').append(answer).append("; "));
+ note.setLength(note.length() - 2);
+ note.append('.');
+
+ // the component matches a resource whose location without
extension ends with the request path without its
+ // leading slash; for the root path that is the empty string, so
any mock file matches
+ String fileName = "/".equals(path) ? "camel-mock/index" + ext :
"camel-mock" + sample + ext;
+ if ("/".equals(path)) {
+ note.append(" Camel matches the root path to any mock file
(the first one found), so with more than"
+ + " one file the one that answers / is not
predictable.");
+ }
+ files.add(new MockFileInfo(fileName, path, operationIds, example,
note.toString()));
+ }
+ return files;
+ }
+
+ /**
+ * The example camel-rest-openapi answers in mock mode for an operation
without mock file: the example, or the first
+ * of the examples, of a media type of its "200" response.
Review Comment:
🔍 **Nit:** `firstExample()` iterates all media types and returns the first
non-null example, regardless of content type. The Javadoc says it's "the
example camel-rest-openapi answers" — but at runtime, `loadMockData` does
content-type negotiation: it picks the media type that matches the request's
`Content-Type`/`Accept`, then takes the example from that specific media type.
When a `200` response has multiple media types with different examples (e.g.
`application/json` and `application/xml` with distinct values),
`firstExample()` may return the JSON example while an `Accept: application/xml`
request would receive the XML one.
The `exampleContent` field is advisory (it shows a sample to the MCP caller,
not what Camel necessarily serves), so this is a documentation inaccuracy
rather than a runtime bug. Suggest:
```suggestion
* An example from the "200" response of the spec, used as a preview
hint. At runtime,
* camel-rest-openapi picks the example from the media type that matches
the request's
* Content-Type or Accept header; this method returns the first non-null
example across
* all media types of the response.
--
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]