Skip to content

Commit d368d90

Browse files
authored
Add example and examples to @Header annotation and verify in TCK (#728)
* Add `example` and `examples` to `@Header` annotation and verify in TCK Signed-off-by: Michael Edgar <michael@xlate.io> * Align Header#example JavaDoc with ExampleObject#value Signed-off-by: Michael Edgar <michael@xlate.io> --------- Signed-off-by: Michael Edgar <michael@xlate.io>
1 parent db888d0 commit d368d90

5 files changed

Lines changed: 87 additions & 8 deletions

File tree

api/src/main/java/org/eclipse/microprofile/openapi/annotations/headers/Header.java

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,10 +23,18 @@
2323
import java.lang.annotation.Target;
2424

2525
import org.eclipse.microprofile.openapi.annotations.extensions.Extension;
26+
import org.eclipse.microprofile.openapi.annotations.media.ExampleObject;
2627
import org.eclipse.microprofile.openapi.annotations.media.Schema;
2728

2829
/**
29-
* Describes a single header object
30+
* Describes a single header object.
31+
* <p>
32+
* Note that although the {@linkplain #org.eclipse.microprofile.openapi.models.headers.Header header} model contains a
33+
* {@link org.eclipse.microprofile.openapi.models.headers.Header#getContent() content} property, modeling that
34+
* information using annotations is not possible because the content annotation's
35+
* {@link org.eclipse.microprofile.openapi.annotations.media.Content#encoding() encoding} references this annotation, a
36+
* cycle disallowed by the Java language.
37+
* </p>
3038
*
3139
* @see <a href= "https://spec.openapis.org/oas/v3.1.0.html#header-object">OpenAPI Specification Header Object</a>
3240
**/
@@ -102,4 +110,32 @@
102110
* @since 3.1
103111
*/
104112
Extension[] extensions() default {};
113+
114+
/**
115+
* Example of the header's potential value. The example SHOULD match the specified schema and encoding properties if
116+
* present. The {@code example} field is mutually exclusive of the {@link #examples() examples} field. Furthermore,
117+
* if referencing a {@link #schema() schema} that contains an example, the {@code example} value SHALL override the
118+
* example provided by the schema.
119+
* <p>
120+
* If the media type associated with the example allows parsing into an object, it may be converted from a string.
121+
* </p>
122+
*
123+
* @return an example of the header
124+
*
125+
* @since 4.2
126+
**/
127+
String example() default "";
128+
129+
/**
130+
* Examples of the header's potential value. Each example SHOULD contain a value in the correct format as specified
131+
* in the parameter encoding. The {@code examples} field is mutually exclusive of the {@link #example() example}
132+
* field. Furthermore, if referencing a schema that contains an example, the {@code examples} value SHALL override
133+
* the example provided by the schema.
134+
*
135+
* @return the list of examples for this header
136+
*
137+
* @since 4.2
138+
**/
139+
ExampleObject[] examples() default {};
140+
105141
}

api/src/main/java/org/eclipse/microprofile/openapi/annotations/headers/package-info.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,6 @@
2626
* </pre>
2727
*/
2828

29-
@org.osgi.annotation.versioning.Version("1.1")
29+
@org.osgi.annotation.versioning.Version("1.2")
3030
@org.osgi.annotation.versioning.ProviderType
31-
package org.eclipse.microprofile.openapi.annotations.headers;
31+
package org.eclipse.microprofile.openapi.annotations.headers;

spec/src/main/asciidoc/release_notes.asciidoc

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,16 @@
1919
[[release_notes]]
2020
== Release Notes
2121

