Skip to content

Commit 0d067d0

Browse files
micrycfrantuma
andauthored
Add Header Object missing attributes (#4608)
* Add explode attribute to header annotation * Add hidden attribute * Add example,examples attributes * Add array attribute * Add explode,hidden,example and examples attributes tests * Add Header with ArraySchema attribute test * Delete sout in test * Fix schema implementation parsing process * Delete unused imports and spaces * Fix schema implementation test * Fix APIs backward compatibility * Add space after coma * Refs #4196 - reintroduce original getHeader(s) methods signatures --------- Co-authored-by: frantuma <[email protected]>
1 parent 5186247 commit 0d067d0

File tree

5 files changed

+418
-9
lines changed

5 files changed

+418
-9
lines changed

modules/swagger-annotations/src/main/java/io/swagger/v3/oas/annotations/headers/Header.java

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
package io.swagger.v3.oas.annotations.headers;
22

3+
import io.swagger.v3.oas.annotations.enums.Explode;
4+
import io.swagger.v3.oas.annotations.media.ArraySchema;
5+
import io.swagger.v3.oas.annotations.media.ExampleObject;
36
import io.swagger.v3.oas.annotations.media.Schema;
47

58
import java.lang.annotation.Inherited;
@@ -64,4 +67,40 @@
6467
**/
6568
String ref() default "";
6669

70+
71+
/**
72+
* When this is true, parameter values of type array or object generate separate parameters for each value of the array or key-value pair of the map. For other types of parameters this property has no effect. When style is form, the default value is true. For all other styles, the default value is false. Ignored if the properties content or array are specified.
73+
*
74+
* @return whether or not to expand individual array members
75+
**/
76+
Explode explode() default Explode.DEFAULT;
77+
78+
/**
79+
* Allows this header to be marked as hidden
80+
*
81+
* @return whether or not this header is hidden
82+
*/
83+
boolean hidden() default false;
84+
85+
/**
86+
* Provides an example of the schema. When associated with a specific media type, the example string shall be parsed by the consumer to be treated as an object or an array. Ignored if the properties examples, content or array are specified.
87+
*
88+
* @return an example of the header
89+
**/
90+
String example() default "";
91+
92+
/**
93+
* An array of examples of the schema used to show the use of the associated schema.
94+
*
95+
* @return array of examples of the header
96+
**/
97+
ExampleObject[] examples() default {};
98+
99+
/**
100+
* The schema of the array that defines this header. Ignored if the property content is specified.
101+
*
102+
* @return the schema of the array
103+
*/
104+
ArraySchema array() default @ArraySchema();
105+
67106
}

modules/swagger-core/src/main/java/io/swagger/v3/core/util/AnnotationsUtils.java

Lines changed: 80 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
import io.swagger.v3.core.converter.ModelConverters;
1111
import io.swagger.v3.core.converter.ResolvedSchema;
1212
import io.swagger.v3.oas.annotations.StringToClassMapItem;
13+
import io.swagger.v3.oas.annotations.enums.Explode;
1314
import io.swagger.v3.oas.annotations.extensions.Extension;
1415
import io.swagger.v3.oas.annotations.extensions.ExtensionProperty;
1516
import io.swagger.v3.oas.annotations.links.LinkParameter;
@@ -41,7 +42,6 @@
4142
import org.apache.commons.lang3.math.NumberUtils;
4243
import org.slf4j.Logger;
4344
import org.slf4j.LoggerFactory;
44-
4545
import java.io.IOException;
4646
import java.lang.annotation.Annotation;
4747
import java.lang.reflect.Field;
@@ -1288,17 +1288,24 @@ public static Map<String, String> getLinkParameters(LinkParameter[] linkParamete
12881288
}
12891289

12901290
public static Optional<Map<String, Header>> getHeaders(io.swagger.v3.oas.annotations.headers.Header[] annotationHeaders, JsonView jsonViewAnnotation) {
1291-
return getHeaders(annotationHeaders, jsonViewAnnotation, false);
1291+
return getHeaders(annotationHeaders, null, jsonViewAnnotation);
1292+
}
1293+
public static Optional<Map<String, Header>> getHeaders(io.swagger.v3.oas.annotations.headers.Header[] annotationHeaders, Components components, JsonView jsonViewAnnotation) {
1294+
return getHeaders(annotationHeaders, components, jsonViewAnnotation, false);
12921295
}
12931296

