2 * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
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
8 package org.opendaylight.controller.sal.restconf.impl.test;
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;
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;
64 public class CutDataToCorrectDepthTest extends JerseyTest {
66 private static NormalizedNode<?, ?> depth1Cont;
67 private static NormalizedNode<?, ?> depth2Cont1;
68 private NormalizedNode<?, ?> globalPayload;
69 private static SchemaContext schemaContextModules;
72 public class RestImpl {
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) {
80 final InstanceIdentifierContext iiWithData = ControllerContext.getInstance().toInstanceIdentifier(
83 NormalizedNode<?, ?> data = null;
84 if (identifier.equals("nested-module:depth1-cont/depth2-cont1")) {
86 } else if (identifier.equals("nested-module:depth1-cont")) {
90 final WriterParameters writerParameters = QueryParametersParser.parseWriterParameters(uriInfo);
91 return new NormalizedNodeContext(iiWithData, data, writerParameters);
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);
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();
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);
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);
127 final UnkeyedListNode listAsUnkeyedList = unkeyedList(
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")));
134 final MapNode listAsMap = mapNode(
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")));
139 depth1Cont = container(
143 leafList("depth2-lfLst1", "depth2-lflst1-value1", "depth2-lflst1-value2", "depth2-lflst1-value3"),
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"));
151 depth2Cont1 = listAsUnkeyedList;
154 // TODO: These tests should be fixed/rewriten because they fail randomly due to data not being de-serialized
155 // properly in readers
157 public void getDataWithUriDepthParameterTest() throws WebApplicationException, IOException {
158 getDataWithUriDepthParameter("application/json");
159 getDataWithUriDepthParameter("application/xml");
162 public void getDataWithUriDepthParameter(final String mediaType) throws WebApplicationException, IOException {
163 ControllerContext.getInstance().setGlobalSchema(schemaContextModules);
166 // Test config with depth 1
167 response = target("/config/nested-module:depth1-cont").queryParam("depth", "1").request(mediaType)
169 txtDataToNormalizedNode(response, mediaType, "/config/nested-module:depth1-cont");
170 verifyResponse(nodeDataDepth1());
172 // Test config with depth 2
173 response = target("/config/nested-module:depth1-cont").queryParam("depth", "2").request(mediaType)
175 txtDataToNormalizedNode(response, mediaType, "/config/nested-module:depth1-cont");
176 verifyResponse(nodeDataDepth2());
178 // Test config with depth 3
179 response = target("/config/nested-module:depth1-cont").queryParam("depth", "3").request(mediaType)
181 txtDataToNormalizedNode(response, mediaType, "/config/nested-module:depth1-cont");
182 verifyResponse(nodeDataDepth3());
184 // Test config with depth 4
185 response = target("/config/nested-module:depth1-cont").queryParam("depth", "4").request(mediaType)
187 txtDataToNormalizedNode(response, mediaType, "/config/nested-module:depth1-cont");
188 verifyResponse(nodeDataDepth4());
190 // Test config with depth 5
191 response = target("/config/nested-module:depth1-cont").queryParam("depth", "5").request(mediaType)
193 txtDataToNormalizedNode(response, mediaType, "/config/nested-module:depth1-cont");
194 verifyResponse(nodeDataDepth5());
196 // Test config with depth unbounded
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());
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));
210 private void verifyResponse(final NormalizedNode<?, ?> nodeData) throws WebApplicationException, IOException {
211 assertNotNull(this.globalPayload);
212 assertEquals(this.globalPayload, nodeData);
213 this.globalPayload = null;
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;
226 private static LeafNode<?> leaf(final String localName, final Object value) {
227 return Builders.leafBuilder().withNodeIdentifier(toIdentifier(localName)).withValue(value).build();
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);
235 containerBuilder.withNodeIdentifier(toIdentifier(localName));
236 return containerBuilder.build();
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);
248 return builder.build();
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);
258 return builder.build();
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);
267 return builder.build();
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());
277 builder.withNodeIdentifier(toIdentifier(localName, keys));
279 for (final DataContainerChild<?, ?> child : children) {
280 builder.withChild(child);
282 return builder.build();
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());
292 return builder.build();
295 private static NodeIdentifier toIdentifier(final String localName) {
296 return new NodeIdentifier(QName.create("urn:nested:module", "2014-06-3", localName));
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),
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);
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"));
316 private ContainerNode nodeDataDepth5() {
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"),
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"));
338 private ContainerNode nodeDataDepth4() {
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"),
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"));
352 private ContainerNode nodeDataDepth3() {
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"));
365 private ContainerNode nodeDataDepth2() {
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"));
376 private ContainerNode nodeDataDepth1() {
377 return container("depth1-cont");