From bf6d085ab12bd33210aac3a84df2ff091b1083ac Mon Sep 17 00:00:00 2001 From: Andrew Rouse Date: Wed, 15 May 2024 15:17:54 +0100 Subject: [PATCH] Allow ref with description and summary $ref is now allowed alongside description and summary. - Update model and annotation javadoc accordingly. - Test model - Test annotations --- .../annotations/callbacks/Callback.java | 3 +- .../openapi/annotations/headers/Header.java | 5 ++- .../openapi/annotations/links/Link.java | 5 ++- .../annotations/media/ExampleObject.java | 6 ++- .../annotations/parameters/Parameter.java | 5 ++- .../annotations/parameters/RequestBody.java | 5 ++- .../annotations/responses/APIResponse.java | 5 ++- .../annotations/security/SecurityScheme.java | 5 ++- .../openapi/annotations/tags/Tag.java | 5 ++- .../openapi/models/Reference.java | 7 ++-- .../openapi/apps/airlines/JAXRSApp.java | 41 ++++++++++++++---- .../openapi/reader/MyOASModelReaderImpl.java | 42 ++++++++++++++++++- .../openapi/tck/AirlinesAppTest.java | 31 ++++++++++++++ .../openapi/tck/ModelReaderAppTest.java | 31 ++++++++++++++ 14 files changed, 167 insertions(+), 29 deletions(-) diff --git a/api/src/main/java/org/eclipse/microprofile/openapi/annotations/callbacks/Callback.java b/api/src/main/java/org/eclipse/microprofile/openapi/annotations/callbacks/Callback.java index 46a3bb2b..94d3b526 100644 --- a/api/src/main/java/org/eclipse/microprofile/openapi/annotations/callbacks/Callback.java +++ b/api/src/main/java/org/eclipse/microprofile/openapi/annotations/callbacks/Callback.java @@ -70,7 +70,8 @@ * Reference value to a Callback object. *

