Skip to content

Commit

Permalink
Add Header Object missing attributes (#4608)
Browse files Browse the repository at this point in the history
* 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]>
  • Loading branch information
micryc and frantuma authored Feb 1, 2024
1 parent 5186247 commit 0d067d0
Show file tree
Hide file tree
Showing 5 changed files with 418 additions and 9 deletions.
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
package io.swagger.v3.oas.annotations.headers;

import io.swagger.v3.oas.annotations.enums.Explode;
import io.swagger.v3.oas.annotations.media.ArraySchema;
import io.swagger.v3.oas.annotations.media.ExampleObject;
import io.swagger.v3.oas.annotations.media.Schema;

import java.lang.annotation.Inherited;
Expand Down Expand Up @@ -64,4 +67,40 @@
**/
String ref() default "";


/**
* 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.
*
* @return whether or not to expand individual array members
**/
Explode explode() default Explode.DEFAULT;

/**
* Allows this header to be marked as hidden
*
* @return whether or not this header is hidden
*/
boolean hidden() default false;

/**
* 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.
*
* @return an example of the header
**/
String example() default "";

/**
* An array of examples of the schema used to show the use of the associated schema.
*
* @return array of examples of the header
**/
ExampleObject[] examples() default {};

/**
* The schema of the array that defines this header. Ignored if the property content is specified.
*
* @return the schema of the array
*/
ArraySchema array() default @ArraySchema();

}
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
import io.swagger.v3.core.converter.ModelConverters;
import io.swagger.v3.core.converter.ResolvedSchema;
import io.swagger.v3.oas.annotations.StringToClassMapItem;
import io.swagger.v3.oas.annotations.enums.Explode;
import io.swagger.v3.oas.annotations.extensions.Extension;
import io.swagger.v3.oas.annotations.extensions.ExtensionProperty;
import io.swagger.v3.oas.annotations.links.LinkParameter;
Expand Down Expand Up @@ -41,7 +42,6 @@
import org.apache.commons.lang3.math.NumberUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
Expand Down Expand Up @@ -1288,17 +1288,24 @@ public static Map<String, String> getLinkParameters(LinkParameter[] linkParamete
}

public static Optional<Map<String, Header>> getHeaders(io.swagger.v3.oas.annotations.headers.Header[] annotationHeaders, JsonView jsonViewAnnotation) {
return getHeaders(annotationHeaders, jsonViewAnnotation, false);
return getHeaders(annotationHeaders, null, jsonViewAnnotation);
}
public static Optional<Map<String, Header>> getHeaders(io.swagger.v3.oas.annotations.headers.Header[] annotationHeaders, Components components, JsonView jsonViewAnnotation) {
return getHeaders(annotationHeaders, components, jsonViewAnnotation, false);
}

public static Optional<Map<String, Header>> getHeaders(io.swagger.v3.oas.annotations.headers.Header[] annotationHeaders, JsonView jsonViewAnnotation, boolean openapi31) {
return getHeaders(annotationHeaders, null, jsonViewAnnotation, openapi31);
}