22+
[[release_notes_42]]
23+
=== Release Notes for MicroProfile OpenAPI 4.2
24+
25+
A full list of changes delivered in the 4.2 release can be found at link:https://github.com/microprofile/microprofile-open-api/milestone/14?closed=1[MicroProfile OpenAPI 4.2 Milestone]
26+
27+
[[api_changes_42]]
28+
==== API/SPI changes
29+
30+
* Add `example` and `examples` to `@Header` and verify implementation support in TCK (https://github.com/microprofile/microprofile-open-api/issues/697)[697])
31+
2232
[[release_notes_41]]
2333
=== Release Notes for MicroProfile OpenAPI 4.1
2434

tck/src/main/java/org/eclipse/microprofile/openapi/apps/airlines/resources/UserResource.java

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -205,7 +205,18 @@ public Response updateUser(
205205

206206
@PATCH
207207
@Path("/username/{username}")
208-
@APIResponse(responseCode = "200", description = "Password was changed successfully")
208+
@APIResponse(responseCode = "200", description = "Password was changed successfully", headers = {
209+
@Header(name = "X-Password-Strength", description = "Rating of the strength of the new password value.",
210+
schema = @Schema(type = SchemaType.NUMBER), examples = {
211+
@ExampleObject(name = "strong", summary = "Password is strong", value = "10"),
212+
@ExampleObject(name = "adequate", summary = "Password is adequate", value = "8.5"),
213+
@ExampleObject(name = "weak", summary = "Password is weak", value = "5.1"),
214+
})
215+
})
216+
@APIResponse(responseCode = "400", description = "New password is too weak", headers = {
217+
@Header(name = "X-Password-Strength", description = "Rating of the strength of the new password value.",
218+
schema = @Schema(type = SchemaType.NUMBER), example = "0")
219+
})
209220
@Operation(summary = "Change user password", description = "This changes the password for the logged in user.",
210221
operationId = "changePassword")
211222
@SecurityRequirementsSet(value = {

tck/src/main/java/org/eclipse/microprofile/openapi/tck/AirlinesAppTest.java

Lines changed: 26 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
import static org.hamcrest.Matchers.hasItems;
3333
import static org.hamcrest.Matchers.hasKey;
3434
import static org.hamcrest.Matchers.hasSize;
35+
import static org.hamcrest.Matchers.hasToString;
3536
import static org.hamcrest.Matchers.is;
3637
import static org.hamcrest.Matchers.not;
3738
import static org.hamcrest.Matchers.notNullValue;
@@ -312,10 +313,10 @@ public void testAPIResponse(String type) {
312313
vr.body("paths.'/user/username/{username}'.delete.responses.'404'.description", equalTo("User not found"));
313314

314315
// @APIResponse at class level combined with method level
315-
vr.body("paths.'/user/username/{username}'.patch.responses", aMapWithSize(2));
316-
vr.body("paths.'/user/username/{username}'.patch.responses.'200'.description",
317-
equalTo("Password was changed successfully"));
318-
vr.body("paths.'/user/username/{username}'.patch.responses.'400'.description", equalTo("Invalid request"));
316+
vr.body("paths.'/user/logout'.get.responses", aMapWithSize(2));
317+
vr.body("paths.'/user/logout'.get.responses.'200'.description",
318+
equalTo("Successful user logout."));
319+
vr.body("paths.'/user/logout'.get.responses.'400'.description", equalTo("Invalid request"));
319320
}
320321

321322
@Test(dataProvider = "formatProvider")
@@ -832,6 +833,27 @@ public void testExampleObject(String type) {
832833
equalTo("bsmith"));
833834
}
834835

836+
@Test(dataProvider = "formatProvider")
837+
public void testExamplesInHeaders(String type) {
838+
ValidatableResponse vr = callEndpoint(type);
839+
840+
// Multiple examples in Header
841+
vr.body("paths.'/user/username/{username}'.patch.responses.'200'.headers.'X-Password-Strength'",
842+
hasKey("examples"));
843+
844+
// Implementations MAY parse the example to the data type of the schema, so here we leniently test
845+
// the value as a string using String#valueOf (via hasToString).
846+
847+
vr.body("paths.'/user/username/{username}'.patch.responses.'200'.headers.'X-Password-Strength'.examples", allOf(
848+
hasEntry(equalTo("strong"), hasEntry(equalTo("value"), hasToString("10"))),
849+
hasEntry(equalTo("adequate"), hasEntry(equalTo("value"), hasToString("8.5"))),
850+
hasEntry(equalTo("weak"), hasEntry(equalTo("value"), hasToString("5.1")))));
851+
852+
// Single example in header
853+
vr.body("paths.'/user/username/{username}'.patch.responses.'400'.headers.'X-Password-Strength'",
854+
hasEntry(equalTo("example"), hasToString("0")));
855+
}
856+
835857
@Test(dataProvider = "formatProvider")
836858
public void testContentExampleAttribute(String type) {
837859
ValidatableResponse vr = callEndpoint(type);

0 commit comments

Comments
 (0)