* This property provides a reference to an object defined elsewhere. This property and all other properties are - * mutually exclusive. If other properties are defined in addition to the ref property then the result is undefined. + * mutually exclusive. If other properties are defined in addition to the {@code ref} property then the result is + * undefined. * * @return reference to a callback object definition **/ diff --git a/api/src/main/java/org/eclipse/microprofile/openapi/annotations/headers/Header.java b/api/src/main/java/org/eclipse/microprofile/openapi/annotations/headers/Header.java index e14a5e8f..ce4387c0 100644 --- a/api/src/main/java/org/eclipse/microprofile/openapi/annotations/headers/Header.java +++ b/api/src/main/java/org/eclipse/microprofile/openapi/annotations/headers/Header.java @@ -85,8 +85,9 @@ /** * Reference value to a Header object. *

- * This property provides a reference to an object defined elsewhere. This property and all other properties are - * mutually exclusive. If other properties are defined in addition to the ref property then the result is undefined. + * This property provides a reference to an object defined elsewhere. This property may be used with + * {@link #description()} but is mutually exclusive with all other properties. If properties other than + * {@code description} are defined in addition to the {@code ref} property then the result is undefined. * * @return reference to a header **/ diff --git a/api/src/main/java/org/eclipse/microprofile/openapi/annotations/links/Link.java b/api/src/main/java/org/eclipse/microprofile/openapi/annotations/links/Link.java index b46fef51..23db474c 100644 --- a/api/src/main/java/org/eclipse/microprofile/openapi/annotations/links/Link.java +++ b/api/src/main/java/org/eclipse/microprofile/openapi/annotations/links/Link.java @@ -96,8 +96,9 @@ /** * Reference value to a Link object. *

- * This property provides a reference to an object defined elsewhere. This property and all other properties are - * mutually exclusive. If other properties are defined in addition to the ref property then the result is undefined. + * This property provides a reference to an object defined elsewhere. This property may be used with + * {@link #description()} but is mutually exclusive with all other properties. If properties other than + * {@code description} are defined in addition to the {@code ref} property then the result is undefined. * * @return reference to a link **/ diff --git a/api/src/main/java/org/eclipse/microprofile/openapi/annotations/media/ExampleObject.java b/api/src/main/java/org/eclipse/microprofile/openapi/annotations/media/ExampleObject.java index 1dd530ed..7b69c3fd 100644 --- a/api/src/main/java/org/eclipse/microprofile/openapi/annotations/media/ExampleObject.java +++ b/api/src/main/java/org/eclipse/microprofile/openapi/annotations/media/ExampleObject.java @@ -85,8 +85,10 @@ /** * Reference value to an Example object. *

- * This property provides a reference to an object defined elsewhere. This property and all other properties are - * mutually exclusive. If other properties are defined in addition to the ref property then the result is undefined. + * This property provides a reference to an object defined elsewhere. This property may be used with + * {@link #description()} and {@link #summary()} but is mutually exclusive with all other properties. If properties + * other than {@code description} and {@code summary} are defined in addition to the {@code ref} property then the + * result is undefined. * * @return reference to an example **/ diff --git a/api/src/main/java/org/eclipse/microprofile/openapi/annotations/parameters/Parameter.java b/api/src/main/java/org/eclipse/microprofile/openapi/annotations/parameters/Parameter.java index 634e0af5..7edaa596 100644 --- a/api/src/main/java/org/eclipse/microprofile/openapi/annotations/parameters/Parameter.java +++ b/api/src/main/java/org/eclipse/microprofile/openapi/annotations/parameters/Parameter.java @@ -200,8 +200,9 @@ /** * Reference value to a Parameter object. *

- * This property provides a reference to an object defined elsewhere. This property and all other properties are - * mutually exclusive. If other properties are defined in addition to the ref property then the result is undefined. + * This property provides a reference to an object defined elsewhere. This property may be used with + * {@link #description()} but is mutually exclusive with all other properties. If properties other than + * {@code description} are defined in addition to the {@code ref} property then the result is undefined. * * @return reference to a parameter **/ diff --git a/api/src/main/java/org/eclipse/microprofile/openapi/annotations/parameters/RequestBody.java b/api/src/main/java/org/eclipse/microprofile/openapi/annotations/parameters/RequestBody.java index 0f07d832..bacde139 100644 --- a/api/src/main/java/org/eclipse/microprofile/openapi/annotations/parameters/RequestBody.java +++ b/api/src/main/java/org/eclipse/microprofile/openapi/annotations/parameters/RequestBody.java @@ -74,8 +74,9 @@ /** * Reference value to a RequestBody object. *

- * This property provides a reference to an object defined elsewhere. This property and all other properties are - * mutually exclusive. If other properties are defined in addition to the ref property then the result is undefined. + * This property provides a reference to an object defined elsewhere. This property may be used with + * {@link #description()} but is mutually exclusive with all other properties. If properties other than + * {@code description} are defined in addition to the {@code ref} property then the result is undefined. * * @return reference to a request body **/ diff --git a/api/src/main/java/org/eclipse/microprofile/openapi/annotations/responses/APIResponse.java b/api/src/main/java/org/eclipse/microprofile/openapi/annotations/responses/APIResponse.java index 87cbb817..cce59f7f 100644 --- a/api/src/main/java/org/eclipse/microprofile/openapi/annotations/responses/APIResponse.java +++ b/api/src/main/java/org/eclipse/microprofile/openapi/annotations/responses/APIResponse.java @@ -128,8 +128,9 @@ /** * Reference value to a Response object. *

- * This property provides a reference to an object defined elsewhere. This property and all other properties are - * mutually exclusive. If other properties are defined in addition to the ref property then the result is undefined. + * This property provides a reference to an object defined elsewhere. This property may be used with + * {@link #description()} but is mutually exclusive with all other properties. If properties other than + * {@code description} are defined in addition to the {@code ref} property then the result is undefined. * * @return reference to a response **/ diff --git a/api/src/main/java/org/eclipse/microprofile/openapi/annotations/security/SecurityScheme.java b/api/src/main/java/org/eclipse/microprofile/openapi/annotations/security/SecurityScheme.java index 5155f134..4990678f 100644 --- a/api/src/main/java/org/eclipse/microprofile/openapi/annotations/security/SecurityScheme.java +++ b/api/src/main/java/org/eclipse/microprofile/openapi/annotations/security/SecurityScheme.java @@ -132,8 +132,9 @@ /** * Reference value to a SecurityScheme object. *

- * This property provides a reference to an object defined elsewhere. This property and all other properties are - * mutually exclusive. If other properties are defined in addition to the ref property then the result is undefined. + * This property provides a reference to an object defined elsewhere. This property may be used with + * {@link #description()} but is mutually exclusive with all other properties. If properties other than + * {@code description} are defined in addition to the {@code ref} property then the result is undefined. * * @return reference to a security scheme **/ diff --git a/api/src/main/java/org/eclipse/microprofile/openapi/annotations/tags/Tag.java b/api/src/main/java/org/eclipse/microprofile/openapi/annotations/tags/Tag.java index 34f8fcee..602fdc2d 100644 --- a/api/src/main/java/org/eclipse/microprofile/openapi/annotations/tags/Tag.java +++ b/api/src/main/java/org/eclipse/microprofile/openapi/annotations/tags/Tag.java @@ -101,8 +101,9 @@ /** * Reference value to a Tag object. *

- * This property provides a reference to an object defined elsewhere. This property and all other properties are - * mutually exclusive. If other properties are defined in addition to the ref property then the result is undefined. + * This property provides a reference to an object defined elsewhere. This property may be used with + * {@link #description()} but is mutually exclusive with all other properties. If properties other than + * {@code description} are defined in addition to the {@code ref} property then the result is undefined. * * @return reference to a tag **/ diff --git a/api/src/main/java/org/eclipse/microprofile/openapi/models/Reference.java b/api/src/main/java/org/eclipse/microprofile/openapi/models/Reference.java index 3639a3b4..0226781e 100644 --- a/api/src/main/java/org/eclipse/microprofile/openapi/models/Reference.java +++ b/api/src/main/java/org/eclipse/microprofile/openapi/models/Reference.java @@ -53,9 +53,10 @@ public interface Reference> { * response.setRef("NotFound"); // #/components/responses/NotFound * *

- * This property provides a reference to an object defined elsewhere. This property and all other properties are - * mutually exclusive. If other properties are defined in addition to the reference property then the result is - * undefined. + * This property provides a reference to an object defined elsewhere. This property may be used alongside + * {@code description} and {@code summary} if they exist on the object, but is mutually exclusive with all other + * properties. If properties other than {@code description} and {@code summary} are defined in addition to the + * reference property then the result is undefined. * * @param ref * a reference to a T object in the components section of this OpenAPI document or a JSON pointer to diff --git a/tck/src/main/java/org/eclipse/microprofile/openapi/apps/airlines/JAXRSApp.java b/tck/src/main/java/org/eclipse/microprofile/openapi/apps/airlines/JAXRSApp.java index 50d6aae1..e9cc8a2d 100644 --- a/tck/src/main/java/org/eclipse/microprofile/openapi/apps/airlines/JAXRSApp.java +++ b/tck/src/main/java/org/eclipse/microprofile/openapi/apps/airlines/JAXRSApp.java @@ -137,7 +137,10 @@ @APIResponse(name = "FoundBookings", responseCode = "200", description = "Bookings retrieved", content = @Content(schema = @Schema(type = SchemaType.ARRAY, - implementation = Booking.class))) + implementation = Booking.class))), + @APIResponse(name = "FoundBookingsARef", + ref = "#/components/responses/FoundBookings", + description = "Found Bookings Reference") }, parameters = { @Parameter(name = "departureDate", in = ParameterIn.QUERY, @@ -147,7 +150,10 @@ @Parameter(name = "username", in = ParameterIn.QUERY, description = "The name that needs to be deleted", schema = @Schema(type = SchemaType.STRING), - required = true) + required = true), + @Parameter(name = "usernameARef", + ref = "#/components/parameters/username", + description = "username reference") }, examples = { @ExampleObject(name = "review", summary = "External review example", @@ -156,13 +162,21 @@ extensions = @Extension(name = "x-example-object", value = "test-example-object")), @ExampleObject(name = "user", summary = "External user example", - externalValue = "http://foo.bar/examples/user-example.json") + externalValue = "http://foo.bar/examples/user-example.json"), + @ExampleObject(name = "userARef", + ref = "#/components/examples/user", + description = "User reference", + summary = "Referenced example") }, requestBodies = { @RequestBody(name = "review", content = @Content(mediaType = MediaType.APPLICATION_JSON, schema = @Schema(implementation = Review.class)), - required = true, description = "example review to add") + required = true, + description = "example review to add"), + @RequestBody(name = "reviewARef", + ref = "#/components/requestBodies/review", + description = "Review reference") }, headers = { @Header(name = "Max-Rate", description = "Maximum rate", @@ -173,20 +187,29 @@ value = "test-header")), @Header(name = "Request-Limit", description = "The number of allowed requests in the current period", - schema = @Schema(type = SchemaType.INTEGER)) + schema = @Schema(type = SchemaType.INTEGER)), + @Header(name = "Request-Limit-ARef", + ref = "#/components/headers/Request-Limit", + description = "Request-Limit reference") }, securitySchemes = { @SecurityScheme(securitySchemeName = "httpTestScheme", description = "user security scheme", type = SecuritySchemeType.HTTP, - scheme = "testScheme") + scheme = "testScheme"), + @SecurityScheme(securitySchemeName = "httpTestSchemeARef", + ref = "#/components/securitySchemes/httpTestScheme", + description = "httpTestScheme reference") }, links = { @Link(name = "UserName", description = "The username corresponding to provided user id", operationId = "getUserByName", parameters = @LinkParameter(name = "userId", - expression = "$request.path.id")) + expression = "$request.path.id")), + @Link(name = "UserNameARef", + ref = "#/components/links/UserName", + description = "UserName reference") }, callbacks = { @Callback(name = "GetBookings", @@ -194,7 +217,9 @@ operations = @CallbackOperation(summary = "Retrieve all bookings for current user", responses = { @APIResponse(ref = "FoundBookings") - })) + })), + @Callback(name = "GetBookingsARef", + ref = "#/components/callbacks/GetBookings") }, extensions = @Extension(name = "x-components", value = "test-components")), extensions = @Extension(name = "x-openapi-definition", value = "test-openapi-definition")) diff --git a/tck/src/main/java/org/eclipse/microprofile/openapi/reader/MyOASModelReaderImpl.java b/tck/src/main/java/org/eclipse/microprofile/openapi/reader/MyOASModelReaderImpl.java index 45ce98a3..8f0ed769 100644 --- a/tck/src/main/java/org/eclipse/microprofile/openapi/reader/MyOASModelReaderImpl.java +++ b/tck/src/main/java/org/eclipse/microprofile/openapi/reader/MyOASModelReaderImpl.java @@ -105,6 +105,8 @@ public OpenAPI buildModel() { .title("Airlines")) .addSchema("AirlinesRef", OASFactory.createObject(Schema.class) .ref("#/components/schemas/Airlines")) + .addSchema("Flight", OASFactory.createSchema() + .addType(Schema.SchemaType.OBJECT)) .addSchema("id", OASFactory.createObject(Schema.class) .addType(Schema.SchemaType.INTEGER) .format("int32")) @@ -176,6 +178,9 @@ public OpenAPI buildModel() { .schema(OASFactory.createObject(Schema.class) .addType(Schema.SchemaType.ARRAY) .ref("#/components.schemas.Booking"))))) + .addResponse("FoundBookingsRef", OASFactory.createAPIResponse() + .ref("#/components/responses/FoundBookings") + .description("Found Bookings Reference")) .parameters(new HashMap()) .addParameter("departureDate", OASFactory.createObject(Parameter.class) .required(true) @@ -185,6 +190,9 @@ public OpenAPI buildModel() { .required(true) .description("The name that needs to be deleted") .schema(OASFactory.createObject(Schema.class))) + .addParameter("usernameRef", OASFactory.createParameter() + .ref("#/components/parameters/username") + .description("username reference")) .examples(new HashMap()) .addExample("review", OASFactory.createObject(Example.class) .summary("External review example") @@ -193,6 +201,10 @@ public OpenAPI buildModel() { .addExample("user", OASFactory.createObject(Example.class) .summary("External user example") .externalValue("http://foo.bar/examples/user-example.json")) + .addExample("userRef", OASFactory.createExample() + .ref("#/componets/examples/user") + .summary("Referenced example") + .description("User reference")) .requestBodies(new HashMap()) .addRequestBody("review", OASFactory.createObject(RequestBody.class) .required(true) @@ -201,6 +213,9 @@ public OpenAPI buildModel() { .addMediaType("application/json", OASFactory.createObject(MediaType.class) .schema(OASFactory.createObject(Schema.class) .ref("#/components.schemas.Review"))))) + .addRequestBody("reviewRef", OASFactory.createRequestBody() + .ref("#/components/requestBodies/review") + .description("Review reference")) .headers(new HashMap()) .addHeader("Max-Rate", OASFactory.createObject(Header.class) .description("Maximum rate") @@ -213,17 +228,42 @@ public OpenAPI buildModel() { .description("The number of allowed requests in the current period") .schema(OASFactory.createObject(Schema.class) .addType(Schema.SchemaType.INTEGER))) + .addHeader("Request-Limit-Ref", OASFactory.createHeader() + .ref("#/components/headers/Request-Limit") + .description("Request-Limit reference")) .securitySchemes(new HashMap()) .addSecurityScheme("httpTestScheme", OASFactory.createObject(SecurityScheme.class) .description("user security scheme") .type(SecurityScheme.Type.HTTP) .scheme("testScheme")) + .addSecurityScheme("httpTestSchemeRef", OASFactory.createSecurityScheme() + .ref("#/components/securitySchemes/httpTestScheme") + .description("httpTestScheme reference")) .links(new HashMap()) .addLink("UserName", OASFactory.createObject(Link.class) .description("The username corresponding to provided user id") .operationId("getUserByName") .parameters(new HashMap()) - .addParameter("userId", "$request.link-path.userId"))) + .addParameter("userId", "$request.link-path.userId")) + .addLink("UserNameRef", OASFactory.createLink() + .ref("#/components/links/UserName") + .description("UserName reference")) + .callbacks(new HashMap<>()) + .addCallback("availabilityCallback", OASFactory.createCallback() + .addPathItem("http://localhost:9080/oas3-airlines/availability", OASFactory + .createPathItem() + .GET(OASFactory.createOperation() + .summary("Retrieve available flights") + .responses(OASFactory.createAPIResponses() + .addAPIResponse("200", OASFactory.createAPIResponse() + .description("successful operation") + .content(OASFactory.createContent() + .addMediaType("application/json", OASFactory + .createMediaType() + .schema(OASFactory.createSchema() + .ref("#/components/schemas/Flight"))))))))) + .addCallback("availabilityCallbackRef", OASFactory.createCallback() + .ref("#/components/callbacks/availabilityCallback"))) .tags(new ArrayList()) .addTag(OASFactory.createObject(Tag.class) .name("Get Airlines") diff --git a/tck/src/main/java/org/eclipse/microprofile/openapi/tck/AirlinesAppTest.java b/tck/src/main/java/org/eclipse/microprofile/openapi/tck/AirlinesAppTest.java index b83d3bb2..e2e4b934 100644 --- a/tck/src/main/java/org/eclipse/microprofile/openapi/tck/AirlinesAppTest.java +++ b/tck/src/main/java/org/eclipse/microprofile/openapi/tck/AirlinesAppTest.java @@ -1128,4 +1128,35 @@ public void testOpenAPIDefinitionExtension(String type) { vr.body("x-openapi-definition", equalTo("test-openapi-definition")); } + @Test(dataProvider = "formatProvider") + public void testRef(String type) { + ValidatableResponse vr = callEndpoint(type); + + vr.body("components.responses.FoundBookingsARef.$ref", equalTo("#/components/responses/FoundBookings")); + vr.body("components.responses.FoundBookingsARef.description", equalTo("Found Bookings Reference")); + + vr.body("components.parameters.usernameARef.$ref", equalTo("#/components/parameters/username")); + vr.body("components.parameters.usernameARef.description", equalTo("username reference")); + + vr.body("components.examples.userARef.$ref", equalTo("#/components/examples/user")); + vr.body("components.examples.userARef.description", equalTo("User reference")); + vr.body("components.examples.userARef.summary", equalTo("Referenced example")); + + vr.body("components.requestBodies.reviewARef.$ref", equalTo("#/components/requestBodies/review")); + vr.body("components.requestBodies.reviewARef.description", equalTo("Review reference")); + + vr.body("components.headers.Request-Limit-ARef.$ref", equalTo("#/components/headers/Request-Limit")); + vr.body("components.headers.Request-Limit-ARef.description", equalTo("Request-Limit reference")); + + vr.body("components.securitySchemes.httpTestSchemeARef.$ref", + equalTo("#/components/securitySchemes/httpTestScheme")); + vr.body("components.securitySchemes.httpTestSchemeARef.description", equalTo("httpTestScheme reference")); + + vr.body("components.links.UserNameARef.$ref", equalTo("#/components/links/UserName")); + vr.body("components.links.UserNameARef.description", equalTo("UserName reference")); + + vr.body("components.callbacks.GetBookingsARef.$ref", + equalTo("#/components/callbacks/GetBookings")); + } + } diff --git a/tck/src/main/java/org/eclipse/microprofile/openapi/tck/ModelReaderAppTest.java b/tck/src/main/java/org/eclipse/microprofile/openapi/tck/ModelReaderAppTest.java index ae928428..f7284acb 100644 --- a/tck/src/main/java/org/eclipse/microprofile/openapi/tck/ModelReaderAppTest.java +++ b/tck/src/main/java/org/eclipse/microprofile/openapi/tck/ModelReaderAppTest.java @@ -327,6 +327,37 @@ public void testComponents(String type) { vr.body("components.links.UserName", notNullValue()); } + @Test(dataProvider = "formatProvider") + public void testReferences(String type) { + ValidatableResponse vr = callEndpoint(type); + + vr.body("components.responses.FoundBookingsRef.$ref", equalTo("#/components/responses/FoundBookings")); + vr.body("components.responses.FoundBookingsRef.description", equalTo("Found Bookings Reference")); + + vr.body("components.parameters.usernameRef.$ref", equalTo("#/components/parameters/username")); + vr.body("components.parameters.usernameRef.description", equalTo("username reference")); + + vr.body("components.examples.userRef.$ref", equalTo("#/componets/examples/user")); + vr.body("components.examples.userRef.description", equalTo("User reference")); + vr.body("components.examples.userRef.summary", equalTo("Referenced example")); + + vr.body("components.requestBodies.reviewRef.$ref", equalTo("#/components/requestBodies/review")); + vr.body("components.requestBodies.reviewRef.description", equalTo("Review reference")); + + vr.body("components.headers.Request-Limit-Ref.$ref", equalTo("#/components/headers/Request-Limit")); + vr.body("components.headers.Request-Limit-Ref.description", equalTo("Request-Limit reference")); + + vr.body("components.securitySchemes.httpTestSchemeRef.$ref", + equalTo("#/components/securitySchemes/httpTestScheme")); + vr.body("components.securitySchemes.httpTestSchemeRef.description", equalTo("httpTestScheme reference")); + + vr.body("components.links.UserNameRef.$ref", equalTo("#/components/links/UserName")); + vr.body("components.links.UserNameRef.description", equalTo("UserName reference")); + + vr.body("components.callbacks.availabilityCallbackRef.$ref", + equalTo("#/components/callbacks/availabilityCallback")); + } + @Test(dataProvider = "formatProvider") public void testHeaderInComponents(String type) { ValidatableResponse vr = callEndpoint(type);