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