Skip to content

Commit a5fc911

Browse files
author
bnasslahsen
committed
improve headers supports
1 parent ebd90ec commit a5fc911

File tree

7 files changed

+206
-9
lines changed

7 files changed

+206
-9
lines changed

springdoc-openapi-common/src/main/java/org/springdoc/core/AbstractRequestBuilder.java

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@
4949
import io.swagger.v3.oas.models.media.Content;
5050
import io.swagger.v3.oas.models.media.MediaType;
5151
import io.swagger.v3.oas.models.media.Schema;
52+
import io.swagger.v3.oas.models.media.StringSchema;
5253
import io.swagger.v3.oas.models.parameters.Parameter;
5354
import io.swagger.v3.oas.models.parameters.RequestBody;
5455
import org.apache.commons.lang3.StringUtils;
@@ -84,7 +85,7 @@ public abstract class AbstractRequestBuilder {
8485
private static final List<Class> PARAM_TYPES_TO_IGNORE = new ArrayList<>();
8586

8687
// using string litterals to support both validation-api v1 and v2
87-
private static final String[] ANNOTATIONS_FOR_REQUIRED = { NotNull.class.getName(), org.springframework.web.bind.annotation.RequestBody.class.getName(),"javax.validation.constraints.NotBlank", "javax.validation.constraints.NotEmpty" };
88+
private static final String[] ANNOTATIONS_FOR_REQUIRED = { NotNull.class.getName(), org.springframework.web.bind.annotation.RequestBody.class.getName(), "javax.validation.constraints.NotBlank", "javax.validation.constraints.NotEmpty" };
8889

8990
private static final String POSITIVE_OR_ZERO = "javax.validation.constraints.PositiveOrZero";
9091

@@ -220,6 +221,16 @@ private LinkedHashMap<String, Parameter> getParameterLinkedHashMap(Components co
220221
map.put(entry.getKey(), parameter);
221222
}
222223
}
224+
225+
for (Map.Entry<String, String> entry : methodAttributes.getHeaders().entrySet()) {
226+
Parameter parameter = new Parameter().in(ParameterIn.HEADER.toString()).name(entry.getKey()).schema(new StringSchema().addEnumItem(entry.getValue()));
227+
if (map.containsKey(entry.getKey())){
228+
parameter = map.get(entry.getKey());
229+
parameter.getSchema().addEnumItemObject(entry.getValue());
230+
parameter.setSchema(parameter.getSchema());
231+
}
232+
map.put(entry.getKey(), parameter);
233+
}
223234
return map;
224235
}
225236

springdoc-openapi-common/src/main/java/org/springdoc/core/MethodAttributes.java

Lines changed: 22 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
package org.springdoc.core;
2020

2121
import java.lang.reflect.Method;
22+
import java.util.LinkedHashMap;
2223

