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