12941297
public static Optional<Map<String, Header>> getHeaders(io.swagger.v3.oas.annotations.headers.Header[] annotationHeaders, JsonView jsonViewAnnotation, boolean openapi31) {
1298+
return getHeaders(annotationHeaders, null, jsonViewAnnotation, openapi31);
1299+
}
1300+
1301+
public static Optional<Map<String, Header>> getHeaders(io.swagger.v3.oas.annotations.headers.Header[] annotationHeaders, Components components, JsonView jsonViewAnnotation, boolean openapi31) {
12951302
if (annotationHeaders == null) {
12961303
return Optional.empty();
12971304
}
12981305

12991306
Map<String, Header> headers = new HashMap<>();
13001307
for (io.swagger.v3.oas.annotations.headers.Header header : annotationHeaders) {
1301-
getHeader(header, jsonViewAnnotation).ifPresent(headerResult -> headers.put(header.name(), headerResult));
1308+
getHeader(header, components, jsonViewAnnotation, openapi31).ifPresent(headerResult -> headers.put(header.name(), headerResult));
13021309
}
13031310

13041311
if (headers.size() == 0) {
@@ -1308,12 +1315,18 @@ public static Optional<Map<String, Header>> getHeaders(io.swagger.v3.oas.annotat
13081315
}
13091316

13101317
public static Optional<Header> getHeader(io.swagger.v3.oas.annotations.headers.Header header, JsonView jsonViewAnnotation) {
1311-
return getHeader(header, jsonViewAnnotation, false);
1318+
return getHeader(header, null, jsonViewAnnotation);
1319+
}
1320+
public static Optional<Header> getHeader(io.swagger.v3.oas.annotations.headers.Header header, Components components, JsonView jsonViewAnnotation) {
1321+
return getHeader(header, components, jsonViewAnnotation, false);
13121322
}
13131323

13141324
public static Optional<Header> getHeader(io.swagger.v3.oas.annotations.headers.Header header, JsonView jsonViewAnnotation, boolean openapi31) {
1325+
return getHeader(header, null, jsonViewAnnotation, openapi31);
1326+
}
1327+
public static Optional<Header> getHeader(io.swagger.v3.oas.annotations.headers.Header header, Components components, JsonView jsonViewAnnotation, boolean openapi31) {
13151328

1316-
if (header == null) {
1329+
if (header == null || header.hidden()) {
13171330
return Optional.empty();
13181331
}
13191332

@@ -1327,29 +1340,90 @@ public static Optional<Header> getHeader(io.swagger.v3.oas.annotations.headers.H
13271340
headerObject.set$ref(header.ref());
13281341
isEmpty = false;
13291342
}
1343+
if (StringUtils.isNotBlank(header.example())) {
1344+
try {
1345+
headerObject.setExample(Json.mapper().readTree(header.example()));
1346+
} catch (IOException e) {
1347+
headerObject.setExample(header.example());
1348+
}
1349+
}
13301350
if (header.deprecated()) {
13311351
headerObject.setDeprecated(header.deprecated());
13321352
}
13331353
if (header.required()) {
13341354
headerObject.setRequired(header.required());
13351355
isEmpty = false;
13361356
}
1357+
Map<String, Example> exampleMap = new LinkedHashMap<>();
1358+
if (header.examples().length == 1 && StringUtils.isBlank(header.examples()[0].name())) {
1359+
Optional<Example> exampleOptional = AnnotationsUtils.getExample(header.examples()[0], true);
1360+
exampleOptional.ifPresent(headerObject::setExample);
1361+
} else {
1362+
for (ExampleObject exampleObject : header.examples()) {
1363+
AnnotationsUtils.getExample(exampleObject).ifPresent(example -> exampleMap.put(exampleObject.name(), example));
1364+
}
1365+
}
1366+
if (!exampleMap.isEmpty()) {
1367+
headerObject.setExamples(exampleMap);
1368+
}
13371369
headerObject.setStyle(Header.StyleEnum.SIMPLE);
13381370

13391371
if (header.schema() != null) {
13401372
if (header.schema().implementation().equals(Void.class)) {
13411373
AnnotationsUtils.getSchemaFromAnnotation(header.schema(), jsonViewAnnotation, openapi31).ifPresent(
13421374
headerObject::setSchema);
1375+
}else {
1376+
AnnotatedType annotatedType = new AnnotatedType()
1377+
.type(getSchemaType(header.schema()))
1378+
.resolveAsRef(true)
1379+
.skipOverride(true)
1380+
.jsonViewAnnotation(jsonViewAnnotation);
1381+
1382+
final ResolvedSchema resolvedSchema = ModelConverters.getInstance(openapi31).resolveAsResolvedSchema(annotatedType);
1383+
1384+
if (resolvedSchema.schema != null) {
1385+
headerObject.setSchema(resolvedSchema.schema);
1386+
}
1387+
resolvedSchema.referencedSchemas.forEach(components::addSchemas);
13431388
}
13441389
}
1390+
if (hasArrayAnnotation(header.array())){
1391+
AnnotationsUtils.getArraySchema(header.array(), components, jsonViewAnnotation, openapi31, null, true).ifPresent(
1392+
headerObject::setSchema);
1393+
}
13451394

1395+
setHeaderExplode(headerObject, header);
13461396
if (isEmpty) {
13471397
return Optional.empty();
13481398
}
13491399

13501400
return Optional.of(headerObject);
13511401
}
13521402

1403+
public static void setHeaderExplode (Header header, io.swagger.v3.oas.annotations.headers.Header h) {
1404+
if (isHeaderExplodable(h, header)) {
1405+
if (Explode.TRUE.equals(h.explode())) {
1406+
header.setExplode(Boolean.TRUE);
1407+
} else if (Explode.FALSE.equals(h.explode())) {
1408+
header.setExplode(Boolean.FALSE);
1409+
}
1410+
}
1411+
}
1412+
1413+
private static boolean isHeaderExplodable(io.swagger.v3.oas.annotations.headers.Header h, Header header) {
1414+
io.swagger.v3.oas.annotations.media.Schema schema = h.schema();
1415+
boolean explode = true;
1416+
if (schema != null) {
1417+
Class implementation = schema.implementation();
1418+
if (implementation == Void.class) {
1419+
if (!schema.type().equals("object") && !schema.type().equals("array")) {
1420+
explode = false;
1421+
}
1422+
}
1423+
}
1424+
return explode;
1425+
}
1426+
13531427
public static void addEncodingToMediaType(MediaType mediaType, io.swagger.v3.oas.annotations.media.Encoding encoding, JsonView jsonViewAnnotation) {
13541428
addEncodingToMediaType(mediaType, encoding, jsonViewAnnotation, false);
13551429
}
@@ -1376,7 +1450,7 @@ public static void addEncodingToMediaType(MediaType mediaType, io.swagger.v3.oas
13761450
}
13771451

13781452
if (encoding.headers() != null) {
1379-
getHeaders(encoding.headers(), jsonViewAnnotation, openapi31).ifPresent(encodingObject::headers);
1453+
getHeaders(encoding.headers(), null, jsonViewAnnotation, openapi31).ifPresent(encodingObject::headers);
13801454
}
13811455
if (encoding.extensions() != null && encoding.extensions().length > 0) {
13821456
Map<String, Object> extensions = AnnotationsUtils.getExtensions(openapi31, encoding.extensions());

modules/swagger-core/src/test/java/io/swagger/v3/core/resolving/AnnotationsUtilsHeadersTest.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ public void extensionsTest(String methodName,
4646
.flatMap(response -> Arrays.stream(response.headers())).toArray(Header[]::new);
4747

4848
final Optional<Map<String, io.swagger.v3.oas.models.headers.Header>> optionalMap =
49-
AnnotationsUtils.getHeaders(headers, null);
49+
AnnotationsUtils.getHeaders(headers, null, null);
5050

5151
Assert.assertEquals(optionalMap, expected);
5252
}

modules/swagger-jaxrs2/src/main/java/io/swagger/v3/jaxrs2/OperationParser.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,7 @@ public static Optional<ApiResponses> getApiResponses(final io.swagger.v3.oas.ann
9393

9494
AnnotationsUtils.getContent(response.content(), classProduces == null ? new String[0] : classProduces.value(),
9595
methodProduces == null ? new String[0] : methodProduces.value(), null, components, jsonViewAnnotation, openapi31).ifPresent(apiResponseObject::content);
96-
AnnotationsUtils.getHeaders(response.headers(), jsonViewAnnotation).ifPresent(apiResponseObject::headers);
96+
AnnotationsUtils.getHeaders(response.headers(), components, jsonViewAnnotation).ifPresent(apiResponseObject::headers);
9797
if (StringUtils.isNotBlank(apiResponseObject.getDescription()) || apiResponseObject.getContent() != null || apiResponseObject.getHeaders() != null) {
9898

9999
Map<String, Link> links = AnnotationsUtils.getLinks(response.links());

0 commit comments

Comments
 (0)