2324
import com.fasterxml.jackson.annotation.JsonView;
2425
import org.apache.commons.lang3.ArrayUtils;
@@ -52,15 +53,19 @@ public class MethodAttributes {
5253

5354
private String defaultProducesMediaType;
5455

56+
private LinkedHashMap<String, String> headers;
57+
5558
public MethodAttributes(String[] methodProducesNew, String defaultConsumesMediaType, String defaultProducesMediaType) {
5659
this.methodProduces = methodProducesNew;
5760
this.defaultConsumesMediaType = defaultConsumesMediaType;
5861
this.defaultProducesMediaType = defaultProducesMediaType;
62+
this.headers = new LinkedHashMap<>();
5963
}
6064

6165
public MethodAttributes(String defaultConsumesMediaType, String defaultProducesMediaType) {
6266
this.defaultConsumesMediaType = defaultConsumesMediaType;
6367
this.defaultProducesMediaType = defaultProducesMediaType;
68+
this.headers = new LinkedHashMap<>();
6469
}
6570

6671
public String[] getClassProduces() {
@@ -91,39 +96,39 @@ public String[] getMethodConsumes() {
9196
public void calculateConsumesProduces(Method method) {
9297
PostMapping reqPostMappingMethod = AnnotatedElementUtils.findMergedAnnotation(method, PostMapping.class);
9398
if (reqPostMappingMethod != null) {
94-
fillMethods(reqPostMappingMethod.produces(), reqPostMappingMethod.consumes());
99+
fillMethods(reqPostMappingMethod.produces(), reqPostMappingMethod.consumes(), reqPostMappingMethod.headers());
95100
return;
96101
}
97102
GetMapping reqGetMappingMethod = AnnotatedElementUtils.findMergedAnnotation(method, GetMapping.class);
98103
if (reqGetMappingMethod != null) {
99-
fillMethods(reqGetMappingMethod.produces(), reqGetMappingMethod.consumes());
104+
fillMethods(reqGetMappingMethod.produces(), reqGetMappingMethod.consumes(), reqGetMappingMethod.headers());
100105
return;
101106
}
102107
DeleteMapping reqDeleteMappingMethod = AnnotatedElementUtils.findMergedAnnotation(method, DeleteMapping.class);
103108
if (reqDeleteMappingMethod != null) {
104-
fillMethods(reqDeleteMappingMethod.produces(), reqDeleteMappingMethod.consumes());
109+
fillMethods(reqDeleteMappingMethod.produces(), reqDeleteMappingMethod.consumes(), reqDeleteMappingMethod.headers());
105110
return;
106111
}
107112
PutMapping reqPutMappingMethod = AnnotatedElementUtils.findMergedAnnotation(method, PutMapping.class);
108113
if (reqPutMappingMethod != null) {
109-
fillMethods(reqPutMappingMethod.produces(), reqPutMappingMethod.consumes());
114+
fillMethods(reqPutMappingMethod.produces(), reqPutMappingMethod.consumes(), reqPutMappingMethod.headers());
110115
return;
111116
}
112117
RequestMapping reqMappingMethod = AnnotatedElementUtils.findMergedAnnotation(method, RequestMapping.class);
113118
RequestMapping reqMappingClass = AnnotatedElementUtils.findMergedAnnotation(method.getDeclaringClass(), RequestMapping.class);
114119

115120
if (reqMappingMethod != null && reqMappingClass != null) {
116-
fillMethods(ArrayUtils.addAll(reqMappingMethod.produces(), reqMappingClass.produces()), ArrayUtils.addAll(reqMappingMethod.consumes(), reqMappingClass.consumes()));
121+
fillMethods(ArrayUtils.addAll(reqMappingMethod.produces(), reqMappingClass.produces()), ArrayUtils.addAll(reqMappingMethod.consumes(), reqMappingClass.consumes()), reqMappingMethod.headers());
117122
}
118123
else if (reqMappingMethod != null) {
119-
fillMethods(reqMappingMethod.produces(), reqMappingMethod.consumes());
124+
fillMethods(reqMappingMethod.produces(), reqMappingMethod.consumes(), reqMappingMethod.headers());
120125
}
121126
else if (reqMappingClass != null) {
122-
fillMethods(reqMappingClass.produces(), reqMappingClass.consumes());
127+
fillMethods(reqMappingClass.produces(), reqMappingClass.consumes(), reqMappingClass.headers());
123128
}
124129
}
125130

126-
private void fillMethods(String[] produces, String[] consumes) {
131+
private void fillMethods(String[] produces, String[] consumes, String[] headers) {
127132
if (ArrayUtils.isNotEmpty(produces))
128133
methodProduces = produces;
129134
else if (ArrayUtils.isNotEmpty(classProduces))
@@ -138,6 +143,11 @@ else if (ArrayUtils.isNotEmpty(classConsumes))
138143
else
139144
methodConsumes = new String[] { defaultConsumesMediaType };
140145

146+
if (ArrayUtils.isNotEmpty(headers))
147+
for (String header : headers) {
148+
String[] keyValueHeader = header.split("=");
149+
this.headers.put(keyValueHeader[0], keyValueHeader[1]);
150+
}
141151
}
142152

143153
public boolean isMethodOverloaded() {
@@ -173,4 +183,8 @@ public JsonView getJsonViewAnnotationForRequestBody() {
173183
public void setJsonViewAnnotationForRequestBody(JsonView jsonViewAnnotationForRequestBody) {
174184
this.jsonViewAnnotationForRequestBody = jsonViewAnnotationForRequestBody;
175185
}
186+
187+
public LinkedHashMap<String, String> getHeaders() {
188+
return headers;
189+
}
176190
}
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
/*
2+
*
3+
* * Copyright 2019-2020 the original author or authors.
4+
* *
5+
* * Licensed under the Apache License, Version 2.0 (the "License");
6+
* * you may not use this file except in compliance with the License.
7+
* * You may obtain a copy of the License at
8+
* *
9+
* * https://www.apache.org/licenses/LICENSE-2.0
10+
* *
11+
* * Unless required by applicable law or agreed to in writing, software
12+
* * distributed under the License is distributed on an "AS IS" BASIS,
13+
* * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* * See the License for the specific language governing permissions and
15+
* * limitations under the License.
16+
*
17+
*/
18+
19+
package test.org.springdoc.api.app97;
20+
21+
import org.springframework.web.bind.annotation.GetMapping;
22+
import org.springframework.web.bind.annotation.PostMapping;
23+
import org.springframework.web.bind.annotation.RequestMapping;
24+
import org.springframework.web.bind.annotation.RestController;
25+
26+
@RestController
27+
@RequestMapping("/api")
28+
public class HelloController {
29+
30+
@GetMapping(value = "/student/header", headers = "X-API-VERSION=1")
31+
public StudentV1 headerV1() {
32+
return new StudentV1("Bob Charlie");
33+
}
34+
35+
@GetMapping(value = "/student/header", headers = "X-API-VERSION=2")
36+
public StudentV2 headerV2() {
37+
return new StudentV2( "Charlie");
38+
}
39+
40+
}
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
/*
2+
*
3+
* * Copyright 2019-2020 the original author or authors.
4+
* *
5+
* * Licensed under the Apache License, Version 2.0 (the "License");
6+
* * you may not use this file except in compliance with the License.
7+
* * You may obtain a copy of the License at
8+
* *
9+
* * https://www.apache.org/licenses/LICENSE-2.0
10+
* *
11+
* * Unless required by applicable law or agreed to in writing, software
12+
* * distributed under the License is distributed on an "AS IS" BASIS,
13+
* * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* * See the License for the specific language governing permissions and
15+
* * limitations under the License.
16+
*
17+
*/
18+
19+
package test.org.springdoc.api.app97;
20+
21+
22+
import test.org.springdoc.api.AbstractSpringDocTest;
23+
24+
import org.springframework.boot.autoconfigure.SpringBootApplication;
25+
26+
public class SpringDocApp97Test extends AbstractSpringDocTest {
27+
28+
@SpringBootApplication
29+
static class SpringDocTestApp {}
30+
31+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
package test.org.springdoc.api.app97;
2+
3+
import com.fasterxml.jackson.annotation.JsonProperty;
4+
5+
public class StudentV1 {
6+
7+
@JsonProperty("name")
8+
private String name;
9+
10+
public StudentV1(String name) {
11+
this.name = name;
12+
}
13+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
package test.org.springdoc.api.app97;
2+
3+
import com.fasterxml.jackson.annotation.JsonProperty;
4+
5+
public class StudentV2 {
6+
7+
@JsonProperty("name")
8+
private String name;
9+
10+
public StudentV2(String name) {
11+
this.name = name;
12+
}
13+
}
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
{
2+
"openapi": "3.0.1",
3+
"info": {
4+
"title": "OpenAPI definition",
5+
"version": "v0"
6+
},
7+
"servers": [
8+
{
9+
"url": "http://localhost",
10+
"description": "Generated server url"
11+
}
12+
],
13+
"paths": {
14+
"/api/student/header": {
15+
"get": {
16+
"tags": [
17+
"hello-controller"
18+
],
19+
"operationId": "headerV2_1",
20+
"parameters": [
21+
{
22+
"name": "X-API-VERSION",
23+
"in": "header",
24+
"schema": {
25+
"type": "string",
26+
"enum": [
27+
"2",
28+
"1"
29+
]
30+
}
31+
}
32+
],
33+
"responses": {
34+
"200": {
35+
"description": "default response",
36+
"content": {
37+
"*/*": {
38+
"schema": {
39+
"oneOf": [
40+
{
41+
"$ref": "#/components/schemas/StudentV1"
42+
},
43+
{
44+
"$ref": "#/components/schemas/StudentV2"
45+
}
46+
]
47+
}
48+
}
49+
}
50+
}
51+
}
52+
}
53+
}
54+
},
55+
"components": {
56+
"schemas": {
57+
"StudentV2": {
58+
"type": "object",
59+
"properties": {
60+
"name": {
61+
"type": "string"
62+
}
63+
}
64+
},
65+
"StudentV1": {
66+
"type": "object",
67+
"properties": {
68+
"name": {
69+
"type": "string"
70+
}
71+
}
72+
}
73+
}
74+
}
75+
}

0 commit comments

Comments
 (0)