|
16 | 16 |
|
17 | 17 | package org.springframework.boot.actuate.autoconfigure.endpoint.web.documentation;
|
18 | 18 |
|
| 19 | +import java.util.ArrayList; |
| 20 | +import java.util.Arrays; |
19 | 21 | import java.util.Collection;
|
| 22 | +import java.util.List; |
20 | 23 |
|
21 | 24 | import org.junit.Before;
|
22 | 25 | import org.junit.Rule;
|
|
34 | 37 | import org.springframework.context.annotation.Bean;
|
35 | 38 | import org.springframework.context.annotation.Configuration;
|
36 | 39 | import org.springframework.context.annotation.Import;
|
| 40 | +import org.springframework.http.MediaType; |
37 | 41 | import org.springframework.restdocs.JUnitRestDocumentation;
|
| 42 | +import org.springframework.restdocs.payload.FieldDescriptor; |
38 | 43 | import org.springframework.restdocs.payload.JsonFieldType;
|
39 | 44 | import org.springframework.test.context.junit4.SpringRunner;
|
40 | 45 | import org.springframework.test.web.reactive.server.WebTestClient;
|
| 46 | +import org.springframework.web.bind.annotation.PostMapping; |
| 47 | +import org.springframework.web.bind.annotation.RestController; |
41 | 48 | import org.springframework.web.reactive.function.server.RequestPredicates;
|
42 | 49 | import org.springframework.web.reactive.function.server.RouterFunction;
|
43 | 50 | import org.springframework.web.reactive.function.server.RouterFunctions;
|
@@ -77,49 +84,88 @@ public void webTestClient() {
|
77 | 84 |
|
78 | 85 | @Test
|
79 | 86 | public void mappings() throws Exception {
|
| 87 | + List<FieldDescriptor> requestMappingConditions = Arrays.asList( |
| 88 | + requestMappingConditionField("") |
| 89 | + .description("Details of the request mapping conditions.") |
| 90 | + .optional(), |
| 91 | + requestMappingConditionField(".consumes") |
| 92 | + .description("Details of the consumes condition"), |
| 93 | + requestMappingConditionField(".consumes.[].mediaType") |
| 94 | + .description("Consumed media type."), |
| 95 | + requestMappingConditionField(".consumes.[].negated") |
| 96 | + .description("Whether the media type is negated."), |
| 97 | + requestMappingConditionField(".headers") |
| 98 | + .description("Details of the headers condition."), |
| 99 | + requestMappingConditionField(".headers.[].name") |
| 100 | + .description("Name of the header."), |
| 101 | + requestMappingConditionField(".headers.[].value") |
| 102 | + .description("Required value of the header, if any."), |
| 103 | + requestMappingConditionField(".headers.[].negated") |
| 104 | + .description("Whether the value is negated."), |
| 105 | + requestMappingConditionField(".methods") |
| 106 | + .description("HTTP methods that are handled."), |
| 107 | + requestMappingConditionField(".params") |
| 108 | + .description("Details of the params condition."), |
| 109 | + requestMappingConditionField(".params.[].name") |
| 110 | + .description("Name of the parameter."), |
| 111 | + requestMappingConditionField(".params.[].value") |
| 112 | + .description("Required value of the parameter, if any."), |
| 113 | + requestMappingConditionField(".params.[].negated") |
| 114 | + .description("Whether the value is negated."), |
| 115 | + requestMappingConditionField(".patterns").description( |
| 116 | + "Patterns identifying the paths handled by the mapping."), |
| 117 | + requestMappingConditionField(".produces") |
| 118 | + .description("Details of the produces condition."), |
| 119 | + requestMappingConditionField(".produces.[].mediaType") |
| 120 | + .description("Produced media type."), |
| 121 | + requestMappingConditionField(".produces.[].negated") |
| 122 | + .description("Whether the media type is negated.")); |
| 123 | + List<FieldDescriptor> handlerMethod = Arrays.asList( |
| 124 | + fieldWithPath("*.[].details.handlerMethod").optional() |
| 125 | + .type(JsonFieldType.OBJECT) |
| 126 | + .description("Details of the method, if any, " |
| 127 | + + "that will handle requests to this mapping."), |
| 128 | + fieldWithPath("*.[].details.handlerMethod.className") |
| 129 | + .type(JsonFieldType.STRING) |
| 130 | + .description("Fully qualified name of the class of the method."), |
| 131 | + fieldWithPath("*.[].details.handlerMethod.name") |
| 132 | + .type(JsonFieldType.STRING).description("Name of the method."), |
| 133 | + fieldWithPath("*.[].details.handlerMethod.descriptor") |
| 134 | + .type(JsonFieldType.STRING) |
| 135 | + .description("Descriptor of the method as specified in the Java " |
| 136 | + + "Language Specification.")); |
| 137 | + List<FieldDescriptor> handlerFunction = Arrays.asList( |
| 138 | + fieldWithPath("*.[].details.handlerFunction").optional() |
| 139 | + .type(JsonFieldType.OBJECT) |
| 140 | + .description("Details of the function, if any, that will handle " |
| 141 | + + "requests to this mapping."), |
| 142 | + fieldWithPath("*.[].details.handlerFunction.className") |
| 143 | + .type(JsonFieldType.STRING).description( |
| 144 | + "Fully qualified name of the class of the function.")); |
| 145 | + List<FieldDescriptor> dispatcherHandlerFields = new ArrayList<>(Arrays.asList( |
| 146 | + fieldWithPath("*") |
| 147 | + .description("Dispatcher handler mappings, if any, keyed by " |
| 148 | + + "dispatcher handler bean name."), |
| 149 | + fieldWithPath("*.[].details").optional().type(JsonFieldType.OBJECT) |
| 150 | + .description("Additional implementation-specific " |
| 151 | + + "details about the mapping. Optional."), |
| 152 | + fieldWithPath("*.[].handler").description("Handler for the mapping."), |
| 153 | + fieldWithPath("*.[].predicate") |
| 154 | + .description("Predicate for the mapping."))); |
| 155 | + dispatcherHandlerFields.addAll(requestMappingConditions); |
| 156 | + dispatcherHandlerFields.addAll(handlerMethod); |
| 157 | + dispatcherHandlerFields.addAll(handlerFunction); |
80 | 158 | this.client.get().uri("/actuator/mappings").exchange().expectStatus().isOk()
|
81 | 159 | .expectBody()
|
82 | 160 | .consumeWith(document("mappings",
|
83 | 161 | responseFields(
|
84 | 162 | beneathPath("contexts.*.mappings.dispatcherHandlers")
|
85 | 163 | .withSubsectionId("dispatcher-handlers"),
|
86 |
| - fieldWithPath("*").description( |
87 |
| - "Dispatcher handler mappings, if any, keyed by " |
88 |
| - + "dispatcher handler bean name."), |
89 |
| - fieldWithPath("*.[].handler") |
90 |
| - .description("Handler for the mapping."), |
91 |
| - fieldWithPath("*.[].predicate") |
92 |
| - .description("Predicate for the mapping."), |
93 |
| - fieldWithPath("*.[].details").optional() |
94 |
| - .type(JsonFieldType.OBJECT) |
95 |
| - .description("Additional implementation-specific " |
96 |
| - + "details about the mapping. Optional."), |
97 |
| - fieldWithPath("*.[].details.handlerMethod").optional() |
98 |
| - .type(JsonFieldType.OBJECT) |
99 |
| - .description("Details of the method, if any, " |
100 |
| - + "that will handle requests to " |
101 |
| - + "this mapping."), |
102 |
| - fieldWithPath("*.[].details.handlerMethod.className") |
103 |
| - .type(JsonFieldType.STRING) |
104 |
| - .description("Fully qualified name of the class" |
105 |
| - + " of the method."), |
106 |
| - fieldWithPath("*.[].details.handlerMethod.name") |
107 |
| - .type(JsonFieldType.STRING) |
108 |
| - .description("Name of the method."), |
109 |
| - fieldWithPath("*.[].details.handlerMethod.descriptor") |
110 |
| - .type(JsonFieldType.STRING) |
111 |
| - .description("Descriptor of the method as " |
112 |
| - + "specified in the Java Language " |
113 |
| - + "Specification."), |
114 |
| - fieldWithPath("*.[].details.handlerFunction") |
115 |
| - .optional().type(JsonFieldType.OBJECT) |
116 |
| - .description("Details of the function, if any, " |
117 |
| - + "that will handle requests to this " |
118 |
| - + "mapping."), |
119 |
| - fieldWithPath("*.[].details.handlerFunction.className") |
120 |
| - .type(JsonFieldType.STRING).description( |
121 |
| - "Fully qualified name of the class of " |
122 |
| - + "the function.")))); |
| 164 | + dispatcherHandlerFields))); |
| 165 | + } |
| 166 | + |
| 167 | + private FieldDescriptor requestMappingConditionField(String path) { |
| 168 | + return fieldWithPath("*.[].details.requestMappingConditions" + path); |
123 | 169 | }
|
124 | 170 |
|
125 | 171 | @Configuration
|
@@ -149,6 +195,22 @@ public RouterFunction<ServerResponse> exampleRouter() {
|
149 | 195 | (request) -> ServerResponse.ok().build());
|
150 | 196 | }
|
151 | 197 |
|
| 198 | + @Bean |
| 199 | + public ExampleController exampleController() { |
| 200 | + return new ExampleController(); |
| 201 | + } |
| 202 | + |
| 203 | + } |
| 204 | + |
| 205 | + @RestController |
| 206 | + private static class ExampleController { |
| 207 | + |
| 208 | + @PostMapping(path = "/", consumes = { MediaType.APPLICATION_JSON_VALUE, |
| 209 | + "!application/xml" }, produces = MediaType.TEXT_PLAIN_VALUE, headers = "X-Custom=Foo", params = "a!=alpha") |
| 210 | + public String example() { |
| 211 | + return "Hello World"; |
| 212 | + } |
| 213 | + |
152 | 214 | }
|
153 | 215 |
|
154 | 216 | }
|
0 commit comments