Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Header Object missing attributes #4608

Merged
merged 13 commits into from
Feb 1, 2024
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) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As mentioned we need to maintain APIs backward compatibility, keeping previous method signature calling new one:

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

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) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

APIs backward compatibility, keeping previous method signature calling new one:

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);
}

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) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

APIs backward compatibility, keeping previous method signature calling new one:

public static Optional<Header> getHeader(io.swagger.v3.oas.annotations.headers.Header header, JsonView jsonViewAnnotation) {
    return getHeader(header, null, 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) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

APIs backward compatibility, keeping previous method signature calling new one:

public static Optional<Header> getHeader(io.swagger.v3.oas.annotations.headers.Header header, JsonView jsonViewAnnotation, boolean openapi31) {
    return getHeader(header, null, jsonViewAnnotation, 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
Loading