diff --git a/dev/com.ibm.ws.microprofile.openapi_fat/fat/src/com/ibm/ws/microprofile/openapi/validation/fat/OpenAPIValidationTestFive.java b/dev/com.ibm.ws.microprofile.openapi_fat/fat/src/com/ibm/ws/microprofile/openapi/validation/fat/OpenAPIValidationTestFive.java index 0b70f5e4439..8bbf37ef6d6 100644 --- a/dev/com.ibm.ws.microprofile.openapi_fat/fat/src/com/ibm/ws/microprofile/openapi/validation/fat/OpenAPIValidationTestFive.java +++ b/dev/com.ibm.ws.microprofile.openapi_fat/fat/src/com/ibm/ws/microprofile/openapi/validation/fat/OpenAPIValidationTestFive.java @@ -1,6 +1,11 @@ package com.ibm.ws.microprofile.openapi.validation.fat; +import static org.hamcrest.Matchers.empty; +import static org.hamcrest.Matchers.not; import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertThat; + +import java.util.List; import org.junit.AfterClass; import org.junit.BeforeClass; @@ -35,7 +40,7 @@ public class OpenAPIValidationTestFive { public static LibertyServer server; @ClassRule - public static RepeatTests r = FATSuite.repeatPre40(SERVER_NAME); + public static RepeatTests r = FATSuite.defaultRepeat(SERVER_NAME); private static final String OPENAPI_VALIDATION_YAML = "Validation"; @@ -64,43 +69,48 @@ public static void tearDown() throws Exception { @Test public void testTags() throws Exception { - assertNotNull("The Tag Validator should have been triggered by the missing 'name' field", - server.waitForStringInLog( + assertNotEmpty("The Tag Validator should have been triggered by the missing 'name' field", + server.findStringsInLogs( " - Message: Required \"name\" field is missing or is set to an invalid value, Location: #/tags")); } @Test public void testDiscriminator() throws Exception { - assertNotNull("The Discriminator validator should have been triggered by the missing 'propertyName' field", - server.waitForStringInLog( + assertNotEmpty("The Discriminator validator should have been triggered by the missing 'propertyName' field", + server.findStringsInLogs( "- Message: Required \"propertyName\" field is missing or is set to an invalid value,*")); } @Test public void testSchema() throws Exception { - assertNotNull("The Schema validator should have been triggered by the missing 'items' field", - server.waitForStringInLog( + assertNotEmpty("The Schema validator should have been triggered by the missing 'items' field", + server.findStringsInLogs( " - Message: The Schema Object of \"array\" type must have \"items\" property defined, Location: #/paths/~1availability/get/parameters/schema")); - assertNotNull("The Schema validator should have been triggered by the invalid 'multipleOf' field", - server.waitForStringInLog( + assertNotEmpty("The Schema validator should have been triggered by the invalid 'multipleOf' field", + server.findStringsInLogs( " - Message: The Schema Object must have the \"multipleOf\" property set to a number strictly greater than zero, Location: #/paths/~1availability/get/parameters/schema")); - assertNotNull("The Schema validator should have been triggered by the invalid 'minItems' field", - server.waitForStringInLog( + assertNotEmpty("The Schema validator should have been triggered by the invalid 'minItems' field", + server.findStringsInLogs( "- Message: The \"minItems\" property of the Schema Object must be greater than or equal to zero, Location: #/paths/~1availability/get/parameters/schema")); - assertNotNull("The Schema validator should have been triggered by the invalid 'maxItems' field", - server.waitForStringInLog( + assertNotEmpty("The Schema validator should have been triggered by the invalid 'maxItems' field", + server.findStringsInLogs( " - Message: The \"maxItems\" property of the Schema Object must be greater than or equal to zero, Location: #/paths/~1availability/get/parameters/schema")); - assertNotNull("The Schema validator should have been triggered by the invalid 'minProperties' field", - server.waitForStringInLog( + assertNotEmpty("The Schema validator should have been triggered by the invalid 'minProperties' field", + server.findStringsInLogs( " - Message: The \"minProperties\" property of the Schema Object must be greater than or equal to zero, Location: #/paths/~1availability/get/parameters/schema")); - assertNotNull("The Schema validator should have been triggered by the invalid 'maxProperties' field", - server.waitForStringInLog( + assertNotEmpty("The Schema validator should have been triggered by the invalid 'maxProperties' field", + server.findStringsInLogs( " - Message: The \"maxProperties\" property of the Schema Object must be greater than or equal to zero, Location: #/paths/~1availability/get/parameters/schema")); - assertNotNull("The Schema validator should have been triggered by the invalid 'minItems' field", - server.waitForStringInLog( + assertNotEmpty("The Schema validator should have been triggered by the invalid 'minItems' field", + server.findStringsInLogs( " - Message: The \"minItems\" property is not appropriate for the Schema Object of \"object\" type, Location: #/paths/~1availability/get/parameters/schema")); - assertNotNull("The Schema validator should have been triggered by the invalid 'maxItems' field", - server.waitForStringInLog( + assertNotEmpty("The Schema validator should have been triggered by the invalid 'maxItems' field", + server.findStringsInLogs( " - Message: The \"maxItems\" property is not appropriate for the Schema Object of \"object\" type, Location: #/paths/~1availability/get/parameters/schema")); } + + private void assertNotEmpty(String message, + List stringsInLogs) { + assertThat(message, stringsInLogs, not(empty())); + } } diff --git a/dev/com.ibm.ws.microprofile.openapi_fat/fat/src/com/ibm/ws/microprofile/openapi/validation/fat/OpenAPIValidationTestFour.java b/dev/com.ibm.ws.microprofile.openapi_fat/fat/src/com/ibm/ws/microprofile/openapi/validation/fat/OpenAPIValidationTestFour.java index a6a5db3c04c..d753213608f 100644 --- a/dev/com.ibm.ws.microprofile.openapi_fat/fat/src/com/ibm/ws/microprofile/openapi/validation/fat/OpenAPIValidationTestFour.java +++ b/dev/com.ibm.ws.microprofile.openapi_fat/fat/src/com/ibm/ws/microprofile/openapi/validation/fat/OpenAPIValidationTestFour.java @@ -1,7 +1,14 @@ package com.ibm.ws.microprofile.openapi.validation.fat; +import static componenttest.rules.repeater.MicroProfileActions.MP70_EE10_ID; +import static componenttest.rules.repeater.MicroProfileActions.MP70_EE11_ID; +import static org.hamcrest.Matchers.empty; +import static org.hamcrest.Matchers.hasSize; +import static org.hamcrest.Matchers.not; import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertTrue; +import static org.junit.Assert.assertThat; + +import java.util.List; import org.junit.AfterClass; import org.junit.BeforeClass; @@ -12,6 +19,7 @@ import com.ibm.ws.microprofile.openapi.fat.FATSuite; import componenttest.annotation.Server; +import componenttest.annotation.SkipForRepeat; import componenttest.custom.junit.runner.FATRunner; import componenttest.rules.repeater.RepeatTests; import componenttest.topology.impl.LibertyServer; @@ -34,7 +42,7 @@ public class OpenAPIValidationTestFour { public static LibertyServer server; @ClassRule - public static RepeatTests r = FATSuite.repeatPre40(SERVER_NAME); + public static RepeatTests r = FATSuite.defaultRepeat(SERVER_NAME); private static final String OPENAPI_VALIDATION_YAML = "Validation"; @@ -60,120 +68,134 @@ public static void tearDown() throws Exception { server.stopServer("CWWKO1650E", "CWWKO1651W"); } + @SkipForRepeat({ + MP70_EE10_ID, // Disable for mpOpenAPI-4.0 until we have a fix for + MP70_EE11_ID, // https://github.com/smallrye/smallrye-open-api/issues/1987 + }) @Test public void testRef() throws Exception { - assertNotNull("The Reference validator should have been triggered by the invalid reference", - server.waitForStringInLog( + assertNotEmpty("The Reference validator should have been triggered by the invalid reference", + server.findStringsInLogs( " - Message: The \"#/invalidRef/schemas/testSchema\" reference value is not in a valid format, Location: #/paths/~1/get/responses/200/content/application~1json/schema/items")); - assertNotNull("The Reference validator should have been triggered by the invalid reference", - server.waitForStringInLog( + assertNotEmpty("The Reference validator should have been triggered by the invalid reference", + server.findStringsInLogs( " - Message: The \"#/invalidRef/schemas/testSchema\" value is an invalid reference, Location: #/paths/~1/get/responses/200/content/application~1json/schema/items")); - assertNotNull("The Reference validator should have been triggered by the invalid reference", - server.waitForStringInLog( + assertNotEmpty("The Reference validator should have been triggered by the invalid reference", + server.findStringsInLogs( " - Message: The \"#/components/components/schemas/Date\" reference value is not in a valid format, Location: #/paths/~1availability/get/parameters/schema")); - assertNotNull("The Reference validator should have been triggered by the invalid reference", - server.waitForStringInLog( + assertNotEmpty("The Reference validator should have been triggered by the invalid reference", + server.findStringsInLogs( " - Message: The \"#/components/components/schemas/Date\" value is an invalid reference, Location: #/paths/~1availability/get/parameters/schema")); - assertNotNull("The Reference validator should have been triggered by the invalid reference", - server.waitForStringInLog( + assertNotEmpty("The Reference validator should have been triggered by the invalid reference", + server.findStringsInLogs( " - Message: The \"#/components/schemas/\" reference value is not in a valid format, Location: #/paths/~1availability/get/parameters/schema")); - assertNotNull("The Reference validator should have been triggered by the invalid reference", - server.waitForStringInLog( + assertNotEmpty("The Reference validator should have been triggered by the invalid reference", + server.findStringsInLogs( " - Message: The \"#/components/schemas/\" value is an invalid reference, Location: #/paths/~1availability/get/parameters/schema")); - assertNotNull("The Reference validator should have been triggered by the invalid reference", - server.waitForStringInLog( + assertNotEmpty("The Reference validator should have been triggered by the invalid reference", + server.findStringsInLogs( " - Message: The \"#/components/schemas/ \" reference value is not defined within the Components Object, Location: #/paths/~1availability/get/parameters/schema")); - assertNotNull("The Reference validator should have been triggered by the invalid reference", - server.waitForStringInLog( + assertNotEmpty("The Reference validator should have been triggered by the invalid reference", + server.findStringsInLogs( " - Message: The \"#/components/schemas/ \" value is an invalid reference, Location: #/paths/~1availability/get/parameters/schema")); - assertNotNull("The Reference validator should have been triggered by the invalid reference", - server.waitForStringInLog( + assertNotEmpty("The Reference validator should have been triggered by the invalid reference", + server.findStringsInLogs( " - Message: The \"#/components/schemas/Airport/Cat\" reference value is not in a valid format, Location: #/paths/~1availability/get/parameters/schema")); - assertNotNull("The Reference validator should have been triggered by the invalid reference", - server.waitForStringInLog( + assertNotEmpty("The Reference validator should have been triggered by the invalid reference", + server.findStringsInLogs( " - Message: The \"#/components/schemas/Airport/Cat\" value is an invalid reference, Location: #/paths/~1availability/get/parameters/schema")); - assertNotNull("The Reference validator should have been triggered by the invalid reference", - server.waitForStringInLog( + assertNotEmpty("The Reference validator should have been triggered by the invalid reference", + server.findStringsInLogs( " - Message: The \"#/components/schemas/#\" reference value is not defined within the Components Object, Location: #/paths/~1availability/get/parameters/schema")); - assertNotNull("The Reference validator should have been triggered by the invalid reference", - server.waitForStringInLog( + assertNotEmpty("The Reference validator should have been triggered by the invalid reference", + server.findStringsInLogs( " - Message: The \"#/components/schemas/#\" value is an invalid reference, Location: #/paths/~1availability/get/parameters/schema")); - assertNotNull("The Reference validator should have been triggered by the invalid reference", - server.waitForStringInLog( + assertNotEmpty("The Reference validator should have been triggered by the invalid reference", + server.findStringsInLogs( " - Message: The \"#/\" reference value is not in a valid format, Location: #/paths/~1availability/get/parameters/schema")); - assertNotNull("The Reference validator should have been triggered by the invalid reference", - server.waitForStringInLog( + assertNotEmpty("The Reference validator should have been triggered by the invalid reference", + server.findStringsInLogs( " - Message: The \"#/\" value is an invalid reference, Location: #/paths/~1availability/get/parameters/schema")); - assertNotNull("The Reference validator should have been triggered by the invalid reference", - server.waitForStringInLog( + assertNotEmpty("The Reference validator should have been triggered by the invalid reference", + server.findStringsInLogs( " - Message: The \"#/components/Flight\" reference value is not in a valid format, Location: #/paths/~1availability/get/responses/200/content/applictaion~1json/schema/items")); - assertNotNull("The Reference validator should have been triggered by the invalid reference", - server.waitForStringInLog( + assertNotEmpty("The Reference validator should have been triggered by the invalid reference", + server.findStringsInLogs( " - Message: The \"#/components/Flight\" value is an invalid reference, Location: #/paths/~1availability/get/responses/200/content/applictaion~1json/schema/items")); - assertNotNull("The Reference validator should have been triggered by the invalid reference", - server.waitForStringInLog( + assertNotEmpty("The Reference validator should have been triggered by the invalid reference", + server.findStringsInLogs( " - Message: The \"#/components//Booking\" reference value is invalid, Location: #/paths/~1bookings/get/responses/200/content/application~1json/schema/items")); - assertNotNull("The Reference validator should have been triggered by the invalid reference", - server.waitForStringInLog( + assertNotEmpty("The Reference validator should have been triggered by the invalid reference", + server.findStringsInLogs( " - Message: The \"#/components//Booking\" value is an invalid reference, Location: #/paths/~1bookings/get/responses/200/content/application~1json/schema/items")); - assertNotNull("The Reference validator should have been triggered by the invalid reference", - server.waitForStringInLog( + assertNotEmpty("The Reference validator should have been triggered by the invalid reference", + server.findStringsInLogs( " - Message: The \"#/components/schemas\" reference value is not in a valid format, Location: #/paths/~1bookings/post/callbacks/getBookings//get/responses/200/content/application~1json/schema/items")); - assertNotNull("The Reference validator should have been triggered by the invalid reference", - server.waitForStringInLog( + assertNotEmpty("The Reference validator should have been triggered by the invalid reference", + server.findStringsInLogs( " - Message: The \"#/components/schemas\" value is an invalid reference, Location: #/paths/~1bookings/post/callbacks/getBookings//get/responses/200/content/application~1json/schema/items")); - assertNotNull("The Reference validator should have been triggered by the invalid reference", - server.waitForStringInLog( + assertNotEmpty("The Reference validator should have been triggered by the invalid reference", + server.findStringsInLogs( " - Message: The \"#/components/requestBodies/Pet\" reference value is not defined within the Components Object, Location: #/paths/~1bookings/post/requestBody")); - assertNotNull("The Reference validator should have been triggered by the invalid reference", - server.waitForStringInLog( + assertNotEmpty("The Reference validator should have been triggered by the invalid reference", + server.findStringsInLogs( " - Message: The \"#/components/requestBodies/Pet\" value is an invalid reference, Location: #/paths/~1bookings/post/requestBody")); - assertNotNull("The Reference validator should have been triggered by the invalid reference", - server.waitForStringInLog( + assertNotEmpty("The Reference validator should have been triggered by the invalid reference", + server.findStringsInLogs( " - Message: The \"#/components/responses/Pet\" reference value is not defined within the Components Object,*")); - assertNotNull("The Reference validator should have been triggered by the invalid reference", + assertNotEmpty("The Reference validator should have been triggered by the invalid reference", server - .waitForStringInLog(" - Message: The \"#/components/responses/Pet\" value is an invalid reference,*")); - assertNotNull("The Reference validator should have been triggered by the invalid reference", - server.waitForStringInLog( + .findStringsInLogs(" - Message: The \"#/components/responses/Pet\" value is an invalid reference,*")); + assertNotEmpty("The Reference validator should have been triggered by the invalid reference", + server.findStringsInLogs( " - Message: The \"#/components/schemas/schemas\" reference value is not defined within the Components Object,*")); - assertNotNull("The Reference validator should have been triggered by the invalid reference", - server.waitForStringInLog( + assertNotEmpty("The Reference validator should have been triggered by the invalid reference", + server.findStringsInLogs( " - Message: The \"#/components/schemas/schemas\" value is an invalid reference,*")); - assertNotNull("The Reference validator should have been triggered by the invalid reference", - server.waitForStringInLog( + assertNotEmpty("The Reference validator should have been triggered by the invalid reference", + server.findStringsInLogs( " - Message: The \"#/components/schemas/Pet\" reference value is not defined within the Components Object,*")); - assertNotNull("The Reference validator should have been triggered by the invalid reference", - server.waitForStringInLog(" - Message: The \"#/components/schemas/Pet\" value is an invalid reference,*")); - assertNotNull("The Reference validator should have been triggered by the invalid reference", - server.waitForStringInLog( + assertNotEmpty("The Reference validator should have been triggered by the invalid reference", + server.findStringsInLogs(" - Message: The \"#/components/schemas/Pet\" value is an invalid reference,*")); + assertNotEmpty("The Reference validator should have been triggered by the invalid reference", + server.findStringsInLogs( " - Message: The \"#/components/examples/Pet\" reference value is not defined within the Components Object, Location: #/paths/~1reviews/post/requestBody/content/application~1json/examples/review")); - assertNotNull("The Reference validator should have been triggered by the invalid reference", - server.waitForStringInLog( + assertNotEmpty("The Reference validator should have been triggered by the invalid reference", + server.findStringsInLogs( " - Message: The \"#/components/examples/Pet\" value is an invalid reference, Location: #/paths/~1reviews/post/requestBody/content/application~1json/examples/review")); } @Test public void testCallbacks() throws Exception { - assertNotNull("The Callback validator should have been triggered by the invalid URL", - server.waitForStringInLog( + assertNotEmpty("The Callback validator should have been triggered by the invalid URL", + server.findStringsInLogs( " - Message: The URL template of Callback Object is empty and is not a valid URL, Location: #/paths/~1bookings/post/callbacks/getBookings")); - assertNotNull( + assertNotEmpty( "The Callback validator should have been triggered by the invalid substitution variables in the URL", - server.waitForStringInLog(" - Message: The Callback Object contains invalid substitution variables:*")); - assertNotNull("The Callback validator should have been triggered by the invalid runtime expression", - server.waitForStringInLog( + server.findStringsInLogs(" - Message: The Callback Object contains invalid substitution variables:*")); + assertNotEmpty("The Callback validator should have been triggered by the invalid runtime expression", + server.findStringsInLogs( " - Message: The Callback Object must contain a valid runtime expression as defined in the OpenAPI Specification.*")); } @Test public void testPathItems() throws Exception { - assertNotNull("The PathItem validator should have been triggered by the by ", - server.waitForStringInLog(" - Message: The Path Item Object must contain a valid path.*")); - assertNotNull("The PathItem validator should have been triggered by the missing parameter definition", + assertNotEmpty("The PathItem validator should have been triggered by the by ", + server.findStringsInLogs(" - Message: The Path Item Object must contain a valid path.")); + assertNotEmpty("The PathItem validator should have been triggered by the missing parameter definition", server - .waitForStringInLog(" - Message: The Path Item Object must contain a valid path. The format of the*")); - assertTrue("The PathItem validator should have been triggered by the by the duplicate path", - server.waitForMultipleStringsInLog(2, " - Message: The Path Item Object must contain a valid path.*") == 2); + .findStringsInLogs(" - Message: The Path Item Object must contain a valid path. The format of the")); + assertThat("The PathItem validator should have been triggered by the by the duplicate path", + server.findStringsInLogs(" - Message: The Path Item Object must contain a valid path."), + hasSize(4)); + } + + /** + * @param string + * @param stringsInLogs + */ + private void assertNotEmpty(String message, + List stringsInLogs) { + assertThat(message, stringsInLogs, not(empty())); } } diff --git a/dev/com.ibm.ws.microprofile.openapi_fat/fat/src/com/ibm/ws/microprofile/openapi/validation/fat/OpenAPIValidationTestOne.java b/dev/com.ibm.ws.microprofile.openapi_fat/fat/src/com/ibm/ws/microprofile/openapi/validation/fat/OpenAPIValidationTestOne.java index e1a7cad26ef..a3a562c645b 100644 --- a/dev/com.ibm.ws.microprofile.openapi_fat/fat/src/com/ibm/ws/microprofile/openapi/validation/fat/OpenAPIValidationTestOne.java +++ b/dev/com.ibm.ws.microprofile.openapi_fat/fat/src/com/ibm/ws/microprofile/openapi/validation/fat/OpenAPIValidationTestOne.java @@ -1,6 +1,12 @@ package com.ibm.ws.microprofile.openapi.validation.fat; +import static org.hamcrest.Matchers.empty; +import static org.hamcrest.Matchers.hasSize; +import static org.hamcrest.Matchers.not; import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertThat; + +import java.util.List; import org.junit.AfterClass; import org.junit.BeforeClass; @@ -8,6 +14,7 @@ import org.junit.Test; import org.junit.runner.RunWith; +import com.ibm.websphere.simplicity.log.Log; import com.ibm.ws.microprofile.openapi.fat.FATSuite; import componenttest.annotation.Server; @@ -16,6 +23,7 @@ import componenttest.rules.repeater.MicroProfileActions; import componenttest.rules.repeater.RepeatTests; import componenttest.topology.impl.LibertyServer; +import componenttest.topology.utils.HttpRequest; import componenttest.topology.utils.HttpUtils; /** @@ -52,7 +60,7 @@ public class OpenAPIValidationTestOne { public static LibertyServer server; @ClassRule - public static RepeatTests r = FATSuite.repeatPre40(SERVER_NAME); + public static RepeatTests r = FATSuite.defaultRepeat(SERVER_NAME); private static final String OPENAPI_VALIDATION_YAML = "openapi_validation"; @@ -71,6 +79,10 @@ public static void setUpTest() throws Exception { assertNotNull("The application openapi_validation was processed and an OpenAPI document was produced.", server.waitForStringInLog("CWWKO1660I.*and an OpenAPI document was produced.")); // wait for application to // be processed + assertThat("Validation errors were reported", server.findStringsInLogs("CWWKO1650E"), not(empty())); + assertThat("Validation warnings were reported", server.findStringsInLogs("CWWKO1651W"), not(empty())); + String openApiDoc = new HttpRequest(server, "/openapi").run(String.class); + Log.info(OpenAPIValidationTestOne.class, "setUpTest", "OpenAPI doc:\n" + openApiDoc); } @AfterClass @@ -85,18 +97,20 @@ public static void tearDown() throws Exception { MicroProfileActions.MP41_ID, MicroProfileActions.MP50_ID, MicroProfileActions.MP60_ID, - MicroProfileActions.MP61_ID + MicroProfileActions.MP61_ID, + MicroProfileActions.MP70_EE10_ID, + MicroProfileActions.MP70_EE11_ID }) - public void testInfoValidation() { + public void testInfoValidation() throws Exception { - assertNotNull("The Info Validator should have been triggered by invalid URL", - server.waitForStringInLog( + assertNotEmpty("The Info Validator should have been triggered by invalid URL", + server.findStringsInLogs( "Message: The Info Object must contain a valid URL. The \"not in URL format\" value specified for \"termsOfService\"*")); - assertNotNull("The Info Validator should have been triggered by missing \"version\" field", - server.waitForStringInLog( + assertNotEmpty("The Info Validator should have been triggered by missing \"version\" field", + server.findStringsInLogs( "Message: Required \"version\" field is missing or is set to an invalid value, Location: #/info")); - assertNotNull("The Info Validator should have been triggered by missing \"title\" field", - server.waitForStringInLog( + assertNotEmpty("The Info Validator should have been triggered by missing \"title\" field", + server.findStringsInLogs( "Message: Required \"title\" field is missing or is set to an invalid value, Location: #/info")); } @@ -104,128 +118,134 @@ public void testInfoValidation() { @SkipForRepeat({ MicroProfileActions.MP22_ID, MicroProfileActions.MP33_ID }) - public void testInfoValidation20() { + public void testInfoValidation20() throws Exception { /* * The SmallRye implementation always injects a title and version if one is not * present... so remove the relevant assertions when running against the * mpOpenAPI-2.0 feature */ - assertNotNull("The Info Validator should have been triggered by invalid URL", - server.waitForStringInLog( + assertNotEmpty("The Info Validator should have been triggered by invalid URL", + server.findStringsInLogs( "Message: The Info Object must contain a valid URL. The \"not in URL format\" value specified for \"termsOfService\"*")); } @Test - public void testContactValidation() { - assertNotNull("The Contact Validator should have been triggered by invalid URL", - server.waitForStringInLog( + public void testContactValidation() throws Exception { + assertNotEmpty("The Contact Validator should have been triggered by invalid URL", + server.findStringsInLogs( "Message: The Contact Object must contain a valid URL. The \"not in URL Format\" value specified*")); - assertNotNull("The Contact Validator should have been triggered by invalid email", - server.waitForStringInLog( + assertNotEmpty("The Contact Validator should have been triggered by invalid email", + server.findStringsInLogs( "Message: The Contact Object must contain a valid email address. The \"not an email\" value*")); } @Test - public void testLicenseValidation() { - assertNotNull("The License Validator should have been triggered by missing \"name\" field", - server.waitForStringInLog("Message: Required \"name\" field is missing or is set to an invalid value")); - assertNotNull("The License Validator should have been triggered by invalid URL", - server.waitForStringInLog("The License Object must contain a valid URL. The \"not in URL format\" value")); + public void testLicenseValidation() throws Exception { + assertNotEmpty("The License Validator should have been triggered by missing \"name\" field", + server.findStringsInLogs("Message: Required \"name\" field is missing or is set to an invalid value")); + assertNotEmpty("The License Validator should have been triggered by invalid URL", + server.findStringsInLogs("The License Object must contain a valid URL. The \"not in URL format\" value")); } @Test - public void testServerValidation() { - assertNotNull("The Server Validator should have been triggered by invalid URL", - server.waitForMultipleStringsInLog(4, "Message: The Server Object must contain a valid URL*")); - assertNotNull("The Server Validator should have been triggered by missing \"url\" field", - server.waitForStringInLog( + public void testServerValidation() throws Exception { + assertThat("The Server Validator should have been triggered by invalid URL", + server.findStringsInLogs("Message: The Server Object must contain a valid URL*"), + hasSize(4)); + assertNotEmpty("The Server Validator should have been triggered by missing \"url\" field", + server.findStringsInLogs( "Message: Required \"url\" field is missing or is set to an invalid value, Location: #/paths/~1reviews/get/servers")); - assertNotNull("The Server Validator should have been triggered by undefined variable", - server.waitForStringInLog("The \"extraVariable\" variable in the Server Object is not defined*")); - assertNotNull("The Server Validator should have been triggered by undefined variable", - server.waitForStringInLog("Message: The \"id\" variable in the Server Object is not defined*")); + assertNotEmpty("The Server Validator should have been triggered by undefined variable", + server.findStringsInLogs("The \"extraVariable\" variable in the Server Object is not defined*")); + assertNotEmpty("The Server Validator should have been triggered by undefined variable", + server.findStringsInLogs("Message: The \"id\" variable in the Server Object is not defined*")); } @Test - public void testServerVariableValidation() { - assertNotNull("The Server Variable Validator should have been triggered by a missing \"default\" field", - server.waitForStringInLog("Message: Required \"default\" field is missing or is set to an invalid value*")); + public void testServerVariableValidation() throws Exception { + assertNotEmpty("The Server Variable Validator should have been triggered by a missing \"default\" field", + server.findStringsInLogs("Message: Required \"default\" field is missing or is set to an invalid value*")); } @Test - public void testPathItemValidation() { - assertNotNull( + public void testPathItemValidation() throws Exception { + assertNotEmpty( "The PathItem Validator should have been triggered by teh missing \"required\" field in a path parameter", - server.waitForStringInLog( + server.findStringsInLogs( "The \"id\" path parameter from the \"GET\" operation of the path \"/bookings/\\{id\\}\" does not contain the \"required\" field or its value is not \"true\"")); - assertNotNull("The PathItem Validator should have been triggered by an undeclared path parameter", - server.waitForStringInLog( + assertNotEmpty("The PathItem Validator should have been triggered by an undeclared path parameter", + server.findStringsInLogs( "The \"GET\" operation of the \"/reviews/\\{id\\}\" path does not define a path parameter that is declared: \"id\"")); - assertNotNull("The PathItem Validator should have been triggered by an invalid path", - server.waitForStringInLog( + assertNotEmpty("The PathItem Validator should have been triggered by an invalid path", + server.findStringsInLogs( "The Path Item Object must contain a valid path. The \"GET\" operation from the \"/reviews/\\{airline\\}\" path defines a duplicated \"path\" parameter: \"airline\"")); - assertNotNull("The PathItem Validator should have been triggered by an invalid path", - server.waitForStringInLog( + assertNotEmpty("The PathItem Validator should have been triggered by an invalid path", + server.findStringsInLogs( "The Paths Object contains an invalid path. The \"noSlashPath\" path value does not begin with a slash*")); - assertNotNull("The PathItem Validator should have been triggered by an invalid path", - server.waitForStringInLog( + assertNotEmpty("The PathItem Validator should have been triggered by an invalid path", + server.findStringsInLogs( "The Path Item Object must contain a valid path. The format of the \"/availability/\"*")); - assertNotNull( + assertNotEmpty( "The PathItem Validator should have been triggered by teh missing \"required\" field in a path parameter", - server.waitForStringInLog( + server.findStringsInLogs( " The \"userFirstName\" path parameter from the \"GET\" operation of the path \"/operationWithParam\" does not contain the \"required\" field")); - assertNotNull("The PathItem Validator should have been triggered by an invalid path", - server.waitForStringInLog( + assertNotEmpty("The PathItem Validator should have been triggered by an invalid path", + server.findStringsInLogs( "The Path Item Object must contain a valid path. The \"/\\{username\\}\" path defines \"3\" path parameters that are not declared: \"\\[pathWithUndeclaredParams, usernameParam, accountNumber\\]\"*")); - assertNotNull("The PathItem Validator should have been triggered by an undeclared path parameter", - server.waitForStringInLog( + assertNotEmpty("The PathItem Validator should have been triggered by an undeclared path parameter", + server.findStringsInLogs( "The \"GET\" operation from the \"/operationWithParam\" path defines one path parameter that is not declared: \"\\[userFirstName\\]\"")); } @Test - public void testOperationValidation() { - assertNotNull("The Operation Validator should have been triggered by the missing \"responses\" field", - server.waitForStringInLog( + public void testOperationValidation() throws Exception { + assertNotEmpty("The Operation Validator should have been triggered by the missing \"responses\" field", + server.findStringsInLogs( "Message: Required \"responses\" field is missing or is set to an invalid value, Location: #/paths/~1/get")); - assertNotNull("The Operation Validator should have been triggered by non-unique operationIDs", - server.waitForStringInLog( + assertNotEmpty("The Operation Validator should have been triggered by non-unique operationIDs", + server.findStringsInLogs( "Message: More than one Operation Objects with \"getReviewById\" value for \"operationId\" field was found. The \"operationId\" must be unique")); } @Test - public void testExternalDocsValidation() { - assertNotNull("The ExternalDocumentation Validator should have been triggered by an invalid URL", - server.waitForStringInLog( + public void testExternalDocsValidation() throws Exception { + assertNotEmpty("The ExternalDocumentation Validator should have been triggered by an invalid URL", + server.findStringsInLogs( "Message: The External Documentation Object must contain a valid URL. The \"not a URL\" value")); } @Test - public void testSecurityRequirementValidation() { - assertNotNull("The Security Requirement Validator should have been triggered by undeclared Security Scheme", - server.waitForStringInLog( + public void testSecurityRequirementValidation() throws Exception { + assertNotEmpty("The Security Requirement Validator should have been triggered by undeclared Security Scheme", + server.findStringsInLogs( "The \"reviewoauth2\" name provided for the Security Requirement Object does not correspond to a declared security scheme")); } @Test - public void testRequestBodyValidation() { - assertNotNull("The RequestBody Validator should have been triggered by the missing \"content\" field", - server.waitForStringInLog( + public void testRequestBodyValidation() throws Exception { + assertNotEmpty("The RequestBody Validator should have been triggered by the missing \"content\" field", + server.findStringsInLogs( "Message: Required \"content\" field is missing or is set to an invalid value, Location: #/paths/~1reviews/post/requestBody")); } @Test - public void testResponseValidation() { - assertNotNull("The Response Validator should have been triggered by the missing \"description\" field", - server.waitForStringInLog( + public void testResponseValidation() throws Exception { + assertNotEmpty("The Response Validator should have been triggered by the missing \"description\" field", + server.findStringsInLogs( "Message: Required \"description\" field is missing or is set to an invalid value*")); } @Test - public void testResponsesValidation() { - assertNotNull( + public void testResponsesValidation() throws Exception { + assertNotEmpty( "The Responses Validator should have been triggered by missing response code for successful operation", - server.waitForStringInLog( + server.findStringsInLogs( "Message: The Responses Object should contain at least one response code for a successful operation")); } + + private void assertNotEmpty(String message, + List stringsInLogs) { + assertThat(message, stringsInLogs, not(empty())); + } } diff --git a/dev/com.ibm.ws.microprofile.openapi_fat/fat/src/com/ibm/ws/microprofile/openapi/validation/fat/OpenAPIValidationTestSix.java b/dev/com.ibm.ws.microprofile.openapi_fat/fat/src/com/ibm/ws/microprofile/openapi/validation/fat/OpenAPIValidationTestSix.java index 44615b146c7..7ebe08879d9 100644 --- a/dev/com.ibm.ws.microprofile.openapi_fat/fat/src/com/ibm/ws/microprofile/openapi/validation/fat/OpenAPIValidationTestSix.java +++ b/dev/com.ibm.ws.microprofile.openapi_fat/fat/src/com/ibm/ws/microprofile/openapi/validation/fat/OpenAPIValidationTestSix.java @@ -28,7 +28,7 @@ public class OpenAPIValidationTestSix { public static LibertyServer server; @ClassRule - public static RepeatTests r = FATSuite.repeatPre40(SERVER_NAME); + public static RepeatTests r = FATSuite.defaultRepeat(SERVER_NAME); private static final String OPENAPI_VALIDATION_YAML = "Validation"; diff --git a/dev/com.ibm.ws.microprofile.openapi_fat/fat/src/com/ibm/ws/microprofile/openapi/validation/fat/OpenAPIValidationTestThree.java b/dev/com.ibm.ws.microprofile.openapi_fat/fat/src/com/ibm/ws/microprofile/openapi/validation/fat/OpenAPIValidationTestThree.java index aefe68042c3..d234475829d 100644 --- a/dev/com.ibm.ws.microprofile.openapi_fat/fat/src/com/ibm/ws/microprofile/openapi/validation/fat/OpenAPIValidationTestThree.java +++ b/dev/com.ibm.ws.microprofile.openapi_fat/fat/src/com/ibm/ws/microprofile/openapi/validation/fat/OpenAPIValidationTestThree.java @@ -34,7 +34,7 @@ public class OpenAPIValidationTestThree { public static LibertyServer server; @ClassRule - public static RepeatTests r = FATSuite.repeatPre40(SERVER_NAME); + public static RepeatTests r = FATSuite.defaultRepeat(SERVER_NAME); private static final String OPENAPI_VALIDATION_YAML = "Validation"; @@ -72,7 +72,9 @@ public void testPaths() throws Exception { MicroProfileActions.MP41_ID, MicroProfileActions.MP50_ID, MicroProfileActions.MP60_ID, - MicroProfileActions.MP61_ID + MicroProfileActions.MP61_ID, + MicroProfileActions.MP70_EE10_ID, + MicroProfileActions.MP70_EE11_ID, }) public void testBlankInfo() throws Exception { OpenAPITestUtil.waitForApplicationProcessorProcessedEvent(server, OPENAPI_VALIDATION_YAML); diff --git a/dev/com.ibm.ws.microprofile.openapi_fat/fat/src/com/ibm/ws/microprofile/openapi/validation/fat/OpenAPIValidationTestTwo.java b/dev/com.ibm.ws.microprofile.openapi_fat/fat/src/com/ibm/ws/microprofile/openapi/validation/fat/OpenAPIValidationTestTwo.java index 61964aba625..a2aececfe80 100644 --- a/dev/com.ibm.ws.microprofile.openapi_fat/fat/src/com/ibm/ws/microprofile/openapi/validation/fat/OpenAPIValidationTestTwo.java +++ b/dev/com.ibm.ws.microprofile.openapi_fat/fat/src/com/ibm/ws/microprofile/openapi/validation/fat/OpenAPIValidationTestTwo.java @@ -1,6 +1,12 @@ package com.ibm.ws.microprofile.openapi.validation.fat; +import static org.hamcrest.Matchers.empty; +import static org.hamcrest.Matchers.hasSize; +import static org.hamcrest.Matchers.not; import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertThat; + +import java.util.List; import org.junit.AfterClass; import org.junit.BeforeClass; @@ -42,7 +48,7 @@ public class OpenAPIValidationTestTwo { public static LibertyServer server; @ClassRule - public static RepeatTests r = FATSuite.repeatPre40(SERVER_NAME); + public static RepeatTests r = FATSuite.defaultRepeat(SERVER_NAME); private static final String OPENAPI_VALIDATION_YAML = "openapi_validation"; @@ -71,104 +77,112 @@ public static void tearDown() throws Exception { } @Test - public void testSecuritySchemeValidation() { - assertNotNull("The SecurityScheme Validator should have been triggered by the missing \"type\" field", - server.waitForStringInLog( + public void testSecuritySchemeValidation() throws Exception { + assertNotEmpty("The SecurityScheme Validator should have been triggered by the missing \"type\" field", + server.findStringsInLogs( "Message: Required \"type\" field is missing or is set to an invalid value, Location: #/components/securitySchemes/noType")); - assertNotNull( + assertNotEmpty( "The SecurityScheme Validator should have been triggered by the missing \"openIdConnectUrl\" field", - server.waitForStringInLog( + server.findStringsInLogs( "Message: Required \"openIdConnectUrl\" field is missing or is set to an invalid value, Location: #/components/securitySchemes/openIdConnectWithScheme")); - assertNotNull("The SecurityScheme Validator should have been triggered by the missing \"scheme\" field", - server.waitForStringInLog( + assertNotEmpty("The SecurityScheme Validator should have been triggered by the missing \"scheme\" field", + server.findStringsInLogs( "Message: Required \"scheme\" field is missing or is set to an invalid value, Location: #/components/securitySchemes/airlinesHttp")); - assertNotNull("The SecurityScheme Validator should have been triggered by the missing \"flows\" field", - server.waitForStringInLog( + assertNotEmpty("The SecurityScheme Validator should have been triggered by the missing \"flows\" field", + server.findStringsInLogs( "Message: Required \"flows\" field is missing or is set to an invalid value, Location: #/components/securitySchemes/reviewoauth2")); - assertNotNull("The SecurityScheme Validator should have been triggered by the missing \"scheme\" field", - server.waitForStringInLog( + assertNotEmpty("The SecurityScheme Validator should have been triggered by the missing \"scheme\" field", + server.findStringsInLogs( "Message: Required \"scheme\" field is missing or is set to an invalid value, Location: #/components/securitySchemes/httpWithOpenIdConnectUrl")); - assertNotNull("The SecurityScheme Validator should have been triggered by the missing \"name\" field", - server.waitForStringInLog( + assertNotEmpty("The SecurityScheme Validator should have been triggered by the missing \"name\" field", + server.findStringsInLogs( "Message: Required \"name\" field is missing or is set to an invalid value, Location: #/components/securitySchemes/ApiKeyWithScheme")); - assertNotNull("The SecurityScheme Validator should have been triggered by the missing \"in\" field", - server.waitForStringInLog( + assertNotEmpty("The SecurityScheme Validator should have been triggered by the missing \"in\" field", + server.findStringsInLogs( "Message: Required \"in\" field is missing or is set to an invalid value, Location: #/components/securitySchemes/ApiKeyWithScheme")); - assertNotNull("The SecurityScheme Validator should have been triggered by the missing \"in\" field", - server.waitForStringInLog( + assertNotEmpty("The SecurityScheme Validator should have been triggered by the missing \"in\" field", + server.findStringsInLogs( "Message: Required \"in\" field is missing or is set to an invalid value, Location: #/components/securitySchemes/ApiKeyWithInvalidIn")); - assertNotNull("The SecurityScheme Validator should have been triggered by invalid URL", - server.waitForStringInLog( + assertNotEmpty("The SecurityScheme Validator should have been triggered by invalid URL", + server.findStringsInLogs( "Message: The Security Scheme Object must contain a valid URL. The \"not a URL\" value specified for the URL is not valid*")); - assertNotNull("The SecurityScheme Validator should have been triggered by a non-applicable field", - server.waitForStringInLog( + assertNotEmpty("The SecurityScheme Validator should have been triggered by a non-applicable field", + server.findStringsInLogs( "Message: The \"scheme\" field with \"openIdConnectWithScheme\" value is not applicable for \"Security Scheme Object\" of \"openIdConnect\" type")); - assertNotNull("The SecurityScheme Validator should have been triggered by a non-applicable field", - server.waitForStringInLog( + assertNotEmpty("The SecurityScheme Validator should have been triggered by a non-applicable field", + server.findStringsInLogs( "Message: The \"name\" field with \"oauth2WithName\" value is not applicable for \"Security Scheme Object\" of \"oauth2\" type")); - assertNotNull("The SecurityScheme Validator should have been triggered by a non-applicable field", - server.waitForStringInLog( + assertNotEmpty("The SecurityScheme Validator should have been triggered by a non-applicable field", + server.findStringsInLogs( "Message: The \"openIdConnectUrl\" field with \"http://www.url.com\" value is not applicable for \"Security Scheme Object\" of \"http\" type")); - assertNotNull("The SecurityScheme Validator should have been triggered by a non-applicable field", - server.waitForStringInLog( + assertNotEmpty("The SecurityScheme Validator should have been triggered by a non-applicable field", + server.findStringsInLogs( "Message: The \"flows\" field is not applicable for \"Security Scheme Object\" of \"http\" type")); } @Test - public void testSecurityRequirementValidation() { - assertNotNull( + public void testSecurityRequirementValidation() throws Exception { + assertNotEmpty( "The SecurityRequirement Validator should have been triggered by SecurityScheme name that does not correspond to declared Security Scheme", - server.waitForStringInLog( + server.findStringsInLogs( "Message: The \"schemeNotInComponent\" name provided for the Security Requirement Object does not correspond to a declared security scheme, Location: #/paths/~1availability/get/security")); - assertNotNull( + assertNotEmpty( "The SecurityRequirement Validator should have been triggered by non-empty scope for an http Security Requirement Object", - server.waitForStringInLog( + server.findStringsInLogs( "Message: The \"airlinesHttp\" field of Security Requirement Object should be empty, but is: \"\\[write:app, read:app\\]\"")); - assertNotNull( + assertNotEmpty( "The SecurityRequirement Validator should have been triggered by an empty scope for openIdConnect Security Requirement Object", - server.waitForStringInLog( + server.findStringsInLogs( "Message: The \"openIdConnectWithScheme\" Security Requirement Object should specify be a list of scope names required for execution")); } @Test - public void testOAuthFlowValidation() { - assertNotNull("The OAuthFlow Validator should have been triggered by missing \"scopes\" field", - server.waitForMultipleStringsInLog(3, - "Message: Required \"scopes\" field is missing or is set to an invalid value*")); - assertNotNull("The OAuthFlow Validator should have been triggered by invalid URL", - server.waitForStringInLog( + public void testOAuthFlowValidation() throws Exception { + assertThat("The OAuthFlow Validator should have been triggered by missing \"scopes\" field", + server.findStringsInLogs( + "Message: Required \"scopes\" field is missing or is set to an invalid value*"), + hasSize(3)); + assertNotEmpty("The OAuthFlow Validator should have been triggered by invalid URL", + server.findStringsInLogs( "Message: The OAuth Flow Object must contain a valid URL. The \"invalid URL example\" value*")); } @Test - public void testOAuthFlowsValidation() { - assertNotNull("The OAuthFlows Validator should have been triggered by missing \"tokenUrl\" field", - server.waitForMultipleStringsInLog(2, - "Message: Required \"tokenUrl\" field is missing or is set to an invalid value")); - assertNotNull("The OAuthFlows Validator should have been triggered by non applicable field", - server.waitForStringInLog( + public void testOAuthFlowsValidation() throws Exception { + assertThat("The OAuthFlows Validator should have been triggered by missing \"tokenUrl\" field", + server.findStringsInLogs( + "Message: Required \"tokenUrl\" field is missing or is set to an invalid value"), + hasSize(2)); + assertNotEmpty("The OAuthFlows Validator should have been triggered by non applicable field", + server.findStringsInLogs( "Message: The \"authorizationUrl\" field with \"https://example.com/api/oauth/dialog\" value is not applicable for \"OAuth Flow Object\" of \"password\" type")); } @Test - public void testMediaTypeValidation() { - assertNotNull("The MediaType Validator should have been triggered by non-existant encoding property", - server.waitForMultipleStringsInLog(2, - "Message: The \"nonExistingField\" encoding property specified in the MediaType Object does not exist")); - assertNotNull( + public void testMediaTypeValidation() throws Exception { + assertThat("The MediaType Validator should have been triggered by non-existant encoding property", + server.findStringsInLogs( + "Message: The \"nonExistingField\" encoding property specified in the MediaType Object does not exist"), + hasSize(2)); + assertNotEmpty( "The MediaType Validator should have been triggered by mutually exclusive \"examples\" and \"example\" fields", - server.waitForStringInLog( + server.findStringsInLogs( "Message: The MediaType Object cannot have both \"examples\" and \"example\" fields*")); - assertNotNull("The MediaType Validator should have been triggered by null schema", - server.waitForStringInLog( + assertNotEmpty("The MediaType Validator should have been triggered by null schema", + server.findStringsInLogs( "Message: The encoding property specified cannot be validated because the corresponding schema property is null")); } @Test - public void testExampleValidation() { - assertNotNull( + public void testExampleValidation() throws Exception { + assertNotEmpty( "The Example Validator should have been triggered by mutually exclusive \"value\" and \"externalValue\" fields", - server.waitForStringInLog( + server.findStringsInLogs( "Message: The \"booking\" Example Object specifies both \"value\" and \"externalValue\" fields*")); } + + private void assertNotEmpty(String message, + List stringsInLogs) { + assertThat(message, stringsInLogs, not(empty())); + } } diff --git a/dev/io.openliberty.microprofile.openapi.2.0.internal.services/src/io/openliberty/microprofile/openapi20/internal/services/impl/OpenAPI30Validator.java b/dev/io.openliberty.microprofile.openapi.2.0.internal.services/src/io/openliberty/microprofile/openapi20/internal/services/impl/OpenAPI30Validator.java deleted file mode 100644 index bbd22cfdcd6..00000000000 --- a/dev/io.openliberty.microprofile.openapi.2.0.internal.services/src/io/openliberty/microprofile/openapi20/internal/services/impl/OpenAPI30Validator.java +++ /dev/null @@ -1,21 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2024 IBM Corporation and others. - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License 2.0 - * which accompanies this distribution, and is available at - * http://www.eclipse.org/legal/epl-2.0/ - * - * SPDX-License-Identifier: EPL-2.0 - *******************************************************************************/ -package io.openliberty.microprofile.openapi20.internal.services.impl; - -import org.osgi.service.component.annotations.Component; -import org.osgi.service.component.annotations.ConfigurationPolicy; - -import io.openliberty.microprofile.openapi20.internal.services.OASValidator; -import io.openliberty.microprofile.openapi20.internal.validation.OASValidatorImpl; - -@Component(service = OASValidator.class, configurationPolicy = ConfigurationPolicy.IGNORE) -public class OpenAPI30Validator extends OASValidatorImpl { - -} diff --git a/dev/io.openliberty.microprofile.openapi.2.0.internal/bnd.bnd b/dev/io.openliberty.microprofile.openapi.2.0.internal/bnd.bnd index 7f5943303f9..cae45f9ece7 100644 --- a/dev/io.openliberty.microprofile.openapi.2.0.internal/bnd.bnd +++ b/dev/io.openliberty.microprofile.openapi.2.0.internal/bnd.bnd @@ -1,5 +1,5 @@ #******************************************************************************* -# Copyright (c) 2020, 2022 IBM Corporation and others. +# Copyright (c) 2020, 2024 IBM Corporation and others. # All rights reserved. This program and the accompanying materials # are made available under the terms of the Eclipse Public License 2.0 # which accompanies this distribution, and is available at @@ -51,9 +51,11 @@ IBM-Default-Config: OSGI-INF/wlp/defaultInstances.xml io.openliberty.microprofile.openapi20.internal.ApplicationProcessor,\ io.openliberty.microprofile.openapi20.internal.MergeDisabledAlerter,\ io.openliberty.microprofile.openapi20.internal.OLOASFactoryResolverImpl,\ + io.openliberty.microprofile.openapi20.internal.ValidationComponent,\ io.openliberty.microprofile.openapi20.internal.css.CustomCSSProcessor,\ io.openliberty.microprofile.openapi20.internal.DefaultHostListenerImpl,\ - io.openliberty.microprofile.openapi20.internal.cache.ConfigSerializer + io.openliberty.microprofile.openapi20.internal.cache.ConfigSerializer,\ + io.openliberty.microprofile.openapi20.internal.validation.OASValidator30Impl WS-TraceGroup: MPOPENAPI diff --git a/dev/io.openliberty.microprofile.openapi.2.0.internal/src/io/openliberty/microprofile/openapi20/internal/ApplicationProcessor.java b/dev/io.openliberty.microprofile.openapi.2.0.internal/src/io/openliberty/microprofile/openapi20/internal/ApplicationProcessor.java index 8e17a171d6d..b56d14adc48 100644 --- a/dev/io.openliberty.microprofile.openapi.2.0.internal/src/io/openliberty/microprofile/openapi20/internal/ApplicationProcessor.java +++ b/dev/io.openliberty.microprofile.openapi.2.0.internal/src/io/openliberty/microprofile/openapi20/internal/ApplicationProcessor.java @@ -85,6 +85,9 @@ public class ApplicationProcessor { @Reference private ModelGenerator modelGenerator; + @Reference + private ValidationComponent validationComponent; + /** * The processApplication method processes applications that are added to the OpenLiberty instance. * @@ -283,7 +286,7 @@ private OpenAPIProvider processWebModule(final Container appContainer, final Web if (LoggingUtils.isEventEnabled(tc)) { Tr.event(tc, "Validate document"); } - OpenAPIUtils.validateDocument(openAPIModel); + validationComponent.validateAndReportErrors(openAPIModel); } catch (Throwable e) { if (LoggingUtils.isEventEnabled(tc)) { Tr.event(tc, "Failed to call OASValidator: " + e.getMessage()); diff --git a/dev/io.openliberty.microprofile.openapi.2.0.internal/src/io/openliberty/microprofile/openapi20/internal/ValidationComponent.java b/dev/io.openliberty.microprofile.openapi.2.0.internal/src/io/openliberty/microprofile/openapi20/internal/ValidationComponent.java new file mode 100644 index 00000000000..93fcca3725b --- /dev/null +++ b/dev/io.openliberty.microprofile.openapi.2.0.internal/src/io/openliberty/microprofile/openapi20/internal/ValidationComponent.java @@ -0,0 +1,123 @@ +/******************************************************************************* + * Copyright (c) 2024 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package io.openliberty.microprofile.openapi20.internal; + +import java.util.List; + +import org.eclipse.microprofile.openapi.models.OpenAPI; +import org.osgi.framework.ServiceReference; +import org.osgi.service.component.ComponentContext; +import org.osgi.service.component.annotations.Activate; +import org.osgi.service.component.annotations.Component; +import org.osgi.service.component.annotations.ConfigurationPolicy; +import org.osgi.service.component.annotations.Reference; + +import com.ibm.websphere.ras.Tr; +import com.ibm.websphere.ras.TraceComponent; + +import io.openliberty.microprofile.openapi20.internal.services.OASValidationResult; +import io.openliberty.microprofile.openapi20.internal.services.OASValidationResult.ValidationEvent; +import io.openliberty.microprofile.openapi20.internal.services.OASValidationResult.ValidationEvent.Severity; +import io.openliberty.microprofile.openapi20.internal.services.OASValidator; +import io.openliberty.microprofile.openapi20.internal.utils.MessageConstants; +import io.openliberty.microprofile.openapi20.internal.utils.ValidationMessageConstants; +import io.openliberty.microprofile.openapi20.internal.validation.ValidatorUtils; + +/** + * Calls {@link OASValidator} services to validate a model and reports the validation result in the log. + *

+ * The {@code OASValidator} to use is determined based on the OpenAPI spec version used by the model. + */ +@Component(configurationPolicy = ConfigurationPolicy.IGNORE, service = ValidationComponent.class) +public class ValidationComponent { + + private static final TraceComponent tc = Tr.register(ValidationComponent.class); + + private static final String VALIDATOR_REF = "validator"; + + protected ComponentContext ctx; + + @Reference(name = VALIDATOR_REF) + protected volatile List> validators; + + @Activate + private void activate(ComponentContext ctx) { + this.ctx = ctx; + } + + /** + * Validate an Open API model and report the results in the messages log + * + * @param model the model to validate + */ + public void validateAndReportErrors(OpenAPI model) { + OASValidationResult result = validateModel(model); + reportResults(result); + } + + /** + * Validate an OpenAPI model + * + * @param model the model to validate + * @return the validation result + */ + public OASValidationResult validateModel(OpenAPI model) { + String modelVersion = model.getOpenapi(); + + if (modelVersion == null) { + modelVersion = ""; // Avoid nulls + } + + // Find and call a validator for the the model version + OASValidationResult result = null; + for (ServiceReference ref : validators) { + String validatorVersion = (String) ref.getProperty("openapi.version"); + if (modelVersion.startsWith(validatorVersion)) { + OASValidator validator = ctx.locateService(VALIDATOR_REF, ref); + result = validator.validate(model); + break; + } + } + + // If we didn't find a validator, report error for invalid version + if (result == null) { + result = new OASValidationResult(); + String message = ValidatorUtils.formatMessage(ValidationMessageConstants.OPENAPI_VERSION_INVALID, modelVersion); + result.getEvents().add(new ValidationEvent(Severity.ERROR, "#", message)); + } + return result; + } + + private void reportResults(OASValidationResult result) { + final StringBuilder sbError = new StringBuilder(); + final StringBuilder sbWarnings = new StringBuilder(); + if (result.hasEvents()) { + result.getEvents().stream().forEach(v -> { + final String message = ValidatorUtils.formatMessage(ValidationMessageConstants.VALIDATION_MESSAGE, v.message, v.location); + if (v.severity == Severity.ERROR) { + sbError.append("\n - " + message); + } else if (v.severity == Severity.WARNING) { + sbWarnings.append("\n - " + message); + } + }); + + String errors = sbError.toString(); + if (!errors.isEmpty()) { + Tr.error(tc, MessageConstants.OPENAPI_DOCUMENT_VALIDATION_ERROR, errors + "\n"); + } + + String warnings = sbWarnings.toString(); + if (!warnings.isEmpty()) { + Tr.warning(tc, MessageConstants.OPENAPI_DOCUMENT_VALIDATION_WARNING, warnings + "\n"); + } + } + } + +} diff --git a/dev/io.openliberty.microprofile.openapi.2.0.internal/src/io/openliberty/microprofile/openapi20/internal/services/OASValidator.java b/dev/io.openliberty.microprofile.openapi.2.0.internal/src/io/openliberty/microprofile/openapi20/internal/services/OASValidator.java index a0f5eb5a3ed..c3261ba68b0 100644 --- a/dev/io.openliberty.microprofile.openapi.2.0.internal/src/io/openliberty/microprofile/openapi20/internal/services/OASValidator.java +++ b/dev/io.openliberty.microprofile.openapi.2.0.internal/src/io/openliberty/microprofile/openapi20/internal/services/OASValidator.java @@ -11,8 +11,14 @@ import org.eclipse.microprofile.openapi.models.OpenAPI; +import io.openliberty.microprofile.openapi20.internal.ValidationComponent; + /** * OpenAPI model validator. Checks structural constraints on the model and reports errors for violations. + *

+ * Each provider of this service should set the property {@code openapi.version} to the version of the OpenAPI spec they validate. + *

+ * Generally, this service should be called through {@link ValidationComponent} which will identify the correct service depending on the version of the OpenAPI spec the model declares. */ public interface OASValidator { diff --git a/dev/io.openliberty.microprofile.openapi.2.0.internal/src/io/openliberty/microprofile/openapi20/internal/services/OpenAPIModelOperations.java b/dev/io.openliberty.microprofile.openapi.2.0.internal/src/io/openliberty/microprofile/openapi20/internal/services/OpenAPIModelOperations.java index d9bdf079259..651e99a5687 100644 --- a/dev/io.openliberty.microprofile.openapi.2.0.internal/src/io/openliberty/microprofile/openapi20/internal/services/OpenAPIModelOperations.java +++ b/dev/io.openliberty.microprofile.openapi.2.0.internal/src/io/openliberty/microprofile/openapi20/internal/services/OpenAPIModelOperations.java @@ -9,8 +9,12 @@ *******************************************************************************/ package io.openliberty.microprofile.openapi20.internal.services; +import java.util.List; + import org.eclipse.microprofile.openapi.models.OpenAPI; import org.eclipse.microprofile.openapi.models.info.Info; +import org.eclipse.microprofile.openapi.models.media.Schema; +import org.eclipse.microprofile.openapi.models.media.Schema.SchemaType; import io.smallrye.openapi.runtime.OpenApiRuntimeException; @@ -53,4 +57,16 @@ public interface OpenAPIModelOperations { */ public OpenAPI createDefaultOpenApiModel(); + /** + * Get the list of accepted types from a schema + *

+ * Works around the change in type signature of the Schema.getType method in MP OpenAPI 4.0. + *

+ * OpenAPI v3.0 only allows a single type here, whereas OpenAPI v3.1 allows multiple. + * + * @param schema the schema + * @return the list of schema types, or {@code null} if the {@code type} property is not set + */ + public List getTypes(Schema schema); + } diff --git a/dev/io.openliberty.microprofile.openapi.2.0.internal/src/io/openliberty/microprofile/openapi20/internal/utils/OpenAPIModelOperationsImpl.java b/dev/io.openliberty.microprofile.openapi.2.0.internal/src/io/openliberty/microprofile/openapi20/internal/utils/OpenAPIModelOperationsImpl.java index f86171efc06..f243e236c91 100644 --- a/dev/io.openliberty.microprofile.openapi.2.0.internal/src/io/openliberty/microprofile/openapi20/internal/utils/OpenAPIModelOperationsImpl.java +++ b/dev/io.openliberty.microprofile.openapi.2.0.internal/src/io/openliberty/microprofile/openapi20/internal/utils/OpenAPIModelOperationsImpl.java @@ -10,10 +10,14 @@ package io.openliberty.microprofile.openapi20.internal.utils; import java.io.IOException; +import java.util.Collections; +import java.util.List; import org.eclipse.microprofile.openapi.OASFactory; import org.eclipse.microprofile.openapi.models.OpenAPI; import org.eclipse.microprofile.openapi.models.info.Info; +import org.eclipse.microprofile.openapi.models.media.Schema; +import org.eclipse.microprofile.openapi.models.media.Schema.SchemaType; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; @@ -126,4 +130,10 @@ public OpenAPI createDefaultOpenApiModel() { return openAPI; } + @Override + public List getTypes(Schema schema) { + SchemaType type = schema.getType(); + return type == null ? null : Collections.singletonList(type); + } + } diff --git a/dev/io.openliberty.microprofile.openapi.2.0.internal/src/io/openliberty/microprofile/openapi20/internal/utils/OpenAPIUtils.java b/dev/io.openliberty.microprofile.openapi.2.0.internal/src/io/openliberty/microprofile/openapi20/internal/utils/OpenAPIUtils.java index aefc41190f2..0bbdcb0e384 100644 --- a/dev/io.openliberty.microprofile.openapi.2.0.internal/src/io/openliberty/microprofile/openapi20/internal/utils/OpenAPIUtils.java +++ b/dev/io.openliberty.microprofile.openapi.2.0.internal/src/io/openliberty/microprofile/openapi20/internal/utils/OpenAPIUtils.java @@ -16,7 +16,6 @@ import java.util.Iterator; import java.util.List; import java.util.Map; -import java.util.NoSuchElementException; import java.util.Optional; import java.util.function.BiPredicate; @@ -32,18 +31,13 @@ import com.ibm.ws.ffdc.annotation.FFDCIgnore; import com.ibm.ws.kernel.service.util.ServiceCaller; -import io.openliberty.microprofile.openapi20.internal.services.OASValidationResult; -import io.openliberty.microprofile.openapi20.internal.services.OASValidationResult.ValidationEvent.Severity; -import io.openliberty.microprofile.openapi20.internal.services.OASValidator; import io.openliberty.microprofile.openapi20.internal.services.OpenAPIModelOperations; -import io.openliberty.microprofile.openapi20.internal.validation.ValidatorUtils; import io.smallrye.openapi.runtime.OpenApiRuntimeException; import io.smallrye.openapi.runtime.io.Format; import io.smallrye.openapi.runtime.io.OpenApiSerializer; public class OpenAPIUtils { private static final TraceComponent tc = Tr.register(OpenAPIUtils.class); - private static final ServiceCaller validatorService = new ServiceCaller<>(OpenAPIUtils.class, OASValidator.class); private static final ServiceCaller modelOpsService = new ServiceCaller<>(OpenAPIUtils.class, OpenAPIModelOperations.class); /** @@ -77,41 +71,6 @@ public static String getOpenAPIDocument(final OpenAPI openAPIModel, final Format return oasResult; } - /** - * The validateDocument method validates the generated OpenAPI model and logs any warnings/errors. - * - * @param document - * The OpenAPI document (model) to validate - */ - @Trivial - public static void validateDocument(OpenAPI document) { - OASValidationResult result = validatorService.run(v -> { - return v.validate(document); - }).orElseThrow(() -> new NoSuchElementException("validatorService")); - final StringBuilder sbError = new StringBuilder(); - final StringBuilder sbWarnings = new StringBuilder(); - if (result.hasEvents()) { - result.getEvents().stream().forEach(v -> { - final String message = ValidatorUtils.formatMessage(ValidationMessageConstants.VALIDATION_MESSAGE, v.message, v.location); - if (v.severity == Severity.ERROR) { - sbError.append("\n - " + message); - } else if (v.severity == Severity.WARNING) { - sbWarnings.append("\n - " + message); - } - }); - - String errors = sbError.toString(); - if (!errors.isEmpty()) { - Tr.error(tc, MessageConstants.OPENAPI_DOCUMENT_VALIDATION_ERROR, errors + "\n"); - } - - String warnings = sbWarnings.toString(); - if (!warnings.isEmpty()) { - Tr.warning(tc, MessageConstants.OPENAPI_DOCUMENT_VALIDATION_WARNING, warnings + "\n"); - } - } - } - /** * The containsServersDefinition method checks whether the specified OpenAPI model defines any servers. * diff --git a/dev/io.openliberty.microprofile.openapi.2.0.internal/src/io/openliberty/microprofile/openapi20/internal/utils/ValidationMessageConstants.java b/dev/io.openliberty.microprofile.openapi.2.0.internal/src/io/openliberty/microprofile/openapi20/internal/utils/ValidationMessageConstants.java index eaf52a4f557..0c617f5e7d8 100644 --- a/dev/io.openliberty.microprofile.openapi.2.0.internal/src/io/openliberty/microprofile/openapi20/internal/utils/ValidationMessageConstants.java +++ b/dev/io.openliberty.microprofile.openapi.2.0.internal/src/io/openliberty/microprofile/openapi20/internal/utils/ValidationMessageConstants.java @@ -1,10 +1,10 @@ /******************************************************************************* - * Copyright (c) 2020 IBM Corporation and others. + * Copyright (c) 2020, 2024 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License 2.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-2.0/ - * + * * SPDX-License-Identifier: EPL-2.0 * * Contributors: @@ -57,6 +57,7 @@ public class ValidationMessageConstants { public static final String OPENAPI_VERSION_INVALID = "openAPIVersionInvalid"; //$NON-NLS-1$ public static final String OPENAPI_TAG_IS_NOT_UNIQUE = "openAPITagIsNotUnique"; //$NON-NLS-1$ + public static final String OPENAPI_MISSING_REQUIRED_FIELDS = "openAPIMissingRequiredFields"; public static final String OPERATION_IDS_MUST_BE_UNIQUE = "operationIdsMustBeUnique"; //$NON-NLS-1$ @@ -86,6 +87,7 @@ public class ValidationMessageConstants { public static final String REFERENCE_NOT_VALID_FORMAT = "referenceNotValidFormat"; //$NON-NLS-1$ public static final String REFERENCE_NULL = "referenceNull"; //$NON-NLS-1$ public static final String REFERENCE_TO_OBJECT_INVALID = "referenceToObjectInvalid"; //$NON-NLS-1$ + public static final String REFERENCE_NOT_VALID_URI = "referenceNotValidUri"; //$NON-NLS-1$ public static final String REQUIRED_FIELD_MISSING = "requiredFieldMissing"; //$NON-NLS-1$ @@ -98,6 +100,13 @@ public class ValidationMessageConstants { public static final String SCHEMA_PROPERTY_LESS_THAN_ZERO = "schemaPropertyLessThanZero"; //$NON-NLS-1$ public static final String SCHEMA_TYPE_DOES_NOT_MATCH_PROPERTY = "schemaTypeDoesNotMatchProperty"; //$NON-NLS-1$ + public static final String SCHEMA_PROPERTY_WRONG_TYPE = "schemaPropertyWrongType"; //$NON-NLS-1$ + public static final String SCHEMA_PROPERTY_EMPTY_ARRAY = "schemaPropertyEmptyArray"; //$NON-NLS-1$ + public static final String SCHEMA_PROPERTY_NOT_UNIQUE = "schemaPropertyNotUnique"; //$NON-NLS-1$ + public static final String SCHEMA_INVALID_TYPE = "schemaInvalidType"; //$NON-NLS-1$ + public static final String SCHEMA_COLLECTION_ELEMENT_WRONG_TYPE = "schemaCollectionElementWrongType"; //$NON-NLS-1$ + public static final String SCHEMA_DEPENDENT_REQUIRED_UNIQUE_STRINGS = "schemaDependentRequiredUniqueStrings"; + public static final String SECURITY_REQ_NOT_DECLARED = "securityRequirementNotDeclared"; //$NON-NLS-1$ public static final String SECURITY_REQ_SCOPE_NAMES_REQUIRED = "securityRequirementScopeNamesRequired"; //$NON-NLS-1$ public static final String SECURITY_REQ_FIELD_NOT_EMPTY = "securityRequirementFieldNotEmpty"; //$NON-NLS-1$ @@ -109,6 +118,9 @@ public class ValidationMessageConstants { public static final String SERVER_VARIABLE_NOT_DEFINED = "serverVariableNotDefined"; //$NON-NLS-1$ public static final String SERVER_INVALID_URL = "serverInvalidURL"; //$NON-NLS-1$ + public static final String SERVER_VARIABLE_ARRAY_EMPTY = "serverVariableArrayEmpty"; + public static final String SERVER_VARIABLE_DEFAULT_NOT_IN_ENUM = "serverVariableDefaultNotInEnum"; + public static final String VALIDATION_MESSAGE = "validationMessage"; //$NON-NLS-1$ public static final String VARIABLE_OAUTH_FLOW_OBJECT = "OAuth Flow Object"; //$NON-NLS-1$ diff --git a/dev/io.openliberty.microprofile.openapi.2.0.internal/src/io/openliberty/microprofile/openapi20/internal/validation/ExampleValidator.java b/dev/io.openliberty.microprofile.openapi.2.0.internal/src/io/openliberty/microprofile/openapi20/internal/validation/ExampleValidator.java index aca7a705d0b..62f0c2cd045 100644 --- a/dev/io.openliberty.microprofile.openapi.2.0.internal/src/io/openliberty/microprofile/openapi20/internal/validation/ExampleValidator.java +++ b/dev/io.openliberty.microprofile.openapi.2.0.internal/src/io/openliberty/microprofile/openapi20/internal/validation/ExampleValidator.java @@ -1,10 +1,10 @@ /******************************************************************************* - * Copyright (c) 2020 IBM Corporation and others. + * Copyright (c) 2020, 2024 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License 2.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-2.0/ - * + * * SPDX-License-Identifier: EPL-2.0 * * Contributors: @@ -17,8 +17,8 @@ import com.ibm.websphere.ras.Tr; import com.ibm.websphere.ras.TraceComponent; -import io.openliberty.microprofile.openapi20.internal.utils.OpenAPIModelWalker.Context; import io.openliberty.microprofile.openapi20.internal.services.OASValidationResult.ValidationEvent; +import io.openliberty.microprofile.openapi20.internal.utils.OpenAPIModelWalker.Context; import io.openliberty.microprofile.openapi20.internal.utils.ValidationMessageConstants; /** @@ -44,7 +44,7 @@ public void validate(ValidationHelper helper, Context context, String key, Examp String reference = t.getRef(); if (reference != null && !reference.isEmpty()) { - ValidatorUtils.referenceValidatorHelper(reference, t, helper, context, key); + helper.validateReference(context, key, reference, Example.class); return; } diff --git a/dev/io.openliberty.microprofile.openapi.2.0.internal/src/io/openliberty/microprofile/openapi20/internal/validation/HeaderValidator.java b/dev/io.openliberty.microprofile.openapi.2.0.internal/src/io/openliberty/microprofile/openapi20/internal/validation/HeaderValidator.java index 1f86c1e96b8..c36d05ad3ff 100644 --- a/dev/io.openliberty.microprofile.openapi.2.0.internal/src/io/openliberty/microprofile/openapi20/internal/validation/HeaderValidator.java +++ b/dev/io.openliberty.microprofile.openapi.2.0.internal/src/io/openliberty/microprofile/openapi20/internal/validation/HeaderValidator.java @@ -1,10 +1,10 @@ /******************************************************************************* - * Copyright (c) 2020 IBM Corporation and others. + * Copyright (c) 2020, 2024 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License 2.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-2.0/ - * + * * SPDX-License-Identifier: EPL-2.0 * * Contributors: @@ -22,8 +22,8 @@ import com.ibm.websphere.ras.Tr; import com.ibm.websphere.ras.TraceComponent; -import io.openliberty.microprofile.openapi20.internal.utils.OpenAPIModelWalker.Context; import io.openliberty.microprofile.openapi20.internal.services.OASValidationResult.ValidationEvent; +import io.openliberty.microprofile.openapi20.internal.utils.OpenAPIModelWalker.Context; import io.openliberty.microprofile.openapi20.internal.utils.ValidationMessageConstants; /** @@ -50,7 +50,7 @@ public void validate(ValidationHelper helper, Context context, String key, Heade String reference = t.getRef(); if (reference != null && !reference.isEmpty()) { - ValidatorUtils.referenceValidatorHelper(reference, t, helper, context, key); + helper.validateReference(context, key, reference, Header.class); return; } diff --git a/dev/io.openliberty.microprofile.openapi.2.0.internal/src/io/openliberty/microprofile/openapi20/internal/validation/LinkValidator.java b/dev/io.openliberty.microprofile.openapi.2.0.internal/src/io/openliberty/microprofile/openapi20/internal/validation/LinkValidator.java index da472a4a955..7f6f0e7fd00 100644 --- a/dev/io.openliberty.microprofile.openapi.2.0.internal/src/io/openliberty/microprofile/openapi20/internal/validation/LinkValidator.java +++ b/dev/io.openliberty.microprofile.openapi.2.0.internal/src/io/openliberty/microprofile/openapi20/internal/validation/LinkValidator.java @@ -1,10 +1,10 @@ /******************************************************************************* - * Copyright (c) 2020 IBM Corporation and others. + * Copyright (c) 2020, 2024 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License 2.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-2.0/ - * + * * SPDX-License-Identifier: EPL-2.0 * * Contributors: @@ -20,8 +20,8 @@ import com.ibm.websphere.ras.Tr; import com.ibm.websphere.ras.TraceComponent; -import io.openliberty.microprofile.openapi20.internal.utils.OpenAPIModelWalker.Context; import io.openliberty.microprofile.openapi20.internal.services.OASValidationResult.ValidationEvent; +import io.openliberty.microprofile.openapi20.internal.utils.OpenAPIModelWalker.Context; import io.openliberty.microprofile.openapi20.internal.utils.ValidationMessageConstants; /** @@ -48,7 +48,7 @@ public void validate(ValidationHelper helper, Context context, String key, Link String reference = t.getRef(); if (reference != null && !reference.isEmpty()) { - ValidatorUtils.referenceValidatorHelper(reference, t, helper, context, key); + helper.validateReference(context, key, reference, Link.class); return; } diff --git a/dev/io.openliberty.microprofile.openapi.2.0.internal/src/io/openliberty/microprofile/openapi20/internal/validation/MediaTypeValidator.java b/dev/io.openliberty.microprofile.openapi.2.0.internal/src/io/openliberty/microprofile/openapi20/internal/validation/MediaTypeValidator.java index c60b775cd45..fd57fe67c98 100644 --- a/dev/io.openliberty.microprofile.openapi.2.0.internal/src/io/openliberty/microprofile/openapi20/internal/validation/MediaTypeValidator.java +++ b/dev/io.openliberty.microprofile.openapi.2.0.internal/src/io/openliberty/microprofile/openapi20/internal/validation/MediaTypeValidator.java @@ -1,10 +1,10 @@ /******************************************************************************* - * Copyright (c) 2020 IBM Corporation and others. + * Copyright (c) 2020, 2024 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License 2.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-2.0/ - * + * * SPDX-License-Identifier: EPL-2.0 * * Contributors: @@ -24,9 +24,8 @@ import com.ibm.websphere.ras.Tr; import com.ibm.websphere.ras.TraceComponent; -import io.openliberty.microprofile.openapi20.internal.utils.OpenAPIModelWalker.Context; import io.openliberty.microprofile.openapi20.internal.services.OASValidationResult.ValidationEvent; -import io.openliberty.microprofile.openapi20.internal.services.OASValidationResult.ValidationEvent.Severity; +import io.openliberty.microprofile.openapi20.internal.utils.OpenAPIModelWalker.Context; import io.openliberty.microprofile.openapi20.internal.utils.ValidationMessageConstants; /** @@ -65,13 +64,8 @@ public void validate(ValidationHelper helper, Context context, String key, Media String ref = schema.getRef(); if (StringUtils.isNotBlank(ref)) { //if $ref is set, go to the class specified in $ref and look at the properties - ReferenceValidator referenceValidator = ReferenceValidator.getInstance(); - Object component = referenceValidator.validate(helper, context, key, ref); - if (!schema.getClass().isInstance(component)) { - final String message = Tr.formatMessage(tc, ValidationMessageConstants.REFERENCE_TO_OBJECT_INVALID, ref); - helper.addValidationEvent(new ValidationEvent(Severity.ERROR, context.getLocation(), message)); - } else { - Schema componentSchema = (Schema) component; + Schema componentSchema = helper.validateReference(context, key, ref, Schema.class); + if (componentSchema != null) { Map schemaProperties = componentSchema != null ? componentSchema.getProperties() : null; for (String encodingProperty : encodingProperties) { diff --git a/dev/io.openliberty.microprofile.openapi.2.0.internal/src/io/openliberty/microprofile/openapi20/internal/validation/OASValidatorImpl.java b/dev/io.openliberty.microprofile.openapi.2.0.internal/src/io/openliberty/microprofile/openapi20/internal/validation/OASValidator30Impl.java similarity index 92% rename from dev/io.openliberty.microprofile.openapi.2.0.internal/src/io/openliberty/microprofile/openapi20/internal/validation/OASValidatorImpl.java rename to dev/io.openliberty.microprofile.openapi.2.0.internal/src/io/openliberty/microprofile/openapi20/internal/validation/OASValidator30Impl.java index e5b2041b73f..883037f5c59 100644 --- a/dev/io.openliberty.microprofile.openapi.2.0.internal/src/io/openliberty/microprofile/openapi20/internal/validation/OASValidatorImpl.java +++ b/dev/io.openliberty.microprofile.openapi.2.0.internal/src/io/openliberty/microprofile/openapi20/internal/validation/OASValidator30Impl.java @@ -42,6 +42,8 @@ import org.eclipse.microprofile.openapi.models.servers.Server; import org.eclipse.microprofile.openapi.models.servers.ServerVariable; import org.eclipse.microprofile.openapi.models.tags.Tag; +import org.osgi.service.component.annotations.Component; +import org.osgi.service.component.annotations.ConfigurationPolicy; import org.osgi.service.component.annotations.Reference; import com.ibm.websphere.ras.Tr; @@ -57,10 +59,11 @@ import io.openliberty.microprofile.openapi20.internal.utils.ValidationMessageConstants; /** - * OpenAPI model validator. Checks structural constraints on the model and reports errors for violations. + * OpenAPI v3.0 model validator. Checks structural constraints on the model and reports errors for violations. */ -public class OASValidatorImpl implements OASValidator { - private static final TraceComponent tc = Tr.register(OASValidatorImpl.class); +@Component(configurationPolicy = ConfigurationPolicy.IGNORE, property = "openapi.version=3.0") +public class OASValidator30Impl implements OASValidator { + private static final TraceComponent tc = Tr.register(OASValidator30Impl.class); @Reference protected OpenAPIModelWalker modelWalker; @@ -118,6 +121,23 @@ public void addLinkOperationId(String operationId, String location) { } } + @Override + public T validateReference(Context context, String key, String ref, Class clazz) { + Object component = validateReference(context, key, ref); + if (!clazz.isInstance(component)) { + final String message = Tr.formatMessage(tc, ValidationMessageConstants.REFERENCE_TO_OBJECT_INVALID, ref); + addValidationEvent(new ValidationEvent(Severity.ERROR, context.getLocation(), message)); + return null; + } else { + return clazz.cast(component); + } + } + + @Override + public Object validateReference(Context context, String key, String ref) { + return ReferenceValidator.getInstance().validate(this, context, key, ref); + } + public void validateLinkOperationIds() { for (String k : linkOperationIds.keySet()) { if (!operationIds.contains(k)) { diff --git a/dev/io.openliberty.microprofile.openapi.2.0.internal/src/io/openliberty/microprofile/openapi20/internal/validation/OpenAPIValidator.java b/dev/io.openliberty.microprofile.openapi.2.0.internal/src/io/openliberty/microprofile/openapi20/internal/validation/OpenAPIValidator.java index 6e999885d06..032fe3100ce 100644 --- a/dev/io.openliberty.microprofile.openapi.2.0.internal/src/io/openliberty/microprofile/openapi20/internal/validation/OpenAPIValidator.java +++ b/dev/io.openliberty.microprofile.openapi.2.0.internal/src/io/openliberty/microprofile/openapi20/internal/validation/OpenAPIValidator.java @@ -1,10 +1,10 @@ /******************************************************************************* - * Copyright (c) 2020 IBM Corporation and others. + * Copyright (c) 2020, 2024 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License 2.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-2.0/ - * + * * SPDX-License-Identifier: EPL-2.0 * * Contributors: @@ -22,8 +22,8 @@ import com.ibm.websphere.ras.Tr; import com.ibm.websphere.ras.TraceComponent; -import io.openliberty.microprofile.openapi20.internal.utils.OpenAPIModelWalker.Context; import io.openliberty.microprofile.openapi20.internal.services.OASValidationResult.ValidationEvent; +import io.openliberty.microprofile.openapi20.internal.utils.OpenAPIModelWalker.Context; import io.openliberty.microprofile.openapi20.internal.utils.ValidationMessageConstants; import io.smallrye.openapi.runtime.io.definition.DefinitionConstant; @@ -56,14 +56,18 @@ public void validate(ValidationHelper helper, Context context, String key, OpenA helper.addValidationEvent(new ValidationEvent(ValidationEvent.Severity.ERROR, context.getLocation(), message)); } - List tags = t.getTags(); - if (tags != null) { - Set tagNames = new HashSet<>(); - for (Tag tag : tags) { - if (!tagNames.add(tag.getName())) { - final String message = Tr.formatMessage(tc, ValidationMessageConstants.OPENAPI_TAG_IS_NOT_UNIQUE, tag.getName()); - helper.addValidationEvent(new ValidationEvent(ValidationEvent.Severity.ERROR, context.getLocation(), message)); - } + validateTags(helper, context, t); + } + } + + public void validateTags(ValidationHelper helper, Context context, OpenAPI t) { + List tags = t.getTags(); + if (tags != null) { + Set tagNames = new HashSet<>(); + for (Tag tag : tags) { + if (!tagNames.add(tag.getName())) { + final String message = Tr.formatMessage(tc, ValidationMessageConstants.OPENAPI_TAG_IS_NOT_UNIQUE, tag.getName()); + helper.addValidationEvent(new ValidationEvent(ValidationEvent.Severity.ERROR, context.getLocation(), message)); } } } diff --git a/dev/io.openliberty.microprofile.openapi.2.0.internal/src/io/openliberty/microprofile/openapi20/internal/validation/OperationValidator.java b/dev/io.openliberty.microprofile.openapi.2.0.internal/src/io/openliberty/microprofile/openapi20/internal/validation/OperationValidator.java index db28bb3cf63..f96b90bfd12 100644 --- a/dev/io.openliberty.microprofile.openapi.2.0.internal/src/io/openliberty/microprofile/openapi20/internal/validation/OperationValidator.java +++ b/dev/io.openliberty.microprofile.openapi.2.0.internal/src/io/openliberty/microprofile/openapi20/internal/validation/OperationValidator.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2020 IBM Corporation and others. + * Copyright (c) 2020, 2024 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License 2.0 * which accompanies this distribution, and is available at @@ -43,13 +43,17 @@ private OperationValidator() {} @Override public void validate(ValidationHelper helper, Context context, String key, Operation t) { if (t != null) { - final String id = t.getOperationId(); - if (id != null && helper.addOperationId(id)) { - final String message = Tr.formatMessage(tc, ValidationMessageConstants.OPERATION_IDS_MUST_BE_UNIQUE, id); - helper.addValidationEvent(new ValidationEvent(Severity.ERROR, context.getLocation(OperationConstant.PROP_OPERATION_ID), message)); - } + validateId(helper, context, t); final APIResponses responses = t.getResponses(); ValidatorUtils.validateRequiredField(responses, context, OperationConstant.PROP_RESPONSES).ifPresent(helper::addValidationEvent); } } + + public void validateId(ValidationHelper helper, Context context, Operation t) { + final String id = t.getOperationId(); + if (id != null && helper.addOperationId(id)) { + final String message = Tr.formatMessage(tc, ValidationMessageConstants.OPERATION_IDS_MUST_BE_UNIQUE, id); + helper.addValidationEvent(new ValidationEvent(Severity.ERROR, context.getLocation(OperationConstant.PROP_OPERATION_ID), message)); + } + } } diff --git a/dev/io.openliberty.microprofile.openapi.2.0.internal/src/io/openliberty/microprofile/openapi20/internal/validation/ParameterValidator.java b/dev/io.openliberty.microprofile.openapi.2.0.internal/src/io/openliberty/microprofile/openapi20/internal/validation/ParameterValidator.java index 7d76f75b74e..8c30f3ec5ff 100644 --- a/dev/io.openliberty.microprofile.openapi.2.0.internal/src/io/openliberty/microprofile/openapi20/internal/validation/ParameterValidator.java +++ b/dev/io.openliberty.microprofile.openapi.2.0.internal/src/io/openliberty/microprofile/openapi20/internal/validation/ParameterValidator.java @@ -1,10 +1,10 @@ /******************************************************************************* - * Copyright (c) 2020 IBM Corporation and others. + * Copyright (c) 2020, 2024 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License 2.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-2.0/ - * + * * SPDX-License-Identifier: EPL-2.0 * * Contributors: @@ -23,8 +23,8 @@ import com.ibm.websphere.ras.Tr; import com.ibm.websphere.ras.TraceComponent; -import io.openliberty.microprofile.openapi20.internal.utils.OpenAPIModelWalker.Context; import io.openliberty.microprofile.openapi20.internal.services.OASValidationResult.ValidationEvent; +import io.openliberty.microprofile.openapi20.internal.utils.OpenAPIModelWalker.Context; import io.openliberty.microprofile.openapi20.internal.utils.ValidationMessageConstants; import io.smallrye.openapi.runtime.io.Parameterizable; import io.smallrye.openapi.runtime.io.parameter.ParameterConstant; @@ -53,7 +53,7 @@ public void validate(ValidationHelper helper, Context context, String key, Param String reference = t.getRef(); if (reference != null && !reference.isEmpty()) { - ValidatorUtils.referenceValidatorHelper(reference, t, helper, context, key); + helper.validateReference(context, key, reference, Parameter.class); return; } diff --git a/dev/io.openliberty.microprofile.openapi.2.0.internal/src/io/openliberty/microprofile/openapi20/internal/validation/PathItemValidator.java b/dev/io.openliberty.microprofile.openapi.2.0.internal/src/io/openliberty/microprofile/openapi20/internal/validation/PathItemValidator.java index 40423914034..03bfad0c24b 100644 --- a/dev/io.openliberty.microprofile.openapi.2.0.internal/src/io/openliberty/microprofile/openapi20/internal/validation/PathItemValidator.java +++ b/dev/io.openliberty.microprofile.openapi.2.0.internal/src/io/openliberty/microprofile/openapi20/internal/validation/PathItemValidator.java @@ -1,10 +1,10 @@ /******************************************************************************* - * Copyright (c) 2020 IBM Corporation and others. + * Copyright (c) 2020, 2024 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License 2.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-2.0/ - * + * * SPDX-License-Identifier: EPL-2.0 * * Contributors: @@ -69,7 +69,7 @@ public void validate(ValidationHelper helper, Context context, String key, PathI } } - private void validateParameters(ValidationHelper helper, Context context, String pathStr, PathItem pathItem) { + public void validateParameters(ValidationHelper helper, Context context, String pathStr, PathItem pathItem) { Set definedSharedPathParameters = new HashSet<>(), definedSharedQueryParameters = new HashSet<>(), definedSharedHeaderParameters = new HashSet<>(), definedSharedCookieParameters = new HashSet<>(); @@ -80,9 +80,9 @@ private void validateParameters(ValidationHelper helper, Context context, String Parameter parameter = param; String reference = parameter.getRef(); if (reference != null && !reference.isEmpty()) { - Object componentItem = ReferenceValidator.getInstance().validate(helper, context, null, reference); - if (parameter.getClass().isInstance(componentItem)) { - parameter = (Parameter) componentItem; + Parameter referencedParameter = helper.validateReference(context, null, reference, Parameter.class); + if (referencedParameter != null) { + parameter = referencedParameter; } } @@ -144,9 +144,9 @@ private void validateOperationParameters(ValidationHelper helper, Context contex String reference = parameter.getRef(); if (reference != null && !reference.isEmpty()) { - Object componentItem = ReferenceValidator.getInstance().validate(helper, context, null, reference); - if (parameter.getClass().isInstance(componentItem)) { - parameter = (Parameter) componentItem; + Parameter referencedParameter = helper.validateReference(context, null, reference, Parameter.class); + if (referencedParameter != null) { + parameter = referencedParameter; } } diff --git a/dev/io.openliberty.microprofile.openapi.2.0.internal/src/io/openliberty/microprofile/openapi20/internal/validation/RequestBodyValidator.java b/dev/io.openliberty.microprofile.openapi.2.0.internal/src/io/openliberty/microprofile/openapi20/internal/validation/RequestBodyValidator.java index 26f52d537d3..b8e322c4b79 100644 --- a/dev/io.openliberty.microprofile.openapi.2.0.internal/src/io/openliberty/microprofile/openapi20/internal/validation/RequestBodyValidator.java +++ b/dev/io.openliberty.microprofile.openapi.2.0.internal/src/io/openliberty/microprofile/openapi20/internal/validation/RequestBodyValidator.java @@ -4,7 +4,7 @@ * are made available under the terms of the Eclipse Public License 2.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-2.0/ - * + * * SPDX-License-Identifier: EPL-2.0 * * Contributors: @@ -42,7 +42,7 @@ public void validate(ValidationHelper helper, Context context, String key, Reque String reference = t.getRef(); if (reference != null && !reference.isEmpty()) { - ValidatorUtils.referenceValidatorHelper(reference, t, helper, context, key); + helper.validateReference(context, key, reference, RequestBody.class); return; } ValidatorUtils.validateRequiredField(t.getContent(), context, RequestBodyConstant.PROP_CONTENT).ifPresent(helper::addValidationEvent); diff --git a/dev/io.openliberty.microprofile.openapi.2.0.internal/src/io/openliberty/microprofile/openapi20/internal/validation/ResponseValidator.java b/dev/io.openliberty.microprofile.openapi.2.0.internal/src/io/openliberty/microprofile/openapi20/internal/validation/ResponseValidator.java index 31e2ac9d7d3..608b14365e0 100644 --- a/dev/io.openliberty.microprofile.openapi.2.0.internal/src/io/openliberty/microprofile/openapi20/internal/validation/ResponseValidator.java +++ b/dev/io.openliberty.microprofile.openapi.2.0.internal/src/io/openliberty/microprofile/openapi20/internal/validation/ResponseValidator.java @@ -4,7 +4,7 @@ * are made available under the terms of the Eclipse Public License 2.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-2.0/ - * + * * SPDX-License-Identifier: EPL-2.0 * * Contributors: @@ -43,7 +43,7 @@ public void validate(ValidationHelper helper, Context context, String key, APIRe String reference = t.getRef(); if (reference != null && !reference.isEmpty()) { - ValidatorUtils.referenceValidatorHelper(reference, t, helper, context, key); + helper.validateReference(context, key, reference, APIResponse.class); return; } diff --git a/dev/io.openliberty.microprofile.openapi.2.0.internal/src/io/openliberty/microprofile/openapi20/internal/validation/SchemaValidator.java b/dev/io.openliberty.microprofile.openapi.2.0.internal/src/io/openliberty/microprofile/openapi20/internal/validation/SchemaValidator.java index 935fe6cac79..e0aed98be80 100644 --- a/dev/io.openliberty.microprofile.openapi.2.0.internal/src/io/openliberty/microprofile/openapi20/internal/validation/SchemaValidator.java +++ b/dev/io.openliberty.microprofile.openapi.2.0.internal/src/io/openliberty/microprofile/openapi20/internal/validation/SchemaValidator.java @@ -1,10 +1,10 @@ /******************************************************************************* - * Copyright (c) 2020 IBM Corporation and others. + * Copyright (c) 2020, 2024 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License 2.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-2.0/ - * + * * SPDX-License-Identifier: EPL-2.0 * * Contributors: @@ -12,15 +12,21 @@ *******************************************************************************/ package io.openliberty.microprofile.openapi20.internal.validation; +import static java.util.stream.Collectors.joining; + import java.math.BigDecimal; import java.util.ArrayList; +import java.util.Set; +import java.util.stream.Collectors; import org.eclipse.microprofile.openapi.models.media.Schema; import com.ibm.websphere.ras.Tr; import com.ibm.websphere.ras.TraceComponent; +import com.ibm.ws.kernel.service.util.ServiceCaller; import io.openliberty.microprofile.openapi20.internal.services.OASValidationResult.ValidationEvent; +import io.openliberty.microprofile.openapi20.internal.services.OpenAPIModelOperations; import io.openliberty.microprofile.openapi20.internal.utils.Constants; import io.openliberty.microprofile.openapi20.internal.utils.OpenAPIModelWalker.Context; import io.openliberty.microprofile.openapi20.internal.utils.ValidationMessageConstants; @@ -33,6 +39,9 @@ public class SchemaValidator extends TypeValidator { private static final TraceComponent tc = Tr.register(SchemaValidator.class); + // Non-final for unit testing + private static ServiceCaller MODEL_OPS = new ServiceCaller<>(SchemaValidator.class, OpenAPIModelOperations.class); + private static final SchemaValidator INSTANCE = new SchemaValidator(); public static SchemaValidator getInstance() { @@ -49,11 +58,17 @@ public void validate(ValidationHelper helper, Context context, String key, Schem String reference = t.getRef(); if (reference != null && !reference.isEmpty()) { - ValidatorUtils.referenceValidatorHelper(reference, t, helper, context, key); + helper.validateReference(context, key, reference, Schema.class); return; } - if (t.getType() != null && Constants.SCHEMA_TYPE_ARRAY.equals(t.getType().toString()) && t.getItems() == null) { + Set types = MODEL_OPS.run(s -> s.getTypes(t)) + .map(typeList -> typeList.stream() + .map(Object::toString) + .collect(Collectors.toSet())) + .orElse(null); + + if (types != null && types.contains(Constants.SCHEMA_TYPE_ARRAY) && t.getItems() == null) { final String message = Tr.formatMessage(tc, ValidationMessageConstants.SCHEMA_TYPE_ARRAY_NULL_ITEMS); helper.addValidationEvent(new ValidationEvent(ValidationEvent.Severity.ERROR, context.getLocation(), message)); } @@ -68,14 +83,13 @@ public void validate(ValidationHelper helper, Context context, String key, Schem helper.addValidationEvent(new ValidationEvent(ValidationEvent.Severity.ERROR, context.getLocation(), message)); } - String type = (t.getType() != null) ? t.getType().toString() : Constants.SCHEMA_TYPE_NULL; ArrayList propertiesInvalidValue = new ArrayList<>(); ArrayList propertiesNotForSchemaType = new ArrayList<>(); if (t.getMaxLength() != null) { if (t.getMaxLength().intValue() < 0) { propertiesInvalidValue.add(SchemaConstant.PROP_MAX_LENGTH); } - if (!type.equals(Constants.SCHEMA_TYPE_STRING)) { + if (types != null && !types.contains(Constants.SCHEMA_TYPE_STRING)) { propertiesNotForSchemaType.add(SchemaConstant.PROP_MAX_LENGTH); } } @@ -84,7 +98,7 @@ public void validate(ValidationHelper helper, Context context, String key, Schem if (t.getMinLength().intValue() < 0) { propertiesInvalidValue.add(SchemaConstant.PROP_MIN_LENGTH); } - if (!type.equals(Constants.SCHEMA_TYPE_STRING)) { + if (types != null && !types.contains(Constants.SCHEMA_TYPE_STRING)) { propertiesNotForSchemaType.add(SchemaConstant.PROP_MIN_LENGTH); } } @@ -93,7 +107,7 @@ public void validate(ValidationHelper helper, Context context, String key, Schem if (t.getMinItems().intValue() < 0) { propertiesInvalidValue.add(SchemaConstant.PROP_MIN_ITEMS); } - if (!type.equals(Constants.SCHEMA_TYPE_ARRAY)) { + if (types != null && !types.contains(Constants.SCHEMA_TYPE_ARRAY)) { propertiesNotForSchemaType.add(SchemaConstant.PROP_MIN_ITEMS); } } @@ -102,12 +116,12 @@ public void validate(ValidationHelper helper, Context context, String key, Schem if (t.getMaxItems().intValue() < 0) { propertiesInvalidValue.add(SchemaConstant.PROP_MAX_ITEMS); } - if (!type.equals(Constants.SCHEMA_TYPE_ARRAY)) { + if (types != null && !types.contains(Constants.SCHEMA_TYPE_ARRAY)) { propertiesNotForSchemaType.add(SchemaConstant.PROP_MAX_ITEMS); } } - if (t.getUniqueItems() != null && !type.equals(Constants.SCHEMA_TYPE_ARRAY)) { + if (t.getUniqueItems() != null && types != null && !types.contains(Constants.SCHEMA_TYPE_ARRAY)) { propertiesNotForSchemaType.add(SchemaConstant.PROP_UNIQUE_ITEMS); } @@ -115,7 +129,7 @@ public void validate(ValidationHelper helper, Context context, String key, Schem if (t.getMinProperties().intValue() < 0) { propertiesInvalidValue.add(SchemaConstant.PROP_MIN_PROPERTIES); } - if (!type.equals(Constants.SCHEMA_TYPE_OBJECT)) { + if (types != null && !types.contains(Constants.SCHEMA_TYPE_OBJECT)) { propertiesNotForSchemaType.add(SchemaConstant.PROP_MIN_PROPERTIES); } } @@ -124,7 +138,7 @@ public void validate(ValidationHelper helper, Context context, String key, Schem if (t.getMaxProperties().intValue() < 0) { propertiesInvalidValue.add(SchemaConstant.PROP_MAX_PROPERTIES); } - if (!type.equals(Constants.SCHEMA_TYPE_OBJECT)) { + if (types != null && !types.contains(Constants.SCHEMA_TYPE_OBJECT)) { propertiesNotForSchemaType.add(SchemaConstant.PROP_MAX_PROPERTIES); } } @@ -138,7 +152,8 @@ public void validate(ValidationHelper helper, Context context, String key, Schem if (!propertiesNotForSchemaType.isEmpty()) { for (String s : propertiesNotForSchemaType) { - final String message = Tr.formatMessage(tc, ValidationMessageConstants.SCHEMA_TYPE_DOES_NOT_MATCH_PROPERTY, s, type); + String typeString = types.size() == 1 ? types.iterator().next() : types.stream().collect(joining(",", "[", "]")); + final String message = Tr.formatMessage(tc, ValidationMessageConstants.SCHEMA_TYPE_DOES_NOT_MATCH_PROPERTY, s, typeString); helper.addValidationEvent(new ValidationEvent(ValidationEvent.Severity.WARNING, context.getLocation(), message)); } } diff --git a/dev/io.openliberty.microprofile.openapi.2.0.internal/src/io/openliberty/microprofile/openapi20/internal/validation/SecuritySchemeValidator.java b/dev/io.openliberty.microprofile.openapi.2.0.internal/src/io/openliberty/microprofile/openapi20/internal/validation/SecuritySchemeValidator.java index 7500792a65a..61ba3adee1b 100644 --- a/dev/io.openliberty.microprofile.openapi.2.0.internal/src/io/openliberty/microprofile/openapi20/internal/validation/SecuritySchemeValidator.java +++ b/dev/io.openliberty.microprofile.openapi.2.0.internal/src/io/openliberty/microprofile/openapi20/internal/validation/SecuritySchemeValidator.java @@ -4,7 +4,7 @@ * are made available under the terms of the Eclipse Public License 2.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-2.0/ - * + * * SPDX-License-Identifier: EPL-2.0 * * Contributors: @@ -23,8 +23,8 @@ import com.ibm.websphere.ras.Tr; import com.ibm.websphere.ras.TraceComponent; -import io.openliberty.microprofile.openapi20.internal.utils.OpenAPIModelWalker.Context; import io.openliberty.microprofile.openapi20.internal.services.OASValidationResult.ValidationEvent; +import io.openliberty.microprofile.openapi20.internal.utils.OpenAPIModelWalker.Context; import io.openliberty.microprofile.openapi20.internal.utils.ValidationMessageConstants; import io.smallrye.openapi.runtime.io.securityscheme.SecuritySchemeConstant; @@ -50,7 +50,7 @@ public void validate(ValidationHelper helper, Context context, String key, Secur String reference = t.getRef(); if (reference != null && !reference.isEmpty()) { - ValidatorUtils.referenceValidatorHelper(reference, t, helper, context, key); + helper.validateReference(context, key, reference, SecurityScheme.class); return; } diff --git a/dev/io.openliberty.microprofile.openapi.2.0.internal/src/io/openliberty/microprofile/openapi20/internal/validation/ValidationHelper.java b/dev/io.openliberty.microprofile.openapi.2.0.internal/src/io/openliberty/microprofile/openapi20/internal/validation/ValidationHelper.java index 9d39cb5c086..2dfe29924ba 100644 --- a/dev/io.openliberty.microprofile.openapi.2.0.internal/src/io/openliberty/microprofile/openapi20/internal/validation/ValidationHelper.java +++ b/dev/io.openliberty.microprofile.openapi.2.0.internal/src/io/openliberty/microprofile/openapi20/internal/validation/ValidationHelper.java @@ -1,10 +1,10 @@ /******************************************************************************* - * Copyright (c) 2020 IBM Corporation and others. + * Copyright (c) 2020, 2024 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License 2.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-2.0/ - * + * * SPDX-License-Identifier: EPL-2.0 * * Contributors: @@ -13,6 +13,7 @@ package io.openliberty.microprofile.openapi20.internal.validation; import io.openliberty.microprofile.openapi20.internal.services.OASValidationResult.ValidationEvent; +import io.openliberty.microprofile.openapi20.internal.utils.OpenAPIModelWalker; /** * @@ -33,4 +34,37 @@ public interface ValidationHelper { * Adds an operationId and location of Link object specifying it to the map of IDs and locations. */ void addLinkOperationId(String operationId, String location); + + /** + * Validates a reference and returns the referenced object. + *

+ * Validation events are recorded to this {@code ValidationHelper} for any problems found. + *

+ * The validator may choose to ignore certain types of reference that it can't or doesn't validate, so it's possible to have no validation events registered and {@code null} be + * returned. + * + * @param context the walker context + * @param key the key of the referring object + * @param ref the JSON pointer reference + * @return the object being referenced if one was found, otherwise {@code null} + */ + Object validateReference(OpenAPIModelWalker.Context context, String key, String ref); + + /** + * Validates a reference and checks the referenced object is of the correct type. + *

+ * If a referenced object of the correct type is found, it is returned. + *

+ * Validation events are recorded to this {@code ValidationHelper} for any problems found. + *

+ * The validator may choose to ignore certain types of reference that it can't or doesn't validate, so it's possible to have no validation events registered and {@code null} be + * returned. + * + * @param context the walker context + * @param key the key of the referring object + * @param ref the JSON pointer reference + * @param clazz the expected type of the referenced object + * @return the object being referenced if one was found, otherwise {@code null} + */ + T validateReference(OpenAPIModelWalker.Context context, String key, String ref, Class clazz); } \ No newline at end of file diff --git a/dev/io.openliberty.microprofile.openapi.2.0.internal/src/io/openliberty/microprofile/openapi20/internal/validation/ValidatorUtils.java b/dev/io.openliberty.microprofile.openapi.2.0.internal/src/io/openliberty/microprofile/openapi20/internal/validation/ValidatorUtils.java index 4c182f8fddf..dab91431d07 100644 --- a/dev/io.openliberty.microprofile.openapi.2.0.internal/src/io/openliberty/microprofile/openapi20/internal/validation/ValidatorUtils.java +++ b/dev/io.openliberty.microprofile.openapi.2.0.internal/src/io/openliberty/microprofile/openapi20/internal/validation/ValidatorUtils.java @@ -1,10 +1,10 @@ /******************************************************************************* - * Copyright (c) 2020 IBM Corporation and others. + * Copyright (c) 2020, 2024 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License 2.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-2.0/ - * + * * SPDX-License-Identifier: EPL-2.0 * * Contributors: @@ -135,16 +135,7 @@ public static boolean isValidEmailAddress(String email) { return Constants.REGEX_EMAIL_PATTERN.matcher(email).matches(); } - public static void referenceValidatorHelper(String reference, Object t, ValidationHelper helper, Context context, String key) { - ReferenceValidator referenceValidator = ReferenceValidator.getInstance(); - Object component = referenceValidator.validate(helper, context, key, reference); - if (!t.getClass().isInstance(component)) { - final String message = Tr.formatMessage(tc, ValidationMessageConstants.REFERENCE_TO_OBJECT_INVALID, reference); - helper.addValidationEvent(new ValidationEvent(Severity.ERROR, context.getLocation(), message)); - } - } - - public static String formatMessage(String messageId, String... strings) { + public static String formatMessage(String messageId, Object... strings) { return Tr.formatMessage(tc, messageId, strings); } } diff --git a/dev/io.openliberty.microprofile.openapi.2.0.internal/src/io/openliberty/microprofile/openapi20/internal/validation/package-info.java b/dev/io.openliberty.microprofile.openapi.2.0.internal/src/io/openliberty/microprofile/openapi20/internal/validation/package-info.java index 74ece8fad37..5f58adc0f1e 100644 --- a/dev/io.openliberty.microprofile.openapi.2.0.internal/src/io/openliberty/microprofile/openapi20/internal/validation/package-info.java +++ b/dev/io.openliberty.microprofile.openapi.2.0.internal/src/io/openliberty/microprofile/openapi20/internal/validation/package-info.java @@ -1,10 +1,10 @@ /******************************************************************************* - * Copyright (c) 2020, 2022 IBM Corporation and others. + * Copyright (c) 2020, 2024 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License 2.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-2.0/ - * + * * SPDX-License-Identifier: EPL-2.0 * * Contributors: @@ -12,9 +12,9 @@ *******************************************************************************/ /** - * Handles validation of OpenAPI documents. + * Handles validation of OpenAPI v3.0 documents. *

- * {@link OASValidatorImpl#validate(OpenAPI)} is the entry point to validate an OpenAPI document. + * {@link OASValidator30Impl} provides the {@link OASValidator} service for validating OpenAPI v3.0 documents. */ @Version(Constants.OSGI_VERSION) @TraceOptions(traceGroup = Constants.TRACE_GROUP, messageBundle = Constants.TRACE_VALIDATION) diff --git a/dev/io.openliberty.microprofile.openapi.2.0.internal_test/bnd.bnd b/dev/io.openliberty.microprofile.openapi.2.0.internal_test/bnd.bnd index 5183580844c..5a9d03be56c 100644 --- a/dev/io.openliberty.microprofile.openapi.2.0.internal_test/bnd.bnd +++ b/dev/io.openliberty.microprofile.openapi.2.0.internal_test/bnd.bnd @@ -19,7 +19,8 @@ Import-Package: \ * Export-Package: \ - io.openliberty.microprofile.openapi20.test.merge.* + io.openliberty.microprofile.openapi20.test.*,\ + io.openliberty.microprofile.openapi20.validation.test.* test.project:true @@ -30,6 +31,7 @@ test.project:true com.ibm.ws.org.jboss.jandex;version=latest,\ com.ibm.ws.adaptable.module;version=latest,\ com.ibm.ws.logging;version=latest,\ + com.ibm.ws.kernel.service;version=latest,\ com.ibm.websphere.org.osgi.core;version=latest,\ com.ibm.websphere.org.osgi.service.component;version=latest,\ com.ibm.wsspi.org.osgi.service.component.annotations;version=latest,\ @@ -44,6 +46,6 @@ test.project:true com.ibm.ws.org.slf4j.api;version=latest, \ com.ibm.ws.org.slf4j.jdk14;version=latest, \ org.jboss.logging:jboss-logging,\ - com.ibm.ws.kernel.service;version=latest,\ - io.openliberty.microprofile.openapi.internal.common;version=latest - + io.openliberty.microprofile.openapi.internal.common;version=latest,\ + com.ibm.ws.org.apache.commons.lang3;version=latest,\ + com.ibm.websphere.javaee.jaxrs.2.1;version=latest diff --git a/dev/io.openliberty.microprofile.openapi.2.0.internal_test/src/io/openliberty/microprofile/openapi20/test/utils/TestServiceCaller.java b/dev/io.openliberty.microprofile.openapi.2.0.internal_test/src/io/openliberty/microprofile/openapi20/test/utils/TestServiceCaller.java new file mode 100644 index 00000000000..9f598b0a26d --- /dev/null +++ b/dev/io.openliberty.microprofile.openapi.2.0.internal_test/src/io/openliberty/microprofile/openapi20/test/utils/TestServiceCaller.java @@ -0,0 +1,51 @@ +/******************************************************************************* + * Copyright (c) 2024 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package io.openliberty.microprofile.openapi20.test.utils; + +import java.util.Optional; +import java.util.function.Consumer; +import java.util.function.Function; + +import com.ibm.ws.kernel.service.util.ServiceCaller; + +/** + * ServiceCaller implementation for unit tests which always uses a manually provided instance. + * + * @param the service type + */ +public class TestServiceCaller extends ServiceCaller { + + private S instance; + + public TestServiceCaller(Class service, S testInstance) { + super(TestServiceCaller.class, service); + this.instance = testInstance; + } + + @Override + public boolean call(Consumer consumer) { + consumer.accept(instance); + return true; + } + + @Override + public Optional run(Function function) { + return Optional.ofNullable(function.apply(instance)); + } + + @Override + public Optional current() { + return Optional.of(instance); + } + + @Override + public void unget() {} + +} diff --git a/dev/io.openliberty.microprofile.openapi.2.0.internal/test/io/openliberty/microprofile/openapi20/test/utils/TestValidationContextHelper.java b/dev/io.openliberty.microprofile.openapi.2.0.internal_test/src/io/openliberty/microprofile/openapi20/test/utils/TestValidationContextHelper.java similarity index 93% rename from dev/io.openliberty.microprofile.openapi.2.0.internal/test/io/openliberty/microprofile/openapi20/test/utils/TestValidationContextHelper.java rename to dev/io.openliberty.microprofile.openapi.2.0.internal_test/src/io/openliberty/microprofile/openapi20/test/utils/TestValidationContextHelper.java index 6264cb66d38..1ff8eee590a 100644 --- a/dev/io.openliberty.microprofile.openapi.2.0.internal/test/io/openliberty/microprofile/openapi20/test/utils/TestValidationContextHelper.java +++ b/dev/io.openliberty.microprofile.openapi.2.0.internal_test/src/io/openliberty/microprofile/openapi20/test/utils/TestValidationContextHelper.java @@ -4,7 +4,7 @@ * are made available under the terms of the Eclipse Public License 2.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-2.0/ - * + * * SPDX-License-Identifier: EPL-2.0 * * Contributors: @@ -19,14 +19,13 @@ import org.eclipse.microprofile.openapi.models.OpenAPI; import io.openliberty.microprofile.openapi20.internal.utils.OpenAPIModelWalker.Context; -import io.smallrye.openapi.api.models.OpenAPIImpl; public class TestValidationContextHelper implements Context { - private final OpenAPIImpl openAPI; + private final OpenAPI openAPI; private final Deque pathSegments = new ArrayDeque<>(); - public TestValidationContextHelper(OpenAPIImpl openAPI) { + public TestValidationContextHelper(OpenAPI openAPI) { this.openAPI = openAPI; } diff --git a/dev/io.openliberty.microprofile.openapi.2.0.internal/test/io/openliberty/microprofile/openapi20/test/utils/TestValidationHelper.java b/dev/io.openliberty.microprofile.openapi.2.0.internal_test/src/io/openliberty/microprofile/openapi20/test/utils/TestValidationHelper.java similarity index 71% rename from dev/io.openliberty.microprofile.openapi.2.0.internal/test/io/openliberty/microprofile/openapi20/test/utils/TestValidationHelper.java rename to dev/io.openliberty.microprofile.openapi.2.0.internal_test/src/io/openliberty/microprofile/openapi20/test/utils/TestValidationHelper.java index ca88e9e45a9..64a54925521 100644 --- a/dev/io.openliberty.microprofile.openapi.2.0.internal/test/io/openliberty/microprofile/openapi20/test/utils/TestValidationHelper.java +++ b/dev/io.openliberty.microprofile.openapi.2.0.internal_test/src/io/openliberty/microprofile/openapi20/test/utils/TestValidationHelper.java @@ -1,10 +1,10 @@ /******************************************************************************* - * Copyright (c) 2020 IBM Corporation and others. + * Copyright (c) 2020, 2024 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License 2.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-2.0/ - * + * * SPDX-License-Identifier: EPL-2.0 * * Contributors: @@ -23,6 +23,9 @@ import io.openliberty.microprofile.openapi20.internal.services.OASValidationResult; import io.openliberty.microprofile.openapi20.internal.services.OASValidationResult.ValidationEvent; import io.openliberty.microprofile.openapi20.internal.services.OASValidationResult.ValidationEvent.Severity; +import io.openliberty.microprofile.openapi20.internal.utils.OpenAPIModelWalker.Context; +import io.openliberty.microprofile.openapi20.internal.utils.ValidationMessageConstants; +import io.openliberty.microprofile.openapi20.internal.validation.ReferenceValidator; import io.openliberty.microprofile.openapi20.internal.validation.ValidationHelper; public class TestValidationHelper implements ValidationHelper { @@ -60,6 +63,28 @@ public void addLinkOperationId(String operationId, String location) { validateLinkOperationIds(); } + @Override + public T validateReference(Context context, String key, String ref, Class clazz) { + Object component = validateReference(context, key, ref); + if (component == null) { + // We didn't get an object back + // This could be because the reference was invalid, or because it was external, or because we didn't know how to validate it + // Any relevant validation events will have been raised, there's nothing more for us to do + return null; + } else if (clazz.isInstance(component)) { + return clazz.cast(component); + } else { + final String message = Tr.formatMessage(tc, ValidationMessageConstants.REFERENCE_TO_OBJECT_INVALID, ref); + addValidationEvent(new ValidationEvent(Severity.ERROR, context.getLocation(), message)); + return null; + } + } + + @Override + public Object validateReference(Context context, String key, String ref) { + return ReferenceValidator.getInstance().validate(this, context, key, ref); + } + public void validateLinkOperationIds() { for (String k : linkOperationIds.keySet()) { if (!operationIds.contains(k)) { diff --git a/dev/io.openliberty.microprofile.openapi.2.0.internal_test/src/io/openliberty/microprofile/openapi20/test/utils/ValidationResultMatcher.java b/dev/io.openliberty.microprofile.openapi.2.0.internal_test/src/io/openliberty/microprofile/openapi20/test/utils/ValidationResultMatcher.java new file mode 100644 index 00000000000..1ec02a1a61b --- /dev/null +++ b/dev/io.openliberty.microprofile.openapi.2.0.internal_test/src/io/openliberty/microprofile/openapi20/test/utils/ValidationResultMatcher.java @@ -0,0 +1,254 @@ +/******************************************************************************* + * Copyright (c) 2024 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package io.openliberty.microprofile.openapi20.test.utils; + +import static org.hamcrest.Matchers.equalTo; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +import org.hamcrest.Description; +import org.hamcrest.Matcher; +import org.hamcrest.TypeSafeDiagnosingMatcher; + +import io.openliberty.microprofile.openapi20.internal.services.OASValidationResult; +import io.openliberty.microprofile.openapi20.internal.services.OASValidationResult.ValidationEvent; +import io.openliberty.microprofile.openapi20.internal.services.OASValidationResult.ValidationEvent.Severity; + +/** + * Checks whether an {@link OASValidationResult} contains the expected validation events + *

+ * Examples: + * + *

+ * 
+ * assertThat(result, successful());
+ * assertThat(result, hasError("The foo reference is not valid");
+ * assertThat(result, failedWithEvents().error("some error")
+ *                                      .warning(containsString("foo"));
+ * 
+ * 
+ */ +public class ValidationResultMatcher extends TypeSafeDiagnosingMatcher { + + private final List expectedEvents; + private final boolean requireAll; + + public ValidationResultMatcher(boolean requireAll) { + this.expectedEvents = new ArrayList<>(); + this.requireAll = true; + } + + public ValidationResultMatcher(List expectedEvents) { + this.expectedEvents = expectedEvents; + this.requireAll = true; + } + + @Override + public void describeTo(Description desc) { + desc.appendText("Validation result"); + if (expectedEvents.isEmpty()) { + desc.appendText(" with no events"); + } else { + desc.appendText(" with events"); + for (ExpectedEvent e : expectedEvents) { + desc.appendText("\n "); + describeExpectedEvent(desc, e); + } + } + } + + @Override + protected boolean matchesSafely(OASValidationResult value, Description desc) { + List events = new ArrayList<>(value.getEvents()); + List unmetExpectations = new ArrayList<>(); + for (ExpectedEvent expected : expectedEvents) { + boolean expectationMatched = false; + for (ValidationEvent e : events) { + if (expected.expectedSeverity != null && e.severity != expected.expectedSeverity) { + continue; + } + if (expected.locationMatcher != null && !expected.locationMatcher.matches(e.location)) { + continue; + } + if (expected.messageMatcher != null && !expected.messageMatcher.matches(e.message)) { + continue; + } + expectationMatched = true; + events.remove(e); + break; + } + if (!expectationMatched) { + unmetExpectations.add(expected); + } + } + + for (ExpectedEvent event : unmetExpectations) { + desc.appendText("Expected error not found: "); + describeExpectedEvent(desc, event); + desc.appendText("\n"); + } + + if (requireAll) { + for (ValidationEvent event : events) { + desc.appendText("Unexpected error: "); + describeValidationEvent(desc, event); + desc.appendText("\n"); + } + } + + if (requireAll) { + return unmetExpectations.isEmpty() && events.isEmpty(); + } else { + return unmetExpectations.isEmpty(); + } + } + + private void describeExpectedEvent(Description desc, ExpectedEvent e) { + if (e.expectedSeverity != null) { + desc.appendText("severity = ").appendValue(e.expectedSeverity).appendText(", "); + } + if (e.messageMatcher != null) { + desc.appendText("message ").appendDescriptionOf(e.messageMatcher).appendText(", "); + } + if (e.locationMatcher != null) { + desc.appendText("location ").appendDescriptionOf(e.locationMatcher).appendText(", "); + } + } + + private void describeValidationEvent(Description desc, ValidationEvent event) { + desc.appendText("severity = ").appendValue(event.severity).appendText(", "); + desc.appendText("message = ").appendValue(event.message).appendText(", "); + desc.appendText("location = ").appendValue(event.location); + } + + private static class ExpectedEvent { + Severity expectedSeverity; + Matcher locationMatcher; + Matcher messageMatcher; + + public ExpectedEvent(Severity expectedSeverity, Matcher locationMatcher, Matcher messageMatcher) { + super(); + this.expectedSeverity = expectedSeverity; + this.locationMatcher = locationMatcher; + this.messageMatcher = messageMatcher; + } + } + + /** + * Add an expected warning to this matcher + * + * @param message the expected warning message + * @return {@code this} + */ + public ValidationResultMatcher warning(String message) { + return error(equalTo(message)); + } + + /** + * Add an expected warning to this matcher + * + * @param messageMatcher a matcher which must match the warning message + * @return {@code this} + */ + public ValidationResultMatcher warning(Matcher messageMatcher) { + return event(Severity.WARNING, messageMatcher, null); + } + + /** + * Add an expected error to this matcher + * + * @param message the expected error message + * @return {@code this} + */ + public ValidationResultMatcher error(String message) { + return error(equalTo(message)); + } + + /** + * Add an expected error to this matcher + * + * @param messageMatcher a matcher which must match the error message + * @return {@code this} + */ + public ValidationResultMatcher error(Matcher messageMatcher) { + return event(Severity.ERROR, messageMatcher, null); + } + + /** + * Add an expected event to this matcher + * + * @param severity the expected severity, or {@code null} to not assert the severity + * @param messageMatcher a matcher that must match the event message, or {@code null} to not assert the message + * @param locationMatcher a matcher that must match the event location, or {@code null} to not assert the location + * @return {@code this} + */ + public ValidationResultMatcher event(Severity severity, Matcher messageMatcher, Matcher locationMatcher) { + expectedEvents.add(new ExpectedEvent(severity, locationMatcher, messageMatcher)); + return this; + } + + /** + * Creates a matcher that asserts there are no validation events + * + * @return the new matcher + */ + public static ValidationResultMatcher successful() { + return new ValidationResultMatcher(Collections.emptyList()); + } + + /** + * Creates a matcher which will assert that exactly the expected events are contained within the result. + *

+ * If the result contains any unexpected events, the assertion will fail, even if all the expected events are found. + *

+ * Expected events should be added before using the matcher. + * + * @return the new matcher + */ + public static ValidationResultMatcher failedWithEvents() { + return new ValidationResultMatcher(true); + } + + /** + * Creates a matcher which will assert that at least the expected events are contained within the result. + *

+ * If the result contains any unexpected events, the assertion can still succeed as long as all the expected events are found. + *

+ * Expected events should be added before using the matcher. + * + * @return the new matcher + */ + public static ValidationResultMatcher failedIncludingEvents() { + return new ValidationResultMatcher(false); + } + + /** + * Creates a matcher that asserts that the result contains one error with the given message + * + * @param message the expected error message + * @return the new matcher + */ + public static ValidationResultMatcher hasError(String message) { + return hasError(equalTo(message)); + } + + /** + * Creates a matcher that asserts that the result contains one error which is accepted by the given matcher + * + * @param messageMatcher the matcher for the expected error message + * @return the new matcher + */ + public static ValidationResultMatcher hasError(Matcher messageMatcher) { + return failedWithEvents().error(messageMatcher); + } + +} diff --git a/dev/io.openliberty.microprofile.openapi.2.0.internal_test/src/io/openliberty/microprofile/openapi20/test/utils/package-info.java b/dev/io.openliberty.microprofile.openapi.2.0.internal_test/src/io/openliberty/microprofile/openapi20/test/utils/package-info.java new file mode 100644 index 00000000000..c82e749bc6b --- /dev/null +++ b/dev/io.openliberty.microprofile.openapi.2.0.internal_test/src/io/openliberty/microprofile/openapi20/test/utils/package-info.java @@ -0,0 +1,11 @@ +/******************************************************************************* + * Copyright (c) 2024 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +@org.osgi.annotation.versioning.Version("0.1") +package io.openliberty.microprofile.openapi20.test.utils; \ No newline at end of file diff --git a/dev/io.openliberty.microprofile.openapi.2.0.internal/test/io/openliberty/microprofile/openapi20/validation/test/CallbacksValidatorTest.java b/dev/io.openliberty.microprofile.openapi.2.0.internal_test/src/io/openliberty/microprofile/openapi20/validation/test/CallbacksValidatorTest.java similarity index 100% rename from dev/io.openliberty.microprofile.openapi.2.0.internal/test/io/openliberty/microprofile/openapi20/validation/test/CallbacksValidatorTest.java rename to dev/io.openliberty.microprofile.openapi.2.0.internal_test/src/io/openliberty/microprofile/openapi20/validation/test/CallbacksValidatorTest.java diff --git a/dev/io.openliberty.microprofile.openapi.2.0.internal/test/io/openliberty/microprofile/openapi20/validation/test/ComponentsValidatorTest.java b/dev/io.openliberty.microprofile.openapi.2.0.internal_test/src/io/openliberty/microprofile/openapi20/validation/test/ComponentsValidatorTest.java similarity index 100% rename from dev/io.openliberty.microprofile.openapi.2.0.internal/test/io/openliberty/microprofile/openapi20/validation/test/ComponentsValidatorTest.java rename to dev/io.openliberty.microprofile.openapi.2.0.internal_test/src/io/openliberty/microprofile/openapi20/validation/test/ComponentsValidatorTest.java diff --git a/dev/io.openliberty.microprofile.openapi.2.0.internal/test/io/openliberty/microprofile/openapi20/validation/test/ContactValidatorTest.java b/dev/io.openliberty.microprofile.openapi.2.0.internal_test/src/io/openliberty/microprofile/openapi20/validation/test/ContactValidatorTest.java similarity index 100% rename from dev/io.openliberty.microprofile.openapi.2.0.internal/test/io/openliberty/microprofile/openapi20/validation/test/ContactValidatorTest.java rename to dev/io.openliberty.microprofile.openapi.2.0.internal_test/src/io/openliberty/microprofile/openapi20/validation/test/ContactValidatorTest.java diff --git a/dev/io.openliberty.microprofile.openapi.2.0.internal/test/io/openliberty/microprofile/openapi20/validation/test/DiscriminatorValidatorTest.java b/dev/io.openliberty.microprofile.openapi.2.0.internal_test/src/io/openliberty/microprofile/openapi20/validation/test/DiscriminatorValidatorTest.java similarity index 100% rename from dev/io.openliberty.microprofile.openapi.2.0.internal/test/io/openliberty/microprofile/openapi20/validation/test/DiscriminatorValidatorTest.java rename to dev/io.openliberty.microprofile.openapi.2.0.internal_test/src/io/openliberty/microprofile/openapi20/validation/test/DiscriminatorValidatorTest.java diff --git a/dev/io.openliberty.microprofile.openapi.2.0.internal/test/io/openliberty/microprofile/openapi20/validation/test/ExampleValidatorTest.java b/dev/io.openliberty.microprofile.openapi.2.0.internal_test/src/io/openliberty/microprofile/openapi20/validation/test/ExampleValidatorTest.java similarity index 100% rename from dev/io.openliberty.microprofile.openapi.2.0.internal/test/io/openliberty/microprofile/openapi20/validation/test/ExampleValidatorTest.java rename to dev/io.openliberty.microprofile.openapi.2.0.internal_test/src/io/openliberty/microprofile/openapi20/validation/test/ExampleValidatorTest.java diff --git a/dev/io.openliberty.microprofile.openapi.2.0.internal/test/io/openliberty/microprofile/openapi20/validation/test/ExtensionValidatorTest.java b/dev/io.openliberty.microprofile.openapi.2.0.internal_test/src/io/openliberty/microprofile/openapi20/validation/test/ExtensionValidatorTest.java similarity index 100% rename from dev/io.openliberty.microprofile.openapi.2.0.internal/test/io/openliberty/microprofile/openapi20/validation/test/ExtensionValidatorTest.java rename to dev/io.openliberty.microprofile.openapi.2.0.internal_test/src/io/openliberty/microprofile/openapi20/validation/test/ExtensionValidatorTest.java diff --git a/dev/io.openliberty.microprofile.openapi.2.0.internal/test/io/openliberty/microprofile/openapi20/validation/test/ExternalDocumentationValidatorTest.java b/dev/io.openliberty.microprofile.openapi.2.0.internal_test/src/io/openliberty/microprofile/openapi20/validation/test/ExternalDocumentationValidatorTest.java similarity index 100% rename from dev/io.openliberty.microprofile.openapi.2.0.internal/test/io/openliberty/microprofile/openapi20/validation/test/ExternalDocumentationValidatorTest.java rename to dev/io.openliberty.microprofile.openapi.2.0.internal_test/src/io/openliberty/microprofile/openapi20/validation/test/ExternalDocumentationValidatorTest.java diff --git a/dev/io.openliberty.microprofile.openapi.2.0.internal/test/io/openliberty/microprofile/openapi20/validation/test/HeaderValidatorTest.java b/dev/io.openliberty.microprofile.openapi.2.0.internal_test/src/io/openliberty/microprofile/openapi20/validation/test/HeaderValidatorTest.java similarity index 100% rename from dev/io.openliberty.microprofile.openapi.2.0.internal/test/io/openliberty/microprofile/openapi20/validation/test/HeaderValidatorTest.java rename to dev/io.openliberty.microprofile.openapi.2.0.internal_test/src/io/openliberty/microprofile/openapi20/validation/test/HeaderValidatorTest.java diff --git a/dev/io.openliberty.microprofile.openapi.2.0.internal/test/io/openliberty/microprofile/openapi20/validation/test/InfoValidatorTest.java b/dev/io.openliberty.microprofile.openapi.2.0.internal_test/src/io/openliberty/microprofile/openapi20/validation/test/InfoValidatorTest.java similarity index 100% rename from dev/io.openliberty.microprofile.openapi.2.0.internal/test/io/openliberty/microprofile/openapi20/validation/test/InfoValidatorTest.java rename to dev/io.openliberty.microprofile.openapi.2.0.internal_test/src/io/openliberty/microprofile/openapi20/validation/test/InfoValidatorTest.java diff --git a/dev/io.openliberty.microprofile.openapi.2.0.internal/test/io/openliberty/microprofile/openapi20/validation/test/LicenseValidatorTest.java b/dev/io.openliberty.microprofile.openapi.2.0.internal_test/src/io/openliberty/microprofile/openapi20/validation/test/LicenseValidatorTest.java similarity index 100% rename from dev/io.openliberty.microprofile.openapi.2.0.internal/test/io/openliberty/microprofile/openapi20/validation/test/LicenseValidatorTest.java rename to dev/io.openliberty.microprofile.openapi.2.0.internal_test/src/io/openliberty/microprofile/openapi20/validation/test/LicenseValidatorTest.java diff --git a/dev/io.openliberty.microprofile.openapi.2.0.internal/test/io/openliberty/microprofile/openapi20/validation/test/LinkValidatorTest.java b/dev/io.openliberty.microprofile.openapi.2.0.internal_test/src/io/openliberty/microprofile/openapi20/validation/test/LinkValidatorTest.java similarity index 100% rename from dev/io.openliberty.microprofile.openapi.2.0.internal/test/io/openliberty/microprofile/openapi20/validation/test/LinkValidatorTest.java rename to dev/io.openliberty.microprofile.openapi.2.0.internal_test/src/io/openliberty/microprofile/openapi20/validation/test/LinkValidatorTest.java diff --git a/dev/io.openliberty.microprofile.openapi.2.0.internal/test/io/openliberty/microprofile/openapi20/validation/test/MediaTypeValidatorTest.java b/dev/io.openliberty.microprofile.openapi.2.0.internal_test/src/io/openliberty/microprofile/openapi20/validation/test/MediaTypeValidatorTest.java similarity index 100% rename from dev/io.openliberty.microprofile.openapi.2.0.internal/test/io/openliberty/microprofile/openapi20/validation/test/MediaTypeValidatorTest.java rename to dev/io.openliberty.microprofile.openapi.2.0.internal_test/src/io/openliberty/microprofile/openapi20/validation/test/MediaTypeValidatorTest.java diff --git a/dev/io.openliberty.microprofile.openapi.2.0.internal/test/io/openliberty/microprofile/openapi20/validation/test/OAuthFlowValidatorTest.java b/dev/io.openliberty.microprofile.openapi.2.0.internal_test/src/io/openliberty/microprofile/openapi20/validation/test/OAuthFlowValidatorTest.java similarity index 100% rename from dev/io.openliberty.microprofile.openapi.2.0.internal/test/io/openliberty/microprofile/openapi20/validation/test/OAuthFlowValidatorTest.java rename to dev/io.openliberty.microprofile.openapi.2.0.internal_test/src/io/openliberty/microprofile/openapi20/validation/test/OAuthFlowValidatorTest.java diff --git a/dev/io.openliberty.microprofile.openapi.2.0.internal/test/io/openliberty/microprofile/openapi20/validation/test/OAuthFlowsValidatorTest.java b/dev/io.openliberty.microprofile.openapi.2.0.internal_test/src/io/openliberty/microprofile/openapi20/validation/test/OAuthFlowsValidatorTest.java similarity index 100% rename from dev/io.openliberty.microprofile.openapi.2.0.internal/test/io/openliberty/microprofile/openapi20/validation/test/OAuthFlowsValidatorTest.java rename to dev/io.openliberty.microprofile.openapi.2.0.internal_test/src/io/openliberty/microprofile/openapi20/validation/test/OAuthFlowsValidatorTest.java diff --git a/dev/io.openliberty.microprofile.openapi.2.0.internal/test/io/openliberty/microprofile/openapi20/validation/test/OpenAPIRuntimeExpressionTest.java b/dev/io.openliberty.microprofile.openapi.2.0.internal_test/src/io/openliberty/microprofile/openapi20/validation/test/OpenAPIRuntimeExpressionTest.java similarity index 100% rename from dev/io.openliberty.microprofile.openapi.2.0.internal/test/io/openliberty/microprofile/openapi20/validation/test/OpenAPIRuntimeExpressionTest.java rename to dev/io.openliberty.microprofile.openapi.2.0.internal_test/src/io/openliberty/microprofile/openapi20/validation/test/OpenAPIRuntimeExpressionTest.java diff --git a/dev/io.openliberty.microprofile.openapi.2.0.internal/test/io/openliberty/microprofile/openapi20/validation/test/OpenAPIValidatorTest.java b/dev/io.openliberty.microprofile.openapi.2.0.internal_test/src/io/openliberty/microprofile/openapi20/validation/test/OpenAPIValidatorTest.java similarity index 84% rename from dev/io.openliberty.microprofile.openapi.2.0.internal/test/io/openliberty/microprofile/openapi20/validation/test/OpenAPIValidatorTest.java rename to dev/io.openliberty.microprofile.openapi.2.0.internal_test/src/io/openliberty/microprofile/openapi20/validation/test/OpenAPIValidatorTest.java index 4922f30e5a1..5f79afc5420 100644 --- a/dev/io.openliberty.microprofile.openapi.2.0.internal/test/io/openliberty/microprofile/openapi20/validation/test/OpenAPIValidatorTest.java +++ b/dev/io.openliberty.microprofile.openapi.2.0.internal_test/src/io/openliberty/microprofile/openapi20/validation/test/OpenAPIValidatorTest.java @@ -1,10 +1,10 @@ /******************************************************************************* - * Copyright (c) 2020 IBM Corporation and others. + * Copyright (c) 2020, 2024 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License 2.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-2.0/ - * + * * SPDX-License-Identifier: EPL-2.0 * * Contributors: @@ -12,15 +12,21 @@ *******************************************************************************/ package io.openliberty.microprofile.openapi20.validation.test; +import static io.openliberty.microprofile.openapi20.test.utils.ValidationResultMatcher.hasError; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.containsString; + import java.util.ArrayList; import java.util.List; +import org.eclipse.microprofile.openapi.models.OpenAPI; import org.eclipse.microprofile.openapi.models.tags.Tag; import org.junit.Assert; import org.junit.Test; import io.openliberty.microprofile.openapi20.internal.utils.OpenAPIModelWalker.Context; import io.openliberty.microprofile.openapi20.internal.validation.OpenAPIValidator; +import io.openliberty.microprofile.openapi20.internal.validation.TypeValidator; import io.openliberty.microprofile.openapi20.test.utils.TestValidationContextHelper; import io.openliberty.microprofile.openapi20.test.utils.TestValidationHelper; import io.smallrye.openapi.api.models.OpenAPIImpl; @@ -32,12 +38,16 @@ public class OpenAPIValidatorTest { OpenAPIImpl model = new OpenAPIImpl(); - Context context = new TestValidationContextHelper(model); + protected Context context = new TestValidationContextHelper(model); + + protected TypeValidator getValidator() { + return OpenAPIValidator.getInstance(); + } @Test public void testCorrectOpenAPI() { - OpenAPIValidator validator = OpenAPIValidator.getInstance(); + TypeValidator validator = getValidator(); TestValidationHelper vh = new TestValidationHelper(); OpenAPIImpl openapi = new OpenAPIImpl(); @@ -70,7 +80,7 @@ public void testCorrectOpenAPI() { @Test public void testNullOpenAPI() { - OpenAPIValidator validator = OpenAPIValidator.getInstance(); + TypeValidator validator = getValidator(); TestValidationHelper vh = new TestValidationHelper(); OpenAPIImpl openapi = null; @@ -80,7 +90,7 @@ public void testNullOpenAPI() { @Test public void testNoOpenAPI() { - OpenAPIValidator validator = OpenAPIValidator.getInstance(); + TypeValidator validator = getValidator(); TestValidationHelper vh = new TestValidationHelper(); OpenAPIImpl openapi = new OpenAPIImpl(); @@ -108,13 +118,12 @@ public void testNoOpenAPI() { openapi.setTags(tags); validator.validate(vh, context, openapi); - Assert.assertEquals(1, vh.getEventsSize()); - Assert.assertTrue(vh.getResult().getEvents().get(0).message.contains("Required \"openapi\" field is missing or is set to an invalid value")); + assertThat(vh.getResult(), hasError(containsString("Required \"openapi\" field is missing or is set to an invalid value"))); } @Test public void testNoInfoOpenAPI() { - OpenAPIValidator validator = OpenAPIValidator.getInstance(); + TypeValidator validator = getValidator(); TestValidationHelper vh = new TestValidationHelper(); OpenAPIImpl openapi = new OpenAPIImpl(); @@ -146,7 +155,7 @@ public void testNoInfoOpenAPI() { @Test public void testNoPathsOpenAPI() { - OpenAPIValidator validator = OpenAPIValidator.getInstance(); + TypeValidator validator = getValidator(); TestValidationHelper vh = new TestValidationHelper(); OpenAPIImpl openapi = new OpenAPIImpl(); @@ -172,13 +181,12 @@ public void testNoPathsOpenAPI() { openapi.setTags(tags); validator.validate(vh, context, openapi); - Assert.assertEquals(1, vh.getEventsSize()); - Assert.assertTrue(vh.getResult().getEvents().get(0).message.contains("Required \"paths\" field is missing or is set to an invalid value")); + assertThat(vh.getResult(), hasError("Required \"paths\" field is missing or is set to an invalid value")); } @Test public void testOpenAPIWithInvalidVersion() { - OpenAPIValidator validator = OpenAPIValidator.getInstance(); + TypeValidator validator = getValidator(); TestValidationHelper vh = new TestValidationHelper(); OpenAPIImpl openapi = new OpenAPIImpl(); @@ -206,13 +214,12 @@ public void testOpenAPIWithInvalidVersion() { openapi.setTags(tags); validator.validate(vh, context, openapi); - Assert.assertEquals(1, vh.getEventsSize()); - Assert.assertTrue(vh.getResult().getEvents().get(0).message.contains("The OpenAPI Object must contain a valid OpenAPI specification version.")); + assertThat(vh.getResult(), hasError(containsString("The OpenAPI Object must contain a valid OpenAPI specification version."))); } @Test public void testNullTagsOpenAPI() { - OpenAPIValidator validator = OpenAPIValidator.getInstance(); + TypeValidator validator = getValidator(); TestValidationHelper vh = new TestValidationHelper(); OpenAPIImpl openapi = new OpenAPIImpl(); @@ -235,7 +242,7 @@ public void testNullTagsOpenAPI() { @Test public void testOpenAPITagsNotUnique() { - OpenAPIValidator validator = OpenAPIValidator.getInstance(); + TypeValidator validator = getValidator(); TestValidationHelper vh = new TestValidationHelper(); OpenAPIImpl openapi = new OpenAPIImpl(); diff --git a/dev/io.openliberty.microprofile.openapi.2.0.internal/test/io/openliberty/microprofile/openapi20/validation/test/OperationValidatorTest.java b/dev/io.openliberty.microprofile.openapi.2.0.internal_test/src/io/openliberty/microprofile/openapi20/validation/test/OperationValidatorTest.java similarity index 91% rename from dev/io.openliberty.microprofile.openapi.2.0.internal/test/io/openliberty/microprofile/openapi20/validation/test/OperationValidatorTest.java rename to dev/io.openliberty.microprofile.openapi.2.0.internal_test/src/io/openliberty/microprofile/openapi20/validation/test/OperationValidatorTest.java index 7e78cb6fc39..4b408508e3a 100644 --- a/dev/io.openliberty.microprofile.openapi.2.0.internal/test/io/openliberty/microprofile/openapi20/validation/test/OperationValidatorTest.java +++ b/dev/io.openliberty.microprofile.openapi.2.0.internal_test/src/io/openliberty/microprofile/openapi20/validation/test/OperationValidatorTest.java @@ -1,10 +1,10 @@ /******************************************************************************* - * Copyright (c) 2020 IBM Corporation and others. + * Copyright (c) 2020, 2024 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License 2.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-2.0/ - * + * * SPDX-License-Identifier: EPL-2.0 * * Contributors: @@ -12,11 +12,13 @@ *******************************************************************************/ package io.openliberty.microprofile.openapi20.validation.test; +import org.eclipse.microprofile.openapi.models.Operation; import org.junit.Assert; import org.junit.Test; import io.openliberty.microprofile.openapi20.internal.utils.OpenAPIModelWalker.Context; import io.openliberty.microprofile.openapi20.internal.validation.OperationValidator; +import io.openliberty.microprofile.openapi20.internal.validation.TypeValidator; import io.openliberty.microprofile.openapi20.test.utils.TestValidationContextHelper; import io.openliberty.microprofile.openapi20.test.utils.TestValidationHelper; import io.smallrye.openapi.api.models.OpenAPIImpl; @@ -33,9 +35,13 @@ public class OperationValidatorTest { String key = null; + protected TypeValidator getValidator() { + return OperationValidator.getInstance(); + } + @Test public void testCorrectOperation() { - OperationValidator validator = OperationValidator.getInstance(); + TypeValidator validator = getValidator(); TestValidationHelper vh = new TestValidationHelper(); String pathNameOne = "/my-test-path-one/"; @@ -63,7 +69,7 @@ public void testCorrectOperation() { @Test public void testNullOperation() { - OperationValidator validator = OperationValidator.getInstance(); + TypeValidator validator = getValidator(); TestValidationHelper vh = new TestValidationHelper(); OpenAPIImpl model = new OpenAPIImpl(); @@ -77,7 +83,7 @@ public void testNullOperation() { @Test public void testOperationWithNoResponses() { - OperationValidator validator = OperationValidator.getInstance(); + TypeValidator validator = getValidator(); TestValidationHelper vh = new TestValidationHelper(); String pathNameOne = "/my-test-path-one/"; @@ -101,7 +107,7 @@ public void testOperationWithNoResponses() { @Test public void testOperationWithNonUniqueIds() { - OperationValidator validator = OperationValidator.getInstance(); + TypeValidator validator = getValidator(); TestValidationHelper vh = new TestValidationHelper(); String pathNameOne = "/my-test-path-one/"; diff --git a/dev/io.openliberty.microprofile.openapi.2.0.internal/test/io/openliberty/microprofile/openapi20/validation/test/ParameterValidatorTest.java b/dev/io.openliberty.microprofile.openapi.2.0.internal_test/src/io/openliberty/microprofile/openapi20/validation/test/ParameterValidatorTest.java similarity index 100% rename from dev/io.openliberty.microprofile.openapi.2.0.internal/test/io/openliberty/microprofile/openapi20/validation/test/ParameterValidatorTest.java rename to dev/io.openliberty.microprofile.openapi.2.0.internal_test/src/io/openliberty/microprofile/openapi20/validation/test/ParameterValidatorTest.java diff --git a/dev/io.openliberty.microprofile.openapi.2.0.internal/test/io/openliberty/microprofile/openapi20/validation/test/PathItemValidatorTest.java b/dev/io.openliberty.microprofile.openapi.2.0.internal_test/src/io/openliberty/microprofile/openapi20/validation/test/PathItemValidatorTest.java similarity index 94% rename from dev/io.openliberty.microprofile.openapi.2.0.internal/test/io/openliberty/microprofile/openapi20/validation/test/PathItemValidatorTest.java rename to dev/io.openliberty.microprofile.openapi.2.0.internal_test/src/io/openliberty/microprofile/openapi20/validation/test/PathItemValidatorTest.java index 314d9dff5a4..b79a5d4f936 100644 --- a/dev/io.openliberty.microprofile.openapi.2.0.internal/test/io/openliberty/microprofile/openapi20/validation/test/PathItemValidatorTest.java +++ b/dev/io.openliberty.microprofile.openapi.2.0.internal_test/src/io/openliberty/microprofile/openapi20/validation/test/PathItemValidatorTest.java @@ -1,10 +1,10 @@ /******************************************************************************* - * Copyright (c) 2020 IBM Corporation and others. + * Copyright (c) 2020, 2024 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License 2.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-2.0/ - * + * * SPDX-License-Identifier: EPL-2.0 * * Contributors: @@ -12,10 +12,14 @@ *******************************************************************************/ package io.openliberty.microprofile.openapi20.validation.test; +import static io.openliberty.microprofile.openapi20.test.utils.ValidationResultMatcher.successful; +import static org.hamcrest.MatcherAssert.assertThat; + import java.util.ArrayList; import java.util.List; import org.eclipse.microprofile.openapi.models.Components; +import org.eclipse.microprofile.openapi.models.PathItem; import org.eclipse.microprofile.openapi.models.parameters.Parameter; import org.eclipse.microprofile.openapi.models.parameters.Parameter.In; import org.junit.Assert; @@ -23,6 +27,7 @@ import io.openliberty.microprofile.openapi20.internal.utils.OpenAPIModelWalker.Context; import io.openliberty.microprofile.openapi20.internal.validation.PathItemValidator; +import io.openliberty.microprofile.openapi20.internal.validation.TypeValidator; import io.openliberty.microprofile.openapi20.test.utils.TestValidationContextHelper; import io.openliberty.microprofile.openapi20.test.utils.TestValidationHelper; import io.smallrye.openapi.api.models.ComponentsImpl; @@ -38,19 +43,19 @@ */ public class PathItemValidatorTest { - OpenAPIImpl model = new OpenAPIImpl(); - Context context = new TestValidationContextHelper(model); + protected OpenAPIImpl model = new OpenAPIImpl(); + protected Context context = new TestValidationContextHelper(model); @Test public void testCorrectPathItem() { String key = "{username}"; - PathItemValidator validator = PathItemValidator.getInstance(); + TypeValidator validator = getValidator(); TestValidationHelper vh = new TestValidationHelper(); PathItemImpl pathItem = new PathItemImpl(); - pathItem.setSummary("This is a correctly set pathItem for testing of PathItemValidator."); + pathItem.setSummary("This is a correctly set pathItem for testing of TypeValidator."); ParameterImpl pathParamHeader = new ParameterImpl(); pathParamHeader.in(In.HEADER).name("token"); @@ -142,11 +147,15 @@ public void testCorrectPathItem() { Assert.assertEquals(0, vh.getEventsSize()); } + protected TypeValidator getValidator() { + return PathItemValidator.getInstance(); + } + @Test public void testNullPathItem() { String key = "{username}"; - PathItemValidator validator = PathItemValidator.getInstance(); + TypeValidator validator = getValidator(); TestValidationHelper vh = new TestValidationHelper(); PathItemImpl pathItem = null; @@ -159,7 +168,7 @@ public void testNullPathItem() { public void testNullRefInPathItem() { String key = "{username}"; - PathItemValidator validator = PathItemValidator.getInstance(); + TypeValidator validator = getValidator(); TestValidationHelper vh = new TestValidationHelper(); PathItemImpl pathItem = new PathItemImpl(); @@ -173,21 +182,21 @@ public void testNullRefInPathItem() { public void testExternalRefInPathItem() { String key = "{username}"; - PathItemValidator validator = PathItemValidator.getInstance(); + TypeValidator validator = getValidator(); TestValidationHelper vh = new TestValidationHelper(); PathItemImpl pathItem = new PathItemImpl(); pathItem.ref("http://test-this-ref.com"); validator.validate(vh, context, key, pathItem); - Assert.assertEquals(0, vh.getEventsSize()); + assertThat(vh.getResult(), successful()); } @Test public void testInternalRefInPathItem() { String key = "{username}"; - PathItemValidator validator = PathItemValidator.getInstance(); + TypeValidator validator = getValidator(); TestValidationHelper vh = new TestValidationHelper(); PathItemImpl pathItem = new PathItemImpl(); @@ -201,7 +210,7 @@ public void testInternalRefInPathItem() { public void testPathItemWithVar() { String key = "{$request.query.callbackUrl}/data"; - PathItemValidator validator = PathItemValidator.getInstance(); + TypeValidator validator = getValidator(); TestValidationHelper vh = new TestValidationHelper(); PathItemImpl pathItem = new PathItemImpl(); @@ -214,7 +223,7 @@ public void testPathItemWithVar() { public void testPathItemWithPathParamAndRequiredFalse() { String key = "{username}"; - PathItemValidator validator = PathItemValidator.getInstance(); + TypeValidator validator = getValidator(); TestValidationHelper vh = new TestValidationHelper(); PathItemImpl pathItem = new PathItemImpl(); @@ -248,7 +257,7 @@ public void testPathItemWithPathParamAndRequiredFalse() { public void testPathItemWithDuplicateParams() { String key = "{username}"; - PathItemValidator validator = PathItemValidator.getInstance(); + TypeValidator validator = getValidator(); TestValidationHelper vh = new TestValidationHelper(); PathItemImpl pathItem = new PathItemImpl(); @@ -287,7 +296,7 @@ public void testPathItemWithDuplicateParams() { public void testPathItemWithInvalidPathStrOne() { String key = "username}"; - PathItemValidator validator = PathItemValidator.getInstance(); + TypeValidator validator = getValidator(); TestValidationHelper vh = new TestValidationHelper(); PathItemImpl pathItem = new PathItemImpl(); @@ -324,7 +333,7 @@ public void testPathItemWithInvalidPathStrOne() { public void testPathItemWithInvalidPathStrTwo() { String key = "{username"; - PathItemValidator validator = PathItemValidator.getInstance(); + TypeValidator validator = getValidator(); TestValidationHelper vh = new TestValidationHelper(); PathItemImpl pathItem = new PathItemImpl(); @@ -361,7 +370,7 @@ public void testPathItemWithInvalidPathStrTwo() { public void testPathItemWithInvalidPathStrThree() { String key = "}username{"; - PathItemValidator validator = PathItemValidator.getInstance(); + TypeValidator validator = getValidator(); TestValidationHelper vh = new TestValidationHelper(); PathItemImpl pathItem = new PathItemImpl(); @@ -398,7 +407,7 @@ public void testPathItemWithInvalidPathStrThree() { public void testPathItemWithInvalidPathStrFour() { String key = "{}"; - PathItemValidator validator = PathItemValidator.getInstance(); + TypeValidator validator = getValidator(); TestValidationHelper vh = new TestValidationHelper(); PathItemImpl pathItem = new PathItemImpl(); @@ -435,7 +444,7 @@ public void testPathItemWithInvalidPathStrFour() { public void testPathItemWithInvalidPathStrFive() { String key = "{us{ername}"; - PathItemValidator validator = PathItemValidator.getInstance(); + TypeValidator validator = getValidator(); TestValidationHelper vh = new TestValidationHelper(); PathItemImpl pathItem = new PathItemImpl(); @@ -472,7 +481,7 @@ public void testPathItemWithInvalidPathStrFive() { public void testPathItemWithInvalidPathStrSix() { String key = "{username/}"; - PathItemValidator validator = PathItemValidator.getInstance(); + TypeValidator validator = getValidator(); TestValidationHelper vh = new TestValidationHelper(); PathItemImpl pathItem = new PathItemImpl(); @@ -509,7 +518,7 @@ public void testPathItemWithInvalidPathStrSix() { public void testPathItemWithDeclaredMultiplePathParam() { String key = "{username}/{id}"; - PathItemValidator validator = PathItemValidator.getInstance(); + TypeValidator validator = getValidator(); TestValidationHelper vh = new TestValidationHelper(); PathItemImpl pathItem = new PathItemImpl(); @@ -548,7 +557,7 @@ public void testPathItemWithDeclaredMultiplePathParam() { public void testPathItemWithUndeclaredPathParam() { String key = "{username}"; - PathItemValidator validator = PathItemValidator.getInstance(); + TypeValidator validator = getValidator(); TestValidationHelper vh = new TestValidationHelper(); PathItemImpl pathItem = new PathItemImpl(); @@ -589,7 +598,7 @@ public void testPathItemWithUndeclaredPathParam() { public void testPathItemWithMultipleUndeclaredPathParam() { String key = "{username}"; - PathItemValidator validator = PathItemValidator.getInstance(); + TypeValidator validator = getValidator(); TestValidationHelper vh = new TestValidationHelper(); PathItemImpl pathItem = new PathItemImpl(); @@ -635,7 +644,7 @@ public void testPathItemWithMultipleUndeclaredPathParam() { public void testPathItemWithOperationPathParamAndRequiredFalse() { String key = "{username}"; - PathItemValidator validator = PathItemValidator.getInstance(); + TypeValidator validator = getValidator(); TestValidationHelper vh = new TestValidationHelper(); PathItemImpl pathItem = new PathItemImpl(); @@ -739,7 +748,7 @@ public void testPathItemRefParameter() { model.setComponents(component); Context context = new TestValidationContextHelper(model); - PathItemValidator validator = PathItemValidator.getInstance(); + TypeValidator validator = getValidator(); TestValidationHelper vh = new TestValidationHelper(); PathItemImpl pathItem = new PathItemImpl(); @@ -775,7 +784,7 @@ public void testPathItemRefParameterInOperation() { model.setComponents(component); Context context = new TestValidationContextHelper(model); - PathItemValidator validator = PathItemValidator.getInstance(); + TypeValidator validator = getValidator(); TestValidationHelper vh = new TestValidationHelper(); PathItemImpl pathItem = new PathItemImpl(); diff --git a/dev/io.openliberty.microprofile.openapi.2.0.internal/test/io/openliberty/microprofile/openapi20/validation/test/PathsValidatorTest.java b/dev/io.openliberty.microprofile.openapi.2.0.internal_test/src/io/openliberty/microprofile/openapi20/validation/test/PathsValidatorTest.java similarity index 100% rename from dev/io.openliberty.microprofile.openapi.2.0.internal/test/io/openliberty/microprofile/openapi20/validation/test/PathsValidatorTest.java rename to dev/io.openliberty.microprofile.openapi.2.0.internal_test/src/io/openliberty/microprofile/openapi20/validation/test/PathsValidatorTest.java diff --git a/dev/io.openliberty.microprofile.openapi.2.0.internal/test/io/openliberty/microprofile/openapi20/validation/test/ReferenceValidatorTest.java b/dev/io.openliberty.microprofile.openapi.2.0.internal_test/src/io/openliberty/microprofile/openapi20/validation/test/ReferenceValidatorTest.java similarity index 100% rename from dev/io.openliberty.microprofile.openapi.2.0.internal/test/io/openliberty/microprofile/openapi20/validation/test/ReferenceValidatorTest.java rename to dev/io.openliberty.microprofile.openapi.2.0.internal_test/src/io/openliberty/microprofile/openapi20/validation/test/ReferenceValidatorTest.java diff --git a/dev/io.openliberty.microprofile.openapi.2.0.internal/test/io/openliberty/microprofile/openapi20/validation/test/RequestBodyValidatorTest.java b/dev/io.openliberty.microprofile.openapi.2.0.internal_test/src/io/openliberty/microprofile/openapi20/validation/test/RequestBodyValidatorTest.java similarity index 100% rename from dev/io.openliberty.microprofile.openapi.2.0.internal/test/io/openliberty/microprofile/openapi20/validation/test/RequestBodyValidatorTest.java rename to dev/io.openliberty.microprofile.openapi.2.0.internal_test/src/io/openliberty/microprofile/openapi20/validation/test/RequestBodyValidatorTest.java diff --git a/dev/io.openliberty.microprofile.openapi.2.0.internal/test/io/openliberty/microprofile/openapi20/validation/test/ResponseValidatorTest.java b/dev/io.openliberty.microprofile.openapi.2.0.internal_test/src/io/openliberty/microprofile/openapi20/validation/test/ResponseValidatorTest.java similarity index 100% rename from dev/io.openliberty.microprofile.openapi.2.0.internal/test/io/openliberty/microprofile/openapi20/validation/test/ResponseValidatorTest.java rename to dev/io.openliberty.microprofile.openapi.2.0.internal_test/src/io/openliberty/microprofile/openapi20/validation/test/ResponseValidatorTest.java diff --git a/dev/io.openliberty.microprofile.openapi.2.0.internal/test/io/openliberty/microprofile/openapi20/validation/test/ResponsesValidatorTest.java b/dev/io.openliberty.microprofile.openapi.2.0.internal_test/src/io/openliberty/microprofile/openapi20/validation/test/ResponsesValidatorTest.java similarity index 100% rename from dev/io.openliberty.microprofile.openapi.2.0.internal/test/io/openliberty/microprofile/openapi20/validation/test/ResponsesValidatorTest.java rename to dev/io.openliberty.microprofile.openapi.2.0.internal_test/src/io/openliberty/microprofile/openapi20/validation/test/ResponsesValidatorTest.java diff --git a/dev/io.openliberty.microprofile.openapi.2.0.internal/test/io/openliberty/microprofile/openapi20/validation/test/SchemaValidatorTest.java b/dev/io.openliberty.microprofile.openapi.2.0.internal_test/src/io/openliberty/microprofile/openapi20/validation/test/SchemaValidatorTest.java similarity index 93% rename from dev/io.openliberty.microprofile.openapi.2.0.internal/test/io/openliberty/microprofile/openapi20/validation/test/SchemaValidatorTest.java rename to dev/io.openliberty.microprofile.openapi.2.0.internal_test/src/io/openliberty/microprofile/openapi20/validation/test/SchemaValidatorTest.java index 2259294254b..d683bf56838 100644 --- a/dev/io.openliberty.microprofile.openapi.2.0.internal/test/io/openliberty/microprofile/openapi20/validation/test/SchemaValidatorTest.java +++ b/dev/io.openliberty.microprofile.openapi.2.0.internal_test/src/io/openliberty/microprofile/openapi20/validation/test/SchemaValidatorTest.java @@ -1,17 +1,15 @@ /******************************************************************************* - * Copyright (c) 2020 IBM Corporation and others. + * Copyright (c) 2020, 2024 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License 2.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-2.0/ - * - * SPDX-License-Identifier: EPL-2.0 * - * Contributors: - * IBM Corporation - initial API and implementation + * SPDX-License-Identifier: EPL-2.0 *******************************************************************************/ package io.openliberty.microprofile.openapi20.validation.test; +import java.lang.reflect.Field; import java.math.BigDecimal; import java.util.ArrayList; import java.util.Arrays; @@ -22,23 +20,32 @@ import org.eclipse.microprofile.openapi.models.media.Schema; import org.eclipse.microprofile.openapi.models.media.Schema.SchemaType; import org.junit.Assert; +import org.junit.BeforeClass; import org.junit.Test; +import io.openliberty.microprofile.openapi20.internal.services.OpenAPIModelOperations; +import io.openliberty.microprofile.openapi20.internal.utils.OpenAPIModelOperationsImpl; import io.openliberty.microprofile.openapi20.internal.utils.OpenAPIModelWalker.Context; import io.openliberty.microprofile.openapi20.internal.validation.SchemaValidator; +import io.openliberty.microprofile.openapi20.test.utils.TestServiceCaller; import io.openliberty.microprofile.openapi20.test.utils.TestValidationContextHelper; import io.openliberty.microprofile.openapi20.test.utils.TestValidationHelper; import io.smallrye.openapi.api.models.OpenAPIImpl; import io.smallrye.openapi.api.models.media.SchemaImpl; -/** - * - */ public class SchemaValidatorTest { OpenAPIImpl model = new OpenAPIImpl(); Context context = new TestValidationContextHelper(model); + @BeforeClass + public static void setup() throws Exception { + // Manually provide required OSGi services + Field f = SchemaValidator.class.getDeclaredField("MODEL_OPS"); + f.setAccessible(true); + f.set(null, new TestServiceCaller<>(OpenAPIModelOperations.class, new OpenAPIModelOperationsImpl())); + } + @Test public void testSchemaCorrect() { diff --git a/dev/io.openliberty.microprofile.openapi.2.0.internal/test/io/openliberty/microprofile/openapi20/validation/test/SecurityRequirementValidatorTest.java b/dev/io.openliberty.microprofile.openapi.2.0.internal_test/src/io/openliberty/microprofile/openapi20/validation/test/SecurityRequirementValidatorTest.java similarity index 100% rename from dev/io.openliberty.microprofile.openapi.2.0.internal/test/io/openliberty/microprofile/openapi20/validation/test/SecurityRequirementValidatorTest.java rename to dev/io.openliberty.microprofile.openapi.2.0.internal_test/src/io/openliberty/microprofile/openapi20/validation/test/SecurityRequirementValidatorTest.java diff --git a/dev/io.openliberty.microprofile.openapi.2.0.internal/test/io/openliberty/microprofile/openapi20/validation/test/SecuritySchemeValidatorTest.java b/dev/io.openliberty.microprofile.openapi.2.0.internal_test/src/io/openliberty/microprofile/openapi20/validation/test/SecuritySchemeValidatorTest.java similarity index 100% rename from dev/io.openliberty.microprofile.openapi.2.0.internal/test/io/openliberty/microprofile/openapi20/validation/test/SecuritySchemeValidatorTest.java rename to dev/io.openliberty.microprofile.openapi.2.0.internal_test/src/io/openliberty/microprofile/openapi20/validation/test/SecuritySchemeValidatorTest.java diff --git a/dev/io.openliberty.microprofile.openapi.2.0.internal/test/io/openliberty/microprofile/openapi20/validation/test/ServerValidatorTest.java b/dev/io.openliberty.microprofile.openapi.2.0.internal_test/src/io/openliberty/microprofile/openapi20/validation/test/ServerValidatorTest.java similarity index 100% rename from dev/io.openliberty.microprofile.openapi.2.0.internal/test/io/openliberty/microprofile/openapi20/validation/test/ServerValidatorTest.java rename to dev/io.openliberty.microprofile.openapi.2.0.internal_test/src/io/openliberty/microprofile/openapi20/validation/test/ServerValidatorTest.java diff --git a/dev/io.openliberty.microprofile.openapi.2.0.internal/test/io/openliberty/microprofile/openapi20/validation/test/ServerVariableValidatorTest.java b/dev/io.openliberty.microprofile.openapi.2.0.internal_test/src/io/openliberty/microprofile/openapi20/validation/test/ServerVariableValidatorTest.java similarity index 76% rename from dev/io.openliberty.microprofile.openapi.2.0.internal/test/io/openliberty/microprofile/openapi20/validation/test/ServerVariableValidatorTest.java rename to dev/io.openliberty.microprofile.openapi.2.0.internal_test/src/io/openliberty/microprofile/openapi20/validation/test/ServerVariableValidatorTest.java index 0676baa8cf4..215f4666f58 100644 --- a/dev/io.openliberty.microprofile.openapi.2.0.internal/test/io/openliberty/microprofile/openapi20/validation/test/ServerVariableValidatorTest.java +++ b/dev/io.openliberty.microprofile.openapi.2.0.internal_test/src/io/openliberty/microprofile/openapi20/validation/test/ServerVariableValidatorTest.java @@ -1,10 +1,10 @@ /******************************************************************************* - * Copyright (c) 2020 IBM Corporation and others. + * Copyright (c) 2020, 2024 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License 2.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-2.0/ - * + * * SPDX-License-Identifier: EPL-2.0 * * Contributors: @@ -12,11 +12,13 @@ *******************************************************************************/ package io.openliberty.microprofile.openapi20.validation.test; +import org.eclipse.microprofile.openapi.models.servers.ServerVariable; import org.junit.Assert; import org.junit.Test; import io.openliberty.microprofile.openapi20.internal.utils.OpenAPIModelWalker.Context; import io.openliberty.microprofile.openapi20.internal.validation.ServerVariableValidator; +import io.openliberty.microprofile.openapi20.internal.validation.TypeValidator; import io.openliberty.microprofile.openapi20.test.utils.TestValidationContextHelper; import io.openliberty.microprofile.openapi20.test.utils.TestValidationHelper; import io.smallrye.openapi.api.models.OpenAPIImpl; @@ -27,12 +29,16 @@ */ public class ServerVariableValidatorTest { - OpenAPIImpl model = new OpenAPIImpl(); - Context context = new TestValidationContextHelper(model); + protected OpenAPIImpl model = new OpenAPIImpl(); + protected Context context = new TestValidationContextHelper(model); + + protected TypeValidator getValidator() { + return ServerVariableValidator.getInstance(); + } @Test public void testServerVariableTest() { - ServerVariableValidator validator = ServerVariableValidator.getInstance(); + TypeValidator validator = getValidator(); TestValidationHelper validationHelper = new TestValidationHelper(); ServerVariableImpl serverVariable = new ServerVariableImpl(); @@ -46,7 +52,6 @@ public void testServerVariableTest() { validator.validate(validationHelper, context, serverVariable); Assert.assertEquals(0, validationHelper.getEventsSize()); - } } diff --git a/dev/io.openliberty.microprofile.openapi.2.0.internal/test/io/openliberty/microprofile/openapi20/validation/test/TagValidatorTest.java b/dev/io.openliberty.microprofile.openapi.2.0.internal_test/src/io/openliberty/microprofile/openapi20/validation/test/TagValidatorTest.java similarity index 100% rename from dev/io.openliberty.microprofile.openapi.2.0.internal/test/io/openliberty/microprofile/openapi20/validation/test/TagValidatorTest.java rename to dev/io.openliberty.microprofile.openapi.2.0.internal_test/src/io/openliberty/microprofile/openapi20/validation/test/TagValidatorTest.java diff --git a/dev/io.openliberty.microprofile.openapi.2.0.internal_test/src/io/openliberty/microprofile/openapi20/validation/test/package-info.java b/dev/io.openliberty.microprofile.openapi.2.0.internal_test/src/io/openliberty/microprofile/openapi20/validation/test/package-info.java new file mode 100644 index 00000000000..95164a49f18 --- /dev/null +++ b/dev/io.openliberty.microprofile.openapi.2.0.internal_test/src/io/openliberty/microprofile/openapi20/validation/test/package-info.java @@ -0,0 +1,11 @@ +/******************************************************************************* + * Copyright (c) 2024 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +@org.osgi.annotation.versioning.Version("0.1") +package io.openliberty.microprofile.openapi20.validation.test; \ No newline at end of file diff --git a/dev/io.openliberty.microprofile.openapi.2.0.internal_test/test/io/openliberty/microprofile/openapi20/validation/test/ValidationTests.java b/dev/io.openliberty.microprofile.openapi.2.0.internal_test/test/io/openliberty/microprofile/openapi20/validation/test/ValidationTests.java new file mode 100644 index 00000000000..3d4e306a293 --- /dev/null +++ b/dev/io.openliberty.microprofile.openapi.2.0.internal_test/test/io/openliberty/microprofile/openapi20/validation/test/ValidationTests.java @@ -0,0 +1,49 @@ +/******************************************************************************* + * Copyright (c) 2024 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package io.openliberty.microprofile.openapi20.validation.test; + +import org.junit.runner.RunWith; +import org.junit.runners.Suite; +import org.junit.runners.Suite.SuiteClasses; + +@RunWith(Suite.class) +@SuiteClasses({ + CallbacksValidatorTest.class, + ComponentsValidatorTest.class, + ContactValidatorTest.class, + DiscriminatorValidatorTest.class, + ExampleValidatorTest.class, + ExtensionValidatorTest.class, + ExternalDocumentationValidatorTest.class, + HeaderValidatorTest.class, + InfoValidatorTest.class, + LicenseValidatorTest.class, + LinkValidatorTest.class, + MediaTypeValidatorTest.class, + OAuthFlowsValidatorTest.class, + OAuthFlowValidatorTest.class, + OpenAPIRuntimeExpressionTest.class, + OpenAPIValidatorTest.class, + OperationValidatorTest.class, + ParameterValidatorTest.class, + PathItemValidatorTest.class, + PathsValidatorTest.class, + ReferenceValidatorTest.class, + RequestBodyValidatorTest.class, + ResponsesValidatorTest.class, + ResponseValidatorTest.class, + SchemaValidatorTest.class, + SecurityRequirementValidatorTest.class, + SecuritySchemeValidatorTest.class, + ServerValidatorTest.class, + ServerVariableValidatorTest.class, + TagValidatorTest.class +}) +public class ValidationTests {} diff --git a/dev/io.openliberty.microprofile.openapi.3.1.internal.services/src/io/openliberty/microprofile/openapi31/internal/services/impl/OpenAPI30Validator.java b/dev/io.openliberty.microprofile.openapi.3.1.internal.services/src/io/openliberty/microprofile/openapi31/internal/services/impl/OpenAPI30Validator.java deleted file mode 100644 index 7c426a9085f..00000000000 --- a/dev/io.openliberty.microprofile.openapi.3.1.internal.services/src/io/openliberty/microprofile/openapi31/internal/services/impl/OpenAPI30Validator.java +++ /dev/null @@ -1,21 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2024 IBM Corporation and others. - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License 2.0 - * which accompanies this distribution, and is available at - * http://www.eclipse.org/legal/epl-2.0/ - * - * SPDX-License-Identifier: EPL-2.0 - *******************************************************************************/ -package io.openliberty.microprofile.openapi31.internal.services.impl; - -import org.osgi.service.component.annotations.Component; -import org.osgi.service.component.annotations.ConfigurationPolicy; - -import io.openliberty.microprofile.openapi20.internal.services.OASValidator; -import io.openliberty.microprofile.openapi20.internal.validation.OASValidatorImpl; - -@Component(service = OASValidator.class, configurationPolicy = ConfigurationPolicy.IGNORE) -public class OpenAPI30Validator extends OASValidatorImpl { - -} diff --git a/dev/io.openliberty.microprofile.openapi.4.0.internal.services/bnd.bnd b/dev/io.openliberty.microprofile.openapi.4.0.internal.services/bnd.bnd index 1155263c7e0..17e669354b5 100644 --- a/dev/io.openliberty.microprofile.openapi.4.0.internal.services/bnd.bnd +++ b/dev/io.openliberty.microprofile.openapi.4.0.internal.services/bnd.bnd @@ -25,9 +25,11 @@ javac.target: 11 Require-Capability: osgi.ee; filter:="(&(osgi.ee=JavaSE)(version=11))" Import-Package: \ + io.openliberty.microprofile.openapi.internal.resources.validation,\ * -Private-Package: io.openliberty.microprofile.openapi40.internal.services.impl +Private-Package: io.openliberty.microprofile.openapi40.internal.services.impl,\ + io.openliberty.microprofile.openapi40.internal.services.validation -dsannotations-options: inherit -dsannotations: io.openliberty.microprofile.openapi40.internal.services.impl.* @@ -58,6 +60,5 @@ WS-TraceGroup: MPOPENAPI io.openliberty.microprofile.openapi.2.0.internal_test;version=latest,\ io.openliberty.microprofile.openapi.internal.common;version=latest,\ io.openliberty.com.fasterxml.jackson;version=latest,\ - com.ibm.ws.kernel.service;version=latest - - \ No newline at end of file + com.ibm.ws.kernel.service;version=latest,\ + com.ibm.ws.org.apache.commons.lang3;version=latest diff --git a/dev/io.openliberty.microprofile.openapi.4.0.internal.services/src/io/openliberty/microprofile/openapi40/internal/services/impl/OpenAPI31ModelOperations.java b/dev/io.openliberty.microprofile.openapi.4.0.internal.services/src/io/openliberty/microprofile/openapi40/internal/services/impl/OpenAPI31ModelOperations.java index cf4ab93e429..c38f3f0f78b 100644 --- a/dev/io.openliberty.microprofile.openapi.4.0.internal.services/src/io/openliberty/microprofile/openapi40/internal/services/impl/OpenAPI31ModelOperations.java +++ b/dev/io.openliberty.microprofile.openapi.4.0.internal.services/src/io/openliberty/microprofile/openapi40/internal/services/impl/OpenAPI31ModelOperations.java @@ -9,8 +9,12 @@ *******************************************************************************/ package io.openliberty.microprofile.openapi40.internal.services.impl; +import java.util.List; + import org.eclipse.microprofile.openapi.models.OpenAPI; import org.eclipse.microprofile.openapi.models.info.Info; +import org.eclipse.microprofile.openapi.models.media.Schema; +import org.eclipse.microprofile.openapi.models.media.Schema.SchemaType; import org.osgi.service.component.annotations.Component; import org.osgi.service.component.annotations.ConfigurationPolicy; @@ -92,4 +96,8 @@ public OpenAPI createDefaultOpenApiModel() { return openAPI; } + @Override + public List getTypes(Schema schema) { + return schema.getType(); + } } diff --git a/dev/io.openliberty.microprofile.openapi.4.0.internal.services/src/io/openliberty/microprofile/openapi40/internal/services/impl/OpenAPI31ModelWalkerImpl.java b/dev/io.openliberty.microprofile.openapi.4.0.internal.services/src/io/openliberty/microprofile/openapi40/internal/services/impl/OpenAPI31ModelWalkerImpl.java index 1047f591d71..da7a61e1c10 100644 --- a/dev/io.openliberty.microprofile.openapi.4.0.internal.services/src/io/openliberty/microprofile/openapi40/internal/services/impl/OpenAPI31ModelWalkerImpl.java +++ b/dev/io.openliberty.microprofile.openapi.4.0.internal.services/src/io/openliberty/microprofile/openapi40/internal/services/impl/OpenAPI31ModelWalkerImpl.java @@ -1,3 +1,12 @@ +/******************************************************************************* + * Copyright (c) 2024 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ package io.openliberty.microprofile.openapi40.internal.services.impl; import java.util.List; @@ -122,7 +131,7 @@ private void traverseList(List list) { int i = 0; for (Object element : list) { String key = Integer.toString(i); - pathSegments.add(key); + pathSegments.push(key); traverseObject(element, key); pathSegments.pop(); i++; diff --git a/dev/io.openliberty.microprofile.openapi.4.0.internal.services/src/io/openliberty/microprofile/openapi40/internal/services/impl/OpenAPI31Validator.java b/dev/io.openliberty.microprofile.openapi.4.0.internal.services/src/io/openliberty/microprofile/openapi40/internal/services/impl/OpenAPI31Validator.java index ad8bacf3fdc..abcd425e5ff 100644 --- a/dev/io.openliberty.microprofile.openapi.4.0.internal.services/src/io/openliberty/microprofile/openapi40/internal/services/impl/OpenAPI31Validator.java +++ b/dev/io.openliberty.microprofile.openapi.4.0.internal.services/src/io/openliberty/microprofile/openapi40/internal/services/impl/OpenAPI31Validator.java @@ -4,25 +4,109 @@ * are made available under the terms of the Eclipse Public License 2.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-2.0/ - * + * * SPDX-License-Identifier: EPL-2.0 *******************************************************************************/ package io.openliberty.microprofile.openapi40.internal.services.impl; import org.eclipse.microprofile.openapi.models.OpenAPI; +import org.eclipse.microprofile.openapi.models.Operation; +import org.eclipse.microprofile.openapi.models.PathItem; +import org.eclipse.microprofile.openapi.models.media.Schema; +import org.eclipse.microprofile.openapi.models.servers.ServerVariable; import org.osgi.service.component.annotations.Component; import org.osgi.service.component.annotations.ConfigurationPolicy; +import com.ibm.websphere.ras.Tr; +import com.ibm.websphere.ras.TraceComponent; + import io.openliberty.microprofile.openapi20.internal.services.OASValidationResult; +import io.openliberty.microprofile.openapi20.internal.services.OASValidationResult.ValidationEvent; +import io.openliberty.microprofile.openapi20.internal.services.OASValidationResult.ValidationEvent.Severity; import io.openliberty.microprofile.openapi20.internal.services.OASValidator; +import io.openliberty.microprofile.openapi20.internal.utils.OpenAPIModelWalker.Context; +import io.openliberty.microprofile.openapi20.internal.utils.ValidationMessageConstants; +import io.openliberty.microprofile.openapi20.internal.validation.OASValidator30Impl; +import io.openliberty.microprofile.openapi20.internal.validation.ValidatorUtils; +import io.openliberty.microprofile.openapi40.internal.services.validation.OpenAPIDefinition31Validator; +import io.openliberty.microprofile.openapi40.internal.services.validation.Operation31Validator; +import io.openliberty.microprofile.openapi40.internal.services.validation.PathItem31Validator; +import io.openliberty.microprofile.openapi40.internal.services.validation.Reference31Validator; +import io.openliberty.microprofile.openapi40.internal.services.validation.Schema31Validator; +import io.openliberty.microprofile.openapi40.internal.services.validation.ServerVariable31Validator; + +@Component(service = OASValidator.class, configurationPolicy = ConfigurationPolicy.IGNORE, property = "openapi.version=3.1") +public class OpenAPI31Validator extends OASValidator30Impl { -@Component(service = OASValidator.class, configurationPolicy = ConfigurationPolicy.IGNORE) -public class OpenAPI31Validator implements OASValidator { + private static final TraceComponent tc = Tr.register(OpenAPI31Validator.class); @Override public OASValidationResult validate(OpenAPI model) { - // TODO: implement validation - return new OASValidationResult(); + ValidationOperation31 validator = new ValidationOperation31(model); + return validator.run(); } + public class ValidationOperation31 extends ValidationOperation { + + public ValidationOperation31(OpenAPI model) { + super(model); + } + + @Override + public void visitOpenAPI(Context context) { + OpenAPIDefinition31Validator.getInstance().validate(this, context, context.getModel()); + } + + @Override + public Operation visitOperation(Context context, Operation operation) { + Operation31Validator.getInstance().validate(this, context, operation); + return operation; + } + + @Override + public ServerVariable visitServerVariable(Context context, String key, ServerVariable sv) { + ServerVariable31Validator.getInstance().validate(this, context, key, sv); + return sv; + } + + @Override + public PathItem visitPathItem(Context context, String key, PathItem item) { + PathItem31Validator.getInstance().validate(this, context, key, item); + return item; + } + + @Override + public T validateReference(Context context, String key, String ref, Class clazz) { + Object component = validateReference(context, key, ref); + if (component == null) { + // We didn't get an object back + // This could be because the reference was invalid, or because it was external, or because we didn't know how to validate it + // Any relevant validation events will have been raised, there's nothing more for us to do + return null; + } else if (clazz.isInstance(component)) { + return clazz.cast(component); + } else { + final String message = ValidatorUtils.formatMessage(ValidationMessageConstants.REFERENCE_TO_OBJECT_INVALID, ref); + addValidationEvent(new ValidationEvent(Severity.ERROR, context.getLocation(), message)); + return null; + } + } + + @Override + public Schema visitSchema(Context context, Schema schema) { + Schema31Validator.getInstance().validate(this, context, schema); + return schema; + } + + @Override + public Schema visitSchema(Context context, String key, Schema schema) { + Schema31Validator.getInstance().validate(this, context, key, schema); + return schema; + } + + @Override + public Object validateReference(Context context, String key, String ref) { + return Reference31Validator.getInstance().validate(this, context, ref); + } + } } diff --git a/dev/io.openliberty.microprofile.openapi.4.0.internal.services/src/io/openliberty/microprofile/openapi40/internal/services/validation/OpenAPIDefinition31Validator.java b/dev/io.openliberty.microprofile.openapi.4.0.internal.services/src/io/openliberty/microprofile/openapi40/internal/services/validation/OpenAPIDefinition31Validator.java new file mode 100644 index 00000000000..22e7247e61c --- /dev/null +++ b/dev/io.openliberty.microprofile.openapi.4.0.internal.services/src/io/openliberty/microprofile/openapi40/internal/services/validation/OpenAPIDefinition31Validator.java @@ -0,0 +1,58 @@ +/******************************************************************************* + * Copyright (c) 2024 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package io.openliberty.microprofile.openapi40.internal.services.validation; + +import org.eclipse.microprofile.openapi.models.OpenAPI; + +import com.ibm.websphere.ras.Tr; +import com.ibm.websphere.ras.TraceComponent; + +import io.openliberty.microprofile.openapi20.internal.services.OASValidationResult.ValidationEvent; +import io.openliberty.microprofile.openapi20.internal.utils.OpenAPIModelWalker.Context; +import io.openliberty.microprofile.openapi20.internal.utils.ValidationMessageConstants; +import io.openliberty.microprofile.openapi20.internal.validation.OpenAPIValidator; +import io.openliberty.microprofile.openapi20.internal.validation.TypeValidator; +import io.openliberty.microprofile.openapi20.internal.validation.ValidationHelper; +import io.openliberty.microprofile.openapi20.internal.validation.ValidatorUtils; +import io.smallrye.openapi.runtime.io.OpenAPIDefinitionIO; + +public class OpenAPIDefinition31Validator extends TypeValidator { + private static final TraceComponent tc = Tr.register(OpenAPIDefinition31Validator.class); + + private static final OpenAPIDefinition31Validator INSTANCE = new OpenAPIDefinition31Validator(); + + public static OpenAPIDefinition31Validator getInstance() { + return INSTANCE; + } + + private OpenAPIDefinition31Validator() {} + + @Override + public void validate(ValidationHelper helper, Context context, String key, OpenAPI t) { + if (t != null) { + String openapiVersion = t.getOpenapi(); + ValidatorUtils.validateRequiredField(openapiVersion, context, OpenAPIDefinitionIO.PROP_OPENAPI).ifPresent(helper::addValidationEvent); + ValidatorUtils.validateRequiredField(t.getInfo(), context, OpenAPIDefinitionIO.PROP_INFO).ifPresent(helper::addValidationEvent); + + if (openapiVersion != null && !openapiVersion.startsWith("3.")) { + final String message = Tr.formatMessage(tc, ValidationMessageConstants.OPENAPI_VERSION_INVALID, openapiVersion); + helper.addValidationEvent(new ValidationEvent(ValidationEvent.Severity.ERROR, context.getLocation(), message)); + } + + if (t.getPaths() == null && t.getComponents() == null && t.getWebhooks() == null) { + final String message = Tr.formatMessage(tc, ValidationMessageConstants.OPENAPI_MISSING_REQUIRED_FIELDS); + helper.addValidationEvent(new ValidationEvent(ValidationEvent.Severity.ERROR, context.getLocation(), message)); + } + + OpenAPIValidator.getInstance().validateTags(helper, context, t); + } + } + +} diff --git a/dev/io.openliberty.microprofile.openapi.4.0.internal.services/src/io/openliberty/microprofile/openapi40/internal/services/validation/Operation31Validator.java b/dev/io.openliberty.microprofile.openapi.4.0.internal.services/src/io/openliberty/microprofile/openapi40/internal/services/validation/Operation31Validator.java new file mode 100644 index 00000000000..f26ba483af1 --- /dev/null +++ b/dev/io.openliberty.microprofile.openapi.4.0.internal.services/src/io/openliberty/microprofile/openapi40/internal/services/validation/Operation31Validator.java @@ -0,0 +1,36 @@ +/******************************************************************************* + * Copyright (c) 2024 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package io.openliberty.microprofile.openapi40.internal.services.validation; + +import org.eclipse.microprofile.openapi.models.Operation; + +import io.openliberty.microprofile.openapi20.internal.utils.OpenAPIModelWalker.Context; +import io.openliberty.microprofile.openapi20.internal.validation.OperationValidator; +import io.openliberty.microprofile.openapi20.internal.validation.TypeValidator; +import io.openliberty.microprofile.openapi20.internal.validation.ValidationHelper; + +public class Operation31Validator extends TypeValidator { + + private static final Operation31Validator INSTANCE = new Operation31Validator(); + + public static Operation31Validator getInstance() { + return INSTANCE; + } + + private Operation31Validator() {} + + @Override + public void validate(ValidationHelper helper, Context context, String key, Operation t) { + if (t != null) { + OperationValidator.getInstance().validateId(helper, context, t); + } + } + +} diff --git a/dev/io.openliberty.microprofile.openapi.4.0.internal.services/src/io/openliberty/microprofile/openapi40/internal/services/validation/PathItem31Validator.java b/dev/io.openliberty.microprofile.openapi.4.0.internal.services/src/io/openliberty/microprofile/openapi40/internal/services/validation/PathItem31Validator.java new file mode 100644 index 00000000000..f129a8ef8c6 --- /dev/null +++ b/dev/io.openliberty.microprofile.openapi.4.0.internal.services/src/io/openliberty/microprofile/openapi40/internal/services/validation/PathItem31Validator.java @@ -0,0 +1,56 @@ +/******************************************************************************* + * Copyright (c) 2024 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package io.openliberty.microprofile.openapi40.internal.services.validation; + +import org.eclipse.microprofile.openapi.models.PathItem; + +import com.ibm.websphere.ras.Tr; +import com.ibm.websphere.ras.TraceComponent; + +import io.openliberty.microprofile.openapi20.internal.utils.LoggingUtils; +import io.openliberty.microprofile.openapi20.internal.utils.OpenAPIModelWalker.Context; +import io.openliberty.microprofile.openapi20.internal.validation.PathItemValidator; +import io.openliberty.microprofile.openapi20.internal.validation.TypeValidator; +import io.openliberty.microprofile.openapi20.internal.validation.ValidationHelper; + +public class PathItem31Validator extends TypeValidator { + + private static final TraceComponent tc = Tr.register(PathItem31Validator.class); + + private static final PathItem31Validator INSTANCE = new PathItem31Validator(); + + public static PathItem31Validator getInstance() { + return INSTANCE; + } + + private PathItem31Validator() {} + + @Override + public void validate(ValidationHelper helper, Context context, String key, PathItem t) { + if (t != null) { + String ref = t.getRef(); + if (ref != null && !ref.isEmpty()) { + helper.validateReference(context, key, ref, PathItem.class); + return; + } + + if (key.contains("{$")) { + //Path within a Callback can contain variables (e.g. {$request.query.callbackUrl}/data ) which shouldn't be validated since they are not path params + if (LoggingUtils.isDebugEnabled(tc)) { + Tr.debug(tc, "Path contains variables. Skip validation: " + key); + } + return; + } + + PathItemValidator.getInstance().validateParameters(helper, context, key, t); + } + } + +} diff --git a/dev/io.openliberty.microprofile.openapi.4.0.internal.services/src/io/openliberty/microprofile/openapi40/internal/services/validation/Reference31Validator.java b/dev/io.openliberty.microprofile.openapi.4.0.internal.services/src/io/openliberty/microprofile/openapi40/internal/services/validation/Reference31Validator.java new file mode 100644 index 00000000000..7e651a86cef --- /dev/null +++ b/dev/io.openliberty.microprofile.openapi.4.0.internal.services/src/io/openliberty/microprofile/openapi40/internal/services/validation/Reference31Validator.java @@ -0,0 +1,161 @@ +/******************************************************************************* + * Copyright (c) 2024 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package io.openliberty.microprofile.openapi40.internal.services.validation; + +import java.net.URI; +import java.net.URISyntaxException; +import java.util.Map; +import java.util.Optional; +import java.util.function.Function; + +import org.eclipse.microprofile.openapi.models.Components; + +import com.ibm.websphere.ras.Tr; +import com.ibm.websphere.ras.TraceComponent; +import com.ibm.ws.ffdc.annotation.FFDCIgnore; + +import io.openliberty.microprofile.openapi20.internal.services.OASValidationResult.ValidationEvent; +import io.openliberty.microprofile.openapi20.internal.services.OASValidationResult.ValidationEvent.Severity; +import io.openliberty.microprofile.openapi20.internal.utils.OpenAPIModelWalker.Context; +import io.openliberty.microprofile.openapi20.internal.utils.ValidationMessageConstants; +import io.openliberty.microprofile.openapi20.internal.validation.ReferenceValidator; +import io.openliberty.microprofile.openapi20.internal.validation.ValidationHelper; + +/** + * Validates references for OpenAPI 3.1 documents + *

+ * Main differences from {@link ReferenceValidator} are + *

    + *
  • Handles {@code PathItem} references + *
  • Only validates references to {@code #/components//}. Other references may be valid, but we can't validate them. + *
  • Validates that the reference is a valid URI + *
+ */ +public class Reference31Validator { + + private static final TraceComponent tc = Tr.register(Reference31Validator.class); + + private static final Reference31Validator INSTANCE = new Reference31Validator(); + + public static Reference31Validator getInstance() { + return INSTANCE; + } + + public static final Map>> COMPONENT_GETTERS; + static { + COMPONENT_GETTERS = Map.of("callbacks", t -> t.getCallbacks(), + "links", t -> t.getLinks(), + "securitySchemes", t -> t.getSecuritySchemes(), + "headers", t -> t.getHeaders(), + "requestBodies", t -> t.getRequestBodies(), + "examples", t -> t.getExamples(), + "parameters", t -> t.getParameters(), + "responses", t -> t.getResponses(), + "schemas", t -> t.getSchemas(), + "pathItems", t -> t.getPathItems()); + } + + private Reference31Validator() {} + + @FFDCIgnore(URISyntaxException.class) + public Object validate(ValidationHelper helper, Context context, String ref) { + // Check for a null or empty reference + if (ref == null || ref.trim().isEmpty()) { + final String message = Tr.formatMessage(tc, ValidationMessageConstants.REFERENCE_NULL); + helper.addValidationEvent(new ValidationEvent(ValidationEvent.Severity.ERROR, context.getLocation(), message)); + return null; + } + + try { + new URI(ref); // Parse as a URI + } catch (URISyntaxException e) { + final String message = Tr.formatMessage(tc, ValidationMessageConstants.REFERENCE_NOT_VALID_URI, ref); + helper.addValidationEvent(new ValidationEvent(Severity.ERROR, context.getLocation(), message)); + return null; + } + + String[] references = ref.split("/"); + + // Only validate references of the format #/components// + if (!ref.startsWith("#/components/")) { + if (TraceComponent.isAnyTracingEnabled() && tc.isDebugEnabled()) { + Tr.debug(this, tc, "Reference does not target components, not validating", ref); + } + return null; + } + + // If we don't have a components object, it can't be valid + Components components = context.getModel().getComponents(); + if (components == null) { + final String message = Tr.formatMessage(tc, ValidationMessageConstants.REFERENCE_NOT_PART_OF_MODEL, ref); + helper.addValidationEvent(new ValidationEvent(ValidationEvent.Severity.ERROR, context.getLocation(), message)); + return null; + } + + // Extract from #/components// + String type = references[2]; + + // If type is not one of the known names, we don't validate it + // It could be under an extension + if (!COMPONENT_GETTERS.containsKey(type)) { + if (type.startsWith("x-")) { + // Reference to something in an extension, don't validate + return null; + } else { + // Bad type name, can't exist + final String message = Tr.formatMessage(tc, ValidationMessageConstants.REFERENCE_NOT_PART_OF_MODEL, ref); + helper.addValidationEvent(new ValidationEvent(ValidationEvent.Severity.ERROR, context.getLocation(), message)); + return null; + } + } + + Optional> component = Optional.ofNullable(COMPONENT_GETTERS.get(type)) // Find the getter for the type + .map(f -> f.apply(components)); // Call the getter to get the map for the type + + if (references.length == 3) { + // Direct reference to something under components. Return so we can validate that it's of the wrong type. + if (component.isPresent()) { + return component.get(); + } else { + final String message = Tr.formatMessage(tc, ValidationMessageConstants.REFERENCE_NOT_PART_OF_MODEL, ref); + helper.addValidationEvent(new ValidationEvent(ValidationEvent.Severity.ERROR, context.getLocation(), message)); + return null; + } + } + + // Extract from #/components// + String name = decodeJsonPointerToken(references[3]); + + // Look up the referenced object + Optional object = component.map(m -> m.get(name)); // Look for the entry within the map + + // Check if the referenced object is present + if (!object.isPresent()) { + final String message = Tr.formatMessage(tc, ValidationMessageConstants.REFERENCE_NOT_PART_OF_MODEL, ref); + helper.addValidationEvent(new ValidationEvent(ValidationEvent.Severity.ERROR, context.getLocation(), message)); + return null; + } + + // We found the referenced object, return it + return object.get(); + } + + /** + * Decode single part of a reference, according to https://www.rfc-editor.org/rfc/rfc6901#section-4 + * + * @param token the encoded token + * @return the decoded token + */ + private String decodeJsonPointerToken(String token) { + token = token.replaceAll("~1", "/"); + token = token.replaceAll("~0", "~"); + return token; + } +} diff --git a/dev/io.openliberty.microprofile.openapi.4.0.internal.services/src/io/openliberty/microprofile/openapi40/internal/services/validation/Schema31Validator.java b/dev/io.openliberty.microprofile.openapi.4.0.internal.services/src/io/openliberty/microprofile/openapi40/internal/services/validation/Schema31Validator.java new file mode 100644 index 00000000000..4d372fedc1e --- /dev/null +++ b/dev/io.openliberty.microprofile.openapi.4.0.internal.services/src/io/openliberty/microprofile/openapi40/internal/services/validation/Schema31Validator.java @@ -0,0 +1,490 @@ +/******************************************************************************* + * Copyright (c) 2024 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package io.openliberty.microprofile.openapi40.internal.services.validation; + +import java.math.BigDecimal; +import java.math.BigInteger; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.eclipse.microprofile.openapi.models.Constructible; +import org.eclipse.microprofile.openapi.models.media.Schema; +import org.eclipse.microprofile.openapi.models.media.Schema.SchemaType; + +import com.ibm.websphere.ras.Tr; +import com.ibm.websphere.ras.TraceComponent; + +import io.openliberty.microprofile.openapi20.internal.services.OASValidationResult.ValidationEvent; +import io.openliberty.microprofile.openapi20.internal.services.OASValidationResult.ValidationEvent.Severity; +import io.openliberty.microprofile.openapi20.internal.utils.OpenAPIModelWalker.Context; +import io.openliberty.microprofile.openapi20.internal.utils.ValidationMessageConstants; +import io.openliberty.microprofile.openapi20.internal.validation.TypeValidator; +import io.openliberty.microprofile.openapi20.internal.validation.ValidationHelper; +import io.smallrye.openapi.runtime.io.schema.SchemaConstant; + +public class Schema31Validator extends TypeValidator { + private static final TraceComponent tc = Tr.register(Schema31Validator.class); + + private static final Set VALID_TYPES = Set.of("null", "boolean", "object", "array", "number", "integer", "string"); + + private static final Schema31Validator INSTANCE = new Schema31Validator(); + + public static Schema31Validator getInstance() { + return INSTANCE; + } + + private Schema31Validator() {} + + @Override + public void validate(ValidationHelper helper, Context context, String key, Schema schema) { + + if (schema == null) { + return; + } + + if (!isSupportedDialect(schema, context)) { + if (TraceComponent.isAnyTracingEnabled() && tc.isDebugEnabled()) { + Tr.debug(this, tc, "Schema uses an unsupported dialect, not validating.", schema.getSchemaDialect()); + } + return; + } + + if (schema.getBooleanSchema() != null) { + // No validation required for a boolean schema + return; + } + + String ref = schema.getRef(); + if (ref != null) { + helper.validateReference(context, key, schema.getRef(), Schema.class); + } + + // JSON Schema Core + validatePropType(helper, context, schema, SchemaConstant.PROP_COMMENT, PropertyType.STRING); + validateArrayOfSchema(helper, context, schema, SchemaConstant.PROP_ALL_OF); + validateArrayOfSchema(helper, context, schema, SchemaConstant.PROP_ANY_OF); + validateArrayOfSchema(helper, context, schema, SchemaConstant.PROP_ONE_OF); + validateIsSchema(helper, context, schema, SchemaConstant.PROP_NOT); + validateIsSchema(helper, context, schema, SchemaConstant.PROP_IF); + validateIsSchema(helper, context, schema, SchemaConstant.PROP_THEN); + validateIsSchema(helper, context, schema, SchemaConstant.PROP_ELSE); + validateIsMapOfNamedSchemas(helper, context, schema, SchemaConstant.PROP_DEPENDENT_SCHEMAS); + validateArrayOfSchema(helper, context, schema, SchemaConstant.PROP_PREFIX_ITEMS); + validateIsSchema(helper, context, schema, SchemaConstant.PROP_ITEMS); + validateIsSchema(helper, context, schema, SchemaConstant.PROP_CONTAINS); + validateIsMapOfNamedSchemas(helper, context, schema, SchemaConstant.PROP_PROPERTIES); + validateIsMapOfNamedSchemas(helper, context, schema, SchemaConstant.PROP_PATTERN_PROPERTIES); + validateIsSchema(helper, context, schema, SchemaConstant.PROP_ADDITIONAL_PROPERTIES); + validateIsSchema(helper, context, schema, SchemaConstant.PROP_PROPERTY_NAMES); + validateIsSchema(helper, context, schema, SchemaConstant.PROP_UNEVALUATED_ITEMS); + validateIsSchema(helper, context, schema, SchemaConstant.PROP_UNEVALUATED_PROPERTIES); + + // JSON Schema Validation + validateType(helper, context, schema); // String or array, values unique, values from set + validateEnum(helper, context, schema); // Array, should be non-empty, elements should be unique + validateMultipleOf(helper, context, schema); + validatePropType(helper, context, schema, SchemaConstant.PROP_MAXIMUM, PropertyType.NUMBER); + validatePropType(helper, context, schema, SchemaConstant.PROP_EXCLUSIVE_MAXIMUM, PropertyType.NUMBER); + validatePropType(helper, context, schema, SchemaConstant.PROP_MINIMUM, PropertyType.NUMBER); + validatePropType(helper, context, schema, SchemaConstant.PROP_EXCLUSIVE_MINIMUM, PropertyType.NUMBER); + validateIntZeroOrGreater(helper, context, schema, SchemaConstant.PROP_MAX_LENGTH); + validateIntZeroOrGreater(helper, context, schema, SchemaConstant.PROP_MIN_LENGTH); + validatePropType(helper, context, schema, SchemaConstant.PROP_PATTERN, PropertyType.STRING); // Should be regex + validateIntZeroOrGreater(helper, context, schema, SchemaConstant.PROP_MAX_ITEMS); + validateIntZeroOrGreater(helper, context, schema, SchemaConstant.PROP_MIN_ITEMS); + validatePropType(helper, context, schema, SchemaConstant.PROP_UNIQUE_ITEMS, PropertyType.BOOLEAN); + validateIntZeroOrGreater(helper, context, schema, SchemaConstant.PROP_MAX_CONTAINS); + validateIntZeroOrGreater(helper, context, schema, SchemaConstant.PROP_MIN_CONTAINS); + validateIntZeroOrGreater(helper, context, schema, SchemaConstant.PROP_MAX_PROPERTIES); + validateIntZeroOrGreater(helper, context, schema, SchemaConstant.PROP_MIN_PROPERTIES); + validateRequired(helper, context, schema); // Array of unique strings + validateDependentRequred(helper, context, schema); // Map of Arrays of unique strings + validatePropType(helper, context, schema, SchemaConstant.PROP_FORMAT, PropertyType.STRING); + validatePropType(helper, context, schema, SchemaConstant.PROP_CONTENT_ENCODING, PropertyType.STRING); + validatePropType(helper, context, schema, SchemaConstant.PROP_CONTENT_MEDIA_TYPE, PropertyType.STRING); // must be a media type + validateIsSchema(helper, context, schema, SchemaConstant.PROP_CONTENT_SCHEMA); // Ignored if contentMediaType not present + validatePropType(helper, context, schema, SchemaConstant.PROP_TITLE, PropertyType.STRING); + validatePropType(helper, context, schema, SchemaConstant.PROP_DESCRIPTION, PropertyType.STRING); + validatePropType(helper, context, schema, SchemaConstant.PROP_DEPRECATED, PropertyType.BOOLEAN); + validatePropType(helper, context, schema, SchemaConstant.PROP_READ_ONLY, PropertyType.BOOLEAN); + validatePropType(helper, context, schema, SchemaConstant.PROP_WRITE_ONLY, PropertyType.BOOLEAN); // Warning if both specified? + validatePropType(helper, context, schema, SchemaConstant.PROP_EXAMPLES, PropertyType.ARRAY); + } + + private void validateType(ValidationHelper helper, Context context, Schema schema) { + Object o = schema.get(SchemaConstant.PROP_TYPE); + if (o == null) { + // no value to validate + return; + } + + boolean isValid; + if (o instanceof List) { + isValid = true; + for (Object e : (List) o) { + if (!isValidTypeElement(e)) { + isValid = false; + break; + } + } + } else { + isValid = isValidTypeElement(o); + } + + if (!isValid) { + String message = Tr.formatMessage(tc, ValidationMessageConstants.SCHEMA_INVALID_TYPE); + helper.addValidationEvent(new ValidationEvent(Severity.ERROR, context.getLocation(), message)); + return; + } + + if (o instanceof List) { + Set set = new HashSet<>(); + for (Object e : (List) o) { + if (!set.add(e)) { + String message = Tr.formatMessage(tc, ValidationMessageConstants.SCHEMA_PROPERTY_NOT_UNIQUE, SchemaConstant.PROP_TYPE); + helper.addValidationEvent(new ValidationEvent(Severity.ERROR, context.getLocation(), message)); + return; + } + } + } + } + + private boolean isValidTypeElement(Object o) { + if (o instanceof SchemaType) { + return true; + } else if (o instanceof String) { + return VALID_TYPES.contains(o); + } else { + return false; + } + } + + private void validateEnum(ValidationHelper helper, Context context, Schema schema) { + Object o = schema.get(SchemaConstant.PROP_ENUM); + + if (o == null) { + // No value to validate + return; + } + + if (!(o instanceof List)) { + String message = Tr.formatMessage(tc, ValidationMessageConstants.SCHEMA_PROPERTY_WRONG_TYPE, SchemaConstant.PROP_ENUM, "array"); + helper.addValidationEvent(new ValidationEvent(Severity.ERROR, context.getLocation(), message)); + return; + } + + List list = (List) o; + if (list.size() == 0) { + String message = Tr.formatMessage(tc, ValidationMessageConstants.SCHEMA_PROPERTY_EMPTY_ARRAY, SchemaConstant.PROP_ENUM); + helper.addValidationEvent(new ValidationEvent(Severity.WARNING, context.getLocation(), message)); + return; + } + + HashSet set = new HashSet<>(); + for (Object e : list) { + if (!set.add(e)) { + String message = Tr.formatMessage(tc, ValidationMessageConstants.SCHEMA_PROPERTY_NOT_UNIQUE, SchemaConstant.PROP_ENUM); + helper.addValidationEvent(new ValidationEvent(Severity.WARNING, context.getLocation(), message)); + return; + } + } + } + + private void validateMultipleOf(ValidationHelper helper, Context context, Schema schema) { + Object o = schema.get(SchemaConstant.PROP_MULTIPLE_OF); + if (o == null) { + // no value to validate + return; + } + + if (!(o instanceof Number)) { + String message = Tr.formatMessage(tc, ValidationMessageConstants.SCHEMA_PROPERTY_WRONG_TYPE, SchemaConstant.PROP_MULTIPLE_OF, "number"); + helper.addValidationEvent(new ValidationEvent(Severity.ERROR, context.getLocation(), message)); + return; + } + + boolean isGreaterThanZero; + if (o instanceof BigDecimal) { // Expected case + BigDecimal decimal = (BigDecimal) o; + isGreaterThanZero = decimal.compareTo(BigDecimal.ZERO) > 0; // decimal > 0 + } else if (o instanceof Double) { + Double d = (Double) o; + isGreaterThanZero = d > 0; + } else if (o instanceof Float) { + Float f = (Float) o; + isGreaterThanZero = f > 0; + } else if (o instanceof BigInteger) { + BigInteger bigInteger = (BigInteger) o; + isGreaterThanZero = bigInteger.compareTo(BigInteger.ZERO) > 0; // bigInteger > 0 + } else { + Number n = (Number) o; + isGreaterThanZero = n.doubleValue() > 0; + } + + if (!isGreaterThanZero) { + String message = Tr.formatMessage(tc, ValidationMessageConstants.SCHEMA_MULTIPLE_OF_NOT_GREATER_THAN_ZERO); + helper.addValidationEvent(new ValidationEvent(Severity.ERROR, context.getLocation(), message)); + } + } + + private void validateRequired(ValidationHelper helper, Context context, Schema schema) { + // Array of unique strings + Object o = schema.get(SchemaConstant.PROP_REQUIRED); + + if (o == null) { + // No value to validate + return; + } + + // Check is list + if (!(o instanceof List)) { + String message = Tr.formatMessage(tc, ValidationMessageConstants.SCHEMA_PROPERTY_WRONG_TYPE, SchemaConstant.PROP_REQUIRED, "array"); + helper.addValidationEvent(new ValidationEvent(Severity.ERROR, context.getLocation(), message)); + return; + } + + // Check all strings + List list = (List) o; + for (Object e : list) { + if (!(e instanceof String)) { + String message = Tr.formatMessage(tc, ValidationMessageConstants.SCHEMA_COLLECTION_ELEMENT_WRONG_TYPE, SchemaConstant.PROP_REQUIRED, "string"); + helper.addValidationEvent(new ValidationEvent(Severity.ERROR, context.getLocation(), message)); + return; + } + } + + // Check all unique + Set set = new HashSet<>(); + for (Object e : list) { + if (!set.add(e)) { + String message = Tr.formatMessage(tc, ValidationMessageConstants.SCHEMA_PROPERTY_NOT_UNIQUE, SchemaConstant.PROP_REQUIRED); + helper.addValidationEvent(new ValidationEvent(Severity.ERROR, context.getLocation(), message)); + return; + } + } + } + + private void validateDependentRequred(ValidationHelper helper, Context context, Schema schema) { + // Map of Arrays of unique strings + Object o = schema.get(SchemaConstant.PROP_DEPENDENT_REQUIRED); + + if (o == null) { + // No value to validate + return; + } + + // Check is map + if (!(o instanceof Map)) { + String message = Tr.formatMessage(tc, ValidationMessageConstants.SCHEMA_PROPERTY_WRONG_TYPE, SchemaConstant.PROP_DEPENDENT_REQUIRED, "object"); + helper.addValidationEvent(new ValidationEvent(Severity.ERROR, context.getLocation(), message)); + return; + } + + Map map = (Map) o; + + // Check each value is an array + for (Object value : map.values()) { + if (!(value instanceof List)) { + String message = Tr.formatMessage(tc, ValidationMessageConstants.SCHEMA_COLLECTION_ELEMENT_WRONG_TYPE, SchemaConstant.PROP_DEPENDENT_REQUIRED, "array"); + helper.addValidationEvent(new ValidationEvent(Severity.ERROR, context.getLocation(), message)); + return; + } + } + + // Check each array consists of unique strings + for (Object value : map.values()) { + List list = (List) value; // Types checked above + Set set = new HashSet<>(); + for (Object element : list) { + if (!(element instanceof String) || !set.add(element)) { + String message = Tr.formatMessage(tc, ValidationMessageConstants.SCHEMA_DEPENDENT_REQUIRED_UNIQUE_STRINGS); + helper.addValidationEvent(new ValidationEvent(Severity.ERROR, context.getLocation(), message)); + return; + } + } + } + } + + private void validateIntZeroOrGreater(ValidationHelper helper, Context context, Schema schema, String prop) { + Object o = schema.get(prop); + if (o == null) { + // no value to validate + return; + } + + boolean isZeroOrGreater; + if (o instanceof Integer) { + Integer integer = (Integer) o; + isZeroOrGreater = integer >= 0; + } else if (o instanceof Long) { + Long value = (Long) o; + isZeroOrGreater = value >= 0L; + } else if (o instanceof Short) { + Short s = (Short) o; + isZeroOrGreater = s >= 0; + } else if (o instanceof Byte) { + Byte value = (Byte) o; + isZeroOrGreater = value >= 0; + } else if (o instanceof BigInteger) { + BigInteger value = (BigInteger) o; + isZeroOrGreater = value.compareTo(BigInteger.ZERO) >= 0; // value >= 0 + } else { + // invalid type + String message = Tr.formatMessage(tc, ValidationMessageConstants.SCHEMA_PROPERTY_WRONG_TYPE, prop, "integer"); + helper.addValidationEvent(new ValidationEvent(Severity.ERROR, context.getLocation(), message)); + return; + } + + if (!isZeroOrGreater) { + String message = Tr.formatMessage(tc, ValidationMessageConstants.SCHEMA_PROPERTY_LESS_THAN_ZERO, prop); + helper.addValidationEvent(new ValidationEvent(Severity.ERROR, context.getLocation(), message)); + } + } + + private boolean isSchema(Object o) { + if (o instanceof Schema) { + return true; + } else if (o instanceof Boolean) { + // A boolean is a valid schema + return true; + } else if (o instanceof Map) { + // A map _could_ be a valid schema, we won't validate it but don't want to report an error either + return true; + } else { + return false; + } + } + + private void validateIsSchema(ValidationHelper helper, Context context, Schema schema, String prop) { + Object o = schema.get(prop); + if (o == null) { + // No value to validate + return; + } + + if (!isSchema(o)) { + String message = Tr.formatMessage(tc, ValidationMessageConstants.SCHEMA_PROPERTY_WRONG_TYPE, prop, "schema"); + helper.addValidationEvent(new ValidationEvent(Severity.ERROR, context.getLocation(), message)); + } + } + + private void validateIsMapOfNamedSchemas(ValidationHelper helper, Context context, Schema schema, String prop) { + Object o = schema.get(prop); + if (o == null) { + // No value to validate + return; + } + + if (!(o instanceof Map)) { + String message = Tr.formatMessage(tc, ValidationMessageConstants.SCHEMA_PROPERTY_WRONG_TYPE, prop, "object"); + helper.addValidationEvent(new ValidationEvent(Severity.ERROR, context.getLocation(), message)); + return; + } + + Map map = (Map) o; + for (Object e : map.values()) { + if (!isSchema(e)) { + String message = Tr.formatMessage(tc, ValidationMessageConstants.SCHEMA_COLLECTION_ELEMENT_WRONG_TYPE, prop, "schema"); + helper.addValidationEvent(new ValidationEvent(Severity.ERROR, context.getLocation(), message)); + return; + } + } + } + + private void validateArrayOfSchema(ValidationHelper helper, Context context, Schema schema, String prop) { + Object o = schema.get(prop); + if (o == null) { + // No value to validate + return; + } + + if (!(o instanceof List)) { + String message = Tr.formatMessage(tc, ValidationMessageConstants.SCHEMA_PROPERTY_WRONG_TYPE, prop, "array"); + helper.addValidationEvent(new ValidationEvent(Severity.ERROR, context.getLocation(), message)); + return; + } + + List list = (List) o; + for (Object e : list) { + if (!isSchema(e)) { + String message = Tr.formatMessage(tc, ValidationMessageConstants.SCHEMA_COLLECTION_ELEMENT_WRONG_TYPE, prop, "schema"); + helper.addValidationEvent(new ValidationEvent(Severity.ERROR, context.getLocation(), message)); + return; + } + } + } + + private void validatePropType(ValidationHelper helper, Context context, Schema schema, String prop, PropertyType type) { + Object o = schema.get(prop); + if (o == null) { + // Nothing to validate + return; + } + PropertyType actualType; + if (o instanceof Number) { + actualType = PropertyType.NUMBER; + } else if (o instanceof String) { + actualType = PropertyType.STRING; + } else if (o instanceof List) { + actualType = PropertyType.ARRAY; + } else if (o instanceof Map) { + actualType = PropertyType.OBJECT; + } else if (o instanceof Boolean) { + actualType = PropertyType.BOOLEAN; + } else if (o instanceof Schema) { + actualType = ((Schema) o).getBooleanSchema() ? PropertyType.BOOLEAN : PropertyType.OBJECT; + } else if (o instanceof Constructible) { + actualType = PropertyType.OBJECT; + } else { + actualType = PropertyType.UNKNOWN; + } + + if (actualType != type) { + String message = Tr.formatMessage(tc, ValidationMessageConstants.SCHEMA_PROPERTY_WRONG_TYPE, prop, type); + helper.addValidationEvent(new ValidationEvent(Severity.ERROR, context.getLocation(), message)); + } + } + + private boolean isSupportedDialect(Schema schema, Context context) { + String dialect = schema.getSchemaDialect(); + + // NOTE: we should get the default dialect from the root OpenAPI here, but it's missing from the API + + if (dialect == null || dialect.equals(SchemaConstant.DIALECT_JSON_2020_12) || dialect.equals(SchemaConstant.DIALECT_OAS31)) { + return true; + } else { + return false; + } + } + + private enum PropertyType { + STRING("string"), + NUMBER("number"), + ARRAY("array"), + OBJECT("object"), + BOOLEAN("boolean"), + UNKNOWN("unknown"); + + private String value; + + private PropertyType(String value) { + this.value = value; + } + + @Override + public String toString() { + return value; + } + + } + +} diff --git a/dev/io.openliberty.microprofile.openapi.4.0.internal.services/src/io/openliberty/microprofile/openapi40/internal/services/validation/ServerVariable31Validator.java b/dev/io.openliberty.microprofile.openapi.4.0.internal.services/src/io/openliberty/microprofile/openapi40/internal/services/validation/ServerVariable31Validator.java new file mode 100644 index 00000000000..97e57e6dd97 --- /dev/null +++ b/dev/io.openliberty.microprofile.openapi.4.0.internal.services/src/io/openliberty/microprofile/openapi40/internal/services/validation/ServerVariable31Validator.java @@ -0,0 +1,59 @@ +/******************************************************************************* + * Copyright (c) 2024 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package io.openliberty.microprofile.openapi40.internal.services.validation; + +import java.util.List; + +import org.eclipse.microprofile.openapi.models.servers.ServerVariable; + +import com.ibm.websphere.ras.Tr; +import com.ibm.websphere.ras.TraceComponent; + +import io.openliberty.microprofile.openapi20.internal.services.OASValidationResult.ValidationEvent; +import io.openliberty.microprofile.openapi20.internal.services.OASValidationResult.ValidationEvent.Severity; +import io.openliberty.microprofile.openapi20.internal.utils.OpenAPIModelWalker.Context; +import io.openliberty.microprofile.openapi20.internal.utils.ValidationMessageConstants; +import io.openliberty.microprofile.openapi20.internal.validation.TypeValidator; +import io.openliberty.microprofile.openapi20.internal.validation.ValidationHelper; +import io.openliberty.microprofile.openapi20.internal.validation.ValidatorUtils; + +public class ServerVariable31Validator extends TypeValidator { + private static final TraceComponent tc = Tr.register(ServerVariable31Validator.class); + + private static final String PROP_DEFAULT = "default"; + + private static final ServerVariable31Validator INSTANCE = new ServerVariable31Validator(); + + public static ServerVariable31Validator getInstance() { + return INSTANCE; + } + + private ServerVariable31Validator() {} + + @Override + public void validate(ValidationHelper helper, Context context, String key, ServerVariable t) { + ValidatorUtils.validateRequiredField(t.getDefaultValue(), context, PROP_DEFAULT).ifPresent(helper::addValidationEvent); + + String defaultValue = t.getDefaultValue(); + List enumeration = t.getEnumeration(); + if (enumeration != null) { + if (enumeration.isEmpty()) { + // Enum must not be empty if present + String message = Tr.formatMessage(tc, ValidationMessageConstants.SERVER_VARIABLE_ARRAY_EMPTY, "enum"); + helper.addValidationEvent(new ValidationEvent(Severity.ERROR, context.getLocation(), message)); + } else if (defaultValue != null && !enumeration.contains(defaultValue)) { + // Enum must contain default value if present + String message = Tr.formatMessage(tc, ValidationMessageConstants.SERVER_VARIABLE_DEFAULT_NOT_IN_ENUM, t.getDefaultValue()); + helper.addValidationEvent(new ValidationEvent(Severity.ERROR, context.getLocation(), message)); + } + } + } + +} diff --git a/dev/io.openliberty.microprofile.openapi.4.0.internal.services/src/io/openliberty/microprofile/openapi40/internal/services/validation/package-info.java b/dev/io.openliberty.microprofile.openapi.4.0.internal.services/src/io/openliberty/microprofile/openapi40/internal/services/validation/package-info.java new file mode 100644 index 00000000000..32a2fb145a8 --- /dev/null +++ b/dev/io.openliberty.microprofile.openapi.4.0.internal.services/src/io/openliberty/microprofile/openapi40/internal/services/validation/package-info.java @@ -0,0 +1,16 @@ +/******************************************************************************* + * Copyright (c) 2024 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ + +@TraceOptions(traceGroup = Constants.TRACE_GROUP, messageBundle = Constants.TRACE_VALIDATION) +package io.openliberty.microprofile.openapi40.internal.services.validation; + +import com.ibm.websphere.ras.annotation.TraceOptions; + +import io.openliberty.microprofile.openapi20.internal.utils.Constants; diff --git a/dev/io.openliberty.microprofile.openapi.4.0.internal.services/test/io/openliberty/microprofile/openapi40/internal/services/validation/test/OpenAPIDefinition31ValidatorTest.java b/dev/io.openliberty.microprofile.openapi.4.0.internal.services/test/io/openliberty/microprofile/openapi40/internal/services/validation/test/OpenAPIDefinition31ValidatorTest.java new file mode 100644 index 00000000000..212c12f5055 --- /dev/null +++ b/dev/io.openliberty.microprofile.openapi.4.0.internal.services/test/io/openliberty/microprofile/openapi40/internal/services/validation/test/OpenAPIDefinition31ValidatorTest.java @@ -0,0 +1,94 @@ +/******************************************************************************* + * Copyright (c) 2024 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package io.openliberty.microprofile.openapi40.internal.services.validation.test; + +import static io.openliberty.microprofile.openapi20.test.utils.ValidationResultMatcher.hasError; +import static io.openliberty.microprofile.openapi20.test.utils.ValidationResultMatcher.successful; +import static org.hamcrest.MatcherAssert.assertThat; + +import java.util.Collections; + +import org.eclipse.microprofile.openapi.models.OpenAPI; +import org.junit.Test; + +import io.openliberty.microprofile.openapi20.internal.validation.TypeValidator; +import io.openliberty.microprofile.openapi20.validation.test.OpenAPIValidatorTest; +import io.openliberty.microprofile.openapi40.internal.services.validation.OpenAPIDefinition31Validator; +import io.smallrye.openapi.api.models.ComponentsImpl; +import io.smallrye.openapi.api.models.OpenAPIImpl; +import io.smallrye.openapi.api.models.info.InfoImpl; + +public class OpenAPIDefinition31ValidatorTest extends OpenAPIValidatorTest { + + @Override + protected TypeValidator getValidator() { + return OpenAPIDefinition31Validator.getInstance(); + } + + @Test + public void testComponentsOnly() { + TypeValidator validator = getValidator(); + TestValidationHelper31 vh = new TestValidationHelper31(); + + OpenAPIImpl openapi = new OpenAPIImpl(); + openapi.setOpenapi("3.1.0"); + + InfoImpl info = new InfoImpl(); + info.title("Test OpenAPI model").version("2.3.0"); + openapi.setInfo(info); + + openapi.setPaths(null); + openapi.setComponents(new ComponentsImpl()); + openapi.setWebhooks(null); + + validator.validate(vh, context, openapi); + assertThat(vh.getResult(), successful()); + } + + @Test + public void testWebhooksOnly() { + TypeValidator validator = getValidator(); + TestValidationHelper31 vh = new TestValidationHelper31(); + + OpenAPIImpl openapi = new OpenAPIImpl(); + openapi.setOpenapi("3.1.0"); + + InfoImpl info = new InfoImpl(); + info.title("Test OpenAPI model").version("2.3.0"); + openapi.setInfo(info); + + openapi.setPaths(null); + openapi.setComponents(null); + openapi.setWebhooks(Collections.emptyMap()); + + validator.validate(vh, context, openapi); + assertThat(vh.getResult(), successful()); + } + + @Override + public void testNoPathsOpenAPI() { + TypeValidator validator = getValidator(); + TestValidationHelper31 vh = new TestValidationHelper31(); + + OpenAPIImpl openapi = new OpenAPIImpl(); + + openapi.setOpenapi("3.1.0"); + + InfoImpl info = new InfoImpl(); + info.title("Test OpenAPI model").version("2.3.0"); + openapi.setInfo(info); + + openapi.setPaths(null); + + validator.validate(vh, context, openapi); + assertThat(vh.getResult(), hasError("The OpenAPI Object must contain at least one of the \"paths\", \"components\", or \"webhooks\" properties")); + } + +} diff --git a/dev/io.openliberty.microprofile.openapi.4.0.internal.services/test/io/openliberty/microprofile/openapi40/internal/services/validation/test/Operation31ValidatorTest.java b/dev/io.openliberty.microprofile.openapi.4.0.internal.services/test/io/openliberty/microprofile/openapi40/internal/services/validation/test/Operation31ValidatorTest.java new file mode 100644 index 00000000000..465856b6cb6 --- /dev/null +++ b/dev/io.openliberty.microprofile.openapi.4.0.internal.services/test/io/openliberty/microprofile/openapi40/internal/services/validation/test/Operation31ValidatorTest.java @@ -0,0 +1,61 @@ +/******************************************************************************* + * Copyright (c) 2024 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package io.openliberty.microprofile.openapi40.internal.services.validation.test; + +import static io.openliberty.microprofile.openapi20.test.utils.ValidationResultMatcher.successful; +import static org.hamcrest.MatcherAssert.assertThat; + +import org.eclipse.microprofile.openapi.models.Operation; + +import io.openliberty.microprofile.openapi20.internal.utils.OpenAPIModelWalker.Context; +import io.openliberty.microprofile.openapi20.internal.validation.TypeValidator; +import io.openliberty.microprofile.openapi20.test.utils.TestValidationContextHelper; +import io.openliberty.microprofile.openapi20.validation.test.OperationValidatorTest; +import io.openliberty.microprofile.openapi40.internal.services.validation.Operation31Validator; +import io.smallrye.openapi.api.models.OpenAPIImpl; +import io.smallrye.openapi.api.models.OperationImpl; +import io.smallrye.openapi.api.models.PathItemImpl; +import io.smallrye.openapi.api.models.PathsImpl; + +/** + * + */ +public class Operation31ValidatorTest extends OperationValidatorTest { + + @Override + protected TypeValidator getValidator() { + return Operation31Validator.getInstance(); + } + + @Override + public void testOperationWithNoResponses() { + TypeValidator validator = getValidator(); + TestValidationHelper31 vh = new TestValidationHelper31(); + + String pathNameOne = "/my-test-path-one/"; + + PathItemImpl pathItemOne = new PathItemImpl(); + OperationImpl getPathItemOne = new OperationImpl(); + getPathItemOne.operationId("pathItemOneGetId"); + pathItemOne.setGET(getPathItemOne); + + PathsImpl paths = new PathsImpl(); + paths.addPathItem(pathNameOne, pathItemOne); + + OpenAPIImpl model = new OpenAPIImpl(); + Context context = new TestValidationContextHelper(model); + model.setPaths(paths); + + validator.validate(vh, context, null, getPathItemOne); + // In 3.1, having no responses is valid + assertThat(vh.getResult(), successful()); + } + +} diff --git a/dev/io.openliberty.microprofile.openapi.4.0.internal.services/test/io/openliberty/microprofile/openapi40/internal/services/validation/test/PathItem31ValidatorTest.java b/dev/io.openliberty.microprofile.openapi.4.0.internal.services/test/io/openliberty/microprofile/openapi40/internal/services/validation/test/PathItem31ValidatorTest.java new file mode 100644 index 00000000000..609618abca9 --- /dev/null +++ b/dev/io.openliberty.microprofile.openapi.4.0.internal.services/test/io/openliberty/microprofile/openapi40/internal/services/validation/test/PathItem31ValidatorTest.java @@ -0,0 +1,65 @@ +/******************************************************************************* + * Copyright (c) 2024 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package io.openliberty.microprofile.openapi40.internal.services.validation.test; + +import static io.openliberty.microprofile.openapi20.test.utils.ValidationResultMatcher.successful; +import static org.hamcrest.MatcherAssert.assertThat; + +import org.eclipse.microprofile.openapi.models.Components; +import org.eclipse.microprofile.openapi.models.OpenAPI; +import org.eclipse.microprofile.openapi.models.PathItem; +import org.junit.Test; + +import io.openliberty.microprofile.openapi20.internal.utils.OpenAPIModelWalker.Context; +import io.openliberty.microprofile.openapi20.internal.validation.TypeValidator; +import io.openliberty.microprofile.openapi20.test.utils.TestValidationContextHelper; +import io.openliberty.microprofile.openapi20.validation.test.PathItemValidatorTest; +import io.openliberty.microprofile.openapi40.internal.services.validation.PathItem31Validator; +import io.smallrye.openapi.api.models.ComponentsImpl; +import io.smallrye.openapi.api.models.OpenAPIImpl; +import io.smallrye.openapi.api.models.OperationImpl; +import io.smallrye.openapi.api.models.PathItemImpl; + +public class PathItem31ValidatorTest extends PathItemValidatorTest { + + @Override + protected TypeValidator getValidator() { + return PathItem31Validator.getInstance(); + } + + /** + * Internal refs are valid in path items in 3.1 + */ + @Override + @Test + public void testInternalRefInPathItem() { + String key = "{username}"; + + TypeValidator validator = getValidator(); + TestValidationHelper31 vh = new TestValidationHelper31(); + + // Need a model with a PathItem to reference + PathItemImpl sharedPathItem = new PathItemImpl(); + sharedPathItem.GET(new OperationImpl().description("testOp")); + + Components components = new ComponentsImpl(); + components.addPathItem("sharedPathItem", sharedPathItem); + + OpenAPI model = new OpenAPIImpl().components(components); + + PathItemImpl pathItem = new PathItemImpl(); + pathItem.ref("#/components/pathItems/sharedPathItem"); + + Context context = new TestValidationContextHelper(model); + + validator.validate(vh, context, key, pathItem); + assertThat(vh.getResult(), successful()); + } +} diff --git a/dev/io.openliberty.microprofile.openapi.4.0.internal.services/test/io/openliberty/microprofile/openapi40/internal/services/validation/test/Reference31ValidatorTest.java b/dev/io.openliberty.microprofile.openapi.4.0.internal.services/test/io/openliberty/microprofile/openapi40/internal/services/validation/test/Reference31ValidatorTest.java new file mode 100644 index 00000000000..dd2c8d643ee --- /dev/null +++ b/dev/io.openliberty.microprofile.openapi.4.0.internal.services/test/io/openliberty/microprofile/openapi40/internal/services/validation/test/Reference31ValidatorTest.java @@ -0,0 +1,530 @@ +/******************************************************************************* + * Copyright (c) 2020 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +package io.openliberty.microprofile.openapi40.internal.services.validation.test; + +import static io.openliberty.microprofile.openapi20.test.utils.ValidationResultMatcher.hasError; +import static io.openliberty.microprofile.openapi20.test.utils.ValidationResultMatcher.successful; +import static org.hamcrest.MatcherAssert.assertThat; + +import java.util.HashMap; +import java.util.Map; + +import org.eclipse.microprofile.openapi.models.Components; +import org.eclipse.microprofile.openapi.models.PathItem; +import org.eclipse.microprofile.openapi.models.examples.Example; +import org.eclipse.microprofile.openapi.models.headers.Header; +import org.eclipse.microprofile.openapi.models.links.Link; +import org.eclipse.microprofile.openapi.models.media.Schema; +import org.eclipse.microprofile.openapi.models.parameters.Parameter; +import org.eclipse.microprofile.openapi.models.parameters.Parameter.In; +import org.eclipse.microprofile.openapi.models.parameters.RequestBody; +import org.eclipse.microprofile.openapi.models.responses.APIResponse; +import org.eclipse.microprofile.openapi.models.security.SecurityScheme; +import org.hamcrest.Matcher; +import org.junit.Before; +import org.junit.Test; + +import io.openliberty.microprofile.openapi20.internal.services.OASValidationResult; +import io.openliberty.microprofile.openapi20.internal.utils.OpenAPIModelWalker.Context; +import io.openliberty.microprofile.openapi20.test.utils.TestValidationContextHelper; +import io.openliberty.microprofile.openapi20.test.utils.TestValidationHelper; +import io.openliberty.microprofile.openapi40.internal.services.validation.Reference31Validator; +import io.smallrye.openapi.api.models.ComponentsImpl; +import io.smallrye.openapi.api.models.OpenAPIImpl; +import io.smallrye.openapi.api.models.PathItemImpl; +import io.smallrye.openapi.api.models.examples.ExampleImpl; +import io.smallrye.openapi.api.models.headers.HeaderImpl; +import io.smallrye.openapi.api.models.links.LinkImpl; +import io.smallrye.openapi.api.models.media.SchemaImpl; +import io.smallrye.openapi.api.models.parameters.ParameterImpl; +import io.smallrye.openapi.api.models.parameters.RequestBodyImpl; +import io.smallrye.openapi.api.models.responses.APIResponseImpl; +import io.smallrye.openapi.api.models.security.SecuritySchemeImpl; + +/** + * + */ +public class Reference31ValidatorTest { + + String key; + OpenAPIImpl model; + Context context; + Reference31Validator validator; + TestValidationHelper vh; + + private OpenAPIImpl setModel() { + + OpenAPIImpl model = new OpenAPIImpl(); + + ComponentsImpl components = new ComponentsImpl(); + model.setComponents(components); + + SchemaImpl schema = new SchemaImpl(); + Map schemas = new HashMap<>(); + schemas.put("testSchema", schema); + components.setSchemas(schemas); + schema.setTitle("testSchema"); + schema.setDescription("Basic schema for testing"); + schema.setFormat("string"); + + APIResponseImpl response = new APIResponseImpl(); + Map responses = new HashMap<>(); + responses.put("testResponse", response); + components.setResponses(responses); + response.setDescription("A simple test response."); + + ParameterImpl parameter = new ParameterImpl(); + Map parameters = new HashMap<>(); + parameters.put("testParameter", parameter); + components.setParameters(parameters); + parameter.setName("Accept"); + parameter.setIn(In.HEADER); + + ExampleImpl example = new ExampleImpl(); + Map examples = new HashMap<>(); + examples.put("testExample", example); + components.setExamples(examples); + example.setSummary("A test example"); + example.setDescription("A simple example for testing"); + + RequestBodyImpl requestBody = new RequestBodyImpl(); + Map requestBodies = new HashMap<>(); + requestBodies.put("testRequestBody", requestBody); + components.setRequestBodies(requestBodies); + + HeaderImpl header = new HeaderImpl(); + Map headers = new HashMap<>(); + headers.put("testHeader", header); + components.setHeaders(headers); + + SecuritySchemeImpl securityScheme = new SecuritySchemeImpl(); + Map securitySchemes = new HashMap<>(); + securitySchemes.put("testSecurityScheme", securityScheme); + components.setSecuritySchemes(securitySchemes); + + LinkImpl link = new LinkImpl(); + Map links = new HashMap<>(); + links.put("testLink", link); + components.setLinks(links); + + PathItemImpl pathItem = new PathItemImpl(); + Map pathItems = new HashMap<>(); + pathItems.put("testPathItem", pathItem); + components.setPathItems(pathItems); + + return model; + + } + + @Before + public void setUp() { + model = setModel(); + context = new TestValidationContextHelper(model); + vh = new TestValidationHelper(); + validator = Reference31Validator.getInstance(); + } + + @Test + public void testNullComponents() { + String ref = "#/components/schemas/Pet"; + + validator.validate(vh, context, ref); + assertThat(vh.getResult(), targetMissing(ref)); + } + + @Test + public void testExternalFileRef() { + String ref = "Pet.yaml"; + + validator.validate(vh, context, ref); + assertThat(vh.getResult(), successful()); + + vh.resetResults(); + + ref = "Pet.json"; + validator.validate(vh, context, ref); + assertThat(vh.getResult(), successful()); + + vh.resetResults(); + + ref = "Pet.yml"; + validator.validate(vh, context, ref); + assertThat(vh.getResult(), successful()); + } + + @Test + public void testExternalFileWithEmbeddedRef() { + String ref = "definitions.yaml#/Pet"; + + validator.validate(vh, context, ref); + + assertThat(vh.getResult(), successful()); + + vh.resetResults(); + + ref = "definitions.json#/Pet"; + validator.validate(vh, context, ref); + assertThat(vh.getResult(), successful()); + + vh.resetResults(); + + ref = "definitions.yml#/Pet"; + validator.validate(vh, context, ref); + assertThat(vh.getResult(), successful()); + } + + @Test + public void testExtensionRef() { + String ref = "application/x-www-form-urlencoded"; + + validator.validate(vh, context, ref); + + assertThat(vh.getResult(), successful()); + } + + @Test + public void testHttpLinkRef() { + + String ref = "http://foo.bar#/examples/zip-example"; + + validator.validate(vh, context, ref); + assertThat(vh.getResult(), successful()); + } + + @Test + public void testInvalidRef() { + + String ref = "#/invalidRef/schemas/testSchema"; + + validator.validate(vh, context, ref); + assertThat(vh.getResult(), successful()); // In 3.1, we don't validate references we don't recognize + + vh.resetResults(); + + ref = "#/components/schemas/Pet/Cat"; + validator.validate(vh, context, ref); + assertThat(vh.getResult(), targetMissing(ref)); // #/components/schemas/Pet is not present + + vh.resetResults(); + + ref = "#/components/schemas/testSchema/Cat"; + validator.validate(vh, context, ref); + assertThat(vh.getResult(), successful()); // #/components/schemas/testSchema is present and we don't know how to validate under that + + vh.resetResults(); + + ref = "#/components/components/schemas/testSchema"; + validator.validate(vh, context, ref); + assertThat(vh.getResult(), targetMissing(ref)); // #/components/components is not present + + vh.resetResults(); + + ref = ""; + validator.validate(vh, context, ref); + assertThat(vh.getResult(), isNullRef()); // We do report a blank reference + + vh.resetResults(); + + ref = null; + validator.validate(vh, context, ref); + assertThat(vh.getResult(), isNullRef()); // We do report a null reference + + vh.resetResults(); + + ref = " "; + validator.validate(vh, context, ref); + assertThat(vh.getResult(), isNullRef()); // We do report a blank reference + vh.resetResults(); + + ref = "#"; + validator.validate(vh, context, ref); + assertThat(vh.getResult(), successful()); // In 3.1, we don't validate references we don't recognize + + vh.resetResults(); + + ref = "#/"; + validator.validate(vh, context, ref); + assertThat(vh.getResult(), successful()); // In 3.1, we don't validate references we don't recognize + + vh.resetResults(); + + ref = "#/components/Pet"; + validator.validate(vh, context, ref); + assertThat(vh.getResult(), targetMissing(ref)); // Illegal component type + + vh.resetResults(); + + ref = "#/components//Pet"; + validator.validate(vh, context, ref); + assertThat(vh.getResult(), targetMissing(ref)); // Illegal component type + + vh.resetResults(); + + ref = "#/components/invalid/Pet"; + validator.validate(vh, context, ref); + assertThat(vh.getResult(), targetMissing(ref)); // Illegal component type + + vh.resetResults(); + + ref = "#/components/x-invalid/Pet"; + validator.validate(vh, context, ref); + assertThat(vh.getResult(), successful()); // Don't validate within an extension component type + + vh.resetResults(); + + ref = "#invalid/components/schemas/Pet"; + validator.validate(vh, context, ref); + assertThat(vh.getResult(), successful()); // In 3.1, we don't validate references we don't recognize + + vh.resetResults(); + + ref = "#/components/schemas"; + validator.validate(vh, context, ref); + assertThat(vh.getResult(), successful()); // This resolves to an object under components, so there's no validation error even though it's weird + + vh.resetResults(); + + ref = "#/components/schemas/schemas"; + validator.validate(vh, context, ref); + assertThat(vh.getResult(), targetMissing(ref)); // This is just a reference to a schema named "schemas" which is missing + } + + @Test + public void testExternalFileWithLink() { + String ref = "http://foo.bar#/examples/address-example.json"; + + validator.validate(vh, context, ref); + assertThat(vh.getResult(), successful()); + } + + @Test + public void testExternalFileWithSecureLink() { + String ref = "https://foo.bar#/examples/address-example.json"; + + validator.validate(vh, context, ref); + assertThat(vh.getResult(), successful()); + } + + @Test + public void testNullSchema() { + String ref = "#/components/schemas/Pet"; + + Components component = model.getComponents(); + component.setSchemas(null); + model.setComponents(component); + context = new TestValidationContextHelper(model); + + validator.validate(vh, context, ref); + assertThat(vh.getResult(), targetMissing(ref)); + } + + @Test + public void testNullResponses() { + String ref = "#/components/responses/Pet"; + + Components component = model.getComponents(); + component.setResponses(null); + model.setComponents(component); + context = new TestValidationContextHelper(model); + + validator.validate(vh, context, ref); + assertThat(vh.getResult(), targetMissing(ref)); + } + + @Test + public void testNullParameters() { + String ref = "#/components/parameters/Pet"; + + Components component = model.getComponents(); + component.setParameters(null); + model.setComponents(component); + context = new TestValidationContextHelper(model); + + validator.validate(vh, context, ref); + assertThat(vh.getResult(), targetMissing(ref)); + } + + @Test + public void testNullExamples() { + String ref = "#/components/examples/Pet"; + + Components component = model.getComponents(); + component.setExamples(null); + model.setComponents(component); + context = new TestValidationContextHelper(model); + + validator.validate(vh, context, ref); + assertThat(vh.getResult(), targetMissing(ref)); + } + + @Test + public void testNullRequestBodies() { + String ref = "#/components/requestBodies/Pet"; + + Components component = model.getComponents(); + component.setRequestBodies(null); + model.setComponents(component); + context = new TestValidationContextHelper(model); + + validator.validate(vh, context, ref); + assertThat(vh.getResult(), targetMissing(ref)); + } + + @Test + public void testNullHeaders() { + String ref = "#/components/headers/Pet"; + + Components component = model.getComponents(); + component.setHeaders(null); + model.setComponents(component); + context = new TestValidationContextHelper(model); + + validator.validate(vh, context, ref); + assertThat(vh.getResult(), targetMissing(ref)); + } + + @Test + public void testNullSecuritySchemes() { + String ref = "#/components/securitySchemes/Pet"; + + Components component = model.getComponents(); + component.setSecuritySchemes(null); + model.setComponents(component); + context = new TestValidationContextHelper(model); + + validator.validate(vh, context, ref); + assertThat(vh.getResult(), targetMissing(ref)); + } + + @Test + public void testNullLinks() { + String ref = "#/components/links/Pet"; + + Components component = model.getComponents(); + component.setLinks(null); + model.setComponents(component); + context = new TestValidationContextHelper(model); + + validator.validate(vh, context, ref); + assertThat(vh.getResult(), targetMissing(ref)); + } + + @Test + public void testValidReferences() { + String ref = "#/components/schemas/testSchema"; + validator.validate(vh, context, ref); + assertThat(vh.getResult(), successful()); + + ref = "#/components/responses/testResponse"; + validator.validate(vh, context, ref); + assertThat(vh.getResult(), successful()); + + ref = "#/components/parameters/testParameter"; + validator.validate(vh, context, ref); + assertThat(vh.getResult(), successful()); + + ref = "#/components/examples/testExample"; + validator.validate(vh, context, ref); + assertThat(vh.getResult(), successful()); + + ref = "#/components/requestBodies/testRequestBody"; + validator.validate(vh, context, ref); + assertThat(vh.getResult(), successful()); + + ref = "#/components/headers/testHeader"; + validator.validate(vh, context, ref); + assertThat(vh.getResult(), successful()); + + ref = "#/components/securitySchemes/testSecurityScheme"; + validator.validate(vh, context, ref); + assertThat(vh.getResult(), successful()); + + ref = "#/components/links/testLink"; + validator.validate(vh, context, ref); + assertThat(vh.getResult(), successful()); + } + + @Test + public void testInvalidReferences() { + String ref = "#/components/schemas/testInvalidSchema"; + validator.validate(vh, context, ref); + assertThat(vh.getResult(), targetMissing(ref)); + + vh.resetResults(); + + ref = "#/components/responses/testInvalidResponse"; + validator.validate(vh, context, ref); + assertThat(vh.getResult(), targetMissing(ref)); + + vh.resetResults(); + + ref = "#/components/parameters/testInvalidParameter"; + validator.validate(vh, context, ref); + assertThat(vh.getResult(), targetMissing(ref)); + + vh.resetResults(); + + ref = "#/components/examples/testInvalidExample"; + validator.validate(vh, context, ref); + assertThat(vh.getResult(), targetMissing(ref)); + + vh.resetResults(); + + ref = "#/components/requestBodies/testInvalidRequestBody"; + validator.validate(vh, context, ref); + assertThat(vh.getResult(), targetMissing(ref)); + + vh.resetResults(); + + ref = "#/components/headers/testInvalidHeader"; + validator.validate(vh, context, ref); + assertThat(vh.getResult(), targetMissing(ref)); + + vh.resetResults(); + + ref = "#/components/securitySchemes/testInvalidSecurityScheme"; + validator.validate(vh, context, ref); + assertThat(vh.getResult(), targetMissing(ref)); + + vh.resetResults(); + + ref = "#/components/links/testInvalidLink"; + validator.validate(vh, context, ref); + assertThat(vh.getResult(), targetMissing(ref)); + } + + @Test + public void testNonUri() { + String ref = "http://\n/#/components/schemas/testSchema"; + validator.validate(vh, context, ref); + assertThat(vh.getResult(), notValidUri(ref)); + + vh.resetResults(); + ref = "http://{}/#/components/schemas/testSchema"; + validator.validate(vh, context, ref); + assertThat(vh.getResult(), notValidUri(ref)); + } + + private Matcher targetMissing(String ref) { + return hasError("The \"" + ref + "\" reference value is not defined within the Components Object"); + } + + private Matcher notValidUri(String ref) { + return hasError("The \"" + ref + "\" value is not a valid URI"); + } + + private Matcher isNullRef() { + return hasError("The reference value is null"); + } + +} diff --git a/dev/io.openliberty.microprofile.openapi.4.0.internal.services/test/io/openliberty/microprofile/openapi40/internal/services/validation/test/Schema31ValidatorTest.java b/dev/io.openliberty.microprofile.openapi.4.0.internal.services/test/io/openliberty/microprofile/openapi40/internal/services/validation/test/Schema31ValidatorTest.java new file mode 100644 index 00000000000..6641d6aeaf9 --- /dev/null +++ b/dev/io.openliberty.microprofile.openapi.4.0.internal.services/test/io/openliberty/microprofile/openapi40/internal/services/validation/test/Schema31ValidatorTest.java @@ -0,0 +1,974 @@ +/******************************************************************************* + * Copyright (c) 2024 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package io.openliberty.microprofile.openapi40.internal.services.validation.test; + +import static io.openliberty.microprofile.openapi20.test.utils.ValidationResultMatcher.failedWithEvents; +import static io.openliberty.microprofile.openapi20.test.utils.ValidationResultMatcher.hasError; +import static io.openliberty.microprofile.openapi20.test.utils.ValidationResultMatcher.successful; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.equalTo; + +import java.math.BigDecimal; +import java.math.BigInteger; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.eclipse.microprofile.openapi.models.Components; +import org.eclipse.microprofile.openapi.models.media.Schema.SchemaType; +import org.hamcrest.Matcher; +import org.junit.Before; +import org.junit.Test; + +import io.openliberty.microprofile.openapi20.internal.utils.OpenAPIModelWalker.Context; +import io.openliberty.microprofile.openapi20.test.utils.TestValidationContextHelper; +import io.openliberty.microprofile.openapi20.test.utils.TestValidationHelper; +import io.openliberty.microprofile.openapi40.internal.services.validation.Schema31Validator; +import io.smallrye.openapi.api.models.ComponentsImpl; +import io.smallrye.openapi.api.models.OpenAPIImpl; +import io.smallrye.openapi.api.models.media.SchemaImpl; +import io.smallrye.openapi.runtime.io.schema.SchemaConstant; + +/** + * + */ +public class Schema31ValidatorTest { + + private String key; + private OpenAPIImpl model; + private Context context; + private Schema31Validator validator; + private TestValidationHelper vh; + private SchemaImpl testSchema; + + @Before + public void setup() { + testSchema = new SchemaImpl(); + key = "testSchema"; + + Components components = new ComponentsImpl(); + components.addSchema("testTarget", new SchemaImpl()); + components.addSchema(key, testSchema); + + model = new OpenAPIImpl(); + model.components(new ComponentsImpl()); + + context = new TestValidationContextHelper(model); + vh = new TestValidationHelper31(); + + validator = Schema31Validator.getInstance(); + } + + @Test + public void validateComment() { + testSchema.setComment("test"); + validator.validate(vh, context, testSchema); + assertThat(vh.getResult(), successful()); + + vh.resetResults(); + testSchema.set(SchemaConstant.PROP_COMMENT, 3); // Number is not valid + validator.validate(vh, context, testSchema); + assertThat(vh.getResult(), hasError(propNeedsType(SchemaConstant.PROP_COMMENT, "string"))); + } + + @Test + public void validateAllOf() { + testSchema.addAllOf(new SchemaImpl()); + testSchema.addAllOf(new SchemaImpl()); + validator.validate(vh, context, testSchema); + assertThat(vh.getResult(), successful()); + + vh.resetResults(); + testSchema.set(SchemaConstant.PROP_ALL_OF, List.of(new SchemaImpl(), "invalid")); + validator.validate(vh, context, testSchema); + assertThat(vh.getResult(), hasError(collectionNeedsType(SchemaConstant.PROP_ALL_OF, "schema"))); + } + + @Test + public void validateAnyOf() { + testSchema.addAnyOf(new SchemaImpl()); + testSchema.addAnyOf(new SchemaImpl()); + validator.validate(vh, context, testSchema); + assertThat(vh.getResult(), successful()); + + vh.resetResults(); + testSchema.set(SchemaConstant.PROP_ANY_OF, List.of(new SchemaImpl(), "invalid")); + validator.validate(vh, context, testSchema); + assertThat(vh.getResult(), hasError(collectionNeedsType(SchemaConstant.PROP_ANY_OF, "schema"))); + } + + @Test + public void validateOneOf() { + testSchema.addOneOf(new SchemaImpl()); + testSchema.addOneOf(new SchemaImpl()); + validator.validate(vh, context, testSchema); + assertThat(vh.getResult(), successful()); + + vh.resetResults(); + testSchema.set(SchemaConstant.PROP_ONE_OF, List.of(new SchemaImpl(), "invalid")); + validator.validate(vh, context, testSchema); + assertThat(vh.getResult(), hasError(collectionNeedsType(SchemaConstant.PROP_ONE_OF, "schema"))); + } + + @Test + public void validateNot() { + testSchema.not(new SchemaImpl()); + validator.validate(vh, context, testSchema); + assertThat(vh.getResult(), successful()); + + vh.resetResults(); + testSchema.set(SchemaConstant.PROP_NOT, true); // A boolean is a valid schema + validator.validate(vh, context, testSchema); + assertThat(vh.getResult(), successful()); + + vh.resetResults(); + testSchema.set(SchemaConstant.PROP_NOT, new HashMap<>()); // A map can be a valid schema + validator.validate(vh, context, testSchema); + assertThat(vh.getResult(), successful()); + + vh.resetResults(); + testSchema.set(SchemaConstant.PROP_NOT, 7); // A number is not a valid schema + validator.validate(vh, context, testSchema); + assertThat(vh.getResult(), hasError(propNeedsType(SchemaConstant.PROP_NOT, "schema"))); + } + + @Test + public void validateIf() { + testSchema.ifSchema(new SchemaImpl()); + validator.validate(vh, context, testSchema); + assertThat(vh.getResult(), successful()); + + vh.resetResults(); + testSchema.set(SchemaConstant.PROP_IF, 7); // A number is not a valid schema + validator.validate(vh, context, testSchema); + assertThat(vh.getResult(), hasError(propNeedsType(SchemaConstant.PROP_IF, "schema"))); + } + + @Test + public void validateThen() { + testSchema.thenSchema(new SchemaImpl()); + validator.validate(vh, context, testSchema); + assertThat(vh.getResult(), successful()); + + vh.resetResults(); + testSchema.set(SchemaConstant.PROP_THEN, 7); // A number is not a valid schema + validator.validate(vh, context, testSchema); + assertThat(vh.getResult(), hasError(propNeedsType(SchemaConstant.PROP_THEN, "schema"))); + } + + @Test + public void validateElse() { + testSchema.elseSchema(new SchemaImpl()); + validator.validate(vh, context, testSchema); + assertThat(vh.getResult(), successful()); + + vh.resetResults(); + testSchema.set(SchemaConstant.PROP_ELSE, 7); // A number is not a valid schema + validator.validate(vh, context, testSchema); + assertThat(vh.getResult(), hasError(propNeedsType(SchemaConstant.PROP_ELSE, "schema"))); + } + + @Test + public void validateDependentSchemas() { + testSchema.addDependentSchema("test1", new SchemaImpl()); + testSchema.addDependentSchema("test2", new SchemaImpl()); + validator.validate(vh, context, testSchema); + assertThat(vh.getResult(), successful()); + + vh.resetResults(); + testSchema.set(SchemaConstant.PROP_DEPENDENT_SCHEMAS, Map.of("test1", new SchemaImpl(), "test2", 5)); + validator.validate(vh, context, testSchema); + assertThat(vh.getResult(), hasError(collectionNeedsType(SchemaConstant.PROP_DEPENDENT_SCHEMAS, "schema"))); + + vh.resetResults(); + testSchema.set(SchemaConstant.PROP_DEPENDENT_SCHEMAS, List.of(new SchemaImpl(), new SchemaImpl())); + validator.validate(vh, context, testSchema); + assertThat(vh.getResult(), hasError(propNeedsType(SchemaConstant.PROP_DEPENDENT_SCHEMAS, "object"))); + } + + @Test + public void validatePrefixItems() { + testSchema.addPrefixItem(new SchemaImpl().addType(SchemaType.INTEGER)); + testSchema.addPrefixItem(new SchemaImpl().addType(SchemaType.STRING)); + validator.validate(vh, context, testSchema); + assertThat(vh.getResult(), successful()); + + vh.resetResults(); + testSchema.set(SchemaConstant.PROP_PREFIX_ITEMS, new SchemaImpl().addType(SchemaType.INTEGER)); // Not a list + validator.validate(vh, context, testSchema); + assertThat(vh.getResult(), hasError(propNeedsType(SchemaConstant.PROP_PREFIX_ITEMS, "array"))); + + vh.resetResults(); + testSchema.set(SchemaConstant.PROP_PREFIX_ITEMS, List.of(new SchemaImpl(), 7)); // Number is not a schema + validator.validate(vh, context, testSchema); + assertThat(vh.getResult(), hasError(collectionNeedsType(SchemaConstant.PROP_PREFIX_ITEMS, "schema"))); + } + + @Test + public void validateItems() { + testSchema.items(new SchemaImpl()); + validator.validate(vh, context, testSchema); + assertThat(vh.getResult(), successful()); + + vh.resetResults(); + testSchema.set(SchemaConstant.PROP_ITEMS, 7); // A number is not a valid schema + validator.validate(vh, context, testSchema); + assertThat(vh.getResult(), hasError(propNeedsType(SchemaConstant.PROP_ITEMS, "schema"))); + } + + @Test + public void validateContains() { + testSchema.contains(new SchemaImpl()); + validator.validate(vh, context, testSchema); + assertThat(vh.getResult(), successful()); + + vh.resetResults(); + testSchema.set(SchemaConstant.PROP_CONTAINS, 7); // A number is not a valid schema + validator.validate(vh, context, testSchema); + assertThat(vh.getResult(), hasError(propNeedsType(SchemaConstant.PROP_CONTAINS, "schema"))); + } + + @Test + public void validateProperties() { + testSchema.addProperty("test1", new SchemaImpl()); + testSchema.addProperty("test2", new SchemaImpl()); + validator.validate(vh, context, testSchema); + assertThat(vh.getResult(), successful()); + + vh.resetResults(); + testSchema.set(SchemaConstant.PROP_PROPERTIES, Map.of("test1", new SchemaImpl(), "test2", 5)); + validator.validate(vh, context, testSchema); + assertThat(vh.getResult(), hasError(collectionNeedsType(SchemaConstant.PROP_PROPERTIES, "schema"))); + + vh.resetResults(); + testSchema.set(SchemaConstant.PROP_PROPERTIES, List.of(new SchemaImpl(), new SchemaImpl())); + validator.validate(vh, context, testSchema); + assertThat(vh.getResult(), hasError(propNeedsType(SchemaConstant.PROP_PROPERTIES, "object"))); + } + + @Test + public void validatePatternProperties() { + testSchema.addPatternProperty("test1", new SchemaImpl()); + testSchema.addPatternProperty("test2", new SchemaImpl()); + validator.validate(vh, context, testSchema); + assertThat(vh.getResult(), successful()); + + vh.resetResults(); + testSchema.set(SchemaConstant.PROP_PATTERN_PROPERTIES, Map.of("test1", new SchemaImpl(), "test2", 5)); + validator.validate(vh, context, testSchema); + assertThat(vh.getResult(), hasError(collectionNeedsType(SchemaConstant.PROP_PATTERN_PROPERTIES, "schema"))); + + vh.resetResults(); + testSchema.set(SchemaConstant.PROP_PATTERN_PROPERTIES, List.of(new SchemaImpl(), new SchemaImpl())); + validator.validate(vh, context, testSchema); + assertThat(vh.getResult(), hasError(propNeedsType(SchemaConstant.PROP_PATTERN_PROPERTIES, "object"))); + + // TODO: validate keys are regexes? + } + + @SuppressWarnings("deprecation") + @Test + public void validationAdditionalProperties() { + testSchema.additionalPropertiesSchema(new SchemaImpl()); + validator.validate(vh, context, testSchema); + assertThat(vh.getResult(), successful()); + + vh.resetResults(); + testSchema.additionalPropertiesBoolean(false); + validator.validate(vh, context, testSchema); + assertThat(vh.getResult(), successful()); + + testSchema.set(SchemaConstant.PROP_ADDITIONAL_PROPERTIES, 7); // A number is not a valid schema + validator.validate(vh, context, testSchema); + assertThat(vh.getResult(), hasError(propNeedsType(SchemaConstant.PROP_ADDITIONAL_PROPERTIES, "schema"))); + } + + @Test + public void validatePropertyNames() { + testSchema.propertyNames(new SchemaImpl()); + validator.validate(vh, context, testSchema); + assertThat(vh.getResult(), successful()); + + vh.resetResults(); + testSchema.set(SchemaConstant.PROP_PROPERTY_NAMES, 7); // A number is not a valid schema + validator.validate(vh, context, testSchema); + assertThat(vh.getResult(), hasError(propNeedsType(SchemaConstant.PROP_PROPERTY_NAMES, "schema"))); + } + + @Test + public void validateUnevaluatedItems() { + testSchema.unevaluatedItems(new SchemaImpl()); + validator.validate(vh, context, testSchema); + assertThat(vh.getResult(), successful()); + + vh.resetResults(); + testSchema.set(SchemaConstant.PROP_UNEVALUATED_ITEMS, 7); // A number is not a valid schema + validator.validate(vh, context, testSchema); + assertThat(vh.getResult(), hasError(propNeedsType(SchemaConstant.PROP_UNEVALUATED_ITEMS, "schema"))); + } + + @Test + public void validateUnevaluatedProperties() { + testSchema.unevaluatedProperties(new SchemaImpl()); + validator.validate(vh, context, testSchema); + assertThat(vh.getResult(), successful()); + + vh.resetResults(); + testSchema.set(SchemaConstant.PROP_UNEVALUATED_PROPERTIES, 7); // A number is not a valid schema + validator.validate(vh, context, testSchema); + assertThat(vh.getResult(), hasError(propNeedsType(SchemaConstant.PROP_UNEVALUATED_PROPERTIES, "schema"))); + } + + @Test + public void validateType() { + testSchema.type(List.of(SchemaType.NUMBER)); + validator.validate(vh, context, testSchema); + assertThat(vh.getResult(), successful()); + + testSchema.type(List.of(SchemaType.INTEGER)); + validator.validate(vh, context, testSchema); + assertThat(vh.getResult(), successful()); + + vh.resetResults(); + testSchema.type(List.of()); + validator.validate(vh, context, testSchema); + assertThat(vh.getResult(), successful()); + + vh.resetResults(); + testSchema.type(List.of(SchemaType.OBJECT, SchemaType.ARRAY, SchemaType.NULL)); + validator.validate(vh, context, testSchema); + assertThat(vh.getResult(), successful()); + + vh.resetResults(); + testSchema.type(List.of(SchemaType.NULL, SchemaType.BOOLEAN, SchemaType.OBJECT, SchemaType.ARRAY, SchemaType.NUMBER, SchemaType.STRING, SchemaType.INTEGER)); + validator.validate(vh, context, testSchema); + assertThat(vh.getResult(), successful()); + + vh.resetResults(); + testSchema.set(SchemaConstant.PROP_TYPE, "object"); + validator.validate(vh, context, testSchema); + assertThat(vh.getResult(), successful()); + + vh.resetResults(); + testSchema.set(SchemaConstant.PROP_TYPE, new HashMap<>()); + validator.validate(vh, context, testSchema); + assertThat(vh.getResult(), hasError(schemaInvalidType())); + + vh.resetResults(); + testSchema.type(List.of(SchemaType.OBJECT, SchemaType.ARRAY, SchemaType.OBJECT)); + validator.validate(vh, context, testSchema); + assertThat(vh.getResult(), hasError(elementsNotUnique(SchemaConstant.PROP_TYPE))); + + vh.resetResults(); + testSchema.set(SchemaConstant.PROP_TYPE, List.of(SchemaType.OBJECT, 3)); + validator.validate(vh, context, testSchema); + assertThat(vh.getResult(), hasError(schemaInvalidType())); + } + + @Test + public void validateEnum() { + ArrayList enumeration = new ArrayList<>(); + enumeration.add("test"); + enumeration.add(3); + enumeration.add(null); + enumeration.add(List.of()); + enumeration.add(Map.of("key", "value")); + testSchema.enumeration(enumeration); + validator.validate(vh, context, testSchema); + assertThat(vh.getResult(), successful()); + + vh.resetResults(); + testSchema.set(SchemaConstant.PROP_ENUM, 3); + validator.validate(vh, context, testSchema); + assertThat(vh.getResult(), hasError(propNeedsType(SchemaConstant.PROP_ENUM, "array"))); + + vh.resetResults(); + testSchema.enumeration(List.of()); + validator.validate(vh, context, testSchema); + assertThat(vh.getResult(), failedWithEvents().warning(propArrayIsEmpty(SchemaConstant.PROP_ENUM))); + + vh.resetResults(); + testSchema.enumeration(List.of("test", "test")); + validator.validate(vh, context, testSchema); + assertThat(vh.getResult(), failedWithEvents().warning(elementsNotUnique(SchemaConstant.PROP_ENUM))); + } + + @Test + public void validateConst() { + // Any value is valid in const + testSchema.constValue("test"); + validator.validate(vh, context, testSchema); + assertThat(vh.getResult(), successful()); + + vh.resetResults(); + testSchema.constValue(3); + validator.validate(vh, context, testSchema); + assertThat(vh.getResult(), successful()); + + vh.resetResults(); + testSchema.constValue(List.of(1, 2, "three", List.of(4), 5.0D)); + validator.validate(vh, context, testSchema); + assertThat(vh.getResult(), successful()); + } + + @Test + public void validateMultipleOf() { + testSchema.multipleOf(new BigDecimal("4.5")); + validator.validate(vh, context, testSchema); + assertThat(vh.getResult(), successful()); + + vh.resetResults(); + testSchema.multipleOf(new BigDecimal("0")); + validator.validate(vh, context, testSchema); + assertThat(vh.getResult(), hasError(propLessThanEqualZero(SchemaConstant.PROP_MULTIPLE_OF))); + + vh.resetResults(); + testSchema.multipleOf(new BigDecimal("-1")); + validator.validate(vh, context, testSchema); + assertThat(vh.getResult(), hasError(propLessThanEqualZero(SchemaConstant.PROP_MULTIPLE_OF))); + + vh.resetResults(); + testSchema.multipleOf(new BigDecimal("0.2")); + validator.validate(vh, context, testSchema); + assertThat(vh.getResult(), successful()); + + List validValues = List.of(new BigDecimal("4.5"), + new BigDecimal("0.2"), + 5, 5L, 5.5f, 5.5d, new BigInteger("5")); + for (Object value : validValues) { + vh.resetResults(); + testSchema.set(SchemaConstant.PROP_MULTIPLE_OF, value); + validator.validate(vh, context, testSchema); + assertThat("For value: " + value, vh.getResult(), successful()); + } + + List lteZeroValues = List.of(BigDecimal.ZERO, 0, 0L, 0.0f, 0.0d, BigInteger.ZERO, + new BigDecimal("-1"), -1, -1L, -1.5f, -1.5d, new BigInteger("-5")); + + for (Object value : lteZeroValues) { + vh.resetResults(); + testSchema.set(SchemaConstant.PROP_MULTIPLE_OF, value); + validator.validate(vh, context, testSchema); + assertThat("For value: " + value, vh.getResult(), hasError(propLessThanEqualZero(SchemaConstant.PROP_MULTIPLE_OF))); + } + + vh.resetResults(); + testSchema.set(SchemaConstant.PROP_MULTIPLE_OF, "five"); + validator.validate(vh, context, testSchema); + assertThat(vh.getResult(), hasError(propNeedsType(SchemaConstant.PROP_MULTIPLE_OF, "number"))); + + vh.resetResults(); + testSchema.set(SchemaConstant.PROP_MULTIPLE_OF, List.of(3, 5)); + validator.validate(vh, context, testSchema); + assertThat(vh.getResult(), hasError(propNeedsType(SchemaConstant.PROP_MULTIPLE_OF, "number"))); + } + + @Test + public void validateMaximum() { + testSchema.maximum(new BigDecimal("99999999999999999999999999")); // Large number to ensure we're not casting to int anywhere + validator.validate(vh, context, testSchema); + assertThat(vh.getResult(), successful()); + + vh.resetResults(); + testSchema.maximum(new BigDecimal("-99999999999999999999999999")); + validator.validate(vh, context, testSchema); + assertThat(vh.getResult(), successful()); + + vh.resetResults(); + testSchema.maximum(BigDecimal.ZERO); // Large number to ensure we're not casting to int anywhere + validator.validate(vh, context, testSchema); + assertThat(vh.getResult(), successful()); + + List validValues = List.of(new BigDecimal("4.5"), + new BigDecimal("0.2"), + 5, 5L, 5.5f, 5.5d, new BigInteger("5")); + for (Object value : validValues) { + vh.resetResults(); + testSchema.set(SchemaConstant.PROP_MAXIMUM, value); + validator.validate(vh, context, testSchema); + assertThat("For value: " + value, vh.getResult(), successful()); + } + + List badTypeValues = List.of("test", Map.of(), List.of(), true); + for (Object value : badTypeValues) { + vh.resetResults(); + testSchema.set(SchemaConstant.PROP_MAXIMUM, value); + validator.validate(vh, context, testSchema); + assertThat("For value: " + value, vh.getResult(), hasError(propNeedsType(SchemaConstant.PROP_MAXIMUM, "number"))); + } + } + + @Test + public void validateExclusiveMaximum() { + testSchema.exclusiveMaximum(new BigDecimal("99999999999999999999999999")); // Large number to ensure we're not casting to int anywhere + validator.validate(vh, context, testSchema); + assertThat(vh.getResult(), successful()); + + vh.resetResults(); + testSchema.exclusiveMaximum(new BigDecimal("-99999999999999999999999999")); + validator.validate(vh, context, testSchema); + assertThat(vh.getResult(), successful()); + + vh.resetResults(); + testSchema.exclusiveMaximum(BigDecimal.ZERO); // Large number to ensure we're not casting to int anywhere + validator.validate(vh, context, testSchema); + assertThat(vh.getResult(), successful()); + + List validValues = List.of(new BigDecimal("4.5"), + new BigDecimal("0.2"), + 5, 5L, 5.5f, 5.5d, new BigInteger("5")); + for (Object value : validValues) { + vh.resetResults(); + testSchema.set(SchemaConstant.PROP_EXCLUSIVE_MAXIMUM, value); + validator.validate(vh, context, testSchema); + assertThat("For value: " + value, vh.getResult(), successful()); + } + + List badTypeValues = List.of("test", Map.of(), List.of(), true); + for (Object value : badTypeValues) { + vh.resetResults(); + testSchema.set(SchemaConstant.PROP_EXCLUSIVE_MAXIMUM, value); + validator.validate(vh, context, testSchema); + assertThat("For value: " + value, vh.getResult(), hasError(propNeedsType(SchemaConstant.PROP_EXCLUSIVE_MAXIMUM, "number"))); + } + } + + @Test + public void validateMinimum() { + testSchema.minimum(new BigDecimal("99999999999999999999999999")); // Large number to ensure we're not casting to int anywhere + validator.validate(vh, context, testSchema); + assertThat(vh.getResult(), successful()); + + vh.resetResults(); + testSchema.minimum(new BigDecimal("-99999999999999999999999999")); + validator.validate(vh, context, testSchema); + assertThat(vh.getResult(), successful()); + + vh.resetResults(); + testSchema.minimum(BigDecimal.ZERO); // Large number to ensure we're not casting to int anywhere + validator.validate(vh, context, testSchema); + assertThat(vh.getResult(), successful()); + + List validValues = List.of(new BigDecimal("4.5"), + new BigDecimal("0.2"), + 5, 5L, 5.5f, 5.5d, new BigInteger("5")); + for (Object value : validValues) { + vh.resetResults(); + testSchema.set(SchemaConstant.PROP_MINIMUM, value); + validator.validate(vh, context, testSchema); + assertThat("For value: " + value, vh.getResult(), successful()); + } + + List badTypeValues = List.of("test", Map.of(), List.of(), true); + for (Object value : badTypeValues) { + vh.resetResults(); + testSchema.set(SchemaConstant.PROP_MINIMUM, value); + validator.validate(vh, context, testSchema); + assertThat("For value: " + value, vh.getResult(), hasError(propNeedsType(SchemaConstant.PROP_MINIMUM, "number"))); + } + } + + @Test + public void validateExclusiveMinimum() { + testSchema.exclusiveMinimum(new BigDecimal("99999999999999999999999999")); // Large number to ensure we're not casting to int anywhere + validator.validate(vh, context, testSchema); + assertThat(vh.getResult(), successful()); + + vh.resetResults(); + testSchema.exclusiveMinimum(new BigDecimal("-99999999999999999999999999")); + validator.validate(vh, context, testSchema); + assertThat(vh.getResult(), successful()); + + vh.resetResults(); + testSchema.exclusiveMinimum(BigDecimal.ZERO); // Large number to ensure we're not casting to int anywhere + validator.validate(vh, context, testSchema); + assertThat(vh.getResult(), successful()); + + List validValues = List.of(new BigDecimal("4.5"), + new BigDecimal("0.2"), + 5, 5L, 5.5f, 5.5d, new BigInteger("5")); + for (Object value : validValues) { + vh.resetResults(); + testSchema.set(SchemaConstant.PROP_EXCLUSIVE_MINIMUM, value); + validator.validate(vh, context, testSchema); + assertThat("For value: " + value, vh.getResult(), successful()); + } + + List badTypeValues = List.of("test", Map.of(), List.of(), true); + for (Object value : badTypeValues) { + vh.resetResults(); + testSchema.set(SchemaConstant.PROP_EXCLUSIVE_MINIMUM, value); + validator.validate(vh, context, testSchema); + assertThat("For value: " + value, vh.getResult(), hasError(propNeedsType(SchemaConstant.PROP_EXCLUSIVE_MINIMUM, "number"))); + } + } + + @Test + public void validateMaxLength() { + testSchema.maxLength(99999); + validator.validate(vh, context, testSchema); + assertThat(vh.getResult(), successful()); + + vh.resetResults(); + testSchema.maxLength(0); + validator.validate(vh, context, testSchema); + assertThat(vh.getResult(), successful()); + + vh.resetResults(); + testSchema.maxLength(-1); + validator.validate(vh, context, testSchema); + assertThat(vh.getResult(), hasError(propLessThanZero(SchemaConstant.PROP_MAX_LENGTH))); + + List validValues = List.of(0, 0L, (short) 0, BigInteger.ZERO, + 20, 20L, (short) 20, new BigInteger("20")); + for (Object value : validValues) { + vh.resetResults(); + testSchema.set(SchemaConstant.PROP_MAX_LENGTH, value); + validator.validate(vh, context, testSchema); + assertThat("For value: " + value, vh.getResult(), successful()); + } + + List negativeValues = List.of(-1, -1L, (short) -1, new BigInteger("-1")); + for (Object value : negativeValues) { + vh.resetResults(); + testSchema.set(SchemaConstant.PROP_MAX_LENGTH, value); + validator.validate(vh, context, testSchema); + assertThat("For value: " + value, vh.getResult(), hasError(propLessThanZero(SchemaConstant.PROP_MAX_LENGTH))); + } + + List badTypeValues = List.of(0.5f, 0.5d, "test", Map.of(), List.of(), true, new BigDecimal("0.5")); + for (Object value : badTypeValues) { + vh.resetResults(); + testSchema.set(SchemaConstant.PROP_MAX_LENGTH, value); + validator.validate(vh, context, testSchema); + assertThat("For value: " + value, vh.getResult(), hasError(propNeedsType(SchemaConstant.PROP_MAX_LENGTH, "integer"))); + } + } + + @Test + public void validateMinLength() { + testSchema.minLength(99999); + validator.validate(vh, context, testSchema); + assertThat(vh.getResult(), successful()); + + vh.resetResults(); + testSchema.minLength(0); + validator.validate(vh, context, testSchema); + assertThat(vh.getResult(), successful()); + + vh.resetResults(); + testSchema.minLength(-1); + validator.validate(vh, context, testSchema); + assertThat(vh.getResult(), hasError(propLessThanZero(SchemaConstant.PROP_MIN_LENGTH))); + + List validValues = List.of(0, 0L, (short) 0, BigInteger.ZERO, + 20, 20L, (short) 20, new BigInteger("20")); + for (Object value : validValues) { + vh.resetResults(); + testSchema.set(SchemaConstant.PROP_MIN_LENGTH, value); + validator.validate(vh, context, testSchema); + assertThat("For value: " + value, vh.getResult(), successful()); + } + + List negativeValues = List.of(-1, -1L, (short) -1, new BigInteger("-1")); + for (Object value : negativeValues) { + vh.resetResults(); + testSchema.set(SchemaConstant.PROP_MIN_LENGTH, value); + validator.validate(vh, context, testSchema); + assertThat("For value: " + value, vh.getResult(), hasError(propLessThanZero(SchemaConstant.PROP_MIN_LENGTH))); + } + + List badTypeValues = List.of(0.5f, 0.5d, "test", Map.of(), List.of(), true, new BigDecimal("0.5")); + for (Object value : badTypeValues) { + vh.resetResults(); + testSchema.set(SchemaConstant.PROP_MIN_LENGTH, value); + validator.validate(vh, context, testSchema); + assertThat("For value: " + value, vh.getResult(), hasError(propNeedsType(SchemaConstant.PROP_MIN_LENGTH, "integer"))); + } + } + + @Test + public void validateRequired() { + testSchema.required(List.of()); + validator.validate(vh, context, testSchema); + assertThat(vh.getResult(), successful()); + + vh.resetResults(); + testSchema.required(List.of("test")); + validator.validate(vh, context, testSchema); + assertThat(vh.getResult(), successful()); + + vh.resetResults(); + testSchema.required(List.of("test1", "test2")); + validator.validate(vh, context, testSchema); + assertThat(vh.getResult(), successful()); + + vh.resetResults(); + testSchema.set(SchemaConstant.PROP_REQUIRED, List.of("test1", "test2")); + validator.validate(vh, context, testSchema); + assertThat(vh.getResult(), successful()); + + vh.resetResults(); + testSchema.required(List.of("test1", "test1")); + validator.validate(vh, context, testSchema); + assertThat(vh.getResult(), hasError(elementsNotUnique(SchemaConstant.PROP_REQUIRED))); + + List badTypeValues = List.of(3, "test", true, Map.of()); + for (Object value : badTypeValues) { + vh.resetResults(); + testSchema.set(SchemaConstant.PROP_REQUIRED, value); + validator.validate(vh, context, testSchema); + assertThat("For value: " + value, vh.getResult(), hasError(propNeedsType(SchemaConstant.PROP_REQUIRED, "array"))); + } + + List badElementValues = List.of(3, true, Map.of(), List.of()); + for (Object value : badElementValues) { + vh.resetResults(); + testSchema.set(SchemaConstant.PROP_REQUIRED, List.of("test", value)); + validator.validate(vh, context, testSchema); + assertThat("For value: " + value, vh.getResult(), hasError(collectionNeedsType(SchemaConstant.PROP_REQUIRED, "string"))); + } + } + + @Test + public void validateDependentRequired() { + testSchema.dependentRequired(Map.of()); + validator.validate(vh, context, testSchema); + assertThat(vh.getResult(), successful()); + + vh.resetResults(); + testSchema.dependentRequired(Map.of("test1", List.of("test2", "test3"), + "test4", List.of("test5"), + "test6", List.of())); + validator.validate(vh, context, testSchema); + assertThat(vh.getResult(), successful()); + + vh.resetResults(); + testSchema.dependentRequired(Map.of("test1", List.of("test2", "test2"))); + validator.validate(vh, context, testSchema); + assertThat(vh.getResult(), hasError(dependentRequiredNotUniqueStrings())); + + List badTypeValues = List.of(3, "test", true, List.of()); + for (Object value : badTypeValues) { + vh.resetResults(); + testSchema.set(SchemaConstant.PROP_DEPENDENT_REQUIRED, value); + validator.validate(vh, context, testSchema); + assertThat("For value: " + value, vh.getResult(), hasError(propNeedsType(SchemaConstant.PROP_DEPENDENT_REQUIRED, "object"))); + } + + List badElementValues = List.of("test", 3, true, Map.of()); + for (Object value : badElementValues) { + vh.resetResults(); + testSchema.set(SchemaConstant.PROP_DEPENDENT_REQUIRED, Map.of("test", value)); + validator.validate(vh, context, testSchema); + assertThat("For value: " + value, vh.getResult(), hasError(collectionNeedsType(SchemaConstant.PROP_DEPENDENT_REQUIRED, "array"))); + } + + List badListValues = List.of(3, true, Map.of(), List.of()); + for (Object value : badListValues) { + vh.resetResults(); + testSchema.set(SchemaConstant.PROP_DEPENDENT_REQUIRED, Map.of("test", List.of(value))); + validator.validate(vh, context, testSchema); + assertThat("For value: " + value, vh.getResult(), hasError(dependentRequiredNotUniqueStrings())); + } + } + + @Test + public void validateFormat() { + testSchema.format("test"); + validator.validate(vh, context, testSchema); + assertThat(vh.getResult(), successful()); + + List badTypeValues = List.of(3, 7.0d, true, List.of(), Map.of()); + for (Object value : badTypeValues) { + vh.resetResults(); + testSchema.set(SchemaConstant.PROP_FORMAT, value); + validator.validate(vh, context, testSchema); + assertThat("For value: " + value, vh.getResult(), hasError(propNeedsType(SchemaConstant.PROP_FORMAT, "string"))); + } + } + + @Test + public void validateContentEncoding() { + testSchema.contentEncoding("test"); + validator.validate(vh, context, testSchema); + assertThat(vh.getResult(), successful()); + + List badTypeValues = List.of(3, 7.0d, true, List.of(), Map.of()); + for (Object value : badTypeValues) { + vh.resetResults(); + testSchema.set(SchemaConstant.PROP_CONTENT_ENCODING, value); + validator.validate(vh, context, testSchema); + assertThat("For value: " + value, vh.getResult(), hasError(propNeedsType(SchemaConstant.PROP_CONTENT_ENCODING, "string"))); + } + } + + @Test + public void validateContentMediaType() { + testSchema.contentMediaType("test"); + validator.validate(vh, context, testSchema); + assertThat(vh.getResult(), successful()); + + List badTypeValues = List.of(3, 7.0d, true, List.of(), Map.of()); + for (Object value : badTypeValues) { + vh.resetResults(); + testSchema.set(SchemaConstant.PROP_CONTENT_MEDIA_TYPE, value); + validator.validate(vh, context, testSchema); + assertThat("For value: " + value, vh.getResult(), hasError(propNeedsType(SchemaConstant.PROP_CONTENT_MEDIA_TYPE, "string"))); + } + } + + @Test + public void validateContentSchema() { + testSchema.contentSchema(new SchemaImpl()); + validator.validate(vh, context, testSchema); + assertThat(vh.getResult(), successful()); + + vh.resetResults(); + testSchema.set(SchemaConstant.PROP_CONTENT_SCHEMA, true); // A boolean is a valid schema + validator.validate(vh, context, testSchema); + assertThat(vh.getResult(), successful()); + + vh.resetResults(); + testSchema.set(SchemaConstant.PROP_CONTENT_SCHEMA, new HashMap<>()); // A map can be a valid schema + validator.validate(vh, context, testSchema); + assertThat(vh.getResult(), successful()); + + vh.resetResults(); + testSchema.set(SchemaConstant.PROP_CONTENT_SCHEMA, 7); // A number is not a valid schema + validator.validate(vh, context, testSchema); + assertThat(vh.getResult(), hasError(propNeedsType(SchemaConstant.PROP_CONTENT_SCHEMA, "schema"))); + } + + @Test + public void validateTitle() { + testSchema.title("test"); + validator.validate(vh, context, testSchema); + assertThat(vh.getResult(), successful()); + + List badTypeValues = List.of(3, 7.0d, true, List.of(), Map.of()); + for (Object value : badTypeValues) { + vh.resetResults(); + testSchema.set(SchemaConstant.PROP_TITLE, value); + validator.validate(vh, context, testSchema); + assertThat("For value: " + value, vh.getResult(), hasError(propNeedsType(SchemaConstant.PROP_TITLE, "string"))); + } + } + + @Test + public void validateDescription() { + testSchema.description("test"); + validator.validate(vh, context, testSchema); + assertThat(vh.getResult(), successful()); + + List badTypeValues = List.of(3, 7.0d, true, List.of(), Map.of()); + for (Object value : badTypeValues) { + vh.resetResults(); + testSchema.set(SchemaConstant.PROP_DESCRIPTION, value); + validator.validate(vh, context, testSchema); + assertThat("For value: " + value, vh.getResult(), hasError(propNeedsType(SchemaConstant.PROP_DESCRIPTION, "string"))); + } + } + + @Test + public void validateDeprecated() { + testSchema.deprecated(true); + validator.validate(vh, context, testSchema); + assertThat(vh.getResult(), successful()); + + List badTypeValues = List.of(3, 7.0d, "test", List.of(), Map.of()); + for (Object value : badTypeValues) { + vh.resetResults(); + testSchema.set(SchemaConstant.PROP_DEPRECATED, value); + validator.validate(vh, context, testSchema); + assertThat("For value: " + value, vh.getResult(), hasError(propNeedsType(SchemaConstant.PROP_DEPRECATED, "boolean"))); + } + } + + @Test + public void validateReadOnly() { + testSchema.readOnly(true); + validator.validate(vh, context, testSchema); + assertThat(vh.getResult(), successful()); + + List badTypeValues = List.of(3, 7.0d, "test", List.of(), Map.of()); + for (Object value : badTypeValues) { + vh.resetResults(); + testSchema.set(SchemaConstant.PROP_READ_ONLY, value); + validator.validate(vh, context, testSchema); + assertThat("For value: " + value, vh.getResult(), hasError(propNeedsType(SchemaConstant.PROP_READ_ONLY, "boolean"))); + } + } + + @Test + public void validateWriteOnly() { + testSchema.writeOnly(true); + validator.validate(vh, context, testSchema); + assertThat(vh.getResult(), successful()); + + List badTypeValues = List.of(3, 7.0d, "test", List.of(), Map.of()); + for (Object value : badTypeValues) { + vh.resetResults(); + testSchema.set(SchemaConstant.PROP_WRITE_ONLY, value); + validator.validate(vh, context, testSchema); + assertThat("For value: " + value, vh.getResult(), hasError(propNeedsType(SchemaConstant.PROP_WRITE_ONLY, "boolean"))); + } + } + + @Test + public void validateExamples() { + testSchema.examples(List.of("test", 3, Map.of("abc", "xyz"))); + validator.validate(vh, context, testSchema); + assertThat(vh.getResult(), successful()); + + List badTypeValues = List.of(3, 7.0d, "test", false, Map.of()); + for (Object value : badTypeValues) { + vh.resetResults(); + testSchema.set(SchemaConstant.PROP_EXAMPLES, value); + validator.validate(vh, context, testSchema); + assertThat("For value: " + value, vh.getResult(), hasError(propNeedsType(SchemaConstant.PROP_EXAMPLES, "array"))); + } + + } + + private Matcher propNeedsType(String prop, String type) { + return equalTo("The type of the \"" + prop + "\" property of the Schema Object must be \"" + type + "\""); + } + + private Matcher collectionNeedsType(String propName, String type) { + return equalTo("The \"" + propName + "\" property of the Schema Object contains a value whose type is not \"" + type + "\""); + } + + private Matcher elementsNotUnique(String propName) { + return equalTo("The elements of the \"" + propName + "\" property of the Schema Object are not unique"); + } + + private Matcher schemaInvalidType() { + return equalTo("The \"type\" property of the Schema Object must contain values that are one of (\"null\", \"boolean\", \"object\", \"array\", \"number\", \"integer\", or \"string\")"); + } + + private Matcher propArrayIsEmpty(String propName) { + return equalTo("The \"" + propName + "\" property of the Schema Object is an empty array"); + } + + private Matcher propLessThanEqualZero(String propName) { + return equalTo("The Schema Object must have the \"" + propName + "\" property set to a number strictly greater than zero"); + } + + private Matcher propLessThanZero(String propName) { + return equalTo("The \"" + propName + "\" property of the Schema Object must be greater than or equal to zero"); + } + + private Matcher dependentRequiredNotUniqueStrings() { + return equalTo("Each element of the \"dependentRequired\" property must be an array of unique strings"); + } + +} diff --git a/dev/io.openliberty.microprofile.openapi.4.0.internal.services/test/io/openliberty/microprofile/openapi40/internal/services/validation/test/ServerVariable31ValidatorTest.java b/dev/io.openliberty.microprofile.openapi.4.0.internal.services/test/io/openliberty/microprofile/openapi40/internal/services/validation/test/ServerVariable31ValidatorTest.java new file mode 100644 index 00000000000..de75f330109 --- /dev/null +++ b/dev/io.openliberty.microprofile.openapi.4.0.internal.services/test/io/openliberty/microprofile/openapi40/internal/services/validation/test/ServerVariable31ValidatorTest.java @@ -0,0 +1,60 @@ +/******************************************************************************* + * Copyright (c) 2024 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package io.openliberty.microprofile.openapi40.internal.services.validation.test; + +import static io.openliberty.microprofile.openapi20.test.utils.ValidationResultMatcher.hasError; +import static java.util.Collections.emptyList; +import static org.junit.Assert.assertThat; + +import java.util.List; + +import org.eclipse.microprofile.openapi.models.servers.ServerVariable; +import org.junit.Test; + +import io.openliberty.microprofile.openapi20.internal.validation.TypeValidator; +import io.openliberty.microprofile.openapi20.test.utils.TestValidationHelper; +import io.openliberty.microprofile.openapi20.validation.test.ServerVariableValidatorTest; +import io.openliberty.microprofile.openapi40.internal.services.validation.ServerVariable31Validator; +import io.smallrye.openapi.api.models.servers.ServerVariableImpl; + +public class ServerVariable31ValidatorTest extends ServerVariableValidatorTest { + + @Override + protected TypeValidator getValidator() { + return ServerVariable31Validator.getInstance(); + } + + @Test + public void testEmptyEnum() { + TypeValidator validator = getValidator(); + TestValidationHelper validationHelper = new TestValidationHelper(); + + ServerVariableImpl serverVariable = new ServerVariableImpl(); + serverVariable.defaultValue("foo") + .enumeration(emptyList()); + + validator.validate(validationHelper, context, serverVariable); + assertThat(validationHelper.getResult(), hasError("The \"enum\" array in the Server Variable Object is empty")); + } + + @Test + public void testDefaultMissingFromEnum() { + TypeValidator validator = getValidator(); + TestValidationHelper validationHelper = new TestValidationHelper(); + + ServerVariableImpl serverVariable = new ServerVariableImpl(); + serverVariable.defaultValue("foo") + .enumeration(List.of("bar", "baz", "qux")); + + validator.validate(validationHelper, context, serverVariable); + assertThat(validationHelper.getResult(), hasError("The \"foo\" value of the \"default\" property is not listed in the \"enum\" array")); + } + +} diff --git a/dev/io.openliberty.microprofile.openapi.4.0.internal.services/test/io/openliberty/microprofile/openapi40/internal/services/validation/test/TestValidationHelper31.java b/dev/io.openliberty.microprofile.openapi.4.0.internal.services/test/io/openliberty/microprofile/openapi40/internal/services/validation/test/TestValidationHelper31.java new file mode 100644 index 00000000000..dc7a6d739c6 --- /dev/null +++ b/dev/io.openliberty.microprofile.openapi.4.0.internal.services/test/io/openliberty/microprofile/openapi40/internal/services/validation/test/TestValidationHelper31.java @@ -0,0 +1,26 @@ +/******************************************************************************* + * Copyright (c) 2024 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package io.openliberty.microprofile.openapi40.internal.services.validation.test; + +import io.openliberty.microprofile.openapi20.internal.utils.OpenAPIModelWalker.Context; +import io.openliberty.microprofile.openapi20.test.utils.TestValidationHelper; +import io.openliberty.microprofile.openapi40.internal.services.validation.Reference31Validator; + +/** + * + */ +public class TestValidationHelper31 extends TestValidationHelper { + + @Override + public Object validateReference(Context context, String key, String ref) { + return Reference31Validator.getInstance().validate(this, context, ref); + } + +} diff --git a/dev/io.openliberty.microprofile.openapi.4.0.internal_fat/.classpath b/dev/io.openliberty.microprofile.openapi.4.0.internal_fat/.classpath new file mode 100644 index 00000000000..61e710c1a5e --- /dev/null +++ b/dev/io.openliberty.microprofile.openapi.4.0.internal_fat/.classpath @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/dev/io.openliberty.microprofile.openapi.4.0.internal_fat/.project b/dev/io.openliberty.microprofile.openapi.4.0.internal_fat/.project new file mode 100644 index 00000000000..926477e610b --- /dev/null +++ b/dev/io.openliberty.microprofile.openapi.4.0.internal_fat/.project @@ -0,0 +1,23 @@ + + + io.openliberty.microprofile.openapi.4.0.internal_fat + + + + + + org.eclipse.jdt.core.javabuilder + + + + + bndtools.core.bndbuilder + + + + + + org.eclipse.jdt.core.javanature + bndtools.core.bndnature + + diff --git a/dev/io.openliberty.microprofile.openapi.4.0.internal_fat/.settings/bndtools.core.prefs b/dev/io.openliberty.microprofile.openapi.4.0.internal_fat/.settings/bndtools.core.prefs new file mode 100644 index 00000000000..789fa99991a --- /dev/null +++ b/dev/io.openliberty.microprofile.openapi.4.0.internal_fat/.settings/bndtools.core.prefs @@ -0,0 +1,2 @@ +compileErrorAction=build +eclipse.preferences.version=1 diff --git a/dev/io.openliberty.microprofile.openapi.4.0.internal_fat/.settings/org.eclipse.core.resources.prefs b/dev/io.openliberty.microprofile.openapi.4.0.internal_fat/.settings/org.eclipse.core.resources.prefs new file mode 100644 index 00000000000..1554c6f848a --- /dev/null +++ b/dev/io.openliberty.microprofile.openapi.4.0.internal_fat/.settings/org.eclipse.core.resources.prefs @@ -0,0 +1,2 @@ +eclipse.preferences.version=1 +encoding/bnd.bnd=UTF-8 diff --git a/dev/io.openliberty.microprofile.openapi.4.0.internal_fat/.settings/org.eclipse.jdt.core.prefs b/dev/io.openliberty.microprofile.openapi.4.0.internal_fat/.settings/org.eclipse.jdt.core.prefs new file mode 100644 index 00000000000..0a9789f208d --- /dev/null +++ b/dev/io.openliberty.microprofile.openapi.4.0.internal_fat/.settings/org.eclipse.jdt.core.prefs @@ -0,0 +1,412 @@ +eclipse.preferences.version=1 +org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled +org.eclipse.jdt.core.compiler.codegen.targetPlatform=11 +org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve +org.eclipse.jdt.core.compiler.compliance=11 +org.eclipse.jdt.core.compiler.debug.lineNumber=generate +org.eclipse.jdt.core.compiler.debug.localVariable=generate +org.eclipse.jdt.core.compiler.debug.sourceFile=generate +org.eclipse.jdt.core.compiler.problem.assertIdentifier=error +org.eclipse.jdt.core.compiler.problem.enumIdentifier=error +org.eclipse.jdt.core.compiler.release=disabled +org.eclipse.jdt.core.compiler.source=11 +org.eclipse.jdt.core.formatter.align_arrows_in_switch_on_columns=false +org.eclipse.jdt.core.formatter.align_assignment_statements_on_columns=false +org.eclipse.jdt.core.formatter.align_fields_grouping_blank_lines=2147483647 +org.eclipse.jdt.core.formatter.align_selector_in_method_invocation_on_expression_first_line=false +org.eclipse.jdt.core.formatter.align_type_members_on_columns=false +org.eclipse.jdt.core.formatter.align_variable_declarations_on_columns=false +org.eclipse.jdt.core.formatter.align_with_spaces=false +org.eclipse.jdt.core.formatter.alignment_for_additive_operator=18 +org.eclipse.jdt.core.formatter.alignment_for_annotations_on_enum_constant=49 +org.eclipse.jdt.core.formatter.alignment_for_annotations_on_field=49 +org.eclipse.jdt.core.formatter.alignment_for_annotations_on_local_variable=49 +org.eclipse.jdt.core.formatter.alignment_for_annotations_on_method=49 +org.eclipse.jdt.core.formatter.alignment_for_annotations_on_package=49 +org.eclipse.jdt.core.formatter.alignment_for_annotations_on_parameter=0 +org.eclipse.jdt.core.formatter.alignment_for_annotations_on_type=49 +org.eclipse.jdt.core.formatter.alignment_for_arguments_in_allocation_expression=18 +org.eclipse.jdt.core.formatter.alignment_for_arguments_in_annotation=18 +org.eclipse.jdt.core.formatter.alignment_for_arguments_in_enum_constant=2 +org.eclipse.jdt.core.formatter.alignment_for_arguments_in_explicit_constructor_call=18 +org.eclipse.jdt.core.formatter.alignment_for_arguments_in_method_invocation=18 +org.eclipse.jdt.core.formatter.alignment_for_arguments_in_qualified_allocation_expression=18 +org.eclipse.jdt.core.formatter.alignment_for_assertion_message=0 +org.eclipse.jdt.core.formatter.alignment_for_assignment=0 +org.eclipse.jdt.core.formatter.alignment_for_bitwise_operator=18 +org.eclipse.jdt.core.formatter.alignment_for_compact_if=16 +org.eclipse.jdt.core.formatter.alignment_for_compact_loops=16 +org.eclipse.jdt.core.formatter.alignment_for_conditional_expression=0 +org.eclipse.jdt.core.formatter.alignment_for_conditional_expression_chain=0 +org.eclipse.jdt.core.formatter.alignment_for_enum_constants=16 +org.eclipse.jdt.core.formatter.alignment_for_expressions_in_array_initializer=18 +org.eclipse.jdt.core.formatter.alignment_for_expressions_in_for_loop_header=0 +org.eclipse.jdt.core.formatter.alignment_for_expressions_in_switch_case_with_arrow=0 +org.eclipse.jdt.core.formatter.alignment_for_expressions_in_switch_case_with_colon=0 +org.eclipse.jdt.core.formatter.alignment_for_logical_operator=18 +org.eclipse.jdt.core.formatter.alignment_for_method_declaration=0 +org.eclipse.jdt.core.formatter.alignment_for_module_statements=16 +org.eclipse.jdt.core.formatter.alignment_for_multiple_fields=16 +org.eclipse.jdt.core.formatter.alignment_for_multiplicative_operator=18 +org.eclipse.jdt.core.formatter.alignment_for_parameterized_type_references=0 +org.eclipse.jdt.core.formatter.alignment_for_parameters_in_constructor_declaration=18 +org.eclipse.jdt.core.formatter.alignment_for_parameters_in_method_declaration=18 +org.eclipse.jdt.core.formatter.alignment_for_permitted_types_in_type_declaration=0 +org.eclipse.jdt.core.formatter.alignment_for_record_components=18 +org.eclipse.jdt.core.formatter.alignment_for_relational_operator=0 +org.eclipse.jdt.core.formatter.alignment_for_resources_in_try=80 +org.eclipse.jdt.core.formatter.alignment_for_selector_in_method_invocation=18 +org.eclipse.jdt.core.formatter.alignment_for_shift_operator=0 +org.eclipse.jdt.core.formatter.alignment_for_string_concatenation=18 +org.eclipse.jdt.core.formatter.alignment_for_superclass_in_type_declaration=0 +org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_enum_declaration=20 +org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_record_declaration=0 +org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_type_declaration=0 +org.eclipse.jdt.core.formatter.alignment_for_switch_case_with_arrow=0 +org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_constructor_declaration=4 +org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_method_declaration=0 +org.eclipse.jdt.core.formatter.alignment_for_type_annotations=0 +org.eclipse.jdt.core.formatter.alignment_for_type_arguments=0 +org.eclipse.jdt.core.formatter.alignment_for_type_parameters=0 +org.eclipse.jdt.core.formatter.alignment_for_union_type_in_multicatch=16 +org.eclipse.jdt.core.formatter.blank_lines_after_imports=1 +org.eclipse.jdt.core.formatter.blank_lines_after_last_class_body_declaration=0 +org.eclipse.jdt.core.formatter.blank_lines_after_package=1 +org.eclipse.jdt.core.formatter.blank_lines_before_abstract_method=1 +org.eclipse.jdt.core.formatter.blank_lines_before_field=0 +org.eclipse.jdt.core.formatter.blank_lines_before_first_class_body_declaration=0 +org.eclipse.jdt.core.formatter.blank_lines_before_imports=1 +org.eclipse.jdt.core.formatter.blank_lines_before_member_type=1 +org.eclipse.jdt.core.formatter.blank_lines_before_method=1 +org.eclipse.jdt.core.formatter.blank_lines_before_new_chunk=1 +org.eclipse.jdt.core.formatter.blank_lines_before_package=0 +org.eclipse.jdt.core.formatter.blank_lines_between_import_groups=1 +org.eclipse.jdt.core.formatter.blank_lines_between_statement_group_in_switch=0 +org.eclipse.jdt.core.formatter.blank_lines_between_type_declarations=1 +org.eclipse.jdt.core.formatter.brace_position_for_annotation_type_declaration=end_of_line +org.eclipse.jdt.core.formatter.brace_position_for_anonymous_type_declaration=end_of_line +org.eclipse.jdt.core.formatter.brace_position_for_array_initializer=end_of_line +org.eclipse.jdt.core.formatter.brace_position_for_block=end_of_line +org.eclipse.jdt.core.formatter.brace_position_for_block_in_case=end_of_line +org.eclipse.jdt.core.formatter.brace_position_for_block_in_case_after_arrow=end_of_line +org.eclipse.jdt.core.formatter.brace_position_for_constructor_declaration=end_of_line +org.eclipse.jdt.core.formatter.brace_position_for_enum_constant=end_of_line +org.eclipse.jdt.core.formatter.brace_position_for_enum_declaration=end_of_line +org.eclipse.jdt.core.formatter.brace_position_for_lambda_body=end_of_line +org.eclipse.jdt.core.formatter.brace_position_for_method_declaration=end_of_line +org.eclipse.jdt.core.formatter.brace_position_for_record_constructor=end_of_line +org.eclipse.jdt.core.formatter.brace_position_for_record_declaration=end_of_line +org.eclipse.jdt.core.formatter.brace_position_for_switch=end_of_line +org.eclipse.jdt.core.formatter.brace_position_for_type_declaration=end_of_line +org.eclipse.jdt.core.formatter.comment.align_tags_descriptions_grouped=false +org.eclipse.jdt.core.formatter.comment.align_tags_names_descriptions=false +org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_block_comment=false +org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_javadoc_comment=false +org.eclipse.jdt.core.formatter.comment.count_line_length_from_starting_position=false +org.eclipse.jdt.core.formatter.comment.format_block_comments=true +org.eclipse.jdt.core.formatter.comment.format_header=false +org.eclipse.jdt.core.formatter.comment.format_html=false +org.eclipse.jdt.core.formatter.comment.format_javadoc_comments=true +org.eclipse.jdt.core.formatter.comment.format_line_comments=false +org.eclipse.jdt.core.formatter.comment.format_source_code=true +org.eclipse.jdt.core.formatter.comment.indent_parameter_description=true +org.eclipse.jdt.core.formatter.comment.indent_root_tags=false +org.eclipse.jdt.core.formatter.comment.indent_tag_description=false +org.eclipse.jdt.core.formatter.comment.insert_new_line_before_root_tags=insert +org.eclipse.jdt.core.formatter.comment.insert_new_line_between_different_tags=do not insert +org.eclipse.jdt.core.formatter.comment.insert_new_line_for_parameter=do not insert +org.eclipse.jdt.core.formatter.comment.javadoc_do_not_separate_block_tags=false +org.eclipse.jdt.core.formatter.comment.line_length=180 +org.eclipse.jdt.core.formatter.comment.new_lines_at_block_boundaries=true +org.eclipse.jdt.core.formatter.comment.new_lines_at_javadoc_boundaries=true +org.eclipse.jdt.core.formatter.comment.preserve_white_space_between_code_and_line_comments=false +org.eclipse.jdt.core.formatter.compact_else_if=true +org.eclipse.jdt.core.formatter.continuation_indentation=4 +org.eclipse.jdt.core.formatter.continuation_indentation_for_array_initializer=4 +org.eclipse.jdt.core.formatter.disabling_tag=@formatter\:off +org.eclipse.jdt.core.formatter.enabling_tag=@formatter\:on +org.eclipse.jdt.core.formatter.format_guardian_clause_on_one_line=false +org.eclipse.jdt.core.formatter.format_line_comment_starting_on_first_column=false +org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_annotation_declaration_header=true +org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_constant_header=true +org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_declaration_header=true +org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_record_header=true +org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_type_header=true +org.eclipse.jdt.core.formatter.indent_breaks_compare_to_cases=true +org.eclipse.jdt.core.formatter.indent_empty_lines=false +org.eclipse.jdt.core.formatter.indent_statements_compare_to_block=true +org.eclipse.jdt.core.formatter.indent_statements_compare_to_body=true +org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_cases=true +org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_switch=true +org.eclipse.jdt.core.formatter.indentation.size=8 +org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_enum_constant=insert +org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_field=insert +org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_local_variable=insert +org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_method=insert +org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_package=insert +org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_parameter=do not insert +org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_type=insert +org.eclipse.jdt.core.formatter.insert_new_line_after_label=do not insert +org.eclipse.jdt.core.formatter.insert_new_line_after_opening_brace_in_array_initializer=do not insert +org.eclipse.jdt.core.formatter.insert_new_line_after_type_annotation=do not insert +org.eclipse.jdt.core.formatter.insert_new_line_at_end_of_file_if_missing=do not insert +org.eclipse.jdt.core.formatter.insert_new_line_before_catch_in_try_statement=do not insert +org.eclipse.jdt.core.formatter.insert_new_line_before_closing_brace_in_array_initializer=do not insert +org.eclipse.jdt.core.formatter.insert_new_line_before_else_in_if_statement=do not insert +org.eclipse.jdt.core.formatter.insert_new_line_before_finally_in_try_statement=do not insert +org.eclipse.jdt.core.formatter.insert_new_line_before_while_in_do_statement=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_additive_operator=insert +org.eclipse.jdt.core.formatter.insert_space_after_and_in_type_parameter=insert +org.eclipse.jdt.core.formatter.insert_space_after_arrow_in_switch_case=insert +org.eclipse.jdt.core.formatter.insert_space_after_arrow_in_switch_default=insert +org.eclipse.jdt.core.formatter.insert_space_after_assignment_operator=insert +org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation_type_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_bitwise_operator=insert +org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_arguments=insert +org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_parameters=insert +org.eclipse.jdt.core.formatter.insert_space_after_closing_brace_in_block=insert +org.eclipse.jdt.core.formatter.insert_space_after_closing_paren_in_cast=insert +org.eclipse.jdt.core.formatter.insert_space_after_colon_in_assert=insert +org.eclipse.jdt.core.formatter.insert_space_after_colon_in_case=insert +org.eclipse.jdt.core.formatter.insert_space_after_colon_in_conditional=insert +org.eclipse.jdt.core.formatter.insert_space_after_colon_in_for=insert +org.eclipse.jdt.core.formatter.insert_space_after_colon_in_labeled_statement=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_allocation_expression=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_annotation=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_array_initializer=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_parameters=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_throws=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_constant_arguments=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_declarations=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_explicitconstructorcall_arguments=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_increments=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_inits=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_parameters=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_throws=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_invocation_arguments=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_field_declarations=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_local_declarations=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_parameterized_type_reference=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_permitted_types=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_record_components=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_superinterfaces=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_switch_case_expressions=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_arguments=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_parameters=insert +org.eclipse.jdt.core.formatter.insert_space_after_ellipsis=insert +org.eclipse.jdt.core.formatter.insert_space_after_lambda_arrow=insert +org.eclipse.jdt.core.formatter.insert_space_after_logical_operator=insert +org.eclipse.jdt.core.formatter.insert_space_after_multiplicative_operator=insert +org.eclipse.jdt.core.formatter.insert_space_after_not_operator=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_parameterized_type_reference=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_arguments=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_parameters=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_brace_in_array_initializer=insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_allocation_expression=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_reference=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_annotation=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_cast=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_catch=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_constructor_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_enum_constant=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_for=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_if=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_invocation=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_parenthesized_expression=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_record_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_switch=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_synchronized=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_try=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_while=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_postfix_operator=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_prefix_operator=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_question_in_conditional=insert +org.eclipse.jdt.core.formatter.insert_space_after_question_in_wildcard=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_relational_operator=insert +org.eclipse.jdt.core.formatter.insert_space_after_semicolon_in_for=insert +org.eclipse.jdt.core.formatter.insert_space_after_semicolon_in_try_resources=insert +org.eclipse.jdt.core.formatter.insert_space_after_shift_operator=insert +org.eclipse.jdt.core.formatter.insert_space_after_string_concatenation=insert +org.eclipse.jdt.core.formatter.insert_space_after_unary_operator=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_additive_operator=insert +org.eclipse.jdt.core.formatter.insert_space_before_and_in_type_parameter=insert +org.eclipse.jdt.core.formatter.insert_space_before_arrow_in_switch_case=insert +org.eclipse.jdt.core.formatter.insert_space_before_arrow_in_switch_default=insert +org.eclipse.jdt.core.formatter.insert_space_before_assignment_operator=insert +org.eclipse.jdt.core.formatter.insert_space_before_at_in_annotation_type_declaration=insert +org.eclipse.jdt.core.formatter.insert_space_before_bitwise_operator=insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_parameterized_type_reference=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_arguments=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_parameters=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_brace_in_array_initializer=insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_allocation_expression=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_reference=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_annotation=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_cast=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_catch=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_constructor_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_enum_constant=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_for=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_if=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_invocation=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_parenthesized_expression=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_record_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_switch=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_synchronized=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_try=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_while=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_colon_in_assert=insert +org.eclipse.jdt.core.formatter.insert_space_before_colon_in_case=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_colon_in_conditional=insert +org.eclipse.jdt.core.formatter.insert_space_before_colon_in_default=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_colon_in_for=insert +org.eclipse.jdt.core.formatter.insert_space_before_colon_in_labeled_statement=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_allocation_expression=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_annotation=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_array_initializer=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_parameters=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_throws=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_constant_arguments=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_declarations=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_explicitconstructorcall_arguments=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_increments=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_inits=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_parameters=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_throws=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_invocation_arguments=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_field_declarations=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_local_declarations=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_parameterized_type_reference=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_permitted_types=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_record_components=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_superinterfaces=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_switch_case_expressions=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_arguments=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_parameters=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_ellipsis=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_lambda_arrow=insert +org.eclipse.jdt.core.formatter.insert_space_before_logical_operator=insert +org.eclipse.jdt.core.formatter.insert_space_before_multiplicative_operator=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_parameterized_type_reference=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_arguments=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_parameters=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_annotation_type_declaration=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_anonymous_type_declaration=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_array_initializer=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_block=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_constructor_declaration=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_constant=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_declaration=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_method_declaration=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_record_constructor=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_record_declaration=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_switch=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_type_declaration=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_allocation_expression=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_reference=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_type_reference=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation_type_member_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_catch=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_constructor_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_enum_constant=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_for=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_if=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_invocation=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_parenthesized_expression=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_record_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_switch=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_synchronized=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_try=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_while=insert +org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_return=insert +org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_throw=insert +org.eclipse.jdt.core.formatter.insert_space_before_postfix_operator=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_prefix_operator=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_question_in_conditional=insert +org.eclipse.jdt.core.formatter.insert_space_before_question_in_wildcard=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_relational_operator=insert +org.eclipse.jdt.core.formatter.insert_space_before_semicolon=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_semicolon_in_for=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_semicolon_in_try_resources=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_shift_operator=insert +org.eclipse.jdt.core.formatter.insert_space_before_string_concatenation=insert +org.eclipse.jdt.core.formatter.insert_space_before_unary_operator=do not insert +org.eclipse.jdt.core.formatter.insert_space_between_brackets_in_array_type_reference=do not insert +org.eclipse.jdt.core.formatter.insert_space_between_empty_braces_in_array_initializer=do not insert +org.eclipse.jdt.core.formatter.insert_space_between_empty_brackets_in_array_allocation_expression=do not insert +org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_annotation_type_member_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_constructor_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_enum_constant=do not insert +org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_invocation=do not insert +org.eclipse.jdt.core.formatter.join_line_comments=false +org.eclipse.jdt.core.formatter.join_lines_in_comments=false +org.eclipse.jdt.core.formatter.join_wrapped_lines=false +org.eclipse.jdt.core.formatter.keep_annotation_declaration_on_one_line=one_line_if_empty +org.eclipse.jdt.core.formatter.keep_anonymous_type_declaration_on_one_line=one_line_if_empty +org.eclipse.jdt.core.formatter.keep_code_block_on_one_line=one_line_never +org.eclipse.jdt.core.formatter.keep_else_statement_on_same_line=false +org.eclipse.jdt.core.formatter.keep_empty_array_initializer_on_one_line=false +org.eclipse.jdt.core.formatter.keep_enum_constant_declaration_on_one_line=one_line_if_empty +org.eclipse.jdt.core.formatter.keep_enum_declaration_on_one_line=one_line_if_empty +org.eclipse.jdt.core.formatter.keep_if_then_body_block_on_one_line=one_line_never +org.eclipse.jdt.core.formatter.keep_imple_if_on_one_line=false +org.eclipse.jdt.core.formatter.keep_lambda_body_block_on_one_line=one_line_never +org.eclipse.jdt.core.formatter.keep_loop_body_block_on_one_line=one_line_never +org.eclipse.jdt.core.formatter.keep_method_body_on_one_line=one_line_if_empty +org.eclipse.jdt.core.formatter.keep_record_constructor_on_one_line=one_line_if_empty +org.eclipse.jdt.core.formatter.keep_record_declaration_on_one_line=one_line_if_empty +org.eclipse.jdt.core.formatter.keep_simple_do_while_body_on_same_line=false +org.eclipse.jdt.core.formatter.keep_simple_for_body_on_same_line=false +org.eclipse.jdt.core.formatter.keep_simple_getter_setter_on_one_line=false +org.eclipse.jdt.core.formatter.keep_simple_while_body_on_same_line=false +org.eclipse.jdt.core.formatter.keep_switch_body_block_on_one_line=one_line_never +org.eclipse.jdt.core.formatter.keep_switch_case_with_arrow_on_one_line=one_line_never +org.eclipse.jdt.core.formatter.keep_then_statement_on_same_line=false +org.eclipse.jdt.core.formatter.keep_type_declaration_on_one_line=one_line_if_empty +org.eclipse.jdt.core.formatter.lineSplit=180 +org.eclipse.jdt.core.formatter.never_indent_block_comments_on_first_column=true +org.eclipse.jdt.core.formatter.never_indent_line_comments_on_first_column=true +org.eclipse.jdt.core.formatter.number_of_blank_lines_after_code_block=0 +org.eclipse.jdt.core.formatter.number_of_blank_lines_at_beginning_of_code_block=0 +org.eclipse.jdt.core.formatter.number_of_blank_lines_at_beginning_of_method_body=0 +org.eclipse.jdt.core.formatter.number_of_blank_lines_at_end_of_code_block=0 +org.eclipse.jdt.core.formatter.number_of_blank_lines_at_end_of_method_body=0 +org.eclipse.jdt.core.formatter.number_of_blank_lines_before_code_block=0 +org.eclipse.jdt.core.formatter.number_of_empty_lines_to_preserve=1 +org.eclipse.jdt.core.formatter.parentheses_positions_in_annotation=common_lines +org.eclipse.jdt.core.formatter.parentheses_positions_in_catch_clause=common_lines +org.eclipse.jdt.core.formatter.parentheses_positions_in_enum_constant_declaration=common_lines +org.eclipse.jdt.core.formatter.parentheses_positions_in_for_statment=common_lines +org.eclipse.jdt.core.formatter.parentheses_positions_in_if_while_statement=common_lines +org.eclipse.jdt.core.formatter.parentheses_positions_in_lambda_declaration=common_lines +org.eclipse.jdt.core.formatter.parentheses_positions_in_method_delcaration=common_lines +org.eclipse.jdt.core.formatter.parentheses_positions_in_method_invocation=common_lines +org.eclipse.jdt.core.formatter.parentheses_positions_in_record_declaration=common_lines +org.eclipse.jdt.core.formatter.parentheses_positions_in_switch_statement=common_lines +org.eclipse.jdt.core.formatter.parentheses_positions_in_try_clause=common_lines +org.eclipse.jdt.core.formatter.put_empty_statement_on_new_line=false +org.eclipse.jdt.core.formatter.tabulation.char=space +org.eclipse.jdt.core.formatter.tabulation.size=4 +org.eclipse.jdt.core.formatter.text_block_indentation=0 +org.eclipse.jdt.core.formatter.use_on_off_tags=false +org.eclipse.jdt.core.formatter.use_tabs_only_for_leading_indentations=false +org.eclipse.jdt.core.formatter.wrap_before_additive_operator=true +org.eclipse.jdt.core.formatter.wrap_before_assertion_message_operator=true +org.eclipse.jdt.core.formatter.wrap_before_assignment_operator=false +org.eclipse.jdt.core.formatter.wrap_before_bitwise_operator=true +org.eclipse.jdt.core.formatter.wrap_before_conditional_operator=true +org.eclipse.jdt.core.formatter.wrap_before_logical_operator=true +org.eclipse.jdt.core.formatter.wrap_before_multiplicative_operator=true +org.eclipse.jdt.core.formatter.wrap_before_or_operator_multicatch=true +org.eclipse.jdt.core.formatter.wrap_before_relational_operator=true +org.eclipse.jdt.core.formatter.wrap_before_shift_operator=true +org.eclipse.jdt.core.formatter.wrap_before_string_concatenation=true +org.eclipse.jdt.core.formatter.wrap_before_switch_case_arrow_operator=false +org.eclipse.jdt.core.formatter.wrap_outer_expressions_when_nested=true +org.eclipse.jdt.core.javaFormatter=org.eclipse.jdt.core.defaultJavaFormatter diff --git a/dev/io.openliberty.microprofile.openapi.4.0.internal_fat/.settings/org.eclipse.jdt.ui.prefs b/dev/io.openliberty.microprofile.openapi.4.0.internal_fat/.settings/org.eclipse.jdt.ui.prefs new file mode 100644 index 00000000000..2d7a2b77217 --- /dev/null +++ b/dev/io.openliberty.microprofile.openapi.4.0.internal_fat/.settings/org.eclipse.jdt.ui.prefs @@ -0,0 +1,287 @@ +cleanup.add_all=false +cleanup.add_default_serial_version_id=true +cleanup.add_generated_serial_version_id=false +cleanup.add_missing_annotations=true +cleanup.add_missing_deprecated_annotations=true +cleanup.add_missing_methods=false +cleanup.add_missing_nls_tags=false +cleanup.add_missing_override_annotations=true +cleanup.add_missing_override_annotations_interface_methods=true +cleanup.add_serial_version_id=false +cleanup.always_use_blocks=true +cleanup.always_use_parentheses_in_expressions=false +cleanup.always_use_this_for_non_static_field_access=false +cleanup.always_use_this_for_non_static_method_access=false +cleanup.array_with_curly=false +cleanup.arrays_fill=false +cleanup.bitwise_conditional_expression=false +cleanup.boolean_literal=false +cleanup.boolean_value_rather_than_comparison=true +cleanup.break_loop=false +cleanup.collection_cloning=false +cleanup.comparing_on_criteria=false +cleanup.comparison_statement=false +cleanup.controlflow_merge=false +cleanup.convert_functional_interfaces=true +cleanup.convert_to_enhanced_for_loop=false +cleanup.convert_to_enhanced_for_loop_if_loop_var_used=false +cleanup.convert_to_switch_expressions=false +cleanup.correct_indentation=true +cleanup.do_while_rather_than_while=true +cleanup.double_negation=false +cleanup.else_if=false +cleanup.embedded_if=false +cleanup.evaluate_nullable=false +cleanup.extract_increment=false +cleanup.format_source_code=true +cleanup.format_source_code_changes_only=false +cleanup.hash=false +cleanup.if_condition=false +cleanup.insert_inferred_type_arguments=false +cleanup.instanceof=false +cleanup.instanceof_keyword=false +cleanup.invert_equals=false +cleanup.join=false +cleanup.lazy_logical_operator=false +cleanup.make_local_variable_final=true +cleanup.make_parameters_final=false +cleanup.make_private_fields_final=true +cleanup.make_type_abstract_if_missing_method=false +cleanup.make_variable_declarations_final=false +cleanup.map_cloning=false +cleanup.merge_conditional_blocks=false +cleanup.multi_catch=false +cleanup.never_use_blocks=false +cleanup.never_use_parentheses_in_expressions=true +cleanup.no_string_creation=false +cleanup.no_super=false +cleanup.number_suffix=false +cleanup.objects_equals=false +cleanup.one_if_rather_than_duplicate_blocks_that_fall_through=false +cleanup.operand_factorization=false +cleanup.organize_imports=true +cleanup.overridden_assignment=false +cleanup.plain_replacement=false +cleanup.precompile_regex=false +cleanup.primitive_comparison=false +cleanup.primitive_parsing=false +cleanup.primitive_rather_than_wrapper=true +cleanup.primitive_serialization=false +cleanup.pull_out_if_from_if_else=false +cleanup.pull_up_assignment=false +cleanup.push_down_negation=false +cleanup.qualify_static_field_accesses_with_declaring_class=false +cleanup.qualify_static_member_accesses_through_instances_with_declaring_class=true +cleanup.qualify_static_member_accesses_through_subtypes_with_declaring_class=true +cleanup.qualify_static_member_accesses_with_declaring_class=true +cleanup.qualify_static_method_accesses_with_declaring_class=false +cleanup.reduce_indentation=false +cleanup.redundant_comparator=false +cleanup.redundant_falling_through_block_end=false +cleanup.remove_private_constructors=true +cleanup.remove_redundant_modifiers=false +cleanup.remove_redundant_semicolons=false +cleanup.remove_redundant_type_arguments=true +cleanup.remove_trailing_whitespaces=true +cleanup.remove_trailing_whitespaces_all=true +cleanup.remove_trailing_whitespaces_ignore_empty=false +cleanup.remove_unnecessary_array_creation=false +cleanup.remove_unnecessary_casts=true +cleanup.remove_unnecessary_nls_tags=true +cleanup.remove_unused_imports=true +cleanup.remove_unused_local_variables=false +cleanup.remove_unused_private_fields=true +cleanup.remove_unused_private_members=false +cleanup.remove_unused_private_methods=true +cleanup.remove_unused_private_types=true +cleanup.return_expression=false +cleanup.simplify_lambda_expression_and_method_ref=false +cleanup.single_used_field=false +cleanup.sort_members=false +cleanup.sort_members_all=false +cleanup.standard_comparison=false +cleanup.static_inner_class=false +cleanup.strictly_equal_or_different=false +cleanup.stringbuffer_to_stringbuilder=false +cleanup.stringbuilder=false +cleanup.stringbuilder_for_local_vars=true +cleanup.stringconcat_to_textblock=false +cleanup.substring=false +cleanup.switch=false +cleanup.system_property=false +cleanup.system_property_boolean=false +cleanup.system_property_file_encoding=false +cleanup.system_property_file_separator=false +cleanup.system_property_line_separator=false +cleanup.system_property_path_separator=false +cleanup.ternary_operator=false +cleanup.try_with_resource=false +cleanup.unlooped_while=false +cleanup.unreachable_block=false +cleanup.use_anonymous_class_creation=false +cleanup.use_autoboxing=false +cleanup.use_blocks=false +cleanup.use_blocks_only_for_return_and_throw=false +cleanup.use_directly_map_method=false +cleanup.use_lambda=true +cleanup.use_parentheses_in_expressions=false +cleanup.use_string_is_blank=false +cleanup.use_this_for_non_static_field_access=false +cleanup.use_this_for_non_static_field_access_only_if_necessary=true +cleanup.use_this_for_non_static_method_access=false +cleanup.use_this_for_non_static_method_access_only_if_necessary=true +cleanup.use_unboxing=false +cleanup.use_var=false +cleanup.useless_continue=false +cleanup.useless_return=false +cleanup.valueof_rather_than_instantiation=false +cleanup_profile=_OL basic +cleanup_settings_version=2 +eclipse.preferences.version=1 +editor_save_participant_org.eclipse.jdt.ui.postsavelistener.cleanup=true +formatter_profile=_OL Standard +formatter_settings_version=23 +org.eclipse.jdt.ui.ignorelowercasenames=true +org.eclipse.jdt.ui.importorder=java;javax;org;com; +org.eclipse.jdt.ui.javadoc=true +org.eclipse.jdt.ui.ondemandthreshold=99 +org.eclipse.jdt.ui.staticondemandthreshold=99 +org.eclipse.jdt.ui.text.custom_code_templates= +sp_cleanup.add_all=false +sp_cleanup.add_default_serial_version_id=true +sp_cleanup.add_generated_serial_version_id=false +sp_cleanup.add_missing_annotations=true +sp_cleanup.add_missing_deprecated_annotations=true +sp_cleanup.add_missing_methods=false +sp_cleanup.add_missing_nls_tags=false +sp_cleanup.add_missing_override_annotations=true +sp_cleanup.add_missing_override_annotations_interface_methods=true +sp_cleanup.add_serial_version_id=false +sp_cleanup.always_use_blocks=true +sp_cleanup.always_use_parentheses_in_expressions=false +sp_cleanup.always_use_this_for_non_static_field_access=false +sp_cleanup.always_use_this_for_non_static_method_access=false +sp_cleanup.array_with_curly=false +sp_cleanup.arrays_fill=false +sp_cleanup.bitwise_conditional_expression=false +sp_cleanup.boolean_literal=false +sp_cleanup.boolean_value_rather_than_comparison=false +sp_cleanup.break_loop=false +sp_cleanup.collection_cloning=false +sp_cleanup.comparing_on_criteria=false +sp_cleanup.comparison_statement=false +sp_cleanup.controlflow_merge=false +sp_cleanup.convert_functional_interfaces=false +sp_cleanup.convert_to_enhanced_for_loop=false +sp_cleanup.convert_to_enhanced_for_loop_if_loop_var_used=false +sp_cleanup.convert_to_switch_expressions=false +sp_cleanup.correct_indentation=true +sp_cleanup.do_while_rather_than_while=false +sp_cleanup.double_negation=false +sp_cleanup.else_if=false +sp_cleanup.embedded_if=false +sp_cleanup.evaluate_nullable=false +sp_cleanup.extract_increment=false +sp_cleanup.format_source_code=true +sp_cleanup.format_source_code_changes_only=false +sp_cleanup.hash=false +sp_cleanup.if_condition=false +sp_cleanup.insert_inferred_type_arguments=false +sp_cleanup.instanceof=false +sp_cleanup.instanceof_keyword=false +sp_cleanup.invert_equals=false +sp_cleanup.join=false +sp_cleanup.lazy_logical_operator=false +sp_cleanup.make_local_variable_final=true +sp_cleanup.make_parameters_final=false +sp_cleanup.make_private_fields_final=true +sp_cleanup.make_type_abstract_if_missing_method=false +sp_cleanup.make_variable_declarations_final=false +sp_cleanup.map_cloning=false +sp_cleanup.merge_conditional_blocks=false +sp_cleanup.multi_catch=false +sp_cleanup.never_use_blocks=false +sp_cleanup.never_use_parentheses_in_expressions=true +sp_cleanup.no_string_creation=false +sp_cleanup.no_super=false +sp_cleanup.number_suffix=false +sp_cleanup.objects_equals=false +sp_cleanup.on_save_use_additional_actions=true +sp_cleanup.one_if_rather_than_duplicate_blocks_that_fall_through=false +sp_cleanup.operand_factorization=false +sp_cleanup.organize_imports=true +sp_cleanup.overridden_assignment=false +sp_cleanup.plain_replacement=false +sp_cleanup.precompile_regex=false +sp_cleanup.primitive_comparison=false +sp_cleanup.primitive_parsing=false +sp_cleanup.primitive_rather_than_wrapper=false +sp_cleanup.primitive_serialization=false +sp_cleanup.pull_out_if_from_if_else=false +sp_cleanup.pull_up_assignment=false +sp_cleanup.push_down_negation=false +sp_cleanup.qualify_static_field_accesses_with_declaring_class=false +sp_cleanup.qualify_static_member_accesses_through_instances_with_declaring_class=true +sp_cleanup.qualify_static_member_accesses_through_subtypes_with_declaring_class=true +sp_cleanup.qualify_static_member_accesses_with_declaring_class=false +sp_cleanup.qualify_static_method_accesses_with_declaring_class=false +sp_cleanup.reduce_indentation=false +sp_cleanup.redundant_comparator=false +sp_cleanup.redundant_falling_through_block_end=false +sp_cleanup.remove_private_constructors=true +sp_cleanup.remove_redundant_modifiers=false +sp_cleanup.remove_redundant_semicolons=false +sp_cleanup.remove_redundant_type_arguments=true +sp_cleanup.remove_trailing_whitespaces=true +sp_cleanup.remove_trailing_whitespaces_all=true +sp_cleanup.remove_trailing_whitespaces_ignore_empty=false +sp_cleanup.remove_unnecessary_array_creation=false +sp_cleanup.remove_unnecessary_casts=true +sp_cleanup.remove_unnecessary_nls_tags=true +sp_cleanup.remove_unused_imports=false +sp_cleanup.remove_unused_local_variables=false +sp_cleanup.remove_unused_private_fields=true +sp_cleanup.remove_unused_private_members=false +sp_cleanup.remove_unused_private_methods=true +sp_cleanup.remove_unused_private_types=true +sp_cleanup.return_expression=false +sp_cleanup.simplify_lambda_expression_and_method_ref=false +sp_cleanup.single_used_field=false +sp_cleanup.sort_members=false +sp_cleanup.sort_members_all=false +sp_cleanup.standard_comparison=false +sp_cleanup.static_inner_class=false +sp_cleanup.strictly_equal_or_different=false +sp_cleanup.stringbuffer_to_stringbuilder=false +sp_cleanup.stringbuilder=false +sp_cleanup.stringbuilder_for_local_vars=true +sp_cleanup.stringconcat_to_textblock=false +sp_cleanup.substring=false +sp_cleanup.switch=false +sp_cleanup.system_property=false +sp_cleanup.system_property_boolean=false +sp_cleanup.system_property_file_encoding=false +sp_cleanup.system_property_file_separator=false +sp_cleanup.system_property_line_separator=false +sp_cleanup.system_property_path_separator=false +sp_cleanup.ternary_operator=false +sp_cleanup.try_with_resource=false +sp_cleanup.unlooped_while=false +sp_cleanup.unreachable_block=false +sp_cleanup.use_anonymous_class_creation=false +sp_cleanup.use_autoboxing=false +sp_cleanup.use_blocks=false +sp_cleanup.use_blocks_only_for_return_and_throw=false +sp_cleanup.use_directly_map_method=false +sp_cleanup.use_lambda=true +sp_cleanup.use_parentheses_in_expressions=false +sp_cleanup.use_string_is_blank=false +sp_cleanup.use_this_for_non_static_field_access=false +sp_cleanup.use_this_for_non_static_field_access_only_if_necessary=true +sp_cleanup.use_this_for_non_static_method_access=false +sp_cleanup.use_this_for_non_static_method_access_only_if_necessary=true +sp_cleanup.use_unboxing=false +sp_cleanup.use_var=false +sp_cleanup.useless_continue=false +sp_cleanup.useless_return=false +sp_cleanup.valueof_rather_than_instantiation=false diff --git a/dev/io.openliberty.microprofile.openapi.4.0.internal_fat/bnd.bnd b/dev/io.openliberty.microprofile.openapi.4.0.internal_fat/bnd.bnd new file mode 100644 index 00000000000..6cf606ff23b --- /dev/null +++ b/dev/io.openliberty.microprofile.openapi.4.0.internal_fat/bnd.bnd @@ -0,0 +1,17 @@ +#******************************************************************************* +# Copyright (c) 2024 IBM Corporation and others. +# All rights reserved. This program and the accompanying materials +# are made available under the terms of the Eclipse Public License 2.0 +# which accompanies this distribution, and is available at +# http://www.eclipse.org/legal/epl-2.0/ +# +# SPDX-License-Identifier: EPL-2.0 +#******************************************************************************* +-include= ~../cnf/resources/bnd/bundle.props +bVersion=1.0 + + +src: \ + fat/src + +fat.project: true diff --git a/dev/io.openliberty.microprofile.openapi.4.0.internal_fat/fat/src/io/openliberty/microprofile/openapi40/fat/FATSuite.java b/dev/io.openliberty.microprofile.openapi.4.0.internal_fat/fat/src/io/openliberty/microprofile/openapi40/fat/FATSuite.java new file mode 100644 index 00000000000..0b9a5ea433d --- /dev/null +++ b/dev/io.openliberty.microprofile.openapi.4.0.internal_fat/fat/src/io/openliberty/microprofile/openapi40/fat/FATSuite.java @@ -0,0 +1,32 @@ +/******************************************************************************* + * Copyright (c) 2024 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package io.openliberty.microprofile.openapi40.fat; + +import org.junit.runner.RunWith; +import org.junit.runners.Suite; +import org.junit.runners.Suite.SuiteClasses; + +import io.openliberty.microprofile.openapi40.fat.validation.ValidationTestFive; +import io.openliberty.microprofile.openapi40.fat.validation.ValidationTestFour; +import io.openliberty.microprofile.openapi40.fat.validation.ValidationTestMissing; +import io.openliberty.microprofile.openapi40.fat.validation.ValidationTestNoErrors; +import io.openliberty.microprofile.openapi40.fat.validation.ValidationTestOne; +import io.openliberty.microprofile.openapi40.fat.validation.ValidationTestTwo; + +@SuiteClasses({ + ValidationTestOne.class, + ValidationTestTwo.class, + ValidationTestMissing.class, + ValidationTestFour.class, + ValidationTestFive.class, + ValidationTestNoErrors.class +}) +@RunWith(Suite.class) +public class FATSuite {} diff --git a/dev/io.openliberty.microprofile.openapi.4.0.internal_fat/fat/src/io/openliberty/microprofile/openapi40/fat/validation/ValidationTestFive.java b/dev/io.openliberty.microprofile.openapi.4.0.internal_fat/fat/src/io/openliberty/microprofile/openapi40/fat/validation/ValidationTestFive.java new file mode 100644 index 00000000000..d6f7b8bdd49 --- /dev/null +++ b/dev/io.openliberty.microprofile.openapi.4.0.internal_fat/fat/src/io/openliberty/microprofile/openapi40/fat/validation/ValidationTestFive.java @@ -0,0 +1,89 @@ +/******************************************************************************* + * Copyright (c) 2024 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package io.openliberty.microprofile.openapi40.fat.validation; + +import static com.ibm.websphere.simplicity.ShrinkHelper.DeployOptions.SERVER_ONLY; +import static io.openliberty.microprofile.openapi40.fat.validation.ValidationTestUtils.assertMessage; +import static io.openliberty.microprofile.openapi40.fat.validation.ValidationTestUtils.assertNoMessage; + +import org.jboss.shrinkwrap.api.ShrinkWrap; +import org.jboss.shrinkwrap.api.spec.WebArchive; +import org.junit.AfterClass; +import org.junit.BeforeClass; +import org.junit.Test; +import org.junit.runner.RunWith; + +import com.ibm.websphere.simplicity.ShrinkHelper; + +import componenttest.annotation.Server; +import componenttest.custom.junit.runner.FATRunner; +import componenttest.topology.impl.LibertyServer; + +/** + * Validation tests for Tags, Discriminator, Schema and Extension + *

+ * Ported from OpenAPIValidationTestFive and converted to run on OpenAPI v3.1 + *

+ * The validation tests for Schema in particular are quite different for OpenAPI 3.1 + */ +@RunWith(FATRunner.class) +public class ValidationTestFive { + private static final String SERVER_NAME = "OpenAPIValidationServer"; + + @Server(SERVER_NAME) + public static LibertyServer server; + + @BeforeClass + public static void setup() throws Exception { + WebArchive war = ShrinkWrap.create(WebArchive.class, "validation5.war") + .addAsManifestResource(ValidationTestFive.class.getPackage(), "validation5.yml", "openapi.yml"); + ShrinkHelper.exportDropinAppToServer(server, war, SERVER_ONLY); + + server.startServer(); + } + + @AfterClass + public static void shutdown() throws Exception { + server.stopServer("CWWKO1650E", // Validation errors found + "CWWKO1651W");// Validation warnings found + } + + @Test + public void testTags() throws Exception { + assertMessage(server, "- Message: Required \"name\" field is missing or is set to an invalid value, Location: #/tags"); + } + + @Test + public void testDiscriminator() throws Exception { + assertMessage(server, "- Message: Required \"propertyName\" field is missing or is set to an invalid value,*"); + } + + @Test + public void testSchema() throws Exception { + assertMessage(server, " - Message: The Schema Object must have the \"multipleOf\" property set to a number strictly greater than zero, " + + "Location: #/paths/~1availability/get/parameters/schema"); + assertMessage(server, " - Message: The \"minItems\" property of the Schema Object must be greater than or equal to zero, " + + "Location: #/paths/~1availability/get/parameters/schema"); + assertMessage(server, " - Message: The \"maxItems\" property of the Schema Object must be greater than or equal to zero, " + + "Location: #/paths/~1availability/get/parameters/schema"); + assertMessage(server, " - Message: The \"minProperties\" property of the Schema Object must be greater than or equal to zero, " + + "Location: #/paths/~1availability/get/parameters/schema"); + assertMessage(server, " - Message: The \"maxProperties\" property of the Schema Object must be greater than or equal to zero, " + + "Location: #/paths/~1availability/get/parameters/schema"); + + // Warnings not currently emitted for 3.1 + assertNoMessage(server, " - Message: The \"minItems\" property is not appropriate for the Schema Object of \"object\" type"); + assertNoMessage(server, " - Message: The \"maxItems\" property is not appropriate for the Schema Object of \"object\" type"); + + // Dubious error reported for 3.0, not reported for 3.1 + assertNoMessage(server, " - Message: The Schema Object of \"array\" type must have \"items\" property defined"); + } + +} diff --git a/dev/io.openliberty.microprofile.openapi.4.0.internal_fat/fat/src/io/openliberty/microprofile/openapi40/fat/validation/ValidationTestFour.java b/dev/io.openliberty.microprofile.openapi.4.0.internal_fat/fat/src/io/openliberty/microprofile/openapi40/fat/validation/ValidationTestFour.java new file mode 100644 index 00000000000..63782e4092d --- /dev/null +++ b/dev/io.openliberty.microprofile.openapi.4.0.internal_fat/fat/src/io/openliberty/microprofile/openapi40/fat/validation/ValidationTestFour.java @@ -0,0 +1,134 @@ +/******************************************************************************* + * Copyright (c) 2024 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package io.openliberty.microprofile.openapi40.fat.validation; + +import static com.ibm.websphere.simplicity.ShrinkHelper.DeployOptions.SERVER_ONLY; +import static io.openliberty.microprofile.openapi40.fat.validation.ValidationTestUtils.assertMessage; + +import org.jboss.shrinkwrap.api.ShrinkWrap; +import org.jboss.shrinkwrap.api.spec.WebArchive; +import org.junit.AfterClass; +import org.junit.BeforeClass; +import org.junit.Test; +import org.junit.runner.RunWith; + +import com.ibm.websphere.simplicity.ShrinkHelper; + +import componenttest.annotation.Server; +import componenttest.custom.junit.runner.FATRunner; +import componenttest.topology.impl.LibertyServer; + +/** + * Validation tests for References, Callbacks and PathItems + *

+ * Ported from OpenAPIValidationTestFour and converted to run on OpenAPI v3.1 + */ +@RunWith(FATRunner.class) +public class ValidationTestFour { + private static final String SERVER_NAME = "OpenAPIValidationServer"; + + @Server(SERVER_NAME) + public static LibertyServer server; + + @BeforeClass + public static void setup() throws Exception { + WebArchive war = ShrinkWrap.create(WebArchive.class, "validation4.war") + .addAsManifestResource(ValidationTestFour.class.getPackage(), "validation4.yml", "openapi.yml"); + ShrinkHelper.exportDropinAppToServer(server, war, SERVER_ONLY); + + server.startServer(); + } + + @AfterClass + public static void shutdown() throws Exception { + server.stopServer("CWWKO1650E", // Validation errors found + "CWWKO1651W");// Validation warnings found + } + + @Test + public void testRef() throws Exception { + + // 3.1 Validation cases + // Reference is null/empty (reference is null) + // Reference is not a valid URI (URI.create does not parse it) (reference is not a valid URI) + // Ref is within components and components is missing (reference not defined within Components) + // Ref is to a known name within components and the object is missing (reference not defined within Components) + // Ref is to an object but the object is of the wrong type (is an invalid reference) - only reported if no other errors + + // Main differences from 3.0: + // We directly test whether the reference parses as a URI + // We don't report the "is an invalid reference" error if we report another problem with the same reference + // We don't validate references which aren't of the form #/components// - 3.0 reports some of these as "not in a valid format" + // - the spec doesn't restrict where a json pointer to point to + // - we could do better validation here, but we can't currently navigate the model reflectively by name, so following a json pointer is not easy + + // Currently outstanding issues: + // Unqualified schema references do not get automatically prefixed with '#/components/schemas/' + // - https://github.com/smallrye/smallrye-open-api/issues/1987 + +// assertMessage(server, " - Message: The \"#/components/schemas/\" reference value is not in a valid format, Location: #/paths/~1availability/get/parameters/schema"); +// assertMessage(server, " - Message: The \"#/components/schemas/ \" reference value is not defined within the Components Object, " +// + "Location: #/paths/~1availability/get/parameters/schema"); +// assertMessage(server, " - Message: The \"#/components/schemas/#\" reference value is not defined within the Components Object, " +// + "Location: #/paths/~1availability/get/parameters/schema"); + + // 3-part reference with an invalid type + assertMessage(server, " - Message: The \"#/components/Flight\" reference value is not defined within the Components Object, " + + "Location: #/paths/~1availability/get/responses/200/content/applictaion~1json/schema/items"); + // 4-part reference with an invalid type + assertMessage(server, " - Message: The \"#/components//Booking\" reference value is not defined within the Components Object, " + + "Location: #/paths/~1bookings/get/responses/200/content/application~1json/schema/items"); + // 3-part reference with a valid type + // When it's a schema, this is technically a valid reference since almost any map is a valid schema with extra fields + // When it's not a schema, we should get an error + assertMessage(server, " - Message: The \"#/components/schemas\" value is an invalid reference, " + + "Location: #/paths/~1availability/get/parameters"); + + assertMessage(server, " - Message: The \"#/components/schemas/Airport/Cat\" reference value is not defined within the Components Object, " + + "Location: #/paths/~1availability/get/parameters/schema"); + assertMessage(server, " - Message: The \"#/components/requestBodies/Pet\" reference value is not defined within the Components Object, " + + "Location: #/paths/~1bookings/post/requestBody"); + assertMessage(server, " - Message: The \"#/components/responses/Pet\" reference value is not defined within the Components Object,"); + assertMessage(server, " - Message: The \"#/components/schemas/schemas\" reference value is not defined within the Components Object,"); + assertMessage(server, " - Message: The \"#/components/schemas/Pet\" reference value is not defined within the Components Object,"); + assertMessage(server, " - Message: The \"#/components/examples/Pet\" reference value is not defined within the Components Object, " + + "Location: #/paths/~1reviews/post/requestBody/content/application~1json/examples/review"); + + // Valid references to something of the wrong type + assertMessage(server, " - Message: The \"#/components/schemas/Flight\" value is an invalid reference, " + + "Location: #/paths/~1availability/get/parameters"); + assertMessage(server, "The \"http://\\{\\}/#/test\" value is not a valid URI, Location: #/paths/~1availability/get/parameters"); + + // Missing pathItem reference + assertMessage(server, " - Message: The \"#/components/pathItems/latestBookings\" reference value is not defined within the Components Object, " + + "Location: #/paths/~1bookings~1latest"); + } + + @Test + public void testCallbacks() throws Exception { + assertMessage(server, " - Message: The URL template of Callback Object is empty and is not a valid URL, Location: #/paths/~1bookings/post/callbacks/getBookings"); + assertMessage(server, " - Message: The Callback Object contains invalid substitution variables:*"); + assertMessage(server, " - Message: The Callback Object must contain a valid runtime expression as defined in the OpenAPI Specification.*"); + } + + @Test + public void testPathItems() throws Exception { + assertMessage(server, " - Message: The Path Item Object must contain a valid path\\. " + + "The \"DELETE\" operation of the \"/bookings/\\{id\\}\" path does not define a path parameter that is declared"); + assertMessage(server, " - Message: The Path Item Object must contain a valid path\\. " + + "The format of the \"http://localhost:9080/o\\{as3-ai\\{rl\\}ines/booking\" path is invalid"); + assertMessage(server, " - Message: The Path Item Object must contain a valid path\\. " + + "The \"GET\" operation from the \"/reviews/\\{airline\\}\" path defines a duplicated \"path\" parameter: \"airline\""); + assertMessage(server, " - Message: The Path Item Object must contain a valid path\\. " + + "The \"PUT\" operation from the \"/reviews\" path defines one path parameter that is not declared: \"\\[id\\]\""); + assertMessage(server, 4, " - Message: The Path Item Object must contain a valid path."); + } + +} diff --git a/dev/io.openliberty.microprofile.openapi.4.0.internal_fat/fat/src/io/openliberty/microprofile/openapi40/fat/validation/ValidationTestMissing.java b/dev/io.openliberty.microprofile.openapi.4.0.internal_fat/fat/src/io/openliberty/microprofile/openapi40/fat/validation/ValidationTestMissing.java new file mode 100644 index 00000000000..69c246c327c --- /dev/null +++ b/dev/io.openliberty.microprofile.openapi.4.0.internal_fat/fat/src/io/openliberty/microprofile/openapi40/fat/validation/ValidationTestMissing.java @@ -0,0 +1,63 @@ +/******************************************************************************* + * Copyright (c) 2024 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package io.openliberty.microprofile.openapi40.fat.validation; + +import static com.ibm.websphere.simplicity.ShrinkHelper.DeployOptions.SERVER_ONLY; +import static io.openliberty.microprofile.openapi40.fat.validation.ValidationTestUtils.assertNoMessage; + +import org.jboss.shrinkwrap.api.ShrinkWrap; +import org.jboss.shrinkwrap.api.spec.WebArchive; +import org.junit.AfterClass; +import org.junit.BeforeClass; +import org.junit.Test; +import org.junit.runner.RunWith; + +import com.ibm.websphere.simplicity.ShrinkHelper; + +import componenttest.annotation.Server; +import componenttest.custom.junit.runner.FATRunner; +import componenttest.topology.impl.LibertyServer; + +/** + * Validate main required elements being missing + *

+ * Includes the tests from OpenAPIValidationTestThree, updated for OpenAPI v3.1 + */ +@RunWith(FATRunner.class) +public class ValidationTestMissing { + private static final String SERVER_NAME = "OpenAPIValidationServer"; + + @Server(SERVER_NAME) + public static LibertyServer server; + + @BeforeClass + public static void setup() throws Exception { + WebArchive war = ShrinkWrap.create(WebArchive.class, "validation-missing.war") + .addAsManifestResource(ValidationTestMissing.class.getPackage(), "validation-missing.yml", "openapi.yml"); + ShrinkHelper.exportDropinAppToServer(server, war, SERVER_ONLY); + + server.startServer(); + } + + @AfterClass + public static void shutdown() throws Exception { + server.stopServer(); + } + + @Test + public void testEmpty() throws Exception { + // Smallrye OpenAPI always generates an empty paths object and a minimal info object if there isn't one present + // This is explicitly valid: https://spec.openapis.org/oas/v3.1.0.html#paths-object + // This also means we can't actually hit the case where none of paths, components or webhooks are present + assertNoMessage(server, "CWWKO1650E"); // Assert no validation errors + assertNoMessage(server, "CWWKO1651W"); // Assert no validation warnings + } + +} diff --git a/dev/io.openliberty.microprofile.openapi.4.0.internal_fat/fat/src/io/openliberty/microprofile/openapi40/fat/validation/ValidationTestNoErrors.java b/dev/io.openliberty.microprofile.openapi.4.0.internal_fat/fat/src/io/openliberty/microprofile/openapi40/fat/validation/ValidationTestNoErrors.java new file mode 100644 index 00000000000..9af2e06be2d --- /dev/null +++ b/dev/io.openliberty.microprofile.openapi.4.0.internal_fat/fat/src/io/openliberty/microprofile/openapi40/fat/validation/ValidationTestNoErrors.java @@ -0,0 +1,55 @@ +/******************************************************************************* + * Copyright (c) 2024 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package io.openliberty.microprofile.openapi40.fat.validation; + +import static com.ibm.websphere.simplicity.ShrinkHelper.DeployOptions.SERVER_ONLY; +import static io.openliberty.microprofile.openapi40.fat.validation.ValidationTestUtils.assertNoMessage; + +import org.jboss.shrinkwrap.api.ShrinkWrap; +import org.jboss.shrinkwrap.api.spec.WebArchive; +import org.junit.AfterClass; +import org.junit.BeforeClass; +import org.junit.Test; +import org.junit.runner.RunWith; + +import com.ibm.websphere.simplicity.ShrinkHelper; + +import componenttest.annotation.Server; +import componenttest.custom.junit.runner.FATRunner; +import componenttest.topology.impl.LibertyServer; + +@RunWith(FATRunner.class) +public class ValidationTestNoErrors { + private static final String SERVER_NAME = "OpenAPIValidationServer"; + + @Server(SERVER_NAME) + public static LibertyServer server; + + @BeforeClass + public static void setup() throws Exception { + WebArchive war = ShrinkWrap.create(WebArchive.class, "validation-noerrors.war") + .addAsManifestResource(ValidationTestNoErrors.class.getPackage(), "validation-noerrors.yml", "openapi.yml"); + ShrinkHelper.exportDropinAppToServer(server, war, SERVER_ONLY); + + server.startServer(); + } + + @AfterClass + public static void shutdown() throws Exception { + server.stopServer(); + } + + @Test + public void testNoErrors() throws Exception { + assertNoMessage(server, "CWWKO1650E"); // Assert no validation errors + assertNoMessage(server, "CWWKO1651W"); // Assert no validation warnings + } + +} diff --git a/dev/io.openliberty.microprofile.openapi.4.0.internal_fat/fat/src/io/openliberty/microprofile/openapi40/fat/validation/ValidationTestOne.java b/dev/io.openliberty.microprofile.openapi.4.0.internal_fat/fat/src/io/openliberty/microprofile/openapi40/fat/validation/ValidationTestOne.java new file mode 100644 index 00000000000..d3ea894af02 --- /dev/null +++ b/dev/io.openliberty.microprofile.openapi.4.0.internal_fat/fat/src/io/openliberty/microprofile/openapi40/fat/validation/ValidationTestOne.java @@ -0,0 +1,138 @@ +/******************************************************************************* + * Copyright (c) 2024 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package io.openliberty.microprofile.openapi40.fat.validation; + +import static com.ibm.websphere.simplicity.ShrinkHelper.DeployOptions.SERVER_ONLY; +import static io.openliberty.microprofile.openapi40.fat.validation.ValidationTestUtils.assertMessage; +import static io.openliberty.microprofile.openapi40.fat.validation.ValidationTestUtils.assertNoMessage; + +import org.jboss.shrinkwrap.api.ShrinkWrap; +import org.jboss.shrinkwrap.api.spec.WebArchive; +import org.junit.AfterClass; +import org.junit.BeforeClass; +import org.junit.Test; +import org.junit.runner.RunWith; + +import com.ibm.websphere.simplicity.ShrinkHelper; + +import componenttest.annotation.Server; +import componenttest.custom.junit.runner.FATRunner; +import componenttest.topology.impl.LibertyServer; +import componenttest.topology.utils.FATServletClient; + +/** + * Does the same validation checks from OpenAPIValidationTestOne but using an OpenAPI 3.1 document + * + * Covers: Info, Contact, License, ServerVariable(s), Server(s), PathItem, Operation, ExternalDocumentation, + * SecurityRequirement, RequestBody, Response, Responses + */ +@RunWith(FATRunner.class) +public class ValidationTestOne extends FATServletClient { + + private static final String SERVER_NAME = "OpenAPIValidationServer"; + + @Server(SERVER_NAME) + public static LibertyServer server; + + @BeforeClass + public static void setup() throws Exception { + WebArchive war = ShrinkWrap.create(WebArchive.class, "validation.war") + .addAsManifestResource(ValidationTestOne.class.getPackage(), "validation1.yml", "openapi.yml"); + ShrinkHelper.exportDropinAppToServer(server, war, SERVER_ONLY); + + server.startServer(); + } + + @AfterClass + public static void shutdown() throws Exception { + server.stopServer("CWWKO1650E", // Validation errors found + "CWWKO1651W");// Validation warnings found + } + + @Test + public void testErrorAndWarningMessages() throws Exception { + assertMessage(server, "CWWKO1650E"); // Validation errors found + assertMessage(server, "CWWKO1651W"); // Validation warnings found + } + + @Test + public void testInfoValidation() throws Exception { + assertMessage(server, "Message: The Info Object must contain a valid URL. The \"not in URL format\" value specified for \"termsOfService\""); + } + + @Test + public void testContactValidation() throws Exception { + assertMessage(server, "Message: The Contact Object must contain a valid URL. The \"not in URL Format\" value specified"); + assertMessage(server, "Message: The Contact Object must contain a valid email address. The \"not an email\" value"); + } + + @Test + public void testServerValidation() throws Exception { + assertMessage(server, 4, "Message: The Server Object must contain a valid URL"); + assertMessage(server, "Message: Required \"url\" field is missing or is set to an invalid value, Location: #/paths/~1reviews/get/servers"); + assertMessage(server, "The \"extraVariable\" variable in the Server Object is not defined"); + assertMessage(server, "Message: The \"id\" variable in the Server Object is not defined"); + } + + @Test + public void testServerVariableValidation() throws Exception { + assertMessage(server, "Message: Required \"default\" field is missing or is set to an invalid value, Location: .*id"); + assertMessage(server, "Message: The \"foo\" value of the \"default\" property is not listed in the \"enum\" array, Location: .*name1"); + assertMessage(server, "Message: The \"enum\" array in the Server Variable Object is empty, Location: .*name2"); + } + + @Test + public void testPathItemValidation() throws Exception { + assertMessage(server, + "The \"id\" path parameter from the \"GET\" operation of the path \"/bookings/\\{id\\}\" does not contain the \"required\" field or its value is not \"true\""); + assertMessage(server, "The \"GET\" operation of the \"/reviews/\\{id\\}\" path does not define a path parameter that is declared: \"id\""); + assertMessage(server, + "The Path Item Object must contain a valid path. The \"GET\" operation from the \"/reviews/\\{airline\\}\" path defines a duplicated \"path\" parameter: \"airline\""); + assertMessage(server, "The Paths Object contains an invalid path. The \"noSlashPath\" path value does not begin with a slash"); + assertMessage(server, "The Path Item Object must contain a valid path. The format of the \"/availability/"); + assertMessage(server, " The \"userFirstName\" path parameter from the \"GET\" operation of the path \"/operationWithParam\" does not contain the \"required\" field"); + assertMessage(server, + "The Path Item Object must contain a valid path. The \"/\\{username\\}\" path defines \"3\" path parameters that are not declared: \"\\[pathWithUndeclaredParams, usernameParam, accountNumber\\]\""); + assertMessage(server, "The \"GET\" operation from the \"/operationWithParam\" path defines one path parameter that is not declared: \"\\[userFirstName\\]\""); + } + + @Test + public void testOperationValidation() throws Exception { + // This is no longer an error in OpenAPI 3.1 + assertNoMessage(server, "Message: Required \"responses\" field is missing or is set to an invalid value, Location: #/paths/~1/get"); + assertMessage(server, "Message: More than one Operation Objects with \"getReviewById\" value for \"operationId\" field was found. The \"operationId\" must be unique"); + } + + @Test + public void testExternalDocsValidation() throws Exception { + assertMessage(server, "Message: The External Documentation Object must contain a valid URL. The \"not a URL\" value"); + } + + @Test + public void testSecurityRequirementValidation() throws Exception { + assertMessage(server, "The \"reviewoauth2\" name provided for the Security Requirement Object does not correspond to a declared security scheme"); + } + + @Test + public void testRequestBodyValidation() throws Exception { + assertMessage(server, "Message: Required \"content\" field is missing or is set to an invalid value, Location: #/paths/~1reviews/post/requestBody"); + } + + @Test + public void testResponseValidation() throws Exception { + assertMessage(server, "Message: Required \"description\" field is missing or is set to an invalid value"); + } + + @Test + public void testResponsesValidation() throws Exception { + assertMessage(server, "Message: The Responses Object should contain at least one response code for a successful operation"); + } + +} diff --git a/dev/io.openliberty.microprofile.openapi.4.0.internal_fat/fat/src/io/openliberty/microprofile/openapi40/fat/validation/ValidationTestTwo.java b/dev/io.openliberty.microprofile.openapi.4.0.internal_fat/fat/src/io/openliberty/microprofile/openapi40/fat/validation/ValidationTestTwo.java new file mode 100644 index 00000000000..affe3fcbddd --- /dev/null +++ b/dev/io.openliberty.microprofile.openapi.4.0.internal_fat/fat/src/io/openliberty/microprofile/openapi40/fat/validation/ValidationTestTwo.java @@ -0,0 +1,106 @@ +/******************************************************************************* + * Copyright (c) 2024 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package io.openliberty.microprofile.openapi40.fat.validation; + +import static com.ibm.websphere.simplicity.ShrinkHelper.DeployOptions.SERVER_ONLY; +import static io.openliberty.microprofile.openapi40.fat.validation.ValidationTestUtils.assertMessage; + +import org.jboss.shrinkwrap.api.ShrinkWrap; +import org.jboss.shrinkwrap.api.spec.WebArchive; +import org.junit.AfterClass; +import org.junit.BeforeClass; +import org.junit.Test; +import org.junit.runner.RunWith; + +import com.ibm.websphere.simplicity.ShrinkHelper; + +import componenttest.annotation.Server; +import componenttest.custom.junit.runner.FATRunner; +import componenttest.topology.impl.LibertyServer; + +/** + * Test validation rules for SecurityScheme, SecurityRequirement, OAuthFlow, OAuthFlows, MediaType and Example + *

+ * Ported from OpenAPIValidationTestTwo and converted to run on OpenAPI v3.1 + */ +@RunWith(FATRunner.class) +public class ValidationTestTwo { + private static final String SERVER_NAME = "OpenAPIValidationServer"; + + @Server(SERVER_NAME) + public static LibertyServer server; + + @BeforeClass + public static void setup() throws Exception { + WebArchive war = ShrinkWrap.create(WebArchive.class, "validation2.war") + .addAsManifestResource(ValidationTestTwo.class.getPackage(), "validation2.yml", "openapi.yml"); + ShrinkHelper.exportDropinAppToServer(server, war, SERVER_ONLY); + + server.startServer(); + } + + @AfterClass + public static void shutdown() throws Exception { + server.stopServer("CWWKO1650E", // Validation errors found + "CWWKO1651W");// Validation warnings found + } + + @Test + public void testSecuritySchemeValidation() throws Exception { + assertMessage(server, "Message: Required \"type\" field is missing or is set to an invalid value, Location: #/components/securitySchemes/noType"); + assertMessage(server, "Message: Required \"openIdConnectUrl\" field is missing or is set to an invalid value," + + " Location: #/components/securitySchemes/openIdConnectWithScheme"); + assertMessage(server, "Message: Required \"scheme\" field is missing or is set to an invalid value, Location: #/components/securitySchemes/airlinesHttp"); + assertMessage(server, "Message: Required \"flows\" field is missing or is set to an invalid value, Location: #/components/securitySchemes/reviewoauth2"); + assertMessage(server, "Message: Required \"scheme\" field is missing or is set to an invalid value, Location: #/components/securitySchemes/httpWithOpenIdConnectUrl"); + assertMessage(server, "Message: Required \"name\" field is missing or is set to an invalid value, Location: #/components/securitySchemes/ApiKeyWithScheme"); + assertMessage(server, "Message: Required \"in\" field is missing or is set to an invalid value, Location: #/components/securitySchemes/ApiKeyWithScheme"); + assertMessage(server, "Message: Required \"in\" field is missing or is set to an invalid value, Location: #/components/securitySchemes/ApiKeyWithInvalidIn"); + assertMessage(server, "Message: The Security Scheme Object must contain a valid URL. The \"not a URL\" value specified for the URL is not valid*"); + assertMessage(server, "Message: The \"scheme\" field with \"openIdConnectWithScheme\" value is not applicable for \"Security Scheme Object\" of \"openIdConnect\" type"); + assertMessage(server, "Message: The \"name\" field with \"oauth2WithName\" value is not applicable for \"Security Scheme Object\" of \"oauth2\" type"); + assertMessage(server, "Message: The \"openIdConnectUrl\" field with \"http://www.url.com\" value is not applicable for \"Security Scheme Object\" of \"http\" type"); + assertMessage(server, "Message: The \"flows\" field is not applicable for \"Security Scheme Object\" of \"http\" type"); + } + + @Test + public void testSecurityRequirementValidation() throws Exception { + assertMessage(server, "Message: The \"schemeNotInComponent\" name provided for the Security Requirement Object" + + " does not correspond to a declared security scheme, Location: #/paths/~1availability/get/security"); + assertMessage(server, "Message: The \"airlinesHttp\" field of Security Requirement Object should be empty, but is: \"\\[write:app, read:app\\]\""); + assertMessage(server, "Message: The \"openIdConnectWithScheme\" Security Requirement Object should specify be a list of scope names required for execution"); + } + + @Test + public void testOAuthFlowValidation() throws Exception { + assertMessage(server, 3, "Message: Required \"scopes\" field is missing or is set to an invalid value"); + assertMessage(server, "Message: The OAuth Flow Object must contain a valid URL. The \"invalid URL example\" value"); + } + + @Test + public void testOAuthFlowsValidation() throws Exception { + assertMessage(server, 2, "Message: Required \"tokenUrl\" field is missing or is set to an invalid value"); + assertMessage(server, "Message: The \"authorizationUrl\" field with \"https://example.com/api/oauth/dialog\" value" + + " is not applicable for \"OAuth Flow Object\" of \"password\" type"); + } + + @Test + public void testMediaTypeValidation() throws Exception { + assertMessage(server, 2, "Message: The \"nonExistingField\" encoding property specified in the MediaType Object does not exist"); + assertMessage(server, "Message: The MediaType Object cannot have both \"examples\" and \"example\" fields"); + assertMessage(server, "Message: The encoding property specified cannot be validated because the corresponding schema property is null"); + } + + @Test + public void testExampleValidation() throws Exception { + assertMessage(server, "Message: The \"booking\" Example Object specifies both \"value\" and \"externalValue\" fields"); + } + +} diff --git a/dev/io.openliberty.microprofile.openapi.4.0.internal_fat/fat/src/io/openliberty/microprofile/openapi40/fat/validation/ValidationTestUtils.java b/dev/io.openliberty.microprofile.openapi.4.0.internal_fat/fat/src/io/openliberty/microprofile/openapi40/fat/validation/ValidationTestUtils.java new file mode 100644 index 00000000000..cc7d7f59a45 --- /dev/null +++ b/dev/io.openliberty.microprofile.openapi.4.0.internal_fat/fat/src/io/openliberty/microprofile/openapi40/fat/validation/ValidationTestUtils.java @@ -0,0 +1,62 @@ +/******************************************************************************* + * Copyright (c) 2024 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package io.openliberty.microprofile.openapi40.fat.validation; + +import static org.hamcrest.Matchers.empty; +import static org.hamcrest.Matchers.hasSize; +import static org.hamcrest.Matchers.is; +import static org.hamcrest.Matchers.not; +import static org.junit.Assert.assertThat; + +import componenttest.topology.impl.LibertyServer; + +/** + * + */ +public class ValidationTestUtils { + + /** + * Assert that a message is found in the messages.log + * + * @param server the server to check + * @param message the message to find + */ + public static void assertMessage(LibertyServer server, String message) throws Exception { + assertThat("Message not found: " + message, + server.findStringsInLogs(message), + not(empty())); + } + + /** + * Assert that a message is found a specific number of times in the messages.log + * + * @param server the server to check + * @param count the number of times the message is expected + * @param message the message to look for + */ + public static void assertMessage(LibertyServer server, int count, String message) throws Exception { + assertThat("Message not found " + count + " times: " + message, + server.findStringsInLogs(message), + hasSize(count)); + } + + /** + * Assert that a message is not found in the messages log + * + * @param server the server to check + * @param message the message to search for + */ + public static void assertNoMessage(LibertyServer server, String message) throws Exception { + assertThat("Unexpected message found: " + message, + server.findStringsInLogs(message), + is(empty())); + } + +} diff --git a/dev/io.openliberty.microprofile.openapi.4.0.internal_fat/fat/src/io/openliberty/microprofile/openapi40/fat/validation/validation-missing.yml b/dev/io.openliberty.microprofile.openapi.4.0.internal_fat/fat/src/io/openliberty/microprofile/openapi40/fat/validation/validation-missing.yml new file mode 100644 index 00000000000..b3fc5ca3249 --- /dev/null +++ b/dev/io.openliberty.microprofile.openapi.4.0.internal_fat/fat/src/io/openliberty/microprofile/openapi40/fat/validation/validation-missing.yml @@ -0,0 +1,4 @@ +openapi: 3.1.0 +info: + version: 0.1 + title: empty OpenAPI doc \ No newline at end of file diff --git a/dev/io.openliberty.microprofile.openapi.4.0.internal_fat/fat/src/io/openliberty/microprofile/openapi40/fat/validation/validation-noerrors.yml b/dev/io.openliberty.microprofile.openapi.4.0.internal_fat/fat/src/io/openliberty/microprofile/openapi40/fat/validation/validation-noerrors.yml new file mode 100644 index 00000000000..d019d77166c --- /dev/null +++ b/dev/io.openliberty.microprofile.openapi.4.0.internal_fat/fat/src/io/openliberty/microprofile/openapi40/fat/validation/validation-noerrors.yml @@ -0,0 +1,567 @@ +openapi: 3.1.0 +info: + title: Validation App + version: "1.0" + termsOfService: http://www.termsofservice.com + contact: + name: AirlinesRatingApp API Support + url: http://www.contacts.com + email: airlines@gmail.com + license: + name: Apache 2.0 + url: http://www.license.com +externalDocs: + description: instructions for how to deploy this app + url: http://www.externaldocumentation.com +servers: +- url: http://localhost:9080 +tags: +- name: Airlines + description: airlines app +- name: airline + description: all the airlines methods +- name: availability + description: all the availibility methods +- name: bookings + description: all the bookings methods +- name: reviews + description: all the review methods +paths: + /: + get: + tags: + - airline + summary: Retrieve all available airlines + responses: + 202: + description: failed operation + content: + applictaion/json: + schema: + $ref: '#/components/schemas/Flight' + operationId: getAirlines + servers: + - url: localhost:9080/oas3-airlines/airlines/id + description: view of all the bookings + /availability: + get: + tags: + - availability + summary: Retrieve all available flights + operationId: getFlights + parameters: + - name: departureDate + in: query + description: Customer departure date + required: true + schema: + type: string + - name: airportFrom + in: query + description: Airport the customer departs from + required: true + schema: + type: string + - name: returningDate + in: query + description: Customer return date + required: true + schema: + type: string + - name: airportTo + in: query + description: Airport the customer returns to + required: true + schema: + type: string + - name: numberOfAdults + in: query + description: Number of adults on the flight + required: true + schema: + minimum: 0 + type: string + - name: numberOfChildren + in: query + description: Number of children on the flight + required: true + schema: + minimum: 0 + type: string + responses: + 202: + description: failed operation + content: + applictaion/json: + schema: + $ref: '#/components/schemas/Flight' + 404: + description: No available flights found + content: + n/a: {} + servers: + - url: localhost:9080/oas3-airlines/availability + description: view of all the bookings + /bookings: + get: + tags: + - bookings + summary: Retrieve all bookings for current user + operationId: getBookings + responses: + 200: + description: Bookings retrieved + content: + application/json: + schema: + type: string + 404: + description: No bookings found for the user. + post: + tags: + - bookings + summary: Create a booking + description: Create a new booking record with the booking information provided. + operationId: createBooking + requestBody: + description: Create a new booking with the provided information. + content: + application/json: + schema: + $ref: '#/components/schemas/Booking' + examples: + booking: + summary: External booking example + externalValue: http://foo.bar/examples/booking-example.json + responses: + 201: + description: Booking created + content: + application/json: + schema: + type: string + description: id of the new booking + + /bookings/{id}: + get: + tags: + - bookings + summary: Get a booking with ID + operationId: getBooking + parameters: + - name: id + required: true + in: path + description: ID of the booking + schema: + type: integer + responses: + 200: + description: booking retrieved + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/Booking' + 404: + description: No bookings found for the user. + servers: + - url: localhost:9080/oas3-airlines/bookings/{id} + description: view of all the bookings for this user + variables: + id: + default: "1" + description: id of the review + put: + tags: + - bookings + summary: Update a booking with ID + operationId: updateBooking + parameters: + - name: id + in: path + description: ID of the booking + required: true + schema: + type: integer + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/Booking' + responses: + 200: + description: Booking updated + 404: + description: Booking not found + delete: + tags: + - bookings + summary: Delete a booking with ID + operationId: deleteBooking + parameters: + - name: id + in: path + required: true + schema: + type: integer + responses: + 200: + description: Booking deleted successfully. + 404: + description: Booking not found. + /bookings/latest: + $ref: '#/components/pathItems/latestBookings' + /reviews: + get: + tags: + - reviews + summary: get all the reviews + operationId: getReview + responses: + 200: + description: successful operation + content: + application/json: + schema: + type: array + items: + oneOf: + - $ref: '#/components/schemas/Review' + servers: + - url: localhost:9080/oas3-airlines/reviews + description: endpoint for all the review related methods + post: + tags: + - reviews + summary: Create a Review + operationId: createReview + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/Review' + description: example review to add + required: true + responses: + 201: + description: review created + content: + application/json: + schema: + type: string + description: id of the new review + callbacks: + testCallback: + http://localhost:9080/oas3-airlines/reviews: {} + security: + - reviewoauth2: + - write:reviews + servers: + - url: localhost:9080/oas3-airlines/reviews/{id} + description: view of all the reviews + variables: + id: + description: id of the review + default: "1" + /reviews/{id}: + get: + tags: + - reviews + summary: Get a review with ID + operationId: getReviewById + parameters: + - name: id + in: path + description: ID of the booking + required: true + content: + '*/*': + schema: + type: integer + responses: + 200: + description: Review retrieved + content: + application/json: + schema: + $ref: '#/components/schemas/Review' + 404: + description: Review not found + servers: + - url: localhost:9080/oas3-airlines/reviews/{id} + description: endpoint for all the review related methods + variables: + id: + default: "11" + description: id of the review + delete: + tags: + - reviews + summary: Delete a Review with ID + operationId: deleteReview + parameters: + - name: id + in: path + required: true + schema: + type: integer + responses: + 200: + description: Review deleted + 404: + description: Review not found + /reviews/{user}: + get: + tags: + - reviews + summary: Get all reviews by user + operationId: getReviewByUser + parameters: + - name: user + in: path + description: username of the user for the reviews + required: true + content: + '*/*': + schema: + type: string + examples: + ? + : value: bsmith + responses: + 200: + description: Review(s) retrieved + content: + application/json: + schema: + oneOf: + - $ref: '#/components/schemas/Review' + - $ref: '#/components/schemas/User' + discriminator: + propertyName: pet_type + mapping: + review: '#/components/schemas/Review' + user: '#/components/schemas/User' + 404: + description: Review(s) not found + /reviews/{airline}: + get: + tags: + - reviews + summary: Get all reviews by airlines + operationId: getReviewByAirline + parameters: + - name: airline + in: path + description: name of the airlines for the reviews + required: true + content: + '*/*': + schema: + type: string + examples: + ? + : value: Acme Air + responses: + 200: + description: Review(s) retrieved + content: + application/json: + schema: + $ref: '#/components/schemas/Review' + 404: + description: Review(s) not found + /reviews/{user}/{airlines}: + get: + tags: + - reviews + summary: Get all reviews for an airline by User + operationId: getReviewByAirlineAndUser + parameters: + - name: user + in: path + required: true + schema: + type: string + - name: airlines + in: path + required: true + schema: + type: string + responses: + 200: + description: Review(s) retrieved + content: + application/json: + schema: + $ref: '#/components/schemas/Review' + 404: + description: Review(s) not found +components: + schemas: + User: + required: + - age + - email + - firstName + - lastName + - password + - phone + - sex + type: object + properties: + password: + type: string + example: bobSm37 + firstName: + type: string + example: Bob + lastName: + type: string + example: Smith + sex: + type: string + example: M + age: + type: integer + example: 37 + email: + type: string + example: bob@test.ca + phone: + type: string + example: 123-456-7890 + id: + type: integer + username: + type: string + status: + title: User Status + type: integer + Airline: + required: + - contactPhone + - name + type: object + properties: + name: + type: string + example: Acme Air + contactPhone: + type: string + example: 1-888-1234-567 + Flight: + required: + - airportFrom + - airportTo + - dateTime + - number + - price + - status + type: object + properties: + airline: + $ref: '#/components/schemas/Airline' + dateTime: + pattern: dateTime + type: string + example: 2016-03-05 18:00 + number: + type: string + example: AC190 + status: + type: string + example: On Schedule + airportFrom: + type: string + example: YYZ + airportTo: + type: string + example: LAX + price: + type: string + example: US$350 + Booking: + required: + - airMiles + - seatPreference + type: object + properties: + departtureFlight: + $ref: '#/components/schemas/Flight' + returningFlight: + $ref: '#/components/schemas/Flight' + creditCard: + $ref: '#/components/schemas/CreditCard' + airMiles: + type: string + example: 32126319 + seatPreference: + type: string + example: window + Review: + required: + - id + - rating + type: object + properties: + id: + type: string + example: 0 + user: + $ref: '#/components/schemas/User' + airlines: + $ref: '#/components/schemas/Airline' + rating: + type: integer + example: 8 + comment: + type: string + example: Great service! + CreditCard: + required: + - cardNumber + - cardholderName + - cvv + - expiryDate + - issuer + type: object + properties: + issuer: + type: string + example: VISA + cardholderName: + type: string + example: Joe Smith + cardNumber: + type: string + example: '**********1234' + cvv: + type: string + example: "0322" + expiryDate: + type: string + example: 04/19 + securitySchemes: + reviewoauth2: + type: oauth2 + description: authentication needed to create and delete reviews + flows: + implicit: + authorizationUrl: https://example.com/api/oauth/dialog + scopes: + write:reviews: create a review + pathItems: + latestBookings: + get: + description: retrieve latest bookings + responses: + '200': + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/Booking' diff --git a/dev/io.openliberty.microprofile.openapi.4.0.internal_fat/fat/src/io/openliberty/microprofile/openapi40/fat/validation/validation1.yml b/dev/io.openliberty.microprofile.openapi.4.0.internal_fat/fat/src/io/openliberty/microprofile/openapi40/fat/validation/validation1.yml new file mode 100644 index 00000000000..f77522e968a --- /dev/null +++ b/dev/io.openliberty.microprofile.openapi.4.0.internal_fat/fat/src/io/openliberty/microprofile/openapi40/fat/validation/validation1.yml @@ -0,0 +1,578 @@ +openapi: 3.1.0 +info: + termsOfService: not in URL format + contact: + name: AirlinesRatingApp API Support + url: not in URL Format + email: not an email + license: + url: not in URL format +externalDocs: + description: instructions for how to deploy this app + url: not a URL +servers: +- url: http://localhost:9080 +tags: +- name: Airlines + description: airlines app +- name: airline + description: all the airlines methods +- name: availability + description: all the availibility methods +- name: bookings + description: all the bookings methods +- name: reviews + description: all the review methods +paths: + noSlashPath: + get: + summary: A path with no slash + operationId: noSlashPath + /availability/{us{ser}}: + get: + summary: Invalid string path + /{username}: + parameters: + - name: pathWithUndeclaredParams + description: Path with undecalred parameters + in: path + schema: + type: string + required: true + - name: usernameParam + description: Declared path parameter + in: path + schema: + type: string + required: true + - name: accountNumber + description: Another undeclared path parameter + in: path + schema: + type: string + required: true + /operationWithParam: + get: + summary: A get method with path parameter and required set to false + parameters: + - name: userFirstName + description: username parameter + in: path + schema: + type: string + required: false + /: + get: + tags: + - airline + summary: Retrieve all available airlines + operationId: getAirlines + servers: + - url: localhost:9080/oas3-airlines{/}airlines/id + description: view of all the bookings + /availability: + get: + tags: + - availability + summary: Retrieve all available flights + operationId: getFlights + parameters: + - name: departureDate + description: Customer departure date + required: true + schema: + type: string + - name: airportFrom + in: query + description: Airport the customer departs from + required: true + schema: + type: string + - name: returningDate + in: query + description: Customer return date + required: true + schema: + type: string + - name: airportTo + in: query + description: Airport the customer returns to + required: true + schema: + type: string + - name: numberOfAdults + in: query + description: Number of adults on the flight + required: true + schema: + minimum: 0 + type: string + - name: numberOfChildren + in: query + description: Number of children on the flight + required: true + schema: + minimum: 0 + type: string + responses: + 402: + description: failed operation + content: + applictaion/json: + schema: + $ref: '#/components/schemas/Flight' + 404: + description: No available flights found + content: + n/a: {} + servers: + - url: localhost:9080/oas3-airlines{}/availability + description: view of all the bookings + /bookings: + get: + tags: + - bookings + summary: Retrieve all bookings for current user + operationId: getBookings + responses: + 200: + description: Bookings retrieved + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/Booking' + 404: + description: No bookings found for the user. + servers: + - url: localhost:9080/oas3-ai{rlines/bookings/ + description: view of all the bookings for this user + variables: + id: + description: id of the review + name1: + default: foo + enum: + - bar + - baz + name2: + default: foo + enum: [] + post: + tags: + - bookings + summary: Create a booking + description: Create a new booking record with the booking information provided. + operationId: createBooking + requestBody: + description: Create a new booking with the provided information. + content: + application/json: + schema: + $ref: '#/components/schemas/Booking' + examples: + booking: + summary: External booking example + externalValue: http://foo.bar/examples/booking-example.json + responses: + 201: + description: Booking created + content: + application/json: + schema: + type: string + description: id of the new booking + callbacks: + get all the bookings: + http://localhost:9080/airlines/bookings: {} + /bookings/{id}: + get: + tags: + - bookings + summary: Get a booking with ID + operationId: getBooking + parameters: + - name: id + in: path + description: ID of the booking + schema: + type: integer + responses: + 200: + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/Booking' + 404: + description: No bookings found for the user. + servers: + - url: localhost:9080/}oas3-airlines{/bookings/id + description: view of all the bookings + put: + tags: + - bookings + summary: Update a booking with ID + operationId: updateBooking + parameters: + - name: id + in: path + description: ID of the booking + required: true + schema: + type: integer + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/Booking' + responses: + 200: + description: Booking updated + 404: + description: Booking not found + delete: + tags: + - bookings + summary: Delete a booking with ID + operationId: deleteBooking + parameters: + - name: id + in: path + required: true + schema: + type: integer + responses: + 200: + description: Booking deleted successfully. + 404: + description: Booking not found. + /reviews: + get: + tags: + - reviews + summary: get all the reviews + operationId: getReviewById + responses: + 200: + description: successful operation + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/Review' + servers: + - description: endpoint for all the review related methods + post: + tags: + - reviews + summary: Create a Review + operationId: createReview + requestBody: + description: example review to add + required: true + responses: + 201: + description: review created + content: + application/json: + schema: + type: string + description: id of the new review + callbacks: + testCallback: + http://localhost:9080/oas3-airlines/reviews: {} + security: + - reviewoauth2: + - write:reviews + - httpTestScheme: [] + servers: + - url: localhost:9080/oas3-airlines/reviews/{id}/{extraVariable}/ + description: view of all the reviews + variables: + id: + description: id of the review + default: "1" + '': + description: test + /reviews/{id}: + get: + tags: + - reviews + summary: Get a review with ID + operationId: getReviewById + parameters: + - name: id + description: ID of the booking + required: true + content: + '*/*': + schema: + type: integer + responses: + 200: + description: Review retrieved + content: + application/json: + schema: + $ref: '#/components/schemas/Review' + 404: + description: Review not found + servers: + - url: localhost:9080/oas3-airlines/reviews/{id} + description: endpoint for all the review related methods + delete: + tags: + - reviews + summary: Delete a Review with ID + operationId: deleteReview + parameters: + - name: id + in: path + required: true + schema: + type: integer + responses: + 200: + description: Review deleted + 404: + description: Review not found + /reviews/{user}: + get: + tags: + - reviews + summary: Get all reviews by user + operationId: getReviewByUser + parameters: + - name: user + in: path + description: username of the user for the reviews + required: true + content: + '*/*': + schema: + type: string + examples: + ? + : value: bsmith + responses: + 200: + description: Review(s) retrieved + content: + application/json: + schema: + $ref: '#/components/schemas/Review' + 404: + description: Review(s) not found + /reviews/{airline}: + get: + tags: + - reviews + summary: Get all reviews by airlines + operationId: getReviewByAirline + parameters: + - name: airline + in: path + description: name of the airlines for the reviews + required: true + content: + '*/*': + schema: + type: string + examples: + ? + : value: Acme Air + - name: airline + in: path + description: name of the airlines for the reviews + required: true + content: + '*/*': + schema: + type: string + examples: + ? + : value: Acme Air + responses: + 200: + description: Review(s) retrieved + content: + application/json: + schema: + $ref: '#/components/schemas/Review' + 404: + description: Review(s) not found + /reviews/{user}/{airlines}: + get: + tags: + - reviews + summary: Get all reviews for an airline by User + operationId: getReviewByAirlineAndUser + parameters: + - name: user + in: path + required: true + schema: + type: string + - name: airlines + in: path + required: true + schema: + type: string + responses: + 200: + description: Review(s) retrieved + content: + application/json: + schema: + $ref: '#/components/schemas/Review' + 404: + description: Review(s) not found +components: + schemas: + User: + required: + - age + - email + - firstName + - lastName + - password + - phone + - sex + type: object + properties: + password: + type: string + examples: [bobSm37] + firstName: + type: string + example: Bob + lastName: + type: string + example: Smith + sex: + type: string + example: M + age: + type: integer + example: 37 + email: + type: string + example: bob@test.ca + phone: + type: string + example: 123-456-7890 + id: + type: integer + username: + type: string + status: + title: User Status + type: integer + Airline: + required: + - contactPhone + - name + type: object + properties: + name: + type: string + example: Acme Air + contactPhone: + type: string + example: 1-888-1234-567 + Flight: + required: + - airportFrom + - airportTo + - dateTime + - number + - price + - status + type: object + properties: + airline: + $ref: '#/components/schemas/Airline' + dateTime: + pattern: dateTime + type: string + example: 2016-03-05 18:00 + number: + type: string + example: AC190 + status: + type: string + example: On Schedule + airportFrom: + type: string + example: YYZ + airportTo: + type: string + example: LAX + price: + type: string + example: US$350 + Booking: + required: + - airMiles + - seatPreference + type: object + properties: + departtureFlight: + $ref: '#/components/schemas/Flight' + returningFlight: + $ref: '#/components/schemas/Flight' + creditCard: + $ref: '#/components/schemas/CreditCard' + airMiles: + type: string + example: 32126319 + seatPreference: + type: string + example: window + Review: + required: + - id + - rating + type: object + properties: + id: + type: string + example: 0 + user: + $ref: '#/components/schemas/User' + airlines: + $ref: '#/components/schemas/Airline' + rating: + type: integer + example: 8 + comment: + type: string + example: Great service! + CreditCard: + required: + - cardNumber + - cardholderName + - cvv + - expiryDate + - issuer + type: object + properties: + issuer: + type: string + example: VISA + cardholderName: + type: string + example: Joe Smith + cardNumber: + type: string + example: '**********1234' + cvv: + type: string + example: "0322" + expiryDate: + type: string + example: 04/19 \ No newline at end of file diff --git a/dev/io.openliberty.microprofile.openapi.4.0.internal_fat/fat/src/io/openliberty/microprofile/openapi40/fat/validation/validation2.yml b/dev/io.openliberty.microprofile.openapi.4.0.internal_fat/fat/src/io/openliberty/microprofile/openapi40/fat/validation/validation2.yml new file mode 100644 index 00000000000..d1fd869ffb4 --- /dev/null +++ b/dev/io.openliberty.microprofile.openapi.4.0.internal_fat/fat/src/io/openliberty/microprofile/openapi40/fat/validation/validation2.yml @@ -0,0 +1,645 @@ +openapi: 3.1.0 +info: + title: "Validation App" + version: "1.0" + termsOfService: http://www.termsofservice.com/terms + contact: + name: AirlinesRatingApp API Support + url: https://github.com/microservices-api/oas3-airlines + license: + name: Apache 2.0 + url: http://www.apache.org/licenses/LICENSE-2.0.html +externalDocs: + description: instructions for how to deploy this app +servers: +- url: http://localhost:9080 +tags: +- name: Airlines + description: airlines app +- name: airline + description: all the airlines methods +- name: availability + description: all the availibility methods +- name: bookings + description: all the bookings methods +- name: reviews + description: all the review methods +paths: + /: + get: + tags: + - airline + summary: Retrieve all available airlines + operationId: getAirlines + responses: + 200: + description: successful operation + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/Airline' + 404: + description: No airlines found + content: + n/a: {} + servers: + - url: localhost:9080/oas3-airlines/airlines/id + description: view of all the bookings + /availability: + get: + tags: + - availability + summary: Retrieve all available flights + operationId: getFlights + parameters: + - name: departureDate + in: query + description: Customer departure date + required: true + schema: + type: string + - name: airportFrom + in: query + description: Airport the customer departs from + required: true + schema: + type: string + - name: returningDate + in: query + description: Customer return date + required: true + schema: + type: string + - name: airportTo + in: query + description: Airport the customer returns to + required: true + schema: + type: string + - name: numberOfAdults + in: query + description: Number of adults on the flight + required: true + schema: + minimum: 0 + type: string + - name: numberOfChildren + in: query + description: Number of children on the flight + required: true + schema: + minimum: 0 + type: string + responses: + 200: + description: successful operation + content: + applictaion/json: + schema: + type: array + items: + $ref: '#/components/schemas/Flight' + 404: + description: No available flights found + content: + n/a: {} + security: + - schemeNotInComponent: [] + - airlinesHttp: + - write:app + - read:app + - openIdConnectWithScheme: [] + /bookings: + get: + tags: + - bookings + summary: Retrieve all bookings for current user + operationId: getBookings + security: + nullScheme: [] + responses: + 200: + description: Bookings retrieved + content: + application/json: + schema: + type: object + required: + - airMiles + - seatPreference + properties: + departtureFlight: + $ref: '#/components/schemas/Flight' + returningFlight: + $ref: '#/components/schemas/Flight' + creditCard: + $ref: '#/components/schemas/CreditCard' + airMiles: + type: string + example: 32126319 + seatPreference: + type: string + example: window + encoding: + airMiles: + contentType: text/plain + nonExistingField: + contentType: text/plain + examples: + booking: + summary: External booking example + value: http://foo.bar/examples/booking-example.json + example: + booking: + summary: booking example + value: http://foo.bar/examples/booking-example.json + 404: + description: No bookings found for the user. + servers: + - url: localhost:9080/oas3-airlines/bookings/{id} + description: view of all the bookings for this user + variables: + id: + description: id of the review + default: "1" + post: + tags: + - bookings + summary: Create a booking + description: Create a new booking record with the booking information provided. + operationId: createBooking + requestBody: + description: Create a new booking with the provided information. + content: + application/json: + schema: + $ref: '#/components/schemas/Booking' + encoding: + airMiles: + contentType: text/plain + nonExistingField: + contentType: text/plain + examples: + booking: + summary: External booking example + value: http://foo.bar/examples/booking-example.json + externalValue: http://foo.bar/examples/booking-example.json + responses: + 201: + description: Booking created + content: + application/json: + schema: + type: string + description: id of the new booking + security: + callbacks: + get all the bookings: + http://localhost:9080/airlines/bookings: {} + /bookings/{id}: + get: + tags: + - bookings + summary: Get a booking with ID + operationId: getBooking + parameters: + - name: id + in: path + description: ID of the booking + required: true + schema: + type: integer + responses: + 200: + description: Bookings retrieved + content: + application/json: + schema: + '': '' + encoding: + airMiles: + contentType: text/plain + nonExistingField: + contentType: text/plain + 404: + description: No bookings found for the user. + servers: + - url: localhost:9080/oas3-airlines/bookings/id + description: view of all the bookings + put: + tags: + - bookings + summary: Update a booking with ID + operationId: updateBooking + parameters: + - name: id + in: path + description: ID of the booking + required: true + schema: + type: integer + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/Booking' + responses: + 200: + description: Booking updated + 404: + description: Booking not found + delete: + tags: + - bookings + summary: Delete a booking with ID + operationId: deleteBooking + parameters: + - name: id + in: path + required: true + schema: + type: integer + responses: + 200: + description: Booking deleted successfully. + 404: + description: Booking not found. + /reviews: + get: + tags: [] + summary: get all the reviews + operationId: getAllReviews + responses: + 200: + description: successful operation + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/Review' + servers: + - url: http://localhost:9080/airlines/reviews/ + description: endpoint for all the review related methods + post: + tags: [] + summary: Create a Review + operationId: createReview + requestBody: + description: example review to add + content: + application/json: + schema: + $ref: '#/components/schemas/Review' + examples: + review: + summary: External review example + externalValue: http://foo.bar/examples/review-example.json + required: true + responses: + 201: + description: review created + content: + application/json: + schema: + type: string + description: id of the new review + callbacks: + testCallback: + http://localhost:9080/oas3-airlines/reviews: {} + security: + - reviewoauth2: + - write:reviews + servers: + - url: localhost:9080/oas3-airlines/reviews/{id} + description: view of all the reviews + variables: + id: + description: id of the review + default: "1" + /reviews/{id}: + get: + tags: [] + summary: Get a review with ID + operationId: getReviewById + parameters: + - name: id + in: path + description: ID of the booking + required: true + content: + '*/*': + schema: + type: integer + examples: + id: + value: 1 + responses: + 200: + description: Review retrieved + content: + application/json: + schema: + $ref: '#/components/schemas/Review' + 404: + description: Review not found + servers: + - url: http://localhost:9080/airlines/reviews/ + description: endpoint for all the review related methods + delete: + tags: [] + summary: Delete a Review with ID + operationId: deleteReview + parameters: + - name: id + in: path + required: true + schema: + type: integer + responses: + 200: + description: Review deleted + 404: + description: Review not found + servers: + - url: http://localhost:9080/airlines/reviews/ + description: endpoint for all the review related methods + /reviews/{user}: + get: + tags: [] + summary: Get all reviews by user + operationId: getReviewByUser + parameters: + - name: user + in: path + description: username of the user for the reviews + required: true + content: + '*/*': + schema: + type: string + examples: + user: + value: bsmith + responses: + 200: + description: Review(s) retrieved + content: + application/json: + schema: + $ref: '#/components/schemas/Review' + 404: + description: Review(s) not found + servers: + - url: http://localhost:9080/airlines/reviews/ + description: endpoint for all the review related methods + /reviews/{airline}: + get: + tags: [] + summary: Get all reviews by airlines + operationId: getReviewByAirline + parameters: + - name: airline + in: path + description: name of the airlines for the reviews + required: true + content: + '*/*': + schema: + type: string + examples: + airline: + value: Acme Air + responses: + 200: + description: Review(s) retrieved + content: + application/json: + schema: + $ref: '#/components/schemas/Review' + 404: + description: Review(s) not found + servers: + - url: http://localhost:9080/airlines/reviews/ + description: endpoint for all the review related methods + /reviews/{user}/{airlines}: + get: + tags: [] + summary: Get all reviews for an airline by User + operationId: getReviewByAirlineAndUser + parameters: + - name: user + in: path + required: true + schema: + type: string + - name: airlines + in: path + required: true + schema: + type: string + responses: + 200: + description: Review(s) retrieved + content: + application/json: + schema: + $ref: '#/components/schemas/Review' + 404: + description: Review(s) not found + servers: + - url: http://localhost:9080/airlines/reviews/ + description: endpoint for all the review related methods +components: + schemas: + User: + required: + - age + - email + - firstName + - lastName + - password + - phone + - sex + type: object + properties: + password: + type: string + example: bobSm37 + firstName: + type: string + example: Bob + lastName: + type: string + example: Smith + sex: + type: string + example: M + age: + type: integer + example: 37 + email: + type: string + example: bob@test.ca + phone: + type: string + example: 123-456-7890 + id: + type: integer + username: + type: string + status: + title: User Status + type: integer + Airline: + required: + - contactPhone + - name + type: object + properties: + name: + type: string + example: Acme Air + contactPhone: + type: string + example: 1-888-1234-567 + Flight: + required: + - airportFrom + - airportTo + - dateTime + - number + - price + - status + type: object + properties: + airline: + $ref: '#/components/schemas/Airline' + dateTime: + pattern: dateTime + type: string + example: 2016-03-05 18:00 + number: + type: string + example: AC190 + status: + type: string + example: On Schedule + airportFrom: + type: string + example: YYZ + airportTo: + type: string + example: LAX + price: + type: string + example: US$350 + Booking: + required: + - airMiles + - seatPreference + type: object + properties: + departtureFlight: + $ref: '#/components/schemas/Flight' + returningFlight: + $ref: '#/components/schemas/Flight' + creditCard: + $ref: '#/components/schemas/CreditCard' + airMiles: + type: string + example: 32126319 + seatPreference: + type: string + example: window + Review: + required: + - id + - rating + type: object + properties: + id: + type: string + example: 0 + user: + $ref: '#/components/schemas/User' + airlines: + $ref: '#/components/schemas/Airline' + rating: + type: integer + example: 8 + comment: + type: string + example: Great service! + CreditCard: + required: + - cardNumber + - cardholderName + - cvv + - expiryDate + - issuer + type: object + properties: + issuer: + type: string + example: VISA + cardholderName: + type: string + example: Joe Smith + cardNumber: + type: string + example: '**********1234' + cvv: + type: string + example: "0322" + expiryDate: + type: string + example: 04/19 + securitySchemes: + nullScheme: + null + noType: + description: authentication to view availabilities + openIdConnectWithScheme: + type: openIdConnect + description: authentication needed to view all the airlines + scheme: openIdConnectWithScheme + airlinesHttp: + type: http + description: authentication needed to view all the airlines + reviewoauth2: + type: oauth2 + description: authentication needed to create and delete reviews + name: oauth2WithName + availabilityApiKey: + type: openIdConnect + description: authentication to view availabilities + openIdConnectUrl: not a URL + httpWithOpenIdConnectUrl: + type: http + flows: + implicit: + authorizationUrl: https://example.com/api/oauth/dialog + scopes: + write:reviews: create a review + authorizationCode: + authorizationUrl: https://example.com/api/oauth/dialog + tokenUrl: https://example.com/api/oauth/token + password: + authorizationUrl: https://example.com/api/oauth/dialog + clientCredentials: + refreshUrl: invalid URL example + openIdConnectUrl: http://www.url.com + availabilityOpenIdConnect: + type: openIdConnect + description: authentication to view availabilities + ApiKeyWithScheme: + type: apiKey + description: authentication needed to view all the airlines + ApiKeyWithInvalidIn: + type: apiKey + name: myApiKey + in: path + description: authentication needed to view all the airlines \ No newline at end of file diff --git a/dev/io.openliberty.microprofile.openapi.4.0.internal_fat/fat/src/io/openliberty/microprofile/openapi40/fat/validation/validation4.yml b/dev/io.openliberty.microprofile.openapi.4.0.internal_fat/fat/src/io/openliberty/microprofile/openapi40/fat/validation/validation4.yml new file mode 100644 index 00000000000..7ccd2297976 --- /dev/null +++ b/dev/io.openliberty.microprofile.openapi.4.0.internal_fat/fat/src/io/openliberty/microprofile/openapi40/fat/validation/validation4.yml @@ -0,0 +1,692 @@ +openapi: 3.1.0 +info: + title: validation + version: "1.0" + termsOfService: http://www.termsofservice.com/terms + contact: + name: AirlinesRatingApp API Support + url: https://github.com/microservices-api/oas3-airlines + license: + name: Apache 2.0 + url: http://www.apache.org/licenses/LICENSE-2.0.html +externalDocs: + description: instructions for how to deploy this app + url: https://github.com/microservices-api/oas3-airlines +servers: +- url: http://localhost:9080 +tags: +- name: Airlines + description: airlines app +- name: airline + description: all the airlines methods +- name: availability + description: all the availibility methods +- name: bookings + description: all the bookings methods +- name: reviews + description: all the review methods +paths: + /: + get: + tags: + - airline + summary: Retrieve all available airlines + operationId: getAirlines + responses: + 200: + description: successful operation + content: + application/json: + schema: + type: array + items: + $ref: '#/invalidRef/schemas/testSchema' + 404: + description: No airlines found + content: + n/a: {} + servers: + - url: localhost:9080/oas3-airlines/airlines/id + description: view of all the bookings + /availability: + get: + tags: + - availability + summary: Retrieve all available flights + operationId: getFlights + parameters: + - name: departureDate + in: query + description: Customer departure date + required: true + schema: + type: object + $ref: "#/components/components/schemas/Date" + - name: airportFrom + in: query + description: Airport the customer departs from + required: true + schema: + type: object + $ref: "" + - name: returningDate + in: query + description: Customer return date + required: true + schema: + type: object + $ref: " " + - name: airportTo + in: query + description: Airport the customer returns to + required: true + schema: + type: object + $ref: "#/components/schemas/Airport/Cat" + - name: numberOfAdults + in: query + description: Number of adults on the flight + required: true + schema: + minimum: 0 + type: object + $ref: "#" + - name: numberOfChildren + in: query + description: Number of children on the flight + required: true + schema: + minimum: 0 + type: object + $ref: "#/" + - $ref: '#/components/schemas/Flight' + - $ref: http://{}/#/test + - $ref: '#/components/schemas' + responses: + 200: + description: successful operation + content: + applictaion/json: + schema: + type: array + items: + $ref: '#/components/Flight' + headers: + X-Rate-Limit-Limit: + name: x + in: header + description: The number of allowed requests in the current period + schema: + type: integer + 404: + description: No available flights found + content: + n/a: {} + /bookings: + get: + tags: + - bookings + summary: Retrieve all bookings for current user + operationId: getBookings + responses: + 200: + description: Bookings retrieved + content: + application/json: + schema: + type: array + items: + $ref: '#/components//Booking' + 404: + description: No bookings found for the user. + servers: + - url: localhost:9080/oas3-airlines/bookings/{id} + description: view of all the bookings for this user + variables: + id: + description: id of the review + default: "1" + post: + tags: + - bookings + summary: Create a booking + description: Create a new booking record with the booking information provided. + operationId: createBooking + requestBody: + $ref: "#/components/requestBodies/Pet" + responses: + 201: + description: Booking created + content: + application/json: + schema: + type: string + description: id of the new booking + callbacks: + getBookings: + ? + : + get: + summary: Retrieve all bookings for current user + operationId: getBookings3 + responses: + 200: + description: Bookings retrieved + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas' + 404: + description: No bookings found for the user. + /bookings/{id}: + get: + tags: + - bookings + summary: Get a booking with ID + operationId: getBooking + parameters: + - name: id + in: path + description: ID of the booking + required: true + schema: + type: integer + responses: + 200: + description: Bookings retrieved + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/schemas' + headers: + X-Rate-Limit-Limit: + description: The number of allowed requests in the current period + in: header + name: X-Rate-Limit-Limit + schema: + type: integer + + 404: + description: No bookings found for the user. + servers: + - url: localhost:9080/oas3-airlines/bookings/id + description: view of all the bookings + put: + tags: + - bookings + summary: Update a booking with ID + operationId: updateBooking + parameters: + - name: id + in: path + description: ID of the booking + required: true + schema: + type: integer + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/Pet' + responses: + 200: + description: Booking updated + 404: + description: Booking not found + callbacks: + testCallback: + h://localhost:9080/oas3-airlines/booking: + get: + summary: Retrieve all bookings for current user + operationId: getBookings1 + responses: + 200: + description: Bookings retrieved + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/Booking' + 404: + description: No bookings found for the user. + delete: + tags: + - bookings + summary: Delete a booking with ID + operationId: deleteBooking + parameters: + - name: id + responses: + 200: + $ref: "#/components/responses/Pet" + 404: + description: Booking not found. + callbacks: + testCallback: + http://localhost:9080/o{as3-ai{rl}ines/booking: + get: + summary: Retrieve all bookings for current user + operationId: getBookings2 + responses: + 200: + description: Bookings retrieved + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/Booking' + 404: + description: No bookings found for the user. + /bookings/latest: + $ref: '#/components/pathItems/latestBookings' + /reviews: + get: + tags: + - reviews + summary: get all the reviews + operationId: getAllReviews + responses: + 200: + description: successful operation + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/Review' + servers: + - url: http://localhost:9080/airlines/reviews/ + description: endpoint for all the review related methods + post: + tags: + - reviews + summary: Create a Review + operationId: createReview + requestBody: + description: example review to add + content: + application/json: + schema: + $ref: '#/components/schemas/Review' + examples: + review: + $ref: "#/components/examples/Pet" + required: true + responses: + 201: + description: review created + content: + application/json: + schema: + type: string + description: id of the new review + callbacks: + testCallback: + http://localhost:9080/oas3-airlines/reviews: {} + security: + - reviewoauth2: + - write:reviews + servers: + - url: localhost:9080/oas3-airlines/reviews/{id} + description: view of all the reviews + variables: + id: + description: id of the review + default: "1" + put: + tags: + - reviews + summary: Update a review with ID + operationId: updateReview + parameters: + - name: id + in: path + description: ID of the review + required: true + schema: + type: integer + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/Review' + links: + review: + operationRef: "#/paths/reviews/get" + operationId: getUserREview + parameters: + userId: $request.path.id + responses: + 200: + description: Review updated + 404: + description: Review not found + callbacks: + testCallback: + http://abc.com/path/{$url}/version/{$method}/root/{$statusCodeXXX}/path: + get: + summary: Retrieve all reviews for current user + operationId: getReviews + responses: + 200: + description: Rview retrieved + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/Review' + 404: + description: No bookings found for the user. + /reviews/{id}: + get: + tags: + - reviews + summary: Get a review with ID + operationId: getReviewById + parameters: + - name: id + in: path + description: ID of the booking + required: true + content: + '*/*': + schema: + type: integer + examples: + id: + value: 1 + responses: + 200: + description: Review retrieved + content: + application/json: + schema: + $ref: '#/components/schemas/Review' + 404: + description: Review not found + servers: + - url: http://localhost:9080/airlines/reviews/ + description: endpoint for all the review related methods + delete: + tags: + - reviews + summary: Delete a Review with ID + operationId: deleteReview + parameters: + - name: id + in: path + required: true + schema: + type: integer + responses: + 200: + description: Review deleted + 404: + description: Review not found + servers: + - url: http://localhost:9080/airlines/reviews/ + description: endpoint for all the review related methods + /reviews/{user}: + get: + tags: + - reviews + summary: Get all reviews by user + operationId: getReviewByUser + parameters: + - name: user + in: path + description: username of the user for the reviews + required: true + content: + '*/*': + schema: + type: string + examples: + user: + value: bsmith + responses: + 200: + description: Review(s) retrieved + content: + application/json: + schema: + $ref: '#/components/schemas/Review' + 404: + description: Review(s) not found + servers: + - url: http://localhost:9080/airlines/reviews/ + description: endpoint for all the review related methods + /reviews/{airline}: + get: + tags: + - reviews + summary: Get all reviews by airlines + operationId: getReviewByAirline + parameters: + - name: airline + in: path + description: name of the airlines for the reviews + required: true + content: + '*/*': + schema: + type: string + examples: + airline: + value: Acme Air + - name: airline + in: path + description: name of the airlines for the reviews + required: true + content: + '*/*': + schema: + type: string + examples: + airline: + value: Acme Air + responses: + 200: + description: Review(s) retrieved + content: + application/json: + schema: + $ref: '#/components/schemas/Review' + 404: + description: Review(s) not found + servers: + - url: http://localhost:9080/airlines/reviews/ + description: endpoint for all the review related methods + /reviews/{user}/{airlines}: + get: + tags: + - reviews + summary: Get all reviews for an airline by User + operationId: getReviewByAirlineAndUser + parameters: + - name: user + in: path + required: true + schema: + type: string + - name: airlines + in: path + required: true + schema: + type: string + responses: + 200: + description: Review(s) retrieved + content: + application/json: + schema: + $ref: '#/components/schemas/Review' + 404: + description: Review(s) not found + servers: + - url: http://localhost:9080/airlines/reviews/ + description: endpoint for all the review related methods +components: + schemas: + User: + required: + - age + - email + - firstName + - lastName + - password + - phone + - sex + type: object + properties: + password: + type: string + example: bobSm37 + firstName: + type: string + example: Bob + lastName: + type: string + example: Smith + sex: + type: string + example: M + age: + type: integer + example: 37 + email: + type: string + example: bob@test.ca + phone: + type: string + example: 123-456-7890 + id: + type: integer + username: + type: string + status: + title: User Status + type: integer + Airline: + required: + - contactPhone + - name + type: object + properties: + name: + type: string + example: Acme Air + contactPhone: + type: string + example: 1-888-1234-567 + Flight: + required: + - airportFrom + - airportTo + - dateTime + - number + - price + - status + type: object + properties: + airline: + $ref: '#/components/schemas/Airline' + dateTime: + pattern: dateTime + type: string + example: 2016-03-05 18:00 + number: + type: string + example: AC190 + status: + type: string + example: On Schedule + airportFrom: + type: string + example: YYZ + airportTo: + type: string + example: LAX + price: + type: string + example: US$350 + Booking: + required: + - airMiles + - seatPreference + type: object + properties: + departtureFlight: + $ref: '#/components/schemas/Flight' + returningFlight: + $ref: '#/components/schemas/Flight' + creditCard: + $ref: '#/components/schemas/CreditCard' + airMiles: + type: string + example: 32126319 + seatPreference: + type: string + example: window + Review: + required: + - id + - rating + type: object + properties: + id: + type: string + example: 0 + user: + $ref: '#/components/schemas/User' + airlines: + $ref: '#/components/schemas/Airline' + rating: + type: integer + example: 8 + comment: + type: string + example: Great service! + CreditCard: + required: + - cardNumber + - cardholderName + - cvv + - expiryDate + - issuer + type: object + properties: + issuer: + type: string + example: VISA + cardholderName: + type: string + example: Joe Smith + cardNumber: + type: string + example: '**********1234' + cvv: + type: string + example: "0322" + expiryDate: + type: string + example: 04/19 + securitySchemes: + reviewoauth2: + type: oauth2 + description: authentication needed to create and delete reviews + flows: + implicit: + authorizationUrl: https://example.com/api/oauth/dialog + scopes: + write:reviews: create a review + authorizationCode: + authorizationUrl: https://example.com/api/oauth/dialog + tokenUrl: https://example.com/api/oauth/token \ No newline at end of file diff --git a/dev/io.openliberty.microprofile.openapi.4.0.internal_fat/fat/src/io/openliberty/microprofile/openapi40/fat/validation/validation5.yml b/dev/io.openliberty.microprofile.openapi.4.0.internal_fat/fat/src/io/openliberty/microprofile/openapi40/fat/validation/validation5.yml new file mode 100644 index 00000000000..8519d4cee39 --- /dev/null +++ b/dev/io.openliberty.microprofile.openapi.4.0.internal_fat/fat/src/io/openliberty/microprofile/openapi40/fat/validation/validation5.yml @@ -0,0 +1,590 @@ +openapi: 3.1.0 +info: + title: Validation App + version: 1.0 + termsOfService: http://www.termsofservice.com + contact: + name: AirlinesRatingApp API Support + url: http://www.contacts.com + email: airlines@gmail.com + license: + name: Apache 2.0 + url: http://www.license.com +externalDocs: + description: instructions for how to deploy this app + url: http://www.externaldocumentation.com +servers: +- url: http://localhost:9080 +tags: +- description: airlines app +- name: airline + description: all the airlines methods +- name: availability + description: all the availibility methods +- name: bookings + description: all the bookings methods +- name: reviews + description: all the review methods +paths: + /: + get: + tags: + - airline + summary: Retrieve all available airlines + responses: + 202: + description: failed operation + content: + applictaion/json: + schema: + $ref: '#/components/schemas/Flight' + operationId: getAirlines + servers: + - url: localhost:9080/oas3-airlines/airlines/id + description: view of all the bookings + /availability: + get: + tags: + - availability + summary: Retrieve all available flights + operationId: getFlights + parameters: + - name: departureDate + description: Customer departure date + required: true + schema: + type: string + readOnly: true + writeOnly: true + uniqueItems: true + maxLength: -1 + minLength: -3 + - name: airportFrom + in: query + description: Airport the customer departs from + required: true + schema: + type: array + maxItems: -2 + minItems: -3 + multipleOf: 0 + - name: returningDate + in: query + description: Customer return date + required: true + schema: + type: object + minItems: 1 + maxItems: 5 + minProperties: -3 + maxProperties: -5 + - name: airportTo + in: query + description: Airport the customer returns to + required: true + schema: + type: string + - name: numberOfAdults + in: query + description: Number of adults on the flight + required: true + schema: + minimum: 0 + type: string + - name: numberOfChildren + in: query + description: Number of children on the flight + required: true + schema: + minimum: 0 + type: string + responses: + 202: + description: failed operation + content: + applictaion/json: + schema: + $ref: '#/components/schemas/Flight' + 404: + description: No available flights found + content: + n/a: {} + servers: + - url: localhost:9080/oas3-airlines/availability + description: view of all the bookings + /bookings: + get: + tags: + - bookings + summary: Retrieve all bookings for current user + operationId: getBookings + responses: + 200: + description: Bookings retrieved + content: + application/json: + schema: + type: array + items: + 404: + description: No bookings found for the user. + post: + tags: + - bookings + summary: Create a booking + description: Create a new booking record with the booking information provided. + operationId: createBooking + requestBody: + description: Create a new booking with the provided information. + content: + application/json: + schema: + $ref: '#/components/schemas/Booking' + examples: + booking: + summary: External booking example + externalValue: http://foo.bar/examples/booking-example.json + responses: + 201: + description: Booking created + content: + application/json: + schema: + type: string + description: id of the new booking + callbacks: + get all the bookings: + http://localhost:9080/airlines/bookings: + get: + tags: + - bookings + summary: Get a booking with ID + operationId: getBookingCallback + extensions: + - name: 'invalidExtensionVal' + value: true + parameters: + - name: id + required: true + in: query + description: ID of the booking + schema: + type: integer + responses: + 200: + description: booking retrieved + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/Booking' + 404: + description: No bookings found for the user. + /bookings/{id}: + get: + tags: + - bookings + summary: Get a booking with ID + operationId: getBooking + parameters: + - name: id + required: true + in: path + description: ID of the booking + schema: + type: integer + responses: + 200: + description: booking retrieved + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/Booking' + 404: + description: No bookings found for the user. + servers: + - url: localhost:9080/oas3-airlines/bookings/{id} + description: view of all the bookings for this user + variables: + id: + default: "1" + description: id of the review + put: + tags: + - bookings + summary: Update a booking with ID + operationId: updateBooking + parameters: + - name: id + in: path + description: ID of the booking + required: true + schema: + type: integer + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/Booking' + responses: + 200: + description: Booking updated + 404: + description: Booking not found + delete: + tags: + - bookings + summary: Delete a booking with ID + operationId: deleteBooking + parameters: + - name: id + in: path + required: true + schema: + type: integer + responses: + 200: + description: Booking deleted successfully. + 404: + description: Booking not found. + /reviews: + get: + tags: + - reviews + summary: get all the reviews + operationId: getReview + responses: + 200: + description: successful operation + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/Review' + servers: + - url: localhost:9080/oas3-airlines/reviews + description: endpoint for all the review related methods + post: + tags: + - reviews + summary: Create a Review + operationId: createReview + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/Review' + description: example review to add + required: true + responses: + 201: + description: review created + content: + application/json: + schema: + type: string + description: id of the new review + callbacks: + testCallback: + http://localhost:9080/oas3-airlines/reviews: {} + security: + - reviewoauth2: + - write:reviews + servers: + - url: localhost:9080/oas3-airlines/reviews/{id} + description: view of all the reviews + variables: + id: + description: id of the review + default: "1" + /reviews/{id}: + get: + tags: + - reviews + summary: Get a review with ID + operationId: getReviewById + parameters: + - name: id + in: path + description: ID of the booking + required: true + content: + '*/*': + schema: + type: integer + responses: + 200: + description: Review retrieved + content: + application/json: + schema: + $ref: '#/components/schemas/Review' + 404: + description: Review not found + servers: + - url: localhost:9080/oas3-airlines/reviews/{id} + description: endpoint for all the review related methods + variables: + id: + default: "11" + description: id of the review + delete: + tags: + - reviews + summary: Delete a Review with ID + operationId: deleteReview + parameters: + - name: id + in: path + required: true + schema: + type: integer + responses: + 200: + description: Review deleted + 404: + description: Review not found + /reviews/{user}: + get: + tags: + - reviews + summary: Get all reviews by user + operationId: getReviewByUser + parameters: + - name: user + in: path + description: username of the user for the reviews + required: true + content: + '*/*': + schema: + type: string + examples: + ? + : value: bsmith + responses: + 200: + description: Review(s) retrieved + content: + application/json: + schema: + oneOf: + - $ref: '#/components/schemas/Review' + - $ref: '#/components/schemas/User' + discriminator: + mapping: + review: '#/components/schemas/Review' + user: '#/components/schemas/User' + 404: + description: Review(s) not found + /reviews/{airline}: + get: + tags: + - reviews + summary: Get all reviews by airlines + operationId: getReviewByAirline + parameters: + - name: airline + in: path + description: name of the airlines for the reviews + required: true + content: + '*/*': + schema: + type: string + examples: + ? + : value: Acme Air + responses: + 200: + description: Review(s) retrieved + content: + application/json: + schema: + $ref: '#/components/schemas/Review' + 404: + description: Review(s) not found + /reviews/{user}/{airlines}: + get: + tags: + - reviews + summary: Get all reviews for an airline by User + operationId: getReviewByAirlineAndUser + parameters: + - name: user + in: path + required: true + schema: + type: string + - name: airlines + in: path + required: true + schema: + type: string + responses: + 200: + description: Review(s) retrieved + content: + application/json: + schema: + $ref: '#/components/schemas/Review' + 404: + description: Review(s) not found +components: + schemas: + User: + required: + - age + - email + - firstName + - lastName + - password + - phone + - sex + type: object + properties: + password: + type: string + example: bobSm37 + firstName: + type: string + example: Bob + lastName: + type: string + example: Smith + sex: + type: string + example: M + age: + type: integer + example: 37 + email: + type: string + example: bob@test.ca + phone: + type: string + example: 123-456-7890 + id: + type: integer + username: + type: string + status: + title: User Status + type: integer + Airline: + required: + - contactPhone + - name + type: object + properties: + name: + type: string + example: Acme Air + contactPhone: + type: string + example: 1-888-1234-567 + Flight: + required: + - airportFrom + - airportTo + - dateTime + - number + - price + - status + type: object + properties: + airline: + $ref: '#/components/schemas/Airline' + dateTime: + pattern: dateTime + type: string + example: 2016-03-05 18:00 + number: + type: string + example: AC190 + status: + type: string + example: On Schedule + airportFrom: + type: string + example: YYZ + airportTo: + type: string + example: LAX + price: + type: string + example: US$350 + Booking: + required: + - airMiles + - seatPreference + type: object + properties: + departtureFlight: + $ref: '#/components/schemas/Flight' + returningFlight: + $ref: '#/components/schemas/Flight' + creditCard: + $ref: '#/components/schemas/CreditCard' + airMiles: + type: string + example: 32126319 + seatPreference: + type: string + example: window + Review: + required: + - id + - rating + type: object + properties: + id: + type: string + example: 0 + user: + $ref: '#/components/schemas/User' + airlines: + $ref: '#/components/schemas/Airline' + rating: + type: integer + example: 8 + comment: + type: string + example: Great service! + CreditCard: + required: + - cardNumber + - cardholderName + - cvv + - expiryDate + - issuer + type: object + properties: + issuer: + type: string + example: VISA + cardholderName: + type: string + example: Joe Smith + cardNumber: + type: string + example: '**********1234' + cvv: + type: string + example: "0322" + expiryDate: + type: string + example: 04/19 + securitySchemes: + reviewoauth2: + type: oauth2 + description: authentication needed to create and delete reviews + flows: + implicit: + authorizationUrl: https://example.com/api/oauth/dialog + scopes: + write:reviews: create a review diff --git a/dev/io.openliberty.microprofile.openapi.4.0.internal_fat/publish/servers/OpenAPIValidationServer/bootstrap.properties b/dev/io.openliberty.microprofile.openapi.4.0.internal_fat/publish/servers/OpenAPIValidationServer/bootstrap.properties new file mode 100644 index 00000000000..25dde95fb6c --- /dev/null +++ b/dev/io.openliberty.microprofile.openapi.4.0.internal_fat/publish/servers/OpenAPIValidationServer/bootstrap.properties @@ -0,0 +1,2 @@ +bootstrap.include=../testports.properties +com.ibm.ws.logging.trace.specification=*=info=enabled:mpOpenAPI=debug \ No newline at end of file diff --git a/dev/io.openliberty.microprofile.openapi.4.0.internal_fat/publish/servers/OpenAPIValidationServer/server.xml b/dev/io.openliberty.microprofile.openapi.4.0.internal_fat/publish/servers/OpenAPIValidationServer/server.xml new file mode 100644 index 00000000000..76b2a5ccaf5 --- /dev/null +++ b/dev/io.openliberty.microprofile.openapi.4.0.internal_fat/publish/servers/OpenAPIValidationServer/server.xml @@ -0,0 +1,13 @@ + + + + + mpOpenAPI-4.0 + componenttest-2.0 + restfulWS-3.1 + mpConfig-3.1 + + + + + \ No newline at end of file diff --git a/dev/io.openliberty.microprofile.openapi.internal.common/resources/io/openliberty/microprofile/openapi/internal/resources/validation/ValidationMessages.nlsprops b/dev/io.openliberty.microprofile.openapi.internal.common/resources/io/openliberty/microprofile/openapi/internal/resources/validation/ValidationMessages.nlsprops index dbd8d31d8e5..3c9dc9b28e2 100644 --- a/dev/io.openliberty.microprofile.openapi.internal.common/resources/io/openliberty/microprofile/openapi/internal/resources/validation/ValidationMessages.nlsprops +++ b/dev/io.openliberty.microprofile.openapi.internal.common/resources/io/openliberty/microprofile/openapi/internal/resources/validation/ValidationMessages.nlsprops @@ -1,4 +1,4 @@ -# Copyright (c) 2018 IBM Corporation and others. +# Copyright (c) 2018, 2024 IBM Corporation and others. # All rights reserved. This program and the accompanying materials # are made available under the terms of the Eclipse Public License 2.0 # which accompanies this distribution, and is available at @@ -64,6 +64,8 @@ nullValueInMap=The map specifies an invalid value for the \"{0}\" key. A map sho openAPIVersionInvalid=The OpenAPI Object must contain a valid OpenAPI specification version. The \"{0}\" value specified for the OpenAPI specification version is not valid openAPITagIsNotUnique=The OpenAPI Object must contain unique tag names. The \"{0}\" tag name is not unique +#3.1 +openAPIMissingRequiredFields=The OpenAPI Object must contain at least one of the \"paths\", \"components\", or \"webhooks\" properties operationIdsMustBeUnique=More than one Operation Objects with \"{0}\" value for \"operationId\" field was found. The \"operationId\" must be unique among all operations described in the API @@ -94,6 +96,7 @@ referenceNotValid=The \"{0}\" reference value is invalid referenceNotValidFormat=The \"{0}\" reference value is not in a valid format referenceNull=The reference value is null referenceToObjectInvalid=The \"{0}\" value is an invalid reference +referenceNotValidUri=The \"{0}\" value is not a valid URI responseMustContainOneCode=The Responses Object must contain at least one response code responseShouldContainSuccess=The Responses Object should contain at least one response code for a successful operation @@ -112,9 +115,21 @@ schemaMultipleOfNotGreaterThanZero=The Schema Object must have the \"multipleOf\ schemaPropertyLessThanZero=The \"{0}\" property of the Schema Object must be greater than or equal to zero schemaTypeDoesNotMatchProperty=The \"{0}\" property is not appropriate for the Schema Object of \"{1}\" type +#3.1 +schemaPropertyWrongType=The type of the \"{0}\" property of the Schema Object must be \"{1}\" +schemaPropertyEmptyArray=The \"{0}\" property of the Schema Object is an empty array +schemaPropertyNotUnique=The elements of the \"{0}\" property of the Schema Object are not unique +schemaInvalidType=The \"type\" property of the Schema Object must contain values that are one of (\"null\", \"boolean\", \"object\", \"array\", \"number\", \"integer\", or \"string\") +schemaCollectionElementWrongType=The \"{0}\" property of the Schema Object contains a value whose type is not \"{1}\" +schemaDependentRequiredUniqueStrings=Each element of the \"dependentRequired\" property must be an array of unique strings + keyNotARegex=The \"{0}\" name declared within the Components Object is invalid. It must match the regular expression as defined by the OpenAPI Specification serverVariableNotDefined=The \"{0}\" variable in the Server Object is not defined serverInvalidURL=The Server Object must contain a valid URL. The \"{0}\" value specified for the URL is not valid +#3.1 +serverVariableArrayEmpty=The \"{0}\" array in the Server Variable Object is empty +serverVariableDefaultNotInEnum=The \"{0}\" value of the \"default\" property is not listed in the \"enum\" array + requiredFieldMissing=Required \"{0}\" field is missing or is set to an invalid value