public static Optional<Map<String, Header>> getHeaders(io.swagger.v3.oas.annotations.headers.Header[] annotationHeaders, Components components, JsonView jsonViewAnnotation, boolean openapi31) {
if (annotationHeaders == null) {
return Optional.empty();
}

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

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

public static Optional<Header> getHeader(io.swagger.v3.oas.annotations.headers.Header header, JsonView jsonViewAnnotation) {
return getHeader(header, jsonViewAnnotation, false);
return getHeader(header, null, jsonViewAnnotation);
}
public static Optional<Header> getHeader(io.swagger.v3.oas.annotations.headers.Header header, Components components, JsonView jsonViewAnnotation) {
return getHeader(header, components, jsonViewAnnotation, false);
}

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

if (header == null) {
if (header == null || header.hidden()) {
return Optional.empty();
}

Expand All @@ -1327,29 +1340,90 @@ public static Optional<Header> getHeader(io.swagger.v3.oas.annotations.headers.H
headerObject.set$ref(header.ref());
isEmpty = false;
}
if (StringUtils.isNotBlank(header.example())) {
try {
headerObject.setExample(Json.mapper().readTree(header.example()));
} catch (IOException e) {
headerObject.setExample(header.example());
}
}
if (header.deprecated()) {
headerObject.setDeprecated(header.deprecated());
}
if (header.required()) {
headerObject.setRequired(header.required());
isEmpty = false;
}
Map<String, Example> exampleMap = new LinkedHashMap<>();
if (header.examples().length == 1 && StringUtils.isBlank(header.examples()[0].name())) {
Optional<Example> exampleOptional = AnnotationsUtils.getExample(header.examples()[0], true);
exampleOptional.ifPresent(headerObject::setExample);
} else {
for (ExampleObject exampleObject : header.examples()) {
AnnotationsUtils.getExample(exampleObject).ifPresent(example -> exampleMap.put(exampleObject.name(), example));
}
}
if (!exampleMap.isEmpty()) {
headerObject.setExamples(exampleMap);
}
headerObject.setStyle(Header.StyleEnum.SIMPLE);

if (header.schema() != null) {
if (header.schema().implementation().equals(Void.class)) {
AnnotationsUtils.getSchemaFromAnnotation(header.schema(), jsonViewAnnotation, openapi31).ifPresent(
headerObject::setSchema);
}else {
AnnotatedType annotatedType = new AnnotatedType()
.type(getSchemaType(header.schema()))
.resolveAsRef(true)
.skipOverride(true)
.jsonViewAnnotation(jsonViewAnnotation);

final ResolvedSchema resolvedSchema = ModelConverters.getInstance(openapi31).resolveAsResolvedSchema(annotatedType);

if (resolvedSchema.schema != null) {
headerObject.setSchema(resolvedSchema.schema);
}
resolvedSchema.referencedSchemas.forEach(components::addSchemas);
}
}
if (hasArrayAnnotation(header.array())){
AnnotationsUtils.getArraySchema(header.array(), components, jsonViewAnnotation, openapi31, null, true).ifPresent(
headerObject::setSchema);
}

setHeaderExplode(headerObject, header);
if (isEmpty) {
return Optional.empty();
}

return Optional.of(headerObject);
}

public static void setHeaderExplode (Header header, io.swagger.v3.oas.annotations.headers.Header h) {
if (isHeaderExplodable(h, header)) {
if (Explode.TRUE.equals(h.explode())) {
header.setExplode(Boolean.TRUE);
} else if (Explode.FALSE.equals(h.explode())) {
header.setExplode(Boolean.FALSE);
}
}
}

private static boolean isHeaderExplodable(io.swagger.v3.oas.annotations.headers.Header h, Header header) {
io.swagger.v3.oas.annotations.media.Schema schema = h.schema();
boolean explode = true;
if (schema != null) {
Class implementation = schema.implementation();
if (implementation == Void.class) {
if (!schema.type().equals("object") && !schema.type().equals("array")) {
explode = false;
}
}
}
return explode;
}

public static void addEncodingToMediaType(MediaType mediaType, io.swagger.v3.oas.annotations.media.Encoding encoding, JsonView jsonViewAnnotation) {
addEncodingToMediaType(mediaType, encoding, jsonViewAnnotation, false);
}
Expand All @@ -1376,7 +1450,7 @@ public static void addEncodingToMediaType(MediaType mediaType, io.swagger.v3.oas
}

if (encoding.headers() != null) {
getHeaders(encoding.headers(), jsonViewAnnotation, openapi31).ifPresent(encodingObject::headers);
getHeaders(encoding.headers(), null, jsonViewAnnotation, openapi31).ifPresent(encodingObject::headers);
}
if (encoding.extensions() != null && encoding.extensions().length > 0) {
Map<String, Object> extensions = AnnotationsUtils.getExtensions(openapi31, encoding.extensions());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ public void extensionsTest(String methodName,
.flatMap(response -> Arrays.stream(response.headers())).toArray(Header[]::new);

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

Assert.assertEquals(optionalMap, expected);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ public static Optional<ApiResponses> getApiResponses(final io.swagger.v3.oas.ann

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

Map<String, Link> links = AnnotationsUtils.getLinks(response.links());
Expand Down
Loading

0 comments on commit 0d067d0

Please sign in to comment.