1224c3d389e298fe4f35edd833fa856030fef56c
[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.NormalizedNodeJsonBodyWriter;
36 import org.opendaylight.netconf.sal.rest.impl.NormalizedNodeXmlBodyWriter;
37 import org.opendaylight.netconf.sal.rest.impl.RestconfDocumentedExceptionMapper;
38 import org.opendaylight.netconf.sal.rest.impl.XmlNormalizedNodeBodyReader;
39 import org.opendaylight.netconf.sal.restconf.impl.ControllerContext;
40 import org.opendaylight.netconf.sal.restconf.impl.QueryParametersParser;
41 import org.opendaylight.restconf.common.context.InstanceIdentifierContext;
42 import org.opendaylight.restconf.common.context.NormalizedNodeContext;
43 import org.opendaylight.restconf.common.context.WriterParameters;
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.LeafSetEntryNode;
52 import org.opendaylight.yangtools.yang.data.api.schema.LeafSetNode;
53 import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode;
54 import org.opendaylight.yangtools.yang.data.api.schema.MapNode;
55 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
56 import org.opendaylight.yangtools.yang.data.api.schema.UnkeyedListEntryNode;
57 import org.opendaylight.yangtools.yang.data.api.schema.UnkeyedListNode;
58 import org.opendaylight.yangtools.yang.data.impl.schema.Builders;
59 import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.CollectionNodeBuilder;
60 import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.DataContainerNodeAttrBuilder;
61 import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.ListNodeBuilder;
62 import org.opendaylight.yangtools.yang.model.api.Module;
63 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
64 import org.opendaylight.yangtools.yang.parser.spi.meta.ReactorException;
65 import org.slf4j.Logger;
66 import org.slf4j.LoggerFactory;
67
68 public class CutDataToCorrectDepthTest extends JerseyTest {
69
70     private static final Logger LOG = LoggerFactory.getLogger(JerseyTest.class);
71
72     private static NormalizedNode<?, ?> depth1Cont;
73     private static NormalizedNode<?, ?> depth2Cont1;
74     private NormalizedNode<?, ?> globalPayload;
75     private static SchemaContext schemaContextModules;
76
77     private final ControllerContext controllerContext =
78             TestRestconfUtils.newControllerContext(schemaContextModules, null);
79
80     @Path("/")
81     public class RestImpl {
82
83         @GET
84         @Path("/config/{identifier:.+}")
85         @Produces({ "application/json", "application/xml" })
86         public NormalizedNodeContext getData(@Encoded @PathParam("identifier") final String identifier,
87                                              @Context final UriInfo uriInfo) {
88
89             final InstanceIdentifierContext<?> iiWithData = controllerContext.toInstanceIdentifier(identifier);
90
91             NormalizedNode<?, ?> data = null;
92             if (identifier.equals("nested-module:depth1-cont/depth2-cont1")) {
93                 data = depth2Cont1;
94             } else if (identifier.equals("nested-module:depth1-cont")) {
95                 data = depth1Cont;
96             }
97
98             final WriterParameters writerParameters = QueryParametersParser.parseWriterParameters(uriInfo);
99             return new NormalizedNodeContext(iiWithData, data, writerParameters);
100         }
101
102         @GET
103         @Path("/operational/{identifier:.+}")
104         @Produces({ "application/json", "application/xml" })
105         public NormalizedNodeContext getDataOperational(@Encoded @PathParam("identifier") final String identifier,
106                                                         @Context final UriInfo uriInfo) {
107             return getData(identifier, uriInfo);
108         }
109
110         @PUT
111         @Path("/config/{identifier:.+}")
112         @Consumes({ "application/json", "application/xml" })
113         public void normalizedData(@Encoded @PathParam("identifier") final String identifier,
114                                    final NormalizedNodeContext payload) throws InterruptedException {
115             LOG.info("Payload: {}.", payload);
116             LOG.info("Instance identifier of payload: {}.",
117                     payload.getInstanceIdentifierContext().getInstanceIdentifier());
118             LOG.info("Data of payload: {}.", payload.getData());
119             CutDataToCorrectDepthTest.this.globalPayload = payload.getData();
120         }
121
122         @PUT
123         @Path("/operational/{identifier:.+}")
124         @Consumes({ "application/json", "application/xml" })
125         public void normalizedDataOperational(@Encoded @PathParam("identifier") final String identifier,
126                                               final NormalizedNodeContext payload) throws InterruptedException {
127             normalizedData(identifier, payload);
128         }
129     }
130
131     @BeforeClass
132     public static void initialize() throws FileNotFoundException, ReactorException {
133         schemaContextModules = TestUtils.loadSchemaContext("/modules");
134         final Module module = TestUtils.findModule(schemaContextModules.getModules(), "nested-module");
135         assertNotNull(module);
136
137         final UnkeyedListNode listAsUnkeyedList = unkeyedList(
138                 "depth2-cont1",
139                 unkeyedEntry("depth2-cont1",
140                         container("depth3-cont1",
141                             container("depth4-cont1", leaf("depth5-leaf1", "depth5-leaf1-value")),
142                             leaf("depth4-leaf1", "depth4-leaf1-value")), leaf("depth3-leaf1", "depth3-leaf1-value")));
143
144         final MapNode listAsMap = mapNode(
145                 "depth2-list2",
146                 mapEntryNode("depth2-list2", 2, leaf("depth3-lf1-key", "depth3-lf1-key-value"),
147                         leaf("depth3-lf2-key", "depth3-lf2-key-value"), leaf("depth3-lf3", "depth3-lf3-value")));
148
149         depth1Cont = container(
150                 "depth1-cont",
151                 listAsUnkeyedList,
152                 listAsMap,
153                 leafList("depth2-lfLst1", "depth2-lflst1-value1", "depth2-lflst1-value2", "depth2-lflst1-value3"),
154                 container(
155                         "depth2-cont2",
156                         container("depth3-cont2",
157                             container("depth4-cont2", leaf("depth5-leaf2", "depth5-leaf2-value")),
158                             leaf("depth4-leaf2", "depth4-leaf2-value")), leaf("depth3-leaf2", "depth3-leaf2-value")),
159                 leaf("depth2-leaf1", "depth2-leaf1-value"));
160
161         depth2Cont1 = listAsUnkeyedList;
162     }
163
164     // TODO: These tests should be fixed/rewriten because they fail randomly due to data not being de-serialized
165     // properly in readers
166     //@Test
167     public void getDataWithUriDepthParameterTest() throws WebApplicationException, IOException {
168         getDataWithUriDepthParameter("application/json");
169         getDataWithUriDepthParameter("application/xml");
170     }
171
172     public void getDataWithUriDepthParameter(final String mediaType) throws WebApplicationException, IOException {
173         Response response;
174
175         // Test config with depth 1
176         response = target("/config/nested-module:depth1-cont").queryParam("depth", "1").request(mediaType)
177                 .get();
178         txtDataToNormalizedNode(response, mediaType, "/config/nested-module:depth1-cont");
179         verifyResponse(nodeDataDepth1());
180
181         // Test config with depth 2
182         response = target("/config/nested-module:depth1-cont").queryParam("depth", "2").request(mediaType)
183                 .get();
184         txtDataToNormalizedNode(response, mediaType, "/config/nested-module:depth1-cont");
185         verifyResponse(nodeDataDepth2());
186
187         // Test config with depth 3
188         response = target("/config/nested-module:depth1-cont").queryParam("depth", "3").request(mediaType)
189                 .get();
190         txtDataToNormalizedNode(response, mediaType, "/config/nested-module:depth1-cont");
191         verifyResponse(nodeDataDepth3());
192
193         // Test config with depth 4
194         response = target("/config/nested-module:depth1-cont").queryParam("depth", "4").request(mediaType)
195                 .get();
196         txtDataToNormalizedNode(response, mediaType, "/config/nested-module:depth1-cont");
197         verifyResponse(nodeDataDepth4());
198
199         // Test config with depth 5
200         response = target("/config/nested-module:depth1-cont").queryParam("depth", "5").request(mediaType)
201                 .get();
202         txtDataToNormalizedNode(response, mediaType, "/config/nested-module:depth1-cont");
203         verifyResponse(nodeDataDepth5());
204
205         // Test config with depth unbounded
206
207         response = target("/config/nested-module:depth1-cont").queryParam("depth", "unbounded")
208                 .request(mediaType).get();
209         txtDataToNormalizedNode(response, mediaType, "/config/nested-module:depth1-cont");
210         verifyResponse(nodeDataDepth5());
211     }
212
213     private void txtDataToNormalizedNode(final Response response, final String mediaType, final String uri) {
214         final String responseStr = response.readEntity(String.class);
215         LOG.info("Response entity message: {}.", responseStr);
216         target(uri).request(mediaType).put(Entity.entity(responseStr, mediaType));
217     }
218
219     private void verifyResponse(final NormalizedNode<?, ?> nodeData) throws WebApplicationException, IOException {
220         assertNotNull(this.globalPayload);
221         assertEquals(this.globalPayload, nodeData);
222         this.globalPayload = null;
223     }
224
225     @Override
226     protected Application configure() {
227         ResourceConfig resourceConfig = new ResourceConfig();
228         resourceConfig = resourceConfig.registerInstances(new RestImpl());
229         resourceConfig.registerClasses(XmlNormalizedNodeBodyReader.class, NormalizedNodeXmlBodyWriter.class,
230                 JsonNormalizedNodeBodyReader.class, NormalizedNodeJsonBodyWriter.class,
231                 RestconfDocumentedExceptionMapper.class);
232         return resourceConfig;
233     }
234
235     private static LeafNode<?> leaf(final String localName, final Object value) {
236         return Builders.leafBuilder().withNodeIdentifier(toIdentifier(localName)).withValue(value).build();
237     }
238
239     private static ContainerNode container(final String localName, final DataContainerChild<?, ?>... children) {
240         final DataContainerNodeAttrBuilder<NodeIdentifier, ContainerNode> containerBuilder =
241                 Builders.containerBuilder();
242         for (final DataContainerChild<?, ?> child : children) {
243             containerBuilder.withChild(child);
244         }
245         containerBuilder.withNodeIdentifier(toIdentifier(localName));
246         return containerBuilder.build();
247     }
248
249     private static UnkeyedListNode unkeyedList(
250             final String localName,
251             final UnkeyedListEntryNode... entryNodes) {
252         final CollectionNodeBuilder<UnkeyedListEntryNode, UnkeyedListNode> builder = Builders.unkeyedListBuilder();
253         final NodeIdentifier identifier = toIdentifier(localName);
254         builder.withNodeIdentifier(identifier);
255         for (final UnkeyedListEntryNode unkeyedListEntryNode : entryNodes) {
256             builder.withChild(unkeyedListEntryNode);
257         }
258         return builder.build();
259     }
260
261     private static UnkeyedListEntryNode unkeyedEntry(final String localName,
262                                                      final DataContainerChild<?, ?>... children) {
263         final DataContainerNodeAttrBuilder<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, MapNode> 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 DataContainerNodeAttrBuilder<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].getNodeType(), children[i].getValue());
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, LeafSetEntryNode<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 new NodeIdentifierWithPredicates(QName.create("urn:nested:module", "2014-06-03", localName),
313                 keys);
314     }
315
316     private static NodeWithValue<?> toIdentifier(final String localName, final Object value) {
317         return new NodeWithValue<>(QName.create("urn:nested:module", "2014-06-03", localName), value);
318     }
319
320     private static UnkeyedListEntryNode nodeDataDepth3Operational() {
321         return unkeyedEntry("depth2-cont1",
322                 container("depth3-cont1", container("depth4-cont1"), leaf("depth4-leaf1", "depth4-leaf1-value")),
323                 leaf("depth3-leaf1", "depth3-leaf1-value"));
324     }
325
326     private static ContainerNode nodeDataDepth5() {
327         return container(
328                 "depth1-cont",
329                 unkeyedList(
330                         "depth2-cont1",
331                         unkeyedEntry("depth2-cont1",
332                                 container("depth3-cont1",
333                                         container("depth4-cont1", leaf("depth5-leaf1", "depth5-leaf1-value")),
334                                         leaf("depth4-leaf1", "depth4-leaf1-value")),
335                                 leaf("depth3-leaf1", "depth3-leaf1-value"))),
336                 mapNode("depth2-list2",
337                         mapEntryNode("depth2-list2", 2, leaf("depth3-lf1-key", "depth3-lf1-key-value"),
338                             leaf("depth3-lf2-key", "depth3-lf2-key-value"), leaf("depth3-lf3", "depth3-lf3-value"))),
339                 leafList("depth2-lfLst1", "depth2-lflst1-value1", "depth2-lflst1-value2", "depth2-lflst1-value3"),
340                 container(
341                         "depth2-cont2",
342                         container("depth3-cont2",
343                             container("depth4-cont2", leaf("depth5-leaf2", "depth5-leaf2-value")),
344                             leaf("depth4-leaf2", "depth4-leaf2-value")), leaf("depth3-leaf2", "depth3-leaf2-value")),
345                 leaf("depth2-leaf1", "depth2-leaf1-value"));
346     }
347
348     private static ContainerNode nodeDataDepth4() {
349         return container(
350                 "depth1-cont",
351                 unkeyedList("depth2-cont1", nodeDataDepth3Operational()),
352                 mapNode("depth2-list2",
353                         mapEntryNode("depth2-list2", 2, leaf("depth3-lf1-key", "depth3-lf1-key-value"),
354                             leaf("depth3-lf2-key", "depth3-lf2-key-value"), leaf("depth3-lf3", "depth3-lf3-value"))),
355                 leafList("depth2-lfLst1", "depth2-lflst1-value1", "depth2-lflst1-value2", "depth2-lflst1-value3"),
356                 container(
357                     "depth2-cont2",
358                     container("depth3-cont2", container("depth4-cont2"), leaf("depth4-leaf2", "depth4-leaf2-value")),
359                     leaf("depth3-leaf2", "depth3-leaf2-value")), leaf("depth2-leaf1", "depth2-leaf1-value"));
360     }
361
362     private static ContainerNode nodeDataDepth3() {
363         return container(
364             "depth1-cont",
365             unkeyedList("depth2-cont1",
366                 unkeyedEntry("depth2-cont1", container("depth3-cont1"), leaf("depth3-leaf1", "depth3-leaf1-value"))),
367             mapNode("depth2-list2",
368                     mapEntryNode("depth2-list2", 2, leaf("depth3-lf1-key", "depth3-lf1-key-value"),
369                         leaf("depth3-lf2-key", "depth3-lf2-key-value"), leaf("depth3-lf3", "depth3-lf3-value"))),
370             leafList("depth2-lfLst1", "depth2-lflst1-value1", "depth2-lflst1-value2", "depth2-lflst1-value3"),
371             container("depth2-cont2", container("depth3-cont2"), leaf("depth3-leaf2", "depth3-leaf2-value")),
372             leaf("depth2-leaf1", "depth2-leaf1-value"));
373     }
374
375     private static ContainerNode nodeDataDepth2() {
376         return container(
377                 "depth1-cont",
378                 unkeyedList("depth2-cont1", unkeyedEntry("depth2-cont1")),
379                 mapNode("depth2-list2",
380                         mapEntryNode("depth2-list2", 2, leaf("depth3-lf1-key", "depth3-lf1-key-value"),
381                                 leaf("depth3-lf2-key", "depth3-lf2-key-value"))), container("depth2-cont2"),
382 //                leafList("depth2-lfLst1"),
383                 leaf("depth2-leaf1", "depth2-leaf1-value"));
384     }
385
386     private static ContainerNode nodeDataDepth1() {
387         return container("depth1-cont");
388     }
389 }