Fix OpenApi ignoring min-elements for list
[netconf.git] / restconf / restconf-openapi / src / test / java / org / opendaylight / restconf / openapi / impl / OpenApiGeneratorRFC8040Test.java
index 51702c79fb9b13c84f35b5706b804c15620640b7..711dc48c00ced9ee0f66839e3277f71401f2a24c 100644 (file)
@@ -45,7 +45,6 @@ public final class OpenApiGeneratorRFC8040Test {
     private static final String CONFIG_ROOT_CONTAINER = "mandatory-test_root-container";
     private static final String CONFIG_MANDATORY_CONTAINER = "mandatory-test_root-container_mandatory-container";
     private static final String CONFIG_MANDATORY_LIST = "mandatory-test_root-container_mandatory-list";
-    private static final String MANDATORY_TEST_MODULE = "mandatory-test_module";
     private static final String CONTAINER = "container";
     private static final String LIST = "list";
 
@@ -94,8 +93,8 @@ public final class OpenApiGeneratorRFC8040Test {
                 "/rests/data/toaster2:lst={lf1}/cont1/cont11",
                 "/rests/data/toaster2:lst={lf1}/cont1/lst11={lf111}",
                 "/rests/data/toaster2:lst={lf1}/lst1={key1},{key2}");
-        final List<String> configPathsForPost = List.of("/rests/data/toaster2:lst={lf1}/cont1",
-                "/rests/data/toaster2:lst={lf1}/cont1/cont11");
+        final String configPathForPostCont = "/rests/data/toaster2:lst={lf1}/cont1";
+        final String configPathForPostLeaf = "/rests/data/toaster2:lst={lf1}/cont1/cont11";
 
         final OpenApiObject doc = generator.getApiDeclaration(TOASTER_2, REVISION_DATE, uriInfo);
 
@@ -107,10 +106,12 @@ public final class OpenApiGeneratorRFC8040Test {
             assertNotNull(node.patch());
         }
 
-        for (final String path : configPathsForPost) {
-            final Path node = doc.paths().get(path);
-            assertNotNull(node.post());
-        }
+        final Path node = doc.paths().get(configPathForPostCont);
+        assertNotNull(node.post());
+
+        // Assert we do not generate post for container which contains only leafs.
+        final Path nodeLeaf = doc.paths().get(configPathForPostLeaf);
+        assertNull(nodeLeaf.post());
     }
 
     /**
@@ -210,10 +211,6 @@ public final class OpenApiGeneratorRFC8040Test {
         verifyRequiredField(schemas.get(CONFIG_MANDATORY_LIST), reqMandatoryListElements);
         containersWithRequired.add(CONFIG_MANDATORY_LIST);
 
-        final var testModuleMandatoryArray = List.of("root-container", "root-mandatory-list");
-        verifyRequiredField(schemas.get(MANDATORY_TEST_MODULE), testModuleMandatoryArray);
-        containersWithRequired.add(MANDATORY_TEST_MODULE);
-
         verifyThatOthersNodeDoesNotHaveRequiredField(containersWithRequired, schemas);
     }
 
@@ -294,6 +291,22 @@ public final class OpenApiGeneratorRFC8040Test {
         assertEquals(List.of("name"), getPathGetParameters(doc.paths(), pathToList5));
     }
 
+    /**
+     * Test that request parameters are correctly typed.
+     */
+    @Test
+    public void testParametersTypes() {
+        final var doc = generator.getApiDeclaration("typed-params", "2023-10-24", uriInfo);
+        final var pathToContainer = "/rests/data/typed-params:typed/";
+        final var integerTypes = List.of("uint64", "uint32", "uint16", "uint8", "int64", "int32", "int16", "int8");
+        for (final var type: integerTypes) {
+            final var typeKey = type + "-key";
+            final var path = pathToContainer + type + "={" + typeKey + "}";
+            assertTrue(doc.paths().containsKey(path));
+            assertEquals("integer", doc.paths().get(path).get().parameters().get(0).schema().type());
+        }
+    }
+
     /**
      * Test that request for actions is correct and has parameters.
      */
