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;
13 import java.io.FileNotFoundException;
14 import java.io.IOException;
15 import java.util.HashMap;
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.NormalizedNodeContext;
36 import org.opendaylight.netconf.sal.rest.impl.NormalizedNodeJsonBodyWriter;
37 import org.opendaylight.netconf.sal.rest.impl.NormalizedNodeXmlBodyWriter;
38 import org.opendaylight.netconf.sal.rest.impl.RestconfDocumentedExceptionMapper;
39 import org.opendaylight.netconf.sal.rest.impl.WriterParameters;
40 import org.opendaylight.netconf.sal.rest.impl.XmlNormalizedNodeBodyReader;
41 import org.opendaylight.netconf.sal.restconf.impl.ControllerContext;
42 import org.opendaylight.netconf.sal.restconf.impl.QueryParametersParser;
43 import org.opendaylight.restconf.common.context.InstanceIdentifierContext;
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.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.SystemLeafSetNode;
56 import org.opendaylight.yangtools.yang.data.api.schema.SystemMapNode;
57 import org.opendaylight.yangtools.yang.data.api.schema.UnkeyedListEntryNode;
58 import org.opendaylight.yangtools.yang.data.api.schema.UnkeyedListNode;
59 import org.opendaylight.yangtools.yang.data.api.schema.builder.CollectionNodeBuilder;
60 import org.opendaylight.yangtools.yang.data.api.schema.builder.DataContainerNodeBuilder;
61 import org.opendaylight.yangtools.yang.data.api.schema.builder.ListNodeBuilder;
62 import org.opendaylight.yangtools.yang.data.impl.schema.Builders;
63 import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext;
64 import org.opendaylight.yangtools.yang.model.api.Module;
65 import org.opendaylight.yangtools.yang.parser.spi.meta.ReactorException;
66 import org.slf4j.Logger;
67 import org.slf4j.LoggerFactory;
69 public class CutDataToCorrectDepthTest extends JerseyTest {
71 private static final Logger LOG = LoggerFactory.getLogger(JerseyTest.class);
73 private static NormalizedNode depth1Cont;
74 private static NormalizedNode depth2Cont1;
75 private NormalizedNode globalPayload;
76 private static EffectiveModelContext schemaContextModules;
78 private final ControllerContext controllerContext =
79 TestRestconfUtils.newControllerContext(schemaContextModules, null);
82 public class RestImpl {
85 @Path("/config/{identifier:.+}")
86 @Produces({ "application/json", "application/xml" })
87 public NormalizedNodeContext getData(@Encoded @PathParam("identifier") final String identifier,
88 @Context final UriInfo uriInfo) {
90 final InstanceIdentifierContext iiWithData = controllerContext.toInstanceIdentifier(identifier);
92 NormalizedNode data = null;
93 if (identifier.equals("nested-module:depth1-cont/depth2-cont1")) {
95 } else if (identifier.equals("nested-module:depth1-cont")) {
99 final WriterParameters writerParameters = QueryParametersParser.parseWriterParameters(uriInfo);
100 return new NormalizedNodeContext(iiWithData, data, writerParameters);
104 @Path("/operational/{identifier:.+}")
105 @Produces({ "application/json", "application/xml" })
106 public NormalizedNodeContext getDataOperational(@Encoded @PathParam("identifier") final String identifier,
107 @Context final UriInfo uriInfo) {
108 return getData(identifier, uriInfo);
112 @Path("/config/{identifier:.+}")
113 @Consumes({ "application/json", "application/xml" })
114 public void normalizedData(@Encoded @PathParam("identifier") final String identifier,
115 final NormalizedNodeContext payload) throws InterruptedException {
116 LOG.info("Payload: {}.", payload);
117 LOG.info("Instance identifier of payload: {}.",
118 payload.getInstanceIdentifierContext().getInstanceIdentifier());
119 LOG.info("Data of payload: {}.", payload.getData());
120 globalPayload = payload.getData();
124 @Path("/operational/{identifier:.+}")
125 @Consumes({ "application/json", "application/xml" })
126 public void normalizedDataOperational(@Encoded @PathParam("identifier") final String identifier,
127 final NormalizedNodeContext payload) throws InterruptedException {
128 normalizedData(identifier, payload);
133 public static void initialize() throws FileNotFoundException, ReactorException {
134 schemaContextModules = TestUtils.loadSchemaContext("/modules");
135 final Module module = TestUtils.findModule(schemaContextModules.getModules(), "nested-module");
136 assertNotNull(module);
138 final UnkeyedListNode listAsUnkeyedList = unkeyedList(
140 unkeyedEntry("depth2-cont1",
141 container("depth3-cont1",
142 container("depth4-cont1", leaf("depth5-leaf1", "depth5-leaf1-value")),
143 leaf("depth4-leaf1", "depth4-leaf1-value")), leaf("depth3-leaf1", "depth3-leaf1-value")));
145 final MapNode listAsMap = mapNode(
147 mapEntryNode("depth2-list2", 2, leaf("depth3-lf1-key", "depth3-lf1-key-value"),
148 leaf("depth3-lf2-key", "depth3-lf2-key-value"), leaf("depth3-lf3", "depth3-lf3-value")));
150 depth1Cont = container(
154 leafList("depth2-lfLst1", "depth2-lflst1-value1", "depth2-lflst1-value2", "depth2-lflst1-value3"),
157 container("depth3-cont2",
158 container("depth4-cont2", leaf("depth5-leaf2", "depth5-leaf2-value")),
159 leaf("depth4-leaf2", "depth4-leaf2-value")), leaf("depth3-leaf2", "depth3-leaf2-value")),
160 leaf("depth2-leaf1", "depth2-leaf1-value"));
162 depth2Cont1 = listAsUnkeyedList;
165 // TODO: These tests should be fixed/rewriten because they fail randomly due to data not being de-serialized
166 // properly in readers
168 public void getDataWithUriDepthParameterTest() throws WebApplicationException, IOException {
169 getDataWithUriDepthParameter("application/json");
170 getDataWithUriDepthParameter("application/xml");
173 public void getDataWithUriDepthParameter(final String mediaType) throws WebApplicationException, IOException {
176 // Test config with depth 1
177 response = target("/config/nested-module:depth1-cont").queryParam("depth", "1").request(mediaType)
179 txtDataToNormalizedNode(response, mediaType, "/config/nested-module:depth1-cont");
180 verifyResponse(nodeDataDepth1());
182 // Test config with depth 2
183 response = target("/config/nested-module:depth1-cont").queryParam("depth", "2").request(mediaType)
185 txtDataToNormalizedNode(response, mediaType, "/config/nested-module:depth1-cont");
186 verifyResponse(nodeDataDepth2());
188 // Test config with depth 3
189 response = target("/config/nested-module:depth1-cont").queryParam("depth", "3").request(mediaType)
191 txtDataToNormalizedNode(response, mediaType, "/config/nested-module:depth1-cont");
192 verifyResponse(nodeDataDepth3());
194 // Test config with depth 4
195 response = target("/config/nested-module:depth1-cont").queryParam("depth", "4").request(mediaType)
197 txtDataToNormalizedNode(response, mediaType, "/config/nested-module:depth1-cont");
198 verifyResponse(nodeDataDepth4());
200 // Test config with depth 5
201 response = target("/config/nested-module:depth1-cont").queryParam("depth", "5").request(mediaType)
203 txtDataToNormalizedNode(response, mediaType, "/config/nested-module:depth1-cont");
204 verifyResponse(nodeDataDepth5());
206 // Test config with depth unbounded
208 response = target("/config/nested-module:depth1-cont").queryParam("depth", "unbounded")
209 .request(mediaType).get();
210 txtDataToNormalizedNode(response, mediaType, "/config/nested-module:depth1-cont");
211 verifyResponse(nodeDataDepth5());
214 private void txtDataToNormalizedNode(final Response response, final String mediaType, final String uri) {
215 final String responseStr = response.readEntity(String.class);
216 LOG.info("Response entity message: {}.", responseStr);
217 target(uri).request(mediaType).put(Entity.entity(responseStr, mediaType));
220 private void verifyResponse(final NormalizedNode nodeData) throws WebApplicationException, IOException {
221 assertNotNull(globalPayload);
222 assertEquals(globalPayload, nodeData);
223 globalPayload = null;
227 protected Application configure() {
228 ResourceConfig resourceConfig = new ResourceConfig();
229 resourceConfig = resourceConfig.registerInstances(new RestImpl());
230 resourceConfig.registerClasses(XmlNormalizedNodeBodyReader.class, NormalizedNodeXmlBodyWriter.class,
231 JsonNormalizedNodeBodyReader.class, NormalizedNodeJsonBodyWriter.class,
232 RestconfDocumentedExceptionMapper.class);
233 return resourceConfig;
236 private static LeafNode<?> leaf(final String localName, final Object value) {
237 return Builders.leafBuilder().withNodeIdentifier(toIdentifier(localName)).withValue(value).build();
240 private static ContainerNode container(final String localName, final DataContainerChild... children) {
241 final DataContainerNodeBuilder<NodeIdentifier, ContainerNode> containerBuilder =
242 Builders.containerBuilder();
243 for (final DataContainerChild child : children) {
244 containerBuilder.withChild(child);
246 containerBuilder.withNodeIdentifier(toIdentifier(localName));
247 return containerBuilder.build();
250 private static UnkeyedListNode unkeyedList(
251 final String localName,
252 final UnkeyedListEntryNode... entryNodes) {
253 final CollectionNodeBuilder<UnkeyedListEntryNode, UnkeyedListNode> builder = Builders.unkeyedListBuilder();
254 final NodeIdentifier identifier = toIdentifier(localName);
255 builder.withNodeIdentifier(identifier);
256 for (final UnkeyedListEntryNode unkeyedListEntryNode : entryNodes) {
257 builder.withChild(unkeyedListEntryNode);
259 return builder.build();
262 private static UnkeyedListEntryNode unkeyedEntry(final String localName, final DataContainerChild... children) {
263 final DataContainerNodeBuilder<NodeIdentifier, UnkeyedListEntryNode> builder =
264 Builders.unkeyedListEntryBuilder();
265 builder.withNodeIdentifier(toIdentifier(localName));
266 for (final DataContainerChild child : children) {
267 builder.withChild(child);
269 return builder.build();
272 private static MapNode mapNode(final String localName, final MapEntryNode... entryNodes) {
273 final CollectionNodeBuilder<MapEntryNode, SystemMapNode> builder = Builders.mapBuilder();
274 builder.withNodeIdentifier(toIdentifier(localName));
275 for (final MapEntryNode mapEntryNode : entryNodes) {
276 builder.withChild(mapEntryNode);
278 return builder.build();
281 private static MapEntryNode mapEntryNode(final String localName, final int keysNumber,
282 final DataContainerChild... children) {
283 final DataContainerNodeBuilder<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].getIdentifier().getNodeType(), children[i].body());
289 builder.withNodeIdentifier(toIdentifier(localName, keys));
291 for (final DataContainerChild child : children) {
292 builder.withChild(child);
294 return builder.build();
297 private static LeafSetNode<?> leafList(final String localName, final String... children) {
298 final ListNodeBuilder<Object, SystemLeafSetNode<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());
304 return builder.build();
307 private static NodeIdentifier toIdentifier(final String localName) {
308 return new NodeIdentifier(QName.create("urn:nested:module", "2014-06-03", localName));
311 private static NodeIdentifierWithPredicates toIdentifier(final String localName, final Map<QName, Object> keys) {
312 return NodeIdentifierWithPredicates.of(QName.create("urn:nested:module", "2014-06-03", localName), keys);
315 private static NodeWithValue<?> toIdentifier(final String localName, final Object value) {
316 return new NodeWithValue<>(QName.create("urn:nested:module", "2014-06-03", localName), value);
319 private static UnkeyedListEntryNode nodeDataDepth3Operational() {
320 return unkeyedEntry("depth2-cont1",
321 container("depth3-cont1", container("depth4-cont1"), leaf("depth4-leaf1", "depth4-leaf1-value")),
322 leaf("depth3-leaf1", "depth3-leaf1-value"));
325 private static ContainerNode nodeDataDepth5() {
330 unkeyedEntry("depth2-cont1",
331 container("depth3-cont1",
332 container("depth4-cont1", leaf("depth5-leaf1", "depth5-leaf1-value")),
333 leaf("depth4-leaf1", "depth4-leaf1-value")),
334 leaf("depth3-leaf1", "depth3-leaf1-value"))),
335 mapNode("depth2-list2",
336 mapEntryNode("depth2-list2", 2, leaf("depth3-lf1-key", "depth3-lf1-key-value"),
337 leaf("depth3-lf2-key", "depth3-lf2-key-value"), leaf("depth3-lf3", "depth3-lf3-value"))),
338 leafList("depth2-lfLst1", "depth2-lflst1-value1", "depth2-lflst1-value2", "depth2-lflst1-value3"),
341 container("depth3-cont2",
342 container("depth4-cont2", leaf("depth5-leaf2", "depth5-leaf2-value")),
343 leaf("depth4-leaf2", "depth4-leaf2-value")), leaf("depth3-leaf2", "depth3-leaf2-value")),
344 leaf("depth2-leaf1", "depth2-leaf1-value"));
347 private static ContainerNode nodeDataDepth4() {
350 unkeyedList("depth2-cont1", nodeDataDepth3Operational()),
351 mapNode("depth2-list2",
352 mapEntryNode("depth2-list2", 2, leaf("depth3-lf1-key", "depth3-lf1-key-value"),
353 leaf("depth3-lf2-key", "depth3-lf2-key-value"), leaf("depth3-lf3", "depth3-lf3-value"))),
354 leafList("depth2-lfLst1", "depth2-lflst1-value1", "depth2-lflst1-value2", "depth2-lflst1-value3"),
357 container("depth3-cont2", container("depth4-cont2"), leaf("depth4-leaf2", "depth4-leaf2-value")),
358 leaf("depth3-leaf2", "depth3-leaf2-value")), leaf("depth2-leaf1", "depth2-leaf1-value"));
361 private static ContainerNode nodeDataDepth3() {
364 unkeyedList("depth2-cont1",
365 unkeyedEntry("depth2-cont1", container("depth3-cont1"), leaf("depth3-leaf1", "depth3-leaf1-value"))),
366 mapNode("depth2-list2",
367 mapEntryNode("depth2-list2", 2, leaf("depth3-lf1-key", "depth3-lf1-key-value"),
368 leaf("depth3-lf2-key", "depth3-lf2-key-value"), leaf("depth3-lf3", "depth3-lf3-value"))),
369 leafList("depth2-lfLst1", "depth2-lflst1-value1", "depth2-lflst1-value2", "depth2-lflst1-value3"),
370 container("depth2-cont2", container("depth3-cont2"), leaf("depth3-leaf2", "depth3-leaf2-value")),
371 leaf("depth2-leaf1", "depth2-leaf1-value"));
374 private static ContainerNode nodeDataDepth2() {
377 unkeyedList("depth2-cont1", unkeyedEntry("depth2-cont1")),
378 mapNode("depth2-list2",
379 mapEntryNode("depth2-list2", 2, leaf("depth3-lf1-key", "depth3-lf1-key-value"),
380 leaf("depth3-lf2-key", "depth3-lf2-key-value"))), container("depth2-cont2"),
381 // leafList("depth2-lfLst1"),
382 leaf("depth2-leaf1", "depth2-leaf1-value"));
385 private static ContainerNode nodeDataDepth1() {
386 return container("depth1-cont");