diff --git a/flash-extensions/flash-ext-openapi/src/main/java/dev/relism/flash/ext/openapi/OpenApiBuilder.java b/flash-extensions/flash-ext-openapi/src/main/java/dev/relism/flash/ext/openapi/OpenApiBuilder.java index a420587..63c18c4 100644 --- a/flash-extensions/flash-ext-openapi/src/main/java/dev/relism/flash/ext/openapi/OpenApiBuilder.java +++ b/flash-extensions/flash-ext-openapi/src/main/java/dev/relism/flash/ext/openapi/OpenApiBuilder.java @@ -176,7 +176,8 @@ public final class OpenApiBuilder { } if (responseByCode.isEmpty()) { - responseByCode.put(200, Map.of("description", "OK")); + // Mutable: contributors merge descriptions and headers into it. + responseByCode.put(200, new LinkedHashMap<>(Map.of("description", "OK"))); } applyContributorResponses(responseByCode, cls); diff --git a/flash-extensions/flash-ext-openapi/src/test/java/dev/relism/flash/ext/openapi/OpenApiBuilderTest.java b/flash-extensions/flash-ext-openapi/src/test/java/dev/relism/flash/ext/openapi/OpenApiBuilderTest.java index 4362b46..b3f79aa 100644 --- a/flash-extensions/flash-ext-openapi/src/test/java/dev/relism/flash/ext/openapi/OpenApiBuilderTest.java +++ b/flash-extensions/flash-ext-openapi/src/test/java/dev/relism/flash/ext/openapi/OpenApiBuilderTest.java @@ -101,6 +101,15 @@ class OpenApiBuilderTest { } } + @GET("/plain") + @ApiOperation(summary = "Plain") + static class PlainHandler extends RequestHandler { + @Override + public Object handle(Request request, Response response) { + return null; + } + } + @Schema(name = "UserDTO", title = "User model", description = "DTO", deprecated = true) @JsonIgnoreProperties({"ignoredByType"}) static class UserDto { @@ -341,6 +350,34 @@ class OpenApiBuilderTest { assertEquals("new", header.get("description")); } + @Test + void contributor_merges_into_the_default_response_of_an_operation_without_annotations() { + OpenApiBuilder b = new OpenApiBuilder(); + OpenApiContributorRegistry registry = new OpenApiContributorRegistry(); + registry.add(new OpenApiContributor() { + @Override + public OpenApiOperationContribution operationFor(Class handlerClass) { + return OpenApiOperationContribution.builder() + .allResponses(OpenApiResponseContribution.builder() + .header("X-Trace", Map.of("schema", Map.of("type", "string"))) + .build()) + .response(401, OpenApiResponseContribution.of("Authentication required")) + .build(); + } + }); + b.setContributorRegistry(registry); + b.addOperation(OpenApiBuilder.routeOf(PlainHandler.class), PlainHandler.class.getAnnotation(ApiOperation.class), PlainHandler.class); + + Map spec = b.build(); + Map responses = cast(getOperation(spec, "/plain", "get").get("responses")); + Map resp200 = cast(responses.get("200")); + Map resp401 = cast(responses.get("401")); + Map headers = cast(resp200.get("headers")); + assertEquals("OK", resp200.get("description")); + assertTrue(headers.containsKey("X-Trace")); + assertEquals("Authentication required", resp401.get("description")); + } + private static Map getOperation(Map spec, String path, String method) { Map paths = cast(spec.get("paths")); Map pathItem = cast(paths.get(path));