@@ -316,16 +329,13 @@ public final class OpenApiGeneratorRFC8040Test {
 
         assertEquals(Set.of("/rests/data", "/rests/data/my-yang:data"), doc.paths().keySet());
         final var JsonNodeMyYangData = doc.paths().get("/rests/data/my-yang:data");
-        verifyPostDataRequestRef(JsonNodeMyYangData.post(), "#/components/schemas/my-yang_data",
-            "#/components/schemas/my-yang_data");
         verifyRequestRef(JsonNodeMyYangData.put(), "#/components/schemas/my-yang_data", CONTAINER);
         verifyRequestRef(JsonNodeMyYangData.get(), "#/components/schemas/my-yang_data", CONTAINER);
 
         // Test `components/schemas` objects
         final var definitions = doc.components().schemas();
-        assertEquals(2, definitions.size());
+        assertEquals(1, definitions.size());
         assertTrue(definitions.containsKey("my-yang_data"));
-        assertTrue(definitions.containsKey("my-yang_module"));
     }
 
     @Test
@@ -343,8 +353,6 @@ public final class OpenApiGeneratorRFC8040Test {
 
         final var jsonNodeSlotInfo = doc.paths().get(
             "/rests/data/toaster2:toaster/toasterSlot={slotId}/toaster-augmented:slotInfo");
-        verifyPostDataRequestRef(jsonNodeSlotInfo.post(), "#/components/schemas/toaster2_toaster_toasterSlot_slotInfo",
-            "#/components/schemas/toaster2_toaster_toasterSlot_slotInfo");
         verifyRequestRef(jsonNodeSlotInfo.put(), "#/components/schemas/toaster2_toaster_toasterSlot_slotInfo",
             CONTAINER);
         verifyRequestRef(jsonNodeSlotInfo.get(), "#/components/schemas/toaster2_toaster_toasterSlot_slotInfo",
@@ -373,7 +381,7 @@ public final class OpenApiGeneratorRFC8040Test {
 
         // Test `components/schemas` objects
         final var definitions = doc.components().schemas();
-        assertEquals(11, definitions.size());
+        assertEquals(10, definitions.size());
     }
 
     /**
@@ -444,23 +452,108 @@ public final class OpenApiGeneratorRFC8040Test {
     }
 
     /**
-     *  Test JSON and XML references for request operation.
+     * Test that checks if list min-elements and max-elements are present.
+     * Also checks if number of example elements meets the min-elements condition
+     * and if key defined leaf have unique values.
      */
-    private static void verifyPostDataRequestRef(final Operation operation, final String expectedJsonRef,
-            final String expectedXmlRef) {
-        final Map<String, MediaTypeObject> postContent;
-        if (operation.requestBody() != null) {
-            postContent = operation.requestBody().content();
-        } else {
-            postContent = operation.responses().get("200").content();
+    @Test
+    public void testListExamplesWithNonKeyLeaf() {
+        final var doc = generator.getApiDeclaration("test-container-childs", "2023-09-28", uriInfo);
+        assertNotNull("Failed to find Datastore API", doc);
+        final var components = doc.components();
+        final var component = components.schemas().get("test-container-childs_root-container_nested-container");
+        assertNotNull(component);
+        assertNotNull(component.properties());
+        final var property = component.properties().get("mandatory-list");
+        assertNotNull(property);
+        assertNotNull(property.minItems());
+        assertNotNull(property.maxItems());
+        assertEquals(3, (int) property.minItems());
+        assertEquals(5, (int) property.maxItems());
+        final var example = property.example();
+        assertNotNull(example);
+        assertEquals(3, ((List<?>)example).size());
+        assertTrue(checkUniqueExample(example, "id"));
+    }
+
+    /**
+     * Test that checks if multiple key leafs have unique values.
+     * Also checks if nested container node is ignored.
+     */
+    @Test
+    public void testListExamplesWithTwoKeys() {
+        final var doc = generator.getApiDeclaration("test-container-childs", "2023-09-28", uriInfo);
+        assertNotNull("Failed to find Datastore API", doc);
+        final var components = doc.components();
+        final var component = components.schemas()
+            .get("test-container-childs_root-container-two-keys_nested-container-two-keys");
+        assertNotNull(component);
+        assertNotNull(component.properties());
+        final var property = component.properties().get("mandatory-list-two-keys");
+        assertNotNull(property);
+        final var example = property.example();
+        assertNotNull(example);
+        assertTrue(checkUniqueExample(example, "id"));
+        assertTrue(checkUniqueExample(example, "name"));
+        assertEquals(3, ((ArrayList<Map<?,?>>)example).get(0).size());
+    }
+
+    /**
+     * Test that checks if sets of unique defined leafs have unique combination of values.
+     */
+    @Test
+    public void testListExamplesWithUnique() {
+        final var doc = generator.getApiDeclaration("test-container-childs", "2023-09-28", uriInfo);
+        assertNotNull("Failed to find Datastore API", doc);
+        final var components = doc.components();
+        final var component = components.schemas()
+            .get("test-container-childs_root-container-unique_nested-container-unique");
+        assertNotNull(component);
+        assertNotNull(component.properties());
+        final var property = component.properties().get("mandatory-list-unique");
+        assertNotNull(property);
+        final var example = property.example();
+        assertNotNull(example);
+        assertTrue(checkUniqueExample(example, "id"));
+        assertTrue(checkUniqueExample(example, "name") || checkUniqueExample(example, "address"));
+    }
+
+    private static boolean checkUniqueExample(final Object examples, final String key) {
+        assertEquals(ArrayList.class, examples.getClass());
+        final var exampleValues = new HashSet<>();
+
+        for (final Map<String, Object> example : (ArrayList<Map<String, Object>>)examples) {
+            exampleValues.add(example.get(key));
         }
-        assertNotNull(postContent);
-        final var postJsonRef = postContent.get("application/json").schema().ref();
-        assertNotNull(postJsonRef);
-        assertEquals(expectedJsonRef, postJsonRef);
-        final var postXmlRef = postContent.get("application/xml").schema().ref();
-        assertNotNull(postXmlRef);
-        assertEquals(expectedXmlRef, postXmlRef);
+        return (exampleValues.size() == ((ArrayList<?>) examples).size());
+    }
+
+    /**
+     * Test that number of elements in payload is correct.
+     */
+    @SuppressWarnings("unchecked")
+    @Test
+    public void testLeafListWithMinElementsPayload() {
+        final var doc = generator.getApiDeclaration(MANDATORY_TEST, null, uriInfo);
+        assertNotNull(doc);
+        final var paths = doc.paths();
+        final var path = paths.get("/rests/data/mandatory-test:root-container/mandatory-container");
+        assertNotNull(path);
+        final var requestBody = path.put().requestBody().content();
+        assertNotNull(requestBody);
+        final var jsonRef = requestBody.get("application/json").schema().properties()
+            .get("mandatory-test:mandatory-container").ref();
+        assertNotNull(jsonRef);
+        final var xmlRef = requestBody.get("application/xml").schema().ref();
+        assertNotNull(xmlRef);
+        final var schema = doc.components().schemas().get("mandatory-test_root-container_mandatory-container");
+        assertNotNull(schema);
+        final var minItems = schema.properties().get("leaf-list-with-min-elements").minItems();
+        assertNotNull(minItems);
+        final var listOfExamples = ((List<String>) schema.properties().get("leaf-list-with-min-elements").example());
+        assertNotNull(listOfExamples);
+        assertEquals(jsonRef, xmlRef);
+        assertEquals(listOfExamples.size(), minItems.intValue());
     }
 
     private static void verifyRequestRef(final Operation operation, final String expectedRef, final String nodeType) {