OpenApi create required operational data 94/106894/15
authorPeter Suna <peter.suna@pantheon.tech>
Thu, 13 Jul 2023 13:23:05 +0000 (15:23 +0200)
committerIvan Hrasko <ivan.hrasko@pantheon.tech>
Tue, 22 Aug 2023 11:09:01 +0000 (13:09 +0200)
Create only required operational data to provide example
for operational GET request.
This removed duplicity with configurational and operational nodes
in OpenApi schemas by creating only operational container and lists.

For container and lists are created specific GET request only for
operational data. This patch solves this by creating only required
operational data and not mixing them with configurational data.

JIRA: NETCONF-1142
Change-Id: I4f1f544f9cf65b6f58ad9c2068cec6ac1c0927f0
Signed-off-by: Peter Suna <peter.suna@pantheon.tech>
Signed-off-by: Ivan Hrasko <ivan.hrasko@pantheon.tech>
restconf/restconf-openapi/src/main/java/org/opendaylight/restconf/openapi/impl/DefinitionGenerator.java
restconf/restconf-openapi/src/main/java/org/opendaylight/restconf/openapi/model/builder/OperationBuilder.java
restconf/restconf-openapi/src/test/java/org/opendaylight/restconf/openapi/OperationalDataTest.java

index fa023a07190ff8914c0a0e41cda64b24aa9b3122..5404d243d81add99d2a6ac983d9b10edefdb0127 100644 (file)
@@ -255,10 +255,8 @@ public final class DefinitionGenerator {
             stack.enterSchemaTree(childNode.getQName());
             // For every container and list in the module
             if (childNode instanceof ContainerSchemaNode || childNode instanceof ListSchemaNode) {
-                if (childNode.isConfiguration()) {
-                    processDataNodeContainer((DataNodeContainer) childNode, moduleName, definitions, definitionNames,
-                        stack, module);
-                }
+                processDataNodeContainer((DataNodeContainer) childNode, moduleName, definitions, definitionNames,
+                    stack, module, true);
                 processActionNodeContainer(childNode, moduleName, definitions, definitionNames, stack, module);
             }
             stack.exit();
@@ -308,7 +306,7 @@ public final class DefinitionGenerator {
                 .type(OBJECT_TYPE)
                 .xml(JsonNodeFactory.instance.objectNode().put(NAME_KEY, isInput ? INPUT : OUTPUT));
             processChildren(childSchemaBuilder, container.getChildNodes(), parentName, definitions, definitionNames,
-                stack, module);
+                stack, module, false);
             final String discriminator =
                 definitionNames.pickDiscriminator(container, List.of(filename));
             definitions.put(filename + discriminator, childSchemaBuilder.build());
@@ -370,7 +368,7 @@ public final class DefinitionGenerator {
 
     private static ObjectNode processDataNodeContainer(final DataNodeContainer dataNode, final String parentName,
             final Map<String, Schema> definitions, final DefinitionNames definitionNames,
-            final SchemaInferenceStack stack, final Module module) throws IOException {
+            final SchemaInferenceStack stack, final Module module, final boolean isParentConfig) throws IOException {
         final Collection<? extends DataSchemaNode> containerChildren = dataNode.getChildNodes();
         final SchemaNode schemaNode = (SchemaNode) dataNode;
         final String localName = schemaNode.getQName().getLocalName();
@@ -379,9 +377,9 @@ public final class DefinitionGenerator {
             .type(OBJECT_TYPE)
             .title(nodeName)
             .description(schemaNode.getDescription().orElse(""));
-
+        final boolean isConfig = ((DataSchemaNode) dataNode).isConfiguration() && isParentConfig;
         childSchemaBuilder.properties(processChildren(childSchemaBuilder, containerChildren,
-            parentName + "_" + localName, definitions, definitionNames, stack, module));
+            parentName + "_" + localName, definitions, definitionNames, stack, module, isConfig));
 
         final String discriminator;
         if (!definitionNames.isListedNode(schemaNode)) {
@@ -405,20 +403,21 @@ public final class DefinitionGenerator {
     private static ObjectNode processChildren(final Schema.Builder parentNodeBuilder,
             final Collection<? extends DataSchemaNode> nodes, final String parentName,
             final Map<String, Schema> definitions, final DefinitionNames definitionNames,
-            final SchemaInferenceStack stack, final Module module) throws IOException {
+            final SchemaInferenceStack stack, final Module module, final boolean isParentConfig) throws IOException {
         final ObjectNode properties = JsonNodeFactory.instance.objectNode();
         final ArrayNode required = JsonNodeFactory.instance.arrayNode();
         for (final DataSchemaNode node : nodes) {
-            if (node.isConfiguration()) {
-                if (node instanceof ChoiceSchemaNode choice) {
-                    stack.enterSchemaTree(node.getQName());
-                    final Map<String, ObjectNode> choiceProperties = processChoiceNodeRecursively(parentName,
-                        definitions, definitionNames, stack, required, choice, module);
-                    choiceProperties.forEach(properties::set);
-                    stack.exit();
-                } else {
-                    final ObjectNode property = processChildNode(node, parentName, definitions, definitionNames,
-                        stack, required, module);
+            if (node instanceof ChoiceSchemaNode choice) {
+                stack.enterSchemaTree(node.getQName());
+                final boolean isConfig = isParentConfig && node.isConfiguration();
+                final Map<String, ObjectNode> choiceProperties = processChoiceNodeRecursively(parentName,
+                    definitions, definitionNames, isConfig, stack, required, choice, module);
+                choiceProperties.forEach(properties::set);
+                stack.exit();
+            } else {
+                final ObjectNode property = processChildNode(node, parentName, definitions, definitionNames,
+                    stack, required, module, isParentConfig);
+                if (property != null) {
                     properties.set(node.getQName().getLocalName(), property);
                 }
             }
@@ -428,7 +427,7 @@ public final class DefinitionGenerator {
     }
 
     private static Map<String, ObjectNode> processChoiceNodeRecursively(final String parentName,
-            final Map<String, Schema> definitions, final DefinitionNames definitionNames,
+            final Map<String, Schema> definitions, final DefinitionNames definitionNames, final boolean isConfig,
             final SchemaInferenceStack stack, final ArrayNode required, final ChoiceSchemaNode choice,
             final Module module) throws IOException {
         if (!choice.getCases().isEmpty()) {
@@ -438,15 +437,18 @@ public final class DefinitionGenerator {
             stack.enterSchemaTree(caseSchemaNode.getQName());
             for (final var childNode : caseSchemaNode.getChildNodes()) {
                 if (childNode instanceof ChoiceSchemaNode childChoice) {
+                    final var isChildConfig = isConfig && childNode.isConfiguration();
                     stack.enterSchemaTree(childNode.getQName());
                     final var childProperties = processChoiceNodeRecursively(parentName, definitions, definitionNames,
-                        stack, required, childChoice, module);
+                        isChildConfig, stack, required, childChoice, module);
                     properties.putAll(childProperties);
                     stack.exit();
                 } else {
                     final var property = processChildNode(childNode, parentName, definitions, definitionNames, stack,
-                        required, module);
-                    properties.put(childNode.getQName().getLocalName(), property);
+                        required, module, isConfig);
+                    if (property != null) {
+                        properties.put(childNode.getQName().getLocalName(), property);
+                    }
                 }
             }
             stack.exit();
@@ -457,8 +459,8 @@ public final class DefinitionGenerator {
 
     private static ObjectNode processChildNode(final DataSchemaNode node, final String parentName,
             final Map<String, Schema> definitions, final DefinitionNames definitionNames,
-            final SchemaInferenceStack stack, final ArrayNode required,
-            final Module module) throws IOException {
+            final SchemaInferenceStack stack, final ArrayNode required, final Module module,
+            final boolean isParentConfig) throws IOException {
         final XMLNamespace parentNamespace = stack.toSchemaNodeIdentifier().lastNodeIdentifier().getNamespace();
         stack.enterSchemaTree(node.getQName());
         /*
@@ -466,26 +468,38 @@ public final class DefinitionGenerator {
             use RestDocGenUtil#resolveNodesName for creating property name
          */
         final String name = node.getQName().getLocalName();
-        final ObjectNode property;
-        if (node instanceof LeafSchemaNode leaf) {
-            property = processLeafNode(leaf, name, required, stack, definitions, definitionNames, parentNamespace,
-                module);
-        } else if (node instanceof AnyxmlSchemaNode || node instanceof AnydataSchemaNode) {
-            property = processUnknownDataSchemaNode(node, name, required, parentNamespace);
-        } else if (node instanceof ListSchemaNode || node instanceof ContainerSchemaNode) {
-            if (isSchemaNodeMandatory(node)) {
-                required.add(name);
+        /*
+            If the parent is operational, then current node is also operational and should be added as a child
+            even if node.isConfiguration()==true.
+            If the parent is configuration, then current node should be added as a child only if
+            node.isConfiguration()==true.
+        */
+        final boolean shouldBeAddedAsChild = !isParentConfig || node.isConfiguration();
+        ObjectNode property = null;
+        if (node instanceof ListSchemaNode || node instanceof ContainerSchemaNode) {
+            final ObjectNode dataNodeContainer = processDataNodeContainer((DataNodeContainer) node, parentName,
+                definitions, definitionNames, stack, module, isParentConfig);
+            if (shouldBeAddedAsChild) {
+                if (isSchemaNodeMandatory(node)) {
+                    required.add(name);
+                }
+                property = dataNodeContainer;
             }
-            property = processDataNodeContainer((DataNodeContainer) node, parentName, definitions, definitionNames,
-                stack, module);
             processActionNodeContainer(node, parentName, definitions, definitionNames, stack, module);
-        } else if (node instanceof LeafListSchemaNode leafList) {
-            if (isSchemaNodeMandatory(node)) {
-                required.add(name);
+        } else if (shouldBeAddedAsChild) {
+            if (node instanceof LeafSchemaNode leaf) {
+                property = processLeafNode(leaf, name, required, stack, definitions, definitionNames, parentNamespace,
+                    module);
+            } else if (node instanceof AnyxmlSchemaNode || node instanceof AnydataSchemaNode) {
+                property = processUnknownDataSchemaNode(node, name, required, parentNamespace);
+            } else if (node instanceof LeafListSchemaNode leafList) {
+                if (isSchemaNodeMandatory(node)) {
+                    required.add(name);
+                }
+                property = processLeafListNode(leafList, stack, definitions, definitionNames, module);
+            } else {
+                throw new IllegalArgumentException("Unknown DataSchemaNode type: " + node.getClass());
             }
-            property = processLeafListNode(leafList, stack, definitions, definitionNames, module);
-        } else {
-            throw new IllegalArgumentException("Unknown DataSchemaNode type: " + node.getClass());
         }
         stack.exit();
         return property;
index 903ca6bd68efd3d19c554c779b1d578f1ee112cf..16b7554c0557158e1ae095ca11e58a5e004c96c9 100644 (file)
@@ -97,14 +97,8 @@ public final class OperationBuilder {
         final ObjectNode responses = JsonNodeFactory.instance.objectNode();
 
         final boolean isList = node instanceof ListSchemaNode;
-        final ObjectNode response;
-        if (isConfig) {
-            response = createRequestBodyParameter(defName, nodeName, isList, summary,
+        final ObjectNode response = createRequestBodyParameter(defName, nodeName, isList, summary,
                 String.valueOf(Response.Status.OK.getStatusCode()));
-        } else {
-            response = JsonNodeFactory.instance.objectNode();
-            response.put(DESCRIPTION_KEY, Response.Status.OK.getReasonPhrase());
-        }
         responses.set(String.valueOf(Response.Status.OK.getStatusCode()), response);
 
         return new Operation.Builder()
@@ -324,18 +318,16 @@ public final class OperationBuilder {
     public static ObjectNode buildResponse(final String description, final ObjectNode schema,
             final ObjectNode xmlSchema) {
         final ObjectNode response = JsonNodeFactory.instance.objectNode();
-        if (!schema.isEmpty()) {
-            final ObjectNode content = JsonNodeFactory.instance.objectNode();
-            final ObjectNode body = JsonNodeFactory.instance.objectNode();
-            final ObjectNode xmlBody = JsonNodeFactory.instance.objectNode();
+        final ObjectNode content = JsonNodeFactory.instance.objectNode();
+        final ObjectNode body = JsonNodeFactory.instance.objectNode();
+        final ObjectNode xmlBody = JsonNodeFactory.instance.objectNode();
 
-            body.set(SCHEMA_KEY, schema);
-            xmlBody.set(SCHEMA_KEY, xmlSchema);
-            content.set(MediaType.APPLICATION_JSON, body);
-            content.set(MediaType.APPLICATION_XML, xmlBody);
+        body.set(SCHEMA_KEY, schema);
+        xmlBody.set(SCHEMA_KEY, xmlSchema);
+        content.set(MediaType.APPLICATION_JSON, body);
+        content.set(MediaType.APPLICATION_XML, xmlBody);
 
-            response.set(CONTENT_KEY, content);
-        }
+        response.set(CONTENT_KEY, content);
 
         response.put(DESCRIPTION_KEY, description);
         return response;
index f1712d0d4cc4b3eee427363ca9c4eea13eadf045..55bf21034b78e649aeff71a058b2d6c291e5dd50 100644 (file)
@@ -46,7 +46,12 @@ public class OperationalDataTest {
         "action-types_list-action_input",
         "action-types_multi-container_inner-container",
         "operational_root",
-        "operational_root_config-container");
+        "operational_root_config-container",
+        "operational_root_config-container_config-container-oper-list",
+        "operational_root_oper-container",
+        "operational_root_oper-container_config-container",
+        "operational_root_oper-container_oper-container-list");
+
     private static final Set<String> EXPECTED_PATHS = Set.of(
         OPERATIONS_MP_URI + "/action-types:list={name}/list-action",
         OPERATIONS_MP_URI + "/action-types:container/container-action",
@@ -99,7 +104,7 @@ public class OperationalDataTest {
                 final var responses = path.get().responses();
                 final var response = responses.elements().next();
                 final var content = response.get("content");
-                // In case of 200 no content and Operational data
+                // In case of 200 no content
                 if (content != null) {
                     verifyOperationHaveCorrectXmlReference(content.get("application/xml").get("schema"));
                     verifyOperationHaveCorrectJsonReference(content.get("application/json").get("schema"));
@@ -136,7 +141,32 @@ public class OperationalDataTest {
         final var configRoot = schemas.get("operational_root");
         assertNotNull(configRoot);
         final var actualProperties = getSetOfProperties(configRoot);
-        assertEquals(Set.of("leaf-config", "leaf-first-case", "config-container"), actualProperties);
+        assertEquals(Set.of("leaf-config", "config-container"), actualProperties);
+    }
+
+    @Test
+    public void testOperationalConfigContOperListSchemaProperties() {
+        final var configContOperList = schemas.get(
+            "operational_root_config-container_config-container-oper-list");
+        assertNotNull(configContOperList);
+        final var actualProperties = getSetOfProperties(configContOperList);
+        assertEquals(Set.of("oper-container-list-leaf"), actualProperties);
+    }
+
+    @Test
+    public void testOperationalContListSchemaProperties() {
+        final var operContList = schemas.get("operational_root_oper-container_oper-container-list");
+        assertNotNull(operContList);
+        final var actualProperties = getSetOfProperties(operContList);
+        assertEquals(Set.of("oper-container-list-leaf"), actualProperties);
+    }
+
+    @Test
+    public void testOperationalConConfigContSchemaProperties() {
+        final var operConConfigCont = schemas.get("operational_root_oper-container_config-container");
+        assertNotNull(operConConfigCont);
+        final var actualProperties = getSetOfProperties(operConConfigCont);
+        assertEquals(Set.of("config-container-config-leaf", "opconfig-container-oper-leaf"), actualProperties);
     }
 
     @Test
@@ -144,8 +174,16 @@ public class OperationalDataTest {
         final var configCont = schemas.get("operational_root_config-container");
         assertNotNull(configCont);
         final var actualProperties = getSetOfProperties(configCont);
-        assertEquals(Set.of("config-container-config-leaf", "leaf-second-case", "oper-leaf-second-case"),
-            actualProperties);
+        assertEquals(Set.of("config-container-config-leaf", "leaf-second-case"), actualProperties);
+    }
+
+    @Test
+    public void testOperationalContSchemaProperties() {
+        final var operCont = schemas.get("operational_root_oper-container");
+        assertNotNull(operCont);
+        final var actualProperties = getSetOfProperties(operCont);
+        assertEquals(Set.of("config-container", "oper-container-list", "leaf-first-case", "oper-leaf-first-case",
+            "oper-container-config-leaf-list"), actualProperties);
     }
 
     @Test