e379b3a9d00cd807f7814118d31b29e92985531c
[netconf.git] / restconf / restconf-nb-bierman02 / src / test / java / org / opendaylight / controller / sal / restconf / impl / test / CutDataToCorrectDepthTest.java
1 /*
2  * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
3  *
4  * This program and the accompanying materials are made available under the
5  * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6  * and is available at http://www.eclipse.org/legal/epl-v10.html
7  */
8 package org.opendaylight.controller.sal.restconf.impl.test;
9
10 import static org.junit.Assert.assertEquals;
11 import static org.junit.Assert.assertNotNull;
12
13 import java.io.FileNotFoundException;
14 import java.io.IOException;
15 import java.util.HashMap;
16 import java.util.Map;
17 import javax.ws.rs.Consumes;
18 import javax.ws.rs.Encoded;
19 import javax.ws.rs.GET;
20 import javax.ws.rs.PUT;
21 import javax.ws.rs.Path;
22 import javax.ws.rs.PathParam;
23 import javax.ws.rs.Produces;
24 import javax.ws.rs.WebApplicationException;
25 import javax.ws.rs.client.Entity;
26 import javax.ws.rs.core.Application;
27 import javax.ws.rs.core.Context;
28 import javax.ws.rs.core.Response;
29 import javax.ws.rs.core.UriInfo;
30 import org.glassfish.jersey.server.ResourceConfig;
31 import org.glassfish.jersey.test.JerseyTest;
32 import org.junit.BeforeClass;
33 import org.opendaylight.controller.md.sal.rest.common.TestRestconfUtils;
34 import org.opendaylight.netconf.sal.rest.impl.JsonNormalizedNodeBodyReader;
35 import org.opendaylight.netconf.sal.rest.impl.NormalizedNodeContext;
36 import org.opendaylight.netconf.sal.rest.impl.NormalizedNodeJsonBodyWriter;
37 import org.opendaylight.netconf.sal.rest.impl.NormalizedNodeXmlBodyWriter;
38 import org.opendaylight.netconf.sal.rest.impl.RestconfDocumentedExceptionMapper;
39 import org.opendaylight.netconf.sal.rest.impl.WriterParameters;
40 import org.opendaylight.netconf.sal.rest.impl.XmlNormalizedNodeBodyReader;
41 import org.opendaylight.netconf.sal.restconf.impl.ControllerContext;
42 import org.opendaylight.netconf.sal.restconf.impl.QueryParametersParser;
43 import org.opendaylight.restconf.common.context.InstanceIdentifierContext;
44 import org.opendaylight.yangtools.yang.common.QName;
45 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
46 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
47 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeWithValue;
48 import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
49 import org.opendaylight.yangtools.yang.data.api.schema.DataContainerChild;
50 import org.opendaylight.yangtools.yang.data.api.schema.LeafNode;
51 import org.opendaylight.yangtools.yang.data.api.schema.LeafSetNode;
52 import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode;
53 import org.opendaylight.yangtools.yang.data.api.schema.MapNode;
54 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
55 import org.opendaylight.yangtools.yang.data.api.schema.SystemLeafSetNode;
56 import org.opendaylight.yangtools.yang.data.api.schema.SystemMapNode;
57 import org.opendaylight.yangtools.yang.data.api.schema.UnkeyedListEntryNode;
58 import org.opendaylight.yangtools.yang.data.api.schema.UnkeyedListNode;
59 import org.opendaylight.yangtools.yang.data.api.schema.builder.CollectionNodeBuilder;
60 import org.opendaylight.yangtools.yang.data.api.schema.builder.DataContainerNodeBuilder;
61 import org.opendaylight.yangtools.yang.data.api.schema.builder.ListNodeBuilder;
62 import org.opendaylight.yangtools.yang.data.impl.schema.Builders;
63 import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext;
64 import org.opendaylight.yangtools.yang.model.api.Module;
65 import org.opendaylight.yangtools.yang.parser.spi.meta.ReactorException;
66 import org.slf4j.Logger;
67 import org.slf4j.LoggerFactory;
68
69 public class CutDataToCorrectDepthTest extends JerseyTest {
70
71     private static final Logger LOG = LoggerFactory.getLogger(JerseyTest.class);
72
73     private static NormalizedNode depth1Cont;
74     private static NormalizedNode depth2Cont1;
75     private NormalizedNode globalPayload;
76     private static EffectiveModelContext schemaContextModules;
77
78     private final ControllerContext controllerContext =
79             TestRestconfUtils.newControllerContext(schemaContextModules, null);
80
81     @Path("/")
82     public class RestImpl {
83
84         @GET
85         @Path("/config/{identifier:.+}")
86         @Produces({ "application/json", "application/xml" })
87         public NormalizedNodeContext getData(@Encoded @PathParam("identifier") final String identifier,
88                                              @Context final UriInfo uriInfo) {
89
90             final InstanceIdentifierContext iiWithData = controllerContext.toInstanceIdentifier(identifier);
91
92             NormalizedNode data = null;
93             if (identifier.equals("nested-module:depth1-cont/depth2-cont1")) {
94                 data = depth2Cont1;
95             } else if (identifier.equals("nested-module:depth1-cont")) {
96                 data = depth1Cont;
97             }
98
99             final WriterParameters writerParameters = QueryParametersParser.parseWriterParameters(uriInfo);
100             return new NormalizedNodeContext(iiWithData, data, writerParameters);
101         }
102
103         @GET
104         @Path("/operational/{identifier:.+}")
105         @Produces({ "application/json", "application/xml" })
106         public NormalizedNodeContext getDataOperational(@Encoded @PathParam("identifier") final String identifier,
107                                                         @Context final UriInfo uriInfo) {
108             return getData(identifier, uriInfo);
109         }
110
111         @PUT
112         @Path("/config/{identifier:.+}")
113         @Consumes({ "application/json", "application/xml" })
114         public void normalizedData(@Encoded @PathParam("identifier") final String identifier,
115                                    final NormalizedNodeContext payload) throws InterruptedException {
116             LOG.info("Payload: {}.", payload);
117             LOG.info("Instance identifier of payload: {}.",
118                     payload.getInstanceIdentifierContext().getInstanceIdentifier());
119             LOG.info("Data of payload: {}.", payload.getData());
120             globalPayload = payload.getData();
121         }
122
123         @PUT
124         @Path("/operational/{identifier:.+}")
125         @Consumes({ "application/json", "application/xml" })
126         public void normalizedDataOperational(@Encoded @PathParam("identifier") final String identifier,
127                                               final NormalizedNodeContext payload) throws InterruptedException {
128             normalizedData(identifier, payload);
129         }
130     }
131
132     @BeforeClass
133     public static void initialize() throws FileNotFoundException, ReactorException {
134         schemaContextModules = TestUtils.loadSchemaContext("/modules");
135         final Module module = TestUtils.findModule(schemaContextModules.getModules(), "nested-module");
136         assertNotNull(module);
137
138         final UnkeyedListNode listAsUnkeyedList = unkeyedList(
139                 "depth2-cont1",
140                 unkeyedEntry("depth2-cont1",
141                         container("depth3-cont1",
142                             container("depth4-cont1", leaf("depth5-leaf1", "depth5-leaf1-value")),
143                             leaf("depth4-leaf1", "depth4-leaf1-value")), leaf("depth3-leaf1", "depth3-leaf1-value")));
144
145         final MapNode listAsMap = mapNode(
146                 "depth2-list2",
147                 mapEntryNode("depth2-list2", 2, leaf("depth3-lf1-key", "depth3-lf1-key-value"),
148                         leaf("depth3-lf2-key", "depth3-lf2-key-value"), leaf("depth3-lf3", "depth3-lf3-value")));
149
150         depth1Cont = container(
151                 "depth1-cont",
152                 listAsUnkeyedList,
153                 listAsMap,
154                 leafList("depth2-lfLst1", "depth2-lflst1-value1", "depth2-lflst1-value2", "depth2-lflst1-value3"),
155                 container(
156                         "depth2-cont2",
157                         container("depth3-cont2",
158                             container("depth4-cont2", leaf("depth5-leaf2", "depth5-leaf2-value")),
159                             leaf("depth4-leaf2", "depth4-leaf2-value")), leaf("depth3-leaf2", "depth3-leaf2-value")),
160                 leaf("depth2-leaf1", "depth2-leaf1-value"));
161
162         depth2Cont1 = listAsUnkeyedList;
163     }
164
165     // TODO: These tests should be fixed/rewriten because they fail randomly due to data not being de-serialized
166     // properly in readers
167     //@Test
168     public void getDataWithUriDepthParameterTest() throws WebApplicationException, IOException {
169         getDataWithUriDepthParameter("application/json");
170         getDataWithUriDepthParameter("application/xml");
171     }
172
173     public void getDataWithUriDepthParameter(final String mediaType) throws WebApplicationException, IOException {
174         Response response;
175
176         // Test config with depth 1
177         response = target("/config/nested-module:depth1-cont").queryParam("depth", "1").request(mediaType)
178                 .get();
179         txtDataToNormalizedNode(response, mediaType, "/config/nested-module:depth1-cont");
180         verifyResponse(nodeDataDepth1());
181
182         // Test config with depth 2
183         response = target("/config/nested-module:depth1-cont").queryParam("depth", "2").request(mediaType)
184                 .get();
185         txtDataToNormalizedNode(response, mediaType, "/config/nested-module:depth1-cont");
186         verifyResponse(nodeDataDepth2());
187
188         // Test config with depth 3
189         response = target("/config/nested-module:depth1-cont").queryParam("depth", "3").request(mediaType)
190                 .get();
191         txtDataToNormalizedNode(response, mediaType, "/config/nested-module:depth1-cont");
192         verifyResponse(nodeDataDepth3());
193
194         // Test config with depth 4
195         response = target("/config/nested-module:depth1-cont").queryParam("depth", "4").request(mediaType)
196                 .get();
197         txtDataToNormalizedNode(response, mediaType, "/config/nested-module:depth1-cont");
198         verifyResponse(nodeDataDepth4());
199
200         // Test config with depth 5
201         response = target("/config/nested-module:depth1-cont").queryParam("depth", "5").request(mediaType)
202                 .get();
203         txtDataToNormalizedNode(response, mediaType, "/config/nested-module:depth1-cont");
204         verifyResponse(nodeDataDepth5());
205
206         // Test config with depth unbounded
207
208         response = target("/config/nested-module:depth1-cont").queryParam("depth", "unbounded")
209                 .request(mediaType).get();
210         txtDataToNormalizedNode(response, mediaType, "/config/nested-module:depth1-cont");
211         verifyResponse(nodeDataDepth5());
212     }
213
214     private void txtDataToNormalizedNode(final Response response, final String mediaType, final String uri) {
215         final String responseStr = response.readEntity(String.class);
216         LOG.info("Response entity message: {}.", responseStr);
217         target(uri).request(mediaType).put(Entity.entity(responseStr, mediaType));
218     }
219
220     private void verifyResponse(final NormalizedNode nodeData) throws WebApplicationException, IOException {
221         assertNotNull(globalPayload);
222         assertEquals(globalPayload, nodeData);
223         globalPayload = null;
224     }
225
226     @Override
227     protected Application configure() {
228         ResourceConfig resourceConfig = new ResourceConfig();
229         resourceConfig = resourceConfig.registerInstances(new RestImpl());
230         resourceConfig.registerClasses(XmlNormalizedNodeBodyReader.class, NormalizedNodeXmlBodyWriter.class,
231                 JsonNormalizedNodeBodyReader.class, NormalizedNodeJsonBodyWriter.class,
232                 RestconfDocumentedExceptionMapper.class);
233         return resourceConfig;
234     }
235
236     private static LeafNode<?> leaf(final String localName, final Object value) {
237         return Builders.leafBuilder().withNodeIdentifier(toIdentifier(localName)).withValue(value).build();
238     }
239
240     private static ContainerNode container(final String localName, final DataContainerChild... children) {
241         final DataContainerNodeBuilder<NodeIdentifier, ContainerNode> containerBuilder =
242                 Builders.containerBuilder();
243         for (final DataContainerChild child : children) {
244             containerBuilder.withChild(child);
245         }
246         containerBuilder.withNodeIdentifier(toIdentifier(localName));
247         return containerBuilder.build();
248     }
249
250     private static UnkeyedListNode unkeyedList(
251             final String localName,
252             final UnkeyedListEntryNode... entryNodes) {
253         final CollectionNodeBuilder<UnkeyedListEntryNode, UnkeyedListNode> builder = Builders.unkeyedListBuilder();
254         final NodeIdentifier identifier = toIdentifier(localName);
255         builder.withNodeIdentifier(identifier);
256         for (final UnkeyedListEntryNode unkeyedListEntryNode : entryNodes) {
257             builder.withChild(unkeyedListEntryNode);
258         }
259         return builder.build();
260     }
261
262     private static UnkeyedListEntryNode unkeyedEntry(final String localName, final DataContainerChild... children) {
263         final DataContainerNodeBuilder<NodeIdentifier, UnkeyedListEntryNode> builder =
264                 Builders.unkeyedListEntryBuilder();
265         builder.withNodeIdentifier(toIdentifier(localName));
266         for (final DataContainerChild child : children) {
267             builder.withChild(child);
268         }
269         return builder.build();
270     }
271
272     private static MapNode mapNode(final String localName, final MapEntryNode... entryNodes) {
273         final CollectionNodeBuilder<MapEntryNode, SystemMapNode> builder = Builders.mapBuilder();
274         builder.withNodeIdentifier(toIdentifier(localName));
275         for (final MapEntryNode mapEntryNode : entryNodes) {
276             builder.withChild(mapEntryNode);
277         }
278         return builder.build();
279     }
280
281     private static MapEntryNode mapEntryNode(final String localName, final int keysNumber,
282                                              final DataContainerChild... children) {
283         final DataContainerNodeBuilder<NodeIdentifierWithPredicates, MapEntryNode> builder =
284                 Builders.mapEntryBuilder();
285         final Map<QName, Object> keys = new HashMap<>();
286         for (int i = 0; i < keysNumber; i++) {
287             keys.put(children[i].getIdentifier().getNodeType(), children[i].body());
288         }
289         builder.withNodeIdentifier(toIdentifier(localName, keys));
290
291         for (final DataContainerChild child : children) {
292             builder.withChild(child);
293         }
294         return builder.build();
295     }
296
297     private static LeafSetNode<?> leafList(final String localName, final String... children) {
298         final ListNodeBuilder<Object, SystemLeafSetNode<Object>> builder = Builders.leafSetBuilder();
299         builder.withNodeIdentifier(toIdentifier(localName));
300         for (final String child : children) {
301             builder.withChild(Builders.leafSetEntryBuilder().withNodeIdentifier(toIdentifier(localName, child))
302                     .withValue(child).build());
303         }
304         return builder.build();
305     }
306
307     private static NodeIdentifier toIdentifier(final String localName) {
308         return new NodeIdentifier(QName.create("urn:nested:module", "2014-06-03", localName));
309     }
310
311     private static NodeIdentifierWithPredicates toIdentifier(final String localName, final Map<QName, Object> keys) {
312         return NodeIdentifierWithPredicates.of(QName.create("urn:nested:module", "2014-06-03", localName), keys);
313     }
314
315     private static NodeWithValue<?> toIdentifier(final String localName, final Object value) {
316         return new NodeWithValue<>(QName.create("urn:nested:module", "2014-06-03", localName), value);
317     }
318
319     private static UnkeyedListEntryNode nodeDataDepth3Operational() {
320         return unkeyedEntry("depth2-cont1",
321                 container("depth3-cont1", container("depth4-cont1"), leaf("depth4-leaf1", "depth4-leaf1-value")),
322                 leaf("depth3-leaf1", "depth3-leaf1-value"));
323     }
324
325     private static ContainerNode nodeDataDepth5() {
326         return container(
327                 "depth1-cont",
328                 unkeyedList(
329                         "depth2-cont1",
330                         unkeyedEntry("depth2-cont1",
331                                 container("depth3-cont1",
332                                         container("depth4-cont1", leaf("depth5-leaf1", "depth5-leaf1-value")),
333                                         leaf("depth4-leaf1", "depth4-leaf1-value")),
334                                 leaf("depth3-leaf1", "depth3-leaf1-value"))),
335                 mapNode("depth2-list2",
336                         mapEntryNode("depth2-list2", 2, leaf("depth3-lf1-key", "depth3-lf1-key-value"),
337                             leaf("depth3-lf2-key", "depth3-lf2-key-value"), leaf("depth3-lf3", "depth3-lf3-value"))),
338                 leafList("depth2-lfLst1", "depth2-lflst1-value1", "depth2-lflst1-value2", "depth2-lflst1-value3"),
339                 container(
340                         "depth2-cont2",
341                         container("depth3-cont2",
342                             container("depth4-cont2", leaf("depth5-leaf2", "depth5-leaf2-value")),
343                             leaf("depth4-leaf2", "depth4-leaf2-value")), leaf("depth3-leaf2", "depth3-leaf2-value")),
344                 leaf("depth2-leaf1", "depth2-leaf1-value"));
345     }
346
347     private static ContainerNode nodeDataDepth4() {
348         return container(
349                 "depth1-cont",
350                 unkeyedList("depth2-cont1", nodeDataDepth3Operational()),
351                 mapNode("depth2-list2",
352                         mapEntryNode("depth2-list2", 2, leaf("depth3-lf1-key", "depth3-lf1-key-value"),
353                             leaf("depth3-lf2-key", "depth3-lf2-key-value"), leaf("depth3-lf3", "depth3-lf3-value"))),
354                 leafList("depth2-lfLst1", "depth2-lflst1-value1", "depth2-lflst1-value2", "depth2-lflst1-value3"),
355                 container(
356                     "depth2-cont2",
357                     container("depth3-cont2", container("depth4-cont2"), leaf("depth4-leaf2", "depth4-leaf2-value")),
358                     leaf("depth3-leaf2", "depth3-leaf2-value")), leaf("depth2-leaf1", "depth2-leaf1-value"));
359     }
360
361     private static ContainerNode nodeDataDepth3() {
362         return container(
363             "depth1-cont",
364             unkeyedList("depth2-cont1",
365                 unkeyedEntry("depth2-cont1", container("depth3-cont1"), leaf("depth3-leaf1", "depth3-leaf1-value"))),
366             mapNode("depth2-list2",
367                     mapEntryNode("depth2-list2", 2, leaf("depth3-lf1-key", "depth3-lf1-key-value"),
368                         leaf("depth3-lf2-key", "depth3-lf2-key-value"), leaf("depth3-lf3", "depth3-lf3-value"))),
369             leafList("depth2-lfLst1", "depth2-lflst1-value1", "depth2-lflst1-value2", "depth2-lflst1-value3"),
370             container("depth2-cont2", container("depth3-cont2"), leaf("depth3-leaf2", "depth3-leaf2-value")),
371             leaf("depth2-leaf1", "depth2-leaf1-value"));
372     }
373
374     private static ContainerNode nodeDataDepth2() {
375         return container(
376                 "depth1-cont",
377                 unkeyedList("depth2-cont1", unkeyedEntry("depth2-cont1")),
378                 mapNode("depth2-list2",
379                         mapEntryNode("depth2-list2", 2, leaf("depth3-lf1-key", "depth3-lf1-key-value"),
380                                 leaf("depth3-lf2-key", "depth3-lf2-key-value"))), container("depth2-cont2"),
381 //                leafList("depth2-lfLst1"),
382                 leaf("depth2-leaf1", "depth2-leaf1-value"));
383     }
384
385     private static ContainerNode nodeDataDepth1() {
386         return container("depth1-cont");
387     }
388 }