BUG 2155 - depth parameter in URI 29/12229/8
authorJozef Gloncak <jgloncak@cisco.com>
Fri, 24 Oct 2014 08:34:06 +0000 (10:34 +0200)
committerJozef Gloncak <jgloncak@cisco.com>
Mon, 10 Nov 2014 06:48:58 +0000 (06:48 +0000)
For stable/helium branch.

- CutDataToCorrectDepth - test of various depth levels for JSON and XML
- NormalizedNodeContext - added attribute with required depth.
- RestGetOperationTest - commented test was moved to CutDataToCorrectDepth
- JsonNormalizedNodeBodyReader - added code for derivation of parent schema
node for specified schemanode (to be used as input parameter of
JsonParserStream)
- NormalizedNodeJson(Xml)BodyWriter - added depth parameter among input
parameters of NormalizedNodeWriter.forStreamWriter() method.
- XmlNormalizedNodeBodyReader - added branch for parsing with using of
  UnkeyedListEntryNodeParser for case when top level node in xml input is
  unkeyed list entry.

WARNING!!!
Following patches should be merged first (else build will fail)
- BUG 1975 patch set should be merged because test CutDataToCorrectDepthTest will
  fail (fox xml are tested cases where UnkeyedListNode is incorrectly loaded
  as MapNode and test fails)
- BUG 2279 pat set should be merged because test CutDataToCorrectDepthTest
  will fail (currently input with top level element list entry data is
  processed:
  -- JSON - top level element is list node
  -- XML - top level element is list entry node)

Remark:
 - JSON part of CutDataToCorrectDepthTest is commented see remark in code

Change-Id: I4c3b97380293ab24155faadda7633c6e0a71fcec
Signed-off-by: Jozef Gloncak <jgloncak@cisco.com>
opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/rest/impl/JsonNormalizedNodeBodyReader.java
opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/rest/impl/NormalizedNodeJsonBodyWriter.java
opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/rest/impl/NormalizedNodeXmlBodyWriter.java
opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/rest/impl/XmlNormalizedNodeBodyReader.java
opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/restconf/impl/NormalizedNodeContext.java
opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/restconf/impl/RestconfImpl.java
opendaylight/md-sal/sal-rest-connector/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/CutDataToCorrectDepthTest.java [new file with mode: 0644]
opendaylight/md-sal/sal-rest-connector/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/RestGetOperationTest.java
opendaylight/md-sal/sal-rest-connector/src/test/resources/modules/nested-module.yang

index dc989d2786baf14d33d762e5b40a2ff6ed283713..85b1296fd6f85c49d62ca1cdb8378e60720cde69 100644 (file)
@@ -31,6 +31,9 @@ import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStre
 import org.opendaylight.yangtools.yang.data.codec.gson.JsonParserStream;
 import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNormalizedNodeStreamWriter;
 import org.opendaylight.yangtools.yang.data.impl.schema.NormalizedNodeResult;
+import org.opendaylight.yangtools.yang.model.api.SchemaNode;
+import org.opendaylight.yangtools.yang.model.api.SchemaPath;
+import org.opendaylight.yangtools.yang.model.util.SchemaContextUtil;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -56,7 +59,7 @@ public class JsonNormalizedNodeBodyReader extends AbstractIdentifierAwareJaxRsPr
             Optional<InstanceIdentifierContext> path = getIdentifierWithSchema();
             NormalizedNodeResult resultHolder = new NormalizedNodeResult();
             NormalizedNodeStreamWriter writer = ImmutableNormalizedNodeStreamWriter.from(resultHolder);
-            JsonParserStream jsonParser = JsonParserStream.create(writer, path.get().getSchemaContext());
+            JsonParserStream jsonParser = JsonParserStream.create(writer, path.get().getSchemaContext(), parentSchemaNode(path));
             JsonReader reader = new JsonReader(new InputStreamReader(entityStream));
             jsonParser.parse(reader);
             return new NormalizedNodeContext(path.get(),resultHolder.getResult());
@@ -67,5 +70,18 @@ public class JsonNormalizedNodeBodyReader extends AbstractIdentifierAwareJaxRsPr
                     ErrorTag.MALFORMED_MESSAGE);
         }
     }
+
+    /**
+     * This method resolve parent schema node or schema context if schema node from {@code path} is top level
+     * @param path
+     * @return
+     */
+    private SchemaNode parentSchemaNode(final Optional<InstanceIdentifierContext> path) {
+        final SchemaPath currentNodeParentPath = path.get().getSchemaNode().getPath().getParent();
+        if (currentNodeParentPath.getPathFromRoot().iterator().hasNext()) {
+            return SchemaContextUtil.findDataSchemaNode(path.get().getSchemaContext(), currentNodeParentPath);
+        }
+        return path.get().getSchemaContext();
+    }
 }
 
index 7a879f33779f1a41343d3cc48d39064c461f57b9..5a66932eda14a1ac454a7de36aa6c93f7e3ed281 100644 (file)
@@ -81,7 +81,7 @@ public class NormalizedNodeJsonBodyWriter implements MessageBodyWriter<Normalize
             initialNs = schema.getQName().getNamespace();
         }
         NormalizedNodeStreamWriter jsonWriter = JSONNormalizedNodeStreamWriter.create(context.getSchemaContext(),path,initialNs,outputWriter);
