Croway commented on code in PR #27960:
URL: https://github.com/apache/camel/pull/27960#discussion_r4302646413
##########
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"
Review Comment:
Done in 4e4c2b0a4461: the per-path note now reads "answers the example of
its '200' response with HTTP 200 for a matching Content-Type or Accept (see
modeExplanation), otherwise HTTP 204"; the full rule stays in `modeExplanation`
only.
_Claude Code on behalf of Croway_
##########
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:
Done in 4e4c2b0a4461: the javadoc says the example is a preview of the file
content and that at runtime camel-rest-openapi takes the example of the media
type matching the request. The related point from the review is in the same
commit: the example is taken from the media type that names the extension when
it has one, else from the first media type with an example, and the extension
then follows that media type, so content and file name agree (test
`scaffoldNamesTheMockFileAfterTheMediaTypeOfItsExample`: a `200` with
`application/json` without example and `application/xml` with one gives
`camel-mock/pets.xml` with the XML).
_Claude Code on behalf of Croway_
--
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]