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