-        NormalizedNodeWriter nnWriter = NormalizedNodeWriter.forStreamWriter(jsonWriter);
+        NormalizedNodeWriter nnWriter = NormalizedNodeWriter.forStreamWriter(jsonWriter, t.getDepth());
         if(isDataRoot) {
             writeDataRoot(outputWriter,nnWriter,(ContainerNode) data);
         } else {
index bab26dfc012b1a620f2e0a619ef3d93d10b0bb2e..8a48f8a2190332fed49463be1bb7af99b706bbd3 100644 (file)
@@ -98,7 +98,7 @@ public class NormalizedNodeXmlBodyWriter implements MessageBodyWriter<Normalized
 
         NormalizedNodeStreamWriter jsonWriter = XMLStreamNormalizedNodeStreamWriter.create(xmlWriter,
                 pathContext.getSchemaContext(), schemaPath);
-        NormalizedNodeWriter nnWriter = NormalizedNodeWriter.forStreamWriter(jsonWriter);
+        NormalizedNodeWriter nnWriter = NormalizedNodeWriter.forStreamWriter(jsonWriter, t.getDepth());
         if (isDataRoot) {
             writeRootElement(xmlWriter, nnWriter, (ContainerNode) data);
         } else {
index 062a4488f369075a70c4322f4d75b5a1ce24841b..33f6547432d57c810e4e47ab85e5c7dddf9c7b1d 100644 (file)
@@ -97,8 +97,11 @@ public class XmlNormalizedNodeBodyReader extends AbstractIdentifierAwareJaxRsPro
         if(schemaNode instanceof ContainerSchemaNode) {
             return DOM_PARSER_FACTORY.getContainerNodeParser().parse(Collections.singletonList(doc.getDocumentElement()), (ContainerSchemaNode) schemaNode);
         } else if(schemaNode instanceof ListSchemaNode) {
-            ListSchemaNode casted = (ListSchemaNode) schemaNode;
-            return DOM_PARSER_FACTORY.getMapEntryNodeParser().parse(elements, casted);
+            ListSchemaNode listSchemaNode = (ListSchemaNode) schemaNode;
+            if (listSchemaNode.getKeyDefinition().isEmpty()) {
+                return DOM_PARSER_FACTORY.getUnkeyedListEntryNodeParser().parse(elements, listSchemaNode);
+            }
+            return DOM_PARSER_FACTORY.getMapEntryNodeParser().parse(elements, listSchemaNode);
         }
         return null;
     }
index e698693b95101e516584da7d4c33be0b889cd0ce..7310d6434374f3c40a09e5d2604bef391ace64c8 100644 (file)
@@ -6,6 +6,12 @@ public class NormalizedNodeContext {
 
     private final InstanceIdentifierContext context;
     private final NormalizedNode<?,?> data;
+    private int depth = Integer.MAX_VALUE;
+
+    public NormalizedNodeContext(InstanceIdentifierContext context, NormalizedNode<?, ?> data, final int depth) {
+        this(context,data);
+        this.depth = depth;
+    }
 
     public NormalizedNodeContext(InstanceIdentifierContext context, NormalizedNode<?, ?> data) {
         this.context = context;
@@ -19,4 +25,8 @@ public class NormalizedNodeContext {
     public NormalizedNode<?, ?> getData() {
         return data;
     }
+
+    public int getDepth() {
+        return depth;
+    }
 }
index cd860efab75073e13cf0cd2cfd93e12fd7200a5e..cd1187dbc990f14a7a88e384c8c94e1b0ce33fde 100644 (file)
@@ -646,7 +646,8 @@ public class RestconfImpl implements RestconfService {
             normalizedII = controllerContext.toNormalized(iiWithData.getInstanceIdentifier());
             data = broker.readConfigurationData(normalizedII);
         }
-        return new NormalizedNodeContext(iiWithData, data);
+        Integer depth = parseDepthParameter(uriInfo);
+        return new NormalizedNodeContext(iiWithData, data, depth);
     }
 
     @SuppressWarnings("unchecked")
@@ -669,10 +670,10 @@ public class RestconfImpl implements RestconfService {
         }
     }
 
-    private Integer parseDepthParameter(final UriInfo info) {
+    private int parseDepthParameter(final UriInfo info) {
         String param = info.getQueryParameters(false).getFirst(UriParameters.DEPTH.toString());
         if (Strings.isNullOrEmpty(param) || "unbounded".equals(param)) {
-            return null;
+            return Integer.MAX_VALUE;
         }
 
         try {
@@ -706,7 +707,8 @@ public class RestconfImpl implements RestconfService {
             data = broker.readOperationalData(normalizedII);
         }
 
-        return new NormalizedNodeContext(iiWithData, data);
+        Integer depth = parseDepthParameter(info);
+        return new NormalizedNodeContext(iiWithData, data, depth);
     }
 
     private boolean parsePrettyPrintParameter(final UriInfo info) {
diff --git a/opendaylight/md-sal/sal-rest-connector/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/CutDataToCorrectDepthTest.java b/opendaylight/md-sal/sal-rest-connector/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/CutDataToCorrectDepthTest.java
new file mode 100644 (file)
index 0000000..9efbde8
--- /dev/null
@@ -0,0 +1,392 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.sal.restconf.impl.test;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Map;
+import javax.ws.rs.Consumes;
+import javax.ws.rs.Encoded;
+import javax.ws.rs.GET;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.Produces;
+import javax.ws.rs.WebApplicationException;
+import javax.ws.rs.client.Entity;
+import javax.ws.rs.core.Application;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.UriInfo;
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.JerseyTest;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.opendaylight.controller.sal.rest.impl.JsonNormalizedNodeBodyReader;
+import org.opendaylight.controller.sal.rest.impl.NormalizedNodeJsonBodyWriter;
+import org.opendaylight.controller.sal.rest.impl.NormalizedNodeXmlBodyWriter;
+import org.opendaylight.controller.sal.rest.impl.RestconfDocumentedExceptionMapper;
+import org.opendaylight.controller.sal.rest.impl.XmlNormalizedNodeBodyReader;
+import org.opendaylight.controller.sal.restconf.impl.ControllerContext;
+import org.opendaylight.controller.sal.restconf.impl.InstanceIdentifierContext;
+import org.opendaylight.controller.sal.restconf.impl.NormalizedNodeContext;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeWithValue;
+import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
+import org.opendaylight.yangtools.yang.data.api.schema.DataContainerChild;
+import org.opendaylight.yangtools.yang.data.api.schema.LeafNode;
+import org.opendaylight.yangtools.yang.data.api.schema.LeafSetEntryNode;
+import org.opendaylight.yangtools.yang.data.api.schema.LeafSetNode;
+import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode;
+import org.opendaylight.yangtools.yang.data.api.schema.MapNode;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.data.api.schema.UnkeyedListEntryNode;
+import org.opendaylight.yangtools.yang.data.api.schema.UnkeyedListNode;
+import org.opendaylight.yangtools.yang.data.impl.schema.Builders;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.CollectionNodeBuilder;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.DataContainerNodeAttrBuilder;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.ListNodeBuilder;
+import org.opendaylight.yangtools.yang.model.api.Module;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+
+public class CutDataToCorrectDepthTest extends JerseyTest {
+
+    private static NormalizedNode<?, ?> depth1Cont;
+
+    private static NormalizedNode<?, ?> depth2Cont1;
+    private NormalizedNode<?, ?> globalPayload;
+
+    @Path("/")
+    public class RestImpl {
+
+        @GET
+        @Path("/config/{identifier:.+}")
+        @Produces({ "application/json", "application/xml" })
+        public NormalizedNodeContext getData(@Encoded @PathParam("identifier") String identifier,
+                @Context UriInfo uriInfo) {
+
+            final InstanceIdentifierContext iiWithData = ControllerContext.getInstance().toInstanceIdentifier(
+                    identifier);
+
+            NormalizedNode<?, ?> data = null;
+            if (identifier.equals("nested-module:depth1-cont/depth2-cont1")) {
+                data = depth2Cont1;
+            } else if (identifier.equals("nested-module:depth1-cont")) {
+                data = depth1Cont;
+            }
+
+            String depthStr = uriInfo.getQueryParameters().getFirst("depth");
+            int depth = Integer.MAX_VALUE;
+            try {
+                depth = Integer.valueOf(depthStr);
+            } catch (NumberFormatException e) {
+            }
+            return new NormalizedNodeContext(iiWithData, data, depth);
+        }
+
+        @GET
+        @Path("/operational/{identifier:.+}")
+        @Produces({ "application/json", "application/xml" })
+        public NormalizedNodeContext getDataOperational(@Encoded @PathParam("identifier") String identifier,
+                @Context UriInfo uriInfo) {
+            return getData(identifier, uriInfo);
+        }
+
+        @POST
+        @Path("/config/{identifier:.+}")
+        @Consumes({ "application/json", "application/xml" })
+        public void normalizedData(@Encoded @PathParam("identifier") String identifier, NormalizedNodeContext payload) {
+            globalPayload = payload.getData();
+        }
+
+        @POST
+        @Path("/operational/{identifier:.+}")
+        @Consumes({ "application/json", "application/xml" })
+        public void normalizedDataOperational(@Encoded @PathParam("identifier") String identifier,
+                NormalizedNodeContext payload) {
+            normalizedData(identifier, payload);
+        }
+    }
+
+    @Override
+    protected Application configure() {
+        ResourceConfig resourceConfig = new ResourceConfig();
+        resourceConfig = resourceConfig.registerInstances(new RestImpl());
+        resourceConfig.registerClasses(XmlNormalizedNodeBodyReader.class, NormalizedNodeXmlBodyWriter.class,
+                JsonNormalizedNodeBodyReader.class, NormalizedNodeJsonBodyWriter.class,
+                RestconfDocumentedExceptionMapper.class);
+        return resourceConfig;
+    }
+
+    private static SchemaContext schemaContextModules = null;
+
+    private static LeafNode<?> leaf(final String localName, final Object value) {
+        return Builders.leafBuilder().withNodeIdentifier(toIdentifier(localName)).withValue(value).build();
+    }
+
+    private static ContainerNode container(final String localName, final DataContainerChild<?, ?>... children) {
+        DataContainerNodeAttrBuilder<NodeIdentifier, ContainerNode> containerBuilder = Builders.containerBuilder();
+        for (DataContainerChild<?, ?> child : children) {
+            containerBuilder.withChild(child);
+        }
+        containerBuilder.withNodeIdentifier(toIdentifier(localName));
+        return containerBuilder.build();
+    }
+
+    private static UnkeyedListNode unkeyedList(
+            final String localName,
+            final UnkeyedListEntryNode... entryNodes) {
+        CollectionNodeBuilder<UnkeyedListEntryNode, UnkeyedListNode> builder = Builders.unkeyedListBuilder();
+        final NodeIdentifier identifier = toIdentifier(localName);
+        builder.withNodeIdentifier(identifier);
+        for (UnkeyedListEntryNode unkeyedListEntryNode : entryNodes) {
+            builder.withChild(unkeyedListEntryNode);
+        }
+        return builder.build();
+    }
+
+    private static UnkeyedListEntryNode unkeyedEntry(final String localName,
+            final DataContainerChild<?, ?>... children) {
+        DataContainerNodeAttrBuilder<NodeIdentifier, UnkeyedListEntryNode> builder = Builders.unkeyedListEntryBuilder();
+        builder.withNodeIdentifier(toIdentifier(localName));
+        for (DataContainerChild<?, ?> child : children) {
+            builder.withChild(child);
+        }
+        return builder.build();
+    }
+
+    private static MapNode mapNode(final String localName, final MapEntryNode... entryNodes) {
+        CollectionNodeBuilder<MapEntryNode, MapNode> builder = Builders.mapBuilder();
+        builder.withNodeIdentifier(toIdentifier(localName));
+        for (MapEntryNode mapEntryNode : entryNodes) {
+            builder.withChild(mapEntryNode);
+        }
+        return builder.build();
+    }
+
+    private static MapEntryNode mapEntryNode(final String localName, final int keysNumber,
+            final DataContainerChild<?, ?>... children) {
+        DataContainerNodeAttrBuilder<NodeIdentifierWithPredicates, MapEntryNode> builder = Builders.mapEntryBuilder();
+        Map<QName, Object> keys = new HashMap<>();
+        for (int i = 0; i < keysNumber; i++) {
+            keys.put(children[i].getNodeType(), children[i].getValue());
+        }
+        builder.withNodeIdentifier(toIdentifier(localName, keys));
+
+        for (DataContainerChild<?, ?> child : children) {
+            builder.withChild(child);
+        }
+        return builder.build();
+    }
+
+    private static LeafSetNode<?> leafList(final String localName, final String... children) {
+        ListNodeBuilder<Object, LeafSetEntryNode<Object>> builder = Builders.leafSetBuilder();
+        builder.withNodeIdentifier(toIdentifier(localName));
+        for (String child : children) {
+            builder.withChild(Builders.leafSetEntryBuilder().withNodeIdentifier(toIdentifier(localName, child))
+                    .withValue(child).build());
+        }
+        return builder.build();
+    }
+
+    private static NodeIdentifier toIdentifier(String localName) {
+        return new NodeIdentifier(QName.create("urn:nested:module", "2014-06-3", localName));
+    }
+
+    private static NodeIdentifierWithPredicates toIdentifier(String localName, Map<QName, Object> keys) {
+        return new NodeIdentifierWithPredicates(QName.create("urn:nested:module", "2014-06-3", localName),
+                keys);
+    }
+
+    private static NodeWithValue toIdentifier(final String localName, final Object value) {
+        return new NodeWithValue(QName.create("urn:nested:module", "2014-06-3", localName), value);
+    }
+
+    @BeforeClass
+    public static void initialize() throws FileNotFoundException {
+        schemaContextModules = TestUtils.loadSchemaContext("/modules");
+        Module module = TestUtils.findModule(schemaContextModules.getModules(), "nested-module");
+        assertNotNull(module);
+
+        UnkeyedListNode listAsUnkeyedList = unkeyedList(
+                "depth2-cont1",
+                unkeyedEntry("depth2-cont1",
+                        container("depth3-cont1",
+                                container("depth4-cont1", leaf("depth5-leaf1", "depth5-leaf1-value")),
+                                leaf("depth4-leaf1", "depth4-leaf1-value")), leaf("depth3-leaf1", "depth3-leaf1-value")));
+
+        MapNode listAsMap = mapNode(
+                "depth2-list2",
+                mapEntryNode("depth2-list2", 2, leaf("depth3-lf1-key", "depth3-lf1-key-value"),
+                        leaf("depth3-lf2-key", "depth3-lf2-key-value"), leaf("depth3-lf3", "depth3-lf3-value")));
+
+        depth1Cont = container(
+                "depth1-cont",
+                 listAsUnkeyedList,
+                listAsMap,
+                leafList("depth2-lfLst1", "depth2-lflst1-value1", "depth2-lflst1-value2", "depth2-lflst1-value3"),
+                container(
+                        "depth2-cont2",
+                        container("depth3-cont2",
+                                container("depth4-cont2", leaf("depth5-leaf2", "depth5-leaf2-value")),
+                                leaf("depth4-leaf2", "depth4-leaf2-value")), leaf("depth3-leaf2", "depth3-leaf2-value")),
+                leaf("depth2-leaf1", "depth2-leaf1-value"));
+
+        depth2Cont1 = listAsUnkeyedList;
+
+    }
+
+    @Test
+    public void getDataWithUriDepthParameterTest() throws WebApplicationException, IOException {
+//        FIXME: Disabled due to bug Bug 2282 - due to error in JsonParserStream. Module name is required for top level element.
+//        getDataWithUriDepthParameter("application/json");
+        getDataWithUriDepthParameter("application/xml");
+    }
+
+    public void getDataWithUriDepthParameter(final String mediaType) throws WebApplicationException, IOException {
+
+        ControllerContext.getInstance().setGlobalSchema(schemaContextModules);
+        Response response = null;
+
+        // Test config with depth 1
+        response = target("/config/nested-module:depth1-cont").queryParam("depth", "1").request(mediaType)
+                .get();
+        txtDataToNormalizedNode(response, mediaType, "/config/nested-module:depth1-cont");
+        verifyXMLResponse(nodeDataDepth1());
+
+        // Test config with depth 2
+        response = target("/config/nested-module:depth1-cont").queryParam("depth", "2").request(mediaType)
+                .get();
+        txtDataToNormalizedNode(response, mediaType, "/config/nested-module:depth1-cont");
+        verifyXMLResponse(nodeDataDepth2());
+
+        // Test config with depth 3
+        response = target("/config/nested-module:depth1-cont").queryParam("depth", "3").request(mediaType)
+                .get();
+        txtDataToNormalizedNode(response, mediaType, "/config/nested-module:depth1-cont");
+        verifyXMLResponse(nodeDataDepth3());
+
+        // Test config with depth 4
+        response = target("/config/nested-module:depth1-cont").queryParam("depth", "4").request(mediaType)
+                .get();
+        txtDataToNormalizedNode(response, mediaType, "/config/nested-module:depth1-cont");
+        verifyXMLResponse(nodeDataDepth4());
+
+        // Test config with depth 5
+        response = target("/config/nested-module:depth1-cont").queryParam("depth", "5").request(mediaType)
+                .get();
+        txtDataToNormalizedNode(response, mediaType, "/config/nested-module:depth1-cont");
+        verifyXMLResponse(nodeDataDepth5());
+
+        // Test config with depth unbounded
+
+        response = target("/config/nested-module:depth1-cont").queryParam("depth", "unbounded")
+                .request(mediaType).get();
+        txtDataToNormalizedNode(response, mediaType, "/config/nested-module:depth1-cont");
+        verifyXMLResponse(nodeDataDepth5());
+
+        // Test operational
+        response = target("/operational/nested-module:depth1-cont/depth2-cont1").queryParam("depth", "3")
+                .request(mediaType).get();
+        txtDataToNormalizedNode(response, mediaType, "/operational/nested-module:depth1-cont/depth2-cont1");
+        verifyXMLResponse(nodeDataDepth3Operational());
+    }
+
+    private UnkeyedListEntryNode nodeDataDepth3Operational() {
+        return unkeyedEntry("depth2-cont1",
+                        container("depth3-cont1", container("depth4-cont1"), leaf("depth4-leaf1", "depth4-leaf1-value")),
+                        leaf("depth3-leaf1", "depth3-leaf1-value"));
+    }
+
+    private ContainerNode nodeDataDepth5() {
+        return container(
+                "depth1-cont",
+                unkeyedList(
+                        "depth2-cont1",
+                        unkeyedEntry("depth2-cont1",
+                                container("depth3-cont1",
+                                        container("depth4-cont1", leaf("depth5-leaf1", "depth5-leaf1-value")),
+                                        leaf("depth4-leaf1", "depth4-leaf1-value")),
+                                leaf("depth3-leaf1", "depth3-leaf1-value"))),
+                mapNode("depth2-list2",
+                        mapEntryNode("depth2-list2", 2, leaf("depth3-lf1-key", "depth3-lf1-key-value"),
+                                leaf("depth3-lf2-key", "depth3-lf2-key-value"), leaf("depth3-lf3", "depth3-lf3-value"))),
+                leafList("depth2-lfLst1", "depth2-lflst1-value1", "depth2-lflst1-value2", "depth2-lflst1-value3"),
+                container(
+                        "depth2-cont2",
+                        container("depth3-cont2",
+                                container("depth4-cont2", leaf("depth5-leaf2", "depth5-leaf2-value")),
+                                leaf("depth4-leaf2", "depth4-leaf2-value")), leaf("depth3-leaf2", "depth3-leaf2-value")),
+                leaf("depth2-leaf1", "depth2-leaf1-value"));
+    }
+
+    private ContainerNode nodeDataDepth4() {
+        return container(
+                "depth1-cont",
+                unkeyedList("depth2-cont1",  nodeDataDepth3Operational()),
+                mapNode("depth2-list2",
+                        mapEntryNode("depth2-list2", 2, leaf("depth3-lf1-key", "depth3-lf1-key-value"),
+                                leaf("depth3-lf2-key", "depth3-lf2-key-value"), leaf("depth3-lf3", "depth3-lf3-value"))),
+                leafList("depth2-lfLst1", "depth2-lflst1-value1", "depth2-lflst1-value2", "depth2-lflst1-value3"),
+                container(
+                        "depth2-cont2",
+                        container("depth3-cont2", container("depth4-cont2"), leaf("depth4-leaf2", "depth4-leaf2-value")),
+                        leaf("depth3-leaf2", "depth3-leaf2-value")), leaf("depth2-leaf1", "depth2-leaf1-value"));
+    }
+
+    private ContainerNode nodeDataDepth3() {
+        return container(
+                "depth1-cont",
+                unkeyedList("depth2-cont1",
+                        unkeyedEntry("depth2-cont1", container("depth3-cont1"), leaf("depth3-leaf1", "depth3-leaf1-value"))),
+                mapNode("depth2-list2",
+                        mapEntryNode("depth2-list2", 2, leaf("depth3-lf1-key", "depth3-lf1-key-value"),
+                                leaf("depth3-lf2-key", "depth3-lf2-key-value"), leaf("depth3-lf3", "depth3-lf3-value"))),
+                leafList("depth2-lfLst1", "depth2-lflst1-value1", "depth2-lflst1-value2", "depth2-lflst1-value3"),
+                container("depth2-cont2", container("depth3-cont2"), leaf("depth3-leaf2", "depth3-leaf2-value")),
+                leaf("depth2-leaf1", "depth2-leaf1-value"));
+    }
+
+    private ContainerNode nodeDataDepth2() {
+        return container(
+                "depth1-cont",
+                unkeyedList("depth2-cont1", unkeyedEntry("depth2-cont1")),
+                mapNode("depth2-list2",
+                        mapEntryNode("depth2-list2", 2, leaf("depth3-lf1-key", "depth3-lf1-key-value"),
+                                leaf("depth3-lf2-key", "depth3-lf2-key-value"))), container("depth2-cont2"),
+//                leafList("depth2-lfLst1"),
+                leaf("depth2-leaf1", "depth2-leaf1-value"));
+    }
+
+    private ContainerNode nodeDataDepth1() {
+        return container("depth1-cont");
+    }
+
+    private void txtDataToNormalizedNode(final Response response, final String mediaType, final String uri) {
+        String responseStr = response.readEntity(String.class);
+        target(uri).request(mediaType).post(Entity.entity(responseStr, mediaType));
+    }
+
+    private void verifyXMLResponse(final NormalizedNode<?, ?> nodeData) throws WebApplicationException, IOException {
+        assertNotNull(globalPayload);
+        verifyContainerElement(globalPayload, nodeData);
+        globalPayload = null;
+    }
+
+    @SuppressWarnings("unchecked")
+    private void verifyContainerElement(final NormalizedNode<?, ?> element, final NormalizedNode<?, ?> nodeData) {
+        assertEquals(nodeData, element);
+    }
+
+}
index 06cfd84b05048513249462de9245871da3905c5f..028723a79b83f1f3d67266b986a8c704f6892962 100644 (file)
@@ -19,8 +19,6 @@ import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.when;
 
 import com.google.common.base.Optional;
-import com.google.common.collect.Lists;
-import com.google.common.collect.Maps;
 import java.io.FileNotFoundException;
 import java.io.UnsupportedEncodingException;
 import java.net.URI;
@@ -31,7 +29,6 @@ import java.util.ArrayList;
 import java.util.Date;
 import java.util.HashSet;
 import java.util.List;
-import java.util.Map;
 import java.util.Set;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
@@ -61,19 +58,14 @@ import org.opendaylight.controller.sal.restconf.impl.ControllerContext;
 import org.opendaylight.controller.sal.restconf.impl.RestconfDocumentedException;
 import org.opendaylight.controller.sal.restconf.impl.RestconfImpl;
 import org.opendaylight.yangtools.yang.common.QName;
-import org.opendaylight.yangtools.yang.data.api.CompositeNode;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
 import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
-import org.opendaylight.yangtools.yang.data.impl.ImmutableCompositeNode;
 import org.opendaylight.yangtools.yang.data.impl.schema.Builders;
 import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableLeafNodeBuilder;
 import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableMapEntryNodeBuilder;
-import org.opendaylight.yangtools.yang.data.impl.util.CompositeNodeBuilder;
-import org.opendaylight.yangtools.yang.model.api.DataNodeContainer;
-import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
 import org.opendaylight.yangtools.yang.model.api.Module;
 import org.opendaylight.yangtools.yang.model.api.RpcDefinition;
 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
@@ -719,189 +711,6 @@ public class RestGetOperationTest extends JerseyTest {
         assertFalse(matcher.matches());
     }
 
-    @Test
-    @Ignore
-    public void getDataWithUriDepthParameterTest() throws UnsupportedEncodingException {
-
-        ControllerContext.getInstance().setGlobalSchema(schemaContextModules);
-
-        CompositeNode depth1Cont = toCompositeNode(toCompositeNodeData(
-                toNestedQName("depth1-cont"),
-                toCompositeNodeData(
-                        toNestedQName("depth2-cont1"),
-                        toCompositeNodeData(
-                                toNestedQName("depth3-cont1"),
-                                toCompositeNodeData(toNestedQName("depth4-cont1"),
-                                        toSimpleNodeData(toNestedQName("depth5-leaf1"), "depth5-leaf1-value")),
-                                toSimpleNodeData(toNestedQName("depth4-leaf1"), "depth4-leaf1-value")),
-                        toSimpleNodeData(toNestedQName("depth3-leaf1"), "depth3-leaf1-value")),
-                toCompositeNodeData(
-                        toNestedQName("depth2-cont2"),
-                        toCompositeNodeData(
-                                toNestedQName("depth3-cont2"),
-                                toCompositeNodeData(toNestedQName("depth4-cont2"),
-                                        toSimpleNodeData(toNestedQName("depth5-leaf2"), "depth5-leaf2-value")),
-                                toSimpleNodeData(toNestedQName("depth4-leaf2"), "depth4-leaf2-value")),
-                        toSimpleNodeData(toNestedQName("depth3-leaf2"), "depth3-leaf2-value")),
-                toSimpleNodeData(toNestedQName("depth2-leaf1"), "depth2-leaf1-value")));
-
-        Module module = TestUtils.findModule(schemaContextModules.getModules(), "nested-module");
-        assertNotNull(module);
-
-        DataSchemaNode dataSchemaNode = TestUtils.resolveDataSchemaNode("depth1-cont", module);
-        assertNotNull(dataSchemaNode);
-
-        when(brokerFacade.readConfigurationData(any(YangInstanceIdentifier.class))).thenReturn(
-                TestUtils.compositeNodeToDatastoreNormalizedNode(depth1Cont, dataSchemaNode));
-
-        // Test config with depth 1
-
-        Response response = target("/config/nested-module:depth1-cont").queryParam("depth", "1")
-                .request("application/xml").get();
-
-        verifyXMLResponse(response, expectEmptyContainer("depth1-cont"));
-
-        // Test config with depth 2
-
-        response = target("/config/nested-module:depth1-cont").queryParam("depth", "2").request("application/xml")
-                .get();
-
-        // String
-        // xml="<depth1-cont><depth2-cont1/><depth2-cont2/><depth2-leaf1>depth2-leaf1-value</depth2-leaf1></depth1-cont>";
-        // Response mr=mock(Response.class);
-        // when(mr.getEntity()).thenReturn( new
-        // java.io.StringBufferInputStream(xml) );
-
-        verifyXMLResponse(
-                response,
-                expectContainer("depth1-cont", expectEmptyContainer("depth2-cont1"),
-                        expectEmptyContainer("depth2-cont2"), expectLeaf("depth2-leaf1", "depth2-leaf1-value")));
-
-        // Test config with depth 3
-
-        response = target("/config/nested-module:depth1-cont").queryParam("depth", "3").request("application/xml")
-                .get();
-
-        verifyXMLResponse(
-                response,
-                expectContainer(
-                        "depth1-cont",
-                        expectContainer("depth2-cont1", expectEmptyContainer("depth3-cont1"),
-                                expectLeaf("depth3-leaf1", "depth3-leaf1-value")),
-                        expectContainer("depth2-cont2", expectEmptyContainer("depth3-cont2"),
-                                expectLeaf("depth3-leaf2", "depth3-leaf2-value")),
-                        expectLeaf("depth2-leaf1", "depth2-leaf1-value")));
-
-        // Test config with depth 4
-
-        response = target("/config/nested-module:depth1-cont").queryParam("depth", "4").request("application/xml")
-                .get();
-
-        verifyXMLResponse(
-                response,
-                expectContainer(
-                        "depth1-cont",
-                        expectContainer(
-                                "depth2-cont1",
-                                expectContainer("depth3-cont1", expectEmptyContainer("depth4-cont1"),
-                                        expectLeaf("depth4-leaf1", "depth4-leaf1-value")),
-                                expectLeaf("depth3-leaf1", "depth3-leaf1-value")),
-                        expectContainer(
-                                "depth2-cont2",
-                                expectContainer("depth3-cont2", expectEmptyContainer("depth4-cont2"),
-                                        expectLeaf("depth4-leaf2", "depth4-leaf2-value")),
-                                expectLeaf("depth3-leaf2", "depth3-leaf2-value")),
-                        expectLeaf("depth2-leaf1", "depth2-leaf1-value")));
-
-        // Test config with depth 5
-
-        response = target("/config/nested-module:depth1-cont").queryParam("depth", "5").request("application/xml")
-                .get();
-
-        verifyXMLResponse(
-                response,
-                expectContainer(
-                        "depth1-cont",
-                        expectContainer(
-                                "depth2-cont1",
-                                expectContainer(
-                                        "depth3-cont1",
-                                        expectContainer("depth4-cont1",
-                                                expectLeaf("depth5-leaf1", "depth5-leaf1-value")),
-                                        expectLeaf("depth4-leaf1", "depth4-leaf1-value")),
-                                expectLeaf("depth3-leaf1", "depth3-leaf1-value")),
-                        expectContainer(
-                                "depth2-cont2",
-                                expectContainer(
-                                        "depth3-cont2",
-                                        expectContainer("depth4-cont2",
-                                                expectLeaf("depth5-leaf2", "depth5-leaf2-value")),
-                                        expectLeaf("depth4-leaf2", "depth4-leaf2-value")),
-                                expectLeaf("depth3-leaf2", "depth3-leaf2-value")),
-                        expectLeaf("depth2-leaf1", "depth2-leaf1-value")));
-
-        // Test config with depth unbounded
-
-        response = target("/config/nested-module:depth1-cont").queryParam("depth", "unbounded")
-                .request("application/xml").get();
-
-        verifyXMLResponse(
-                response,
-                expectContainer(
-                        "depth1-cont",
-                        expectContainer(
-                                "depth2-cont1",
-                                expectContainer(
-                                        "depth3-cont1",
-                                        expectContainer("depth4-cont1",
-                                                expectLeaf("depth5-leaf1", "depth5-leaf1-value")),
-                                        expectLeaf("depth4-leaf1", "depth4-leaf1-value")),
-                                expectLeaf("depth3-leaf1", "depth3-leaf1-value")),
-                        expectContainer(
-                                "depth2-cont2",
-                                expectContainer(
-                                        "depth3-cont2",
-                                        expectContainer("depth4-cont2",
-                                                expectLeaf("depth5-leaf2", "depth5-leaf2-value")),
-                                        expectLeaf("depth4-leaf2", "depth4-leaf2-value")),
-                                expectLeaf("depth3-leaf2", "depth3-leaf2-value")),
-                        expectLeaf("depth2-leaf1", "depth2-leaf1-value")));
-
-        // Test operational
-
-        CompositeNode depth2Cont1 = toCompositeNode(toCompositeNodeData(
-                toNestedQName("depth2-cont1"),
-                toCompositeNodeData(
-                        toNestedQName("depth3-cont1"),
-                        toCompositeNodeData(toNestedQName("depth4-cont1"),
-                                toSimpleNodeData(toNestedQName("depth5-leaf1"), "depth5-leaf1-value")),
-                        toSimpleNodeData(toNestedQName("depth4-leaf1"), "depth4-leaf1-value")),
-                toSimpleNodeData(toNestedQName("depth3-leaf1"), "depth3-leaf1-value")));
-
-        assertTrue(dataSchemaNode instanceof DataNodeContainer);
-        DataSchemaNode depth2cont1Schema = null;
-        for (DataSchemaNode childNode : ((DataNodeContainer) dataSchemaNode).getChildNodes()) {
-            if (childNode.getQName().getLocalName().equals("depth2-cont1")) {
-                depth2cont1Schema = childNode;
-                break;
-            }
-        }
-        assertNotNull(depth2Cont1);
-
-        when(brokerFacade.readOperationalData(any(YangInstanceIdentifier.class))).thenReturn(
-                TestUtils.compositeNodeToDatastoreNormalizedNode(depth2Cont1, depth2cont1Schema));
-
-        response = target("/operational/nested-module:depth1-cont/depth2-cont1").queryParam("depth", "3")
-                .request("application/xml").get();
-
-        verifyXMLResponse(
-                response,
-                expectContainer(
-                        "depth2-cont1",
-                        expectContainer("depth3-cont1", expectEmptyContainer("depth4-cont1"),
-                                expectLeaf("depth4-leaf1", "depth4-leaf1-value")),
-                        expectLeaf("depth3-leaf1", "depth3-leaf1-value")));
-    }
 
     /**
      * Tests behavior when invalid value of depth URI parameter
@@ -945,96 +754,8 @@ public class RestGetOperationTest extends JerseyTest {
         }
     }
 
-    private void verifyXMLResponse(final Response response, final NodeData nodeData) {
-        Document doc = response.readEntity(Document.class);
-//        Document doc = TestUtils.loadDocumentFrom((InputStream) response.getEntity());
-//        System.out.println();
-        assertNotNull("Could not parse XML document", doc);
 
-        // System.out.println(TestUtils.getDocumentInPrintableForm( doc ));
 
-        verifyContainerElement(doc.getDocumentElement(), nodeData);
-    }
-
-    @SuppressWarnings("unchecked")
-    private void verifyContainerElement(final Element element, final NodeData nodeData) {
-
-        assertEquals("Element local name", nodeData.key, element.getLocalName());
 
-        NodeList childNodes = element.getChildNodes();
-        if (nodeData.data == null) { // empty container
-            assertTrue("Expected no child elements for \"" + element.getLocalName() + "\"", childNodes.getLength() == 0);
-            return;
-        }
-
-        Map<String, NodeData> expChildMap = Maps.newHashMap();
-        for (NodeData expChild : (List<NodeData>) nodeData.data) {
-            expChildMap.put(expChild.key.toString(), expChild);
-        }
-
-        for (int i = 0; i < childNodes.getLength(); i++) {
-            org.w3c.dom.Node actualChild = childNodes.item(i);
-            if (!(actualChild instanceof Element)) {
-                continue;
-            }
-
-            Element actualElement = (Element) actualChild;
-            NodeData expChild = expChildMap.remove(actualElement.getLocalName());
-            assertNotNull(
-                    "Unexpected child element for parent \"" + element.getLocalName() + "\": "
-                            + actualElement.getLocalName(), expChild);
-
-            if (expChild.data == null || expChild.data instanceof List) {
-                verifyContainerElement(actualElement, expChild);
-            } else {
-                assertEquals("Text content for element: " + actualElement.getLocalName(), expChild.data,
-                        actualElement.getTextContent());
-            }
-        }
-
-        if (!expChildMap.isEmpty()) {
-            fail("Missing elements for parent \"" + element.getLocalName() + "\": " + expChildMap.keySet());
-        }
-    }
-
-    private NodeData expectContainer(final String name, final NodeData... childData) {
-        return new NodeData(name, Lists.newArrayList(childData));
-    }
-
-    private NodeData expectEmptyContainer(final String name) {
-        return new NodeData(name, null);
-    }
-
-    private NodeData expectLeaf(final String name, final Object value) {
-        return new NodeData(name, value);
-    }
-
-    private QName toNestedQName(final String localName) {
-        return QName.create("urn:nested:module", "2014-06-3", localName);
-    }
-
-    @SuppressWarnings("unchecked")
-    private CompositeNode toCompositeNode(final NodeData nodeData) {
-        CompositeNodeBuilder<ImmutableCompositeNode> builder = ImmutableCompositeNode.builder();
-        builder.setQName((QName) nodeData.key);
-
-        for (NodeData child : (List<NodeData>) nodeData.data) {
-            if (child.data instanceof List) {
-                builder.add(toCompositeNode(child));
-            } else {
-                builder.addLeaf((QName) child.key, child.data);
-            }
-        }
-
-        return builder.toInstance();
-    }
-
-    private NodeData toCompositeNodeData(final QName key, final NodeData... childData) {
-        return new NodeData(key, Lists.newArrayList(childData));
-    }
-
-    private NodeData toSimpleNodeData(final QName key, final Object value) {
-        return new NodeData(key, value);
-    }
 
 }
index 590743c9ca71f5547a5a1e6dd03bf158afbd4f58..0052bc31df436ea824cd99f992be7543ee1cd9ce 100644 (file)
@@ -4,7 +4,7 @@ module nested-module {
     revision "2014-06-3";
 
     container depth1-cont {
-        container depth2-cont1 {
+        list depth2-cont1 {
             container depth3-cont1 {
                 container depth4-cont1 {
                     leaf depth5-leaf1 {
@@ -22,6 +22,24 @@ module nested-module {
             }
         }
         
+/* list depth2-list2 was added to test keyed list */        
+        list depth2-list2 {
+            key "depth3-lf1-key depth3-lf2-key";
+            leaf depth3-lf1-key {
+                type string;
+            }
+            leaf depth3-lf2-key {
+                type string;
+            }
+            leaf depth3-lf3 {
+                type string;
+            }
+        }
+        
+        leaf-list depth2-lfLst1 {
+            type string; 
+        }
+        
         container depth2-cont2 {
             container depth3-cont2 {
                 container depth4-cont2 {